2014年7月22日 获取当前进程
linux使用了轻量级进程(lightweight process)来对多线程应用程序有更好的支持,在task_struct里面存放了一个thread_info来表示当前正在执行的线程信息,并且在thread_info里也存了一个反向到task_struct的指针以便获得当前进程。
获取当前进程正在运行的进程这个行为在linux内核中是非常频繁的,所以内核为了效率采用了将thread_info和内核栈存放在一起的策略,使用了这么一个联合体:
union thread_union {
struct thread_info thread_info;
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
这里的THREAD_SIZE是指内核栈的大小,通常是两个页框的大小。
在内核线程时,esp存放的就是内核栈的栈顶指针,由于栈的生长方向是从上到下的,所以只要将esp的值与~(THREAD_SIZE-1)进行按位与操作之后,就可以得到栈顶的thread_info的指针位置了,具体的处理过程:
static __always_inline struct task_struct * get_current(void)
{
return current_thread_info()->task;
}
static inline struct thread_info *current_thread_info(void)
{
struct thread_info *ti;
__asm__("andl %%esp,%0; ":"=r" (ti) : "" (~(THREAD_SIZE - 1)));
return ti;
}
拿到thread_info之后自然就得到了对应的task_struct对象。
2014年7月22日 pid哈希链表
由于linux存在线程组、进程组、会话等等概念,查找一个pid以及其关联的pid(同进程组、同线程组、同会话)成了内核的常用操作,线性的扫描整个进程链表是非常低效的,因此linux引入了pid哈希链表的数据结构来加速查找,在task_struct有这么一个数据结构:
/* PID/PID hash table linkage. */
struct pid pids[PIDTYPE_MAX];
PIDTYPE_MAX为类型种数,pid的定义为:
struct pid
{
/* Try to keep pid_chain in the same cacheline as nr for find_pid */
int nr;
struct hlist_node pid_chain;
/* list of pids with the same nr, only one of them is in the hash */
struct list_head pid_list;
};
nr为pid的值
pid_chain用来处理hash冲突,即链上pid经过hash函数计算后得到相同值的pid对象
pid_list则是链着同一种类型的pid对象(同线程组、同进程组、同会话等等类型)
这样查找任意一种类型pid都能在很快的时间出来结果。
2014年7月22日 进程等待队列
进程总会碰到需要等待的情况,如等待IO、互斥量等等,Linux内核设计了一种进程等待队列:
用wait_queue_head_t代表一个队列的头指针,里面不含进程描述符。
用__wait_queue来代表等待队列的元素,里面含有进程描述符。
内核里很多数据结构都是这样命名,感觉容易让人糊涂,可能算是linux的一个特点吧。。。
2.6.16内核的源码大概是这样的:
struct __wait_queue {
unsigned int flags;//表示是否为互斥进程也就是WQ_FLAG_EXCLUSIVE
#define WQ_FLAG_EXCLUSIVE 0x01
void *private;//task_struct指针
wait_queue_func_t func;//进程唤醒函数
struct list_head task_list;//和同队列进程的链
};
struct __wait_queue_head {
spinlock_t lock;//整个队列的一个自旋锁
struct list_head task_list;//队列元素(__wait_queue)的链
};
typedef struct __wait_queue_head wait_queue_head_t;
互斥进程的主要用途就是避免进程惊群现象,即唤醒多个进程只为抢占一个资源。__wait_queue结构中的func一般使用default_wake_function和autoremove_wake_function,两个函数都是用来唤醒进程。
default_wake_function是对try_to_wake_up函数的一个封装。
autoremove_wake_function是对default_wake_function的一个封装,并在在调用后会将进程从等待队列里移除。
在一个等待队列中非互斥的进程存放在队列的前头,互斥的进程存放在队列的后头,在使用wake_up_xx等类型的宏来唤醒队列上的进程时,会唤醒所有非互斥类进程,并根据提供的参数来唤醒相应个数的互斥进程。