Linux内核学习笔记(4)– wait、waitpid、wait3 和 wait4

  进程调用 exit() 退出执行后,被安装也僵死状态,那时父进程能够透过
wait4()系统调用查询子进程是否得了,之后再拓展末段之操作,彻底删除进程所占有的内存资源。
wait4() 系统调用由 linux 内核实现,linux 系统平时提供了
wait()、waitpid()、wait3()、wait4()这四单函数,五只函数的参数不同,语义也发生轻的区别,可是都回关于休进程的状态音讯。

如出一辙、 进程创设:

1、wait() 函数:

  Unix
下之进程成立好特别,与广大外操作系统不同,它分点儿步操作来成立同推行过程:
fork() 和 exec() 。首先,fork()通过拷贝当前历程成立一个子过程;然后,exec()函数负责读取可执行文件并拿这载入地址空间起先运行。

  wait() 函数的原型是:

1、fork() :kernel/fork.c

#include <sys/types.h>        // 提供类型 pid_t 的定义
#include <sys/wait.h>

pid_t wait(int *status);

  于Linux系统中,通过调用fork()来成立一个过程。调用 fork()的过程称为大进程,新发生的进程称为子进程。在该调用了时,在返回点这么些相同的位子上,父进程復苏执行,子进程起首实践。fork()系统调用从基础重回两不行:一不行回到大进程,另一样不善回到新发生的子进程。使用fork()创设新历程的流程如下:

  当进程调用 wait() 时,会中断近来进程的实践(即死),由 wait()来自动分析是否当前历程的某某子进程已经退出,假诺找到了这么一个早就化为僵尸进程的子进程,wait
就会晤收集者子进程的音,并将这么些彻底灭绝后回去;假设没有找到这么一个子进程,wait
就会直接不通在此地,直到出现僵尸进程。

  1)fork() 调用clone;

  参数 status 保存着子进程退出时的有的状态(包括
task_struct、thread_info及内核栈当)它是一个对 int
类型的指针;假设未以意子进程的了状态值,只想把这多少个僵尸进程消灭掉(实际上,大多数下还是如此做的),则可将是参数设为
NULL,即:

  2)clone() 调用 do_fork();

pid = wait(NULL);        // 不管子进程的结束状态,直接杀死进程

manbetx手机网页版,  3)do_fork() 调用 copy_process() 函数,copy_process() 函数将做到第
4-11 步;

  倘若 wait()调用成功,则会回到给收集子进程的过程ID;倘使给调用进程没有分进程,则调用退步,再次回到-1

  4)调用 dup_task_struct()为新过程创立一个内核栈、thread_info结构和task_struct,这些价值和当前经过的价值相同;

  接下去用相同段代码来演示一下 wait() 的用法:

 
5)检查并保证新创办是子进程后,当前用户所拥有的进程数目没有盖给它分配的资源的界定;

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 #include <stdlib.h>                                                                    
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 
  7 void main(){
  8     pid_t fpid,rpid;
  9     fpid = fork();
 10     if(fpid < 0){        
 11         perror("error on forking!\n");
 12     }
 13     else if(fpid == 0){
 14         printf("this is a child process! the pid is %d\n",getpid());
 15         sleep(3);
 16     }
 17     else{
 18         rpid = wait(NULL);          // 如果 wait()调用成功,则返回子进程的PID;如果调用失败,则返回 -1
 19         printf("Catch the child process with pid of %d\n",rpid);
 20     }
 21     exit(0);
 22 }    

 
6)清理子进程经过描述符中的一部分分子(清零或初始化,如PID),以要得子进程与四叔进程区别开来;

出口结果如下:

  7)将子进程的状态设置为 TASK_UNINTERRUPTIBLE,保证她不会合投入运行;

manbetx手机网页版 1

  8)调用 copy_flags() 以更新 task_struct 的 flags 成员;

   关于 status
参数,相比较复杂,暂时未做探究,可以参见这里:https://www.ibm.com/developerworks/cn/linux/kernel/syscall/part3/index.html

  9)调用 alloc_pid() 为新历程分配一个卓有功效的 PID;

 

