nginx系列第六篇:结合nginx梳理linux中信号的使用

news2024/9/19 10:43:50

         nginx中master进程来管理整个nginx工作的全过程,运行时其通过接收外部信号输入的方式来对内部进行相关调整。本文对照nginx来梳理一下linux中信号常用API的使用。

目录

1.函数sigaction和signal

2.关于信号集sigset_t

2.1 测试程序1

2.2 测试程序1

3.信号屏蔽和不屏蔽的效果

3.1信号不屏蔽

3.2信号屏蔽

4.nginx中master进程信号处理分析

5.模拟nginx信号捕获处理


信号的本质:内核实现的一种软中断

1.函数sigaction和signal

sigaction是比signal功能更加强大的信号捕捉函数,其依赖于结构体struct sigaction,定义如下:

/* Type of a signal handler.  */
typedef void (*__sighandler_t) (int);

struct sigaction
  {
    /* Signal handler.  */
#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED
    union
      {
    /* Used if SA_SIGINFO is not set.  */
    __sighandler_t sa_handler;
    /* Used if SA_SIGINFO is set.  */
    void (*sa_sigaction) (int, siginfo_t *, void *);
      }
    __sigaction_handler;
# define sa_handler    __sigaction_handler.sa_handler
# define sa_sigaction    __sigaction_handler.sa_sigaction
#else
    __sighandler_t sa_handler;
#endif

    /* Additional set of signals to be blocked.  */
    __sigset_t sa_mask;

    /* Special flags.  */
    int sa_flags;

    /* Restore handler.  */
    void (*sa_restorer) (void);
  };

sigaction可以定义信号处理函数执行时别的信号到来后的动作,结构sigaction中sa_flags赋值为SA_SIGINFO时,信号处理函数为sa_sigaction,否则信号处理函数为(此时退化为和signal函数一样的功能)sa_handler。

2.关于信号集sigset_t

信号集sigset_t定义如下:

#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
  unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;

typedef __sigset_t sigset_t;

信号集sigset_t处理是位图操作,需要通过其api进行操作,常用api如下:
sigemptyset:清空信号集合
sigaddset:信号集中增加某个信号
sigprocmask:用于改变进程的当前阻塞信号集,也可以用来检测当前进程的信号掩码。
    每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。
    备注:每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。
        int sigprocmask(int how,const sigset_t *set,sigset_t * oldset);
        sigprocmask可以用来改变目前的信号掩码,其操作依参数how来决定:
        SIG_BLOCK 新的信号掩码由目前的信号掩码和参数set 指定的信号掩码作并集,也就是将set中的信号掩码添加到现有的信号掩码集合中
        SIG_UNBLOCK 将目前的信号=掩码)=删除掉参数set指定的信号掩码,也就是在现有的信号掩码集合中删去set制定的信号掩码
        SIG_SETMASK 将目前的信号掩码设成参数set指定的信号掩码。
        (SIG_BLOCK是添加,SIG_UNBLOCK是删除,SIG_SETMASK是直接赋值)
        如果参数oldset不是NULL指针,oldset指向目前的信号掩码
    注意事项:sigprocmask()函数只为单线程的进程定义的,在多线程中要使用pthread_sigmask变量,在使用之前需要声明和初始化。
sigsuspend: 信号挂起,等待有信号来,类似于条件变量的功能
    备注:sigsuspend是一个原子操作,包含4个步骤:
    (1) 设置新的mask阻塞当前进程;
    (2) 收到信号,恢复原先mask;
    (3) 调用该进程设置的信号处理函数;
    (4) 待信号处理函数返回后,sigsuspend返回。
sigaction:设置信号处理函数
sigfillset:填充信号集
sigdelset:信号集中删除信号
sigismember:信号在信号集中
sigpending: 检查悬而未决的信号集

2.1 测试程序1

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

void sig_handler (int num) {
    printf("signal %d\n", num);
}

//使用sigprocmask
void test1() {
    sigset_t set;
    signal(SIGINT, sig_handler);
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    sigprocmask(SIG_SETMASK, &set, NULL);
    //sigprocmask(SIG_BLOCK, &set, NULL);
    while(1) {
        printf("master process running ...\n");
        sleep(10);
    }
}

