Linux内核中的等待队列是一种延时机制,其用于当前进程需要等待某些资源而进入一种sleep状态,当等待条件为真时,
进程被唤醒,继续执行。显然,这里涉及三个方面,即,一是等待时当前进程处理,
二是进程等待时所关注的资源处理,三时进程何时被唤醒继续执行。
所以,我们这里需要几个数据结构,主要描述如下:
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct __wait_queue wait_queue_t;
此结构用于把当前进程进入睡眠时保持状态信息,
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
此结构用于定义一个等待队列,task_list是在等待队列上的任务,即等待任务的状态信息,即链表上是
struct __wait_queue 对象。
对于等待队列中的数据结构,有时也可以使用下面这个:
struct wait_bit_key {
void *flags;
int bit_nr;
#define WAIT_ATOMIC_T_BIT_NR -1
unsigned long timeout;
};
struct wait_bit_queue {
struct wait_bit_key key;
wait_queue_t wait;
};
对于等待队列头来说,可以通过下面方式初始化:
#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)
而对于等待队列中的成员来说,其因为使用了struct __wait_queue结构,所以,
一般对等待队列中成员定义如下:
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, \
.func = default_wake_function, \
.task_list = { NULL, NULL } }
#define DECLARE_WAITQUEUE(name, tsk) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
或者其他一些形式定义,如下:
#define __WAIT_BIT_KEY_INITIALIZER(word, bit) \
{ .flags = word, .bit_nr = bit, }
#define __WAIT_ATOMIC_T_KEY_INITIALIZER(p) \
{ .flags = p, .bit_nr = WAIT_ATOMIC_T_BIT_NR, }
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key; \
\
__init_waitqueue_head((q), #q, &__key); \
} while (0)
简单函数形式如下:
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;带唤醒函数
}
static inline void
init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func)
{
q->flags = 0;
q->private = NULL;
q->func = func;设置唤醒函数,但任务未设置
}
简单的加入队列过程:
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
list_add(&new->task_list, &head->task_list);
}
对外简单封装函数有:
一.
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);
}
EXPORT_SYMBOL(add_wait_queue);
二.
void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait);
三.
long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;
long ret = 0;
spin_lock_irqsave(&q->lock, flags);
if (unlikely(signal_pending_state(state, current))) {
/*
* Exclusive waiter must not fail if it was selected by wakeup,
* it should "consume" the condition we were waiting for.
*
* The caller will recheck the condition and return success if
* we were already woken up, we can not miss the event because
* wakeup locks/unlocks the same q->lock.
*
* But we need to ensure that set-condition + wakeup after that
* can't see us, it should wake up another exclusive waiter if
* we fail.
*/
list_del_init(&wait->task_list);
ret = -ERESTARTSYS;
} else {
if (list_empty(&wait->task_list)) {
if (wait->flags & WQ_FLAG_EXCLUSIVE)
__add_wait_queue_tail(q, wait);
else
__add_wait_queue(q, wait);
}
set_current_state(state);
}
spin_unlock_irqrestore(&q->lock, flags);
return ret;
}
四.
static inline void
__add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)
{
wait->flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue(q, wait);
}
等待队列的简单使用:
一, 定义等待队列头对象
如static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait);
二. 等待资源时,把当前进程加入等待队列,队列成员
void netlink_table_grab(void) __acquires(nl_table_lock)
{
might_sleep();
write_lock_irq(&nl_table_lock);
if (atomic_read(&nl_table_users)) {
DECLARE_WAITQUEUE(wait, current);自定义队列成员,并简单初始化
add_wait_queue_exclusive(&nl_table_wait, &wait);只是进入队列,但进程还可以运行
for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE);//改变进程状态
if (atomic_read(&nl_table_users) == 0)必须满足这个条件,否则不会被唤醒
break;//唤醒后,跳出
write_unlock_irq(&nl_table_lock);
schedule();这一步放弃CPU,真正调度出去,回来也是从这里开始
write_lock_irq(&nl_table_lock);//再次回来时,获取锁
}
__set_current_state(TASK_RUNNING);//设置可运行
remove_wait_queue(&nl_table_wait, &wait);//把之前设置释放。
}
}
三. 唤醒路径
void netlink_table_ungrab(void) __releases(nl_table_lock)
{
write_unlock_irq(&nl_table_lock);
wake_up(&nl_table_wait);
}
标签:__,队列,lock,list,queue,flags,内核,Linux,wait From: https://blog.51cto.com/u_11860992/6410365