进程与线程
Linux内核是不区分线程与进程的,线程只是一种特殊的进程,被视为与其他进程共享地址空间的进程。与windows这种在内核专门提供线程支持的操作系统不一样。
task_struct与thread_info
task_struct(进程描述符)描述了一个进程的所有信息
,是一个体系无关的结构,位于include/linux/sched.h
。它包含进程打开的文件、挂起的信号、进程的状态、进程的pid等。保存在tasks双向循环链表中(见上一节的链表结构体总结),因此可以从一个进程结构体出发,索引到任何其他进程结构体。目前内核中,进程描述符是用slab分配器动态生成的,task_struct有一个void *stack成员变量指向进程的内核栈。
thread_info是体系相关的,位于arch/xxx/include/asm/thread_info.h
,它存放了线程上下文,包括是否可以抢占、当前进程是属于哪一种规范的可执行程序等。
早期内核配置中,在X86体系并且CONFIG_THREAD_INFO_IN_TASK没有被时,进程的thread_info存放在进程的内核栈。如果是向下增长的栈,则thread_info在栈顶低地址;向上增长的栈,thread_info还是在低地址,只不过在栈底了。因此,可以通过内核栈的地址快速获得thread_info的地址。
在thread_info中有一个struct task_struct *task变量指向task_struct。后来内核把X86的thread_info结构体中的这个task指针给去掉了(Move thread_info into task_struct),并且将thread_info放到了task_struct中。这个task指针,是为了寄存器在不够多的体系上,可以通过内核栈地址加偏移,找到thread_info,然后快速找到task_struct的。但是X86一直使用的是per_cpu的变量来保存正在使用cpu的进程的进程描述符:
1
| DECLARE_PER_CPU(struct task_struct *, current_task);
|
所以thread_info中的task变量是可有可无的。
在进程调度时,会更新这个变量:
1
| this_cpu_write(current_task, next_p);
|