Linux回收子进程

孤儿进程

孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

#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;
    }

    return 0;
}

僵尸进程

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

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

通过下面的命令可查找僵尸进程: 

ps -A -ostat,ppid,pid,cmd | grep -e '^[Zz]' 

kill -HUP 12339来杀掉这个僵尸进程

wait函数

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。
父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:

  1. 阻塞等待子进程退出
  2. 回收子进程残留资源
  3. 获取子进程结束状态(退出原因)。
  pid_t wait(int *status); 

成功:清理掉的子进程ID;失败:-1 (没有子进程)
当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)
可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:

  1. WIFEXITED(status) 为非0→ 进程正常结束
    WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)
  2. WIFSIGNALED(status) 为非0 → 进程异常终止
    WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
  3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态
    WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
    WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

waitpid函数

作用同wait,但可指定pid进程清理可以不阻塞

pid_t waitpid(pid_t pid, int *status, in options);

成功:返回清理掉的子进程ID;失败:-1(无子进程)
特殊参数和返回情况:
参数pid:

>0 回收指定ID的子进程
-1 回收任意子进程(相当于wait)
0 回收和当前调用waitpid一个组的所有子进程
< -1 回收指定进程组内的任意子进程
返回0:参3为WNOHANG,且子进程正在运行。

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。

父进程用wait函数回收子进程
wait的工作原理:
(1)子进程结束时,系统向其父进程发送SIGCHILD信号
(2)父进程调用wait函数之后就会阻塞在wait函数中,这个时候和scanf的阻塞是类似的
(3)父进程被SIGCHILD信号唤醒,就是父进程阻塞之后一直在等待这个信号,而我们的scanf函数的阻塞则是等待我们输入。唤醒之后父进程就会去回收子进程
(4) 父子进程之间是异步的 ,也就是说子进程什么时候结束父进程是不知道的。两者就是通过SIGCHILD信号来解决之间异步通信的问题
(5)若父进程没有任何子进程的时候函数 wait会返回错误
 
wait函数是一个系统调用。函数原型如下:
pid_t wait(int *status);   注:没有const的表示这个参数是一个输出型参数
status返回的是子进程结束的一个状态。
pid_t则是返回结束的子进程的进程ID ,当前进程有可能存在多个子进程,所以这个时候返回的就是结束的那一个(这里我们会发现一个问题,就是wait函数是没有办法指定回收那一个进程的,只能是有一个子进程结束了,发送一个SIGCHILD信号之后他就执行。比较死板,还不够灵活)。
 
几个宏定义的应用:
WIFEXITED宏定义用来测试子进程是否是正常终止的。
使用方法:WIFEXITED(status)
WIFSIGNALED用来判断是否非正常终止,从字面上我们知道这个是被信号终止的
WEXITSTATUS 用来得到子进程正常终止的返回值,注意这里的返回值是无符号的整型,如果是负数的话会自动补齐。
 
wait和waitpid的区别:
(1)两者的基本功能是一样的,回收子进程
(2)waitpid所拥有的一个新的功能就是 能够回收指定的子进程
(3)waitpid可以使用 阻塞或者非阻塞的方式回收子进程
 
waitpid 的函数原型:
pid_t waitpid(pid_t pid, int *status ,int options);
  • waitpid当中的参数pid如果传入的是-1的话那么就是回收任意一个结束的进程。
  • options传入的就是选择阻塞式和非阻塞式的方式。
 
fork函数是用来创建子进程的,在这里温习一下,fork函数创建一次,返回两次,大于零的返回值既是子进程的进程ID同时我们还可以通过在if当中判断这个返回值进行子进程的程序编写,而返回值等于0的代表的就是我们的父进程,小于0的话就是说发生了错误。
 
竞态:全称就是竞争状态,多进程的环境下多个进程同时拖占系统资源,比如说两个进程需要同时使用一个IO,这个时候就会出现竞争的情况,这个竞争状态对于操作系统来说是有害的,会导致程序的结果的不稳定性。但是我们理想状态下是要保证程序运行结果就是我们想要的内容的。操作系统也提供了很多的方法给我们使用来避免竞争状态的产生,我们在写程序的时候就要考虑到这个方面的内容,用适当的方法解决竞争状状态。
 
 

猜你喜欢

转载自blog.csdn.net/usstmiracle/article/details/107409403
今日推荐