Linux信号机制
信号机制是进程间相互传递消息的一种方法,信号全称为软中断信号,信号是进程控制的一部分。从进程的描述符PCB中,也可以看到进程关于信号处理的身影。
/*
35. 信号处理
1) signal: 指向进程的信号描述符
2) sighand: 指向进程的信号处理程序描述符
*/
struct signal_struct *signal;
struct sighand_struct *sighand;
/*
3) blocked: 表示被阻塞信号的掩码
4) real_blocked: 表示临时掩码
*/
sigset_t blocked, real_blocked;
sigset_t saved_sigmask;
/*
5) pending: 存放私有挂起信号的数据结构
*/
struct sigpending pending;
/*
6) sas_ss_sp: 信号处理程序备用堆栈的地址
7) sas_ss_size: 表示堆栈的大小
*/
unsigned long sas_ss_sp;
size_t sas_ss_size;
/*
8) notifier
设备驱动程序常用notifier指向的函数来阻塞进程的某些信号
9) otifier_data
指的是notifier所指向的函数可能使用的数据。
10) otifier_mask
标识这些信号的位掩码
*/
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;
进程之间可以通过系统调用相互发送信号;内核也可以因为某种事件向用户进程发送某种信号,通知进程发生了某种事件,但是请注意,信号只是用来通知进程发生了事件,其本身不会携带任何信息。
可以从如下几个方面来理解信号:1、进程如何存储信号。2、进程如何感知信号。3、进程如何处理信号。
1.进程如何存储信号
在进程描述符中有一个未决(pending)信号集合(所谓的未决是指信号产生,但还未对信号做出处理决定),信号的注册其实就是指在这个未决(pending)信号集中标记对应的信号数值二进制位为1.
上面代码列出了进程中关于信号处理的所有字段
/*
5) pending: 存放私有挂起信号的数据结构
*/
struct sigpending pending;
struct sigpending pending;
就是用来做信号标记的,给一个进程发送的所有信号都存储在这个结构体中,那么它是如何标记的呢,我们可以对这个结构体进行展开。
struct sigpending {
struct list_head list;
sigset_t signal;
};
/* A `sigset_t' has a bit for each signal. */
# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;
pending
结构体包含2个字段struct list_head
和sigset_t
,其中sigset_t
这个结构体只有一个数组成员,在32位环境下,这个数组大小为32;在64位环境下,这个数组大小为16。个人理解这个数组大小标记了进程可以同时接收的最大信号数目。不管是32位环境还是64位环境,均只能最大容纳16个信号(32位环境下,需要两个数组元素表示64个不同的信号)。
现在我们就可以理解,当某一个进程发送一个信号给当前进程时,操作系统就会将该进程对应的pending
集合中表示相应信号的位图的二进制位中0改为1。位图只是用来标记有没有待处理的信号。
信号的分类
Linux操作系统有64中不同的信号,分为非可靠信号(1-32)和可靠信号(33-64)。二者的区别主要体现在注册方面。
非可靠信号
试图对一个进程发送一个非可靠信号时,若发现位图上对应的位为0,则置为1;若发现位图上对应的位已经为1,则直接返回。简单地说就是若信号还未注册,则注册一下,若已经注册,则什么都不做
可靠信号注册
当试图对一个进程发送一个可靠信号时,若发现位图上对应的位为0,则置为1,若发现位图上对应的位已经为1,对该位不进行操作但依旧在链表里加入一个待处理节点。也就是说,每次对进程发送一个可靠信号时,不管该进程之前是否收到过相同的信号,总是会在list_head
链表里加入待处理节点
未完待续。。。。