孤儿进程/僵尸进程<defunct> Z+


孤儿进程

爹生孩子;爹死了,孩子还或者,孩子叫孤儿进程;孤儿进程被init进程领养,init进程变为孤儿进程的父亲

int main(){                                                            
  pid_t pid=fork();                                                    
  if(pid<0){                                                           
    perror("fork fail");                                               
    exit(1);                                                           
  }                                                                    
  if(pid==0){  //子进程                                                        
    sleep(1); //父进程结束后,子进程变成孤儿进程
    printf("child=%d,parent=%d\n",getpid(),getppid());  //它由                 
 }                                                                     
  if(pid>0){//父进程                                          
    printf("parent=%d\n",getpid()); //printf完成后,父进程直接结束
  }                                                                    
  return 0;                                                            
}  

僵尸进程:< defunct > Z+

爹生孩子;孩子死了,爹还活着,但是爹不去释放PCB

int main(){
  pid_t pid=fork();
  if(pid<0){
    perror("fork fail");
    exit(1);
  }
  if(pid==0){  //子进程printf完成后,直接退出
    printf("child=%d,parent=%d\n",getpid(),getppid());
  }
  if(pid>0){
    while(1){  //但是父进程一直在死循环,没有去回收子进程-->导致子进程变成僵尸进程
      sleep(1);
      printf("parent=%d\n",getpid());
    }
  }
  return 0;
}

[gjw@localhost ~]$ ps aux | grep test
gjw        3914  0.0  0.0   4212   348 pts/0    S+   08:35   0:00 ./test
gjw        3915  0.0  0.0      0     0 pts/0    Z+   08:35   0:00 [test] <defunct>

常用的宏

1.WIFEXITED(status):为非0 --->判断进程是否正常结束
	WEXITSTATUS(status):
		如果WIFEXITED(status)为非0,使用WEXITSTATUS(status)
			--->[获取进程退出状态(exit/return的参数)]
2.WIFSIGNALED(status):为非0--->判断进程是否被信号终止
	WTERMSIG(status):
		如果WIFSIGNALED(status)为非0,使用WTERMSIG(status)
			--->[取得使进程终止的那个信号的编号]
3.WIFSTOPPED(status) //为非0--->如果子进程被暂停/停止
	WSTOPSIG(status) 
			--->返回导致子进程停止执行的信号数量
代码:
  if(WIFEXITED(status))//正常退出--exit/return
  {
    printf("exit normaly with : %d\n",WEXITSTATUS(status));//获取正常退出的状态码
  }
  else if(WIFSIGNALED(status))//被信号终止--signal
  {
    printf("receive a signal with : %d\n",WTERMSIG(status));//获取信号码
  }
  else if(WIFSTOPPED(status))//程序停止了/接收到停止信号--ctrl +c
  {
    printf("process stoped: %d\n",WSTOPSIG(status));//获取信号码
  }
  else//其他方式的停止
  {
    printf("other exit..!\n");
  }

wait-阻塞函数

pid_t wait(int* status);
注意:该函数调用一次只能回收一个子进程
	返回值:
		-1:回收失败,已经没有子进程
		>0:回收是子进程对应的pid
	传出参数:status
		判断子进程是如何死的
			正常退出
			被某个信号杀死了

案例1
程序功能:使用kill -9 子进程pid杀死子进程后,在父进程中调用wait函数去回收子进程。

int main(){          
  pid_t pid=fork();  
  if(pid<0){         
    perror("fork fail");         
    exit(1);         
  }      
  if(pid==0){        
    while(1){   //子进程一直循环printf------>调用[kill -9 子进程pid]杀死该进程           
      sleep(1);      
      printf("child=%d,parent=%d\n",getpid(),getppid()); 
    }    
  }      
  if(pid>0){//父进程:调用wait函数,释放死掉的子进程      
    printf("parent=%d\n",getpid());          
         
    int status;     
    //子进程没死亡,一直阻塞在wait函数处;
    //当子进程死亡后,wait函数回收子进程资源&&退出 
    pid_t w_pid=wait(&status);  
    
    if(WIFEXITED(status))           
      printf("exit value:%d\n",WEXITSTATUS(status));     
    if(WIFSIGNALED(status))      
      printf("exit by signal:%d\n",WTERMSIG(status));    
    
    printf("died child pid=%d\n",w_pid);     
  }      
         
  return 0;          
}

waitpid

在这里插入图片描述

