Linux进程间通信(二):信号集函数 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()

2022-10-25,,,,

我们已经知道,我们可以通过信号来终止进程,也可以通过信号来在进程间进行通信,程序也可以通过指定信号的关联处理函数来改变信号的默认处理方式,也可以屏蔽某些信号,使其不能传递给进程。那么我们应该如何设定我们需要处理的信号,我们不需要处理哪些信号等问题呢?信号集函数就是帮助我们解决这些问题的。

有关Linux进程间使用信号通信的更多内容,可以参阅我的另一篇文章,Linux进程间通信 -- 信号量函数 signal()、sigaction()

下面是信号函数集:

1、int sigemptyset(sigset_t *set);

该函数的作用是将信号集初始化为空。

2、int sigfillset(sigset_t *set);

该函数的作用是把信号集初始化包含所有已定义的信号。

3、int sigaddset(sigset_t *set, int signo);

该函数的作用是把信号signo添加到信号集set中,成功时返回0,失败时返回-1。

4、int sigdelset(sigset_t *set, int signo);

该函数的作用是把信号signo从信号集set中删除,成功时返回0,失败时返回-1.

5、int sigismember(sigset_t *set, int signo);

该函数的作用是判断给定的信号signo是否是信号集中的一个成员,如果是返回1,如果不是,返回0,如果给定的信号无效,返回-1;

6、int sigpromask(int how, const sigset_t *set, sigset_t *oset);

该函数可以根据参数指定的方法修改进程的信号屏蔽字。新的信号屏蔽字由参数set(非空)指定,而原先的信号屏蔽字将保存在oset(非空)中。如果set为空,则how没有意义,但此时调用该函数,如果oset不为空,则把当前信号屏蔽字保存到oset中。

how 的不同取值及操作如下所示:

如果sigpromask成功完成返回0,如果how取值无效返回-1,并设置errno为EINVAL。

注意:调用这个函数才能改变进程的屏蔽字,之前的函数都是为改变一个变量的值而已,并不会真正影响进程的屏蔽字。

7、int sigpending(sigset_t *set);

该函数的作用是将被阻塞的信号中停留在待处理状态的一组信号写到参数set指向的信号集中,成功调用返回0,否则返回-1,并设置errno表明错误原因。

8、int sigsuspend(const sigset_t *sigmask);

该函数通过将进程的屏蔽字替换为由参数sigmask给出的信号集,然后挂起进程的执行。注意操作的先后顺序,是先替换再挂起程序的执行。程序将在信号处理函数执行完毕后继续执行。如果接收到信号终止了程序,sigsuspend()就不会返回,如果接收到的信号没有终止程序,sigsuspend()就返回-1,并将errno设置为EINTR。

特别提醒:如果一个信号被进程阻塞,它就不会传递给进程,但会停留在待处理状态,当进程解除对待处理信号的阻塞时,待处理信号就会立刻被处理。

下面以一个例子来说明上述函数的用法,源文件为 sigset.c,代码如下:

#include <stdio.h>
#include <signal.h>
#include <unistd.h> void handler(int sig)
{
printf("Handle the signal %d\n", sig);
} int main(int argc, char **argv)
{
sigset_t sigset; // 用于记录屏蔽字
sigset_t ign; // 用于记录被阻塞(屏蔽)的信号集
struct sigaction act; // 清空信号集
sigemptyset(&sigset);
sigemptyset(&ign); // 向信号集中添加 SIGINT
sigaddset(&sigset, SIGINT); // 设置处理函数 和 信号集
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0); printf("Wait the signal SIGNAL...\n");
pause(); // 设置进程屏蔽字, 在本例中为屏蔽 SIGINT
sigprocmask(SIG_SETMASK, &sigset, 0);
printf("Please press Ctrl + C in 10 seconds...\n");
sleep(10); // 测试 SIGINT 是否被屏蔽
sigpending(&ign);
if (sigismember(&ign, SIGINT))
{
printf("The SIGINT signal has ignored\n");
} // 从信号集中删除信号 SIGINT
sigdelset(&sigset, SIGINT);
printf("Wait the signal SIGINT...\n"); // 将进程的屏蔽字重新设置, 即取消对 SIGINT 的屏蔽
// 并挂起进程
sigsuspend(&sigset); printf("The app will exit in 5 secondes!\n");
sleep(5); return 0;
}

运行结果如下:

首先,我们能过sigaction()函数改变了SIGINT信号的默认行为,使之执行指定的函数handler,所以输出了语句:Handle the signal 2。然后,通过sigprocmask()设置进程的信号屏蔽字,把SIGINT信号屏蔽起来,所以过了10秒之后,用sigpending()函数去获取被阻塞的信号集时,检测到了被阻塞的信号SIGINT,输出The SIGINT signal has ignored。最后,用函数sigdelset()函数去除先前用sigaddset()函数加在sigset上的信号SIGINT,再调用函数sigsuspend(),把进程的屏蔽字再次修改为sigset(不包含SIGINT),并挂起进程。由于先前的SIGINT信号停留在待处理状态,而现在进程已经不再阻塞该信号,所以进程马上对该信号进行处理,从而在最后,你不用输入 Ctrl+C 也会出现后面的处理语句(可参阅前面特别提醒的内容),最后过了5秒程序就成功退出了。

参考:

http://blog.csdn.net/ljianhui/article/details/10130539

《Linux 高性能服务器编程》

Linux进程间通信(二):信号集函数 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()的相关教程结束。

《Linux进程间通信(二):信号集函数 sigemptyset()、sigprocmask()、sigpending()、sigsuspend().doc》

下载本文的Word格式文档,以方便收藏与打印。