结构定义:

       Linux  task_struct 结构中 涉及进程状态的属性有三个,其中进程对应的状态分为两类, 一类是运行中状态, 另一类是进程退出状态。 

1
2
3
4
5
6
7
8
struct task_struct {
...
	/* -1 unrunnable, 0 runnable, >0 stopped: */
	volatile long			state;
	int				exit_state;
        unsigned int			flags;
...
}

状态说明:

TASK_RUNNING等待运行状态
TASK_INTERRUPTIBLE可中断睡眠状态
TASK_UNINTERRUPTIBLE不可中断睡眠状态
TASK_STOPPED停止状态(收到 SIGSTOP、SIGTTIN、SIGTSTP 、 SIG.TTOU信号后状态)
TASK_TRACED被调试状态
TASK_KILLABLE新可中断睡眠状态
TASK_PARKEDkthread_park使用的特殊状态
TASK_NEW创建任务临时状态
TASK_DEAD任务退出状态
TASK_WAKING被唤醒状态
TASK_IDLE任务空闲状态
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

/*
 * 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.
 */

/* 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)

#define TASK_IDLE			(TASK_UNINTERRUPTIBLE | TASK_NOLOAD)

/* Convenience macros for the sake of wake_up(): */
#define TASK_NORMAL			(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)

/* get_task_state(): */
#define TASK_REPORT			(TASK_RUNNING | TASK_INTERRUPTIBLE | \
					 TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \
					 __TASK_TRACED | EXIT_DEAD | EXIT_ZOMBIE | \
					 TASK_PARKED)

相关代码:

    下面通过内核代码,看下任务状态转换的一个大致过程。


  •     TASK_RUNNING, TASK_INTERRUPTIBLE

           以 poll_schedule_timeout    函数为例

           文件: /fs/select.c

           poll_schedule_timeout 运行->睡眠->唤醒运行等待时间片, 任务状态变化为TASK_RUNNING->TASK_INTERRUPTIBLE->TASK_RUNNING

具体过程:(select为例)

  • 等待队列初始化: select 函数调用 do_select,  do_select 通过poll_initwait将当前进程放入等待队列:

poll_initwait-> init_poll_funcptr ->__pollwait  ->  add_wait_queue  ->__add_wait_queue  ->   list_add  ->__list_add

          poll_initwait current也就是当前进程 放入 pwq->polling_task = current;

  •  轮询监听I/O当前状态并进行处理: 通过vfs对具体的I/O进程了封装,同时将等待队列pt 传送给底层驱动等待队列。

vfs_poll -> file->f_op->poll(file, pt)

  •  如果当前监听的I/O没有事件发生:进程睡眠

poll_schedule_timeout -> set_current_state(TASK_INTERRUPTIBLE)

  • 如果这时候等待的是个鼠标事件,事件发生时唤醒队列的进程。 

mousedev_notify_readers->wake_up_interruptible
mousedev_write->wake_up_interruptible

  • 唤醒队列的进程,设置状态为TASK_RUNNING

__set_current_state(TASK_RUNNING);


  •  TASK_RUNNING, TASK_UNINTERRUPTIBLE

  以 mutex_lock_slowpath  ,mutex_unlock_slowpath

  函数为例

           文件:/kernel/locking/mutex.c

           mutex_lock 分为三个阶段(fastpath,midpath,slowpath),其中slowpath会被放置到wait_queue睡眠并等待被唤醒,被阻塞为TASK_UNINTERRUPTIBLE。 

具体过程:(mutex_lock/unlock为例)

  •    mutex_lock 不满足 fast_path和midpath 调用 slow_path方式, 增加等待任务到等待队列

mutex_lock  ->__mutex_lock_slowpath ->__mutex_lock ->

__mutex_lock_common  ->__mutex_add_waiter

  •  mutex slow_path设置等待任务当前进程将任务状态设置为TASK_UNINTERRUPTIBLE

        -> set_current_state

  •  mutex_unlock_slowpath释放锁资源,唤醒进程

