4. 阻塞态进程构成的等待队列
Linux内核为每一个临界资源设置了一个等待队列,在进程无法获取临界资源时就睡眠在该等待队列上。
①等待队列
wait_queue_head_t 表示一个等待队列/include/linux/wait.h
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
②等待队列节点
struct __wait_queue {
unsigned int flags;//唤醒方式
#define WQ_FLAG_EXCLUSIVE 0x01
void *private;//保存睡眠进程描述符地址
wait_queue_func_t func;//唤醒方法
struct list_head task_list;//
};
唤醒方式有 WQ_FLAG_EXCLUSIVE 或者 0;
WQ_FLAG_EXCLUSIVE:表示节点对应进程对临界资源使用具有排他性。在唤醒是会唤醒所有非排他性进程和一定数量的排他性进程。
③睡眠在等待队列中
进程访问临界资源而阻塞时,先设置自己的状态为睡眠态(中断或者不可中断),然后在该临界资源的等待队列中睡眠,最后才释放处理器,等待该资源可用。
首先初始化等待队列节点(include/linux/wait.h)
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;
}
然后插入到等待队列
void fastcall 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);
}
void fastcall 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);
}
add_wait_queue 首先清唤醒方式,在后插入到对首
add_wait_queue_exclusive先设置唤醒方式,然后插入到对尾。这样就将等待队列中的链表一分为二,前半部分为没有设置WQ_FLAG_EXCLUSIVE,后半部分为设置了WQ_FLAG_EXCLUSIVE;
的节点。这给后面的唤醒带来了好处。
④唤醒
Linux2.6内核提供了8个宏来唤醒等待队列中的进程。他们都是基于函数__wake_up_common
位于ernel/sched.c
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, int sync, void *key)
{
struct list_head *tmp, *next;
list_for_each_safe(tmp, next, &q->task_list) {
wait_queue_t *curr;
unsigned flags;
curr = list_entry(tmp, wait_queue_t, task_list);
flags = curr->flags;
if (curr->func(curr, mode, sync, key) &&
(flags & WQ_FLAG_EXCLUSIVE) &&
!--nr_exclusive)
break;
}
}
ANSI C标准定义逻辑与的运算规则,前面的为真才会继续运算后面的,所以上面的if可以实现唤醒全部得非排他性进程和nr_exclusive个排他性进程。
Linux内核学习笔记之进程进程的组织形式(4)
内容版权声明:除非注明,否则皆为本站原创文章。