zoukankan      html  css  js  c++  java
  • OS之进程管理---孤儿进程和僵尸进程

    僵尸进程

    当一个进程终止时,操作系统会释放其资源,不过它位于进程表中的条目还是在的,直到它的父进程调用wait();这是因为进程表中包含了进程的退出状态。当进程已经终止,但是其父进尚未调用wait(),这样的进程叫做僵尸进程(zombie prpcess)。
    所有进程终止时都会过度到这种状态,但是一般而言僵尸只是短暂存在。一旦父进程调用了wait(),僵尸进程的进程标示符和它在进程表中的条目就会释放。
    这里需要注意:僵尸进程不能通过kill来进行杀死,因为kill是用来终止进程的,僵尸进程已经是终止的。

    设计一个僵尸进程:
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <signal.h>
    #include <errno.h>
    #include <signal.h>
    
    
    //僵尸进程
    int main(void) {
    	
    	pid_t pid;
    	
    	printf("before fork pid:%d
    ", getpid());
    	
    	int abc = 10;
    
    	pid = fork();
    	if(pid < 0) {
    		fprintf(stderr, "fork failed");
    		return 1;
    
    	}
    	else if(pid == 0) {
    		abc++;
    		printf("child:%d, parent:%d
    ",getpid(), getppid());
    		printf("abc:%d", abc);
    		exit(0);
    	}
    	else {
    		abc++;
    		printf("parent:pid:%d 
    ", getpid());
    		printf("abc:%d 
    ", abc);
    		sleep(20);
    	}
    
    	printf("fork after ... 
    ");
    
    }
    
    

    执行结果:

    shanlei@shanlei-Lenovo-ideapad-110-15ISK:/var/www/c_code/操作系统$ ./2
    before fork pid:7203
    parent:pid:7203 
    abc:11 
    child:7204, parent:7203
    abc:11fork after ... 
    shanlei@shanlei-Lenovo-ideapad-110-15ISK:/var/www/c_code/操作系统$ 
    

    查看运行时的进程表:

    F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY        TIME CMD
    0 S  1000  7203  5576  0  80   0 -  1127 hrtime pts/0    00:00:00 2
    1 Z  1000  7204  7203  0  80   0 -     0 -      pts/0    00:00:00 0
    4 R  1000  7205  7084  0  80   0 -  9006 -      pts/1    00:00:00 ps
    

    注意:在进程表中进程状态位于列S,状态位为Z的是僵尸进程,子进程的进程标识符(pid)位于列PID,而父进程的标识符则位于列PPID。

    僵尸进程的危害:

    如果不调用wait / waitpid的话,保留的那段信息可能会一直存在,那么将会一直占用着进程号,如果系统中存在大量的僵尸进程,将会造成进程号短缺而无法产生新进程。并且很可能造成资源泄露。

    如何避免僵尸进程

    1.通过信号机制,子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait/waitpid进行处理僵尸进程。代码如下:

    #include <sys/types.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <signal.h>
    #include <errno.h>
    #include <signal.h>
    static void sig_child(int signo);
    
    //僵尸进程
    int main(void) {
    	
    	pid_t pid;
    	
    	printf("before fork pid:%d
    ", getpid());
    
    	//采用信号量机制
    	signal(SIGCHLD, sig_child);
    
    	int abc = 10;
    
    	pid = fork();
    	if(pid < 0) {
    		fprintf(stderr, "fork failed");
    		return 1;
    
    	}
    	else if(pid == 0) {
    		abc++;
    		printf("child:%d, parent:%d
    ",getpid(), getppid());
    		printf("abc:%d", abc);
    		exit(0);
    	}
    	else {
    		abc++;
    		printf("parent:pid:%d 
    ", getpid());
    		printf("abc:%d 
    ", abc);
    		sleep(20);
    	}
    
    	printf("fork after ... 
    ");
    
    }
    
    static void sig_child(int signo) {
    	pid_t pid;
    	int stat;
    	while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
    		printf("child pid=%d terminated.
    ", pid);
    }
    

    2.或者fork两次,原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。(这种方法不是很理解,所以没有代码…哪位好心人能给我讲一讲吗?)

    孤儿进程

    如果父进程没有调用wait()就终止了,那么对于该父进程的子进程将会成为孤儿进程(orphan process)。Linux/Unix对这种情况的处理是:将init进程作为孤儿进程的父进程,进程init定期调用wait(),以便收集任何孤儿进程的退出状态,并释放孤儿进程标识符和进程表条目。

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    //孤儿进程
    int main(void) {
    
    	pid_t pid;
    	pid = fork();
    
    	if(pid < 0) {
    		fprintf(stderr, "fork failed");
    		return 1;
    	}
    	else if(pid == 0) {
    		printf("I am Child, pid=%d, ppid=%d
    ", getpid(), getppid());
    		sleep(10);
    		printf("I am Child, pid=%d, ppid=%d
    ", getpid(), getppid());
    	}
    	else {
    		sleep(1);
    		printf("I am Parent, pid=%d
    ", getpid());
    	}
    
    	return 0;
    }
    

    执行结果:

    shanlei@shanlei-Lenovo-ideapad-110-15ISK:/var/www/c_code/操作系统$ ./3
    I am Child, pid=8300, ppid=8299
    I am Parent, pid=8299
    shanlei@shanlei-Lenovo-ideapad-110-15ISK:/var/www/c_code/操作系统$ I am Child, pid=8300, ppid=1
    

    进程表条目:

    F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
    4 S     0     1     0  0  80   0 - 56380 -      ?        00:00:09 systemd
    ....
    1 S  1000  8300     1  0  80   0 -  1127 hrtime pts/0    00:00:00 3
    4 R  1000  8301  7084  0  80   0 -  9006 -      pts/1    00:00:00 ps
    

    由于子进程sleep了10s,所以父进程终止时,子进程还没有结束,此时将会该子进程成为孤儿进程,可以看到这个时候该孤儿进程的父进程PID为1,也就是所谓的systemd(init)进程。

    init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。

  • 相关阅读:
    电路原理图分析
    GPIO学习——用户空间操作
    在Android上运行Java和C程序
    Android命令行工具学习总结
    Android蓝牙学习笔记
    33 把数组排成最小的数
    233 Number of Digit One
    32 从1到n整数中1出现的次数
    31 连续子数组的最大和
    《大型网站技术架构》学习笔记
  • 原文地址:https://www.cnblogs.com/lishanlei/p/10707751.html
Copyright © 2011-2022 走看看