Linux系统是一个多用户、多任务的操作系统,它可以同时运行多个用户的多个程序,就必然会产生很多的进程,而不同的进程一般有不同的状态。
下面是网易校招的笔试题
Linux下的进程有哪3种状态?( )
A 精确态,模糊态和随机态
B 运行态,就绪态和等待态
C 准备态,执行态和退出态
D 手动态,自动态和自由态
答案为B选项
实际上Linux系统中进程的状态分为R、S、D、T、Z和X 6种状态。
1.R Running or runnable(on run queue)
处于运行或能(可)运行状态,即进程正在运行或在运行队列(可执行队列)中等待。只有在该状态的进程才可能在CPU上运行,同一时刻可能有多个进程处于该状态。【ps:很多教科书上将正在CPU上执行的进程的状态定义为Running,将可执行但尚未被调度执行的进程状态定义为Ready,这2种状态在Linux下统一为R状态】
2.S Interruptible sleep
处于可中断的睡眠状态,即进程在休眠中,由于在等待某个事件的完成(或等待某个条件的形成或等待某个信号等)【ps:等待socket连接、等待信号量等】而被挂起;当这些事件发生时,对应的等待队列中的一个或多个进程将被唤醒。一般情况下,进程列表中绝大多数进程都处于该状态。
3.D Uninterruptible sleep
处于不可中断的睡眠状态,不可中断指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号,无法用kill命令杀死,进程必须等待直到有中断发生。
一般由等待I/O【磁盘I/O、网络I/O和其他外设I/O】引起,同步I/O在做读或写操作时,此进程不能做其他事情,只能等待;如果程序采用异步I/O,这种状态应该就很少被看见了。
在进程对某些硬件进行操作时(例如进程调用read系统调用对某个设备文件进行读操作),可能需要使用Uninterruptible sleep状态对进程进行保护,以避免进程与设备交互的过程被打断,造成设备陷入不可控的状态。这种情况下Uninterruptible sleep状态非常短暂,通过ps命令基本上不可能捕捉到。
【注】:When the process is sleeping uninterruptibly, the signal will be noticed when the process returns from the system call or trap.
4.T Stopped or Traced
处于暂停或跟踪状态。进程收到SIGSTOP、SIGSTP、SIGTIN、SIGTOU等信号进入暂停状态(除非进程处于不可中断的睡眠状态);当接着向进程发送1个SIGCONT信号,进程可以从暂停状态恢复到运行或能运行状态。
当进程被跟踪时,它处于被跟踪状态。“被跟踪”指进程暂停下来,等待跟踪它的进程对它进行操作。例如在GDB调试中,对被跟踪的进程设置某个断点,进程执行到断点处停下来的时候就处于被跟踪状态。
【注】:对于进程本身而言,暂停状态和被跟踪状态很类似,都表示进程暂停下来;但被跟踪状态相当于在暂停状态之上多了一层保护,处于被跟踪状态的进程不能响应SIGCONT信号而被唤醒,只能等到调试进程通过ptrace系统调用执行ptrace_cont、ptrace_detach等操作(通过ptrace系统调用的参数指定操作),或调试进程退出,被调试的进程才能恢复到R状态。
5. Z Dead-Exit_Zombie
处于僵死状态,也称退出状态。它指进程已经结束,放弃了几乎所有的内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置来记载该进程的退出状态等信息(task_struct结构体[保存了该进程的退出码])供其他进程收集。
如果该子进程的父进程没有收到SIGCHLD信号,故未调用wait(如wait4、waitid)处理函数等待子进程结束,又没有显示忽略该信号,该子进程就一直保持僵死状态。只要父进程不退出,这个僵死的子进程就一直存在。多进程写不好的话,这种状态是常见的。
如果该子进程(PID)的父进程(PPID)结束了,则init进程(PID为1)自动接手该子进程,给它收尸。
当进程退出时,会将它的所有子进程都托管给别的进程(使之成为别的进程的子进程)。托管的进程可能是退出进程所在进程组的下一个进程(如果存在的话)或者是init进程。故每个进程、每时每刻都有父进程存在。
linux系统启动后,第1个被创建的用户态进程就是init进程,它通常有2个作用:
1.执行系统初始化脚本,创建一系列的进程(都是init进程的子孙);
2.在一个死循环中等待其子进程的退出事件,并调用waitid系统调用来完成“收尸”工作。
init进程不会被暂停,也不会被杀死(由内核来保证)。它在等待子进程退出的过程中处于可中断的睡眠状态,“收尸”过程中处于运行状态。
处理僵死进程的办法:先找出父进程号(PPID),然后kill父进程,之后子进程(僵死进程)被托管到其他进程(如init进程),然后由init进程将子进程的尸体(task_struct)释放掉。
可以通过如下命令查看僵死进程:ps -ef|grep defunc
6.X Dead-Exit_Dead
进程在退出过程中可能不会保留它的task_struct。例如某个进程是多线程程序中被detach过的进程;或者父进程通过设置SIGCHLD信号的Handler为SIG_IGN,显示的忽略了SIGCHLD信号。
此时该进程被置于exit_dead退出状态,这意味着接下来的代码立即会将该进程彻底释放。故exit_dead状态非常短暂,几乎不可能通过ps命令捕捉到。
再回过头来看网易的那道题,B选项的运行态和就绪态可以统一称为R状态,而等待态则理解为等待/阻塞态,包括S(可中断的睡眠状态)、D(不可中断的睡眠状态)、T(暂停或跟踪状态)、Z(僵死状态)【X状态很短暂,忽略不计】