龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 操作系统 > LINUX系统 >

内核空间SMP编程

时间:2011-03-20 22:37来源:未知 作者:admin 点击:
分享到:
摘要:多处理机系统正在变得越来越普通。尽管大多数用户空间代码仍将完美地运行,而且有些情况下不需要增加额外的代码就能利用SMP特性的优势,但是内核空间代码必须编写成具备“

  摘要:多处理机系统正在变得越来越普通。尽管大多数用户空间代码仍将完美地运行,而且有些情况下不需要增加额外的代码就能利用SMP特性的优势,但是内核空间代码必须编写成具备“SMP意识”且是“SMP安全的”。以下几段文字解释如何去做。

  

  多处理机系统正在变得越来越普通。尽管大多数用户空间代码仍将完美地运行,而且有些情况下不需要增加额外的代码就能利用SMP特性的优势,但是内核空间代码必须编写成具备“SMP意识”且是“SMP安全的”。以下几段文字解释如何去做。

  

    问题

  

    当有多个CPU时,同样的代码可能同时在两个或多个CPU上执行。这在如下所示用于初始化某个图像设备的例程中可能会出问题。

  

    void init_hardware(void)

    {

    outb(0x1, hardware_base + 0x30);

    outb(0x2, hardware_base + 0x30);

    outb(0x3, hardware_base + 0x30);

    outb(0x4, hardware_base + 0x30);

    }

  

    假设该硬件依赖于寄存器0x30按顺序依次被设为0、1、2、3来初始化,那么要是有另一个CPU来参乎的话,事情就会搞糟。想象有两个CPU的情形,它们都在执行这个例程,不过2号CPU进入得稍慢点:

  

    CPU 1 CPU 2

    0x30 = 1

    0x30 = 2 0x30 = 1

    0x30 = 3 0x30 = 2

    0x30 = 4 0x30 = 3

    0x30 = 4

  

    这会发生什么情况呢?从我们设想的硬件设备看来,它在寄存器0x30上收到的字节按顺序为:1、2、1、3、2、4、3、4。

  

    啊!原本好好的事第二个CPU一来就搞得一团糟了也。所幸的是,我们有防止这类事情发生的办法。

  

    自旋锁小历史

  

    2.0.x版本的Linux内核通过给整个内核引入一个全局变量来防止多于一个CPU会造成的问题。这意味着任何时刻只有一个CPU能够执行来自内核空间的代码。这样尽管能工作,但是当系统开始以多于2个的CPU出现时,扩展性能就不怎么好。

  

    2.1.x版本的内核系列加入了粒度更细的SMP支持。这意味着不再依赖于以前作为全局变量出现的“大锁”,而是每个没有SMP意识的例程现在都需要各自的自旋锁。文件asm/spinlock.h中定义了若干类型的自旋锁。

  

    有了局部化的自旋锁后,不止一个CPU同时执行内核空间代码就变得可能了。

  

    简单的自旋锁

  

    理解自旋锁的最简单方法是把它作为一个变量看待,该变量把一个例程或者标记为“我当前在另一个CPU上运行,请稍等一会”,或者标记为“我当前不在运行”。如果1号CPU首先进入该例程,它就获取该自旋锁。当2号CPU试图进入同一个例程时,该自旋锁告诉它自己已为1号CPU所持有,需等到1号CPU释放自己后才能进入。

  

    spinlock_t my_spinlock = SPIN_LOCK_UNLOCKED;

    unsigned long flags;

    spin_lock (&my_spinlock);

    ...

    critical section

    ...

    spin_unlock (&my_spinlock);

  

    中断

  

    设想我们的硬件的驱动程序还有一个中断处理程序。该处理程序需要修改某些由我们的驱动程序定义的全局变量。这会造成混乱。我们如何解决呢?

  

    保护某个数据结构,使它免遭中断之修改的最初方法是全局地禁止中断。在已知只有自己的中断才会修改自己的驱动程序变量时,这么做效率很低。所幸的是,我们现在有更好的办法了。我们只是在使用共享变量期间禁止中断,此后重新使能。

  

   实现这种办法的函数有三个:

  

    disable_irq()

    enable_irq()

    disable_irq_nosync()

  

    这三个函数都取一个中断号作为参数。注意,禁止一个中断的时间太长会导致难以追踪程序缺陷,丢失数据,甚至更坏。

  

    disable_irq函数的非同步版本允许所指定的IRQ处理程序继续运行,前提是它已经在运行,普通的disable_irq则所指定的IRQ处理程序不在如何CPU上运行。

  

    如果需要在中断处理程序中修改自旋锁,那就不能使用普通的spin_lock()和spin_unlock(),而应该保存中断状态。这可通过给这两个函数添加_irqsave后缀很容易地做到:

  

    spinlock_t my_spinlock = SPIN_LOCK_UNLOCKED;

    unsigned long flags;

    spin_lock_irqsave(&my_spinlock, flags);

    ...

    critical section

    ...

    spin_unlock_irqrestore (&my_spinlock, flags);

  

  

  

  

  

精彩图集

赞助商链接