一:守护进程功能的实现
三章二节
(1)拦截掉SIGHUP,那么终端窗口关闭,进程就不会跟着关闭
(2)守护进程,三章七节,一运行就在后台,不会占着终端。
创建守护进程ngx_daemon();
调用ngx_daemon()的时机: worker()子进程创建之前;
ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd | grep -E 'bash|PID|nginx'
(1)一个master,4个worker进程,状态S,表示休眠状态,但没有+,+号表示位于前台进程组,没有+说明我们这几个进程不在前台进程组;
(2)master进程的ppid是1【老祖宗进程init】,其他几个worker进程的父进程都是master;
(3)tt这列都为?,表示他们都脱离了终端,不与具体的终端挂钩了
(4)他们的进程组PGRP都相同;
结论:
1)守护进程如果通过键盘执行可执行文件来启动,那虽然守护进程与具体终端是脱钩的,但是依旧可以往标准错误上输出内容,这个终端对应的屏幕上可以看到输入的内容;
2)但是如果这个nginx守护进程你不是通过终端启动,你可能开机就启动,那么这个nginx守护进程就完全无法往任何屏幕上显示信息了,这个时候,要排错就要靠日志文件了;
二:信号处理函数的进一步完善
(2.1)避免子进程被杀掉时变成僵尸进程
父进程要处理SIGCHILD信号并在信号处理函数中调用waitpid()来解决僵尸进程的问题;
信号处理函数中的代码,要坚持一些书写原则:
a)代码尽可能简单,尽可能快速的执行完毕返回;
b)用一些全局量做一些标记;尽可能不调用函数;
c)不要在信号处理函数中执行太复杂的代码以免阻塞其他信号的到来,甚至阻塞整个程序执行流程;
3. waitpid讲解
一、前言
首先在了解waitpid()函数之前,我们需要先明确以下几个概念。
1.进程状态
从程序员的角度,我们可以认为进程总是处于下面三种状态之一:
(1)运行。进程要么在CPU上执行,要么在等待被执行且最终会被内核调度。
(2)停止。进程的执行被挂起(suspended),且不会被调度。当收到SIGSTOP、SIGTSTP、SIGTTIN或者SIGTTOU信号时,进程就停止,并且保持停止直到他收到一个SIGCONT信号,在这个时刻,进程再次开始运行。
(3)终止。进程永远的停止了。进程会因为三种原因终止:第一、收到一个信号,信号的默认行为是终止进程。第二、从主程序返回。第三、调用exit函数
2.回收子进程
当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除 。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收(reaped)。 当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程,从此时开始,该进程就不存在了 。一个终止了但还未被回收的进程称为僵死进程 (zombie)。如果一个进程在回收他的子进程之前就终止了,那他的子进程被称为孤儿进程。
如果一个父进程终止了,内核会安排 init 进程成为它的孤儿进程的养父。init进程的 PID 为 1, 是在系统启动时由内核创建的 ,它不会终止 ,是所有进程的祖先 。如果父进程没有回收它的僵死子进程就终止了,那么内核会安排 init 进程去回收它们 。不过,长时间运行的程序,比如 shell 或者服务器,总是应该回收它们的僵死子进程 。即使僵死子进程没有运行,它们仍然消耗系统的内存资源。
一个进程可以通过调用 waitpid 函数来等待它的子进程终止或者停止。
二、waitpid()函数详解
pid_t waitpid (pid_t pid, int* statusp, int options);
返回 :如果成功,则为子进程的PID,如果options为WNOHANG,则返回0,如果发生其他错误,则返回-1。
waitpid函数有点复杂 。默认情况下 (当 options=0 时 ),waitpid挂起调用进程的执行,直到它的等待集合 (wait set) 中的一个子进程终止。如果等待集合中的一个进程在刚调用的时刻就已经 终止了,那么 waitpid 就立即返回 。在这两种情况中,waitpid返回导致 waitpid 返回的已终止子进程的PID此时,已终止的子进程已经被回收,内核会从系统中删除掉它的所有痕迹。
1. 判定等待集合的成员
等待集合的成员是由参数 pid 来确定的:
(1)如果Pid>0,那么等待集合就是一个单独的子进程 ,它的进程 ID 等于 pid。
(2)如果Pid=-1,那么等待集合就是由父进程所有的子进程组成的。
2. 修改默认行为
可以通过将 options 设置为常量 WNOHANG、WUNTRACED 和 WCONTINUED
的各种组合来修改默认行为:
WNOHANG: 如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0)。 默认的行为是挂起调用进程,直到有子进程终止 。在等待子进程终止的同时,如果还想做些有用的工作,这个选项会有用。
WUNTRACED: 挂起调用进程的执行,直到等待集合中的一个进程变成已终止或者被停止 。返回的PID 为导致返回的已终止或被停止子进程的 PID。默认的行为是只返回已终止的子进程。当你想要检査已终止和被停止的子进程时,这个选项会有用。
WCONTINUED: 挂起调用进程的执行,直到等待集合中一个正在运行的进程终止或等待集合中一 个被停止的进程收到 SIGCONT 信号重新开始执行。
可以用或运算把这些选项组合起来 。例如:WNOHANG | WUNTRACED: 立即返回,如果等待集合中的子进程都没有被停止或终止,则返回值为0: 如果有一个停止或终止,则返回值为该子进程的 PID。
3. 检查已回收子进程的退出状态
如果statusp参数是非空的,那么waitpid就会在 status 中放上关于导致返回的子进程的状态信息, status是statusp指向的值。 wait.h头文件定义了解释status参数的几个宏:
WIFEXITED(status):如果子进程通过调用 exit 或者一个返回 (return) 正常终止,就返回真。
WEXITSTATUS(status):返回一个正常终止的子进程的退出状态。只有在WIFEXITED()返回为真时,才会定义这个状态。
WIFSIGNALED(status):如果子进程是因为一个未被捕获的信号终止的,那么就返回真。
WTERMSIG(status):返回导致子进程终止的信号的编号 。只有在 WIFSIGNALED()返回为真时,才定义这个状态。
WIFSTOPPED(status):如果引起返回的子进程当前是停止的,那么就返回真。
WSTOPSIG(status):返回引起子进程停止的信号的编号。只有在 WIFSTOPPED()返回为真时,才定义这个状态。
WIFCONTINUED(status):如果子进程收到SIGCONT信号重新启动,则返回真。
4. 错误条件
如果调用进程没有子进程,那么waitpid返回-1, 并且设置 errno为ECHILD。 如果waitpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR。
注意,程序不会按照特定的顺序回收子进程。子进程回收的顺序是这台特定的计算机系统的属性 。在另一个系统上,甚至在同一个系统上再执行一次,两个子进程都可能以相反的顺序被回收。这是非确定性行为的一个示例,这种非确定性行为使得对并发进行推理非常困难。两种可能的 结果都同样是正确的,作为一个程序员,我们绝不可以假设总是会出现某一个结果,无论多么不可能出现另一个结果 。唯一正确的假设是每一个可能的结果都同样可能出现。
————————————————
版权声明:本文为CSDN博主「西余安轩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45227610/article/details/122841209