linux系统编程—进程—进程的退出、孤儿进程、僵尸进程、wait和waitpid函数

目录

进程的退出

正常退出

异常退出

僵尸进程和孤儿进程

两者简介

问题及其危害

wait和waitpid函数

函数简介

函数头文件、参数以及返回值

wait函数与waitpid函数区别

wait函数实战

孤儿进程代码实现

编程实现僵尸进程


进程的退出

Linux进程的退出分为正常退出和异常退出两种:

正常退出

a.在main()函数中执行return

b.调用exit()函数,标准c库

c.调用_exit()函数或者_Exit(),属于系统调用

d.进程最后一个线程返回

e.最后一个线程调用pthread_exit

异常退出

a.调用abort函数

b.进程收到某个信号,而该信号使程序终止。如ctrl+c

c.最后一个线程对取消(cancellation)请求做出响应

不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。对上述任意一种终止情形,我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit、_exit和_Exit),实现这一点的方法是,将其退出状态(exit status)作为参数传送给函数。在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态(termination status)。在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态。

僵尸进程和孤儿进程

两者简介

在linux中,正常情况下,子进程是通过父进程创建的,子进程再创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。 当一个子进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

简单来说就是子先走父不收尸就是僵尸,父早于子先走就是孤儿。我们在后面wait函数用代码深入了解僵尸进程和孤儿进程

问题及其危害

在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 但这样就导致了问题,如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害

孤儿进程是没有父进程的进程每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。因此孤儿进程并不会有什么危害。

附:任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。

wait和waitpid函数

函数简介

wait函数一般用在父进程中等待回收子进程的资源,而防止僵尸进程的产生。

当父进程调用wait()函数时:

a. 如果其所有子进程都还在运行,则阻塞;

b.只要有一个进程终止,wait就会返回。也就是说只要wait接收到一个SIGCHLD信号,wait()就会返回。对于两个或多个子进程的情况,需要多次调用

c.如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回;

d.如果它没有任何子进程,则立即出错返回;

当父进程调用waitpid时:

waitpid()的作用和wait()一样,但它并不一定要等待第一个终止的子进程,它还有若干选项,如可提供一个非阻塞版本的wait()功能等。

函数头文件、参数以及返回值

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);

pid_t waitpid(pid_t pid, int *status, int options);

wait()与fork()配套出现,如果在fork()之前调用wait(), wait返回-1,正常情况下,应返回销毁的子进程的pid。参数stauts用来保存被收集进程退出时的状态,它是一个指向int类型的指针,如果我们对这个子进程如何结束的不在意,只想把这个僵尸进程消灭掉,就把这个参数置为NULL。如果status的值不是NULL,wait把子进程的退出状态取出并存入其中,这是一个整数值(int)它指出了子进程是正常退出还是非正常结束,以及正常结束的返回值,或被哪个信号结束等信息。使用macro来获取这些信息。

waitpid函数参数:

pid

pid==-1 等待任何一个子进程,此时waitpid的作用与wait相同

pid >0   等待进程ID与pid值相同的子进程

pid==0   等待与调用者进程组ID相同的任意子进程

pid<-1   等待进程组ID与pid绝对值相等的任意子进程

option

WNOHANG   waitpid将不阻塞如果指定的pid并未结束

WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

wait函数与waitpid函数区别

从本质上讲,wait和waitpid的作用是完全相同的,但是waitpid多了两个参数,从而使我们的编程能更加的灵活。
waitpid有wait没有的三个功能:
(1)waitpid能等待一个特定的子进程,而wait只能等待任意的子进程,
(2)系统一旦调用wait函数就会阻塞父进程来等待,直到等到子进程的退出才停止阻塞,而waitpid提供了一种非阻塞方式的等待,也就是第三个参数,当第三个参数设置为WNOHANG,当子进程没有结束,直接返回0,不等待
(3)waitpid支持作业控制,提供用于检查wait和waitpid返回状态的宏这两个函数返回的子进程的状态都保存在status指针中

wait函数实战

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

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

        if(pid > 0)
        {
                wait(NULL);//等待子进程结束
                while(1)
                {
                        printf("cnt = %d\n",cnt);
                        printf("this is father print = %d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1)
                {
                        printf("this is child print,child pid = %d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 5)
                        {
                                exit(0);//结束子进程
                        }
                }
        }

        return 0;
}

编译运行可以看到调用wait后,父进程等待子进程推出之后执行

 修改部分代码,给wait传参,用于接收进程退出时的状态,并打印出值

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

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

        pid = fork();


        if(pid > 0)
        {
                wait(&status);
                printf("child quit,child status = %d\n",WEXITSTATUS(status));
                while(1)
                {
                        printf("cnt = %d\n",cnt);
                        printf("this is father print = %d\n",getpid());
                        sleep(1);
                }
        }
        else if(pid == 0)
        {
                while(1)
                {
                        printf("this is child print,child pid = %d\n",getpid());
                        sleep(1);
                        cnt++;
                        if(cnt == 5)
                        {
                                exit(3);
                        }
                }
        }

        return 0;
}

编译运行

孤儿进程代码实现

让父进程打印父进程pid之后退出,子进程继续执行

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

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

        pid = fork();

        if(pid > 0)
        {
                        printf("this is father print, pid = %d\n",getpid());
        }
        else if(pid == 0)
        {
                while(1)
                {
                        printf("this is child print,child pid = %d,my father pid = %d\n",getpid(),getppid());
                        sleep(1);
                        cnt++;
                        if(cnt == 6)
                        {
                                exit(3);
                        }
                }
        }

        return 0;
}

编译运行发现,在父进程退出之前,子进程getppid获取父进程pid与父进程一致,在父进程退出后子进程getppid获取父进程pid为init的1,这时孤儿进程被init进程收留

编程实现僵尸进程

让子进程先退出,父进程不处理

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

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

        if(pid > 0)
        {
                sleep(30);//30秒的时间足够通过子进程pid搜索子进程状态
        }
        else if(pid == 0)
        {
                printf("Child process exit!!!\n");
                printf("this is father print = %d\n",getpid());//打印子进程pid
                sleep(2);
        }

        return 0;
}

编译运行后 ps -aux | grep xxxx搜索子进程状态,发现是僵尸进程(Z)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值