目录
一、进程描述符
进程描述符保存了与进程相关的一切信息,其数据类型为task_struct,Linux用双向链表和类似HashMap的散列表来保存所有的进程描述符,前者用于调度按照进程优先级快速选择一个可执行的进程,后者用于按照进程pid或者tgid快速查找一个进程或者进程组,给其发送信号等操作。进程描述符中包含很多的字段,重点关注一下几个:
(1) 、state字段
表示进程的状态,共有6种:
1、TASK_RUNNING,表示进程要么正在执行,要么准备执行,等待cpu时间片的调度
2、TASK_INTERRUPTIBLE,表示进程被挂起(睡眠),直到某个条件成立触发CPU中断或者发送信号唤醒该进程,将其状态改成TASK_RUNNING,比如某个TASK_RUNNING的进程需要读取文件,发起系统调用从用户态进入内核态,内核将其状态改成TASK_INTERRUPTIBLE,然后调用磁盘驱动程序读取文件,CPU执行其他任务;待磁盘读取文件完毕,磁盘发送CPU中断信号,CPU将读取的文件内容传给进程,进程由内核态切换到用户态,处理文件内容。一般情况下,进程列表中的绝大多数进程都处于TASK_INTERRUPTIBLE状态,除非机器的负载很高。
3、TASK_UNINTERRUPTIBLE,与TASK_INTERRUPTIBLE类似,区别是不能被外部信号唤醒,只能通过CPU中断唤醒。该状态总是非常短暂的,通过ps命令基本上不可能捕捉,主要用于避免内核某些处理过程被中断,如进程与设备交互的过程,中断会造成设备陷入不可控的状态。
4、TASK_STOPPED,表示进程的执行已停止,向进程发送一个SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU信号,它就会因响应该信号而进入TASK_STOPPED状态,向进程发送一个向进程SIGCONT信号,可以让其恢复到TASK_RUNNING状态。
5、TASK_TRACED,表示进程的执行已停止,等待跟踪它的进程对它进行操作,比如在gdb中对被跟踪的进程下一个断点,进程在断点处停下来的时候就处于TASK_TRACED状态。处于TASK_TRACED状态的进程不能响应SIGCONT信号而被唤醒,只能等到调试进程通过ptrace系统调用执行PTRACE_CONT、PTRACE_DETACH等操作或调试进程退出,被调试的进程才能恢复TASK_RUNNING状态。
6、EXIT_ZOMBIE,表示进程已终止,正等待其父进程执行wait类系统调用收集关于它的一些统计信息如退出码,内核此时无法回收该进程的进程描述符。如果父进程未执行wait类系统调用并退出了,子进程会转交给上一级的父进程,直到最终的init进程,由上一级父进程执行wait类系统调用。
7、EXIT_DEAD,表示进程已终止,父进程已经执行wait类系统调用,进程即将被内核删除,该状态非常短暂。
Linux Kernel 2.6.25 引入了一种新的进程睡眠状态,TASK_KILLABLE
:当进程处于这种可以终止的新睡眠状态中,它的运行原理类似于 TASK_UNINTERRUPTIBLE
,只不过可以响应致命信号。
(2)、pid和tgid字段
pid标识一个唯一的进程,从0开始逐渐递增,到最大值后就开始利用空闲的未分配pid;tgid标识当前进程所属的进程组的id,在Linux系统中,该ID就是该进程组的领头进程(该组中的第一个轻量级进程)相同的PID。
(3)、stack字段
该字段是一个指针变量,表示当前进程的thread_info的地址,thread_info和内核态堆栈紧挨着,存放在两个连续的页框中,通过thread_info中的进程描述符指针快速访问进程描述符,其结构如下图:
图中,esp寄存器用来存放栈顶单元的地址。在80x86系统中,栈起始于顶端,并朝着这个内存区开始的方向增长。从用户态刚切换到内核态以后,进程的内核栈总是空的。因此,esp寄存器指向这个栈的顶端。一旦数据写入堆栈,esp的值就递减。为了快速获取当前CPU上运行进程的task_struct结构,内核提供了current宏, 该宏就是通过esp寄存器保存的栈顶地址快速获取对应的task_struct。
(4)、mm和active_mm字段
mm标识进程所拥有的用户空间内存描述符,内核线程无的mm为NULL;active_mm指向进程运行时所使用的内存描述符, 对于普通进程而言,这两个指针变量的值相同。但是内核线程kernel thread是没有进程地址空间的,所以内核线程的tsk->mm域是空(NULL)。但是内核线程需要访问内核空间,因为所有进程的内核空间都一样,所以它的active_mm成员被初始化为前一个运行进程的mm值,借此访问内核空间。
(5)、thread 字段
该字段是一个指针变量,数据结构为thread_struct,用于进程切换时保存除通用寄存器以外的寄存器的内容,用于恢复进程执行上下文使用,跟CPU架构强相关。通用寄存器的内容保存在内核堆栈中。