引入
在linux中,特别是我们自己写代码时,使用fork()创建子进程的时候,需要知道两种特殊的进程——僵尸进程、孤儿进程。这是我们不可忽视的进程的两种特殊情况。
一、僵尸进程
在C语言编程中,僵尸进程的出现是由于子进程先于父进程退出,但是父进程没有使用wait(),waitpid()对子进程的退出状态进行获取,导致子进程的信息仍被存储在PCB中。这样的结果会导致内存泄漏。
在linux中,进程的状态一般有以下:
R (running), S (sleeping), D (disk sleep), T (stopped), t (tracing stop),X (dead), Z (zombie)。其中,“Z”状态指的就是僵尸状态,拥有僵尸状态的进程就是僵尸进程。
那么我们可以写一段代码,来查看是否能出现僵尸进程:
#include<iostream>#include<unistd.h>using namespace std;int main() {pid_t id = fork();if(id == 0){cout << "这是子进程,我的PID为:" << getpid() << "我的父进程PID为:" << getppid() << endl;}else if(id > 0){while(1){sleep(1); cout << "这是父进程,我的PID为:" << getpid() << "我的父进程PID为:" << getppid() << endl;} }return 0;}
执行程序并用ps指令查看相应进程,可以看到右边白色的部分是有一个Z+的,这个代表这个进程就是僵尸进程。
倘若我们使用wait()来获取子进程的退出状态:
#include<iostream>#include<sys/wait.h>#include<unistd.h>using namespace std;int main(){pid_t id = fork();if(id == 0){cout << "这是子进程,我的PID为:" << getpid() << "我的父进程PID为:" << getppid() << endl;}else if(id > 0){while(1){cout << "这是父进程,我的PID为:" << getpid() << "我的父进程PID为:" << getppid() << endl; int state;wait(&state);cout << state << endl;sleep(1);}}
再看这里,就不存在僵尸进程了。
二、孤儿进程
在Linux中,孤儿进程指的是在父进程退出后,子进程未退出的进程。
孤儿进程可以从字面上来理解,父进程退出后,他的子进程自然就像孤儿一样,成为一个孤儿进程。
当一个进程成为孤儿进程之后,它会被1号进程init进程给领养,那么进程退出状态也会被init进程所读取。
以下代码是让父进程优先于子进程退出,来测试一下孤儿进程
#include<iostream> #include<unistd.h>#include<sys/wait.h>using namespace std;int main(){ pid_t id = fork();if(id == 0){ while(1){ cout << "这是子进程,我的PID为:" << getpid() << "我的父进程PID为:" << getppid() << endl;sleep(1);}}else if(id > 0){ cout << "这是父进程,我的PID为:" << getpid() << "我的父进程PID为:" << getppid() << endl;}return 0;}
运行以后:
可以看到刚开始的时候是子进程的父进程是115763,但是当父进程退出后,子进程的父进程就变为了1号进程。
亲手敲过代码的朋友可能知道,在以上的条件发生后,使用Ctrl+c貌似并不能杀死子进程,这是因为子进程在被init进程领养成为孤儿进程以后,它是默认在后台运行的。所以使用Ctrl+c是不能杀死该孤儿进程的。
要想杀死这个进程,可以使用 kill -9 进程PID 来杀死后台进程。
因为孤儿进程是被init进程领养的,所以不用担心像僵尸进程一样内存泄漏的问题。
值得一提的是,当子进程和父进程都退出,那么系统会自动回收资源,也是不必担心僵尸进程的。但是在写程序的时候还是要细心一点,避免僵尸进程的出现。
#include<iostream> #include<unistd.h>#include<sys/wait.h>using namespace std;int main(){pid_t id = fork();if(id == 0){cout << "这是子进程,我的PID为:" << getpid() << "我的父进程PID为> :" << getppid() << endl;}else if(id > 0){cout << "这是父进程,我的PID为:" << getpid() << "我的父进程PID为> :" << getppid() << endl;sleep(5);}return 0;}