linux调度原理(四)

本篇主要讲解进程的运行状态和退出状态。为了方便对调度进程进行管理,linux对进程不同的运行状态和退出状态进行了划分和标识。

  • 进程的运行状态保存在task_struct结构体中的state成员中

  • 进程的退出状态保存在task_struct结构体中的exit_state成员中

 

进程运行状态

linux主要共划分了6种运行状态:TASK_RUNNING、TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE、__TASK_STOPPED、__TASK_TRACED和TASK_DEAD。

 

TASK_RUNNING(R)

当进程处于运行态或者就绪态等待调度运行时,该进程的运行状态即为TASK_RUNNING。在ps aux命令中用R(Running)标识进程处于TASK_RUNNING状态。

注意,TASK_RUNNING状态不只是指正在cpu上运行的进程状态,还指处于就绪态的进程,此时该进程在等待cpu的调度执行。cpu的运行队列上保存的进程都是处于TASK_RUNNING状态的进程,调度时也是针对这些进程,每次从运行队列上筛选出一个最优的进程调度运行。

TASK_INTERRUPTIBLE(S)

可中断的睡眠状态,又称为浅睡眠状态。表示进程正在睡眠等待,并且睡眠过程中可以被信号唤醒,这是和TASK_UNINTERRUPTIBLE状态最大的不同。在ps aux命令中使用S(Sleep)标识进程处于TASK_INTERRUPTIBLE状态。

 TASK_UNINTERRUPTIBLE(D)

不可中断睡眠,又称为深度睡眠状态。表示进程正在睡眠等待,并且睡眠的过程中不可以被信号唤醒。通常这个进程在等待I/O的情况。由于深度睡眠状态的进程不可被信号唤醒,因此我们不能使用信号将其杀死,包括SIGKILL(9)也不行。该状态通常用于内核中一些不能打断的流程中,如read一些设备文件和物理设备时。

在ps aux命令中使用D标识进程处于TASK_INTERRUPTIBLE状态。

__TASK_STOPPED(T)

终止状态。该状态下的进程停止运行,如进程收到SIGSTOP(19,暂停信号)、SIGTTIN、SIGTSTP或者SIGTTP等信号时,进入该状态。

在ps aux命令中使用T(stop)标识进程处于__TASK_STOPPED状态。

__TASK_TRACED(T) 

进程正在被跟踪状态。注意,“正在被跟踪”是指在进程暂停下来,进程等待跟踪它的进程下发命令操作。如gdb对被跟踪的进程打了一个断点,该进程运行到断点处停下来了,此时该进程即处于__TASK_TRACED状态。而其他时候,该被跟踪进程的状态还是处于上面提到的那些状态,即和正常进程一样。

在ps aux命令中使用T(stop)标识进程处于__TASK_TRACED状态,和__TASK_STOPPED标识相同。

TASK_DEAD

进程在退出时对应的状态。在进程退出的过程中,进程占有的所有资源(包括内存空间、可执行代码等)都会被回收,除了task_struct这个结构体。task_struct这个结构体中保存了进程的退出状态(task_struct中的exit_state),这样方便父进程能获取子进程的退出状态。

进程退出状态

linux主要划分了3种退出状态:EXIT_ZOMBIE、EXIT_DEAD和EXIT_TRACE(EXIT_ZOMBIE | EIXT_DEAD)。

EXIT_ZOMBIE(Z)

僵尸态。如果一个进程退出后,其父进程没有将其内核中的task_struct结构体进行回收,那么该进程即变为僵尸进程,自身处于僵尸态。这里面将两个概念:僵尸进程和孤儿进程。

  • 僵尸进程:如果子进程退出后,其父进程还存在,但没有将其内核中的task_struct结构体进行回收,那么该子进程将变为僵尸进程。
  • 孤儿进程:父进程退出后,它的子进程将由init进程托管,init进程成为它们的父进程,这些子进程成为孤儿进程。

注意,孤儿进程不会对系统有什么危害,但僵尸进程对系统有危害。僵尸进程由于其内核中的task_struct结构体还未释放,继续占用进程表和内存空间。若产生了大量的僵尸进程将消耗大量的系统资源,影响系统稳定性。

在ps aux命令中使用Z(Zombie)标识进程退出时处于EXIT_ZOMBIE状态。

EXIT_DEAD(X)

进程的最终退出状态即为EXIT_DEAD,进程即将被销毁。进程的资源(task_struct结构)被父进程回收后,进程的退出状态会由EXIT_ZOMBIE变为EXIT_DEAD。

进程状态变化

通过上述介绍,我们已经了解到进程有很多状态。在linux运行过程中,进程在一定条件下可以由一种状态变化为另一种状态。

在介绍之前,我们首先要树立一个观念,即进程状态的变化只有两个方向:

  1. 从运行态/就绪态TASK_RUNNING变为其他状态。
  2. 从其他状态变为运行态/就绪态TASK_RUNNING。

任何两个非TASK_RUNNING状态都不能直接转变,它们都需要先转变为TASK_RUNNING状态再变为其他,也即必须通过TASK_RUNNING状态(唤醒)过度。

这个其实也很好理解,进程只有先处于TASK_RUNNING才能得到执行,在执行的过程中再改变其状态。各个运行状态的转化图如下:

 各个状态间的转化分析如下:

  • 我们在通过fork创建子进程时,子进程会继承父进程的状态。由于父进程肯定在执行,因此创建的子进程状态一般也为TASK_RUNNING运行态。处于运行态/就绪态的子进程将被放到cpu的运行队列上。
  • TASK_RUNNING包括两类进程:处于cpu运行队列上就绪态的进程和正在cpu上执行的进程。调度器通过schedule函数将cpu运行队列上处于就绪态的进程放到cpu上运行;                      当正在cpu上执行的进程被抢占时,该进程将被被放回cpu的运行队列上。
  • 若正在cpu上运行的进程若调用sleep函数、等待特定事件阻塞等主动放弃cpu,自身进入睡眠状态,此时进程状态将变为TASK_INTERRUPTIBLE或者TASK_UNINTERRUPTIBLE。          举两个例子:                                                                                                                               1. 若进程是调用锁lock或者sleep进入休眠状态,则进程进入的是TASK_UNINTERRUPTIBLE状态。(相关于有活干,知识缺乏料)                                                                                               2. 若进程是调用epoll_wait进入休眠状态,则进程进入的是TASK_INTERRUPTIBLE状态。(相当于没活儿干,信号可以唤醒它)
  • 当处于睡眠状态的进程(TASK_INTERRUPTIBLE/TASK_UNINTERRUPTIBLE)等待的事件已经发生,任务被唤醒,进程状态将变为TASK_RUNNING,进程将被添加到cpu的运行队列上。
  • 若正在cpu上运行的进程若调用do_exit函数,进程状态将由TASK_RUNNING变为TASK_DEAD,进程被终止。(若调用exit函数时进程不处于TASK_RUNNING状态,则先等待进程状态变为TASK_RUNNING再变为TASK_DEAD)
  • 若正在cpu上运行的进程收到了SIGSTOP、SIGTTIN、SIGTSTP等信号,进程状态将由TASK_RUNNING变为__TASK_STOPPED。(若收到SIGSTOP、SIGTTIN、SIGTSTP等信号时,进程并不处于TASK_RUNNING状态,则先等待进程状态变为TASK_RUNNING再变为__TASK_STOPPED)
  • 若正在cpu上运行的进程被gdb调试运行到了断点处停下来了,等待下发新的命令,则进程的状态将由TASK_RUNNING变为__TASK_TRACED。

进程的运行状态、退出状态总结如下:

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值