信号

前言

信号是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。Linux信号可由如下条件产生:

  • 对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号。比如输入Ctrl+C通常会给进程发送一个中断信号。
  • 系统异常。比如浮点异常和非法内存段访问。
  • 系统状态变化。比如alarm定时器到期将引起SIGALRM信号。
  • 运行kill命令或调用kill函数。

服务器程序必须处理(或至少忽略)一些常见的信号,以免异常终止。

Linux信号概述

发送信号

Linux下,一个进程给其他进程发送信号的API是kill函数。其定义如下:

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid,int sig);

该函数把信号sig发送给目标进程:目标进程由pid参数指定,其可能的取值及含义如下所示:

pid参数含义
pid > 0信号发送给PID为pid的进程
pid = 0信号发送给本进程组内的其他进程
pid = -1信号发送给除init进程外的所有进程,但发送者需要拥有对目标进程发送信号的权限
pid < -1信号发送给组ID为-pid的进程组中的所有成员

该函数成功时返回0,失败则返回-1并设置errno。几种可能的errno如下表所示:

errno含义
EINVAL无效的信号
EPERM该进程没有权限发送信号给任何一个目标进程
EINVAL目标进程或进程组不存在

信号处理方式

目标进在收到信号时,需要定义一个接收函数来处理之。信号处理函数的原型如下:

#include <signal.h>
typedef void (*__sighandler_t) (int);

信号处理函数只带有一个整型参数,该参数用来指示信号类型。信号处理函数应该是可重入的,否则很容易引发一些竞态条件。所以在信号处理函数中严禁调用一些不安全的函数。
除了用户自定义信号处理函数外,bits/signum.h头文件中还定义了信号的两种其他处理方式——SIG_IGN和SIG_DEL:

#include<bits/signum.h>
#define SIG_DFL ((__sighandler_t) 0)
#define SIG_IGN ((__sighandler_t) 1)

SIG_IGN表示忽略目标信号,SIG_DFL表示使用信号的默认处理方式。信号的默认处理方式有如下几种:结束进程(Term)、忽略信号(Ign)、结束进程并生成核心转储文件(Core)、暂停进程(Stop),以及继续进程(Cont)。

Linux信号

Linux的可用信号都定义在bits/signnum.h头文件中,其中包括标准信号和POSIX实时信号。

中断系统调用

如果程序在执行处于阻塞状态的系统调用时接收到信号,并且我们为该信号设置了信号处理函数,则默认情况下系统调用将被中断,并且errno被设置为EINTR。我们可以使用sigaction函数为信号设置SA_RESTART标志以自动重启被该信号中断的系统调用。
对于默认行为是暂停进程的信号(比如SIGSTOP、SIGTTIN),如果我们没有为它们设置信号处理函数,则它们也可以中断某些系统调用(比如connect、epoll_wait)。POSIX没有规定这种行为,这是Linux独有的。

信号函数

signal系统调用

要为一个信号设置处理函数,可以使用下面的signal系统调用:

#include <signal.h>
_sighandler_t signal(int sig,_sighandler_t _handler)

sig参数指出要捕获的信号类型。_handler参数是_sighandler_t类型的函数指针,用于指定信号sig的处理函数。
signal函数成功时返回一个函数指针,该函数指针的类型也是_sighandler_t。这个返回值是前一次调用signal函数时传入的函数指针,或者是信号sig对应的默认处理函数指针SIG_DEF(如果是第一次调用signal的话)。
signal系统调用出错时返回SIG_ERR,并设置errno。

sigaction系统调用

设置信号处理函数的更健壮的接口时如下的系统调用:

#include <signal.h>
int sigaction(int sig,const struct sigaction *act,struct sigaction* oact);

sig参数指出要捕获的信号类型,act参数指定新的信号处理方式,oact参数则输出信号先前的处理方式(如果不为NULL的话)。act和oact都是sigaction结构体类型的指针,sigaction结构体描述了信号处理的细节。

信号集

信号集函数

Linux使用数据结构sigset_t来表示一组信号。其定义如下:

#include <bits/sigset.h>
#define _SIGSET_NWORDS (1024/(8*sizeof(unsigned long int)))
typedef struct
{
	unsigned long int __val[_SIGSET_NWORDS];
}__sigset_t;

由该定义可见,sigset_t实际上是一个长整型数组,数组的每个元素的每个位表示一个信号。这种定义方式和文件描述符集fd_set类似。Linux提供了如下一组函数来设置、修改、删除和查询信号集:

include <signal.h>
int sigemptyset(isigset_t* _set)	/*清空信号集*/
int sigflllset(sigset_t* _set)		/*在信号集中设置所有信号*/
int sigaddset (sigset_t* _set, int _signo)	/*将信号signo添加至信号集中*/
int sigdelset (sigset_t* set, int _signo)	/*将信号_signo 从信号集中删除*/
int sigismember (_const sigset_t* _set,int _signo〉/*测试_signo是否在信号集中*/

统一事件源

网络编程相关信号★★

资料

《Linux高性能服务器编程》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值