Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1811896
  • 博文数量: 195
  • 博客积分: 4227
  • 博客等级: 上校
  • 技术积分: 2835
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-04 10:39
文章分类

全部博文(195)

文章存档

2013年(1)

2012年(26)

2011年(168)

分类: LINUX

2011-02-12 10:50:02

等待队列

谨以此文纪念过往的岁月。

进程的阻塞以及非阻塞,关于其概念可以参考其他的地方。
在linux中主要使用等待队列来实现阻塞,下面主要看等待队列的实现以及主要使用。

一. 等待队列的实现以及主要函数
1.1 定义
等待队列头成员包括自旋锁和任务链表:
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;
};
关于上述两个参数成员的使用以及各个成员的含义,下面看。
1.2 初始化
等待队列的初始化包括等待队列头的初始化和等待队列的初始化。
等待队列初始化:
#define __WAITQUEUE_INITIALIZER(name, tsk) {    \
 .private = tsk,      \          --保存进程信息
 .func  = default_wake_function,   \--wake的默认处理函数
 .task_list = { NULL, NULL } }         --任务链表

#define DECLARE_WAITQUEUE(name, tsk)     \
 wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)  --采用宏定义来实现等待队列的初始化。

#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)

函数init_waitqueue_head其实现的功能与上面的宏定义一致。
void init_waitqueue_head(wait_queue_head_t *q)
{
 spin_lock_init(&q->lock);  --初始化自旋锁
 INIT_LIST_HEAD(&q->task_list); --初始化链表,一般的链表初始化时均会将其前置和后置都指向自己
}
1.3 添加和移除等待队列
添加等待队列,其本质即是将等待队列头添加入等待队列中。
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); --解锁
}
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
 list_add(&new->task_list, &head->task_list);
}
移除等待队列,其本质即是将等待队列头从等待队列中移除。
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);
}

static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
 list_add(&new->task_list, &head->task_list);
}
上面两个函数都是对链表的操作。
1.4 等待事件
等待事件其实是等待condition满足。
与信号量的等待类似。区分于是否可以中断,是否设定条件不满足时的最长时间。
wait_evet不可以中断,也没有条件不满足时间,这种情况在condition一直不满足的情况下会造成僵尸进程。
#define __wait_event(wq, condition)      \
  schedule();      \             --仅仅是切换,对于中断以及调度时间不管

#define __wait_event_timeout(wq, condition, ret)   \
  ret = schedule_timeout(ret);    \    --对于调度时间溢出的处理
  if (!ret)      \
   break;      \

#define __wait_event_interruptible(wq, condition, ret)   \
  if (!signal_pending(current)) {    \       --对于异常中断的处理
   schedule();     \
   continue;     \
  }       \
  ret = -ERESTARTSYS;     \
  break;       \
以上是三种等待事件的区别。从源码中可以看出三种之间的差异。针对上面的共同点来看,以wait_event为例:
#define __wait_event(wq, condition)      \
do {         \
 DEFINE_WAIT(__wait);      \         --新定义name为__wait的队列
 for (;;) {       \
  prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \
  if (condition)      \
   break;      \
  schedule();      \
 }        \
 finish_wait(&wq, &__wait);     \
} while (0)
下面的函数也很好理解
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);
}

void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
 unsigned long flags;

 __set_current_state(TASK_RUNNING);
 if (!list_empty_careful(&wait->task_list)) {
  spin_lock_irqsave(&q->lock, flags);
  list_del_init(&wait->task_list);
  spin_unlock_irqrestore(&q->lock, flags);
 }
}
以上也是简单的链表处理。
1.5 唤醒队列
下面是几种唤醒队列的宏定义,其最终均为调用__wake_up
#define wake_up(x)   __wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_nr(x, nr)  __wake_up(x, TASK_NORMAL, nr, NULL)
#define wake_up_all(x)   __wake_up(x, TASK_NORMAL, 0, NULL)
#define wake_up_locked(x)  __wake_up_locked((x), TASK_NORMAL)

#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
#define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
#define wake_up_interruptible_sync(x) __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)

void __wake_up(wait_queue_head_t *q, unsigned int mode,int nr_exclusive, void *key)
{
 unsigned long flags;

 spin_lock_irqsave(&q->lock, flags);
 __wake_up_common(q, mode, nr_exclusive, 0, key);
 spin_unlock_irqrestore(&q->lock, flags);
}

void __wake_up_common(wait_queue_head_t *q, unsigned int mode,int nr_exclusive, int sync, 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, sync, key) &&(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
   break;
 }
}
唤醒队列上的进程。这个涉及内核进程的切换,这个在以后的进程中再学习。

阅读(1057) | 评论(0) | 转发(1) |
0

上一篇:并发和竞态

下一篇:中断机制简介

给主人留下些什么吧!~~