孤儿进程与僵尸进程

孤儿进程:父进程先于子进程结束(遇到return、exit、异常终止等情况时),则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。可以通过getppid函数来查看孤儿进程的父进程ID,即init进程的ID,init进程的ID具体是多少取决于操作系统对进程的调度,其值是不确定的。在操作系统中,init进程也不止一个,可通过ps aux详细查看。

僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。

特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。

// orphan.c  //shell产生子进程执行该程序

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main(void)
{	pid_t pid;pid = fork();  //创建子进程if (pid == 0) {while (1) {printf("I am child, my parent pid = %d\n", getppid());sleep(1);  //子进程一直运行}} else if (pid > 0) {printf("I am parent, my pid is = %d\n", getpid());sleep(9);printf("------------parent going to die------------\n");  //父进程先于子进程结束,子进程变为孤儿进程} else {perror("fork");return 1; //等价于exit(1),都是结束进程,且进程结束状态置1表示出错}return 0;
}

[root@localhost wait]# ./orphan

I am parent, my pid is = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

I am child, my parent pid = 26900

------------parent going to die------------ //父进程正常结束

I am child, my parent pid = 1  //子进程为孤儿进程,被init进程领养,即其父进程为init进程

[root@localhost wait]# I am child, my parent pid = 1 //父进程结束,shell进程收回前台,等待命令交互

I am child, my parent pid = 1 //子进程一直执行,为孤儿进程

I am child, my parent pid = 1

I am child, my parent pid = 1

I am child, my parent pid = 1

I am child, my parent pid = 1

