其他章节
使用pthread库进行多线程编程1 - UNIX环境高级编程第11章读书笔记
使用pthread库进行多线程编程2 - UNIX高级环境编程第12章读书笔记
10 Signals
1 Introduction & Concepts
Signals是一种软件中断,通知程序某种事件的发生。常见的Signal有SIGABRT(当进程调用abort函数的时候自动发送), SIGALRM(当timer被触发的时候自动发送),等等。
下面的情况可以产生Signal:
1. 按下CTRL+C产生SIGINT
2. 硬件中断,如除0,非法内存访问(SIGSEV)等等
3. Kill函数可以对进程发送Signal
4. Kill命令。实际上是对Kill函数的一个包装
5. 软件中断。如当Alarm Clock超时(SIGURG),当Reader中止之后又向管道写数据(SIGPIPE),等等
当Signal发生的时候,可以有三种选择(称之为Disposition of the signal或者Action associated with a signal)
1. 忽略Signal。大部分Signal都可以Ignore,除了SIGKILL和SIGSTOP,这是为了提供一个确定的方法来Stop或者Kill一个Process。此外,如果我们忽略部分硬件异常产生的Signal,进程的行为未定义。
2. 捕捉Signal。可以让内核来调用我们所指定的函数。SIGKILL和SIGSTOP无法捕捉。
3. 执行缺省行为。如果不做任何处理,则执行缺省动作。大部分Signal的缺省行为都是中止进程。
部分Signal的缺省行为不仅中止进程,同时还会产生core dump,也就是生成一个名为core的文件,其中保存了退出时进程内存的镜像,可以用来调试。在下面情况,不会生成core文件:
1. 当前进程不属于当前user
2. 当前进程不属于当前group
3. 用户在当前目录下无写权限
4. Core文件已存在,用户无写权限
5. 文件过大,超过RLIMIT_CORE
2 Signals
Signal
| Description
|
SIGABRT | 由调用abort函数产生,进程非正常退出 |
SIGALRM | 用alarm函数设置的timer超时或setitimer函数设置的interval timer超时 |
SIGBUS | 某种特定的硬件异常,通常由内存访问引起 |
SIGCANCEL | 由Solaris Thread Library内部使用,通常不会使用 |
SIGCHLD | 进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略 |
SIGCONT | 当被stop的进程恢复运行的时候,自动发送 |
SIGEMT | 和实现相关的硬件异常 |
SIGFPE | 数学相关的异常,如被0除,浮点溢出,等等 |
SIGFREEZE | Solaris专用,Hiberate或者Suspended时候发送 |
SIGHUP | 发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送 |
SIGILL | 非法指令异常 |
SIGINFO | BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程 |
SIGINT | 由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程 |
SIGIO | 异步IO事件 |
SIGIOT | 实现相关的硬件异常,一般对应SIGABRT |
SIGKILL | 无法处理和忽略。中止某个进程 |
SIGLWP | 由Solaris Thread Libray内部使用 |
SIGPIPE | 在reader中止之后写Pipe的时候发送 |
SIGPOLL | 当某个事件发送给Pollable Device的时候发送 |
SIGPROF | Setitimer指定的Profiling Interval Timer所产生 |
SIGPWR | 和系统相关。和UPS相关。 |
SIGQUIT | 输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程 |
SIGSEGV | 非法内存访问 |
SIGSTKFLT | Linux专用,数学协处理器的栈异常 |
SIGSTOP | 中止进程。无法处理和忽略。 |
SIGSYS | 非法系统调用 |
SIGTERM | 请求中止进程,kill命令缺省发送 |
SIGTHAW | Solaris专用,从Suspend恢复时候发送 |
SIGTRAP | 实现相关的硬件异常。一般是调试异常 |
SIGTSTP | Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程 |
SIGTTIN | 当Background Group的进程尝试读取Terminal的时候发送 |
SIGTTOU | 当Background Group的进程尝试写Terminal的时候发送 |
SIGURG | 当out-of-band data接收的时候可能发送 |
SIGUSR1 | 用户自定义signal 1 |
SIGUSR2 | 用户自定义signal 2 |
SIGVTALRM | setitimer函数设置的Virtual Interval Timer超时的时候 |
SIGWAITING | Solaris Thread Library内部实现专用 |
SIGWINCH | 当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程 |
SIGXCPU | 当CPU时间限制超时的时候 |
SIGXFSZ | 进程超过文件大小限制 |
SIGXRES | Solaris专用,进程超过资源限制的时候发送 |
3 Signal Function
#include <signal.h> Void (*signal(int signo, void (*func)(int)))(int); 返回之前的signal处理函数,错误返回SIG_ERR |
1. Signo:signal的名称
2. Func:函数地址,或者是SIG_IGN(忽略Signal)或SIG_DFL(缺省行为)。原型为:void (*)(int)。不过很多UNIX的实现也会传入一些和实现相关的参数
如果用exec创建子进程,那么子进程的所有Signal状态是缺省或者忽略:
1. 如果父进程忽略了某个Signal,那么父进程用exec创建子进程时子进程的这个Signal的状态也是忽略
2. 如果在父进程的某个Signal注册了Signal处理函数的话,那么这个子进程的该Signal的状态会恢复成缺省状态,因为注册的函数地址肯定在另外一个进程中无法调用。
如果用fork创建子进程,那么子进程会继承所有父进程的Signal状态。
4 Unreliable Signals
早期的UNIX系统的Signal是不稳定的:
1. Signal可能会Lost
2. 在signal处理函数中,需要重复注册signal,否则下次收不到signal
3. 不支持pending signal
5 Interrupted System Calls
1. 早期UNIX中,一个进程在调用系统调用被阻赛的时候, 有可能被一个Signal中断。此时系统调用返回错误并且errno被设置为EINTR。
2. 系统调用也被分为两类:slow和一般。slow系统调用是那些可能永远block的系统调用(disk IO是例外,一般情况下只会暂时Block,但也归入Slow一类)
3. 为了处理这个问题,在早期UNIX系统中对于这些系统调用需要作额外的检查和判断,如果错误并且errno=EINTR,需要重新调用
4. Free BSD 4.2对于部分系统调用ioctl, read, readv, write, writev, wait实现了自动重新恢复功能,也就是当函数被中断的时候会自动恢复到原来的执行情况
5. Free BSD 5.2.1, Linux 2.4.22, Mac OS X 10.3支持此功能,但POSIX.1不作强制要求
6 Reentrant Functions
在Signal处理函数中,有可能在执行某个函数的时候,signal产生并调用了signal处理函数,然后再signal处理函数中再次调用了此函数,如果此函数调用了malloc/free,或者使用了static变量等技巧,会造成此函数不可再次被调用,此函数就是不可重入,反之就是可以重入的。UNIX标准定义了一个表,表中的函数都是可重入的。
7 Reliable Signal Terminology and Semantics
1. 在一个Signal被Generate之后,而被Deliver之前,该Signal称为正处于pending状态
2. 进程可以Block某个Signal。如果某个被Block的signal产生的话,该signal一直会处于pending状态,直到进程
a. Unblock这个Signal
b. 改变Signal的状态为Ignore
3. 如果一个被Block的Signal产生多次,POSIX.1允许deliver该signal一次或者多次
4. 每个进程有一个signal mask,表示那些signal被Block,那些被允许
5. sigset_t用来表示多种signal的集合
8 kill and raise Functions
Kill函数对某个进程发送signal,而raise对本进程发送signal: