信号处理
长时间运行的 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时得到的