//zoom.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main(void)
{pid_t pid;pid = fork(); if (pid == 0) {printf("---child, my parent= %d, going to sleep 10s\n", getppid());sleep(10);printf("-------------child die--------------\n"); //子进程先正常结束} else if (pid > 0) {while (1) {printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);sleep(1);}    //父进程一直运行} else {perror("fork");return 1;}return 0;
}

[root@localhost wait]# ./zoom

I am parent, pid = 27152, myson = 27153

---child, my parent= 27152, going to sleep 10s

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

-------------child die--------------  //子进程死亡

I am parent, pid = 27152, myson = 27153 //父进程一直运行,一直占据前台,shell进程无法获得前台交互  且子进程结束后,父进程没有对子进程残留在内核中的PCB进行回收,从而子进程变为僵尸进程。

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

I am parent, pid = 27152, myson = 27153

[root@localhost wait] # ps aux

root     27152  0.0  0.0   4160   352 pts/1    S+   03:11   0:00 ./zoom

root     27153  0.0  0.0      0     0 pts/1    Z+   03:11   0:00 [zoom] <defunct>

root      27155  0.0  0.0      0     0 ?        R    03:12   0:00 [kworker/3:0]

root      27163  0.0  0.0 107892   360 ?        S    03:12   0:00 sleep 60

root      27164  0.0  0.0 123360  1384 pts/0    R+   03:12   0:00 ps aux

[root@localhost wait]# kill 27152 

由上可以看出,父进程27152,为S+,表示该进程在后台运行(注意,ps aux命令是在另一个终端执行的,因此相对于另一个shell终端,父进程在后台运行);子进程27153,为Z+,表示僵尸进程,说明该进程终止后,其残留在内核的PCB资源没有被父进程回收;而ps aux这个命令的进程为R+,表示在前台运行,即就在pts/0设备终端的前台运行。而前两个进程属于pts/1设备。[zoom] <defunct>  defunct表示已故的,不复存在的,但其痕迹仍然残留在内核中,占用内存资源,因此需要做到及时对僵尸进程回收和清除。

总结:在每个进程退出的时候, 内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 另外,如果父进程一直不结束(不终止),在不调用wait或waitpid的情况下,其子进程结束后会变为僵尸进程,残留在内核中,此时若父进程结束了,那么这些僵尸进程因为没有了父进程,就会变为孤儿进程被init进程领养,init进程就会对这些僵尸进程进行回收,然后清除。因此,父进程结束了,其子进程会被回收。孤儿进程结束后,也会被init进程回收。如果要回收一个进程,除了通过其父进程调用wait或waitpid函数外,还可以杀死其父进程,让其变为孤儿进程,被init进程回收。

系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。可以fork两次, 父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385360.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1043. 输出PATest(20)

给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序&#xff0c;按“PATestPATest....”这样的顺序输出&#xff0c;并忽略其它字符。当然&#xff0c;六种字符的个数不一定是一样多的&#xff0c;若某种字符已经输出完&#xff0c;则余下的字符仍按P…

wait函数

#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); 僵尸进程。进程结束后放弃了几乎所有的内存空间&#xff0c;没有任何可以执行的代码&#xff0c;也不能被调度&#xff0c;仅仅在进程列表中保留着一个位置&#xff0c;记载着该进程的退出…

map与unordered_map

时间复杂度&#xff1a; mapunordered_mapOrderingincreasing orderno orderImplementationSelf balancing BSTHash Tablesearch timelog(n) O(1&#xff09;: 平均水ping O(n&#xff09;&#xff1a;最糟糕情况 Insertion timelog(n) RebalanceSame sa searchDelete timelo…

waitpid函数

#include <sys/wait.h> #include <sys/types.h> pid_t waitpid(pid_t pid, int *status, int options); 作用&#xff1a;同wait&#xff0c;但可指定pid进程清理&#xff0c;可以不阻塞。 waitpid函数的第二个参数int *status跟wait函数的形参一样&#xff0c;…

进程间通信的方法

Linux环境下&#xff0c;进程地址空间相互独立&#xff0c;每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程和进程之间不能相互访问&#xff0c;要交换数据必须通过内核&#xff0c;在内核中开辟一块缓冲区&#xff0c;进…

管道的概念(匿名管道)

管道是一种最基本的IPC机制&#xff0c;作用于有血缘关系的进程之间&#xff0c;完成数据传递。调用pipe系统函数即可创建一个管道。管道有如下特质&#xff1a;1.其本质是一个伪文件&#xff08;实为内核缓冲区&#xff09;&#xff0c;伪文件即不是真正的文件&#xff0c;不占…

1023. 组个最小数 (20)

给定数字0-9各若干个。你可以以任意顺序排列这些数字&#xff0c;但必须全部使用。目标是使得最后得到的数尽可能小&#xff08;注意0不能做首位&#xff09;。例如&#xff1a;给定两个0&#xff0c;两个1&#xff0c;三个5&#xff0c;一个8&#xff0c;我们得到的最小的数就…

pipe函数

#include <unistd.h> int pipe(int pipefd[2]); 作用&#xff1a;创建管道 成功&#xff1a;0&#xff1b;失败&#xff1a;-1&#xff0c;设置errno 函数调用成功返回r/w两个文件描述符。无需open&#xff0c;但需手动close。规定&#xff1a;fd[0] …

二分查找总结

二分查找法作为一种常见的查找方法&#xff0c;将原本是线性时间提升到了对数时间范围&#xff0c;大大缩短了搜索时间&#xff0c;具有很大的应用场景&#xff0c;而在LeetCode中&#xff0c;要运用二分搜索法来解的题目也有很多&#xff0c;但是实际上二分查找法的查找目标有…

管道的读写行为

使用管道需要注意以下4种特殊情况&#xff08;默认都是阻塞I/O操作&#xff0c;没有设置O_NONBLOCK标志&#xff09;&#xff1a; 1. 如果所有指向管道写端的文件描述符都关闭了&#xff08;管道写端引用计数为0&#xff09;&#xff0c;而仍然有进程从管道的读端读数据&#…

【C++ Primer | 08】课后习题答案

文章目录练习8.13练习8.13 include <iostream> #include <sstream> #include <fstream> #include <string> #include <vector> using namespace std;struct PersonInfo {string name;vector<string> phones; };bool valid(const string&…

管道缓冲区大小

可以使用ulimit –a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。通常为&#xff1a;pipe size 4K&#xff0c;即一个页面大小。也可以使用fpathconf函数来查看&#xff1a; #include <unistd.h> long fpathconf(int fd, int name); 当需要查看管道的大…

FIFO(命名管道)

FIFO常被称为命名管道&#xff0c;以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过FIFO&#xff0c;不相关的进程也能交换数据。FIFO是Linux基础文件类型中的一种&#xff08;p,管道文件&#xff09;。但FIFO文件在磁盘上没有数据块&#xff0c;仅仅用来标…

文件进程间通信

使用文件也可以完成IPC&#xff0c;理论依据是&#xff0c;fork后&#xff0c;父子进程共享文件描述符。也就共享打开的文件。 //父子进程共享打开的文件。借助文件进行进程间通信&#xff08;可先打开文件&#xff0c;再创建子进程&#xff09; #include <unistd.h> #…

mmap内存映射、system V共享内存和Posix共享内存

linux内核支持多种共享内存方式&#xff0c;如mmap内存映射&#xff0c;Posix共享内存&#xff0c;以system V共享内存。当内核空间和用户空间存在大量数据交互时&#xff0c;共享内存映射就成了这种情况下的不二选择。它能够最大限度的降低内核空间和用户空间之间的数据拷贝&a…

mmap、munmap函数

#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); int munmap(void *addr, size_t length); void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); 返回&#xff1a;成功&…

mmap和munmap对文件进行操作(读写等)

//mmap、munmap函数的使用 #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/mman.h>void sys_err(char *str) {perror(str);exit(1); }…

1017. A除以B (20)

本题要求计算A/B&#xff0c;其中A是不超过1000位的正整数&#xff0c;B是1位正整数。你需要输出商数Q和余数R&#xff0c;使得A B * Q R成立。 输入格式&#xff1a; 输入在1行中依次给出A和B&#xff0c;中间以1空格分隔。 输出格式&#xff1a; 在1行中依次输出Q和R&#…

mmap父子进程间通信

父子等有血缘关系的进程之间也可以通过mmap建立的映射区来完成数据通信。但相应的要在创建映射区的时候指定对应的标志位参数flags&#xff1a;MAP_PRIVATE&#xff1a;&#xff08;私有映射&#xff09;父子进程各自独占映射区&#xff1b;MAP_SHARED&#xff1a;&#xff08;…

匿名映射

通过使用我们发现&#xff0c;使用映射区来完成文件读写操作十分方便&#xff0c;父子进程间通信也较容易。但缺陷是&#xff0c;每次创建映射区一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件&#xff0c;创建好了再unlink、close掉&#xff0c;比较麻烦。…