TypeCodes

一个sigprocmask和sigsuspend函数的综合应用

在Linux服务端后台开发中,经常会用到信号处理函数:sigprocmasksigsuspend。这篇文章主要通过一个综合实例演示如何使用sigprocmask函数屏蔽目标信号(信号掩码)以及sigsuspend函数挂起进程。

Linux中编译执行sigprocmask_sigsuspend程序

1 sigprocmask和sigsuspend函数的说明

关于sigprocmask函数的说明:

头文件:    #include <signal.h>
函数:     int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能:     用于获取或者改变当前进程的信号掩码(当前进程屏蔽的信号集)。
返回值:    0成功,-1失败(具体原因由errno值表示)。

关于sigsuspend函数的说明:

头文件:    #include <signal.h>
函数:     int sigsuspend(const sigset_t *mask);
功能:     阻塞当前进程(TASK_INTERRUPTIBLE可中断状态),等待mask信号集(信号掩码)之外的任何信号的到来。
            在收到pendmask之外)信号后,先调用该信号的处理函数,然后把信号集mask还原为原来的信号集,接着从sigsuspend调用处返回(进程恢复执行)。
返回值:    该系统调用始终返回-1,并将errno设置为EINTR

2 完整的程序实例

在CentOS服务器中使用编译并执行下面这段程序,效果如上图所示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
 * @FileName    sigprocmask_sigsuspend.c
 * @Describe    A simple example for using sigprocmask and sigsuspend functions in linux.
 * @Author      vfhky 2016-02-29 11:21 https://typecodes.com/cseries/sigprocmasksigsuspendapp.html
 * @Compile     gcc sigprocmask_sigsuspend.c -o sigprocmask_sigsuspend
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

//Signal handle function.
void sig_handler( int signal )
{
    printf( "Receive signal=[%d].\n", signal );
    return;
}

int main( int argc, char *argv[] )
{
    printf( "getpid=[%d].\n", getpid() );
    int i = 0;

    //Register a signal.
    struct sigaction new_act;
    sigemptyset( &new_act.sa_mask );
    new_act.sa_handler = sig_handler;
    new_act.sa_flags = 0;
    sigaction( SIGUSR1, &new_act, 0 );
    sigaction( SIGUSR2, &new_act, 0 );
    sigaction( SIGINT, &new_act, 0 );

    //Add SIGINT, SIGUSR1/SIGUSR2 signals to the signal-set of new_set.
    sigset_t new_set, old_set;
    sigemptyset( &new_set );
    sigaddset( &new_set, SIGINT );              //2
    sigaddset( &new_set, SIGUSR1 );             //10
    //sigaddset( &new_set, SIGUSR2 );           //12

    /**
     * Repalce the old mask set with the new mask set.Thus the process will block the signal of SIGINT and SIGUSR1, 
     * but it will excute the function of sig_handler when the signal such as SIGUSR2 other than SIGINT, SIGUSR1 arrives.
     */
    sigprocmask( SIG_SETMASK, &new_set, &old_set );

    //Add SIGUSR1 and SIGUSR2 signals to the signal-set of pendmask.
    sigset_t pendmask;
    sigemptyset( &pendmask );
    sigaddset( &pendmask, SIGUSR1 );            //10
    sigaddset( &pendmask, SIGUSR2 );            //12

    //Replaces the signal mask of the process with pendmask temporarily and suspends the process until delivery of a signal whose action is to invoke a signal handler or to terminate a process.
    i = sigsuspend( &pendmask );
    printf( "Sigsuspend returned with value[%d].\n", i );
    if( errno == EINTR )
    {
        printf( "[%d]Interrupted by a signal.\n", errno );
    }

    while(1)
    {
        printf( "--while.\n" );
        sleep(3);
    }
    return 0;
}

3 发送SIGUSR1和SIGUSR2信号

如下图所示,使用kill命令向进程(PID:7154)发送SIGUSR1SIGUSR2信号。

发送SIGUSR1和SIGUSR2信号

由于sigsuspend函数把信号SIGUSR1SIGUSR2加入到信号掩码pendmask中,所以这两个信号的到来对当前进程没有任何影响。

4 发送SIGINT信号

使用命令kill -SIGINT 7154向当前进程(PID:7154)发送SIGINT信号。由于该信号不在信号掩码pendmask中,所以先调用SIGINT信号的处理函数sig_handler,然后把现在的信号集pendmask还原为原来的new_set信号集。

这时,由于之前由于信号掩码pendmask被屏蔽的未决信号信号SIGUSR1SIGUSR2由内核重新传递,所以进程会执行SIGUSR2信号的处理函数(SIGUSR1信号被信号集new_set所屏蔽,因此不会执行)。接着进程从sigsuspend调用处返回,恢复执行(进入while循环)。

执行SIGINT和SIGUSR2信号处理函数

如果继续发送SIGINT信号,因为被加入到了当前信号掩码new_set中,所以也会被屏蔽(对进程无影响)。最后可以通过命令kill -SIGKILL 7154将整个进程关闭。

继续发送SIGINT和SIGKILL信号

打赏支持

Comments »