mutex_unlock->__mutex_unlock_slowpath   ->wake_q_add ->wake_up_q(&wake_q);


  • __TASK_STOPPED, _TASK_STOPPED

    暂停状态, 收到SIGSTOP,SIGTTIN,SIGTSTP SIGTTOU,进入该状态。不带下划线的宏增加了, TASK_WAKEKILL属性 内核只有在do_signal_stop设置了TASK_STOPPED

         do_signal_stop   -> set_special_state(TASK_STOPPED);          (/kernel/signal.c


  • __TASK_TRACED,TASK_TRACED

跟踪状态:进程被调试后进入该状态,不带下划线的宏增加了, TASK_WAKEKILL属性

ptrace_stop->set_special_state(TASK_TRACED);


  • TASK_PARKED

        这个状态主要是kthread_park 和 kthread_unpark    函数使用。 主要为了解决cpu hotplug问题。 


  • TASK_DEAD

进程退出状态do_exit ->do_task_dead     设置该状态。


  • ****TASK_WAKEKILL,TASK_KILLABLE

用于接收到致命信号唤醒进程, 实现了一种可以被中断的新的睡眠状态


  • TASK_WAKING

这个状态就是就成睡眠是唤醒函数中使用, 具体调用链如下, 标识任务已经被唤醒。

wake_up_process-> try_to_wake_up


  • TASK_IDLE, TASK_NOLOAD

Workqueue机制中再创建内核线程时,线程进入sleep状态,任务状态设置为TASK_IDLE  (= TASK_UNINTERRUPTIBLE | TASK_NOLOAD)

create_worker-> worker_thread

文件:/kernel/workqueue.c


  • TASK_NEW

涉及系统调用fork, clone 以及生成内核线程 kernel_thread 任务创建时, 通过sched_fork 会设置当前任务为 TASK_NEW, 创建完成后通过

wake_up_new_task 设置未TASK_RUNNING

          文件: /kernel/fork.c,     /kernel/sched/core.c

          fork系统调用

SYSCALL_DEFINE0(fork) ->_do_fork ->copy_process ->sched_fork  , wake_up_new_task 


  • TASK_NORMAL

         为wake_up系列函数设置的宏 

          TASK_NORMAL=(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)

          wake_up_process   ->try_to_wake_up     

           /kernel/sched/core.c

          其中try_to_wake_up 判断进程的state是否是 TASK_NORMAL的状态, 及睡眠状态, 非睡眠状态不需要唤醒。 也就是只有TASK_NORMAL包含的这两个状态才执行  try_to_wake_up  相关操作。 


  • TASK_REPORT

         主要应用于*get_task_state函数显示任务状态

         文件:/fs/proc/array.c     

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
 * 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[] = {

	/* states in TASK_REPORT: */
	"R (running)",		/* 0x00 */
	"S (sleeping)",		/* 0x01 */
	"D (disk sleep)",	/* 0x02 */
	"T (stopped)",		/* 0x04 */
	"t (tracing stop)",	/* 0x08 */
	"X (dead)",		/* 0x10 */
	"Z (zombie)",		/* 0x20 */
	"P (parked)",		/* 0x40 */

	/* states beyond TASK_REPORT: */
	"I (idle)",		/* 0x80 */
};

static inline const char *get_task_state(struct task_struct *tsk)
{
	BUILD_BUG_ON(1 + ilog2(TASK_REPORT_MAX) != ARRAY_SIZE(task_state_array));
	return task_state_array[task_state_index(tsk)];
}

   可以看到 get_task_state 函数显示了大多数任务的状态, 与ps man 命令中的状态对比, 多了一个 P (parked)状态, 也就是上面说得解决cpu hotplug 问题引入的状态描述。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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

    任务结构详细介绍可以参考极客时间刘超《趣谈Linux操作系统》 12-14章节。 

参考

What is the “Waiting Channel” of a process?

do_select()函数分析,理解select(),poll(),poll_wait()函数的关系

select()/poll() 的内核实现

Documentation/mutex-design.txt

TASK_KILLABLE:Linux 中的新进程状态

Linux 进程中 Stop, Park, Freeze

kthread: Implement park/unpark facility

Linux CPU core的电源管理(5)_cpu control及cpu hotplug

Documentation for CPU hotplug support

CPU hotplug in the Kernel

[tip:sched/core] sched: Add TASK_WAKING

进程调度API之wake_up_process