Nodejs 进程信号

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nodejs 进程信号相关的知识,希望对你有一定的参考价值。

参考技术A 首先我们来看一个简单的master/worker的例子

worker程序

首先无论在子进程还是父进程都可以接收linux的信号 SIGINT,SIGTERM,SIGHUP 等。

只要是程序能够监听到的退出信号。我们都可以统一通过监听 exit 来处理程序

进程信号

信号的基本概念
信号就是一个软件中断,可以打断进程的执行,让进程处理信号的事件
信号种类:
技术图片
1-31信号是不可靠信号:信号有可能会丢失(非实时信号)

1.向进程发送非实时信号(该信号没有被挂起),信号响应会嵌套(即正在响应某个函数时,有其它信号发来,进程会先去响应其它信号,结束后再继续原来的任务)。
2.当进程正在响应某个信号时(该信号没有被挂起),即响应函数正在执行的过程中,有相同的n个信号相继发来,进程不会嵌套;
当执行完响应函数后,进程只会执行n个信号中的一个。
3.对非实时信号,挂起的信号不会重复(即有n个相同的信号被挂起,进程只会执行一次)

34-64信号是可靠信号:信号不会丢失(实时信号)

1.向进程发送实时信号(该信号没有被挂起),信号响应会嵌套。
2.当进程正在响应某个信号时(该信号没有被挂起),即响应函数正在执行的过程中,有相同的n个信号相继发来,进程不会嵌套;
当执行完响应函数后,进程会继续执行函数n次。
3.对实时信号,挂起的信号可以重复(即有n个相同的信号被挂起,进程会执行n次)

如果进程的挂起信号中含有实时和非实时信号,那么进程优先响应实时信号并会从大到小依次响应,而非实时信号没有固定的次序,甚至某个非实时信号会被丢失(这可说明为什么非实时信号被叫做不可靠信号)。
信号产生方式:
1.硬件产生:ctrl+c(SIGINT),ctrl+z(SIGTSTP),ctrl+|(SEGOUIT)
2.软件产生:kill -信号号码+进程号
eg:kill -9 12345
kill函数:kill(信号号, SIGINT);//给给定信号号的进程发送SIGINT信号
abort函数:void abort();给当前进程发送SIGABRT信号
raise函数:int raise(int sig);给当前进程发送sig信号
alarm函数:unsigned int alarm(unsigned int seconds);
seconds秒之后,给调用进程发送SIGALRM信号
seconds若为0,则表示取消上一个定时器
返回值:上一个定时器的剩余时间

alarm(3);//定时3秒
sleep(1);//表示alarm会响应2秒
alarm(0);//取消3秒定时器

core dumped:核心转储文件命名格式:core.pid
ulimit -a 查看
ulimit -c 设置core文件最大大小,单位为kb ulimit -c 1024
调试方法:gdb ./main->core.file core.pid ->bt
信号的生命周期
信号在进程当中的注册
非可靠信号(1~31):
如果待注册的信号,在pending位图当中已经存在了,则不再去添加当前信号(意味着不增加sigqueue节点)
如果待注册的信号,在pending位图中不存在,将pending位图当中的对应的bit位置位1,然后添加sigqueue节点
可靠信号(34~64):
如果待注册的信号,在pending位图当中存在(意味着对应的bit位为1),另外增加sigqueue节点(意味着这个信号也会被处理)
如果待注册的信号,在pending位图当中不存在,则更改pending位图中对应的bit位,并且增加sigqueue节点
信号在进程当中的注销
1.非可靠信号
将pending位图当中的对应的bit位置为0,并且将sigqueue节点删除
2.可靠信号
如果注销的信号,存在的sigqueue节点只有1个,则将pending位图当中的对应的bit位置为0,并且将sigqueue节点删除
如果注销的信号,存在sigqueue节点有多个,不能将pending位图当中对应的bit位置为0,而是删除一个sigqueue节点

信号的处理
默认处理方式---SIG_DFL
忽略处理方式---SIG_IGN
自定义处理方式

signal函数:可以重置当前操作系统对信号的处理方式

typedef void (* sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);

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