//使用sigprocmask + sigsuspend
void test2() {
    sigset_t set;
    signal(SIGINT, sig_handler);
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    sigprocmask(SIG_SETMASK, &set, NULL);
    //sigprocmask(SIG_BLOCK, &set, NULL);
    sigemptyset(&set);
    while(1) {
        printf("master process running ...\n");
        sigsuspend(&set);
        sleep(10);
    }
}

int main()
{
#ifdef TEST1
    test1();
#else
    test2();
#endif
    
    return 0;
}

(1)测试sigprocmask

g++ test.cpp -DTEST1
./a.out

运行结果如下:

 运行结果分析:

test1()中

sigaddset(&set, SIGINT);

 sigprocmask(SIG_SETMASK, &set, NULL);

屏蔽了信号SIGINT,所以多次发送ctrl + c程序没有相应。

(2)测试sigprocmask + sigsuspend

g++ test.cpp
./a.out

运行结果如下:

 运行结果分析:

test2()中虽然调用

sigaddset(&set, SIGINT);

 sigprocmask(SIG_SETMASK, &set, NULL);

屏蔽了信号SIGINT,但是调用了sigsuspend(&set);

设置新的mask(程序中为空信号集)阻塞当前进程,收到信号,恢复原先mask。执行信号处理函数,sigsuspend是一个原子操作,其包含如下步骤:
    (1) 设置新的mask阻塞当前进程;
    (2) 收到信号,恢复原先mask;
    (3) 调用该进程设置的信号处理函数;
    (4) 待信号处理函数返回后,sigsuspend返回。

2.2 测试程序1

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


//SIGQUIT -> ctrl + \产生
static void sig_quit(int signo) { 
    printf("caught SIGQUIT : %d\n", signo); 
    //将SIGQUIT的动作设为缺省值 
    signal(SIGQUIT, SIG_DFL);
} 

int main() { 
    sigset_t newmask; 
    sigset_t oldmask; 
    sigset_t pendmask;

    //信号量捕捉函数,捕捉到SIGQUIT,跳转到函数指针sig_quit处执行
    signal(SIGQUIT, sig_quit);

    //初始化信号量集 
    sigemptyset(&newmask);

    //将SIGQUIT添加到信号量集中
    sigaddset(&newmask, SIGQUIT);

    //将newmask中的SIGQUIT阻塞掉,并保存当前信号屏蔽字
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    sleep (5);

    //检查信号是悬而未决的 
    sigpending(&pendmask);
    //SIGQUIT是悬而未决的。所谓悬而未决,是指SIGQUIT被阻塞还没有被处理 
    if (sigismember(&pendmask, SIGQUIT)) { 
        printf("SIGQUIT pending\n"); 
    }

    //恢复被屏蔽的信号SIGQUIT
    sigprocmask(SIG_SETMASK, &oldmask, NULL);

    printf("SIGQUIT unblocked\n");

    sleep(5);

    printf("end end\n");

    return 0;
}

运行结果如下:

运行结果分析:

       sigprocmask(SIG_BLOCK, &newmask, &oldmask);屏蔽信号SIGQUIT,给进程发送SIGQUIT,SIGQUIT为未决信号,5秒后 sigpending(&pendmask);获取未决信号集,sigismember(&pendmask, SIGQUIT)调用后发现SIGQUIT位于未决信号集中,输出SIGQUIT pending,恢复最原始信号屏蔽集合,输出SIGQUIT unblocked,此时发送SIGQUIT,执行信号捕捉函数,恢复其默认执行,程序立刻退出。

3.信号屏蔽和不屏蔽的效果

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<sys/time.h>

/*
SIGQUIT:Ctrl+\ 产生 默认行为: Quit (core dumped),程序退出  忽略:signal(SIGINT, SIG_IGN);
SIGINT:Ctrl+c 产生 默认行为:程序退出  忽略:signal(SIGQUIT, SIG_IGN);
无法捕获的信息号:
SIGKILL
*/

void catch_sig_handler(int num) {
    printf("begin call,catch %d sig\n", num);
    sleep(5);
    printf("end call,catch %d sig\n", num);
}

