一linux 信号常识
1linux信号是用来进程间或进程与内核间通信的一种机制,用户可以注册对应的信号处理函数来处理相应的信号。
2linux信号分为实时信号和普通信号,两者之间的区别在于同种类型信号在排队时,实时信号允许有多个同种类型实时信号同时排队,而普通信号只允许同种类型信号只有一个在排队。
3用户可以对进程设置相应的信号阻塞掩码,处于该掩码中的信号,当进程接收到此信号时,不会进行处理,直到解除了对该信号的阻塞。
4对信号的默认处理操作通常有三种:terminal(杀死进程),dump(杀死进程,并产生core文件),ignore(忽略).
5执行信号处理通常发生在系统从内核态返回到用户态的时候。
6对于多线程的应用,线程组中的多个线程会共享同一个信号描述符和信号处理函数描述符,所以在一个线程中使用signal()来注册信号处理函数的话,对整个线程组的影响都是同样的。但是每个线程可以有自己的信号掩码和阻塞信号掩码,在共享的信号描述符中,存放着共享的信号队列,通常信号队列中的信号由线程组中一个不会阻塞该信号的进程来执行。另外如果向线程组发出一个致命的信号(执行动作为terminal或者dump),则会杀死整个线程组而不仅仅是执行该信号处理函数的线程。
7 一个fork的子进程继承父进程的信号掩码;信号掩码可以跨越execve。一个通过fork创建的子进程继承父进程的信号处理方式。当调用execve时,处理方式为handle的将被重置为default,处理方式为ignore的将继续保持ignore。
8一个信号可以生成(未决)并传递给整个线程组(如使用kill(2))或传递给指定线程(如,某些信号,像SIGSEGV和SIGFPE,作为某个线程特定机器指令的执行结果而生成是特定于线程的,如需指定线程则使用pthread_kill(3))。一个针对进程的信号也许被递送到任意一个没有阻塞当前信号的线程。如果没有阻塞该信号的线程多于一个,则内核随意挑选一个。一个线程可以用sigpending获取一个未决信号集。此集合将取进程和调用线程未决信号集的并集。一个fork初始化的子进程未决信号集合为空集。未决信号集可以跨越execve。
二信号的相关数据结构
进程描述符中存放着与信号相关的字段,pending指向一个struct sigpending的结构,该结构用来存放该进程私有的挂起的信号队列,signal指向一个信号描述符,该信号描述符中主要存放着线程组公用的挂起信号队列。sighand字段指向信号处理函数描述符,记录了对于各个信号的对应的处理方式。在多线程应用中,各个线程共享signal字段的信号描述符以及sighand字段的信号处理函数描述符。blocked字段指向的是被阻塞的信号的掩码。
1signal_struct数据结构
struct signal_struct {
atomic_t count;//信号描述符的计数器
atomic_t live;//线程组中活动线程的数目
task_t *curr_target;//接收信号的线程组中的最后一个进程
struct sigpendingshared_pending;//存放公共挂起信号的数据结构
int group_exit;
int group_exit_code;//线程组的终止代码
struct task_struct*group_exit_task;//杀死整个线程组时使用
int notify_count; //杀死整个线程组时使用
int group_stop_count; //停止整个线程组时使用
int stop_state;
:
:
pid_t pgrp;//存放组领头进程
pid_t tty_old_pgrp;
pid_t session;//存放会话领头进程
int leader;
:
struct rlimit rlim[RLIM_NLIMITS];//进程的资源限制数组
};
2sigpending,sigqueue,siginfo_t
struct sigpending {
struct list_head list;//挂起信号的链表头
sigset_t signal;//指定了挂起信号的位掩码
};
struct sigqueue {
struct list_head list;//链接到挂起信号链表上去
spinlock_t *lock;//指向与挂起信号相对应的信号处理程序描述符中的siglock字段
int flags;//sigqueue数据结构的标志
siginfo_t info;//描述产生信号的操作
struct user_struct *user;
};
typedef struct siginfo {
int si_signo;//信号编号
int si_errno;//触发该信号时的错误码
int si_code;//发送信号者的代码,比如SI_USER(kill或raise发出),SI_KERNEL(内核函数发出),SI_TKILL(tkill或者tgkill发出)等
//一个与信号类型相关的联合体,sig_kill情况下存放发送者的pid和uid,段错误情况下,存放引发段错误的地址
union {
int _pad[SI_PAD_SIZE];
struct {
pid_t _pid;
__ARCH_SI_UID_T _uid;
} _kill;
struct {
timer_t _tid;
int _overrun;
char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
sigval_t _sigval;
int _sys_private;
} _timer;
struct {
pid_t _pid;
__ARCH_SI_UID_T _uid;
sigval_t _sigval;
} _rt;
struct {
pid_t _pid;
__ARCH_SI_UID_T _uid;
int _status;
clock_t _utime;
clock_t _stime;
} _sigchld;
struct {
void __user *_addr;
#ifdef __ARCH_SI_TRAPNO
int _trapno;
#endif
} _sigfault;
struct {
__ARCH_SI_BAND_T _band;
int _fd;
} _sigpoll;
} _sifields;
} siginfo_t;
3sighand_struct 及k_sigaction
struct sighand_struct {
atomic_t count;//信号处理描述符的引用计数
struct k_sigactionaction[_NSIG];//对应的信号处理函数
spinlock_t siglock;//保护描述符的自旋锁
};
struct k_sigaction {
struct sigaction sa;
};
struct sigaction {
union {//指定要执行操作的类型,可以指向信号处理函数,也可以使SIG_DEF,SIG_IGN
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int, struct siginfo *, void *);
} _u;
sigset_t sa_mask;//指定运行信号处理函数时要屏蔽的信号
unsigned long sa_flags;//标志,指定如何处理信号
void (*sa_restorer)(void);
};