一,信号集及相关操作函数

信号集被定义为一种数据类型:

typedef struct {

unsigned long sig[_NSIG_WORDS];

} sigset_t

信号集用来描述信号的集合,每个信号占用一位(64位)。Linux所支持的所有信号可以全部或部分的出现在信号集中,主要与信号阻塞相关函数配合使用。下面是为信号集操作定义的相关函数:

#include <signal.h>

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signum)

int sigdelset(sigset_t *set, int signum);

int sigismember(const sigset_t *set, int signum);

sigemptyset(sigset_t *set)初始化由set指定的信号集,信号集里面的所有信号被清空,相当于64为置0;

sigfillset(sigset_t *set)调用该函数后,set指向的信号集中将包含linux支持的64种信号,相当于64为都置1;

sigaddset(sigset_t *set, int signum)在set指向的信号集中加入signum信号,相当于将给定信号所对应的位置1;

sigdelset(sigset_t *set, int signum)在set指向的信号集中删除signum信号,相当于将给定信号所对应的位置0;

sigismember(const sigset_t *set, int signum)判定信号signum是否在set指向的信号集中,相当于检查给定信号所对应的位是0还是1。

示例程序:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
void print_sigset(sigset_t *set);
int main(void)
{
sigset_t myset;
sigemptyset(&myset);
sigaddset(&myset,SIGINT);
sigaddset(&myset,SIGQUIT);
sigaddset(&myset,SIGUSR1);
sigaddset(&myset,SIGRTMIN);
print_sigset(&myset); return 0; }
void print_sigset(sigset_t *set)
{
int i;
for(i = 1; i < NSIG; ++i){
if(sigismember(set,i))
printf("1");
else
printf("0");
}
putchar('\n');
}

结果:

可以看到添加信号的相应位置1.

二,信号阻塞与未决

man帮助说明:

Signal mask and pending signals
       A signal may be blocked, which means that it will not be delivered
until it is later unblocked. Between the time when it is generated
and when it is delivered a signal is said to be pending. Each thread in a process has an independent signal mask, which
indicates the set of signals that the thread is currently blocking.
A thread can manipulate its signal mask using pthread_sigmask(3). In
a traditional single-threaded application, sigprocmask(2) can be used
to manipulate the signal mask.
执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。
信号在内核中的表示可以看作是这样的:
看图说话:
block集(阻塞集、屏蔽集):一个进程所要屏蔽的信号,在对应要屏蔽的信号位置1
pending集(未决信号集):如果某个信号在进程的阻塞集中,则也在未决集中对应位置1,表示该信号不能被递达,不会被处理
handler(信号处理函数集):表示每个信号所对应的信号处理函数,当信号不在未决集中时,将被调用
 
以下是与信号阻塞及未决相关的函数操作:

#include <signal.h>

int  sigprocmask(int  how,  const  sigset_t *set, sigset_t *oldset));

int sigpending(sigset_t *set));

int sigsuspend(const sigset_t *mask));

sigprocmask()函数能够根据参数how来实现对信号集的操作,操作主要有三种:

  • SIG_BLOCK 在进程当前阻塞信号集中添加set指向信号集中的信号,相当于:mask=mask|set
  • SIG_UNBLOCK 如果进程阻塞信号集中包含set指向信号集中的信号,则解除对该信号的阻塞,相当于:mask=mask|~set
  • SIG_SETMASK 更新进程阻塞信号集为set指向的信号集,相当于mask=set

sigpending(sigset_t *set))获得当前已递送到进程,却被阻塞的所有信号,在set指向的信号集中返回结果。

sigsuspend(const sigset_t *mask))用于在接收到某个信号之前, 临时用mask替换进程的信号掩码, 并暂停进程执行,直到收到信号为止。

sigsuspend 返回后将恢复调用之前的信号掩码。信号处理函数完成后,进程将继续执行。该系统调用始终返回-1,并将errno设置为EINTR。

