· 1.基本概念
o 在支持线程的计算机系统里面,进程作为资源分配的基本单位而存在,线程作为调度的基本单位而存在。
线程仅拥有必不可少的一些资源,如:一组寄存器、堆栈信息等等和其他线程共享同一个进程的所有资源。
o 所以,在同一个进程的线程切换时不需要大量的保存和恢复工作,同时由于共享同一个存储空间,不需要更新快表TLB,提高了系统性能。
线程的实现方法有三种:1、用户级线程;2、内核级线程;3、混合线程模型.
o Linux2.6采用改进的线程模型NPTl(Native POSIX Thread Library),采用内核支持线程机制,与Windows和Solaris的线程机制实现方式不同。
对于Linux内核而言进程和线程没有本质上的区别,他们都是调度的基本单位。实际上,Linux内核基于进程机制来实现线程的,
两者都用相同的数据结构task_struct表示;线程是特殊的进程,共享同一地址空间、共同合作。
o 2.进程描述符
o 进程与进程描述符是一一对应的关系。用数据结构task_struct来表示,(定义在 /Linux/sched.h 中),它包含了进程的详细信息,
主要有 进程标识符PID、进程占用的内存区域、相关文件的文件描述符、进程环境、信号处理、同步处理等。
o 2.1进程标识符PID
o POSIX标准中规定一个多线程应用程序中的所有线程都必须有相同的PID,在linux内核引入线程机制时,采用了线程组机制,
同一线程组中的线程有相同的线程组号(Thread Group ID),tgid.线程组组号放在进程描述符的成员变量tgid中.
对于普通的进程而言,线程组号tgid与进程号pid的值相同:对于拥有对个线程的进程来说,该进程创建的第一个线程的线程组号tgid与
进程号pid相同,其他随后创建的线程的线程组号tgid的值为第一个创建线程的进程号.
2.2进程的状态
进程的转移关系如下图:
深度睡眠和浅度睡眠进程得到它需要的资源被唤醒,通过schedule()进入执行态,深度睡眠的进程不能被信号或者定时中断唤醒,
只有它申请的志愿又有效是才能被唤醒。
2.3进程上下文
在进程切换时,需要保存当前运行进程的执行状态,这些状态信息就是进程上下文.(这里介绍IA32体系结构)
IA32体系结构包括了一个任务状态段(task state segment,TSS)的特殊段描述符。该段描述符用来在进程换出时保存进程的上下文,
每一个进程都包含自己独立的任务状态段。在内核中任务状态段用数据结构tss__struct来表示。
任务状态段出在一个缺陷,因为TSS只能保存到全局描述符GDT中,而GDT容量有限,也就是说限制了系统进程个数,这显然是不合理的。
Linux引入一个新的策略来解决这个问题:为系统的每一个处理器设置一个任务状态段。
在每次进程切换的过程中,首先将任务状态段中的信息保存到换出进程描述符中,然后根据换入进程的进程描述符中相关信息来更新该任务状态段的值。
虽然内核中任务状态段用数据结构tss__struct来表示,但是只用到了其中的一部分成员变量,
所以内核设计了一个更小巧的数据结构struct thread_struct 来保存内核使用的相关任务状态段内容,他们在进程描述符的成员变量thread中。
2.4当前进程
Linux2.2内核开始采用宏定义current来获取当前进程的描述符。
内核首先将当前进程的地址及需要快速访问的其他状态标记记录在数据结构struct thread_info中,
然后将该数据结构保存到内核态栈栈空间中的最低地址位置。该数据结构在内核态的位置有数据结构
union thread_union 决定,定义如下:
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
}
这里,内核态栈stack与数据结构thread_info共享同一块内存。由于内核态栈有高地址向地地址方向增长,
且内核态栈占的空间比数据结构thread_info大得多,所以有效的防止他们互相覆盖和冲突。
宏定义current通过下面的函数获取
static inline struct task_struct * get_current(void)
{
return current_thread_info()->task;
}
static inline struct thread_info * current_thread_info(void)
{
struct current_thread_info *ti;
__asm__(“andl %%esp, %0”;”:”=r” (ti) : “0” (~(THREAD_SIZE-1)));
return ti;
}
将当前指针%esp与数值~(THREAD_SIZE-1) 按位与运算,并将结果给ti。ti 保存的值恰好是内核态栈中的最低位地址,
这里正是进程描述中成员变量thread_info所在的位置,也即当前进程描述符的成员变量thread_info的地址。