Linux C:wait函数和waitpid函数

 

如果子进程的退出状态没有被收集,那么这个结束的子进程就会变成僵尸(zombie)进程。

这是一个没有收集状态的demo:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int cnt = 0;
    
    pid = vfork();

    if (pid > 0)
    {
        while(1)
        {
            printf("cnt: %d\n", cnt);
            printf("This is parent print, pid: %d\n", getpid());
            sleep(1);
        }
    }
    else if (pid == 0)
    {
        while (1)
        {
            printf("This is child print, pid: %d\n", getpid());
            sleep(1);
            cnt++;
            if (cnt == 3)
            {
                exit(0);
            }  
        }   
    }
    
    return 0;   
}

编译运行:

这里的pid 3209就的一个僵尸进程(Z+)。

引入wait函数:

/* Wait for a child to die.  When one does, put its status in *STAT_LOC
   and return its process ID.  For errors, return (pid_t) -1.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern __pid_t wait (int *__stat_loc);

浅翻译一下:(父进程)等待一个子进程的结束。当有一个结束后,把他的状态放到*STAT_LOC里,同时返回他的进程标识符。

对于这个函数的调用会等待子进程的结束,此时父进程是出于阻塞状态的。

如果一个子进程结束,正等待的父进程会获取其终止状态,则取得改子进程的终止状态立即返回。

如果没有子进程,那么函数就会立刻出错返回。

先不关心形参:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int cnt = 0;
    
    pid = fork();

    if (pid > 0)
    {
        wait(NULL);
        while(1)
        {
            printf("This is parent print, pid: %d\n", getpid());
            sleep(1);
        }
    }
    else if (pid == 0)
    {
        while (1)
        {
            printf("This is child print, pid: %d\n", getpid());
            printf("cnt: %d\n", cnt);
            sleep(1);
            cnt++;
            if (cnt == 3)
            {
                exit(0);
            }        
        }    
    }
 
    return 0;   
}

编译运行:

可以看到,使用wait函数之后,结束的子进程就们没有再变成僵尸进程了。 

考虑形参和返回值:

返回值很好解决,但是这个形参有一些需要注意的问题。直接用一个整型指针承接返回状态后,把它输入出现了错误的结果:exit(3) 之后的打印值却是768。其原因是需要用宏WEXITSTATUS解析。

/* If WIFEXITED(STATUS), the low-order 8 bits of the status.  */
#define	__WEXITSTATUS(status)	(((status) & 0xff00) >> 8)

 demo6.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    pid_t pid_exit;
    int cnt = 0;
    int status = 10;
    
    pid = fork();

    if (pid > 0)
    {
        pid_exit = wait(&status);
        printf("child(%d) quit, its status:%d\n", pid_exit, WEXITSTATUS(status));
        while(1)
        {
            printf("This is parent print, pid: %d\n", getpid());
            sleep(1);
        }
    }
    else if (pid == 0)
    {
        while (1)
        {
            printf("This is child print, pid: %d\n", getpid());
            printf("cnt: %d\n", cnt);
            sleep(1);
            cnt++;
            if (cnt == 3)
            {
                exit(3);
            }           
        }    
    }
    
    return 0;   
}

 

关于waitpid函数:

waitpid函数和wait函数的最重要的区别就是,waitpid函数有一个选项,可以让调用者不阻塞

/* Wait for a child matching PID to die.
   If PID is greater than 0, match any process whose process ID is PID.
   If PID is (pid_t) -1, match any process.
   If PID is (pid_t) 0, match any process with the
   same process group as the current process.
   If PID is less than -1, match any process whose
   process group is the absolute value of PID.
   If the WNOHANG bit is set in OPTIONS, and that child
   is not already dead, return (pid_t) 0.  If successful,
   return PID and store the dead child's status in STAT_LOC.
   Return (pid_t) -1 for errors.  If the WUNTRACED bit is
   set in OPTIONS, return status for stopped children; otherwise don't.

   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern __pid_t waitpid (__pid_t __pid, int *__stat_loc, int __options);

对于waitpid函数中pid参数的作用解释如下:

pid == -1 等待任一子进程。就这一方面而言,waitpid和wait等效

pid > 0 等待其进程ID与pid相等的子进程。

pid == 0 等待其组ID等于调用进程组ID的任一子进程。

pid < -1 等待其组ID等于pid绝对值的任一子进程。

waitpid的option常量

WCONTINUED        若实现支持作业控制,那么由PID指定的任意子进程在暂停后已经继续,但其状态尚未报告,则返回其状态

WNOHANG        若pid指定的子进程并不是立即可用的,则waitpid不阻塞此时返回值为0

WUNTRACED        若某实现支持作业控制,而由pid指定的任意子进程已处于暂停状态,并且其状态自暂停以来还未报告过,则返回其状态WIFSTOPPED宏确定返回值是否对于一个暂停子进程

以上摘自《UNIX环境高级编程》,加粗表示常用。

demo7.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    //pid_t pid_exit;
    int cnt = 0;
    int status = 10;
    
    pid = fork();

    if (pid > 0)
    {
        //pid_exit = wait(&status);
        waitpid(pid, &status, WNOHANG);
        printf("Waitting for child(%d)\n", pid);
        while(1)
        {
            printf("This is parent print, pid: %d\n", getpid());
            sleep(1);
        }
    }
    else if (pid == 0)
    {
        while (1)
        {
            printf("This is child print, pid: %d\n", getpid());
            printf("cnt: %d\n", cnt);
            sleep(1);
            cnt++;
            if (cnt == 3)
            {
                exit(3);
            }           
        }    
    }
    
    return 0;   
}

编译运行:

 值得留意的是,这里子进程变成了僵尸进程。

目前了解的就这么多,日后用到再补充。

孤儿进程

就是父进程再子进程结束之前结束,这时的子进程就变成了孤儿进程。Linux系统为了避免存在过多的孤儿进程,会由init进程(pid = 1)收留孤儿进程,变成孤儿进程的父进程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值