信号系统综述
信号相当于面向事件编程的一个例子。在计算机中,硬件层面有硬件中断,软件有软件中断。在此要讲的信号属于软件中断。
在计算机科学中,信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。
信号的种类
- 按可靠性分
- 不可靠信号
- 把那些建立在早期机制上的信号叫做"不可靠信号”,信号值小于SIGRTMIN(Red hat 7.2中,SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号
- 为什么这么叫
- 进程每次处理信号后,就将对信号的响应设置为默认动作。导致下次对信号的错误处理。
- 信号可能丢失
- 可靠信号
- 信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,这些信号支持排队
- 信号的发送和安装也出现了新版本:信号发送函数sigqueue()及信号安装函数sigaction()
- 不可靠信号
- 在时间维度上分
- 非实时信号
- 早期Unix系统只定义了32种信号,Ret hat7.2支持64种信号,编号0-63(SIGRTMIN=31,SIGRTMAX=63)
- 前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作
- 非实时信号都不支持排队,都是不可靠信号
- 实时信号
- 后32个信号表示实时信号,等同于前面阐述的可靠信号
- 实时信号都支持排队,都是可靠信号
- 非实时信号
进程对信号的响应
- 三种响应方式
- 忽略,不能忽略
SIGKILL
和SIGSTOP
- 捕捉信号,执行相应处理函数
- 执行缺省动作
- Linux对每种信号都规定了默认操作
- 进程对实时信号的默认操作是进程终止
- 忽略,不能忽略
常见信号
参见下面链接,下面只列出常见信号
Ctrl-C (DEL in older Unixes): SIGINT(interrupt) 终止进程
Ctrl-Z: SIGTSTP(terminal stop) 暂停执行
Ctrl-\: SIGQUIT 进程终止并dump core
Ctrl-T: not supported on all Unixes; SIGINFO 让操作系统显示正在运行命令的信息
名称 默认动作 说明
SIGHUP 终止进程 终端线路挂断
SIGINT 终止进程 中断进程
SIGQUIT 建立CORE文件 终止进程,并且生成core文件
SIGILL 建立CORE文件 非法指令
SIGTRAP 建立CORE文件 跟踪自陷
SIGBUS 建立CORE文件 总线错误
SIGSEGV 建立CORE文件 段非法错误
SIGFPE 建立CORE文件 浮点异常
SIGIOT 建立CORE文件 执行I/O自陷
SIGKILL 终止进程 杀死进程
SIGPIPE 终止进程 向一个没有读进程的管道写数据
SIGALARM 终止进程 计时器到时
SIGTERM 终止进程 软件终止信号
SIGSTOP 停止进程 非终端来的停止信号
SIGTSTP 停止进程 终端来的停止信号
SIGCONT 忽略信号 继续执行一个停止的进程
SIGURG 忽略信号 I/O紧急信号
SIGIO 忽略信号 描述符上可以进行I/O
SIGCHLD 忽略信号 当子进程停止或退出时通知父进程
SIGTTOU 停止进程 后台进程写终端
SIGTTIN 停止进程 后台进程读终端
SIGXGPU 终止进程 CPU时限超时
SIGXFSZ 终止进程 文件长度过长
SIGWINCH 忽略信号 窗口大小发生变化
SIGPROF 终止进程 统计分布图用计时器到时
SIGUSR1 终止进程 用户定义信号1
SIGUSR2 终止进程 用户定义信号2
SIGVTALRM 终止进程 虚拟计时器到时
信号的发送
- 信号发送函数
- kill()
int kill(pid_t pid,int signo)
- 信号的接收者
- pid > 0 进程ID为pid的进程
- pid = 0 同一个进程组的进程
- pid < 0
- pid = -1 除发送进程自身之外,所有进程ID>1的进程
- pid <= -2 进程组ID为abs(pid)的所有进程
- signo 信号值
- 0 空信号
- 成功返回0,否则返回-1
- raise():向进程本身发送信号
- sigqueue():主要针对实时信号,也支持非实时信号,支持传递额外的数据“联合数据结构”
- alarm()
- 专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间
- 进程调用alarm后,任何以前的alarm()调用都将无效。如果参数seconds为零,那么进程内将不再包含任何闹钟时间。
- 返回上一个闹钟时间的剩余时间,或者返回0
- setitimer()
- 支持绝对时间、相对时间、程序运行时间三种定时器
- abort()
- 向进程发送SIGABORT信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。即使SIGABORT被进程设置为阻塞信号,调用abort()后,SIGABORT仍然能被进程接收。
- 该函数无返回值
- kill()
信号的安装
- signal()
- sigation()
信号的阻塞
未决信号集 与信号阻塞有关的几个函数
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset));
int sigpending(sigset_t *set));
int sigsuspend(const sigset_t *mask));
信号的生命周期
信号的诞生 --> 信号在进程中注册 --> 信号在进程中注销 --> 信号处理函数执行完毕
注意事项
- 考虑到程序的可移植性,应该尽量采用POSIX信号函数
- 为了增强程序的稳定性,在信号处理函数中应使用可重入函数(可被多个任务调用)
结论
- 安装信号(推荐使用sigaction())
- 实现三参数信号处理函数,handler(int signal,struct siginfo *info, void *)
- 发送信号,推荐使用sigqueue()