菜单

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

2018年12月16日 - LINUX

  wait() 函数的原型是:

  系统通过调用 release_task() 来刑满释放过程描述吻合。

 

出口结果:

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

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

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

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

结果如下:

·  当 exec() 执行成功时,exec()函数会取代执行其的过程,此时,exec() 函数没有回来值,进程结束。当 exec()函数执行破产时,将回失利新闻(返回-1),进程继续执行前边的代码。

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

  经常,创设新的经过都是为及时实施新的、不同之主次,而随着manbetx网页手机登录版,调用
exec() 这组函数就足以创建新的地址空间,并将新的次载入其中

 

manbetx网页手机登录版 1

  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 }    

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

  参数 status 保存着子进程退出时之一对状态(包括
task_struct、thread_info及内核栈当)它是一个对 int
类型的指针;假若无以意子进程的竣工状态值,只想把这僵尸进程消灭掉(实际上,大多数早晚都是这么做的),则好拿那个参数设为
NULL,即:

10)按照传递给clone() 的参数标志,copy_process()拷贝或共享打开的文件、文件系统新闻、信号处理函数、进程地址空间与命名空间非常;

 本文首要参考:

5)调用 exit_files() 和
exit_fs(),以各自递减文件描述符、文件系统数据的援计数,若其中之一引用计数的价值降到零,则意味着没有经过使相应的资源,可以纵掉进程占用的文件描述符、文件系统资源;

 

  9)调用 alloc_pid() 为新历程分配一个实用之 PID;

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

  1)fork() 调用clone;

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

 

  WUNTRACED;假诺实进程进入暂停实施之意况,则就赶回,但收尾状态不予理睬;

  至此,与经过并行关联的有所资源且深受放掉了,进程不可运行并处在
EXIT_ZOMBIE
退出状态。此时,进程本身所占用的内存还没有自由,如内核栈、thread_info
结构和 task_struct
结构等,它在的意思是为伯伯进程提供音信,当大伯进程收到音讯后,或者通告内核这是风马牛不相及之音后,进程所具有的剩下的内存以受放飞。父进程能够由此wait4()系统调用查询子进程是否停止,这实则叫进程有了等候特定进程执行了的力。

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

#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[]);  

 

  2)clone() 调用 do_fork();

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

 
5)检查并包新创是子进程后,当前用户所具有的历程数目没有过给她分配的资源的范围;

a)正常重临时,waitpid() 重返收集至的子进程的PID;

8)调用 schedule() 切换至新的经过,继续执行。由于 EXIT_ZOMBIE
状态的长河不会晤叫再调度,所以那是过程所举行的结尾一段落代码, do_exit()没有回到值。

  pid = 0 ;等待过程组 ID
与当前过程同之任何子进程(也就是是待与一个经过组中的任何子进程);

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

manbetx网页手机登录版 2

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

 

  在Linux系统中,通过调用fork()来创设一个历程。调用 fork()的经过称为大进程,新出的历程称为子进程。在该调用完时,在再次回到点这么些相同之座席上,父进程恢复生机执行,子进程开端执行。fork()系统调用从水源重临两不好:一不佳回到大爷进程,另一样不良回到到新来的子进程。使用fork()创设新过程的流程如下:

  当进程调用 wait() 时,会停顿如今历程的推行(即死),由 wait()来自动分析是否当前进程的之一子进程已经淡出,假如找到了如此一个早就变成僵尸进程的子进程,wait
就会征集之子进程的消息,并以这彻底销毁后再次来到;假诺没有找到这样一个子经过,wait
就会平素不通在此,直到出现僵尸进程。

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

 

  可以看看,调用 fork()函数后,原本只发一个进程,变成了点儿独过程。这半个经过除了 fpid
的值不同外几乎完全相同,它们还继续执行接下去的先后。由于 fpid
的价不同,由此会合跻身不同的论断语句,这吗是为啥多少个结果爆发不同之处的因由。此外,可以看来,父进程的
PID 刚好比子进程的 PID 小1。 fork()  的归来值有以下两种:

   wait3() 和 wait4()函数除了可以拿到子进程状态音讯外,还得拿到子进程的资源以信息,这一个消息是透过参数
rusage 拿到的。而 wait3() 与 wait4() 之间的区分是,wait3()等待所有进程,而 wait4() 可以按照 pid 的值采纳要候的子进程,参数 pid
的义与 waitpid() 函数的如出一辙。

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

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

