目录
1、孤儿进程
2、僵尸进程
在 Linux 系统中,父子进程关系的生命周期不同,导致会产生两类特殊进程:孤儿进程和僵尸进程。这两类进程在系统资源管理中起着重要作用。
1、孤儿进程
孤儿进程指的是父进程先于子进程结束,导致子进程失去父进程。为了避免这些进程无人管理,Linux 系统自动将孤儿进程的父进程重定向为 init
进程(PID 1),这使得孤儿进程得以继续运行,并由 init
进程负责在子进程结束时回收其资源。
当父进程结束后,子进程可以通过 getppid()
系统调用获取父进程的进程号。如果返回 1
,则说明该子进程已经成为孤儿进程,因为它的父进程变为了 init
。
孤儿进程的创建如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
/* 创建子进程 */
switch (fork()) {
case -1:
perror("fork error");
exit(-1);
case 0: /* 子进程 */
printf("子进程<%d>被创建, 父进程<%d>\n", getpid(), getppid());
sleep(3); //休眠 3 秒等待父进程结束
printf("父进程<%d>\n", getppid()); // 父进程变为 init,getppid() 返回 1
_exit(0);
default:
/* 父进程 */
break;
}
sleep(1); // 休眠 1 秒后父进程结束
printf("父进程结束!\n");
exit(0);
}
在这个代码中,父进程在休眠 1 秒后结束,子进程在 3 秒后醒来并发现其父进程已经变为 init
进程,通过 getppid()
验证这一点。这种情况下,子进程变成了孤儿进程。
2、僵尸进程
僵尸进程是指子进程先于父进程结束,且父进程没有及时回收子进程资源的情况。当一个进程结束时,它的退出状态仍然保留在系统进程表中,直到父进程调用 wait()
函数进行回收。如果父进程没有调用 wait()
或其变体,子进程就会变成僵尸进程,占用系统资源。
如果系统中有大量僵尸进程,这些进程占用的资源将逐渐增加,最终可能耗尽进程表空间,导致无法创建新的进程。因此,及时调用 wait()
函数回收子进程非常重要。
僵尸进程的创建如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
/* 创建子进程 */
switch (fork()) {
case -1:
perror("fork error");
exit(-1);
case 0:
/* 子进程 */
printf("子进程<%d>被创建\n", getpid());
sleep(1);
printf("子进程结束\n");
_exit(0); // 子进程结束
default:
/* 父进程 */
break;
}
for ( ; ; ) { // 父进程没有调用 wait(),导致子进程变为僵尸进程
sleep(1);
}
exit(0);
}
在这个代码中,子进程在 1 秒后结束,而父进程没有调用 wait()
函数,这导致子进程进入僵尸状态。可以通过 ps -aux
命令查看僵尸进程,其状态标记为 Z
(zombie)。
僵尸进程的清理方法:
- 父进程调用
wait()
:当父进程调用wait()
函数后,僵尸进程将被内核彻底移除。 - 杀死父进程:如果父进程没有调用
wait()
,我们可以通过杀死父进程,令init
进程接管僵尸进程,从而清理它。
通过合理的进程管理,例如及时调用 wait()
,可以有效防止僵尸进程的累积并保持系统的正常运行。