Linux 内核等待队列

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); }

 

 

 

 

 

 
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值