如果有一个函数不幸被设计成为这样:
- 函数体内使用了静态的数据结构;
- 函数体内调用了malloc() 或者 free() 函数(谨慎使用堆);
- 函数体内调用了标准 I/O 函数。(缓冲区)
那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。这样的函数是不安全的函数,也叫不可重入函数。
相反,肯定有一个安全的函数,这个安全的函数又叫可重入函数。那么什么是可重入函数呢?所谓可重入是指一个可以被多个任务调用的过程,任务在调用时不必担心数据是否会出错。保证函数的可重入性的方法:
- 在写函数时候尽量使用局部变量(例如寄存器、栈中的变量);
- 对于要使用的全局变量要加以保护(如采取关中断、信号量等互斥方法),这样构成的函数就一定是一个可重入的函数。
Linux常见的可重入函数:
避免僵尸进程
SIGCHLD信号产生的条件
- 子进程终止时
- 子进程接收到SIGSTOP信号停止时
- 子进程处在停止态,接受到SIGCONT后唤醒时
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
void func(int signo)
{
printf("捕捉到信号 %d\n",signo);
printf("有子进程退出\n");
}
int main()
{
pid_t pid = -1;
struct sigaction act;
act.sa_handler = fun;
act.sa_flags = 0;
sigaction(SIGCHLD,&act,NULL);
//创建子进程
pid = fork();
if(-1 == pid)
{
perror("fork");
return 1;
}
//子进程
if(0 == pid)
{
printf("子进程比较累,休息两秒钟...\n");
sleep(2);
printf("子进程休息好了,太无聊了,就退出了...\n");
}
else
{
//父进程
while(1)
{
printf("父进程do wprking...\n");
}
}
}
如何避免僵尸进程
-
最简单的方法,父进程通过 wait() 和 waitpid() 等函数等待子进程结束,但是,这会导致父进程挂起。
-
如果父进程要处理的事情很多,不能够挂起,通过 signal() 函数人为处理信号 SIGCHLD , 只要有子进程退出自动调用指定好的回调函数,因为子进程结束后, 父进程会收到该信号 SIGCHLD ,可以在其回调函数里调用 wait() 或 waitpid() 回收。