11)做片告终工作并重返一个指向子进程的指针。

  从结果被得以看来,在子进程休眠的10s时空里,waitpid()并不曾直接等候,而是径直归回0,然后做自己的工作(睡眠1s),如此重了10浅;当子进程退出时,waitpid()收集至离的子进程,并赶回所收集子进程的PID。

  进程被创建后,最终只要终结。当一个经过终结时,内核必须放它所占有的资源,并把立时同样音讯告知该大进程。系统通过
exit() 系统调用来拍卖终止与剥离过程的相关工作,而多数做事尽管由
do_exit() 来完成 (kernel/exit.c):

  进程调用 exit() 退出行后,被装置为僵死状态,这时父进程能够通过
wait4()系统调用查询子进程是否终止,之后再行举行末的操作,彻底剔除进程所占据的内存资源。
wait4() 系统调用由 linux 内核实现,linux 系统便提供了
wait()、waitpid()、wait3()、wait4()这三只函数,五只函数的参数不同,语义也起轻的出入,但是都回关于休进程的状态音信。

  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 }

1、wait() 函数:

4)调用 sem__exit() 函数,假使经过排队等候 IPC 信号,它即使离开队列;

  函数原型:

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

  也得将即刻点儿个采取组合起来用,使用 OR
操作。假设非思行使这有限独挑选,也可以一向将 options 设为0 ,如下:

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

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

 

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

  4)调用 dup_task_struct()为新进程创制一个内核栈、thread_info结构和task_struct,那多少个价值和眼前进程的价相同;

  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 }     

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

manbetx网页手机登录版 3

c)假如 fork() 调用出错,则赶回负值

输出结果如下:

1)执行文书部分:也即使是函数中之 path
部分,该片段指出了可执行文件的摸索方法。其中
execl、execle、execv、execve的搜索方法都是采取的相对路径,而
execlp和execvp则可但为起文件称举办搜索,系统会打环境变量
“$PATH”中找找相应的路径;

2、waitpid() 函数:

manbetx网页手机登录版 4

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

pid_t wait(int *status);

manbetx网页手机登录版 5

   waitpid() 函数的效率与 wait() 的功力看似,不过,它比 wait()函数多了点滴单参数:

无异于、 进程创立:

 

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

 

 

  函数原型:

  Unix
下的过程创立好特别,与众旁操作系统不同,它分点儿步操作来创建和推行进程:
fork() 和 exec() 。首先,fork()通过拷贝当前经过创立一个子历程;然后,exec()函数负责读取可执行文件并拿那些载入地址空间开头运行。

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

  7)将子进程的状态设置为 TASK_UNINTERRUPTIBLE,保证其不谋面投入运作;

b)即便安了 WNOHANG,而调用 waitpid()时,没有意识曾脱离的子进程可采,则回回0;

a)在二伯进程遭到,fork() 再次来到新创建子进程的 PID;

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

 

  WNOHANG;假使没其他已经截至了的子进程,则立时赶回,不等待;

  3)do_fork() 调用 copy_process() 函数,copy_process() 函数将不辱使命第
4-11 步;

  pid > 0 ;等待其他子进程 ID 也 pid
的子进程,只要指定的子进程还没截至,waitpid() 就会合一贯顶下。

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

  假使 wait()调用成功,则会回来给收集子进程的经过ID;假使让调用进程没有分支进程,则调用战败,再次来到-1

   以斯次中,使用 fork() 创制了一个子过程,随后立时调用 exec()函数簇中之 execv() 函数,execv()函数执行了同等长指令,显示当前系统受存有的历程,结果如下(进程来这个,这里才截了一如既往有些):

c)如若调用出错,则赶回 -1,那时erron
会被装也对应的价值为提醒错误所于。(当 pid
所指示的子进程不错在,或此过程在,但切莫是调用进程的子进程, waitpid()就会面回来出错,这时 erron 被安装为 ECHILD)

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

#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);

1、fork() :kernel/fork.c

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

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

2)命令参数有:也便是函数中的 file
部分,该有提出了参数的传递情势与若传递哪些参数。那里,”l”结尾的函数表示用各样列举的措施传送参数;”v”结尾的意味以拥有参数全体布局成一个指针数组举办传递,然后拿该数组的首地址当做参数传递给它,数组中的末尾一个指南针要求啊
NULL;

次、进程终结

12)回到 do_fork() 函数,如果 copy_process()函数成功再次来到,新创立的子进程将于唤醒并叫这投入运作。

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

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

  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 }

相关文章

发表评论

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

网站地图xml地图