信号系统概述
信号类似于软件层次上的“中断”,很多比较重要的应用程序都需要处理信号,信号提供了一种处理异步事件的方法。例如,终端用户键入中断键,会通过信号机制停止一个程序。
信号处理流程
每个信号都有自己的名字,信号的名字都以“SIG”开头。例如,SIGTERM 是终止信号,向进程发送此信号可以终止一个进程。目前 SylixOS 可支持 63 种不同的信号,其中包括标准信号和实时信号。
很多条件可以产生信号:
- 当用户按某些键时,引发终端产生信号,例如:Ctrl+C 产生 SIGINT 信号。
- alarm 函数设置的定时器超时后产生 SIGALRM 信号。
- 子进程退出或被异常终止后产生 SIGCHLD 信号。
- 访问非法内存产生 SIGSEGV 信号。
- 用户可以调用 kill 命令将信号发送给其他进程,常用此命令终止一个失控的后台进程。
信号异步性意味着,应用程序不用等待事件的发生,当信号发生时应用程序自动陷入到对应的信号处理函数中。产生信号的事件对进程而言是随机出现的。进程不能简单地测试一个变量来判断是否发生了一个信号,而是必须告诉内核“在此信号发生时,请执行下列操作”。
在某个信号发生时,可以告诉内核按下列 3 种方式之一进行处理:
- 忽略信号:大多数信号都可以使用这种方式进行处理,在 SylixOS 中有一类信号不能被忽略(例如:SIGSTOP、SIGKILL 等),这种信号不能被忽略的原因是:它们向内核提供了进程终止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(如非法内存访问)则进程的运行行为是未定义的。
- 捕捉信号:为了做到这一点,要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行用户想要的动作。例如,捕捉到 SIGALRM 信号后,用户可以在相应的处理函数中去控制某个线程。如果捕捉到 SIGCHLD 信号,则表示一个子进程已经终止,所以此信号的捕捉函数可以调用 waitpid 函数以取得该子进程的退出状态。又例如,如果进程创建了临时文件,那么可能要为 SIGTERM 信号编写一个信号捕捉函数以清除临时文件。需要注意的是,同样有一类信号不能被捕捉(例如:SIGSTOP、SIGKILL 等)。
- 执行系统默认动作:对大多数信号的系统默认动作是终止该进程。
支持的信号
信号名 | 说明 |
---|---|
SIGHUP | 挂断控制终端或进程。通常用此通知守护进程再次读取它们的配置文件,因为守护进程不会有控制终端,通常决不会接收到这种信号 |
SIGINT | 来自键盘的中断。一般采用 Ctrl + C 来产生此信号。当一个进程在运行时失控,特别是他正在屏幕上产生大量不需要的输出时,常用此信号终止 |
SIGQUIT | 来自键盘的退出 |
SIGILL | 非法指令 |
SIGTRAP | 跟踪断点 |
SIGABRT | 异常结束 |
SIGUNUSED | 未使用 |
SIGFPE | 协处理出错,如除以 0、浮点溢出等 |
SIGKILL | 强迫进程结束(不能被忽略或捕捉) |
SIGBUS | 总线错误,通常是指示一个实现定义的硬件故障 |
SIGSEGV | 无效内存引用 |
SIGUNUSED2 | 未使用 2 |
SIGPIPE | 管道写错误,无读者 |
SIGALRM | 实时定时器报警 |
SIGTERM | 进程终止。这是 kill 命令的默认动作,由于这个信号是由应用程序捕获的,使用 SIGTERM 也让程序有机会在退出之前做好清理工作,从而优雅的终止 |
SIGCNCL | 线程取消 |
SIGSTOP | 停止进程执行。此信号不能被捕获和忽略(不能被忽略或捕捉) |
SIGTSTP | tty 发出停止进程 |
SIGCONT | 恢复进程继续执行 |
SIGCHLD | 子进程停止或者被终止。系统默认是忽略此信号 |
SIGTTIN | 后台进程请求输入 |
SIGTTOU | 后台进程请求输出 |
SIGCANCEL | 同 SIGTERM 相同 |
SIGIO | 异步 I/O 事件 |
SIGXCPU | 进程超出了软 CPU 事件限制 |
SIGXFSZ | 进程超出了软文件长度限制 |
SIGVTALRM | 函数 setitimer 设置的虚拟间隔定时器已经超时 |
SIGPROF | 函数 setitimer 设置的梗概间隔定时器已经超时 |
SIGWINCH | 更改了窗口的大小 |
SIGINFO | 信息请求 |
SIGPOLL | 同 SIGIO 相同 |
SIGUSR1 | 用户定义信号 1 |
SIGUSR2 | 用户定义信号 2 |
SIGPWR | 电源失败重新开始 |
SIGSYS | 错误的系统调用 |
SIGURG | 网络连接上接到带外的数据时,可选择地产生此信号 |
SIGLOWMEM | 系统内存不足告警 |
SIGSTKSHOW | 打印任务栈信息和上下文信息 |
SIGRTMIN-SIGRTMAX | SylixOS 实现 SIGRTMIN = 48、SIGRTMAX = 63,系统没有指定明确的含义,由用户自定义,并且不应该使用某数值 |
不可靠信号与可靠信号
在早期的 UNIX 版本中,信号是不可靠的,也就是说,信号可能会丢失,这通常会表现为,一个信号发生了,但进程却可能不知道这一点。早期版本中在进程每次接收到信号对其进行处理时,随即将该信号动作重置为默认值(介绍 signal 函数时,我们将详述这一点)。
前面我们说过,信号产生可以来自不同的途径,在 SylixOS 中,信号的来源包含如下表所示的几种类型,当一个信号产生时,内核通常在线程控制块中以某种形式设置一个标志。当信号执行了相应的动作时,代表向进程 / 线程 递送 了一个信号,在信号产生到递送之间的时间间隔内,信号是未决的(pending)。
信号产生源 | 说明 |
---|---|
SI_KILL/SI_USER | 使用 kill 函数发送的信号 |
SI_QUEUE | 使用 sigqueue 函数发送的信号 |
SI_TIMER | POSIX 定时器发送的信号 |
SI_ASYNCIO | 异步 I/O 系统完成发送的信号 |
SI_MESGQ | 接收到一条消息产生的信号 |
SI_KERNEL | SylixOS 内核内部使用 |
进程/线程可以屏蔽(或者说阻塞)信号,如果信号在被屏蔽期间,信号产生了并且对该信号的动作是系统默认或者捕捉,则此信号将保持为未决状态,直到该进程/线程对此信号解除屏蔽,或者设置信号动作为忽略。
如果在进程/线程解除对某个信号的屏蔽之前,这种信号发生了多次,SylixOS 内核将有两种对待方法,一种是 SI_KILL 方式产生的信号将只递送一次,也即信号不会排队,另一种是非 SI_KILL 方式产生的信号将递送多次,也即信号产生了排队。
SylixOS 内核实现中,如果多个不同信号从屏蔽状态被解除时,则优先递送信号数字小的信号。
由此可见,SylixOS 的信号机制摒弃了之前不可靠的信号机制,只要是非 SI_KILL 方式产生的信号,都将会排队。
因为线程是 SylixOS 调度的单位,而每一个需要处理的信号,都将嵌入到线程中去执行,所以以线程的方式来介绍信号更加符合 SylixOS 的特点。实际上,SylixOS 中向进程递送信号是递送给了进程的主线程。