示例程序:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) void handler(int sig);
void printsigset(sigset_t *set)
{
int i;
for (i=1; i<NSIG; ++i)
{
if (sigismember(set, i))
putchar('1');
else
putchar('0');
}
printf("\n");
} int main(int argc, char *argv[])
{
sigset_t pset;
sigset_t bset;
sigemptyset(&bset);
sigaddset(&bset, SIGINT);
if (signal(SIGINT, handler) == SIG_ERR)
ERR_EXIT("signal error");
if (signal(SIGQUIT, handler) == SIG_ERR)
ERR_EXIT("signal error"); sigprocmask(SIG_BLOCK, &bset, NULL);//将信号加入进程阻塞集中
for (;;)
{
sigpending(&pset);
printsigset(&pset);
sleep(1);
}
return 0;
} void handler(int sig)
{
if (sig == SIGINT)
printf("recv a sig=%d\n", sig);
else if (sig == SIGQUIT)
{
sigset_t uset;
sigemptyset(&uset);
sigaddset(&uset, SIGINT);
sigprocmask(SIG_UNBLOCK, &uset, NULL);
}
}

结果:

说明:程序首先将SIGINT信号加入进程阻塞集(屏蔽集)中,一开始并没有发送SIGINT信号,所以进程未决集中没有处于未决态的信号,当我们连续按下ctrl+c时,向进程发送SIGINT信号,由于SIGINT信号处于进程的阻塞集中,所以发送的SIGINT信号不能递达,也是就是处于未决状态,所以当我打印未决集合时发现SIGINT所对应的位为1,现在我们按下ctrl+\,发送SIGQUIT信号,由于此信号并没被进程阻塞,所以SIGQUIT信号直接递达,执行对应的处理函数,在该处理函数中解除进程对SIGINT信号的阻塞,所以之前发送的SIGINT信号递达了,执行对应的处理函数,但由于SIGINT信号是不可靠信号,不支持排队,所以最终只有一个信号递达。

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
#include <unistd.h> /* 版本1, 可靠信号将被递送多次 */
#define MYSIGNAL SIGRTMIN+5
/* 版本2, 不可靠信号只被递送一次 */
//#define MYSIGNAL SIGTERM void sig_handler(int signum)
{
psignal(signum, "catch a signal");
} int main(int argc, char **argv)
{
sigset_t block, pending;
int sig, flag; /* 设置信号的handler */
signal(MYSIGNAL, sig_handler); /* 屏蔽此信号 */
sigemptyset(&block);
sigaddset(&block, MYSIGNAL);
printf("block signal\n");
sigprocmask(SIG_BLOCK, &block, NULL); /* 发两次信号, 看信号将会被触发多少次 */
printf("---> send a signal --->\n");
kill(getpid(), MYSIGNAL);
printf("---> send a signal --->\n");
kill(getpid(), MYSIGNAL); /* 检查当前的未决信号 */
flag = 0;
sigpending(&pending);
for (sig = 1; sig < NSIG; sig++) {
if (sigismember(&pending, sig)) {
flag = 1;
psignal(sig, "this signal is pending");
}
}
if (flag == 0) {
printf("no pending signal\n");
} /* 解除此信号的屏蔽, 未决信号将被递送 */
printf("unblock signal\n");
sigprocmask(SIG_UNBLOCK, &block, NULL); /* 再次检查未决信号 */
flag = 0;
sigpending(&pending);
for (sig = 1; sig < NSIG; sig++) {
if (sigismember(&pending, sig)) {
flag = 1;
psignal(sig, "a pending signal");
}
}
if (flag == 0) {
printf("no pending signal\n");
} return 0;
}

结果:

两次执行结果不同:第一次连续发送两次不可靠信号,最后解除阻塞时,只有一个递达,说明不可靠信号不支持排队。

第二次执行时,连续两次发送可靠信号,解除阻塞后,都递达,说明可靠信号支持排队。

ok,这节就写到这吧

 

