linux的进程组织方式
前面说从认识task_struct开始学习内核,今天又能了解里面的几个域了。
一。进程链表
每个task_struct中都有一个tasks的域来连接到进程链表上去。
struct task_struct{ ... struct list_head tasks; ... char comm[TASK_COMM_LEN];//可执行程序名 ... };
而这个链表的头是init_task.它是0号进程的PCB,0号进程永远不会被撤销,它被静态的分配到内核数据段上。也就是Init_task的PCB是由编译器预先分配的,在程序运行的过程中一直存在,直到程序结束。
struct task_struct init_task = INIT_TASK(init_task);
可以编写以下内核模块,得到所有进程的pid和进程名,并统计出进程总数。
#include<linux/kernel.h> #include<linux/init.h> #include<linux/module.h> #include<linux/sched.h> MODULE_LICENSE("GPL"); static int __init print_pid_init(void) { struct task_struct *task,*p; struct list_head *pos; int count = 0; printk("Begin to print process :\n"); task = &init_task; list_for_each(pos,&task->tasks) { p = list_entry(pos,struct task_struct,tasks); count++; printk("%d ======> %s\n",p->pid,p->comm); } printk("the number of process is %d\n",count); return 0; } static void __exit print_pid_exit(void) { printk("End to print process.\n"); } module_init(print_pid_init); module_exit(print_pid_exit);二。哈希表
由于进程链表是将所有的进程连接到一个链表上去,所以查找一个进程的时间复杂度是O(N),是相当的低效。为此,使用哈希表来提高查找的效率。
1.哈希表的定义
我是在2.6.32.27内核中找到的,较老的版本可能是一个数组。
static struct hlist_head *pid_hash;
2.哈希函数
学过数据结构的应该知道,哈希函数对整个查找是至关重要的,它决定了发生冲突的概率。好的哈希函数能够得到减少冲突。
#define pid_hashfn(nr, ns) \ hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift)
#define hash_long(val, bits) hash_32(val, bits)
static inline u32 hash_32(u32 val, unsigned int bits) { /* On some cpus multiply is faster, on others gcc will do shifts */ u32 hash = val * GOLDEN_RATIO_PRIME_32; /* High bits are more random, so use them. */ return hash >> (32 - bits); }
/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */ #define GOLDEN_RATIO_PRIME_32 0x9e370001UL3.通过pid查找task_struct
内核并不能直接通过pid找到对应的task_struct,而是先通过pid找到对应的struct pid,在通过struct pid 找到对应的task_struct。
下面是详细介绍这两个的链接:
ii.struct pid 到task_struct的内核函数详解
三。就绪队列
与进程链表类似,task_struct也定义了一个连接到就绪队列的域run_list
struct sched_rt_entity { struct list_head run_list; .... }; struct task_struct { .... struct sched_rt_entity rt; ...... };
就绪队列头:
同样,内核中有一个就绪队列头runqueue_head
四.等待队列
1.等待队列的数据结构
typedef struct __wait_queue wait_queue_t; struct __wait_queue { unsigned int flags; #define WQ_FLAG_EXCLUSIVE 0x01 void *private; wait_queue_func_t func; struct list_head task_list; };task_list链接到等待队列上去。
func是一个函数指针,指向唤醒等待队列中进程的函数。prviate是传递给func的参数,用于指定所要唤醒的进程。
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);flags标志进程是否互斥: flags为WQ_FLAG_EXCLUSIVE 时互斥,否则,非互斥。
2.等待队列头
struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t;
因为等待队列是由中断处理程序和主要的内核函数修改的,因此要避免被同时访问。lock自旋锁对其进行同步,避免了双向链表被同时访问。task_list是双向链表的节点。
3.等待队列的操作
i.初始化
首先,使用了下面的宏声明并初始化了一个等待队列头
#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \ .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .task_list = { &(name).task_list, &(name).task_list } } #define DECLARE_WAIT_QUEUE_HEAD(name) \ wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)如果要对列中一个元素初始化,要使用这个函数:
static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p) { q->flags = 0; q->private = p; q->func = default_wake_function;//default_wake_function能够唤醒睡眠的进程p,并将其从等待队列中删除。 }ii.插入/删除
add_wait_queue()将一个非互斥进程插入到等待队列的第一个位置
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; wait->flags &= ~WQ_FLAG_EXCLUSIVE; spin_lock_irqsave(&q->lock, flags); __add_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock, flags); }
add_wait_queue_exclusive()将一个互斥进程插入到等待队列的最后一个位置
void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; wait->flags |= WQ_FLAG_EXCLUSIVE; spin_lock_irqsave(&q->lock, flags); __add_wait_queue_tail(q, wait); spin_unlock_irqrestore(&q->lock, flags); }
之所以将非互斥进程放在队首,而将互斥进程放在队尾,大概是因为非互斥进程不需要临界资源,将它唤醒不会影响其它进程的执行,而互斥进程得到临界资源会导致别的进程无法执行。
remove_wait_queue()将一个进程从等待队列中删除。
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; spin_lock_irqsave(&q->lock, flags); __remove_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock, flags); }iii.检查是否为空队列,直接调用了list.h中的list_empty()
static inline int waitqueue_active(wait_queue_head_t *q) { return !list_empty(&q->task_list); }iv.睡眠
sleep_on_common(wait_queue_head_t *q, int state, long timeout) { unsigned long flags; wait_queue_t wait; init_waitqueue_entry(&wait, current); __set_current_state(state); spin_lock_irqsave(&q->lock, flags); __add_wait_queue(q, &wait); spin_unlock(&q->lock); timeout = schedule_timeout(timeout); spin_lock_irq(&q->lock); __remove_wait_queue(q, &wait); spin_unlock_irqrestore(&q->lock, flags); return timeout; }
v.唤醒
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, int wake_flags, void *key) { wait_queue_t *curr, *next; list_for_each_entry_safe(curr, next, &q->task_list, task_list) { unsigned flags = curr->flags; if (curr->func(curr, mode, wake_flags, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break; } }
《----------------------完结------------》