Linux之进程等待

1.进程等待的必要性

之前讲过,子进程退出,父进程如果不管不顾,就可能造成僵尸进程的问题,进而造成内存泄漏。
另外,一旦一个进程变成僵尸状态,那就刀枪不入,“杀人不咋眼”的kill -9也无能为力,因为谁也没有办法杀死一个已经死去的进程。
最后,父进程派给子进程的任务完成的如何,我们需要知道,如,子进程运行完成,结果对还是不对或者是否正常退出。
父进程通过进程等待的方式,回收子进程的资源,获取子进程退出信息。

2.进程等待的方法

wait方法

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int* status);

返回值:成功,返回被等待进程pid,失败返回-1;
参数:输出型参数,获取子进程退出状态,
waitpid方法

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

返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时,errno会被设置成相应的值以指示错误所在;

参数:pid:
pid=-1,等待任一个子进程,与wait等效
pid>0,等待其进程ID与pid相等的子进程
status:
WIFEXITED(status);若为正常终止子进程返回的状态,则为真。(查看进程是否正常退出)
WEXITSTATUS(status);若WIFEXITED非零,提取子进程的退出码。(查看进程的退出码)
options:
默认为0,表示阻塞等待
WNOHANG(非阻塞式等待):若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则放回该子进程的ID。

waitpid(-1,NULL,0) = wait(NULL);

35    pid_t ret = waitpid(id,&status,0);//非阻塞式等待                                                                                                                                                          
 36     if(ret>0)
 37     {
 38      // printf("等待子进程成功,ret:%d,子进程收到的信号编号:%d,status:%d\n",ret,status&0x7F,(status>>8)&0xFF);
 39      if(WIFEXITED(status))//值:子进程正常退出为1,异常退出为0
 40      {
 41        //子进程是正常退出的
 42        printf("子进程执行完毕,子进程的退出码:%d\n",WEXITSTATUS(status));
 43      }
 44      else 
 45      {
 46        printf("子进程异常退出:%d\n",WIFEXITED(status));
 47      }
 48     }

2.1具体代码(重要)

#include <stdio.h>                                                                                
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 int main()
  7 {
  8   pid_t id = fork();
  9   if(id<0)
 10   {
 11     perror("fork");
 12     exit(1);//标识进程运行完毕,结果不正确
 13   }
 14   else if(id == 0)
 15   {
 16     //子进程
 17     int cnt = 5;
 18     while(cnt)
 19     {
 20       printf("cnt:%d,我是子进程,pid:%d,ppid:%d\n",cnt,getpid(),getppid());
 21       cnt--;
 22     }
 23     exit(54);
 24   }
 25   else 
 26   {
 27     //父进程
 28     printf("我是父进程,pid:%d,ppid:%d\n",getpid(),getppid());
 29    // pid_t ret = wait(NULL);//阻塞式等待
 30    int status = 0;
 31    pid_t ret = waitpid(id,&status,0);//阻塞式等待 等待5s
       //pid_t ret = waitpid(id,&status,WNOHANG);//非阻塞式等待 
 32     if(ret>0)
 33     {
 34       printf("等待子进程成功,ret:%d,status:%d\n",ret,status);
 35     }
 36     //基本没有意义的,只是为了对比演示
 37     while(1)
 38     {
 39       printf("我是父进程,pid:%d,ppid:%d\n",getpid(),getppid());
 40       sleep(1);
 41     }
 42   }
 43   return 0;
 44 }               

运行结果:

[jyf@VM-12-14-centos 进程]$ ./mytest10
我是父进程,pid:6469,ppid:31362
cnt:5,我是子进程,pid:6470,ppid:6469
cnt:4,我是子进程,pid:6470,ppid:6469
cnt:3,我是子进程,pid:6470,ppid:6469
cnt:2,我是子进程,pid:6470,ppid:6469
cnt:1,我是子进程,pid:6470,ppid:6469

// printf("等待子进程成功,ret:%d,status:%d\n",ret,status);  
等待子进程成功,ret:6470,status:13824

// printf("等待子进程成功,ret:%d,status:%d\n",ret,(status>>8)&0xFF);
//等待子进程成功,ret:10928,status:54

我是父进程,pid:6469,ppid:31362
我是父进程,pid:6469,ppid:31362
我是父进程,pid:6469,ppid:31362
我是父进程,pid:6469,ppid:31362
^C

一般都是在内核中阻塞,等待被唤醒,可见伴随着切换,scanf(),cin这些必定封装了系统调用。
通过waitpid参数options的值来控制阻塞式等待还是非阻塞式等待,options的值为0表示阻塞式等待,WNOHANG表示非阻塞式等待;
阻塞式等待时,父进程要等子进程结束后,才开始运行自己的程序;而非阻塞式等待父进程和子进程都在运行,仅仅判断当时那一次子进程的状态就返回了。

在这里插入图片描述

[jyf@VM-12-14-centos 进程]$ grep -ER 'WNOHANG' /usr/include/  //查找某个语句在目标文件夹下的内容
/usr/include/sys/wait.h:   If the WNOHANG bit is set in OPTIONS, and that child
/usr/include/sys/wait.h:   If the WNOHANG bit is set in OPTIONS, and that child
/usr/include/bits/waitflags.h:#define	WNOHANG		1	/* Don't block waiting.  */
/usr/include/valgrind/vki/vki-linux.h:#define VKI_WNOHANG	0x00000001
/usr/include/linux/wait.h:#define WNOHANG		0x0000000

可见WNOHANG是一个宏,默认为1;(Wait No Hang)夯住了,不就是这个进程没有被CPU调度吗。要么是在阻塞队列里,要么是等待被调度。

如果子进程已经返回,调用wait/waitpid后,wait/waitpid会立即返回,并且释放资源,获得子进程的退出信息
如果在任意时刻调用wait/waitpid,子进程还没退出,父进程可能阻塞
如果不存在该子进程,立即出错返回

1.父进程通过wait/waitpid可以拿到子进程的退出结果,为什么要用wait/waitpid函数呢?
进程具有独立性,那么数据会发送写时拷贝,父进程无法拿到,况且,信号呢?
2.既然进程具有独立性,进程退出码不也是子进程的的数据吗?父进程凭什么拿到?wait/waitpid究竟干了什么?
僵尸进程,至少要保留进程的PCB结构体,task_struct里面保留了任何进程退出时的结果信息;wait/waitpid究竟干了什么?本质其实就是读取子进程的task_struct结构体的信息。
3.wait/waitpid是系统调用,归系统管,task_struct是内核数据结构,所以有权限进行读取。

3.status的构成

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

status并不是按照整数来整体使用的。

  1. wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  2. 如果传递NULL,表示不关心子进程的退出状态信息
  3. 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  4. status不能简单的当作整型来看待,可以当作位图来看待,具体细节如下图,status是32个比特位的,我们现在只学习其低16位。

在这里插入图片描述
在这里插入图片描述
程序异常,不光光是内部代码有问题,也有可能是外力直接杀掉(子进程代码跑完了吗?不确定)

.cpp .cc .cxx都是C++文件的后缀名

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值