//发送Ctrl+\发送SIGQUIT消息时,程序立刻 Quit (core dumped)
void test1() {
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = catch_sig_handler;
    
    //清空屏蔽信号集
    sigemptyset(&act.sa_mask);

    //等待按键Ctrl+c,捕捉2号信号
    sigaction(SIGINT, &act, NULL);
    signal(SIGINT, catch_sig_handler);
}

//发送Ctrl+\发送SIGQUIT消息时,SIGINT信号函数处理完毕程序才会Quit (core dumped)
void test2() {
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = catch_sig_handler;
    
    //清空屏蔽信号集
    sigemptyset(&act.sa_mask);

    //临时屏蔽Ctrl+\信号  执行SIGINT信号期间,先不处理SIGQUIT信号
    sigaddset(&act.sa_mask, SIGQUIT);

    //等待按键Ctrl+c,捕捉2号信号
    sigaction(SIGINT, &act, NULL);
}


int main(int argc, char *argv[])
{
#ifdef TEST1
    test1();
#else
    test2();
#endif
    while(1) {
        printf("I am running ......\n");
        sleep(1);
    }

    return 0;
}

3.1信号不屏蔽

g++ test.cpp -DTEST1
./a.out

运行效果:

 运行结果分析:

由于没有屏蔽信号SIGQUIT(Ctrl+\),所以收到SIGQUIT后程序直接退出

3.2信号屏蔽

g++ test.cpp
./a.out

运行效果:

 运行结果分析:

由于调用sigaddset(&act.sa_mask, SIGQUIT);屏蔽信号SIGQUIT,所以在执行SIGINT信号处理函数过程中是不会被SIGQUIT所干扰的,只有信号处理函数执行完毕后才执行SIGQUIT的动作(默认是退出程序)。

4.nginx中master进程信号处理分析

nginx中信号处理在函数ngx_init_signals和ngx_master_process_cycle中,代码调用大致逻辑如下:

int ngx_cdecl
main(int argc, char *const *argv)
{
    ...
    //设置信号处理函数,初始化需要捕获的信号
    if (ngx_init_signals(cycle->log) != NGX_OK) {
        return 1;
    }
    ...
    主线程捕获函数
    ngx_master_process_cycle(cycle);
    ...
    return 0;
}

ngx_init_signals定义如下:

 ngx_master_process_cycle定义如下:

 

通过 sigprocmask + sigsuspend配合在保证master主进程接收外部信号输入并及时处理。

5.模拟nginx信号捕获处理

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <stdint.h>
#include <time.h>
#include <sys/time.h>
 
 
#define GROUP_NUM 2
 
typedef struct {
    int     signo;
    char   *signame;
    char   *name;
    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);
} ngx_signal_t;
 
#define ngx_signal_helper(n)     SIG##n
#define ngx_signal_value(n)      ngx_signal_helper(n)
 
#define ngx_value_helper(n)   #n
#define ngx_value(n)          ngx_value_helper(n)
 
#define NGX_SHUTDOWN_SIGNAL      QUIT
#define NGX_TERMINATE_SIGNAL     TERM
#define NGX_NOACCEPT_SIGNAL      WINCH
#define NGX_RECONFIGURE_SIGNAL   HUP
 
#if (NGX_LINUXTHREADS)
#define NGX_REOPEN_SIGNAL        INFO
#define NGX_CHANGEBIN_SIGNAL     XCPU
#else
#define NGX_REOPEN_SIGNAL        USR1
#define NGX_CHANGEBIN_SIGNAL     USR2
#endif
 
#define ngx_errno                  errno
 
typedef intptr_t        ngx_int_t;
typedef int             ngx_err_t;
typedef uintptr_t       ngx_uint_t;
typedef pid_t       ngx_pid_t;

typedef ngx_uint_t  ngx_rbtree_key_t;
typedef ngx_int_t   ngx_rbtree_key_int_t;

typedef ngx_rbtree_key_t      ngx_msec_t;
typedef ngx_rbtree_key_int_t  ngx_msec_int_t;

#define ngx_getpid   getpid
#define ngx_getppid  getppid

#define ngx_gettimeofday(tp)  (void) gettimeofday(tp, NULL);
#define ngx_msleep(ms)        (void) usleep(ms * 1000)
#define ngx_sleep(s)          (void) sleep(s)
 
