• 僵尸进程与孤儿进程


    1、前言

      前几天看《TCP&IP网络编程》时候看到了相关知识,为了加深印象,在捋一遍。

    2、基础概念

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

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

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

    调用fork函数产生子进程的终止方式:

    1)传递参数并调用exit函数;

    2)main函数中执行return语句并返回值。

      向exit函数传递的参数值和main函数的return语句返回的值都会传递给操作系统。而操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程,处在这种状态下的进程就是僵尸进程。也就是说,将子进程变成僵尸进程的正是操作系统。既然如此,此僵尸进程何时被销毁呢?

      “应该向创建子进程的父进程传递子进程的exit参数值或者return语句的返回值。”

      如何向父进程传递这些值呢?操作系统不会主动把这些值传递给父进程。只有父进程主动发起请求(函数调用)时,操作系统才会传递该值。换言之,如果父进程未主动要求获得子进程的结束状态值,操作系统将一直保存,并让子进程长时间处于僵尸进程状态。也就是说,父母要负责收回自己的孩子。

    3、销毁僵尸进程

      一般,为了防止产生僵尸进程,在fork子进程之后我们都要wait它们;同时,当子进程退出的时候,内核都会给父进程一个SIGCHLD信号,所以我们可以建立一个捕获SIGCHLD信号的信号处理函数,在函数体中调用wait或waitpid,就可以清理退出的子进程以达到防止僵尸进程的目的。

    相关函数介绍:

    1)wait函数

                

      调用此函数时如果已有子进程终止,那么子进程终止时传递的返回值(exit函数的参数值、main函数的return返回值)将保存到该函数的参数所指内存空间。但函数参数中还包含其他信息,因此需要通过下列宏进行分离。

    • WIFEXITED子进程正常终止时返回“真”(true)
    • WEXITSTATUS返回子进程的返回值

      也就是说向wait函数传递变量status的地址时,调用wait函数后应编写如下代码:

    if(WIFEXITED(status))

    {

      puts("normal termination!");

      printf("child pass num:%d ",WEXITSTATUS(status));

    }

    调用wait函数时,如果没有已终止的子进程,那么程序将阻塞直到有子进程终止,因此需谨慎调用该函数。

    2)waitpid函数

      wait函数会引起程序阻塞,还可以考虑调用waitpid函数。这是防止僵尸进程的第二种方法,也是防止阻塞的方法。

              

     3)signal函数

            

      函数名:signal

      参数:int signo,void(* func)(int)

      返回类型:参数为int型,返回void型函数指针

      调用上述函数时,第一个参数为特殊情况信息,第二个参数为特殊情况下将要调用的函数的地址值(指针)。发生第一个参数代表的情况时,调用第二个参数所指的函数。下面是可以在signal函数中注册的部分特殊情况和对应的函数。

    • SIGALRM:已到通过调用alarm函数注册的时间
    • SIGINT:输入Ctrl+C
    • SIGCHLD:子进程终止

    eg:signal(SIGCHLD, mychild)

      signal(SIGALRM, alarm)

      signal(SIGINT, keycontrol) 

     4)sigaction函数

    signal函数在Unix系列的不同操作系统中可能存在区别,但sigaction完全不会。可以代替signal。

          

      此结构体的sa_handler成员保存信号处理函数的指针值,sa_mask和sa_flags的值全初始化为0即可。这两个成员用于指定信号相关的选项和特性。

    eg1:

      struct sigaction act;

      act.sa_handler=timeout;

      sigemptyset(&act.sa_mask);

      act.sa_flags=0;

      sigaction(SIGALRM,&act,0);

    eg2 利用信号处理技术消灭僵尸进程(子进程终止时将产生SIGCHLD信号)

      struct sigaction act; 

      act.sa_handler=child_proc;

      sigemptyset(&act.sa_mask);

      act.sa_flags=0;

      sigaction(SIGCHLD,&act,0);

    参考:https://www.cnblogs.com/Anker/p/3271773.html

      

  • 相关阅读:
    monorepo使用教程
    pnpm教程
    Vite 从入门到精通,玩转新时代前端构建法则
    browserslist 目标浏览器配置表
    VS项目属性的一些配置项的总结
    FastAPI入门教程(持续更新中)
    FastAPI 学习之路(六十)打造系统的日志输出
    FastAPI 学习之路(六十一)使用mysql数据库替换sqlite数据库
    FastAPI 学习之路(五十九)封装统一的json返回处理工具
    FastAPI 学习之路(五十八)对之前的代码进行优化
  • 原文地址:https://www.cnblogs.com/xingguang1130/p/12701726.html
Copyright © 2020-2023  润新知