进程控制:结束进程、等待进程结束、避免僵尸进程

一、结束进程
  void exit(int value);
  void _exit(int value);

  exit()和_exit()函数功能一样,主要的区别在于:exit()是标准库函数,会刷新缓冲区,_exit()是系统调用,不会刷新缓冲区。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    printf("hi, lh, you are so good"); // 打印,没有换行符"\n"

    exit(0);      // 结束进程,标准库函数,刷新缓冲区,printf()的内容能打印出来
    // _exit(0);  // 结束进程,系统调用函数,printf()的内容不会显示到屏幕

    while(1);   // 不让程序结束

    return 0;
}

  
二、等待进程结束
  在每个进程退出的时候,内核会释放该进程所有的资源,包括打开的文件,占用的内存等,但是仍然会为其保留一定的信息,这些信息主要是指进程控制块信息(包括进程号、退出状态、运行时间等)。如果父进程不回收子进程的资源,就会导致僵尸进程的产生。

  当一个进程正常或者异常终止时,内核就会向父进程发送SIGCHLD信号,相当于告诉父进程他哪个子进程挂了。而父进程可以调用wait()或waitpid()函数等待子进程结束,获取子进程结束时的状态,同时回收他们的资源。

#include <sys/wait.h>

pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
//status进程退出时的状态信息,如果不关心终止状态可指定为空指针

//pid有四种情况:
//1.pid==-1 等待任意子进程
//2.pid>0 等待进程ID与pid相等的子进程
//3.pid==0 等待组ID等于调用进程组ID的任意子进程
//4.pid<-1 等待组ID等于pid绝对值的任意子进程

//options控制waitpid的操作:
//1,2是支持作业控制
//1.WCONTINUED
//2.WUNTRACED

//wait/waitpid的返回值
//正常都是返回结束的子进程的pid,但是waitpid稍微复杂一点:
//(1) 如果设置了WNOHANG,而在调用的时候发现没有子进程退出,返回0
//(2) 如果调用出错,返回-1,这是errno会被设置成相应的错误值

//status中包含了多个字段,直接使用没有意义,下面的两个宏取出其中的每个字段
WIFEXITED(status);  //如果进程是正常终止的,取出的字段值非零
WEXITSTATUS(status);  //返回子进程的退出状态,也就是子进程exit(value)的value的值 

三、避免僵尸进程
  进程已经结束,但进程占用的资源未被回收,这样的进程称为僵尸进程。

  当我们fork()一次后,存在父进程和子进程。这时有两种方法来避免僵尸进程:
   1) 父进程调用wait()/waitpid()等函数来接收子进程退出状态
   2)父进程先结束,子进程则自动托管到init进程(pid = 1)

第一种情况使用wait()/waitpid()回收
子进程先于父进程结束,当父进程回收之前,子进程将处于僵尸状态
子进程结束前,父进程阻塞在wait调用

#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>

int main(int agc, char *argv[])
{
    pid_t pid;
    if ((pid = fork()) < 0)
    {
        printf("fork error.\n");
        return -1;
    }
    else if (pid == 0)  //子进程
    {
        printf("in child.\n");
        sllep(2);
    }
    else
    {
        int stat;
        pid_t pid = wait(&stat);
        printf("child exit stat:%d.\n", stat);
    }

    return 0;
}

  在TCP Server中,通常父进程都是一直存在,因此需要用wait()/waitpid()来进行回收,但是由于wait函数会阻塞父进程,所以一般是通常注册信号处理函数,通过非阻塞的方式来处理。因为前面说过,子进程退出时会给父进程发送SIGCHLD信号。

void sig_child(int signo)
{
    int stat;
    pid_t pid;
    while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
    {
        printf("child exit:%d.\n", pid);
    }   
}

typedef void (*signal_handler_t)(int);  //定义函数指针

int main()
{
    //...
    signal_handler_t sig_handler1 = sig_child;
    signal(SIGCHILD, sig_handler1);  //注册信号处理函数
    //...
}

解释一下,为什么要用while循环,且为什么要用waitpid的非阻塞模式:
  1、用while循环
  如果在信号处理函数中,有子进程结束,通过while循环一次性回收

  2、非阻塞模式
  保证在回收最后一个终止的子进程后,没有子进程退出时,waitpid会出错返回,主进程从信号处理函数中跳出而不是阻塞在信号处理函数中。

第二种情况init进程负责回收
父进程先结束,这样子进程将托管给init进程,由init进程回收子进程。
如果一个进程fork一个子进程,但不要它等待子进程终止,也不希望子进程处于僵死状态直到父进程终止,实现这一要求需要用两个fork

int main(void)       
{       
    pid_t pid;       

    if ((pid = fork()) < 0)   //error    
    {       
        fprintf(stderr,"Fork error!\r\n");       
        exit(-1);       
    }       
    else if (pid == 0) //第一个子进程  
    {        
        if ((pid = fork()) < 0)      //在第一个子进程中fork
        {        
            fprintf(stderr,"Fork error!/n");       
            exit(-1);       
        }       
        else if (pid > 0) //第一个子进程,也就是第二个子进程的父进程      
            exit(0); /* parent from second fork == first child */      
            //直接退出
        /*    
        * We're the second child; our parent becomes init as soon    
        * as our real parent calls exit() in the statement above.    
        * Here's where we'd continue executing, knowing that when    
        * we're done, init will reap our status.    
        */   
        sleep(2);       
        printf("Second child, parent pid = %d\r\n", getppid());       
        exit(0);       
    }       

    if (waitpid(pid, NULL, 0) != pid) /* wait for first child */      
    {       
        fprintf(stderr,"Waitpid error!\r\n");       
        exit(-1);       
    }       

    /*    
     * We're the parent (the original process); we continue executing,    
     * knowing that we're not the parent of the second child.    
     */      
    exit(0);       
}  
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REaDME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值