信号是与一定的进程相联系的,而建立其信号和进程的对应关系,这就是信号的安装登记。

Linux主要有两个函数实现信号的安装登记:signal和sigaction。其中signal在系统调用的基础上实现,是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32个非实时信号的安装;而sigaction是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与sigqueue系统调用配合使用。当然,sigaction同样支持非实时信号的安装,sigaction优于signal主要体现在支持信号带有参数。

对于应用程序自行处理的信号来说,信号的生命周期要经过信号的安装登记、信号集操作、信号的发送和信号的处理四个阶段。

  1)信号的安装登记指的是在应用程序中,安装对此信号的处理方法。
  2)信号集操作的作用是用于对指定的一个或多个信号进行信号屏蔽。
  3)信号的发送指的是发送信号,可以通过硬件(如在终端上按下Ctrl-C)发送的信号和软件(如通过kill函数)发送的信号。
  4)信号的处理指的是操作系统对接收信号进程的处理,处理方法是先检查信号集操作函数是否对此信号进行屏蔽,如果没有屏蔽,操作系统将按信号安装函数中登记注册的处理函数完成对此进程的处理。

1.   signal函数

(1)函数说明

在signal函数中,有两个形参,分别代表需要处理的信号编号值和处理信号函数的指针。它主要是用于前32种非实时信号的处理,不支持信号的传递信息。但是由于使用简单,易于理解,因此在许多场合被程序员使用。

对于Unix系统来说,使用signal函数时,自定义处理信号函数执行一次后失效,对该信号的处理回到默认处理方式。下面以一个例子进行说明,例如一程序中使用signal(SIGQUIT, my_func)函数调用,其中my_func是自定义函数。应用进程收到SIGQUIT信号时,会跳转到自定义处理信号函数my_func处执行,执行后信号注册函数my_func失效,对SIGQUIT信号的处理回到操作系统的默认处理方式,当应用进程再次收到SIGQUIT信号时,会按操作系统默认的处理方式进行处理(即不再执行my_func处理函数)。而在Linux系统中,signal函数已被改写,由sigaction函数封装实现,则不存在上述问题。

(2)signal函数原型

signal(设置信号处理方式)

所需头文件

#include <signal.h>

函数说明

设置信号处理方式。signal()会依参数signum指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行

函数原型

void (*signal(int signum,void(* handler)(int)))(int)

函数传入值

signum

指定信号编号

handle

SIG_IGN:忽略参数signum指定的信号

SIG_DFL:将参数signum指定的信号重设为核心预设的信号处理方式,即采用系统默认方式处理信号

自定义信号函数处理指针

函数返回值

成功

返回先前的信号处理函数指针

出错

SIG_ERR(-1)

附加说明

在Unix环境中,在信号发生跳转到自定的handler处理函数执行后,系统会自动将此处理函数换回原来系统预设的处理方式,如果要改变此情形请改用sigaction函数。在Linux环境中不存在此问题

signal函数原型比较复杂,如果使用下面的typedef,则可使其简化。

typedef void sign(int);

sign *signal(int, handler *);

可见,该函数原型首先整体指向一个无返回值带一个整型参数的函数指针,也就是信号的原始配置函数。接着该原型又带有两个参数,其中的第二个参数可以是用户自定义的信号处理函数的函数指针。对这个函数格式可以不理解,但需要学会模仿使用。

(3) signal函数使用实例

该示例表明了如何使用signal函数进行安装登记信号处理函数。当该信号发生时,登记的信号处理函数会捕捉到相应的信号,并做出给定的处理。这里,my_func就是信号处理的函数指针。读者还可以将my_func改为SIG_IGN或SIG_DFL查看运行结果。

signal.c源代码如下:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h> /*自定义信号处理函数*/
void my_func(int sign_no)
{
if(sign_no==SIGINT)
printf("I have get SIGINT\n");
else if(sign_no==SIGQUIT)
printf("I have get SIGQUIT\n");
} int main()
{
printf("Waiting for signal SIGINT or SIGQUIT \n ");
/*发出相应的信号,并跳转到信号处理函数处*/
signal(SIGINT, my_func);
signal(SIGQUIT, my_func);
pause();
exit();
} 编译
gcc signal.c –o signal。
执行
./signal,
执行结果如下:
Waiting for signal SIGINT or SIGQUIT
I have get SIGINT /*按下Ctrl+C,操作系统就会向进程发送SIGINT信号*/
I have get SIGQUIT /*按下Ctrl-\(退出),操作系统就会向进程发送SIGQUIT信号*/

2.   sigaction函数

(1)sigaction函数原型

sigaction函数用来查询和设置信号处理方式,它是用来替换早期的signal函数。sigaction函数原型及说明如下:

