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

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

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. python作业(第十一周)基于RabbitMQ rpc实现的主机管理

    作业需求: 可以对指定机器异步的执行多个命令 例子: >>:run "df -h" --hosts 192.168.3.55 10.4.3.4 task id: 453 ...

  2. nginx反向代理部署与演示(二)

    我们把LB01作为负载均衡器,WEB01和WEB02作为两台web服务器.   WEB01与WEB02虚拟主机配置如下:   我们修改nginx下的conf/nginx.conf文件,在http{}中 ...

  3. vue2.0 tab切换几种方式

    第一种 比较灵活简单的方式(切换改变部分的内容在组件中比较方便操作) <template> <div id="app"> <ul> <li ...

  4. linux bluez

    Linux下开放的蓝牙协议栈主要包括IBM公司的BlueDrekar,Nokia公司的Affix, Axis公司的OpenBT和官方协议栈BlueZ.我们主要对Bluez进行探讨. BlueZ基础代码 ...

  5. Debian8 下面 muduo库编译与使用

    其实<Linux 多线程服务端编程>已经写得很详细 但是考虑到代码版本的更新和操作系统的不同 可能部分位置会有些许出入 这里做个记录 方便以后学习运行 我使用的虚拟 安装的是debian系 ...

  6. 启动多个eclipse 时,因为一个另一个启动报错,

    启动多个eclipse 时,因为一个另一个启动报错, 原因: 可能是 有一个 eclipse  中 的 tomcat  配置出错:preference中  tomcat 配置  context dec ...

  7. 百度词汇检索,计算PMI值

    '''词汇检索百度返回值,并且计算PMI值的类''' from bs4 import BeautifulSoup import requests import re import pandas as ...

  8. C语言基础课第四次作业

    1.实验代码      7-2 打印九九口诀表 (15 分) #include<stdio.h> #include<math.h> int main(void){ int a, ...

  9. String [] args是干什么的

         我相信应该有不少人都疑惑,main后面的这个String [] args是干什么的呢?今天,巩固就为你们解密.      这是干什么的呢?先给大家一个简单定义(本人比较讨厌上来就举例子,因为 ...

  10. 2018.10.18 NOIP训练 01矩阵(组合数学)

    传送门 组合数学好题. 题目要求输出的结果成功把概率转化成了种类数. 本来可以枚举统计最小值为iii时的概率. 现在只需要统计最小值为iii时的方案数,每一行有不少于iii个1的方案数. 显然一行选i ...