解析Linux内核的同步与互斥机制(四)

来源:互联网 发布:java软件系统技术合同 编辑:程序博客网 时间:2024/04/30 21:41

源出处:http://www.startos.com/linux/tips/2011011921499_4.html


2.3.3 prepare_to_wait和finish_wait

  /*

  * Used to distinguish between sync and async io wait context:

  * sync i/o typically specifies a NULL wait queue entry or a wait

  * queue entry bound to a task (current task) to wake up.

  * aio specifies a wait queue entry with an async notification

  * callback routine, not associated with any task.

  */

  #define is_sync_wait(wait) (!(wait) || ((wait)->private))

  同步io等待将唤醒当前进程,异步io等待和当前进程无关,时间到后执行安装的回调函数

  void fastcall

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

  if (is_sync_wait(wait))

  set_current_state(state);

  spin_unlock_irqrestore(&q->lock, flags);

  }

  prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)

  wait->flags |= WQ_FLAG_EXCLUSIVE;

  排他性等待,其余和prepare_to_wait一样

  void fastcall finish_wait(wait_queue_head_t *q, wait_queue_t *wait)

  {

  unsigned long flags;

  __set_current_state(TASK_RUNNING); //确保进程状态为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);

  }

  }

  3 等待事件event

  Linux 内核中最简单的休眠方式是称为 wait_event的宏(及其变种),它实现了休眠和进程等待的条件的检查。形式如下:

  wait_event(queue, condition)/*不可中断休眠,不推荐*/ wait_event_interruptible(queue, condition)/*推荐,返回非零值意味着休眠被中断,且驱动应返回 -ERESTARTSYS*/ wait_event_timeout(queue, condition, timeout) wait_event_interruptible_timeout(queue, condition, timeout) /*有限的时间的休眠;若超时,则不管条件为何值返回0,*/

  上述四个宏函数为内核对外的接口,其他的休眠函数应避免使用。因为宏并不是函数,参数所做的任何修改对调用环境仍然有效,所以queue都是“值传递”,在宏内部会调用底层函数,采用的指针传递。Linux内核中存在大部分这样的宏,其都在接口上都是值传递。

  3.1 wait_event

  认真地看简单休眠中的 wait_event(queue, condition) 和 wait_event_interruptible(queue, condition) 底层源码会发现,其实他们只是手工休眠中的函数的组合。因此在驱动程序中应避免使用手动休眠代码,而应该采用内核已经封装好的四个wait_event系列函数。

  \include\linux\wait.h

  #define __wait_event(wq,condition) \

  do { \

  DEFINE_WAIT(__wait); \

  \

  for(;;) { \

  prepare_to_wait(&wq,&__wait,TASK_UNINTERRUPTIBLE); \

  /// 添加到等待队列中,同时更改进程状态;若已经加入则不会重复添加

  if (condition) \

  break; \

  schedule(); //何时返回呢??? \

  } \

  finish_wait(&wq,&__wait); \

  } while (0)

  // “__”表示内部函数,默认为condition不满足,添加至等待队列,调度

  注意prepare_to_wait和finish_wait的匹配关系

  #define wait_event(wq,condition) \

  do { \

  if(condition) \

  break; \

  __wait_event(wq,condition); \

  }while (0)

  // 对外的接口函数,需要判断condition,若假则等待;若真则直接退出

  --------------------------------------------------------------------------------------------------------------

  等待系列函数架构设计:

  3.2 wait_event_timeout

  #define __wait_event_timeout(wq, condition, ret) \

  do { \

  DEFINE_WAIT(__wait); \

  \

  for (;;) { \

  prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \

  if (condition) \

  break; \

  ret = schedule_timeout(ret); \

  if (!ret) \

  break; //延时到,退出 \

  } \

  finish_wait(&wq, &__wait); \

  } while (0)

  #define wait_event_timeout(wq,condition,timeout) \

  ({ \

  long __ret=timeout; \

  if( !(condition) ) \

  __wait_event_timeout( wq,condition,__ret); \

  __ret; \

  })

  3.3 wait_event_interruptible

  #define __wait_event_interruptible(wq, condition, ret) \

  do { \

  DEFINE_WAIT(__wait); \

  \

  for (;;) { \

  prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \

  if (condition) \

  break; \

  if (!signal_pending(current)) { \

  schedule(); \

  continue; \

  } \

  ret = -ERESTARTSYS; \

  break; \

  } \

  finish_wait(&wq, &__wait); \

  } while (0)


原创粉丝点击