结构定义:
Linux task_struct 结构中 涉及进程状态的属性有三个,其中进程对应的状态分为两类, 一类是运行中状态, 另一类是进程退出状态。
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_PARKED | kthread_park使用的特殊状态 |
TASK_NEW | 创建任务临时状态 |
TASK_DEAD | 任务退出状态 |
TASK_WAKING | 被唤醒状态 |
TASK_IDLE | 任务空闲状态 |
/*
* 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 函数为例
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
函数为例
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
- mutex_unlock_slowpath释放锁资源,唤醒进程
mutex_unlock->__mutex_unlock_slowpath ->wake_q_add ->wake_up_q(&wake_q);
- mutex_lock 通过trylock检查是否获取锁,通过后将进程状态设置为
-
__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)
-
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
其中try_to_wake_up 判断进程的state是否是 TASK_NORMAL的状态, 及睡眠状态, 非睡眠状态不需要唤醒。 也就是只有TASK_NORMAL包含的这两个状态才执行 try_to_wake_up 相关操作。
-
TASK_REPORT
主要应用于*get_task_state函数显示任务状态
/*
* 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 问题引入的状态描述。
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()函数的关系
Documentation/mutex-design.txt
kthread: Implement park/unpark facility
Linux CPU core的电源管理(5)_cpu control及cpu hotplug
Documentation for CPU hotplug support
[tip:sched/core] sched: Add TASK_WAKING
Be First to Comment