操作系统:linux
处理器:arm
内核版本:4.x
目录:
进程状态
执行ps aux命令可以看到进程有以下这些状态:
Ss、S、I<、I、SN、Ssl、S<、S<s、Rsl、Ssl+、SNsl、S<l、R+等
他们的含义可以通过man ps找到:
PROCESS STATE CODES
Here are the different values that the s, stat and state output specifiers
(header "STAT" or "S") will display to describe the state of a process:
D uninterruptible sleep (usually IO)
R running or runnable (on run queue)
S interruptible sleep (waiting for an event to complete)
T stopped by job control signal
t stopped by debugger during the tracing
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z defunct ("zombie") process, terminated but not reaped by its parent
For BSD formats and when the stat keyword is used, additional characters may
be displayed:
< high-priority (not nice to other users)
N low-priority (nice to other users)
L has pages locked into memory (for real-time and custom IO)
s is a session leader
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
+ is in the foreground process group
主要关注以下几个状态:
R (TASK_RUNNING)
只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态,这些进程的task_struct结构(进程控制块)被放入对应 CPU的可执行队列中(一个进程最多只能出现在一个CPU的可执行队列中)。进程调度器的任务就是从各个CPU的可执行队列中分别选择一个进程在该CPU 上运行。(这个状态并不代表正在执行。)
S (TASK_INTERRUPTIBLE)
处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。
D (TASK_UNINTERRUPTIBLE)
与TASK_INTERRUPTIBLE状态类似,进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号。
Z(EXIT_ZOMBIE)
僵尸状态,此时进程相关资源已经被释放,但还保留着task_struct结构体。当父进程执行wait4调用获取其相关信息时,进程将变为DEAD状态人间蒸发。由于这个过程很短暂,所以很难捕捉到。
如上图所示,内核中大部分进程生命周期就在这些状态中转换。通过fork或者clone系统调用,产生新的进程。处于运行状态的进程由于需要等待资源,进入睡眠态,根据能不能被信号唤醒分为TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE两种,资源来临时进程会被wake_up变成TASK_RUNNING,进入rq队列等待调度器调度。进程执行任务完毕后主动退出或被信号杀死时,进入僵尸态等待父进程回收。
其他进程状态
除了上面介绍的那些进程状态外,task_struct的state还可以取其他的值。
/* Used in tsk->state: */
#define TASK_RUNNING 0x0000
#define TASK_INTERRUPTIBLE 0x0001
#define TASK_UNINTERRUPTIBLE 0x0002
#define __TASK_STOPPED 0x0004
#define __TASK_TRACED 0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x0010
#define EXIT_ZOMBIE 0x0020
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED 0x0040
#define TASK_DEAD 0x0080
#define TASK_WAKEKILL 0x0100
#define TASK_WAKING 0x0200
#define TASK_NOLOAD 0x0400
#define TASK_NEW 0x0800
#define TASK_STATE_MAX 0x1000
/* Convenience macros for the sake of set_current_state: */
#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)
TASK_WAKEKILL:收到致命信号时,唤醒进程。调用vfork时,父进程就会处与
TASK_KILLABLE状态(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)。
TASK_PARKED、TASK_WAKEKILL、TASK_NOLOAD、TASK_NEW都是内核为了实现一些特殊功能,而存在的特殊state,这里挖一个小坑不展开。
组织方式
除了这种树以外,内核还采用链表、hash table和radix tree来组织进程PCB。基树是一种节省空间的数据结构,利用它可以从pid找到task_struct的地址。