linux操作系统之进程 子进程回收

回收子进程主要有两个函数:wait(),waitpid()。
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

wait函数
父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:

① (父进程)阻塞等待子进程退出
② 回收子进程残留资源
③ 获取子进程结束状态(退出原因)。

pid_t wait(int *status); 成功:清理掉的子进程ID;失败:-1 (没有子进程)

当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)
可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:
1. WIFEXITED(status) 为非0 → 进程正常结束
WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)
2. WIFSIGNALED(status) 为非0 → 进程异常终止
WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
*3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态
WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

案例一

#include
#include
#include
int main()
{
    pid_t pid;
    int wpid;
    pid =  fork();
    if(pid == -1)
    {
            perror("fork:");
            exit(1);
    }
    if(pid == 0)
    {
            printf("i am child .pid = %ld,ppid = %ld\n",getpid(),getppid());
            sleep(10);
            printf("child did.\n");
    }
    else if(pid > 0)
    {
            sleep(1);
            wait(&wpid);//加了wait函数后,父进程阻塞在这里,不再继续执行,等待子进程执行完,然后回收子进程,释放内核资源,获取子进程退出状态。**当子进程接收,父进程继续执行。**
            if(wpid==-1)
            {
                perror("wait error:");
                exit(1);
            }
            while(1)
            {
                   printf("i am parent. pid = %d,myson= %ld\n",getpid(),pid);
                    sleep(1);
            }
    }
    return 0;
}

waitpid函数
作用同wait,但可指定pid进程清理,可以不阻塞。
pid_t waitpid(pid_t pid, int *status, in options); 成功:返回清理掉的子进程ID;失败:-1(无子进程)
特殊参数和返回情况:

参数pid:

0 回收指定ID的子进程
-1 回收任意子进程(相当于wait)
0 回收和当前调用waitpid一个组的所有子进程
< -1 回收指定进程组内的任意子进程
返回0:当参3为WNOHANG,且子进程正在运行,返回0;

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。
案例2 :

#include
#include
#include

int main(int argc,char *argv[])
{
    int n = 5,i;
    pid_t p,wpid=0;

    if(argc == 2)
    {
            n = atoi(argv[1]);
    }
    for(i=0;i0)
            {
                    wpid=waitpid(-1,NULL,WNOHANG);
                    /*waitpid函数当第三个参数为WNOHONG时,如果子进程未结束则返回0,
                    子进程结束调用成功返回子进程的pid.*/
                    if(wpid>0)//waipid调用成功
                       n--;
            }
            printf("wait finish.\n");
    }
    else
    {
            sleep(i);
            printf("i am %dth child.pid = %d\n",i+1,getpid());

    }
return 0;
}

练习:父进程fork 3 个子进程,三个子进程一个调用ps命令, 一个调用自定义程序1(正常),一个调用自定义程序2(会出段错误)。父进程使用waitpid对其子进程进行回收。

#include
#include
#include

int main()
{
    pid_t pid,wpid=0;
    int n=3,i=0;
    int status;

    for(i=0;i0)
            {
                    wpid = waitpid(-1,&status,WNOHANG);
                    if(wpid>0)
                    {
                            if(WIFEXITED(status))
                                    printf("child exit nomally:%d\n",WEXITSTATUS(status));
                            else if(WIFSIGNALED(status))
                                    printf("child exit unexpected:%d\n",WTERMSIG(status));

                            n--;
                    }
            }
    }
    else
    {
            sleep(i);
            if(i == 0)
            {
                    printf("i am %dth child\n",i+1);
                    execl("ps","ps","-aux",NULL);
            }
            else if(i == 1)
            {
                    printf("i am %dth child\n",i+1);
                    execl("myapp","myapp",NULL);
            }
            else if(i == 2)
            {
                    printf("i am %dth child\n",i+1);
                    execl("myapp2","myapp2",NULL);
            }

    }

    return 0;
}

你可能感兴趣的:(linux操作系统之进程 子进程回收)