转自:http://blog.csdn.net/tommy_wxie/article/details/7425668

版权声明:本文为博主原创文章,未经博主允许不得转载。
2.2、睡眠与唤醒
在操作系统中,睡眠和唤醒原语实际上是操作系统的基本原语,也是实现同步的一种方式,而且它还是实现信号量的基础。当进程请求的资源(如内存、文件等)不能得到满足时,就会主动放弃CPU,进入等待状态(可中断等待或者不可中断等待)。当资源满足时,就会由别的进程唤醒,从而投入运行。
2.2.1、等待队列
等待队列表示一组睡眠的进程,这些进程正在等待特定的事件发生(或者说条件为真),比如,等待足够的内存。等待队列是一个双链表,每个队列都有一个队列头,其定义如下:
[html] view plain copy print?
//include/linux/wait.h
//等待队列头
struct __wait_queue_head {  // 自旋锁  spinlock_t lock;  struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;  等待队列链表中的元素类型为:
[html] view plain copy print?
typedef struct __wait_queue wait_queue_t;
//唤醒函数指针
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int sync, void *key);
//默认的唤醒函数
int default_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key);  struct __wait_queue {  /*取值为WQ_FLAG_EXCLUSIVE(=1)表示互斥进程,由内核有选择的唤醒.为0时表示非互斥进程,由内核在  **事件发生时唤醒所有等待进程.  **/  unsigned int flags;
#define WQ_FLAG_EXCLUSIVE    0x01  //等待的任务描述符  struct task_struct * task;  //唤醒函数,默认为default_wake_function
    wait_queue_func_t func;  struct list_head task_list;
};  其典型的结构如下:等待队列头的初始化:
DECLARE_WAIT_QUEUE_HEAD(name);
其定义如下:
[html] view plain copy print?
//incude/linux/wait.h
#define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                \  .lock        = SPIN_LOCK_UNLOCKED,                \  .task_list    = { &(name).task_list, &(name).task_list } }  //初始化等待队列头
#define DECLARE_WAIT_QUEUE_HEAD(name) \  wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)  或者如下:
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);等待队列元素初始化:
//linux/wait.h
//wait_queue_t初始化
static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
{q->flags = 0;q->task = p;q->func = default_wake_function;
}
2.2.2、等待事件(Waiting on the Event)
内核提供的等待接口包括wait_event(), wait_event_ interruptible(), 和wait_event_interruptible_timeout()。此外sleep_on(), sleep_on_timeout(), 和interruptible_sleep_on()在2.6中仍然支持,但已经过时。这些接口的基本实现如下:具体代码如下:
[html] view plain copy print?
//linux/wait.h
#define wait_event(wq, condition)                     \
do {                                    \  if (condition)    //条件发生                         \  break;                            \  __wait_event(wq, condition);                    \
} while (0)  #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)  //kernel/wait.c
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);  //将等待任务加入等待队列  /*  * don't alter the task state if this is just going to  * queue an async wait queue callback  */  if (is_sync_wait(wait))  set_current_state(state);  //设置任务当前的状态  //释放自旋锁,并恢复处理器状态  spin_unlock_irqrestore(&q->lock, flags);
}  //等待完成之后,应该设置任务的状态为运行状态,并从等待队列中删除
void fastcall 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);  }
}  2.2.3、唤醒(Waking Up)
接口如下:
[html] view plain copy print?
//include/inux/wait.h
#define wake_up(x)            __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
#define wake_up_nr(x, nr)        __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr, NULL)
#define wake_up_all(x)            __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0, NULL)
#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_locked(x)        __wake_up_locked((x), TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)
#define wake_up_interruptible_sync(x)   __wake_up_sync((x),TASK_INTERRUPTIBLE, 1)  具体实现:
[html] view plain copy print?
//kernel/sched.c
void fastcall __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);
}
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,  int nr_exclusive, int sync, void *key)
{  struct list_head *tmp, *next;  list_for_each_safe(tmp, next, &q->task_list) {  wait_queue_t *curr;  unsigned flags;  curr = list_entry(tmp, wait_queue_t, task_list);  flags = curr->flags;  //调用相应的唤醒函数, 唤醒第1个有WQ_FLAG_EXCLUSIVE标志的进程后停止  if (curr->func(curr, mode, sync, key) &&  (flags & WQ_FLAG_EXCLUSIVE) &&  !--nr_exclusive)  break;  }
}
//默认的唤醒函数
int default_wake_function(wait_queue_t *curr, unsigned mode, int sync, void *key)
{  task_t *p = curr->task;  return try_to_wake_up(p, mode, sync);
}  try_to_wake_up是唤醒原语中核心部分,其具体代码如下:
[html] view plain copy print?
/*p被唤醒的进程描述符.
**state被唤醒的进程的状态掩码
**sync禁止被唤醒的进程抢占本地CPU正在运行的进程
*/
static int try_to_wake_up(task_t * p, unsigned int state, int sync)
{  int cpu, this_cpu, success = 0;  unsigned long flags;  long old_state;  runqueue_t *rq;
#ifdef CONFIG_SMP  unsigned long load, this_load;  struct sched_domain *sd;  int new_cpu;
#endif  //关闭中断,并获取最后执行该进程的CPU(可能不同于本地CPU)的运行队列的锁  rq = task_rq_lock(p, &flags);  schedstat_inc(rq, ttwu_cnt);  old_state = p->state;  if (!(old_state & state))  goto out;  if (p->array)  goto out_running;  //最后执行该任务的CPU  cpu = task_cpu(p);  //本地CPU  this_cpu = smp_processor_id();  /*对于多CPU系统,检查要被唤醒的进程是否应该从最近执行该进程的CPU的运行队列,
**转移到另外一个CPU的运行队列.
*/
#ifdef CONFIG_SMP  if (unlikely(task_running(rq, p)))  goto out_activate;  new_cpu = cpu;  if (cpu == this_cpu || unlikely(!cpu_isset(this_cpu, p->cpus_allowed)))  goto out_set_cpu;  load = source_load(cpu);  this_load = target_load(this_cpu);  /*  * If sync wakeup then subtract the (maximum possible) effect of  * the currently running task from the load of the current CPU:  */  if (sync)  this_load -= SCHED_LOAD_SCALE;  /* Don't pull the task off an idle CPU to a busy one */  if (load < SCHED_LOAD_SCALE/2 && this_load > SCHED_LOAD_SCALE/2)  goto out_set_cpu;  new_cpu = this_cpu; /* Wake to this CPU if we can */  /*  * Scan domains for affine wakeup and passive balancing  * possibilities.  */  for_each_domain(this_cpu, sd) {  unsigned int imbalance;  /*  * Start passive balancing when half the imbalance_pct  * limit is reached.  */  imbalance = sd->imbalance_pct + (sd->imbalance_pct - 100) / 2;  if ((sd->flags & SD_WAKE_AFFINE) &&  !task_hot(p, rq->timestamp_last_tick, sd)) {  /*  * This domain has SD_WAKE_AFFINE and p is cache cold  * in this domain.  */  if (cpu_isset(cpu, sd->span)) {  schedstat_inc(sd, ttwu_wake_affine);  goto out_set_cpu;  }  } else if ((sd->flags & SD_WAKE_BALANCE) &&  imbalance*this_load <= 100*load) {  /*  * This domain has SD_WAKE_BALANCE and there is  * an imbalance.  */  if (cpu_isset(cpu, sd->span)) {  schedstat_inc(sd, ttwu_wake_balance);  goto out_set_cpu;  }  }  }  new_cpu = cpu; /* Could not wake to this_cpu. Wake to cpu instead */
out_set_cpu:  schedstat_inc(rq, ttwu_attempts);  new_cpu = wake_idle(new_cpu, p);  if (new_cpu != cpu && cpu_isset(new_cpu, p->cpus_allowed)) {  schedstat_inc(rq, ttwu_moved);  set_task_cpu(p, new_cpu);  task_rq_unlock(rq, &flags);  /* might preempt at this point */  rq = task_rq_lock(p, &flags);  old_state = p->state;  if (!(old_state & state))  goto out;  if (p->array)  goto out_running;  this_cpu = smp_processor_id();  cpu = task_cpu(p);  }  out_activate:
#endif /* CONFIG_SMP */  if (old_state == TASK_UNINTERRUPTIBLE) {  rq->nr_uninterruptible--;  /*  * Tasks on involuntary sleep don't earn  * sleep_avg beyond just interactive state.  */  p->activated = -1;  }  /*  * Sync wakeups (i.e. those types of wakeups where the waker  * has indicated that it will leave the CPU in short order)  * don't trigger a preemption, if the woken up task will run on  * this cpu. (in this case the 'I will reschedule' promise of  * the waker guarantees that the freshly woken up task is going  * to be considered on this CPU.)  */  //将进程p加入目标CPU的可运行队列  activate_task(p, rq, cpu == this_cpu);  /*如果没有设置sync标志(表示允许抢占),且目标CPU不是本地CPU,则检查p是否比rq运行队列中当前进程的动态优先级高.  **即(p)->prio < (rq)->curr->prio,如果是,则调用resched_task()抢占rq->curr。  */  if (!sync || cpu != this_cpu) {  if (TASK_PREEMPTS_CURR(p, rq))  /*在单CPU中,仅仅设置TIF_NEED_RESCHED标志.多CPU系统中,则检查相应标志,并使目标CPU重新调度  */  resched_task(rq->curr);  }  success = 1;  out_running:  //设置进程的状态  p->state = TASK_RUNNING;
out:  //释放rq的锁,并打开本地中断  task_rq_unlock(rq, &flags);  return success;
}  #ifdef CONFIG_SMP
//多CPU系统
static void resched_task(task_t *p)
{  int need_resched, nrpolling;  BUG_ON(!spin_is_locked(&task_rq(p)->lock));  /* minimise the chance of sending an interrupt to poll_idle() */  nrpolling = test_tsk_thread_flag(p,TIF_POLLING_NRFLAG);  need_resched = test_and_set_tsk_thread_flag(p,TIF_NEED_RESCHED);  nrpolling |= test_tsk_thread_flag(p,TIF_POLLING_NRFLAG);  if (!need_resched && !nrpolling && (task_cpu(p) != smp_processor_id()))  //产生IPI,强制目标CPU重新调度
        smp_send_reschedule(task_cpu(p));
}
#else
//单CPU系统
static inline void resched_task(task_t *p)
{  set_tsk_need_resched(p);
}
#endif  2.2.4、互斥等待
当调用wake_up唤醒等待队列时,等待队列上的所有进程都转置为可运行。在一些情况下,这种做法是正确的,比如等待某个特定的事件。但是在另外一些情况,可以提前知道只有一个被唤醒的进程能够成功的获取资源,比如等待临界区资源,其它的进程将再次睡眠。如果等待队列中的进程数量太大,将会严重影响系统性能,这就是所谓的thundering herd行为。为此,内核引入互斥等待,它与非互斥等待的区别如下:
(1) 当一个等待队列入口有 WQ_FLAG_EXCLUSEVE 标志置位, 它被添加到等待队列的尾部. 没有这个标志的入口项, 相反, 添加到开始。
(2) 当 wake_up 被在一个等待队列上调用, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止。
这样,进行互斥等待的进程一次只唤醒一个。使一个进程进入互斥等待是调用prepare_to_wait_exclusive完成的。
[html] view plain copy print?
//kernel/wait.c
void fastcall
prepare_to_wait_exclusive(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_tail(q, wait);  /*  * don't alter the task state if this is just going to  * queue an async wait queue callback  */  if (is_sync_wait(wait))  set_current_state(state);  spin_unlock_irqrestore(&q->lock, flags);
}  

本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/5945877.html,如需转载请自行联系原作者

内核随记(三)--同步(2)【转】相关推荐

  1. linux内核奇遇记之md源代码解读之八阵列同步二

    linux内核奇遇记之md源代码解读之八阵列同步二 转载请注明出处:http://blog.csdn.net/liumangxiong 在上一小节里讲到启动同步线程: 7824 mddev->s ...

  2. 线程同步——内核对象实现线程同步——等待函数

    1 对于内核对象实现线程同步,不得不提三点: 2 1)大多数内核对象既有触发也有未触发两个状态 3 比如:进程.线程.作业.文件流.事件.可等待的计时器.信号量.互斥量 4 2)等待函数:等待函数使线 ...

  3. linux内核奇遇记之md源代码解读之十二raid读写

    linux内核奇遇记之md源代码解读之十二raid读写 转载请注明出处:http://blog.csdn.net/liumangxiong 我们都知道,对一个linux块设备来说,都有一个对应的请求队 ...

  4. [转摘]了解 Windows Vista 内核:第三部分

    了解 Windows Vista 内核:第三部分 概览: 可靠性 可恢复性 安全性 到目前为止,该系列文章涵盖了有关进程.I/O.内存管理.系统启动.关闭和电源管理方面的 Windows Vista ...

  5. Linux内核分析(三)----初识linux内存管理子系统

    原文:Linux内核分析(三)----初识linux内存管理子系统 Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linu ...

  6. Windows核心编程:第9章 用内核对象进行线程同步

    Github https://github.com/gongluck/Windows-Core-Program.git //第9章 用内核对象进行线程同步.cpp: 定义应用程序的入口点. //#in ...

  7. 网络安全保障之“三同步”

    建设关键信息基础设施应当确保其具有支持业务稳定.持续运行的性能,并保证安全技术措施同步规划.同步建设.同步使用. –<网络安全法>第三十三条 信息系统的生命周期层面和安全保障要素层面不是相 ...

  8. 网络安全三同步怎么实施

    网络安全三同步实施的方式主要有以下三种:1.采用安全技术,比如防火墙.入侵检测和防御系统:2.提供安全策略,比如设置安全策略.安全审计和安全实践:3.实施安全控制,比如安全认证.安全访问控制和安全日志 ...

  9. Linux内核学习(三):Bootloader的特种兵-Uboot(一)

    Linux内核学习(三):Bootloader的特种兵-Uboot(一) 内容全部来自–><嵌入式应用开发完全手册> 1.什么是U-Boot U-Boot,全称为Universal ...

