1、进程
进程是出于执行器的程序以及相关的资源的总称,包括打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个或多个执行线程等。
2、进程描述符及任务结构
内核把进程的列表存放在叫做任务队列的双向循环链表中,链表中每一项都是类型为task_struct、称为进程描述符的结构。进程描述符中包含的数据能完整地描述一个正在执行的程序:它打开的文件、进程的地址空间、挂起的信号、进程的状态等等。
2.1 分配进程描述符
Linux通过slab分配器动态生成task_struct结构,只需在栈底(对于向下增长的栈)或栈顶(对于向上增长的栈)创建新的结构struct stread_info。每个任务的thread_info结构在它的内核栈的尾端分配,结构中task域中存放的是指向该任务实际tast_struct的指针。在x86上,struct trhead_info在文件<asm/thread_info.h>中定义如下:
struct thread_info {
struct task_struct *tast;
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
int preempt_count;
mm_segment_t addr_limit;
struct restart_block restart_block;
void *sysenter_return;
int uaccess_err;
};
图摘自《Linux内核设计与实现 第3版》
2.2 进程描述符的存放
内核通过一个唯一的进程标识值或PID来标识每一个进程。PID是一个数,表示为pid_t隐含类型,实际上就是一个int类型。PID的最大值默认为32768(short int短整型的最大值),这个值由<linux/threads.h>中所定义的PID最大值所确定。内核把每个进程的PID存放在他们各自的进程描述符中。这个最大值实际上就是系统中允许同时存在的进程的最大数目,这个值越小,转一圈就越快。
内核中大部分处理进程的代码都是直接通过tast_struct进行的。通过current宏查找到当前正在运行进程的进程描述符的速度尤为重要。硬件体系结构不同,该宏的实现也不同。有的硬件体系结构可以拿出一个专门的寄存器来存放指向当前进程tast_struct的指针,用于加快访问速度。而有些像x86这样的体系结构(其寄存器并不富余),就只能在内核栈的尾端创建thread_info结构,通过计算偏移间接地查找tast_struct结构。
2.3 进程状态
进程描述符中的state域描述了进程的当前状态。系统中的每个进程都必然处于五中进程状态中的一种,该域的值也必为下列五种状态标志之一:
- TAST_RUNNING(运行)——进程是可执行的(正在执行或在运行队列中等待执行)。这是进程在用户空间中执行的唯一可能状态
- TASK+INTERRUPTIBLE(可中断)——进程正在睡眠(被阻塞),等待某些条件达成。一旦条件达成或接收到信号,内核就会把进程状态设置为运行
- TAST_UNINTERRUPTIBLE(不可中断)——就算是收到信号也不会被唤醒。这个状态通常在进程必须在等待时不受干扰或等待事件很快就会发生时出现。
- __TASK_TRACED——被其他进程跟踪的进程
- __TASK_STOPPED——进程停止执行。通常这种状态发生在接收到SIGSTOP、SIGTST、SIGTTIN、SIGTTOU等信号的时候。在调试期间接收到任何信号,都会使进程进入这种状态
图摘自《Linux内核设计与实现 第3版》
2.4 设置进程状态
内核经常需要调整某个进程的状态,最好使用set_task_state(task, state)函数:
set_task_state(task, state); //将任务task的状态设置为state
2.5 进程上下文
可执行程序代码从一个可执行文件载入到进程的地址空间执行。一般程序在用户空间执行,当一个程序执行了系统调用或出发了某个异常,它就陷入了内核空间,城内核“代表进程执行”并处于进程上下文中。在进程上下文中current宏是有效的。
2.6 进程家族树
在Linux系统中,进程之间存在明显的继承关系 ,所有的进程都是PID为1的init进程的后代,init进程的进程描述符是作为init_tast静态分配的。系统中每个进程必有一个父进程,零个或多个子进程。拥有与同一个父进程的所有进程被称为兄弟。进程间的关系存放在进程描述符中,每个tast_struct都包含一个指向其父进程tast_struct的parent指针,和一个称为children的子进程链表。(类似多叉树中的父子表示法)
例,获得当前进程的父进程的进程描述符:
struct tast_struct *my_parent=current->parent;
虽然进程的继承体系很像多叉树,但任务队列实质上还是一个双向循环链表。