Linux进程间通信之信号
简 介 : 简介: 简介:
信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号;内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。
收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:
1. 类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
2. 忽略某个信号,对该信号不做任何处理,就象未发生过一样。
3. 对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。
信 号 的 基 本 术 语 信号的基本术语 信号的基本术语
- 实际执行信号的处理动作称为信号递达(Delivery)
- 信号从产生到递达之间的状态,称为信号未决(Pending)。
- 进程可以选择阻塞 (Block )某个信号。
- 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
进 程 的 生 命 周 期 进程的生命周期 进程的生命周期
当向目标进程发送一枚信号SIGXXX时,Linux内核收到了产生的信号,然后再目标进程的进程描述符里记录了一笔:收到了信号SIGXXX,但是还没有递送给目标进程的这一段时间里,信号处于挂起状态,被称为 未 决 信 号 未决信号 未决信号。内核将信号递送给进程,进程就会暂停当前的控制流,转而去执行信号处理函数,这就是一个信号的完整生命周期。
信 号 的 分 类 信号的分类 信号的分类
不可靠信号:信号值在[1,31]之间的所有信号,被称为不可靠信号;也称非实时信号。对于不可靠信号,内核不一定能递送给目标进程,信号可能会丢失。
可靠信号:在[SIGRTMIN, SIGRTMAX]之间的信号被称为可靠信号;也称为实时信号。可靠信号时为了解决不可靠信号的丢失问题而产生的,Linux内核保证可靠信号能够递送给目标进程而不会丢失。
不同类型信号的特性:
-
对于不可靠信号,内核用位图(即进程表表现中的一个软中断信号域,该域中每一位对应一个信号)来记录该信号是否处于挂起状态。如果收到某不可靠信号,内核发现已经存在该信号处于未决状态,就会简单地丢弃该信号。因此发送不可靠信号,信号可能会丢失,即内核递送给目标进程的次数,可能小于信号发送的次数。
-
对于可靠信号,内核内部有队列来维护,如果收到可靠信号,内核会将信号挂到相应的队列中,因此不会丢弃。严格说来,内核也设有上限,挂起信号的个数也不能无限制地增大,不然耗费内核资源,因此只能说,在一定范围之内,可靠信号不会被丢弃。
简单来说就是:不可靠信号在递达之前产生多次只计⼀次,而可靠信号在递达之前产生多次可以依次放在一个队列⾥
L i n u x 对 不 可 靠 信 号 的 具 体 实 现 Linux对不可靠信号的具体实现 Linux对不可靠信号的具体实现
对于不可靠信号(非实时信号),每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。
每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t 来存储 ,sigset_t称为信号集。
这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态.
sigsett 类型对于每种信号用一个 bit 表示 “ 有效 ” 或 “ 无效 ” 状态 , 至于这个类型内部如何存储这些 bit 则依赖于操作系统实现。
注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作
还 有 一 点 需 要 留 意 的 是 : 还有一点需要留意的是: 还有一点需要留意的是:主控制流和信号响应函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程
相关函数:
- signal函数
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
- kill函数
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
用来向任何进程或进程组发送任何信号
pid > 0:将信号sig发送到进程号为pid的进程
pid = 0:信号sig将发送给当前进程所属进程组里的所有进程
pid = -1:信号sig将发送给除了进程1和自身以外的所有进程
pid < -1:信号sig将发送给属于进程组-pid的所有进程
如果参数sig为0,将不发送信号
- pause函数
#include <unistd.h>
int pause(void);
pause函数使进程进入睡眠,直到收到一个信号
该调用总是返回-1,并设置错误代码为EINTR(接收到一个信号)
如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR, 所以pause只有出错的返回值。错误码EINTR表示“被信号中断”
- alarm函数
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能是设置一个定时器,当定时器计时到达时,将发出一个信号给进程
alarm安排内核为调用进程在指定的seconds秒后发出一个SIGALRM的信号
alarm后一次设定将取消前一次的设定
注意:alarm只设定为发送一次信号,如果要多次发送,就要多次使用alarm调用
- setitimer/getitimer函数
#include <sys/time.h>
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
setitimer用于设置定时器,getitimer用于获取定时器状态
该系统调用给进程提供了三个定时器,它们各自有其独有的计时域,当其中任何一个到达,就发送一个相应的信号给进程,并使得计时器重新开始
三个计时器由参数which指定:
TIMER_REAL:实际时间,计时到达发送SIGALRM信号
TIMER_VIRTUAL:进程执行时才计时,计时到达发送SIGVTALRM
TIMER_PROF:
- raise函数
#include <signal.h>
int raise( int signal );
raise函数用于给自己发送信号
未完。。。
参考: