【Linux】实验报告4 进程管理 您所在的位置:网站首页 查看程序的进程和线程实验收获 【Linux】实验报告4 进程管理

【Linux】实验报告4 进程管理

2023-07-29 14:32| 来源: 网络整理| 查看: 265

实验四 进程管理 实验目录

文章目录 实验四 进程管理实验目录实验目的实验原理1.1 Linux中的进程1.2 进程的基本状态1.3 进程的实体结构1.4 进程存储空间1.5 进程号PID&PPID1.6 进程相关函数所需头文件getpid( )getppid( )fork( )wait( )exec( ) 实验作业1. fork.c实践源代码问题描述程序运行结果结果分析 2. fork2.c实践源代码问题描述程序运行结果结果分析 3. processimage.c实践源代码问题描述程序运行结果结果分析 4. exec.c实践源代码问题描述程序运行结果结果分析 5. wait.c实践源代码问题描述程序运行结果结果分析

实验目的

通过实际上机调试和运行程序了解 Linux 系统中进程的基本编程;了解父进程和子进程的概念

实验原理 1.1 Linux中的进程

在Linux系统中,进程 被认为是 具有一定功能的程序在一个数据集上的一次运行活动,是处于活动状态的计算机程序,操作系统是通过进程去完成一个个任务,进程是管理事务的基本单元

Linux是一个 多进程 的系统,进程之间具有 互不干扰 的特点。其中,每个进程都运行在各自独立的虚拟地址空间。因此,即使一个进程发生了异常,它也不会影响到系统的其他进程。创建一个进程时,系统会分配0—4G 虚拟地址空间,其中0—3G 是用户空间, 3—4G是内核空间,多个进程共用同一份内核,各自进程的用户空间是独立的

1.2 进程的基本状态 就绪态 :程序满足执行的条件 ,等待CPU分配时间片执行态: CPU分配时间片给当前进程 ,进程正在占用CPU执行发任务的过程等待态 :程序在执行过程中,不满足条件,而进行睡眠状态 image-20220519160553669 1.3 进程的实体结构

1. 进程控制块(PCB)

PCB是操作系统为了管理进程设置的一个专门的 结构体 task_struct

操作系统用它来记录进程的外部特征,描述进程的运动变化过程,系统也可以利用PCB来控制和管理进程

image-20220528154700586

2. 程序段

位于内存中,存放该程序的代码

3. 数据段

位于内存中,存放该程序运行过程中存放的各种数据(如变量等) 1.4 进程存储空间

img

内存空间代码验证

#include #include int global_init_val = 100; int global_noninit_val; int main(int argc,char *argv[],char * envp[]){ static int local_static_val = 1000; char *local_val; local_val = malloc(10); printf("address of text:%p\n",main); printf("address of data:%p %p\n",&global_init_val,&local_static_val); printf("address of bss:%p\n",&global_noninit_val); printf("address of heap:%p\n",local_val); printf("address of stack:%p\n",&local_val); free(local_val); printf("&environ = %p, environ = %p\n",&envp,envp); printf("&argv = %p, argv = %p\n",&argv,argv); return 0; }

image-20220528164021913

1.5 进程号PID&PPID

每个进程都由一个进程号 PID 来标识,其类型为 pid_t(无符号整型),进程号的范围:0~32767。进程号总是 唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再次使用。

进程除了 PID 外,还有 PPID (父进程号),任何进程( 除 init 进程)都是由另一个进程创建。所有进程的祖先进程是同一个进程,它叫做init 进程,所以,在 Linux 下面所有的进程都由 init 进程直接或者间接创建。init 进程的PID 为 1, 是内核的第一个启动用户的进程。

image-20220519162437009

缩写中文名称描述获取方式PID进程号标识进程的一个非负整型数。pid_t getpid(void);PPID父进程号若A进程创建了 B 进程,A 的进程号就是 B 进程的父进程号。pid_t getppid(void); 1.6 进程相关函数 所需头文件 #include #include #include #include getpid( ) pid_t getpid() #获取当前进程的PID号 getppid( ) pid_t getppid() #获取当前进程的PID号 fork( ) pid_t pid=fork() #创建一个新的进程 原型pid_t fork(void);函数功能创建一个子进程返回值fork调用一次返回两次,父进程中返回子进程的ID号

父、子进程是 完全一样的(代码、数据),子进程从fork内部开始执行、fork返回子进程的pid后,接着执行下一条语句

fork( )调用的特点是 调用一次,返回两次,它可能有三种不同的返回值:

在父进程中,fork返回新创建子进程PID在子进程中,fork返回0如果出现错误,fork返回一个负值

fork实例:

#include #include #include int main(void){ pid_t pid=fork(); //fork时,子进程诞生,子进程从fork语句后开始执行 if(pid==0){ //子进程 printf("PID=%d, PPID=%d\n",getpid(),getppid()); printf("我是子进程\n"); }else if(pid>0){ printf("PID=%d, PPID=%d\n",getpid(),getppid()); printf("我是父进程\n"); } return 0; }

image-20220602113829971

fork函数视频讲解

fork函数视频讲解2

wait( ) pid_t wait(int *status) 原型pid_t wait(int *status)函数功能调用wait后父进程将被阻塞,wait会分析当前进程的某个子进程是否已经退出。如果它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它销毁后返回;如果没有找到这样一个子进程,wait会一直阻塞,直到有一个出现为止。返回值返回被收集的子进程的PID,若调用进程没有子进程,此时返回-1,同时errno被置为ECHILD。

