文章目录
什么是信号
信号(signal)是一种XPC通信方式。
signal是一个4字节的无符号整形数字,在iOS/OSX中定义了31个已知的信号;
在Unix系统中,crash仅仅是singal触发的一个行为。signal的用途/产生包括但不限于:
- 显示调用kill,killpg触发signal
- 改变子进程的状态
- 致命性中断
- job控制
- timer过期
- 各种通知,如cpu resource limit或file size limit等
signal会导致以下几种行为(action):
- Terminate 杀死进程
- Dump core 杀死进程并创建一个core file
- Stop 暂停进程suspend
- Continue 恢复进程resume
- Ignore 忽略/丢弃该信号
每个signal号都有默认的action,可以通过sigaction() 系统调用来修改signal actions,为SIG_DFL(use the default action),或者修改为SIG_IGN(ignore the signal),或者指定signal handler function(捕获signal)
一般的异常捕获工具会基于上述方式来修改signal的actions或执行signal handler,从而达到异常捕获的目的。我们也可以通过修改signal actions来达到让应用直接忽略某个信号,而不发生异常退出。但是这也有个别例外情况,比如SIGKILL,SIGSTOP 无法被捕获或忽略。
信号如何产生
使用过kill函数来达到向某个进程pid发送signal:
int kill(pid_t pid, int sig);
pid>0,则sig会发送给对应process id的进程;
pid=0,则会发给所有和sender具有相同group id的进程;
pid=-1,如果具有超级权限,则sig会发给除了系统进程以及sender进程外的所有进程;否则sig会发给除了sender以外的所有当前user下的进程;
pid<-1,效果等同killpg
使用pthread_kill向某个线程发送signal:
int pthread_kill(pthread_t thread, int sig);
信号与多线程
signal的分发实现是基于per-thread signal masks的,它确保了多线程情况下信号依旧能被顺序处理,即线程a在处理信号时,其它线程的信号会被阻塞。系统提供了pthread_sigmask()调用来修改signal mask;默认情况下子线程的masks是和创建它的线程是一致的。
如果一个信号是由于trap,illegal instruction或arithmetic exception则该信号会被分发给对应触发的线程(主要是synchronous signal);否则这些信号会被发给第一个不阻塞该signal的线程;
注意:SIGKILL,SIGSTOP,SIGTERM会影响整个进程;
当发送信号时,接收者signal_handler会收到siginfo_t,这个结构存储了详细的信号信息;siginfo_t结构如下
typedef struct __siginfo {
int si_signo; /* signal number */
int si_errno; /* errno association */
int si_code; /* signal code */
pid_t si_pid; /* sending process */
uid_t si_uid; /* sender's ruid */
int si_status; /*