pid_t waitpid(pid_t pid, int *status, int options);
参数:
	1.pid
		pid>0:指定pid的子进程
		pid==-1:等待任意一个子进程(等价于wait)
			—循环回收:while((w_pid=waitpid(-1,&status,WNOANG)!=-1)
			
		pid==0:当前进程组所有的子进程
		pid<02.status:子进程的退出状态,用法同wait函数(使用宏)
	3.options:
		设为0,函数为阻塞
		设为WNOHANG,函数为非阻塞
返回值:
	-1:回收失败,已经没有子进程
	>0:返回清理掉的子进程ID
	如果为非阻塞:
		=0:子进程处于运行状态

案例2

父进程产生两个子进程
	子进程1,调用ls -l
	子进程2,产生段错误
父进程,负责使用wait或waitpid回收退出的子进程
  int main(){
    int i;
    pid_t pid;
    for(i=0;i<2;i++){
      pid=fork();    
      if(pid==0)
        break;
    }
         
    if(i==0){        
      execlp("ls","ls","-l",NULL);           
      perror("execl");           
      exit(1);       
    }    
    if(i==1){        
      char* p="Student";         
      p[5]='K';   //会发生段错误   
    }    
         
    if(i==2){ //父进程,回收死亡的子进程      
      int status;    
      pid_t w_pid;
      
/*case1:wait*/
      while((w_pid=wait(&status))!=-1){

/*         
case2:waitpid
      while( (w_pid=waitpid(-1,&status,WNOHANG))!=-1 ){  //循环回收 
       if(w_pid==0)  //返回值为0,返回的是正在运行的进程的pid(而不是死亡的进程的pid)         
         continue;   
*/
  
       printf("------wait child's pid = %d\n",w_pid);    
  
       if(WIFEXITED(status))
          printf("return value=%d\n",WEXITSTATUS(status));
       if(WIFSIGNALED(status))   
          printf("signal value=%d\n",WTERMSIG(status));
      }
    }
    return 0;        
  }
[gjw@localhost homework]$ ./test 
总用量 16
-rwxrwxr-x. 1 gjw gjw 8696 1015 09:59 test
-rw-rw-r--. 1 gjw gjw  812 1015 09:59 test.c
------wait child's pid = 8730
return value=0
------wait child's pid = 8731
signal value=11       //信号11表示的是[段错误]

综合对比:wait和waitpid

在这里插入图片描述

/*  方法2:错误的方法
void handler(int signo){         
  printf("waitpid child process\n");         
  wait(NULL);      
}
如果有5个客户端连接服务器
当5个客户端同时死掉(5个子进程将会同时死掉)--->5个子进程同时向老爹发送SIGCHLD信号
但是:SIGCHLD是不可靠信号,因此老爹可能收到1个,2个或者5个(结果不确定),因此使用
wait函数,只能清理一个子进程后就退出,--->正确地做法:使用waitpid
*/

//方法3:正确的方法
void handler(int signo){         
  printf("waitpid child process\n");         
  if(signo==SIGCHLD){
    while(waitpid(-1,NULL,WNOHANG)!=-1);     
  }      
}
客户端代码:一次性创建5个客户端,连接同一个服务器

#define SOCKNUM 5
int main(){
  int sockfd[SOCKNUM];
  int i;
  for(i=0;i<SOCKNUM;i++){
    sockfd[i]=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd[i]==-1)
      ERR_EXIT("socket");

    struct sockaddr_in svraddr;
    svraddr.sin_family=AF_INET;
    svraddr.sin_port=htons(8001);
    svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");

    if(connect(sockfd[i],(struct sockaddr*)&svraddr,sizeof(struct sockaddr))<0)
      ERR_EXIT("connect");
   
    printf("connect svr success:svraddr=%s,port=%d\n",inet_ntoa(svraddr.sin_addr),ntohs(svraddr.sin_port));
  }
}
服务器代码
	然后按Ctrl+C终止客户端代码,此时同时断开5条连接---->服务器端代码,waitpid处理僵尸进程

void handler(int signo){         
  printf("waitpid child process\n");         
  if(signo==SIGCHLD){
    while(waitpid(-1,NULL,WNOHANG)!=-1); //正确    
    //wait(NULL); //错误    
  }      
}        
int main(){          
  //signal(SIGCHLD,SIG_IGN);  //case 1     
  signal(SIGCHLD,handler); //case 2
  ... ...
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值