软件中断
。异步事件
的方法。SIG
开头。在头文件signal.h(其中include的bits/signum.h)中,信号名都被定义为正整数常量,不存在编号为0的信号。kill函数对信号编号0有特殊的应用。
很多条件可以产生信号:
- 用户按下某些终端键时:Ctrl+C、Ctrl+/、Ctrl+Z
- 硬件异常产生信号:除数为0、无效的内存引用
- 进程调用kill函数可将任意信号发送给另一个进程或进程组
- 当检测到某些软件条件已经发生,并应将其通知有关进程时产生信号。如:SIGURG(网络连接上传来带外数据)、SIGPipE(在管道的读进程已经终止后,一个进程写此管道)、SIGALRM(进程所设置的定时器超时)
信号是异步事件的经典实例。产生信号的事件对进程而言是随机出现的。进程不能简单地测试一个变量(如errno)来判断是否发生了一个信号,而是必须告诉内核“在此信号发生时,请执行下列操作”。
当某个信号出现时,可以告诉内核按下列3种方式之一进行处理,称之为信号的处理
- 忽略此信号。SIG_IGN。只有两种信号不能被忽略:SIGKILL和SIGSTOP。原因是:它们向内核和超级用户提供了使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(如非法内存引用或除以0),则进程的运行行为是未定义的。
- 捕捉信号。即通知内核在某种信号发生后,调用一个用户函数。
- 执行系统默认动作。对大多数信号的默认动作是终止该进程。
终止+core。大多数Unix系统调试程序都使用core文件检查进程终止时的状态。
在下列条件下不产生core文件:
- 进程是设置用户ID的,而且当前用户并非程序文件的所有者
- 进程是设置组ID的,而且当前用户并非程序文件的组所有者
- 用户没有写当前工作目录的权限
- 文件已存在,而且用户对该文件没有写权限
4个平台对各种signal的支持及默认处理方式
- SIGABRT。调用abort函数时产生此信号。
- SIGALRM。当用alarm函数设置的定时器超时时,产生此信号。
- SIGCHLD。在一个进程终止或停止时,该信号被送给其父进程。按系统默认,将忽略此信号。
- SIGFPE。表示算术运算异常,如除以0、浮点溢出等。
- SIGHUP。如果终端接口检测到一个连接断开,则将此信号送给与该终端相关的控制进程(会话首进程)。通常使用此信号通知守护进程再次读取它们的配置文件。选用此信号的理由是:守护进程不会有控制终端,通常决不会接收到这种信号。
- SIGILL。表示进程执行一条非法硬件指令。
- SIGINT。当用户按下中断键Ctrl+C时,终端驱动程序产生此信号并发送至前台进程组的每一个进程。
- SIGIO。指示一个异步I/O事件。
- SIGTERM。由kill命令发送的系统默认终止信号。
- SIGKILL。不能捕获或忽略。它向管理员提供了一红杀死任一进程的可靠方法。
- SIGPIPE。如果在管道的读进程已终止时写管道,则产生此信号。
- SIGQUIT。当用户在终端上按下退出键Ctrl+/时,终端驱动程序产生此信号并发送给前台进程组中的所有进程。此信号除了终止前台进程组(和SIGINT一样),同时产生一个core文件。
- SIGSEGV。指示进程进行了一次无效的内存引用。
- SIGTSTP。交互停止信号。当用户在终端上按下挂起键Ctrl+Z时,终端驱动程序产生此信号,并发送至前台进程组的所有进程。
- SIGSTOP。类似于交互停止信号(SIGTSTP),但它不能被捕获或忽略。
- SIGCONT。此作业控制信号发送给需要继续运行,但当前处于停止状态的进程。
- SIGTTIN。当一个后台进程组进程试图读其控制终端时,终端驱动程序产生此信号。下列情况例外:1. 读进程忽略或阻塞此信号;2. 读进程所属的进程组是孤儿进程组,此时读操作返回出错,errno设置为EIO
- SIGTTOU。当一个后台进程组进程试图写其控制终端时,终端驱动程序产生此信号。
- SIGURG。通知进程已经发生一个紧急情况。如带外数据到达。
- SIGUSR1、SIGUSR2。用户定义的信号,可用于应用程序。
#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);
Returns: PRevious disposition of signal (see following) if OK, SIG_ERR on error
typedef void Sigfunc(int);
Sigfunc* signal(int, Sigfunc*);
#define SIG_ERR (void (*)())-1
#define SIG_DFL (void (*)())0
#define SIG_IGN (void (*)())1
exec,程序启动
当exec执行一个程序时,所有信号都被设置为它们的默认动作,除非调用exec的进程忽略该信号(则继续保持忽略)。也就是说,exec函数将原先设置为要捕获的信号都更改为默认动作,其他保持不变。因为当exec一个新程序时,信号处理程序的地址很可能在新程序中已无意义。
fork,进程创建
当一个进程调用fork时,其子进程继承父进程的信号处理方式。因为信号处理程序的地址在子进程中是有意义的。
again:
if ((n = read(fd, buf, BUFFSIZE)) < 0) {
if (errno == EINTR)
goto again; /* just an interrupted system call */
/* handle other errors */
}
- 如果进程正在执行malloc,而在信号处理程序中又再次调用malloc,这时会?
- 如果进程正在执行getpwnam,这是将其结果存放在静态存储单元中的函数,而在信号处理程序中又再次调用getpwnam,这时会?
可重入的
,并被称为异步信号安全
的。不可重入的
,因为:
- 它们使用静态数据结构
- 它们调用malloc或free
- 它们是标准的I/O函数。标准I/O库的很多实现都以不可重入方式使用全局数据结构。
递送
了一个信号。在信号产生和递送之间的时间间隔内,称信号是未决
的。信号屏蔽字
,它规定了当前要阻塞递送到该进程的信号集。进程可以调用sigprocmask函数
来检测和更改其当前信号屏蔽字。sigpending函数
来判定哪些信号是设置为阻塞并处于未决状态的。排队
。除非支持POSIX.1实时扩展,否则大多数Unix并不对信号排队,而只递送一次。
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
Both return: 0 if OK, −1 on error
- pid > 0,发送给进程ID为pid的进程
- pid == 0,发送给与发送进程属于同一进程组的所有进程
- pid < 0,发送给其进程组ID等于pid绝对值,而且发送进程具有权限向其发送信号的所有进程
- pid == -1,发送给发送进程具有权限向它们发送信号的所有进程
关于发送信号的权限
- 超级用户可将信号发送给任一进程。
- 非超级用户,其基本规则是发
新闻热点
疑难解答