10)遵照传递让clone() 的参数标志,copy_process()拷贝或共享打开的文书、文件系统音信、信号处理函数、进程地址空间及命名空间卓殊;

2、waitpid() 函数:

11)做一些得了工作并返一个指向子进程的指针。

  函数原型:

12)回到 do_fork() 函数,如果 copy_process()函数成功重临,新创办的子进程将于指示并受该投入运行。

#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid,int *status,int options);

  下边用同截简单的代码演示一下 fork() 函数:

   waitpid() 函数的成效与 wait() 的效能相近,不过,它相比 wait()函数多矣少数独参数:

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 
  4 int main(){
  5     pid_t fpid;
  6     int count= 0;
  7     fpid = fork();              // fpid 为fork()的返回值
  8     if(fpid < 0){               // 当fork()的返回值为负值时,表明调用 fork() 出错
  9         printf("error in fork!");
 10     }
 11     else if(fpid  == 0){        // fork() 返回值为0,表明该进程是子进程
 12         printf("this is a child process, the process id is %d\n",getpid());
 13         count++;
 14     }
 15     else{                       // fork() 返回值大于0,表明该进程是父进程,这时返回值其实是子进程的PID
 16         printf("this is a father process, the process id is %d\n",getpid());
 17         count++;
 18     }
 19     printf("计数 %d 次\n",count);
 20     return 0;                                                                          
 21 }

1)参数 pid 为欲等待的子进程的认识别码:

出口结果:

  pid < -1 ;等待历程组 ID 为 pid 相对值的历程组中的任何子进程;

manbetx手机网页版 2

  pid = -1 ;等待任何子进程,此时 waitpid() 十分给
wait()。实际上,wait()就是 pid = -1、options = 0
的waitpid(),
 且有:

  可以望,调用 fork()函数后,原本只是出一个进程,变成了有限独过程。这一点儿独经过除了 fpid
的价不同外几乎完全相同,它们还继续执行接下去的次序。由于 fpid
的价值不同,由此会师进去不同的判定语句,这也是干吗六个结实发生不同之处的由。此外,能够看,父进程的
PID 刚好比子进程的 PID 小1。 fork()  的返值有以下两种植:

static inline pid_t wait(*status){
    return waitpid(-1,*status,0);  
}

a)在二伯进程面临,fork() 返回新创制子进程的 PID;

  pid = 0 ;等待过程组 ID
与当下进程同的任何子进程(也即是等和一个过程组中的任何子进程);

b)在子进程被,fork() 再次来到0;

  pid > 0 ;等待其他子进程 ID 也 pid
的子进程,只要指定的子进程还尚未终结,waitpid() 就相会一直万分下去。

c)要是 fork() 调用出错,则归负值

2)参数 options 提供一些额外的取舍项来支配 waitpid():

 

  WNOHANG;要是无任何已经完结了的子进程,则随即重返,不等待;

 2、exec() :fs/exec.c (源程序
exec.c 实现对二进制可执行文件和 shell 脚本文件的加载与履行)

  WUNTRACED;假诺实进程上暂停实施之情状,则就回到,但收尾状态不予理睬;

  日常,创制新的经过都是为及时执行新的、不同的顺序,而随之调用
exec() 这组函数就得创造新的地址空间,并拿新的次序载入其中

  也足以以顿时有限只挑选组合起来用,使用 OR
操作。假设无想念选拔这一点儿个挑选,也可以一向将 options 设为0 ,如下:

  exec() 并无是一个函数,而是一个函数簇,一共包含六只函数,分别吗:
execl、execlp、execle、execv、execvp、execve,定义如下:

waitpid(-1,NULL,WNOHANG | WUNTRACED);     // 没有任何已结束了的子进程或子进程进入暂停执行的状态,则马上返回不等待
waitpid(-1,NULL,0);                // options 设为0,则 waitpid() 会一直等待,直到有进程退出
#include <unistd.h>  