当父进程没有等待已终止的子进程时,子进程就会进入一种无父进程的状态,此时的子进程就是 僵尸进程,进而造成内存泄露。进程一旦变成僵尸状态,强制杀死进程的”kill -9”也无能为力,所以我们需要知道子进程的任务完成的如何,是否正常退出。

因此,我们需要让父进程等待自己的子进程,或者 父进程回收自己的子进程资源 包括僵尸进程。我们通过wait( )函数来完成这一功能

参数status 用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL

注意点

wait( )要与fork( )配套出现,如果在使用fork()之前调用wait( ),wait( )的返回值为-1,正常情况下wait( )的返回值为子进程的PID

如果先终止父进程,子进程将继续正常进行,只是它将由init进程(PID=1)继承,当子进程终止时,init进程捕获这个状态

wait函数视频讲解

exec( )

使用fork( )函数创建子进程后,子进程通常会调用 exec函数 来执行另外一个程序

当进程调用exec函数时,该进程由新程序完全替换,即用一个可执行程序 代替当前进程的执行映像,而新程序则 从其main函数开始执行。因为调用exec并不创建新进程,所以 前后的进程ID并未改变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。

exec函数视频讲解

实验作业 1. fork.c实践 源代码 #include #include #include int main(void) { pid_t pid; printf("Process Creation Study\n"); pid = fork(); switch(pid) { case 0: printf("Child process is running,CurPid is %d,ParentPid is %d\n", getpid(), getppid()); break; case -1: perror("Process creation failed\n"); break; default: printf("Parent process is running,CurPid is %d,ChildPid is %d\n", getpid(), pid); break; } return 0; } 问题描述

请回答,程序的运行结果是什么?并且分析程序输出这个结果的原因。

程序运行结果

image-20220602112144317

结果分析 运行父进程时,pid=295319,即为子进程的PID;同时getpid( )返回了该父进程的PID值295318运行子进程时,pid=0,由getpid( )获取子进程的PID值295319;同时getppid( )返回了其PPID值295318 2. fork2.c实践 源代码 #include #include #include #include int main(void) { pid_t pid; char * msg; int k; printf("Process Creation Study\n"); pid = fork(); switch(pid){ case 0: msg = "Child process is running"; k = 3; break; case -1: perror("Process creation failed\n"); break; default: msg = "Parent process is running"; k=5; break; } while(k > 0) { puts(msg); sleep(1); k--; } exit(0); } 问题描述

请回答,程序的运行结果是什么?并且分析程序输出这个结果的原因。

程序运行结果

image-20220602111302161

结果分析

该代码验证了fork( )函数 调用子进程、父进程的顺序。一般来说,fork( )之后是父进程先执行还是子进程先执行是不确定的,这取决于内核所使用的算法。

从代码结果可见,fork( )函数对两者的调用是 交替进行 的,否则得到的结果应该是连续输出5句”Parent process is running“以及连续输出3句"Child process is running",而不是两者交替输出。

3. processimage.c实践 源代码 #include #include #include int main(int argc, char *argv[], char **environ){ int i; printf("I am a process image!\n"); printf("My pid = %d, parentpid = %d\n",getpid(), getppid()); printf("uid:%d, gid%d\n", getuid(), getgid()); for(i=0; i case -1: perror("Process Creation failed\n"); exit(1); case 0: printf("Child process is running\n"); printf("My pid = %d ,parentpid = %d\n",getpid(),getppid()); printf("uid = %d,gid =%d\n",getuid(),getgid()); execve("processimage.out",argv, environ); printf("process never go to here!\n"); exit(0); default: printf("Parent process is running\n"); break; } wait(&stat_val); exit(0); } 问题描述

回答:程序执行的结果是什么?请分析改程序的执行过程。

程序运行结果

image-20220602110525829

结果分析

在本次程序代码运行过程中,fork( )函数首先调用了父进程,即先输出了“Parent process is running"

之后fork( )函数调用了子进程,正常执行到line17。在line18处,由于execve命令,系统调用processimage.out的内容执行并且覆盖原代码,故line19不会被执行,即”process never go to here!“不会输出在屏幕上

5. wait.c实践 源代码 #include #include #include #include #include int main() { pid_t pid; char *msg; int k; int exit_code; printf("Study how to get exit code\n"); pid = fork(); switch(pid) { case 0: msg = "Child process is running"; k = 5; exit_code = 37; break; case -1: perror("Process creation failed\n"); exit(1); default: exit_code = 0; break; } /* 父子进程都会执行以下这段代码子进程中pid值为0,父进程pid值为子进程的ID */ if(pid != 0){ // 父进程等待子进程结束 int stat_val; pid_t child_pid; child_pid = wait(&stat_val); printf("Child procee has exited, pid = %d\n",child_pid); if(WIFEXITED(stat_val)) printf("Child exited with code %d\n",WEXITSTATUS(stat_val)); else printf("Child exited abnormally\n"); }else{ // 子进程暂停5秒,在这个过程中可以运行命令ps aux查看父进程状态 while(k-->0) { puts(msg); sleep(1); } } exit(exit_code); } 问题描述

回答:程序运行的结果是什么?请根据结果结合程序代码分析父进程等待子进程的。

程序运行结果

image-20220602110259543

结果分析 由于line34 wait( )的存在,使得父进程暂时处于阻塞状态,需要等待被fork( )出来的子进程执行完毕,才会继续执行父进程的代码。因此,该源代码首先输出了子进程的内容,即连续输出6句“Child process is running”等待子进程结束后,wait( )回收子进程资源,并且将status赋值为子进程退出的状态值,最后将父进程的输出内容打印在屏幕上


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有