linux系统编程之信号(五):信号集操作函数,信号阻塞与未决的更多相关文章

  1. linux系统编程之进程(五)

    今天继续学习系统编程,学习的主题还是进程,今天主要讨论的是守护进程相关的概念,开始进入正题: 什么是守护进程:       守护进程的创建步骤: 在描述它之前,首先得先了解两个概念:进程组.会话期: ...

  2. Linux系统编程——信号

    目录 信号的介绍 信号的机制 信号的编号 Linux常规信号一览表 信号的产生 终端按键产生信号 硬件异常产生信号 kill函数/命令产生信号 信号的操作函数 信号集设定 sigprocmask函数 ...

  3. linux系统编程之信号(七)

    今天继续学习信号,主要是学习关于时间和定时器相关的函数的使用,关于这个实际上有很多内容,这里先简要进行说明,等之后再慢慢进行相关深入,也主要是为接下来要做的一个综合linux系统编程的例子做准备,好了 ...

  4. linux系统编程--信号

    信号的概念 man 7 siganl  查看man手册 信号在我们的生活中随处可见, 如:古代战争中摔杯为号:现代战争中的信号弹:体育比赛中使用的信号枪......他们都有共性:1. 简单 2. 不能 ...

  5. Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号

    Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...

  6. Linux系统编程(23)——信号的阻塞

    实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending).进程可以选择阻塞(Block)某个信号.被阻塞的信号产生时将保持在未决状态,直到进程 ...

  7. linux系统编程之信号(四)

    今天继续探讨信号相关的东东,话不多说,正入正题: 信号在内核中的表示: 下面用图来进一步描述这种信号从产生到递达之间的状态(信号阻塞与未诀):   那是怎么来决定的呢?下面慢慢来举例分解: 所以,通过 ...

  8. linux系统编程之文件与io(五)

    上一节中已经学习了文件描述符的复制,复制方法有三种,其中最后一种fcntl还并未使用到,关于这个函数,不光只有复制文件描述符的功能,还有其它一些用法,本节就对其进行一一剖析: fcntl常用操作: 这 ...

  9. Linux系统编程@进程通信(一)

    进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...

随机推荐

  1. 网站发布时候,图片,css,js等都不显示

    因为IIS里面的MIME类型没有添加,就是安装IIS时候没有勾选对.需要重新勾选,安装IIS.

  2. 可重入函数reentrant function

    可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数:而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能 ...

  3. rubber

    rubber - 必应词典 美['rʌbər]英['rʌbə(r)] n.橡胶:橡皮:黑板擦 v.涂橡胶于…:〈美俚〉同“rubberneck” 网络橡皮擦:胶皮:橡皮轮胎 变形复数:rubbers:

  4. 网页中引用优酷视频去广告自动播放代码[xyytit]

    很多时候需要在网站中加入视频,可视频太大放自己服务器上太占空间,可以把视频上传到优酷网上,这样节省了空间,打开速度方面也会有不少提升.下面教大家如何引用自动播放的优酷视频.把下面的代码加在你网页适当位 ...

  5. 二叉树中的最大路径和 · Binary Tree Maximum Path Sum

    [抄题]: 给出一棵二叉树,寻找一条路径使其路径和最大,路径可以在任一节点中开始和结束(路径和为两个节点之间所在路径上的节点权值之和) [思维问题]: 不会写分合法 [一句话思路]: 用两次分治:ro ...

  6. 5.Javascript 原型链之原型对象、实例和构造函数三者之间的关系

    前言:用了这么久js,对于它的原型链一直有种模糊的不确切感,很不爽,隧解析之. 本文主要解决的问题有以下三个: (1)constructor 和 prototype 以及实例之间啥关系? (2)pro ...

  7. 面向对象设计模式纵横谈:Adapter 适配器模式(笔记记录)

    适配(转换)的概念无处不在 适配,即在不改变原有实现的基础上,将原先不兼容的接口转换为兼容的接口.生活中适配转换的例子太多了,也是设计模式里面比较容易理解的一个模式. 动机(Motivation) 在 ...

  8. loadrunner12.5-vugen回放脚本提示:URL=“http://www.testclass.net/js/scripts.js”的常规连接当前无套接字 (16 不足) 可用,是什么意思呢?怎么理解呢?

    会发生这个报错,是因为每个浏览器都有一个限制,检查哪个浏览器客户正在模拟, 通常只允许16个并发连接. 如果超过此超过接数,将显示该消息,通知您没有可用的连接. 而max connection的默认值 ...

  9. sdk接入

    文档 http://blog.csdn.net/chenjie19891104/article/details/42217281 视频: https://chuanke.baidu.com/v2869 ...

  10. div浮停在页面最上或最下

    div{ position:fixed; bottom:0px; // top:0px; z-index:999; } bottom:0px 浮停在最下面,top:0px 浮停在最上面:z-index ...