Unix网络编程 之 进程与信号(2)

在上一节文章中,我们说明了怎么去创建一个子进程,在这一节中,我们是时候去说明怎么去回收一个子进程了,在回收子进程的过程中,我们就可以顺便引出信号方面的知识了。

回收子进程
当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除,相反,进程被保持在一种已终止的状态,直到被它的父进程回收(reap)。当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程。从此时开始,该进程就存在了,一个终止了但还未被回收的进程称为僵死进程(zombie)
小故事:
在民间传说中,僵尸是活着的尸体,一种半生半死的实体,僵死进程已经终止了,而内核仍保留着它的某些状态知道父进程回收它位置,从这个意义上是类似的。

如果父进程没有回收它的僵死进程就终止了,那么内核就会安排init进程来回收它们。inti进程的PID为1,并且是在系统初始化时由内核创建的。长时间运行的程序,比如外壳或服务器,总是应该回收它们的僵死子进程。即使僵死进程没有运行,它们仍然消耗系统的存储器资源。
一个进程可以通过调用waitpid函数来等待它的子进程终止或者停止:

#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid, int *status,int options);
/*返回:如果成功,则返回子进程的PID,如果WNOHANG,则为0,如果其他错误,则为-1。*/

默认地(当option=0时),waitpid挂起调用进程的执行,直到它的等待集合中的一个子进程终止。waitpid返回导致返回的已终止子进程的PID,并且将这个已终止的子进程从系统中去除。
当参数pid>0,那么等待集合就是一个单独的子进程,它的进程ID等于pid;当pid=-1,那么等待集合就是由父进程所有子进程组成的。
错误条件:如果调用进程没有子进程,那么waitpid返回-1,并且设置errno为ECHILD。如果waitpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR。

使用waitpid的示例
下面展示一个c语言的示例,使用waitpid函数不按照特定的顺序回收僵死子进程。可能因为并发执行的原因,所以这是一个非确定性的(nondeterministic)行为的示例,当然可以利用数组解决这种不确定性,详细例子请参考p496.

#include "csapp.h"
#define N 2
int main(){
    int status,i;
    pid_t pid;
    for(i=0;i<N;i++){
        if((pid=Fork())==0) exit(100+i);
    }
    while((pid=waitpid(-1,&status,0))>0){
         if(WIFEXITED(status))
            printf("child %d terminated normally with exit status=%d\n", pid, WEXITSTATUS(status));
         else
            printf("child %d terminated abnormally\n",pid);   
    }
    if(error!=ECHILD)
         unix_error("waitpid error");
    exit(0);     
}

如果status参数是非空的,那么waitpid就会在status参数中放上关于导致返回的子进程的状态信息,wait.h头文件定义解释status参数的几个宏:现在暂时列出几个,后面用到再继续列出
WIFEXITED(status):如果子进程通过调用exit或者一个返回(return)正常终止,就返回真。
WEXITSTATUS(status):返回一个正常终止的子进程的退出状态。只有在WIFEXITED返回为真时,才会定义这个状态。

让进程休眠
sleep函数将一个进程挂起一段指定的时间。

#include <unistd.h>
unsigned int sleep(unsigned int secs);

如果请求的时间量已经到了,sleep返回0,否则返回还剩下的要休眠的秒数。pausa函数让调用函数休眠,直到该进程收到一个信号。

#include <unistd.h>
int pause(void); /*总是返回-1*/

加载并运行程序

#include<unistd.h>
int execve(const char *filename,const char *argv[],const char *envp[]);
/*execve函数加载并运行可执行**目标文件filename**,且带**参数列表argv**,和**环境变量列表envp**  */

在execve加载了filename之后,它调用启动代码,启动代码设置栈,并将控制传递给新程序的main函数,该主函数有如下形式的原型:

int main(int argc,char **argv, char **envp);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值