int execl(const char *path, const char *arg, ...);  
int execlp(const char *file, const char *arg, ...);  
int execle(const char *path, const char *arg, ..., char *const envp[]);  
int execv(const char *path, char *const argv[]);  
int execvp(const char *file, char *const argv[]);  
int execve(const char *path, char *const argv[], char *const envp[]);  

3)waitpid() 的归值,有三种:

  这六单函数的职能实在不比不多,只是接受之参数不同。exec()函数的参数首要发生3唯有:执行文书部分、命令参数有以及环境变量部分:

a)正常重返时,waitpid() 再次来到收集及之子进程的PID;

1)执行文书部分:也不怕是函数中的 path
部分,该片段指出了可执行文件的索方法。其中
execl、execle、execv、execve的搜方法依然运的相对路径,而
execlp和execvp则好只有给有文件称展开查找,系统会自环境变量
“$PATH”中觅相应的途径;

b)假诺设置了 WNOHANG,而调用 waitpid()时,没有发觉早已离的子进程可采,则赶回回0;

2)命令参数有:也固然是函数中之 file
部分,该有的提出了参数的传递情势及一旦传送哪些参数。这里,”l”结尾的函数表示用各类列举的法子传送参数;”v”结尾的象征将具有参数全部布局成一个指针数组举办传递,然后以该数组的首地址当做参数传递给她,数组中之最后一个指针要求呢
NULL;

c)倘诺调用出错,则赶回 -1,这时erron
会被安装为相应的价值为提醒错误所当。(当 pid
所指示的子进程不错在,或其一过程在,但不是调用进程的子进程, waitpid()就相会回来出错,那时 erron 被装置也 ECHILD)

3)环境变量部分:exec()函数簇使用了系统默认的环境变量,也堪流传指定的环境变量。其中 execle
和execve 这简单个函数就可在 envp[] 中指定当前经过所动的环境变量。

 

·  当 exec() 执行成功时,exec()函数会取代执行其的经过,此时,exec() 函数没有回去值,进程截止。当 exec()函数执行破产时,将回到败北音讯(再次来到-1),进程继续执行后边的代码。

  1 #include <sys/types.h> 
  2 #include <sys/wait.h>
  3 #include <unistd.h>
  4 #include <stdio.h>
  5 #include <stdlib.h>
  6
  7 void main(){
  8     pid_t fpid,rpid;                          // fpid为fork()的返回值,rpid为waitpid()的返回值
  9     fpid = fork();
 10     if(fpid < 0){
 11         printf("error on forking");
 12     }
 13     else if(fpid == 0){                       // 子进程中 fork() 返回值为0
 14         printf("this is a child process,pid is %d\n",getpid());
 15         sleep(10);                            // 睡眠10s,10s 后子进程退出
 16         exit(0);
 17     }
 18     do{                                  // 父进程中,fork()返回新创建子进程的 PID
 19         rpid = waitpid(fpid,NULL,WNOHANG);    // 等待 PID = fpid 的进程(即子进程)退出,设置了WNOHANG选项,表明当没有发现已退出的子进程时不用等待直接返回,返回值为0;
 20         if(rpid == 0){                        // rpid = 0,说明没有发现已退出的子进程
 21             printf("No child exited\n");
 22             sleep(1);
 23         }
 24     }while(rpid == 0);
 25     if(fpid == rpid)                         // 成功收集了退出的子进程,返回值为被收集子进程的PID
 26         printf("successfully get child process %d\n",rpid);
 27     else
 28         printf("error!\n");
 29 }     

  通常,exec() 会放在 fork()函数的子进程部分,来替代子进程继续执行,exec()执行成功后子进程就会合没有,不过执行破产以来,就必将须要使用 exit()函数来让子进程退出。下面用同段落简单的代码来演示一下 exec()函数簇中之一个函数的用法,另外的参照:https://www.cnblogs.com/dongguolei/p/8098181.html