#define NGX_PROCESS_SINGLE     0
#define NGX_PROCESS_MASTER     1
#define NGX_PROCESS_SIGNALLER  2
#define NGX_PROCESS_WORKER     3
#define NGX_PROCESS_HELPER     4
 
#define  NGX_OK          0
#define  NGX_ERROR      -1
#define  NGX_AGAIN      -2
#define  NGX_BUSY       -3
#define  NGX_DONE       -4
#define  NGX_DECLINED   -5
#define  NGX_ABORT      -6
 
ngx_uint_t    ngx_process;
ngx_uint_t    ngx_worker;
ngx_pid_t     ngx_pid;
ngx_pid_t     ngx_parent;

sig_atomic_t  ngx_reap;
sig_atomic_t  ngx_sigio;
sig_atomic_t  ngx_sigalrm;
sig_atomic_t  ngx_terminate;
sig_atomic_t  ngx_quit;
sig_atomic_t  ngx_debug_quit;
ngx_uint_t    ngx_exiting;
sig_atomic_t  ngx_reconfigure;
sig_atomic_t  ngx_reopen;

sig_atomic_t  ngx_change_binary;
ngx_pid_t     ngx_new_binary;
ngx_uint_t    ngx_inherited;
ngx_uint_t    ngx_daemonized;

sig_atomic_t  ngx_noaccept;
ngx_uint_t    ngx_noaccepting;
ngx_uint_t    ngx_restart;

void
ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext);
 
ngx_signal_t  signals[] = {
    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
      "reload",
      ngx_signal_handler },
 
    { ngx_signal_value(NGX_REOPEN_SIGNAL),
      "SIG" ngx_value(NGX_REOPEN_SIGNAL),
      "reopen",
      ngx_signal_handler },
 
    { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
      "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
      "",
      ngx_signal_handler },
 
    { ngx_signal_value(NGX_TERMINATE_SIGNAL),
      "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
      "stop",
      ngx_signal_handler },
 
    { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
      "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
      "quit",
      ngx_signal_handler },
 
    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
      "",
      ngx_signal_handler },
 
    { SIGALRM, "SIGALRM", "", ngx_signal_handler },
 
    { SIGINT, "SIGINT", "", ngx_signal_handler },
 
    { SIGIO, "SIGIO", "", ngx_signal_handler },
 
    { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
 
    { SIGSYS, "SIGSYS, SIG_IGN", "", NULL },
 
    { SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL },
 
    { 0, NULL, "", NULL }
};
 
