除了nohup,你还可以这么搞

oscube

还记得刚开发后端服务的时候,啪啪啪一顿猛敲,接口测试通过,程序启动上线。对着老大和前端说:我的接口部署好了,你们可以测试了。然后潇洒的收包、闪人,心中默念一句:你们这群低效的加班狗。前脚还没跨出大门,就听到“xxx,你的接口挂了”。

图片

回到座位检查了下,果然无法调用,登录服务器查看,进程都没了,这是咋回事?隔壁的老炮看了下我的操作记录,然后在启动命令上加了“nohup &”,一切ok了。

图片

这是我第一次接触nohup命令,后来才知道这个命令会使程序忽略 HUP 信号,保证程序能够正常进行。在终端退出时,会向它所关联的进程所发SIGHUP信号,进程收到这个信号后就会中止运行。所以如果你不希望进程被这个信号干掉的话,就可以忽略这个信号。而 nohup 命令做的就是这个事情。但是原理是什么呢?

图片

这里我们再来了解两个概念:进程组和会话。

进程组

进程组就是一系列相互关联的进程集合,系统中的每一个进程也必须从属于某一个进程组;每个进程组中都会有一个唯一的 ID(process group id),简称 PGID;PGID 一般等同于进程组的创建进程的 Process ID,而这个进进程一般也会被称为进程组先导(process group leader),同一进程组中除了进程组先导外的其他进程都是其子进程;进程组的存在,方便了系统对多个相关进程执行某些统一的操作,例如,我们可以一次性发送一个信号量给同一进程组中的所有进程。

会话

会话(session)是一个若干进程组的集合,同样的,系统中每一个进程组也都必须从属于某一个会话;一个会话只拥有最多一个控制终端(也可以没有),该终端为会话中所有进程组中的进程所共用。一个会话中前台进程组只会有一个,只有其中的进程才可以和控制终端进行交互;除了前台进程组外的进程组,都是后台进程组;和进程组先导类似,会话中也有会话先导(session leader)的概念,用来表示建立起到控制终端连接的进程。在拥有控制终端的会话中,session leader 也被称为控制进程(controlling process),一般来说控制进程也就是登入系统的 shell 进程(login shell);

图片

执行睡眠后台进程sleep 50 & 之后,通过ps命令查看该进程及shell信息如上图:

•PPID 指父进程 id•PID 指进程 id•PGID 指进程组 id•SID 指会话 id•TTY 指会话的控制终端设备•COMMAND 指进程所执行的命令•TPGID 指前台进程组的 PGID

SIGHUP信号的触发及默认处理

  在对会话的概念有所了解之后,我们现在开始正式介绍一下SIGHUP信号,SIGHUP 信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联. 系统对SIGHUP信号的默认处理是终止收到该信号的进程。所以若程序中没有捕捉该信号,当收到该信号时,进程就会退出。

SIGHUP会在以下3种情况下被发送给相应的进程:

1.终端关闭时,该信号被发送到session首进程以及作为job提交的进程(即用 & 符号提交的进程);2.session首进程退出时,该信号被发送到该session中的前台进程组中的每一个进程;3.若父进程退出导致进程组成为孤儿进程组,且该进程组中有进程处于停止状态(收到SIGSTOP或SIGTSTP信号),该信号会被发送到该进程组中的每一个进程。

原因搞明白了,那除了nohup,还有什么方式可以避免HUP信号导致程序退出呢?答案是c库函数sginal()

void (*signal(int sig, void (*func)(int)))(int)

其中

•sig -- 在信号处理程序中作为变量使用的信号码。文章中提到的SIGHUP就是信号码•func -- 收到信号后的处理函数

我们调用该函数设置一个函数来处理信号,只打印不退出就可以了,实验代码如下:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handler(int a) {
        printf("hello\n");
}

int main()
{
        int i;
        signal(SIGHUP,handler);
        for(i=1;i<1000;i++){
                printf("sleep %d ...\n",i);
                sleep(1);
                fflush(stdout);
        }

        return 0;
}

我们来看一下实验效果,编译启动,然后关闭终端

图片

打开一个新的终端,检查进程是否还在运行,以及设置的信号处理函数是否被调用

图片

程序未退出,hello也成功输出了。除了用nohup,我们可以在代码中设置信号处理函数来达到相同的效果。

此外还有一些场景我们不能直接用默认的处理函数,比如高可用场景下,我们需要使用父子进程的方式,父进程监控并维护子进程的启停和扩展、一些复杂的业务逻辑中,不想被用户Ctrl+C中断的、程序使用了大量外部资源,在退出时需要优雅的回收等等,而很多成熟优秀的程序都是这样做的,比如postgreSQL

图片

引用

https://blog.csdn.net/z\_ryan/article/details/80952498
https://mp.weixin.qq.com/s/nyT-FPdIUdJUiUCYVGEnTg

图片

阅读 312
4 声望
0 粉丝
0 条评论
你知道吗?

4 声望
0 粉丝
宣传栏