void sigbackfunc(int sig)
{
    printf("sig : [%d]
", sig);
    signal(2, SIG_DFL);//遇到2号信号之后,忽略处理
}

int main()
{
    signal(SIGINT, sigbackfunc);//收到2号信号之后,自定义处理(转到sigbackfunc函数)
    while(1)
    {
        printf("hello~
");
        sleep(1);
    }
    return 0;
}

sigaction函数

int sigaction(int signum,const struct sigaction act,struct sigaction oldact);
act:表示将当前信号修改成一种处理方式,让操作系统接收到这个信号的时候,调用哪一个函数处理
oldact:表示之前操作系统对收到该信号的时候 的处理方式
技术图片

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

void sigbackfunc(int sig)
{
    printf("%d
", sig);
}

int main()
{
    struct sigaction newact;
    struct sigaction oldact;

    newact.sa_handler = sigbackfunc;
    newact.sa_flags = 0;
    sigemptyset(&newact.sa_mask);
    sigaction(2, &newact, &oldact);

    while(1)
    {
        printf("hello
");
        sleep(1);
    }
    return 0;
}

信号捕捉流程

技术图片

信号阻塞
信号阻塞不是说信号不能被注册,而是说,从操作系统在判断pending位图是发现接受某个信号了,去查找block位图对应的bit位
如果block对应的位为1,则不处理该信号,sigqueue节点还是在的
如果block对应的位置为0,则处理该信号
int sigprocmask(int how,const sigset_t set,sigset_t oldset);
how:SIG_BLOCK-->设置某个信号为阻塞状态,block(new)=block(old) | set
SIG_UNBLOCK-->设置某个信号为非阻塞状态,block(new)=block & (~set)
SIG_SETMASK-->设置新位图block=set

block:0000 0000
set   : 1111 1111
(阻塞信号)   block(new)=block(old) | set     1111 1111
(非阻塞信号)block(new)=block & (~set)      1111 1111 & (0000 0000)-->0000 0000

注意
9号,19号信号是不能设置为阻塞的

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

void sigcallback(int sig)
{
    printf("sig : [%d]
", sig);
}

int main()
{
    signal(2, sigcallback);
    signal(40, sigcallback);

    //block位图设置一下,也就是阻塞某些信号
    //int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
    sigset_t set, oldset;
    sigemptyset(&set);
    sigemptyset(&oldset);

    //给set位图编程全1
    //int sigfillset(sigset_t *set);
    sigfillset(&set);

    sigprocmask(SIG_BLOCK, &set, &oldset);
    getchar();
    sigprocmask(SIG_UNBLOCK, &set, NULL);
    while(1)
    {
        ;
    }
    return 0;
}

竞态条件
程序的不同的执行流,执行顺序的不同,会导致程序结果的不同,这种我们称之为竞态条件

重入:不同的执行流可以访问同样的资源(代码)
可重入:不同的执行流可以访问同样的资源,不会对程序的结果产生影响
不可重入:不同的执行流可以访问同样的资源,但是对程序的结果产生影响
     不可重入场景:全局变量,malloc和free ,绝大多数的库函数

SIGCHLD--17号信号
SIGCHLD信号的默认行为是不处理,我们可以更改掉SIGCHLD信号的默认处理函数,让收到该信号的时候调用自定义的函数去完成进程等待,从而父进程就可以完成自己的逻辑,而不用调用wait进行阻塞了,意味着了,我们可以在回调函数中调用wait

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

void sigcallback(int sig)
{
    printf("sig : %d
", sig);
    wait(NULL);
}

int main()
{
    signal(SIGCHLD, sigcallback);
    pid_t pid = fork();
    if(pid < 0)
    {
        return pid;
    }
    else if(pid == 0)
    {
        //child
        printf("i am child
");
        sleep(3);
        exit(1);
    }
    else
    {
        //wait
        while(1)
        {
            printf("我不听~~~
");
            sleep(1);
        }
    }
    return 0;
}

以上是关于Nodejs 进程信号的主要内容,如果未能解决你的问题,请参考以下文章

多个nodejs应用程序之间的进程间通信

Nodejs:将 Ctrl+C 发送到 Windows 上的子进程

nodejs 怎样检测子进程执行完成

进程间通信的信号

进程信号

进程信号