结构定义:
Linux task_struct 结构中 涉及进程状态的属性有三个,其中进程对应的状态分为两类, 一类是运行中状态, 另一类是进程退出状态。
| |
状态说明:
| 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_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函数显示任务状态
| |
可以看到 get_task_state 函数显示了大多数任务的状态, 与ps man 命令中的状态对比, 多了一个 P (parked)状态, 也就是上面说得解决cpu hotplug 问题引入的状态描述。
| |
任务结构详细介绍可以参考极客时间刘超《趣谈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