在 Linux 里面,无论是进程,还是线程,到了内核里面,我们统一都叫任务(Task),由一个统一的结构 task_struct
进行管理。代码定义在 include/linux/sched.h
中,且所有的task由链表串联起来
struct list_head tasks;
在 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 开头,如下所示: