前言
每个进程中的数据(资源)包括用户区的数据与内核区的数据。每个进程执行结束后,会自己释放掉自己用户区的数据,但内核区的PCB资源无法被自己释放,需要它的父进程去帮他去释放掉。
而孤儿进程和僵尸进程都是指在操作系统中未被清理的进程,它们具有不同的状态和含义。
孤儿进程
孤儿进程通常指的是父进程结束后,子进程仍然在运行而失去了对该子进程的控制权。这种情况下,该子进程就称为孤儿进程。孤儿进程并不会影响系统及其它进程的正常运行,因为操作系统会将孤儿进程交给 init 进程进行处理,并由 init 进程接管它的所有资源并发出回收命令进行销毁。
例如,在 Linux 系统中:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main() {
// 创建子进程
pid_t pid = fork();
// 判断是父进程还是子进程
if(pid > 0) {
printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());
} else if(pid == 0) {
sleep(1);
// 当前是子进程
printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
}
// for循环
for(int i = 0; i < 3; i++) {
printf("i : %d , pid : %d\n", i , getpid());
}
return 0;
}
以上代码中父进程 fork() 出子进程后等待 2 秒后就结束了,而子进程则打印一条消息并睡眠 5 秒之后结束。运行上述程序时,可以观察到当父进程结束前,子进程仍然在执行,此时子进程就成为了一个孤儿进程。
输出结果:
nowcoder@nowcoder:~/Linux/lession20$ ./orphan
i am parent process, pid : 10558, ppid : 9552
i : 0 , pid : 10558
i : 1 , pid : 10558
i : 2 , pid : 10558
nowcoder@nowcoder:~/Linux/lession20$ i am child process, pid : 10559, ppid : 1
i : 0 , pid : 10559
i : 1 , pid : 10559
i : 2 , pid : 10559
以上代码可以观察到父进程提前结束了运行,而子进程则是先睡了一秒后开始输出自己的内容。并且告诉我它现在的父进程为init进程。也就是进程号为1的进程。
僵尸进程
进程终止时,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。
僵尸进程通常指的是已经完成任务(即退出了)的子进程,但其父进程尚未调用 wait() 或 waitpid() 等命令来获取子进程的退出状态信息。在这种情况下,子进程所占用的资源(如内存)并没有完全释放,这就是僵尸进程。大量的僵尸进程会占据系统的内存,如果超过了预设的值,可能会导致系统崩溃。
例如,以下是一个产生僵尸进程的例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int pid = fork();
if (pid > 0) {
printf("Parent process\n");
} else if (pid == 0) {
printf("Child process\n");
exit(0); // 子进程结束并正常退出
} else {
printf("Fork error!\n");
exit(1);
}
sleep(10); // 让父进程停留一段时间
printf("End of parent process\n");
return 0;
}
总结
孤儿进程就是指,父进程先执行结束了(父进程死了),而父进程的子进程还没有执行结束,子进程就会被init进程所去托管。所以这个时候的子进程就被称为孤儿进程。
僵尸进程是指,当一个父进程的子进程早早的就执行结束了,而父进程还没有执行结束。如果没有调用 wait() 或 waitpid() 来获取子进程的退出状态信息的话,那只有等父进程执行结束了,才能去回收子进程所占用的资源。
在父进程还未执行结束,且未调用wait等函数结束执行完的子进程,这种情况下的子进程称之为僵尸进程。
僵尸进程不可以被kill -9 杀死。