Linux Signal机制
今天吧,来具体了解一下 Linux 的一个 signal 机制。
问题引入
起因在于我向老师抛出一个问题,在 子进程调用 execve 并重定向标准输出为管道时,父进程从管道读取数据时,采用 read 函数去读取,但是会发现一个问题。就是读和写不太能同步,即使程序退出,管道中还残留数据,如果管道中的数据还有,那么我读入不完整,如果没,则继续调用 read 会挂起父进程无法退出,一直想不到有效的办法解决。
请教老师之后,老师推荐我学学 signal 机制,并给我看了一个例程,例程大致简化一下是这样的:
1 |
|
signal机制
Linux 为进程提供了很多的通信机制,除了熟知的管道以外,还有 signal。比如我们平时希望快速结束一个程序,我们会通过 Ctrl + C 的方式去快速结束,可是发现有时候能成功结束,有时候不能,有时候输出一个 Abort 再结束。
其实我们在 Ctrl + C 的时候会发送一个 SIGINT 的 signal,有时候程序内置了对 SIGINT 的处理,比如输出一个 ABORT 再退出,比如我们结束不掉就是程序内部忽略此信号。
signal简介
这里给出一些常见信号的信号表
信号 | 取值 | 默认动作 | 含义(发出信号的原因) |
---|---|---|---|
SIGHUP | 1 | Term | 终端的挂断或进程死亡 |
SIGINT | 2 | Term | 来自键盘的中断信号 |
SIGQUIT | 3 | Core | 来自键盘的离开信号 |
SIGILL | 4 | Core | 非法指令 |
SIGABRT | 6 | Core | 来自abort的异常信号 |
SIGFPE | 8 | Core | 浮点例外 |
SIGKILL | 9 | Term | 杀死 |
SIGSEGV | 11 | Core | 段非法错误(内存引用无效) |
SIGPIPE | 13 | Term | 管道损坏:向一个没有读进程的管道写数据 |
SIGALRM | 14 | Term | 来自alarm的计时器到时信号 |
SIGTERM | 15 | Term | 终止 |
SIGUSR1 | 30,10,16 | Term | 用户自定义信号1 |
SIGUSR2 | 31,12,17 | Term | 用户自定义信号2 |
SIGCHLD | 20,17,18 | Ign | 子进程停止或终止 |
SIGCONT | 19,18,25 | Cont | 如果停止,继续执行 |
SIGSTOP | 17,19,23 | Stop | 非来自终端的停止信号 |
SIGTSTP | 18,20,24 | Stop | 来自终端的停止信号 |
SIGTTIN | 21,21,26 | Stop | 后台进程读终端 |
SIGTTOU | 22,22,27 | Stop | 后台进程写终端 |
SIGBUS | 10,7,10 | Core | 总线错误(内存访问错误) |
SIGPOLL | Term | Pollable事件发生(Sys V),与SIGIO同义 | |
SIGPROF | 27,27,29 | Term | 统计分布图用计时器到时 |
SIGSYS | 12,-,12 | Core | 非法系统调用(SVr4) |
SIGTRAP | 5 | Core | 跟踪/断点自陷 |
SIGURG | 16,23,21 | Ign | socket紧急信号 |
SIGVTALRM | 26,26,28 | Term | 虚拟计时器到时 |
SIGXCPU | 24,24,30 | Core | 超过CPU时限 |
SIGXFSZ | 25,25,31 | Core | 超过文件长度限制 |
SIGIOT | 6 | Core | IOT自陷,与SIGABRT同义 |
SIGEMT | 7,-,7 | Term | |
SIGSTKFLT | -,16,- | Term | 协处理器堆栈错误(不使用) |
SIGIO | 23,29,22 | Term | 描述符上可以进行I/O操作 |
SIGCLD | -,-,18 | Ign | 与SIGCHLD同义 |
SIGPWR | 29,30,19 | Term | 电力故障 |
SIGINFO | 29,-,- | 与SIGPWR同义 | |
SIGLOST | -,-,- | Term | 文件锁丢失 |
SIGWINCH | 28,28,20 | Ign | 窗口大小改变 |
SIGUNUSED | -,31,- | Term | 未使用信号 |
在表中,Term 表示 Terminate终止程序,Core 表示核心转储,Ign 表示忽略,Stop 表示挂起。
在 Linux 的命令当中,我们使用 kill 命令可以向一个程序发出信号,一般我们终止程序发起 9 也就是 SIGKILL 信号,因为该信号不能被捕获,阻塞或者是程序自行忽略。
在 Linux C 当中,可以使用 kill(pid,signal) 向某个进程发起一个 signal
signal函数
signal 函数用于自定义信号处理函数,希望程序在接收到某个信号时作出某些处理。
比如我分别实现上面三个 Ctrl+C 的情况。
输出 Abort 退出
忽略Ctrl+C
当然什么都不注册默认是退出的。
然后这个参数应该是可以让我们知道获取的是什么类型的中断,不至于让我们每一个信号都要写一个处理函数,我们可以写上一整个 signal 的处理方式,用 switch 分发,然后循环给所有信号注册该信号处理函数即可。
学了新的知识点还挺开心,就是这个通信问题解决之后,感觉初始的问题还是没有解决,希望往后能找到更好的方案达到预期目标。