sigaction(查询和设置信号处理方式)

所需头文件

#include <signal.h>

函数说明

sigaction()会依参数signum指定的信号编号来设置该信号的处理函数

函数原型

int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact)

函数传入值

signum

可以指定SIGKILL和SIGSTOP以外的所有信号

act

参数结构sigaction定义如下

struct sigaction

{

void (*sa_handler) (int);

void  (*sa_sigaction)(int, siginfo_t *, void *);

sigset_t sa_mask;

int sa_flags;

void (*sa_restorer) (void);

}

①    sa_handler:此参数和signal()的参数handler相同,此参数主要用来对信号旧的安装函数signal()处理形式的支持

②    sa_sigaction:新的信号安装机制,处理函数被调用的时候,不但可以得到信号编号,而且可以获悉被调用的原因以及产生问题的上下文的相关信息。

③    sa_mask:用来设置在处理该信号时暂时将sa_mask指定的信号搁置

④    sa_restorer: 此参数没有使用

⑤    sa_flags:用来设置信号处理的其他相关操作,下列的数值可用。可用OR 运算(|)组合

Ÿ   A_NOCLDSTOP:如果参数signum为SIGCHLD,则当子进程暂停时并不会通知父进程

Ÿ   SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前,将此信号处理方式改为系统预设的方式

Ÿ   SA_RESTART:被信号中断的系统调用会自行重启

Ÿ   SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来

Ÿ   SA_SIGINFO:信号处理函数是带有三个参数的sa_sigaction

oldact

如果参数oldact不是NULL指针,则原来的信号处理方式会由此结构sigaction返回

函数返回值

成功:0

出错:-1,错误原因存于error中

附加说明

信号处理安装的新旧两种机制:

①     使用旧的处理机制:struct sigaction act;  act.sa_handler=handler_old;

②     使用新的处理机制:struct sigaction act; act.sa_sigaction=handler_new;

并设置sa_flags的SA_SIGINFO位

错误代码

EINVAL:参数signum不合法,或是企图拦截SIGKILL/SIGSTOP信号

EFAULT:参数act,oldact指针地址无法存取

EINTR:此调用被中断

(2)sigaction函数使用实例

sigaction.c源代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h> void new_op(int, siginfo_t *, void *);
int main(int argc,char**argv)
{
struct sigaction act;
int sig; sig=atoi(argv[]);
sigemptyset(&act.sa_mask);
act.sa_flags=SA_SIGINFO;
act.sa_sigaction=new_op; if(sigaction(sig,&act,NULL) < )
{
perror("install sigal error");
return - ;
} while()
{
sleep();
printf("wait for the signal\n");
}
return ;
} void new_op(int signum,siginfo_t *info,void *myact)
{
printf("receive signal %d\n", signum);
sleep();
}
编译
gcc sigaction.c -o sigaction。
执行
./sigaction 2,
执行结果如下:
wait for the signal
receive signal 2 /*按下Ctrl+C */
exit /*按下Ctrl-\ */

3.   信号集操作函数

由于有时需要把多个信号当作一个集合进行处理,这样信号集就产生了,信号集用来描述一类信号的集合,Linux所支持的信号可以全部或部分的出现在信号集中。信号集操作函数最常用的地方就是用于信号屏蔽。比如有时候希望某个进程正确执行,而不想进程受到一些信号的影响,此时就需要用到信号集操作函数完成对这些信号的屏蔽。

信号集操作函数按照功能和使用顺序分为三类,分别为创建信号集函数,设置信号屏蔽位函数和查询被搁置(未决)的信号函数。创建信号集函数只是创建一个信号的集合,设置信号屏蔽位函数对指定信号集中的信号进行屏蔽,查询被搁置的信号函数是用来查询当前“未决”的信号集。信号集函数组并不能完成信号的安装登记工作,信号的安装登记需要通过sigaction函数或signal函数来完成。

查询被搁置的信号是信号处理的后续步骤,但不是必需的。由于有时进程在某时间段内要求阻塞一些信号,程序完成特定工作后解除对该信号阻塞,这个时间段内被阻塞的信号称为“未决”信号。这些信号已经产生,但没有被处理,sigpending函数用来检测进程的这些“未决”信号,并进一步决定对它们做何种处理(包括不处理)。

(1)    创建信号集函数

创建信号集函数有如下5个:

①    sigemptyset:初始化信号集合为空。

②    sigfillset:把所有信号加入到集合中,信号集中将包含Linux支持的64种信号。

③    sigaddset:将指定信号加入到信号集合中去。

④    sigdelset:将指定信号从信号集中删去。

⑤    sigismember:查询指定信号是否在信号集合之中。

创建信号集合函数原型

