Linux下的进程等待

Linux下的进程等待

大家都知道父进程退出的时候,如果没关注到子进程,子进程就会变成僵尸进程,占用资源却不做事(吃白饭),这肯定不能被允许,那么怎么解决呢?
这里给大家提供两种方法。

  • 退出父进程。
    父进程一旦退出,子进程保留的资源也就自然释放。但是显然这种方法不常用,因为我们创建子进程的一部分原因就是为了分身探路,保证父进程的安全。下面主要介绍第二种方法。

  • 进程等待
    父进程一直关注着子进程,等到接到子进程的消息后,子进程就算是结束任务了,资源也就释放了。
    在命令行输入:

man 2 wait

就会有下面两个函数接口:
process wait
这就是两个进程等待的函数,它们都放在unistd.h中。
pid_t是Linux系统自定义的进程类型号,其实是宏定义的unsigned int 类型。
先来研究第一个函数。

  • wait

wait函数可以传入一个int * 型参数用来接收子进程的返回值,这就是status。
wait函数会使父进程阻塞,等待任意一个子进程结束,释放子进程的资源,再开始运行。返回值是子进程的进程号(pid)。

Example:

#include<stdio.h>//printf函数
#include<stdlib.h>//exit函数
#include<unistd.h>//wait函数

  int main(void)
  {
        pid_t pid = fork();//创建一个子进程
        if(pid == 0){
            printf("这是一个child process .\n");
            sleep(5);
            exit(0);  //子进程睡5秒然后退出,
        }
        //阻塞等待子进程运行完毕。
        wait(NULL);//填入NULL表示我不想存储返回值。
        printf("子进程运行完毕。");
        sleep(5);//父进程再睡5秒,便于我截图。
        return 0;
}

运行后就会发现,屏幕上啥都没有,5秒后才打印那句“子进程运行完毕。”,查看进程状态时就会发现,刚开始时,
wait1
父子进程都是在前台sleep,5秒后,子进程结束,没有出现僵尸进程。

  • waitpid

waitpid函数中有3个参数,第一个参数pid为-1表示任意一个子进程退出;pid为某个子进程的进程号时,则等到该子进程退出。
第二个参数跟wait函数一样,用来存储子进程的返回值。
第三个参数为0时表示阻塞等待,为 WNOHANG时表示不阻塞。
waitpid的返回值为0表示子进程未退出,为-1表示出错;如果子进程运行完毕,则返回子进程号。

看了这一段冗长的汉字,大家可能有点晕,来几个简单的例子阐述下,

//例子1
waitpid(-1,NULL,0);//等价于wait(NULL) 等待任意子进程退出,且阻塞等待。
//例子2
int val = 0;
waitpid(-1,&val,0);//表示等待任意子进程退出,将子进程的返回值存储到val中,阻塞等待。
//例子3
pid_t pid = fork();
waitpid(pid,&val,0);//表示等待进程号为pid的子进程退出,将该子进程的返回值放在val中,阻塞等待。

但是大家可能对一个词可能不太理解,就是阻塞,什么叫做阻塞呢?
术语一点就是为了完成某个功能的一个调用,缺少某种条件,一直等待。其实就是不动了,(但还能动)。
接着而来的一个思考就是,我为什么要设置不阻塞这个状态?
**让父进程等待子进程进行完毕不久好了吗?**NO,NO,NO!!!

  • Example2:
    小明在河边钓鱼,突然他想拿出塞在衣服下的《Linux操作系统》来看,怎么办?小明难道能一直盯着鱼钩吗?作为一个时间管理大师,这显然不行的。

所以不阻塞就是为了增加时间利用率,子进程未退出时,父进程干自己的事,直到子进程退出,再回来接收信息,释放资源。我们用循环来实现。

#include<stdio.h>//printf函数
#include<stdlib.h>//exit函数
#include<unistd.h>//wait函数

  int main(void)
  {
        pid_t pid = fork();//创建一个子进程
        if(pid == 0){
            sleep(5);
            exit(0);  //子进程睡5秒然后退出,
        }
        pid_t val;
        //如果子进程没完毕,则进入循环。这也是不阻塞时对父进程的利用。
       while((val=waitpid(pid,NULL,WNOHANG))==0){
       printf("趁子进程没完,打会LOL\n");
       sleep(1);//防止打印太快,
       }
       printf("Oh no,子进程完了\n");
       //printf中一定要加\n,不然缓冲区不会被刷新,就会最后一块打印。
        return 0;
}

结果如下:
LOL
刚开始时,子进程一直睡觉,父进程不管它,一直干自己的工作(循环打LOL),等到子进程结束,再回头去释放资源。

  • 接下来,我们看看子进程的返回值。
    上面我们说了子进程的返回值放在指针status指向的空间中。

  • Example3
    请看代码

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void)
{
        pid_t pid=fork();
        //子进程的分流
        if(pid==0){
                sleep(5);
                return 255;
        }   
        int status=0;//用来接收子进程的返回值
        pid_t val=0;//接收子进程的进程号
        //这也是不堵塞进程的常用做法,循环等待子进程end,
      while((val=waitpid(pid,&status,WNOHANG))==0){
                printf("请等待子进程结束\n");
                sleep(1);
        }  
        printf("子进程的返回值是: %d ",status);
        //输出子进程的返回值 
        printf("子进程的返回值是:%d\n",status);
        printf("我是父进程 .\n");
        printf("我在打LOL .");
        return 0;

结果如下:
255
我们发现,返回值竟然不是255。
当我们把子进程返回值改为256时,发现返回值变成0。
那么这是为什么呢?
实际上,进程的返回值存储的时候只用一个字节,所以256就是在二进制时进了一位,所以全是0。
255转换成16进制是0xff,而65280转换成16进制是0xff00,系统自动进了8个字节,所以得出以下规律:

status一共32位二进制位,而存储一个返回值只用9~16位。
那么我们如何打印出返回值呢?
我们再倒回去8位即可
这样处理

status = (status>>8);

但是,按位移动怎么能保证8位以后的位全是0呢?
按位与上8位以后全是0,之前全是1的数,即0xff。

status = (status>>8) & 0xff ;

最后的代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void)
{
        pid_t pid=fork();
        //子进程的分流
        if(pid==0){
                sleep(5);
                return 255;
        }   
        int status=0;//用来接收子进程的返回值
        pid_t val=0;//接收子进程的进程号
        //这也是不堵塞进程的常用做法,循环等待子进程end,
      while((val=waitpid(pid,&status,WNOHANG))==0){
                printf("请等待子进程结束\n");
                sleep(1);
        }  
        status = (status>>8) && 0xff;
        printf("子进程的返回值是: %d ",status);
        //输出子进程的返回值 
        printf("子进程的返回值是:%d\n",status);
        printf("我是父进程 .\n");
        printf("我在打LOL .");
        return 0;

结果与我们预想的一样:

255_2

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值