Linux 允许进程查询内核以获得其父进程的 PID,或者其任何子进程的执行状态。例如,进程可以创建一个子进程来执行特定的任务,然后调用诸如 wait() 这样的一些库函数检查子进程是否终止。如果子进程已经终止,那么,它的终止代号将告诉父进程这个任务是否已成功地完成。
为了遵循这些设计原则,不允许 Linux 内核在进程一终止后就丢弃包含在进程描述符字段中的数据。只有父进程发出了与被终止的进程相关的 wait() 类系统调用之后,才允许这样做。这就是引入僵死状态的原因:尽管从技术上来说进程已死,但必须保存它的描述符,直到父进程得到通知。
如果一个进程已经终止,但是它的父进程尚未调用 wait() 或 waitpid() 对它进行清理,这时的进程状态称为僵死状态,处于僵死状态的进程称为僵尸进程(zombie process)。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。
僵尸进程是如何产生的
在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程。通过ps命令查看其带有defunct的标志。僵尸进程是一个早已死亡的进程,但在进程表 (processs table)中仍占了一个位置(slot)。
但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个 进程是刚刚结束的这个进程的子进程,如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动 wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。
为了观察到僵尸进程,我们自己写一个不正常的程序,父进程 fork 出子进程,子进程终止,而父进程既不终止也不调用 wait 清理子进程:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(void) { int i = 100; pid_t pid=fork(); if(pid < 0) { perror("fork failed."); exit(1); } if(pid > 0) { printf("This is the parent process. My PID is %d.\n", getpid()); for(; i > 0; i--) { sleep(1); } } else if(pid == 0) { printf("This is the child process. My PID is: %d. My PPID is: %d.\n", getpid(), getppid()); } return 0; }
把上面的代码保存到文件 zomprocdemo.c 文件中,并执行下面的命令编译:
$ gcc zomprocdemo.c -o zomprocdemo
然后运行编译出来的 zomprocdemo 程序:
$ ./zomprocdemo
此时子进程已经退出,但是父进程没有退出也没有通过 wait() 调用处理子进程。我们使用 ps 命令查看进程的状态:
上图红框中的大写字母 "Z" 说明 PID 为 112712 的进程此时处于僵死的状态。
让我们接着往下看!在结束 sleep 后父进程退出。当父进程退出后,子进程会变成孤儿进程,此时它会被一个管理进程收养。在不同的系统中,这个管理进程不太一样,早期一般是 init 进程,Ubuntu 上是 upstart,还有近来的 Systemd。但是它们都完成相同的任务,就是 wiat() 这些孤儿进程,并最终释放它们占用的系统进程表中的资源。这样,这些已经僵死的孤儿进程就彻底的被清除了。
僵尸进程的危害
在进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号 PID,退出状态 the termination status of the process,运行时间 the amount of CPU time taken by the process 等)。直到父进程通过 wait / waitpid 来取时才释放。
如果进程不调用 wait / waitpid 的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。
如何处理僵尸进程
僵尸进程的产生是因为父进程没有 wait() 子进程。所以如果我们自己写程序的话一定要在父进程中通过 wait() 来避免僵尸进程的产生。
当系统中出现了僵尸进程时,我们是无法通过 kill 命令把它清除掉的。但是我们可以杀死它的父进程,让它变成孤儿进程,并进一步被系统中管理孤儿进程的进程收养并清理。
下面的 demo 中,父进程通过 wait() 等待子进程结束:
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(void) { pid_t pid; pid = fork(); if (pid < 0) { perror("fork failed"); exit(1); } if (pid == 0) { int i; for (i = 3; i > 0; i--) { printf("This is the child\n"); sleep(1); } // exit with code 3 for test. exit(3); } else { int stat_val; wait(&stat_val); if (WIFEXITED(stat_val)) { printf("Child exited with code %d\n", WEXITSTATUS(stat_val)); } } return 0; }
demo 中父进程不仅等待子进程结束,还通过 WEXITSTATUS 宏取到了子进程的 exit code。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
Linux,僵尸进程
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]