void
ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
{
    char            *action;
    ngx_int_t        ignore;
    ngx_err_t        err;
    ngx_signal_t    *sig;
 
    ignore = 0;
 
    err = ngx_errno;
 
	printf("signo = [%d]\n", signo);
 
    for (sig = signals; sig->signo != 0; sig++) {
        if (sig->signo == signo) {
            break;
        }
    }
 
    action = "";
 
    switch (ngx_process) {
 
    case NGX_PROCESS_MASTER:
    case NGX_PROCESS_SINGLE:
        switch (signo) {
 
        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
            ngx_quit = 1;
            action = ", shutting down";
            printf("recv signal SIGQUIT\n");
            sleep(5);
            break;
 
        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
        case SIGINT:
            ngx_terminate = 1;
            action = ", exiting";
            printf("recv signal SIGINT\n");
            sleep(5);
            break;
 
        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
			if (ngx_daemonized) {
                ngx_noaccept = 1;
                action = ", stop accepting connections";
            }
			
            break;
 
        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
            ngx_reconfigure = 1;
            action = ", reconfiguring";
            break;
 
        case ngx_signal_value(NGX_REOPEN_SIGNAL):
            ngx_reopen = 1;
            action = ", reopening logs";
            break;
 
        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
            if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) {
 
                action = ", ignoring";
                ignore = 1;
                break;
            }
 
            ngx_change_binary = 1;
            action = ", changing binary";
            break;
 
        case SIGALRM:
            ngx_sigalrm = 1;
            break;
 
        case SIGIO:
            ngx_sigio = 1;
            break;
 
        case SIGCHLD:
            ngx_reap = 1;
            break;
        }
 
        break;
 
    case NGX_PROCESS_WORKER:
    case NGX_PROCESS_HELPER:
        switch (signo) {
 
        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
			if (!ngx_daemonized) {
                break;
            }
            ngx_debug_quit = 1;
            /* fall through */
        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
            ngx_quit = 1;
            action = ", shutting down";
            break;
 
        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
        case SIGINT:
            ngx_terminate = 1;
            action = ", exiting";
            break;
 
        case ngx_signal_value(NGX_REOPEN_SIGNAL):
            ngx_reopen = 1;
            action = ", reopening logs";
            break;
 
        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
        case SIGIO:
            action = ", ignoring";
            break;
        }
 
        break;
    }
 
    if (siginfo && siginfo->si_pid) {
       // printf("signal %d (%s) received from %P%s",
        //              signo, sig->signame, siginfo->si_pid, action);
       printf("signal %d (%s)\n", signo, sig->signame);

    } else {
        /*
        printf("signal %d (%s) received%s",
                      signo, sig->signame, action);
        */
       printf("signal %d (%s)\n", signo, sig->signame);
    }
 
    if (ignore) {
        printf("the changing binary signal is ignored: "
            "you should shutdown or terminate "
            "before either old or new binary's process");
    }
	//printf("action = [%s]\n", action);
}
 
 
ngx_int_t
ngx_init_signals()
{
    ngx_signal_t      *sig;
    struct sigaction   sa;
 
    for (sig = signals; sig->signo != 0; sig++) {
        memset(&sa, 0x00, sizeof(struct sigaction));
 
        if (sig->handler) {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;
 
        } else {
            sa.sa_handler = SIG_IGN;
        }
 
        sigemptyset(&sa.sa_mask);
        if (sigaction(sig->signo, &sa, NULL) == -1) {
#if (NGX_VALGRIND)
            printf("sigaction(%s) failed, ignored", sig->signame);
#else
            printf("sigaction(%s) failed", sig->signame);
            return NGX_ERROR;
#endif
        }
    }
 
    return NGX_OK;
}

void
ngx_master_process_cycle()
{
    char              *title;
    u_char            *p;
    size_t             size;
    ngx_int_t          i;
    ngx_uint_t         sigio;
    sigset_t           set;
    struct itimerval   itv;
    ngx_uint_t         live;
    ngx_msec_t         delay;

    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigaddset(&set, SIGALRM);
    sigaddset(&set, SIGIO);
    sigaddset(&set, SIGINT);
    sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));

	//如下代码的含义: 系统很忙的,没有时间及时相应信号,进程可以先把信号阻塞掉,等系统有空闲时间再去相应,这也保证了信号的可靠性。
    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
        printf("sigprocmask() failed, ngx_errno:%d\n", ngx_errno);
    }

    sigemptyset(&set);

    ngx_new_binary = 0;
    delay = 0;
    sigio = 0;
    live = 1;

    for ( ;; ) {
        printf("master process running ......\n");
        if (delay) {
            /*
            if (ngx_sigalrm) {
                sigio = 0;
                delay *= 2;
                ngx_sigalrm = 0;
            }

            printf("termination cycle: %M\n", delay);

            itv.it_interval.tv_sec = 0;
            itv.it_interval.tv_usec = 0;
            itv.it_value.tv_sec = delay / 1000;
            itv.it_value.tv_usec = (delay % 1000 ) * 1000;

            if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
                printf("setitimer() failed, ngx_errno:%d\n", ngx_errno);
            }
            */
        }

        sigsuspend(&set);


        if (ngx_reap) {
            ngx_reap = 0;
            live = 1;
        }

        if (!live && (ngx_terminate || ngx_quit)) {
            printf("master process exit\n");
            exit(0);
        }

        if (ngx_terminate) {
            if (delay == 0) {
                delay = 50;
            }

            if (sigio) {
                sigio--;
                continue;
            }
            continue;
        }

        if (ngx_quit) {
            continue;
        }

        if (ngx_reconfigure) {
            ngx_reconfigure = 0;

            if (ngx_new_binary) {
                ngx_noaccepting = 0;

                continue;
            }

            printf("reconfiguring \n");

            /* allow new processes to start */
            ngx_msleep(100);

            live = 1;
        }

        if (ngx_restart) {
            ngx_restart = 0;
            live = 1;
        }

        if (ngx_reopen) {
            ngx_reopen = 0;
        }

        if (ngx_change_binary) {
            ngx_change_binary = 0;
        }

        if (ngx_noaccept) {
            ngx_noaccept = 0;
            ngx_noaccepting = 1;
        }
    }
}

