Linux进程所处状态主要包括,linux进程状态

为了管理进程,内核必须对每个进程所做的事情进行清楚的描述,内核使用PCB(Proccess Control Block)来记录这样的信息。在linux系统中,它就是一个 struct task_struct的类型结构,它的字段包含了与一个进程相关的所有信息。

其中,task_struct 中的state字段描述了进程当前所处的状态。它有一组标志组成,其中每个标志描述一种可能的进程状态。linux 进程可能所处的状态在 include/linux/sched.h(linux-3.8.11)中定义:

/*

* Task state bitmask. NOTE! These bits are also

* encoded in fs/proc/array.c: get_task_state().

*

* We have two separate sets of flags: task->state

* is about runnability, while task->exit_state are

* about the task exiting. Confusing, but this way

* modifying one set can't modify the other one by

* mistake.

*/

#define TASK_RUNNING0

#define TASK_INTERRUPTIBLE1

#define TASK_UNINTERRUPTIBLE2

#define __TASK_STOPPED4

#define __TASK_TRACED8

/* in tsk->exit_state */

#define EXIT_ZOMBIE16

#define EXIT_DEAD32

/* in tsk->state again */

#define TASK_DEAD64

#define TASK_WAKEKILL128

#define task_parked512

#define task_state_max1024

下面的定义TASK_STATE_TO_CHAR_STR说明了上面定义的缩写:

#define TASK_STATE_TO_CHAR_STR "RSDTtZXxKW"

下面内核定义了一些通用宏:

/* Convenience macros for the sake of set_task_state */

#define TASK_KILLABLE(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)

#define TASK_STOPPED(TASK_WAKEKILL | __TASK_STOPPED)

#define TASK_TRACED(TASK_WAKEKILL | __TASK_TRACED)

/* Convenience macros for the sake of wake_up */

#define TASK_NORMAL(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)

#define TASK_ALL(TASK_NORMAL | __TASK_STOPPED | __TASK_TRACED)

/* get_task_state() */

#define TASK_REPORT(TASK_RUNNING | TASK_INTERRUPTIBLE | \

TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \

__TASK_TRACED)

在fs/proc/array.c中也有类似这样的定义:

/*

* The task state array is a strange "bitmap" of

* reasons to sleep. Thus "running" is zero, and

* you can test for combinations of others with

* simple bit tests.

*/

static const char * const task_state_array[] = {

"R (running)",/* 0 */

"S (sleeping)",/* 1 */

"D (disk sleep)",/* 2 */

"T (stopped)",/* 4 */

"t (tracing stop)",/* 8 */

"Z (zombie)",/* 16 */

"X (dead)",/* 32 */

"x (dead)",/* 64 */

"K (wakekill)",/* 128 */

"W (waking)",/* 256 */

"P (parked)",/* 512 */

};

这里边的值分别与上面的宏定义一一对应。

所以linux的进程的状态可以有R(Running), S(Interruptible),D(Uninterruptible), T(Stopped), t(Traced), Z(Zombie), X(Exit_Dead), x(Task_Dead), K(WakeKill), W(Waking)这几种状态。

1.可运行状态 R(TASK_RUNNING)

进程要么在CPU上执行,要么准备执行,即进程位于CPU的可执行队列中。

2.可中断的等待状态 S(TASK_INTERRUPTIBLE)

进程被挂起(睡眠),直到某个条件变为真。产生一个硬件中断,释放进程正在等待的资源,或传递一个信号都是可以唤醒进程的条件(把进程的状态重新设置为TASK_RUNNING)。

3.不可中断的等待状态 D (TASK_UNINTERRUPTIBLE)

与TASK_INTERRUPTIBLE状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号。绝大多数情况下,进程处在睡眠状态时,总是应该能够响应异步信号的。而TASK_UNINTERRUPTIBLE状态存在的意义就在于,内核的某些处理流程是不能被打断的。如果响应异步信号,程序的执行流程中就会被插入一段用于处理异步信号的流程(这个插入的流程可能只存在于内核态,也可能延伸到用户态),于是原有的流程就被中断了。(参见《linux内核异步中断浅析》)在进程对某些硬件进行操作时(比如进程调用read系统调用对某个设备文件进行读操作,而read系统调用最终执行到对应设备驱动的代码,并与对应的物理设备进行交互),可能需要使用TASK_UNINTERRUPTIBLE状态对进程进行保护,以避免进程与设备交互的过程被打断,造成设备陷入不可控的状态。这种情况下的TASK_UNINTERRUPTIBLE状态总是非常短暂的,通过ps命令基本上不可能捕捉到。

linux系统中也存在容易捕捉的TASK_UNINTERRUPTIBLE状态。执行vfork系统调用后,父进程将进入TASK_UNINTERRUPTIBLE状态,直到子进程调用exit或exec(参见《神奇的vfork》)。通过下面的代码就能得到处于TASK_UNINTERRUPTIBLE状态的进程:

#include void main() { if (!vfork()) sleep(100); }

