linux 任务状态定义

结构定义:

       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_PARKEDkthread_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    函数为例

           文件: /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     

/*
 * 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()函数的关系

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

 

Be First to Comment

发表回复