最新文章

  1. json支持的最大长度_Swifter.Json 可能是 .Net 平台迄今为止性能最佳的 Json 序列化库【开源】...
  2. 工作322:uni-扩展运算符实现拼接合并操作
  3. 歌谣带你看java面试题
  4. Spring4.x(5)--ApplicationContext接口
  5. vscode之npm不是内部活外部命令
  6. Exchange 2003 和 Exchange 2007最大处理器数、内存支持比较
  7. 基于python爬虫的加盟品牌数据挖掘研究与实现_基于Python 语言的Web 数据挖掘与分析研究...
  8. NTC 热敏电阻温度计算
  9. 计算机专业可以从事平面设计吗,计算机专业和平面设计专业是一个专业不?
  10. 图像超分辨率技术简介
  11. java中的element_element是什么意思
  12. mysql 临时表联表查询_一、MySQL中使用从表A中取出数据来更新表B的内容例如:要update表data中的一些列属性,但是修改属性的内容来源是来自表chanpin。SQL语言中不...
  13. 马太效应/幂律分布的本质以及其数学表述
  14. Package | 解决 Could not build wheels for opencv-python which use PEP 517 and cannot be installed
  15. 使用Scanner收集你的身高体重,并计算出你的BMI值是多少 BMI的计算公式是 体重(kg) / (身高*身高)
  16. 缓存数据库Redis
  17. 音频处理中的瞬态概念 Transient phenomena of Audio Signal Proccess
  18. 计算机考试 北京考场一日游
  19. python毕业设计作品基于django框架 图片分享平台毕设成品(6)开题答辩PPT
  20. InDesign 教程如何控制文档中的页数?

热门文章

  1. 【Tools】Pycharm 2018专业版 linux安装教程 附2018专业版密钥
  2. 通过UltraISO来提取U盘启动盘的ISO镜像文件
  3. [HNOI 2011]XOR和路径
  4. Cannot read property 'nodeType' of null; audio元素默认样式下载按钮
  5. 《第一行代码》学习笔记19-广播接收器Broadcast_Receiver(2)
  6. DIOCP (一) DIOCP常见问题。
  7. smarty模板截取字符串乱码问题完美解决```````
  8. Long类型传到前端失去精度(2):Long类型不是实体类的某一个字段,Long类型是一个函数的返回值
  9. 常用的Mybatis-Plus方法,让你的数据库增删改查(CRUD)一键实现
  10. 《LeetCode力扣练习》第5题 C语言版 (做出来就行,别问我效率。。。。)