信号处理
长时间运行的 linux 程序必须处理信号。
必须处理的信号有:SIGTERM, SIGINT, SIGPIPE, SIGBUS, SIGSEGV, SIGABRT
这些信号分别是什么意思?
| 信号 | 触发 | 阻塞/忽略/捕获 | 默认处理 |
|---|---|---|---|
SIGINT |
输入 INTR 字符时发送,如 ctrl+C。 |
进程终止 | |
SIGTERM |
kill 发送的默认信号 |
可以 | 进程终止 |
SIGKILL |
通过 kill -s 9 发送 |
不可 | 进程终止 |
SIGSTOP |
? | 不可 | 进程暂停 |
SIGHUP |
会话结束时触发 | 可以 | 进程终止 |
SIGPIPE |
向写端关闭的管道或 socket 写入数据 |
可以 | 进程终止 |
SIGBUS |
?未对齐 | 终止进程,core dump |
|
SIGSEGV |
段错误:缓冲区溢出,栈溢出,非法文件访问 | 终止进程,core dump |
|
SIGABRT |
重复 free;assert 失败;执行 abort() |
可捕获,不可阻塞 |
信号处理函数注意:不可在信号处理函数中调用不可重入函数。
fork 与文件描述符
fork 创建的子进程会和父进程共享文件描述符。
stdio 族函数使用了缓存机制,这可能会引发问题。缓存设置在哪里?
stdin, stdout 的缓冲区是设置在用户空间的:参考:[http://www.pixelbeat.org/programming/stdio_buffering/](http://www.pixelbeat.org/programming/stdio_buffering/)
一个例子:
// 这一段代码:fork();printf(".");fflush(stdout);fork();printf(",");// 得到的输出是:..,,,,
如果将 fflush(stdout) 注释掉,输出会变成:
.,.,.,.,
原因在于:stdout 使用了缓冲区,且该缓冲区位于用户进程空间。在使用 fork 得到子进程是,缓冲区也被复制了,缓冲区中还有未写入 stdout 的 . 。所以最终得到的四个进程,每个进程都会输出 ., 到终端。
如图所示:
_exit 和 exit
_exit 在退出之前会做三件事:
- 关闭进程打开的文件
- 将子进程托付给
init进程 - 向父进程发送
SIGCHLD信号
做完这三件事情之后,进程就会退出
exit 是在 _exit 的基础上进行了包装,它做了这些事情:
- 调用在
atexit()中注册的函数,比如一些析构函数 - 刷写缓冲:
fflush - 最后调用
_exit()函数
如果使用 fprintf 写文件,子进程继承了缓冲区,父进程继续写入,子进程此时 exit,将缓冲区中的内容刷入文件,刷入位置还是最开始的位置:
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/wait.h>int main(int argc, char* argv[]){int status;char buffer[1024] = {0};FILE * fp = fopen("test.log","w");fprintf(fp,"a");pid_t pid = fork();if (pid == 0){sleep(1);exit(0);}fprintf(fp,"b");wait(&status);fprintf(fp,"c");return 0;}// 最终文件test.log的内容是:aabc// 子进程后退出,在退出时将缓冲区中的a刷入,刷入位置是fork时得到的
