1.僵死进程
1)一个进程终止了,内核会为它保留一定的信息,包括进程ID、终止状态、使用CPU的时间总量等
2)为什么内核要保留?因为有时候它的父进程想知道它的退出状态
3)一个已经终止的进程,但是其父进程由于很忙,尚未对其进行尚后处理(处理保留的信息,彻底释放它的资源),则这个进程在被处理前为僵死进程
2.孤儿进程
1)一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程;
2)孤儿进程将被init进程收养,孤儿进程不会成为僵死进程,因为init进程时刻都wait它的子进程。
3.wait函数和waitpid函数
pid_t wait(int* statloc); pid_t waitpid(pid_t pid, int* statloc, int options);
3.1函数作用
父进程用来尚后处理终止子进程的函数
3.2参数说明
1)整型指针statloc如果不是NULL,则终止子进程的终止状态会存放在statloc指向的单元中;
2)waitpid中pid的含义依据其具体值而变:
pid==-1 等待任何一个子进程,此时waitpid的作用与wait相同
pid >0 等待进程ID与pid值相同的子进程
pid==0 等待与调用者进程组ID相同的任意子进程
pid<-1 等待进程组ID与pid绝对值相等的任意子进程
3.3区别
1)如果子进程已经是僵死进程,则wait函数会立刻处理它并返回子进程ID,否则wait函数会使调用者阻塞,直到一个子进程终止;而waitpid函数提供了一个非阻塞的版本
2)waitpid函数可以等待一个特定的子进程
4.pthread_join函数
int pthread_join(pthread_t* tid, void **status) //若成功返回0,若出错则返回错误编号
1)调用pthread_join等待一个给定的线程终止,对比进程,类似于进程中的waitpid(一般在主线程中调用,来等待子线程的终止;一个线程终止了,进程也会为它保留线程ID、终止状态等信息)
2)如果status非空,则应该为所等待线程的返回值的指针
5.SIGCHLD信号
作用:一个进程终止时,它会向父进程发送SIGCHLD信号
设置成忽略:设置成SIG_IGN的“忽略”和系统默认值的“忽略”是不一样的语意。在SIG_IGN下,子进程的信息会被内核直接丢弃,不会变成僵尸进程;而在系统默认的“忽略”下,内核是会为子进程保留一定的信息的,等待被wait系列函数处理
6.避免僵死进程的方法
1)父进程在fork后调用wait/waitpid函数
2)父进程捕获SIGCHLD信号,并在捕获函数中调用wait/waitpid函数
3)在进程中设置SIGCHLD信号为忽略,见上
4)调用两次fork:
1.父进程fork产生一个子进程,随后就立即执行waitpid()/wait()函数来等待子进程结束
2.然后儿子进程再fork产生一个孙子进程,接着立即执行exit(0)退出,由于孙子进程失去了它的父进程,那么孙子进程变为孤儿进程被init收养
3.孙子进程先要执行sleep操作,因为要保证它的父进程先终止,保证它正式被init收养
4.这样一来孙子进程就可以代替它的父进程来完成需要的事件(父子进程共享正文段),而不会有僵尸进程出现(孤儿进程不会成为僵死进程)