编译运行,然后我们可以试验一下TASK_UNINTERRUPTIBLE状态的威力。不管kill还是kill -9,这个TASK_UNINTERRUPTIBLE状态的父进程依然屹立不倒。

4.暂停状态 T (TASK_STOPPED)

进程的执行被暂停。当进程收到SIGSTOP,SIGTSTP,SIGTTIN 或 SIGTOU 信号后,进入暂停状态。

5.跟踪状态 t (TASK_TRACED)

当进程正在被跟踪时,它处于TASK_TRACED这个特殊的状态。“正在被跟踪”指的是进程暂停下来,等待跟踪它的进程对它进行操作。比如在gdb中对被跟踪的进程下一个断点,进程在断点处停下来的时候就处于TASK_TRACED状态。而在其他时候,被跟踪的进程还是处于前面提到的那些状态。 对于进程本身来说,TASK_STOPPED和TASK_TRACED状态很类似,都是表示进程暂停下来。而TASK_TRACED状态相当于在TASK_STOPPED之上多了一层保护,处于TASK_TRACED状态的进程不能响应SIGCONT信号而被唤醒。只能等到调试进程通过ptrace系统调用执行PTRACE_CONT、PTRACE_DETACH等操作(通过ptrace系统调用的参数指定操作),或调试进程退出,被调试的进程才能恢复TASK_RUNNING状态。 EXIT_ZOMBIE 和 EXIT_DEAD 进程状态是既可以放在 task_struct 的state字段中,也可以放在exit_state字段中。只有当进程的执行终止时,进程的状态才会变为这两种状态中的一种

6.僵死状态 Z (EXIT_ZOMBIE)

进程的执行被终止,除了task_struct结构(以及少数资源)以外,进程占有的所有资源将被回收。于是进程就只剩下task_struct这么个空壳,故称为僵尸。之所以保留task_struct,是因为task_struct里面保存了进程的退出码、以及一些统计信息。而其父进程很可能会关心这些信息。比如在shell中,$?变量就保存了最后一个退出的前台进程的退出码,而这个退出码往往被作为if语句的判断条件。当然,内核也可以将这些信息保存在别的地方,而将task_struct结构释放掉,以节省一些空间。但是使用task_struct结构更为方便,因为在内核中已经建立了从pid到task_struct查找关系,还有进程间的父子关系。释放掉task_struct,则需要建立一些新的数据结构,以便让父进程找到它的子进程的退出信息。

父进程可以通过wait系列的系统调用(如wait4、waitid)来等待某个或某些子进程的退出,并获取它的退出信息。然后wait系列的系统调用会顺便将子进程的尸体(task_struct)也释放掉。子进程在退出的过程中,内核会给其父进程发送一个信号,通知父进程来“收尸”。这个信号默认是SIGCHLD,但是在通过clone系统调用创建子进程时,可以设置这个信号。

通过下面的代码能够制造一个EXIT_ZOMBIE状态的进程:#include void main() { if (fork()) while(1) sleep(100); }

只要父进程不退出,这个僵尸状态的子进程就一直存在。那么如果父进程退出了呢,谁又来给子进程“收尸”?当进程退出的时候,会将它的所有子进程都托管给别的进程(使之成为别的进程的子进程)。托管给谁呢?可能是退出进程所在进程组的下一个进程(如果存在的话),或者是1号进程。所以每个进程、每时每刻都有父进程存在。除非它是1号进程。1号进程,pid为1的进程,又称init进程。linux系统启动后,第一个被创建的用户态进程就是init进程。它有两项使命:1、执行系统初始化脚本,创建一系列的进程(它们都是init进程的子孙);2、在一个死循环中等待其子进程的退出事件,并调用waitid系统调用来完成“收尸”工作;init进程不会被暂停、也不会被杀死(这是由内核来保证的)。它在等待子进程退出的过程中处于TASK_INTERRUPTIBLE状态,“收尸”过程中则处于TASK_RUNNING状态。

7.僵死撤销状态 X(EXIT_DEAD)

退出状态,进程即将被销毁。

8.  x(TASK_DEAD)

表示已经退出且不需父进程回收的进程的状态。

9.  k(TASK_WAKEKILL)

TASK_WAKEKILL用于在接收到致命信号时唤醒进程。

#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE) TASK_KILLABLE 则是 TASK_WAKEILL 与 TSK_UNINTERRUPTIBLE的组合,它与TASK_UNINTERRUPTIBLE 类似,但是可以响应致命信号。它可以替代有效但可能无法终止的TASK_UNINTERRUPTIBLE进程状态,以及易于唤醒但更加安全的TASK_INTERRUPTIBLE进程状态。这是因为对不可中断睡眠模式的进程的唤醒呼叫可能会由于某些原因不会发生,这会使进程无法被终止,从而最终引发问题,因为惟一的解决方法就是重启系统。

10. W(TASK_WAKING)

防止进程运行,外部事件或者信号都无法唤醒该进程。

11. P(task_parked)

貌似时3.8内核才加入的,具体细节带研究

prevents other wakeups and use this state explicitly for the unpark wakeup

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值