进程状态变迁

1. 进程的几种状态描述

进程是动态的活动的实体,指的是进程会有很多种运行状态,一会运行、一会睡眠、一会暂停、一会又继续执行,下图展示了Linux进程从创建到被回收的全部状态,以及这些状态发生转化的条件

Linux进程状态转化图

  1. “蛋生”:从“蛋生”可以看到,一个进程的诞生是其父进程调用fork()开始的
  2. TASK_RUNNING:进程刚被创建出来时,处于TASK_RUNNING状态,从上图可以看到处于该状态的进程可以是正在进程队列中等待排队,也可以占用CPU正在运行,我们习惯上称前者为就绪态,后者为执行态。当进程状态为TASK_RUNNING且占用CPU运行(执行态)时才是真正的运行
  3. 就绪态:刚被创建的进程处于就绪态,等待系统调用,内核中的shed()称为调度器,它会根据各种参数选择一个处于就绪态的进程来执行,进程占用CPU之后就可以真正的运行了,但是运行的时间有个限制,比如20ms,这段时间称为时间片,时间片耗完之后,如果此时进程还没结束,那么该进程重新进入就绪态。另外,处于执行态的进程即使占用的时间片没有耗完,也可能被更高级别的进程抢占CPU(进程抢占),该进程被迫进入就绪态
  4. 睡眠态/挂起态:进程处于执行态时,可能会由于某些IO资源的不可获取而被置为**“睡眠态/挂起态”,比如进程要读取一个管道文件,而管道文件数据为空,或者进程要获取一个资源锁而不可得,或者进程自己调用睡眠函数sleep()来强制自己挂起,这些都会使得进程装填变为TASK_INTERRUPIBLE或TASK_UNINTERRUPIBLE,它们的区别是后者跟某些硬件设置相关,在睡眠期间不可以响应信号,因此TASK_UNINTERRUPIBLE被称为深度睡眠**,而TASK_UNINTERRUPIBLE是可以响应信号的。当进程所等待的资源变得可获取时,进程状态会被重置为TASK_RUNNING或者就绪态
  5. 暂停态:当进程收到SIGSTOP或者SIGTSTP中的一种信号时,状态会被置为TASK_STOPPED,此时称为“暂停态”,该状态下的进程不参与调度,但系统资源不释放,直到收到SIGCONT信号时,状态置为就绪态
  6. 僵尸态:进程死亡的状态有许多种,可以是寿终正寝,也可以是被异常杀死。比如图中,main函数调用return或者调用exit,包括在线程中调用pthread_exit等都是正常退出,而收到信号被异常杀死属于异常退出。不管进程怎么死亡,最终都会调用内核的do_exit()函数来使得进程变成僵尸态(EXIT_ZOMBIE,进程已经死亡,但是占用的资源没有释放),这里的“僵尸”指的是进程的PCB(进程控制块)。为什么进程都结束了,还留下僵尸呢?因此父进程可以从“尸体”中得到子进程的死亡信息(比如,退出码等)
  7. 死亡态:父进程调用wait()或者waitpid()来查看子进程的“死亡信息”,顺便将子进程的状态由僵尸态(EXIT_ZOMBIE)变为死亡态(EXIT_DEAD),处于死亡态的进程,PCB才能被系统回收

2. 进程状态的代码示例

以下示例代码,综合展示了如何正确使用fork()/exec()函数簇、exit()/_exit()和wait()/waitpid()。程序的功能是:父进程产生一个子进程,让子进程去执行程序child_elf,并且等待它的退出(可以用wait()阻塞等待,也可以用waitpid()非阻塞等待),子进程退出(可以正常退出,也可以异常退出)后,父进程获取子进程的退出状态后打印出来

子进程执行的程序child_elf.c

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

int main(int argc, char**argv){
    printf("I am child_elf...\n");
    #ifdef ABORT
        abort();        //自己给自己发送一个致命信号SIGABRT
    #else
        exit(7);        //正常退出,且退出值为7
    #endif
}

父进程程序wait.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>

int main(int argc, char**argv){

    pid_t pid = fork(); //创建子进程
    if(0 == pid){   //子进程,执行程序child_elf
        execl("./child_elf", "child_elf", NULL);
    }

    if(pid > 0){    //父进程,使用wait()阻塞等待子进程的退出
        int status;
        wait(&status);
        if(WIFEXITED(status)){  //判断子进程是否正常退出
            printf("Child exit normally, exit value is %d\n", WEXITSTATUS(status));
        }
        if(WIFSIGNALED(status)){   //判断子进程是否被信号杀死
            printf("Child killed by signal, signal is %d\n", WTERMSIG(status));
        }
    }
    return 0;
}

代码执行结果

代码执行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

博可睿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值