回收内核空间资源 wait函数 waitpid函数

摘要:本文主要讲述内核空间资源的收回,介绍wait和waitpid函数的基本使用方法,以及它们之间的差异.

回收内核空间资源 wait和waitpid函数

    进程退出时释放了用户空间的资源,但是进程PCB并没有释放,这一工作显然不是自己完成,而是由当前进程的父进程完成的.
    当一个进程正常退出或异常退出时,内核就向其父进程发送SIGCHLD信号.因为子进程终止是一个异步事件,所以这种信号也是内核向父进程发的异步通知.父进程可以选择忽略该信号(如果父进程设置了SA_NOCLDWAIT标志位(SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号),或者提供一个该信号发生时即被调用执行的函数。父进程可以显示地调用wait()函数和waitpid()函数来完成.

1. wait()和waitpid()函数等待子进程结束

函数定义:
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *status);
    pid_t waitpid(pid_t pid, int *status, int options);
函数说明:
    如果等待到任意一个子进程结束,将返回当前结束的子进程PID,同时将子进程退出时的状态存储在“_stat_loc"变量中.如果出错返回-1,错误原因存储在errno中.
    调用wait()函数的父进程,如果其所有的子进程都还在运行,则父进程会阻塞,等待任意一个子进程结束,回收该子进程的内核进程资源.如果父进程没有任何子进程,则会出错.
   如果进程由于接收到SIGCHLD信号而调用wait(),则可期望wait()会立即返回.但是如果在任意时刻调用wait(),则进程可能会阻塞.
参数status:
    参数status是一个整型指针.如果status不是空指针,则终止进程的终止状态就放在它所指向的内存单元内.如果不关心终止进程的状态,则可将参数指定为空指针.

2. wait()和waitpid()函数的区别

(1)在一个子进程终止前,wait()使其调用者阻塞,而waitpid()有一个选项,可使调用者不阻塞.
(2)waitpid()并不等待其在调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程(下文会详细介绍).
例子1:演示wait()函数的基本使用方式
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/errno.h>

int main()
{
        pid_t pid,wait_pid;
        int status;
        pid = fork();    //调用fork函数
        if(pid==-1)
        {
                printf("fork errno:%m\n");
        }
        if(pid==0)
        {
                printf("my pid is :%d\n",getpid());
                sleep(5);
                exit(EXIT_SUCCESS);   //正常退出
        }
        else
        {
                wait_pid = wait(&status);   //等待子进程结束
                if(WIFEXITED(status))       //调用WIFEXITED宏
                {
                        printf("wait on pid:%d,normal exit ,return value is:%4x\n",wait_pid,WEXITSTATUS(status));
                }
                else if(WIFSIGNALED(status))
                {
                        printf("wait on pid:%d,recive signal,return value is:%4x\n",wait_pid,WIFSIGNALED(status));
                }
        }
        return 0;
}

程序正常运行退出,输出:

:my pid is :2573
:wait on pid:2573,normal exit ,return value is:   0

如果在程序运行后,向子进程发送一个信号,则输出:
:my pid is :2573
:wait on pid:2573,normal exit ,return value is:   1

    在程序中还使用了WIFEXITED和WIFSIGNALED宏,其中宏WIFEXITED是用来判断进程是否是正常退出,如果是,此宏值为1.对于这种情况,可执行WEXITSTATUS(status),取子进程传送给exit,_exit或_Exit参数的低8位.
    而宏WIFSIGNALED是用来判断进程是否是因为收到信号后而退出的,如果是,此宏值为1. 对于这种情况,可执行WTERMSIG(status),取使子进程终止的信号编号.
    另外还有两个宏与wait和waitpid所返回的终止状态相关的.WIFSTOPPED和WIFCONTINUED.

3. waitpid()函数等待指定的子进程

    如果一个进程有几个子进程,那么只要有一个子进程结束终止,wait()函数就会返回.但是现在我们想等待某一个子进程而已,其他子进程结束我们不关心,该怎么办?用户可以使用waitpid()函数来等待指定子进程结束.
定义:pid_t waitpid(pid_t pid, int *status, int options);
   其中,第一个参数为进程PID值,对该值有一下的解析:
(1)pid==-1 等待任一子进程结束,此时waitpid()函数等价于wait()函数;
(2)pid>0   等待其进程ID与pid相等的子进程;
(3)pid==0  等待其组ID等于调用进程组ID的任一子进程;
(4)pid<-1  等待其组ID等于pid绝对值得任一子进程.

    第二个参数为调用它的函数中某个变量地址,如果执行成功,则用来存储结束进程的结束状态.
    第三个参数为等待选项,可以设置为0,亦可为WNOHANG(不阻塞等待)和WUNTRACED(报告状态信息).如果options设置为WNOHANG,而此时没有子进程退出,将立即返回0,不会像wait()那样永远等待下去.否则返回子进程PID,并在参数STAT_LOC中获取子进程的状态.

这是两个常数,可以用"|"运算符把它们连接起来使用,比如:
ret = waitpid(-1,NULL, WNOHANG|WUNTRACED);
如果我们不想使用它们,也可以把options设为0,比如:
ret = waitpid(-1, NULL, 0);

waitpid的返回值

由于waitpid函数参数较多,返回值也是根据参数的情况而定,综合分析一共有3种情况:
(1)如果执行成功正常返回,waitpid()返回子进程的PID;
(2)如果参数options设置了选项WNOHANG,而waitpid在执行时没有已退出的子进程,则返回0;
(3)如果调用出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;当指定的进程或进程组不存在,或者参数pid指定的进程不是调用进程的子进程则出错.


4. waipid()函数提供了wait()函数没有的三个功能

(1)waitpid可等待一个特定的进程,而wait则返回任一终止子进程的状态.
(2)waitpid提供了一个wait的非阻塞版本.又是用户希望取得子进程的状态,但不想阻塞.
(3)waitpid支持作业控制.

例子2:等待指定的子进程
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t chlid_pid, wait_pid;
    chlid_pid=fork();
    if(chlid_pid<0)  
        printf("Error occured on forking.\n");
    else if(chlid_pid==0)
    {  
        sleep(5);
        exit(0);
    }
    do
    {
        wait_pid=waitpid(chlid_pid, NULL, WNOHANG);   //不阻塞,直接返回
        if(wait_pid==0)
        {   
            printf("No child exited\n");
            sleep(1);
        }
    }while(wait_pid==0);    
    
    if(wait_pid==chlid_pid)
        printf("successfully release child %d\n", wait_pid);
    else
        printf("some error occured\n");
    return 0;
}

输出:

:No child exited
:No child exited
:No child exited
:No child exited
:No child exited
:successfully release child 2538

    每隔一秒钟,父进程就检查一次当前是否有子进程退出.在前5秒钟,由于没有子进程退出,父进程调用waitpid(chlid_pid, NULL, WNOHANG)直接返回.子进程退出后,便可以处理其后事.

笔者:个人能力有限,只是学习参考...读者若发现文中错误,敬请提出.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙筑高台,静下心来,慢慢地沉淀---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章