结果如下:

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 #include <errno.h>
  4 #include <string.h>
  5 
  6 int main(){
  7     int childpid;
  8     pid_t fpid;
  9     fpid = fork();
 10     if(fpid == 0){                          // 子进程
 11         char *execv_str[] = {"ps","aux",NULL};      // 指令:ps aux 查看系统中所有进程 
 12         if( execv("/usr/bin/ps",execv_str) < 0 ){
 13             perror("error on exec\n");
 14             exit(0);
 15         }
 16     }
 17     else{
 18         wait(&childpid);
 19         printf("execv done\n");
 20     }
 21 }

manbetx手机网页版 3

 

  从结果丁能够看出,在子进程休眠的10s岁月里,waitpid()并无平昔等,而是径直回回0,然后开自己之工作(睡眠1s),如此重了10不佳;当子进程退出时,waitpid()收集及离的子进程,并赶回所收集子进程的PID。

   在是顺序中,使用 fork() 创制了一个子经过,随后登时调用 exec()函数簇中之 execv() 函数,execv()函数执行了相同长长的指令,显示当前系受存有的长河,结果如下(进程来诸多,这里才截了一样局部):

 

manbetx手机网页版 4

 3、wait3()、wait4() 函数:

manbetx手机网页版 5

  函数原型:

  注意看最终两单过程,分别是老子进程同调用 fork() 后创的子进程。

#include <sys/tpyes.h>
#include <sys/wait.h>

pid_t wait3(int *status,int options,struct rusage *rusage);
pid_t wait4(pid_t pid,int *status,int options,struct rusage *rusage);

 

   wait3() 和 wait4()函数除了可拿到子进程状态信息外,还好得到子进程的资源采纳音信,这一个音讯是通过参数
rusage 得到的。而 wait3() 与 wait4() 之间的分别是,wait3()等待所有进程,而 wait4() 可以遵照 pid 的值拔取而等待的子进程,参数 pid
的意义和 waitpid() 函数的同一。

亚、进程终结

 

  进程被制造后,最后要结。当一个过程终结时,内核必须释放它所占的资源,并将当下无异于消息告知该大进程。系统通过
exit() 系统调用来处理终止与退出过程的连锁工作,而大多数办事则是因为
do_exit() 来完成 (kernel/exit.c):

 本文首要参考:

1)将task_struct 中之表明成员设置也 PF_EXITING;

https://www.ibm.com/developerworks/cn/linux/kernel/syscall/part3/index.html

2)调用 del_timer_sync()删除任一内决定时器,以保无定时器在排队,也没有定时器处理程序在运作;

 

3)调用 exit_mm() 函数放过程占用的
mm_struct,如若没另外进程使它们(地址空间为共享),就彻底释放它们;

 

4)调用 sem__exit() 函数,如若经过排队等候 IPC 信号,它则去队列;

 

5)调用 exit_files() 和
exit_fs(),以各自递减文件描述符、文件系统数据的援计数,若其中有引用计数的价降到零,则象征从没经过使相应的资源,能够放掉进程占用的公文描述符、文件系统资源;

 

6)把 task_struct 的 exit_code 成员设置为经过的再次来到值;

7)调用 exit_notify() 向大进程发送信号,并将过程状态设置为
EXIT_ZOMBIE;

8)调用 schedule() 切换至新的长河,继续执行。由于 EXIT_ZOMBIE
状态的经过不碰面被再度调度,所以顿时是经过所实施的结尾一段落代码, do_exit()没有回来值。

  至此,与经过并行关联的持有资源都吃放掉了,进程不可运行并处在
EXIT_ZOMBIE
退出状态。此时,进程本身所占有的内存还从未放,如内核栈、thread_info
结构和 task_struct
结构等,它有的意思是向阳大爷进程提供音讯,当大叔进程收到音信后,或者布告内核这是风马牛不相及之音后,进程所负有的剩余的内存以于放出。父进程可以通过
wait4()系统调用查询子进程是否得了,这事实上叫进程具有了守候特定进程执行完毕的力量。

  系统经过调用 release_task() 来刑满释放过程描述称。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图