int main()
{
	//信号初始化
	ngx_init_signals();
 
    //创建子进程
	for (int i = 0; i < GROUP_NUM; i++) {
		int pid = fork();
		switch (pid) {
		case -1: // error
			return -1;
		case 0: // child
            sleep(2);
			printf("child exit \n");
			exit(0);
		default: // parent
			break;
		}
	}
 
    //主进程执行
    ngx_master_process_cycle();
    
	return 0;
}

编译运行:

 通过运行可以看出程序很快速的交替响应信号SIGINT和SIGQUIT。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/617212.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

宝塔安装ModStart常见问题

Q&#xff1a;环境提示PHP未禁用危险函数 安装系统时通常会需要解禁 system,exec,passthru,shell_exec,popen,proc_open 等危险函数。部分集成环境会提示危险函数风险&#xff0c;通常可以如下方式解决&#xff1a; 该函数在通常只是在系统 系统安装/系统升级/模块安装/模块升…

【SpinalHDL快速入门】4.3、基本类型之UInt/SInt

文章目录 1.1、描述1.2、声明1.3、运算符1.3.1、逻辑运算&#xff08;Logic&#xff09;1.3.2、算术运算&#xff08;Arithmetic&#xff09;1.3.3、比较&#xff08;Comparison&#xff09;1.3.4、类型转换&#xff08;Type Cast&#xff09;1.3.5、部分赋值/提取操作符&#…

第一章:数据库概述

第一章&#xff1a;数据库概述 1.1&#xff1a;为什么要使用数据库 持久化(persistence)&#xff1a;把数据保存到可掉电式存储设备中以供之后使用。大多数情况下&#xff0c;特别是企业级应用&#xff0c;数据持久化意味着将内存中的数据保存到硬盘上加以"固化"&a…

低代码平台简单分享

低代码平台简单分享 文章目录 低代码平台简单分享1、什么是低代码&#xff1f;什么是低代码平台&#xff1f;2、低代码平台的前世今生**一、低代码的起源**二、低代码的分类三、低代码的能力四、低代码开发的特点 3、目前主流的低代码平台有哪些&#xff1f;优缺点&#xff1f;…

常用模拟低通滤波器的设计——契比雪夫II型滤波器

常用模拟低通滤波器的设计——契比雪夫II型滤波器 切比雪夫 II 型滤波器的振幅平方函数为&#xff1a; 式中&#xff0c;为有效带通截止频率&#xff0c; 是与通带波纹有关的参量&#xff0c; 大&#xff0c;波纹大&#xff0c;&#xff1b; 为 N 阶契比雪夫多项式。 在 Matl…

几种常见数据库的表和列信息查询

文章目录 前言1. oracle数据库1.1 表信息和注释信息1.2 表的列信息 2. mysql数据库2.1 常用的几个命令2.2 使用desc查看表结构2.3 表结构信息主要存在information_schema数据库2.4 主要表是columns&#xff0c;tables&#xff0c;schemata2.4.1 schemata 数据库信息2.4.2 table…

三、opengles画三角形

第一部分Java端 1&#xff09;界面 <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.andro…

数据中台浅析——概念、架构以及未来

数据中台浅析 1. 引言 在当今的数字化时代&#xff0c;数据被誉为"新的石油"&#xff0c;越来越多的企业和组织开始深度挖掘数据的价值。在这个过程中&#xff0c;数据中台逐渐成为了数据管理和分析的核心架构&#xff0c;让我们来深入了解一下它。 1.1 数据中台…

算法复杂度分析(一)

