Linux 退出进程和销毁进程

退出进程

在 Linux 中,共有 8 种进程的退出方法,包括 5 种正常的方法和 3 种异常的方法。

通常来说 Linux 的应用代码会调用 exit 系列函数来退出一个进程。

#include
#include
void exit(int status);
void _exit(int status);
void _Exit(int status);

exit 系列函数没有返回值,使用一个终止状态的整型变量作为参数,Linux 内核会对这个终止状态进行检查:当异常终止时,Linux 内核会直接产生一个终止状态字,描述异常终止的原因,可以通过 wait 或者 waitpid 函数来获得终止状态字;父进程也可以通过检查终止状态来获得子进程的状态。

如果是以下三种状态:

  • 在调用 exit 系列函数的时候不带终止状态。
  • main 函数执行了一个无返回值的 return。
  • main 函数的返回值不是一个整型。

则 Linux 会认为该进程的终止状态是未定义的。
如果 main 函数的返回值定义为整型并且 main 函数是执行到最后一条语句返回, 则该进程的终止状态是0。

在 main 函数中调用 return 语句返回,绝大多数等效于调用 exit 系列函数。

这两个函数的调用过程如图
Linux 退出进程和销毁进程_第1张图片

  • _exit 函数:直接使进程停止运行,清除其使用的内存空间,清除其在内核中的各种数据结构。
  • exit 函数:在 _exit 函数的基础上做了一些包装,在执行退出之前加了若干道工序。

这两个函数的最大区别在于:前者在调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件;后者直接使进程停止运行,清除其使用的内存空间,销毁其在内核中的各种数据结构,即图中的 “清理 I/O 缓冲” 一项。

销毁进程

当一个进程使用 exit 系列函数退出时,会在内存中保留部分数据以供父进程查询,同时也会产生一个终止状态字,然后 Linux 内核会发出一个SIGCHLD 信号通知父进程,因为子进程的结束对于父进程是异步的,所以这个 SIGCHLD 信号对于父进程也是异步的,父进程可以不响应。

父进程对于退出之后的子进程的默认状态是不处理的,但是这样会导致系统中的僵尸进程浪费了系统资源,此时应该调用 wait 函数或 waitpid 函数对这些僵尸进程进行处理。

在调用 wait 或者 waitpid 函数之后可能存在如下三种情况:

  • 如果该父进程的所有子进程都还在运行,则阻塞父进程自身以等待子进程的运行结束。.
  • 如果有一个子进程已经结束, 则父进程取得该子进程的终止状态,并且立即返回。
  • 如果该父进程没有任何子进程,则立即出错返回。

对 wait 和 waitpid 函数的标准调用格式说明如下:

#include 
#include 
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
1. wait函数
  • 如果 wait 函数调用成功则返回子进程的标识符,如果失败则返回-1。
  • 其中参数 status 是一个整型指针,可以用于存放子进程的终止状态,也可以定义为一个空指针。
  • wait 函数和 waitpid 函数不同,在有一个子进程终止之前,wait 函数让父进程阻塞以等待子进程退出,而 waitpid 有一个参数可以让父进程不阻塞,并且在一个父进程有多个子进程的情况下,如果其中有一个子进程退出则会返回该子进程的进程标识符。

wait 函数返回的终止状态的宏

说明
WIFEXITED(status) 当子进程正常结束时返回为真
WIFSIGNALED(status) 当子进程异常结束时返回为真
WEXITSTATUS(status) 当WIFEXITED(status)为真时调用,返回状态字的低8位
WTERMSIG(status) 当WIFSIGNALED(status)为真时调用,返回引起终止的信号代号
2. waitpid 函数

在使用 wait 函数的时候,如果父进程的任何一个子进程返回则 wait 函数返回,而 waitpid 函数可以通过参数来指定需要等待的子进程。

waitpid 函数的参数 pid 用于对子进程进行相应的筛选。

  • pid > 0:只等待进程 ID 为 pid 的子进程,不管其他已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid 就一直等待下去。
  • pid = -1:等待任何一个子进程退出,没有任何限制,此时 waitpid 等价于 wait。
  • pid = 0:等待同一个进程组中的任何子进程,如果某一子进程已经加入了其他进程组,则 waitpid 不会对它做任何理睬。
  • pid < -1:等待一个指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。

waitpid 函数的参数 options 用于进一步控制 waitpid 函数的操作,其可以是0,也可以是 WNOHANG 和 WUNTRACED 两个选项之一,或者是使用 “I” 符号连接的“或”操作。

  • WNOHANG:如果由 pid 指定的子进程并不是立即可用的,则 waitpid 函函数不阻塞,此时返回“0”。
  • WUNTRACED:如果其实现支持作业控制,而由 pid 指定的任意子进程已经处于暂停状态,并且未报告过,则返回其状态。

对于 waitpid 函数而言,如果指定的进程或者进程组不存在,或者参数 pid 指定的进程不是父进程所调用的子进程,都会出错。

总体而言,waitid 函数提供了 wait 函数所没有的如下三个功能:

  • 能够等待指定的一个进程结束。
  • 能够不阻塞父进程获得子进程的状态。
  • 支持作业控制。

使用 waitpid 函数退出进程

#include                                                                                                                                                    #include 
#include 
#include 
#include 
#include 
int main()
{
    pid_t pid;
    if((pid=fork())<0)  //创建子进程失败
    {
        perror("创建子进程失败!\n");
        exit(0);
    }
    else if(pid==0)  //进入子进程
    {
        if((pid=fork())<0)
        {
            perror("创建子进程失败!\n");
            exit(0);
        }
        else if(pid>0)
        {
            exit(0);
        }
        else
        {
            sleep(2);
            printf("这是第二个子进程,parent pid=%d\n",getppid());
            exit(0);
        }
    }  
    if(waitpid(pid,NULL,0)!=pid)
    {
        perror("waitpid 销毁进程失败!\n");
        exit(0);
    }
    exit(0);
}

编译运行,可以看到在退出了第2个子进程后会停止运行一直等待,此时可以使用 ctrl + c 退出。

[cassie@localhost 练习]$ gcc waitpid.c -o waitpid
[cassie@localhost 练习]$ ./waitpid
[cassie@localhost 练习]$ 这是第二个子进程,parent pid=1
^C

你可能感兴趣的:(Linux,C语言,Linux,进程,退出进程,销毁进程)