所需头文件

#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(sigset_t *set,int signum)

函数传入值

set:信号集

signum:指定信号值

函数返回值

成功:0(sigismember函数例外,成功返回1,失败返回 0)

出错:-1,错误原因存于error中

(2)    设置信号屏蔽位函数

每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。调用函数sigprocmask可设定信号集内的信号阻塞或不阻塞。其函数原型及说明如下:

sigprocmask(设置信号屏蔽位)

所需头文件

#include <signal.h>

函数原型

int sigprocmask(int how,const sigset_t *set,sigset_t *oset)

函数传入值

how(决定函数的操作方式)

SIG_BLOCK:增加一个信号集合到当前进程的阻塞集合之中

SIG_UNBLOCK:从当前的阻塞集合之中删除一个信号集合

SIG_SETMASK:将当前的信号集合设置为信号阻塞集合

set:指定信号集

oset:信号屏蔽字

函数返回值

成功:0

出错:-1,错误原因存于error中

(3)    查询被搁置(未决)信号函数

sigpending函数用来查询“未决”信号。其函数原型及说明如下:

sigpending(查询未决信号)

所需头文件

#include <signal.h>

函数说明

将被搁置的信号集合由参数set指针返回

函数原型

int sigpending(sigset_t *set)

函数传入值

set:要检测信号集

函数返回值

成功:0

出错:-1,错误原因存于error中

错误代码

EFAULT:参数set指针地址无法存取

EINTR:此调用被中断

(4)    对信号集操作函数的使用方法

对信号集操作函数的使用方法和顺序如下:

①      使用signal或sigaction函数安装和登记信号的处理。

②      使用sigemptyset等定义信号集函数完成对信号集的定义。

③      使用sigprocmask函数设置信号屏蔽位。

④      使用sigpending函数检测未决信号,非必需步骤。

(5)    信号集操作函数使用实例

该实例首先使用sigaction函数对SIGINT信号进行安装登记,安装登记使用了新旧两种机制,其中#if 0进行注释掉的部分为信号安装的新机制。接着程序把SIGQUIT、SIGINT两个信号加入信号集,并把该信号集设为阻塞状态。程序开始睡眠30秒,此时用户按下Ctrl+C,程序将测试到此未决信号(SIGINT);随后程序再睡眠30秒后对SIGINT信号解除阻塞,此时将处理SIGINT登记的信号函数my_func。最后可以用SIGQUIT(Ctrl+\)信号结束进程执行。

sigset.c源代码如下:

#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h> /*自定义的信号处理函数*/
#if 0
void my_funcnew(int signum, siginfo_t *info,void *myact)
#endif void my_func(int signum)
{
printf("If you want to quit,please try SIGQUIT\n");
} int main()
{
sigset_t set, pendset;
struct sigaction action1,action2; /*设置信号处理方式*/
sigemptyset(&action1.sa_mask); #if 0 /*信号新的安装机制*/
action1.sa_flags= SA_SIGINFO;
action1.sa_sigaction=my_funcnew;
#endif /*信号旧的安装机制*/
action1.sa_flags= ;
action1.sa_handler=my_func;
sigaction(SIGINT,&action1,NULL); /*初始化信号集为空*/
if(sigemptyset(&set)<)
{
perror("sigemptyset");
return - ;
}
/*将相应的信号加入信号集*/
if(sigaddset(&set,SIGQUIT)<)
{
perror("sigaddset");
return - ;
} if(sigaddset(&set,SIGINT)<)
{
perror("sigaddset");
return - ;
}
/*设置信号集屏蔽字*/
if(sigprocmask(SIG_BLOCK,&set,NULL)<)
{
perror("sigprocmask");
return - ;
}
else
{
printf("blocked\n");
}
/*测试信号是否加入该信号集*/
if(sigismember(&set,SIGINT)){
printf("SIGINT in set\n") ;
}
sleep( ) ;
/*测试未决信号*/
if ( sigpending(&pendset) < )
{
perror("get pending mask error");
} if(sigismember(&pendset, SIGINT) )
{
printf("signal SIGINT is pending\n");
}
sleep() ;
if(sigprocmask(SIG_UNBLOCK,&set,NULL)<)
{
perror("sigprocmask");
return - ;
}
else
printf("unblock\n"); while()
{
sleep() ;
}
return ;
}
编译 gcc sigset.c -o sigset。

执行 ./sigset,执行结果如下:

blocked
SIGINT in set /*按下Ctrl+C */
signal SIGINT is pending
If you want to quit,please try SIGQUIT /*按下Ctrl+C */
退出

