技术开发 频道

Linux驱动开发必看:详解神秘内核

  3. 案例3:进程和中断上下文,单CPU,抢占内核

  如果内核使能了抢占,仅仅禁止中断将无法确保对临界区的保护,因为另一个处于进程上下文的执行单元可能会进入临界区。重新回到图2-4,现在,除了C以外,执行单元A和B必须提防彼此。显而易见,解决该问题的方法是在进入临界区之前禁止内核抢占、中断,并在退出临界区的时候恢复内核抢占和中断。因此,执行单元A和B使用了自旋锁API的irq变体:

unsigned long flags;

Point A:
  
/* Save interrupt state.
   * Disable interrupts - this implicitly disables preemption
*/
  spin_lock_irqsave(
&mylock, flags);

  
/* ... Critical Section ... */

  
/* Restore interrupt state to what it was at Point A */
  spin_unlock_irqrestore(
&mylock, flags);

  我们不需要在最后显示地恢复Point A的抢占状态,因为内核自身会通过一个名叫抢占计数器的变量维护它。在抢占被禁止时(通过调用preempt_disable()),计数器值会增加;在抢占被使能时(通过调用preempt_enable()),计数器值会减少。只有在计数器值为0的时候,抢占才发挥作用。

  4. 案例4:进程和中断上下文,SMP机器,抢占内核

  现在假设临界区执行于SMP机器上,而且你的内核配置了CONFIG_SMP和CONFIG_PREEMPT。

  到目前为止讨论的场景中,自旋锁原语发挥的作用仅限于使能和禁止抢占和中断,时间的锁功能并未被完全编译进来。在SMP机器内,锁逻辑被编译进来,而且自旋锁原语确保了SMP安全性。SMP使能的含义如下:

unsigned long flags;

Point A:
  
/*
    - Save interrupt state on the local CPU
    - Disable interrupts on the local CPU. This implicitly disables preemption.
    - Lock the section to regulate access by other CPUs
  
*/
  spin_lock_irqsave(
&mylock, flags);

  
/* ... Critical Section ... */

  
/*
    - Restore interrupt state and preemption to what it
      was at Point A for the local CPU
    - Release the lock
  
*/
  spin_unlock_irqrestore(
&mylock, flags);

  在SMP系统上,获取自旋锁时,仅仅本CPU上的中断被禁止。因此,一个进程上下文的执行单元(图2-4中的执行单元A)在一个CPU上运行的同时,一个中断处理函数(图2-4中的执行单元C)可能运行在另一个CPU上。非本CPU上的中断处理函数必须自旋等待本CPU上的进程上下文代码退出临界区。中断上下文需要调用spin_lock()/spin_unlock():

spin_lock(&mylock);

/* ... Critical Section ... */

spin_unlock(
&mylock);

  除了有irq变体以外,自旋锁也有底半部(BH)变体。在锁被获取的时候,spin_lock_bh()会禁止底半部,而spin_unlock_bh()则会在锁被释放时重新使能底半部。我们将在第4章讨论底半部。

  -rt树

  实时(-rt)树,也被称作CONFIG_PREEMPT_RT补丁集,实现了内核中一些针对低延时的修改。该补丁集可以从www.kernel.org/pub/linux/kernel/projects/rt下载,它允许内核的大部分位置可被抢占,但是用自旋锁代替了一些互斥体。它也合并了一些高精度的定时器。数个-rt功能已经被融入了主线内核。详细的文档见http://rt.wiki.kernel.org/。

  为了提高性能,内核也定义了一些针对特定环境的特定的锁原语。使能适用于代码执行场景的互斥机制将使代码更高效。下面来看一下这些特定的互斥机制。

0
相关文章