第10章 信号
- 信号是软件中断
- 信号提供了一种处理异步事件的方法
10.2 信号概念
- 信号都以SIG开头
- 信号名都被定义为正整数常量,不存在编号为0的信号,kill函数对信号编号0有特殊的应用,这种信号称为空信号
- 很多条件可以产生信号
- 当用户按某些终端键,引发终端产生的信号
- 硬件产生的信号:除数为0,无效内存引用
- 进程调用kill可将任意信号发送给另一个进程或进程组
- 用户可调用kill将信号发送给其他进程
- 当检测到某种软件条件已经发生,并将其通知有关的进程时也产生信号
- 信号是异步事件的经典案例。产生信号的的事件对进程来说是随机出现的
- 在某个信号出现时,可以告诉内核按照下列三种方式之一处理
- 忽略
- 大多数信号可以使用这种方式进行处理,但时SIGKILL和SIGSTOP不能被忽略,因为他们向内核和超级用户提供了使进程终止的可靠方法。
- 捕捉
- 做到这一点的前提是要通知内核在某种信号发生时,调用一个用户函数,在用户函数中对这种事件进行处理
- 执行系统默认动作
- 大多数信号的系统默认动作是终止进程
- 在系统默认动作中,有的默认动作回在进程当前工作目录中生成core文件(中间复制了该进程的内存映像)
- 忽略
- 下面几种条件不产生core文件
- 进程是设置用户id的,而且当前用户并非程序文件的所有者
- 进程是设置组id的
- 用户没有写当前工作目录的权限的
- 文件已经存在,而且用户对该文件有写权限
- 文件太大
- 常用信号
- SIGABRT
- 调用abort函数时产生此信号
- SIGBUG
- 硬件故障产生
- SIGCHLD
- 当一个进程终止或停止,SIGCHLD信号被送给父进程。父进程可以使用wait取得子进程id和终止状态
- SIGHUP
- 如果终端检测到一个连接断开,则将此信号送给与该终端相关的控制进程
- SIGILL
- SIGINFO
- SIGINT
- 终端键产生
- SIGIO
- 异步io事件
— SIGKILL - 这是两个不能被捕捉或忽略信号中的一个,直接杀死进程
- 异步io事件
- SIGPIPE
- 如果管道的读进程已终止写管道,产生该信号
- SIGQUIT
- 退出键时触发
- SITTERM
- 这是kill命令发送的的系统默认终止信号。该信号由应用程序捕获,可以让程序有机会在退出前做好清理工作。
- SIGABRT
10.3 函数signal
- 信号机制最简单的接口时signal函数
#include<signal.h>
void(*signal(int signo,void (*func)(int)))(int) ;
成功返回以前的信号处理配置,出错返回SIG_ERR
- 该函数的signal参数是信号名,func值是接收到此信号后要调用的函数的地址。
- 当指定函数地址时,则信号发生时,调用该函数。我们称这种函数为信号处理程序或信号捕捉函数
- 程序启动
- 当执行一个程序时,所有信号状态都是系统默认或忽略。
- shell自动将后台进程对终端和退出信号的处理方式设置为忽略
- 进程创建
- 当一个进程调用fork时,其子进程继承父进程的信号处理方式
10.4 不可靠的信号
在早期的unix版本中,信号是不可靠的,不可靠指的是信号可能会丢失:一个信号发生了,但进程可能不知道这一点。同时,进程对信号的控制能力也很差,他能捕获信号或或忽略它。
10.5 中断的系统调用
-
早期unix系统的特性是:如果进程在执行一个低速系统而阻塞期间捕捉到一个信号,则该系统调用就被中断不在继续执行。
-
当捕捉到某个信号时,被中断的是内核中执行的系统调用
-
为了支持该特性,系统调用分为两类
-
低速系统调用(<span style="color:#a6684d"><span style="color:#e1441c">低速系统调用</span></span>是可能会使进程永远阻塞的的一类系统调用)
- 某些类型文件不存在(读管道,终端和网络设备),则读操作坑会永远阻塞
- 如果这些数据不能被相同类型文件立即接受,则可操作可能会使调用者永远阻塞
- 在某种条件发生之前被相同类型文件立即接受,则可能会发生阻塞(例如打开一个终端设备,需要先等待与之连接的调制解调器应答)
- pause函数(它使调用进程休眠直至捕捉到一个信号)和wait函数
- 某些ioctl操作
- 某些进程间通信函数
-
其他系统调用
-
-
中断系统调用表示某个系统调用被阻塞,需要被中断。
-
与被中断的系统调用相关的问题是必须显示的处理出错返回
-
某些操作系统为了使程序不必处理被中断的系统调用,引入了自动重启动的系统调用
10.6 可重入函数
- 进程捕捉到信号并对其进行处理时,进程正在执行的正常指令序列就被信号处理程序临时中断,它首先执行该信号处理程序中的指令。
- 如果在信号处理的过程中,由于出现克了新的信号,则会导致信息的丢失或被覆盖,因而引入了可重入的函数
- 可重入是一种异步信号安全的函数。在信号处理操作期间,它会阻塞任何会引起信号不一致的信号发送。
- 没有列入表中的大部分是不可重入的函数,不可重入的函数大多将变量存储在全局数据结构中
- 每个线程只有一个errno变量,所以信号处理程序可能会更改其原先值。所以当在信号处理程序中调用read这类函数,在调用前应该保存errno,调用后恢复errno。
10.7 sigcld语义
10.8 可靠的信号术语和语义
- 当造成信号的事件发生时,为进程产生一个信号
- 事件可以是硬件异常,软件条件,终端产生的信号或调用kill函数
- 当一个信号产生的时候,内核通常在进程表中以某种形式设置一个标志
- 当对信号才去了这种的动作的时候,我们说向进程<span style="color:#f00808">递送</span>了一个信号
- 在信号产生和递送之间的事件间隔,信号是<span style="color:#f00808">未决的</span>
- 进程可以选用'阻塞信号递送',如果为进程产生了一个阻塞的信号,而且对该信号的动作时系统默认动作或捕捉该信号,则为该进程将此信号保持为未决状态,直到该进程为对此信号接触了阻塞,或者将该信号的动作改为忽略。
- 内核在递送一个原来被阻塞的信号给进程时(而不是产生该信号时),才决定对他的处理方式
- 进程调用sigpending函数来判定哪些信号是设置为阻塞并处于未决状态的。
- 每个进程都有一个信号屏蔽字(signal mask),它规定了当前号阻塞递送到该进程的信号集。
10.8 kill和raise
- kill函数将信号发送给进程或进程组,raise函数允许进程向自身发送信号
#include <signal.h>
int kill(pid_t pid, int signo) ;
int raise(int signo) ;
//成功返回0,出错返回-1
// raise 调用等价于 kill(getpid(), signo) ;
- kill的pid参数有以下4种情况
- pid>0 将该信号发送给进程id为pid的进程
- pid
- 进程将信号发送给其他进程需要权限
- 对于非超级用户,基本规则是发送者的实际用户id或有效用户id必须等于接受着的实际用户id或有效用户id
10.10 函数alermh和函数pause
- 使用alerm设置一个定时器,在将来的某时刻该定时器超时,产生SIGALRM信号,如果忽略或不捕捉此信号,则其默认动作是终止调用该函数的进程
#include<unistd.h>
unsigned int alarm(unsigned int seconds)
//0 或设置的闹钟时间剩余的秒数
- 每个进程只能有一个闹钟时间
- pause函数使调用进程挂起直至捕捉一个信号
#include<unistd.h>
int pause(void) ;
//-1 ,errno设置为ENTER
10.14 sigaction函数
- sigaction函数的功能是检查或修改与指定信号关联的处理动作
#include <signal.h>
int sigaction(int signo, const struct siaction *restrict act,
struct sigaction *restrict oact) ;
//成功返回0,出错返回-1
//signo是要检测或修改其具体动作的信号编号,若act制作非空,则修改其动作。如果oact指针非空,则系统经由oact指针返回上一个信号的动作。
struct sigaction{
void (*sa_handler)(int) ;
sigset_t sa_mask ;
int sa_flags ;
void (*sa_sigaction)(int, siginfo_t *, void *) ;
}
10.17 函数abort
- 使程序异常终止
#include<stdlib.h>
void abort(void) ;
- 此函数发送SIGABRT信号给调用进程
10.18 函数system
10.19 函数system
10.21 作业控制信号
- 以下几个信号和作业控制有关
- SIGCHLD子进程已停止或者终止
- SIGCONT 如果进程已经停止,则使其继续运行
- SIGSTOP 停止信号
- SIGTSTP 交互式停止信号
- SIGTTIN 后台进程组成员读控制终端
- SIGTTOU 后台进程组成员写控制终端
- 除了SIGCHLD 大部分应用程序并不处理这些信号。
网友评论