前面说从认识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 0x9e370001UL
3.通过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;
}
}
《----------------------完结------------》