Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1722659
  • 博文数量: 199
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 6186
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-30 11:01
个人简介

Linuxer.

文章存档

2015年(4)

2014年(28)

2013年(167)

分类: LINUX

2014-06-05 20:27:47


  1. //Based on linux v3.14 source code
  2. 一、概述
  3. 等待队列在内核中有很多用途,尤其在中断处理、进程同步及定时。等待队列实现事件上的条件等待;希望等待特定事件的进程把自己放在合适的等待队列,并放弃控制权。

  4. 二、相关结构体
  5. 1. 等待队列由双向链表实现,其元素包括指向进程描述符的指针。每个等待队列都有一个等待队列头,即wait_queue_head_t结构。
  6. struct __wait_queue_head {
  7.     spinlock_t lock;
  8.     struct list_head task_list; //等待进程链表
  9. };
  10. typedef struct __wait_queue_head wait_queue_head_t;

  11. 2. 等待队列链表中的元素,即wait_queue_t结构
  12. 等待队列链表中的每个元素代表一个睡眠进程,进程描述符地址存入private字段中。
  13. typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key);

  14. struct __wait_queue {
  15.     unsigned int flags;    //标志
  16. #define WQ_FLAG_EXCLUSIVE 0x01
  17.     void *private;    //进程描述符
  18.     wait_queue_func_t func;    //唤醒函数,缺省为default_wake_function
  19.     struct list_head task_list;    //链接到等待队列链表中
  20. };
  21. typedef struct __wait_queue wait_queue_t;

  22. 三、等待队列操作
  23. 1. 定义等待队列头
  24. (1)静态定义
  25. #define DECLARE_WAIT_QUEUE_HEAD(name) wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

  26. #define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \
  27.     .lock = __SPIN_LOCK_UNLOCKED(name.lock), \
  28.     .task_list = { &(name).task_list, &(name).task_list } }
  29.  
  30. (2)动态初始化
  31. #define init_waitqueue_head(q) \
  32.     do { \
  33.         static struct lock_class_key __key; \
  34.      \
  35.         __init_waitqueue_head((q), #q, &__key); \
  36.     } while (0)

  37. void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key)
  38. {
  39.     spin_lock_init(&q->lock);
  40.     lockdep_set_class_and_name(&q->lock, key, name);
  41.     INIT_LIST_HEAD(&q->task_list);
  42. }

  43. 2. 定义等待队列元素wait_queue_t
  44. (1)静态定义,定义一个名为name的等待队列项,并将当前进程的进程描述符赋值给private,并设置缺省的唤醒函数default_wake_function。
  45. #define DECLARE_WAITQUEUE(name, tsk) \
  46.             wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

  47. #define __WAITQUEUE_INITIALIZER(name, tsk) { \
  48.     .private = tsk, \
  49.     .func = default_wake_function, \
  50.     .task_list = { NULL, NULL } }
  51.  
  52. 3. 将元素插入等待队列
  53. void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
  54. {
  55.     unsigned long flags;
  56.     
  57.     wait->flags &= ~WQ_FLAG_EXCLUSIVE;
  58.     //上锁,关中断
  59.     spin_lock_irqsave(&q->lock, flags);
  60.     //将等待队列项wait挂入等待队列
  61.     __add_wait_queue(q, wait);
  62.     spin_unlock_irqrestore(&q->lock, flags);
  63. }

  64. static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
  65. {
  66.     list_add(&new->task_list, &head->task_list);
  67. }

  68. add_wait_queue()是把一个非互斥进程插入等待队列的第一个位置。
  69. add_wait_queue_exclusive()是把一个互斥进程插入等待队列的最后一个位置。

  70. 4. 将元素从等待队列中删除
  71. void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
  72. {
  73.     unsigned long flags;
  74.          
  75.     spin_lock_irqsave(&q->lock, flags);
  76.     __remove_wait_queue(q, wait);
  77.     spin_unlock_irqrestore(&q->lock, flags);
  78. }

  79. static inline void __remove_wait_queue(wait_queue_head_t *head, wait_queue_t *old)
  80. {
  81.     list_del(&old->task_list);
  82. }

  83. 5. 实例 sleep_on_common()函数
  84. static long __sched sleep_on_common(wait_queue_head_t *q, int state, long timeout)
  85. {
  86.     unsigned long flags;
  87.     wait_queue_t wait;

  88.     //初始化进程所在的等待队列项wait
  89.     init_waitqueue_entry(&wait, current);

  90.     //设置进程状态
  91.     __set_current_state(state);

  92.     spin_lock_irqsave(&q->lock, flags);
  93.     //将当前进程所在的等待队列项wait挂入等待队列中
  94.     __add_wait_queue(q, &wait);
  95.     spin_unlock(&q->lock);
  96.     //进行进程调度
  97.     timeout = schedule_timeout(timeout);
  98.     
  99.     //进程被唤醒
  100.     spin_lock_irq(&q->lock);
  101.     //将该进程进程所在的等待队列项从等待队列中删除
  102.     __remove_wait_queue(q, &wait);
  103.     spin_unlock_irqrestore(&q->lock, flags);

  104.     return timeout;
  105. }

  106. 6. 根据等待队列的实现,linux提供了多种休眠、唤醒方式。
  107. (1) 基于sleep_on_common的休眠。当要必须测试条件,当条件不满足时接着睡眠,就不能使用sleep_on类的睡眠。应当使用手工睡眠,或者wait_event类的函数
  108. //sleep_on将进程设置为TASK_UNINTERRUPTIBLE状态,然后插入等待队列
  109. void __sched sleep_on(wait_queue_head_t *q)
  110. {
  111.     sleep_on_common(q, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
  112. }

  113. //interruptible_sleep_on将进程设置为TASK_INTERRUPTIBLE状态,然后插入等待队列.因此接受一个信号就能唤醒队列
  114. void __sched interruptible_sleep_on(wait_queue_head_t *q)
  115. {
  116.     sleep_on_common(q, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
  117. }

  118. //sleep_on_timeout与sleep_on类似,但是设置了时间间隔,过了间隔会唤醒进程。时间未到会返回剩余时间
  119. long __sched sleep_on_timeout(wait_queue_head_t *q, long timeout)
  120. {
  121.     return sleep_on_common(q, TASK_UNINTERRUPTIBLE, timeout);
  122. }

  123. //interruptible_sleep_on_timeout与interruptible_sleep_on类似,只是设置了时间间隔
  124. long __sched interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout)
  125. {
  126.     return sleep_on_common(q, TASK_INTERRUPTIBLE, timeout);
  127. }

  128. (2)手工休眠
  129. 1)建立并初始化一个等待队列项
  130. DEFINE_WAIT(my_wait) <==> wait_queue_t my_wait; init_wait(&my_wait);
  131. 2)将等待队列项添加到等待队列头中,并设置进程的状态
  132. void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state)
  133. void prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)
  134. 3)调用schedule(),告诉内核调度别的进程运行
  135. 4)schedule返回,完成后续清理工作finish_wait()

  136. void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
  137. {
  138.     unsigned long flags;
  139.     
  140.     wait->flags &= ~WQ_FLAG_EXCLUSIVE;
  141.     spin_lock_irqsave(&q->lock, flags);
  142.     if (list_empty(&wait->task_list))
  143.         __add_wait_queue(q, wait);
  144.     set_current_state(state);
  145.     spin_unlock_irqrestore(&q->lock, flags);
  146. }

  147. void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
  148. {
  149.     unsigned long flags;
  150.         
  151.     __set_current_state(TASK_RUNNING);

  152.     if (!list_empty_careful(&wait->task_list)) {
  153.         spin_lock_irqsave(&q->lock, flags);
  154.         list_del_init(&wait->task_list);
  155.         spin_unlock_irqrestore(&q->lock, flags);
  156.     }
  157. }

  158. (3) wait_event类睡眠,有条件的睡眠,condition是任意一个布尔表达式,在休眠前后多次对condition求值,为真则唤醒
  159. //进程将被置于非中断休眠,只有condition为真进程才退出睡眠
  160. wait_event(queue, condition)
  161. //进程可被信号中断休眠,或者condition为真进程退出睡眠
  162. wait_event_interruptible(queue, condition)
  163. //等待限定时间timeout,或者condition为真退出睡眠
  164. wait_event_timeout(queue, condition, timeout)
  165. //进程可被信号中断休眠,或者等待限定时间timeout,或者condition为真进程退出睡眠
  166. wait_event_interruptible_timeout(queue, condition, timeout)

  167. (4)唤醒进程的基本函数是wake_up
  168. #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
  169. #define wake_up_nr(x, nr) __wake_up(x, TASK_NORMAL, nr, NULL)
  170. #define wake_up_all(x) __wake_up(x, TASK_NORMAL, 0, NULL)
  171. #define wake_up_locked(x) __wake_up_locked((x), TASK_NORMAL, 1)
  172. #define wake_up_all_locked(x) __wake_up_locked((x), TASK_NORMAL, 0)

  173. #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
  174. #define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
  175. #define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
  176. #define wake_up_interruptible_sync(x) __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)
  177. 注意:
  178. <1>所有宏都考虑到处于TASK_INTERRUPTIBLE状态的睡眠进程,如果宏中带有interruptible的,则会也唤醒处于TASK_UNINTERRUPTIBLE状态的睡眠进程.
  179. <2>所有宏都唤醒具有请求状态的非互斥进程。
  180. <3>宏中含有nr的宏,唤醒给定数目的具有请求状态的互斥进程(非互斥进程都被唤醒)
  181. <4>宏中含有all的宏,唤醒所有具有请求状态的互斥进程(非互斥进程都被唤醒)
  182. <5>宏中含有sync的宏,检查被唤醒的进程优先级是否高于系统中正在运行进程的优先级,并在必要时调用schedule().
  183. void __wake_up(wait_queue_head_t *q, unsigned int mode,int nr_exclusive, void *key)
  184. {
  185.     unsigned long flags;
  186.     
  187.     spin_lock_irqsave(&q->lock, flags);
  188.     __wake_up_common(q, mode, nr_exclusive, 0, key);
  189.     spin_unlock_irqrestore(&q->lock, flags);
  190. }

  191. static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,int nr_exclusive, int wake_flags, void *key)
  192. {
  193.     wait_queue_t *curr, *next;
  194.          
  195.     list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
  196.         unsigned flags = curr->flags;
  197.         //调用唤醒函数,缺省为default_wake_function,调用try_to_wake_up将进程更改为可运行状态并置待调度标志
  198.         if (curr->func(curr, mode, wake_flags, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
  199.             break;
  200.     }
  201. }

  202. try_to_wake_up涉及到进程调度,不在这里深究。


阅读(3808) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~