signal, sigaction,信号集合操作的更多相关文章

  1. 信号 signal sigaction补充

    目前linux中的signal()是通过sigation()函数实现的. 由signal()安装的实时信号支持排队,同样不会丢失. 先看signal 和 sigaction 的区别: 关键是 stru ...

  2. linux系统编程之信号(五):信号集操作函数,信号阻塞与未决

    一,信号集及相关操作函数 信号集被定义为一种数据类型: typedef struct { unsigned long sig[_NSIG_WORDS]: } sigset_t 信号集用来描述信号的集合 ...

  3. linux 进程信号集合 sigset_t

    sigset_t 号集及信号集操作函数:信号集被定义为一种数据类型: typedef struct { unsigned long sig[_NSIG_WORDS]: } sigset_t 信号集用来 ...

  4. linux 进程信号集合 sigset_t -(转自linengeir的专栏)

    sigset_t 号集及信号集操作函数:信号集被定义为一种数据类型: typedef struct { unsigned long sig[_NSIG_WORDS]: } sigset_t 信号集用来 ...

  5. [学习笔记&教程] 信号, 集合, 多项式, 以及各种卷积性变换 (FFT,NTT,FWT,FMT)

    目录 信号, 集合, 多项式, 以及卷积性变换 卷积 卷积性变换 傅里叶变换与信号 引入: 信号分析 变换的基础: 复数 傅里叶变换 离散傅里叶变换 FFT 与多项式 \(n\) 次单位复根 消去引理 ...

  6. 函数式Android编程(II):Kotlin语言的集合操作

    原文标题:Functional Android (II): Collection operations in Kotlin 原文链接:http://antonioleiva.com/collectio ...

  7. JAVASE02-Unit05: 集合操作 —— 查找表

    Unit05: 集合操作 -- 查找表 使用该类测试自定义元素的集合排序 package day05; /** * 使用该类测试自定义元素的集合排序 * @author adminitartor * ...

  8. JAVASE02-Unit04: 集合框架 、 集合操作 —— 线性表

    Unit04: 集合框架 . 集合操作 -- 线性表 操作集合元素相关方法 package day04; import java.util.ArrayList; import java.util.Co ...

  9. oracle之集合操作函数---minus、union、intersect

    集合操作符专门用于合并多条select语句的结果,包括:UNION,UNION ALL,INTERSECT,MINUS.当使用集合操作函数时,需保证数据集的字段数据类型和数目一致. 使用集合操作符需要 ...

随机推荐

  1. 【刷题】洛谷 P3809 【模板】后缀排序

    题目背景 这是一道模板题. 题目描述 读入一个长度为 \(n\) 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序从小到大排序,然后按顺序输出后缀的第一个字符在原串中的位置. ...

  2. 洛谷 P4735 最大异或和 解题报告

    P4735 最大异或和 题目描述 给定一个非负整数序列\(\{a\}\),初始长度为\(N\). 有\(M\)个操作,有以下两种操作类型: A x:添加操作,表示在序列末尾添加一个数\(x\),序列的 ...

  3. Communications link failure

    针对数据库Communications link failure的错误,可以理解为有两种策略解决: 策略1(推荐):     数据池配置 <property name="minEvic ...

  4. dubbo介绍以及创建

    1.什么是dubbo? DUBBO是一个分布式服务框架(关于框架,其实就是配置文件加java代码),致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架,每天为2 ...

  5. kubernetes--应用程序健康检查

    版权声明:本文属于原创,欢迎转载,转载请保留出处:http://blog.csdn.net/liyingke112 http://blog.csdn.net/liyingke112/article/d ...

  6. Difference between List View and DataGrid in WPF

    Well, in WPF the difference between ListView and DataGrid is just one. Editing. You need editing use ...

  7. 增强学习Reinforcement Learning经典算法梳理3:TD方法

    转自:http://blog.csdn.net/songrotek/article/details/51382759 博客地址:http://blog.csdn.net/songrotek/artic ...

  8. LightOJ 1151 - Snakes and Ladders 高斯消元+概率DP

    首先来个期望的论文,讲的非常好,里面也提到了使用线性方程组求解,尤其适用于有向图的期望问题. 算法合集之<浅析竞赛中一类数学期望问题的解决方法> http://www.lightoj.co ...

  9. opencv在property panel中新建一行

    是用cv2.QT_NEW_BUTTONBAR和button type通过竖线结合可以在创建一行,如下 cv2.createButton("CV_RADIOBOX2", redraw ...

  10. 【洛谷 P1129】 [ZJOI2007]矩阵游戏 (二分图匹配)

    题目链接 看到题目肯定首先会想到搜索. 然鹅数据范围\(n<=200\)这么大(其实也不算太大),肯定是不行的. 如果\((i,j)\)是\(1\),从\(i\)向\(j\)连一条边,表示第\( ...