在 Linux 里面,无论是进程,还是线程,到了内核里面,我们统一都叫任务(Task),由一个统一的结构 task_struct进行管理。代码定义在 include/linux/sched.h 中,且所有的task由链表串联起来

struct list_head tasks;

任务ID

在 task 的管理上,因为要区分主线程和普通线程,所以有如下变量

pid_t pid;  // 线程id
pid_t tgid;  // 线程对应的主线程id,如果自己就是主线程,那么该值等于pid
// group_leader 指向当前所属进程的主线程
struct task_struct *group_leader;

<aside> 💡 区分开主线程和非主线程id之后,对于 task 的区分和管理就比较方便了。如给某个 task 发送 kill 命令,需要整个进程退出的话,就可以通过上述变量找到主线程

</aside>

任务状态

task 的任务状态由以下几个变量定义

volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
int exit_state;
unsigned int flags;  // 标志

<aside> 💡 state(状态)可以取的值定义在 include/linux/sched.h 头文件中。通过 bitset 方式设置,具体可以搜索 TASK_RUNNING 的定义

</aside>

线程唤醒之后,首先状态是 TASK_RUNNING,但并不是说进程正在运行,而是表示进程在时刻准备运行的状态。当处于这个状态的进程获得时间片的时候,就是在运行中;如果没有获得时间片,就说明它被其他进程抢占了,在等待再次分配时间片。

<aside> 💡 也就是说处于运行态和就绪态之间

</aside>

当 IO 操作时,此时会进入睡眠状态,释放CPU。睡眠状态有几种:

<aside> 💡 TASK_UNINTERRUPTIBLE 比较危险,不可被信号唤醒意味着不能处理 kill 信号,也就无法杀死进程,如果 IO 操作无法完成,则该进程将一直睡眠下去

</aside>

其他状态如:

进程退出时,首先进入 EXIT_ZOMBIE 状态,但是这个时候它的父进程还没有使用 wait() 等系统调用来获知它的终止信息,此时进程就成了僵尸进程。最后进入 EXIT_DEAD 状态。

<aside> 💡 EXIT_ZOMBIE 和 EXIT_DEAD 也可以用于 exit_state

</aside>

最后,对于最后一个状态变量 flags,它的值定义在一些宏中,以 PF 开头,如下所示: