wait函数

#include <sys/types.h>

#include <sys/wait.h>

 

pid_t wait(int *status);

 

僵尸进程。进程结束后放弃了几乎所有的内存空间,没有任何可以执行的代码,也不能被调度,仅仅在进程列表中保留着一个位置,记载着该进程的退出状态等信息供其他进程收集,除此之外僵尸进程不再占有任何内存空间。

当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态,此时是一个僵尸进程。(正常终止→退出值;异常终止→终止信号)

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。

在shell终端执行的命令和程序,都是shell通过fork产生的子进程来完成的,因此在命令和程序结束后,shell终端进程也可以获取子进程的结束状态,通过特殊命令$?查看,如果为0,表示正常结束,如果为非0值,表示出现了错误。Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:1.阻塞等待子进程退出(父进程调用该函数后,如果子进程没死,则父进程阻塞并等待子进程结束);2. 获取子进程结束状态(退出原因);3.回收子进程残留资源 (回收子进程结束后残留在内核中的PCB资源),然后彻底清除掉这个进程。

进程的一生。随着fork的成功执行,一个新的子进程产生,但此时它还只是父进程的克隆。然后随着exec函数族,新进程脱胎换骨,开始独立执行一个全新的程序,并完全替代了原有的父进程。进程结束,可以是自杀,也可以是他杀。自杀:遇到main函数的最后一个“}”、在main函数中使用return或者调用exit_exit;他杀:被其它进程通过另外一种方式杀掉。进程自杀(属于正常死亡),即使程序出错,但是进程自身结束了自己,可以通过return和exit赋给进程结束后一个值,通过wait或waitpid函数来获取该值,从而知道该进程自杀的原因,是否出错(一般0代表正常,非0代表出错),当然可以返回任何值,由wait函数可以获取到。进程如果属于他杀,则为异常结束,在linux操作系统中,所有的异常终止都是由于某一个信号导致的(通过kill –l参数可以查看所有的信号及信号编号),如在程序中给一个常量赋值,会发生段错误,此时进程会收到段错误相应的信号,从而被终止;除数为0时,进程也会收到一个信号(浮点数例外),从而被终止。而终止进程的信号会记录在终止信息中,供父进程调用wait函数获取。进程死掉后,会留下一具僵尸,wait和waitpid则去收集信息,清理尸体。

exit和_exit这两个函数(都是系统调用)都有一个整型形参,用于传递进程结束时的状态(正常结束还是出错结束)。一般,0表示正常结束,非0值(如1)表示出现了错误,非正常结束。可以利用wait系统调用接收子进程的返回值,从而针对不同的情况进行不同的处理。

 

pid_t wait(int *status);

status为传出参数,用于获取子进程结束时的状态(原因)。函数执行成功,则返回清理掉的子进程ID,如果失败则返回-1。进程一旦调用了wait,就立即阻塞自己,由wait自动分析该进程的某个子进程是否已经退出,如果找到了一个已经结束的子进程,则收集其信息,并将其清除掉并返回该子进程ID;如果没有找到这样的一个子进程,则会一直阻塞到那里,直到有一个出现为止。如果父进程没有子进程,则直接返回-1,表示出错。

如果对子进程如何死掉的不关心(不需要收集其残留的信息),只想把僵尸进程消灭掉,可以将函数形参设置为NULL,即pid=wait(NULL);

 

可使用wait函数传出参数status(整型值)来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:

 1. WIFEXITED(status) 为非0   → 表示进程正常结束

WEXITSTATUS(status) 如上宏为真(进程正常结束),使用此宏 → 获取进程退出状态 (exit/_exit/return的参数)

 2. WIFSIGNALED(status) 为非0 → 表示进程异常终止

       WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。可以通过kill –l命令查看编号的详细含义。

3. WIFSTOPPED(status) 为非0 → 表示进程处于暂停状态(进程没有终止,只是暂停运行,如挂起或阻塞)。

WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。

WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

 

//代码举例

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

int main(void)
{
    pid_t pid, wpid;
    int status;
    pid = fork( );

    if(pid == 0)
    {
        printf("---child, my parent= %d, going to sleep 10s\n",getppid( ));
        sleep(10);
        printf("-------------child die--------------\n");
        exit(77); //return 77;  _exit(77);  //子进程正常结束,置其结束状态为77
    }else if(pid > 0)
    {
        wpid = wait(&status);  //父进程阻塞等待子进程结束
        if(wpid==-1)
        {
            perror("wait");
            exit(1); //置结束状态为1
        }

        if( WIFEXITED(status) )  //如果子进程正常结束
        {
            printf( "The child process exit with %d\n",WEXITSTATUS(status));
        }else if( WIFSIGNALED(status) )   //如果子进程异常结束
        {
            printf( "The child process was killed by %dth signal\n",WTERMSIG(status));
        }

         while (1)
        {
            printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);
            sleep(1);
        }   //父进程一直运行

    }else
    {
        perror("fork");
        exit(1);
    }

    return 0;
}

[root@localhost wait]# ./wait_test

---child, my parent= 30770, going to sleep 10s

-------------child die--------------

The child process exit with 77   //获取了结束状态为77

I am parent, pid = 30770, myson = 30771

I am parent, pid = 30770, myson = 30771

I am parent, pid = 30770, myson = 30771

[root@localhost wait]#ps aux

root      30770  0.0  0.0   4164   356 pts/0    S+   18:11   0:00 ./wait_test

root      30791  0.0  0.0 123360  1380 pts/2    R+   18:12   0:00 ps aux

可见,只有父进程在一直运行,子进程不存在了,也不存在僵尸进程,子进程痕迹被完全清除。

 

在子进程运行期间,利用kill -9 将其杀掉,则为异常结束:

[root@localhost wait]#ps aux

[root@localhost wait]# kill 30830

[root@localhost wait]# ./wait_test

---child, my parent= 30829, going to sleep 60s

The child process was killed by 15th signal

I am parent, pid = 30829, myson = 30830

I am parent, pid = 30829, myson = 30830

I am parent, pid = 30829, myson = 30830

I am parent, pid = 30829, myson = 30830

利用kill -l 命令可以查看编号为15的信号为     15) SIGTERM

 

总结,进程正常结束,父进程利用wait函数回收其结束状态,状态值通过return、exit和_exit返回(一般,如果程序正常则为0,出错设非0,但不是必须的),如果进程结束没有指明其值,如main函数中没有return语句且正常结束,则会有默认值(正常0,出错非0)。注意,returnexit_exit返回的值必须不超过128可通过wait函数获取。如果函数异常结束,wait函数回收其终止原因(信号编号)。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值