线程的实现方法有三种:1、用户级线程;2、内核级线程;3、混合线程模型。Linux内核中的进程和线程都用相同的数据结构task_struct表示;线程是特殊的进程,共享同一地址空间、共同合作。
2、进程与进程描述符是一一对应的关系。用数据结构task_struct来表示,(定义在/Linux/sched.h中),它包含了进程的详细信息,主要有进程标识符PID、进程占用的内存区域、相关文件的文件描述符、进程环境、信号处理、同步处理等。
3、进程标识符PID。POSIX标准中规定一个多线程应用程序中的所有线程都必须有相同的PID,在linux内核引入线程机制时,采用了线程组机制,同一线程组中的线程有相同的线程组号(ThreadGroupID),tgid.线程组组号放在进程描述符的成员变量tgid中.
4、进程的状态
进程的转移关系如下图:
深度睡眠和浅度睡眠进程得到它需要的资源被唤醒,通过schedule()进入执行态,深度睡眠的进程不能被信号或者定时中断唤醒,只有它申请的资源又有效是才能被唤醒。
5、进程上下文
在进程切换时,需要保存当前运行进程的执行状态,这些状态信息就是进程上下文。内核设计了一个更小巧的数据结构structthread_struct 来保存内核使用的相关任务状态段内容,他们在进程描述符的成员变量thread中。
6、当前进程
Linux2.2内核开始采用宏定义current来获取当前进程的描述符。
内核首先将当前进程的地址及需要快速访问的其他状态标记记录在数据结构structthread_info中,然后将该数据结构保存到内核态栈栈空间中的最低地址位置。该数据结构在内核态的位置有数据结构unionthread_union 决定,定义如下:
union thread_union {
struct thread_info thread_info;
unsignedlongstack[THREAD_SIZE/sizeof(long)];
}
这里,内核态栈stack与数据结构thread_info共享同一块内存。由于内核态栈由高地址向地地址方向增长,且内核态栈占的空间比数据结构thread_info大得多,所以有效的防止他们互相覆盖和冲突。
宏定义current通过下面的函数获取
static inline struct task_struct * get_current(void)
{
returncurrent_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的地址。