求第n个斐波那契数列 斐波那契数 0 1 1 2 3 5 数列默认从0开始 public static int fib1(int n) {if(n < 1) return n;return fib1(n-1) fib1(n-2);}public static int fib2(int n) {if(n < 1) return n;int first 0;int secend 1;for (int i 0; i < n-1; i) {int…

七、帧缓冲离屏渲染

第一部分基础概念 1)两种帧缓冲的由来 首先opengl能够显示到屏幕&#xff0c;也是有一个默认的framebuffer由窗口系统创建并管理的&#xff0c;将数据放到默认framebuffer 中就可以显示到屏幕上。但是应用程序也想创建额外的非可显示的framebuffer。 应用程序自己创建FBO也是…

【2023RT-Thread全球技术峰会】一套全新的物联网多应用框架xiotman,助你解决多应用的难题

写在前面 就在上周&#xff0c;我作为讲师参与了2023RT-Thread全球技术峰会的主题演讲&#xff0c;我给大家带来了一套全新的解决物联网终端应用多样化的软件架构解决方案&#xff0c;在这里我再次以图文的形式介绍一下给社区的小伙伴&#xff0c;希望借此机会找到更多的同频小…

Nginx优化、Nginx+Tomcat实现负载均衡、动静分离集群部署

Nginx优化、NginxTomcat实现负载均衡、动静分离集群部署 一、Tomcat 优化二、Tomcat多实例部署1、安装好jdk2、安装tomcat3、配置tomcat环境变量4、修改tomcat2中的server.xml文件&#xff0c;要求各tomcat实例配置不能有重复的端口号5、修改各tomcat实例中的startup.sh和shutd…

ARM--$2$驱动模块

目录 1.驱动模块&#xff08;驱动程序的框架&#xff09; 2.内核中的打印函数&#xff08;编写第一个驱动程序&#xff09; Source Insight 使用&#xff1a; 打印函数编写 分析 3.驱动的多文件编译 4.模块传递参数 安装好驱动之后如何传参&#xff1f; 多驱动之间调用…

js内存管理与闭包

JavaScript内存管理 ◼ 不管什么样的编程语言&#xff0c;在代码的执行过程中都是需要给它分配内存的&#xff0c;不同的是某些编程语言需要我们自己手动的管理内存&#xff0c; 某些编程语言会可以自动帮助我们管理内存&#xff1a; ◼ 不管以什么样的方式来管理内存&#xf…

扩展系统功能——装饰模式(二)

装饰模式概述 装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为&#xff0c;在现实生活中&#xff0c;这种情况也到处存在&#xff0c;例如一张照片&#xff0c;我们可以不改变照片本身&#xff0c;给它增加一个相框&#xff0c;使得它具有防潮的功能&…

安装Unity Hub和Unity Editor

1、首先下载UnityHub的安装包&#xff1a; https://unity.com/cn/download 开始您的创意项目并下载 Unity Hub | Unity 2、运行安装包 3、运行Unity Hub 第一次运行Unity Hub会默认弹出登录Unity提示&#xff0c;正常登录就可以了&#xff0c;登录后会在浏览器提示打开 Unity…

nginx+lua(openresty) 安装及使用(一)

前言 OpenResty —— 通过 Lua 扩展 NGINX 实现的可伸缩的 Web 平台。 OpenResty&#xff08;也称为 ngx_openresty&#xff09;是一个基于 Nginx 与 Lua 的高性能 Web 平台&#xff0c;其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超…

全志V3S嵌入式驱动开发(lcd屏幕驱动)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于一些设备&#xff0c;人们是希望从屏幕上面获取结果信息的&#xff0c;这样也显得更直观一些。另外&#xff0c;也有一些设备&#xff0c;它本…

阿里云OSS实现图片上传(后端接口)

文章目录 1、阿里云oss服务开通2、java操作阿里云oss&#xff0c;上传文件到阿里云oss1. 创建操作阿里云oss许可证2. 安装SDK3.修改配置文件4.创建常量类&#xff0c;读取配置文件中的内容5、OssController6、OssService7、OssServiceImpl8、改进1. 多次上传相同名称文件&#…

Segment Anything学习小结

论文地址&#xff1a;Segment Anything 项目地址&#xff1a;https://github.com/facebookresearch/segment-anything 在线Demo&#xff1a; https://segment-anything.com/demo 前言 近日&#xff0c;MetaAI团队开源了一个用于分割的10亿个masks&#xff0c;1100w张图片数…