Linux 核心--12.Linux内核机制( 三 )




11.5Buzz 锁
它使用更频繁的名字叫自旋锁 。这是一种保护数据结构或代码片段的原始方式 。在某个时刻只允许一个进程访问临界区内的代码 。Linux还同时将一个整数域作为锁来限制对数据结构中某些域的存取 。每个希望进入 此区域的进程都试图将此锁的初始值从0改成1 。如果当前值是1则进程将再次尝试,此时进程好象在一段紧循环代码中自旋 。对包含此锁的内存区域的存取必须是原子性的,即检验值是否为0并将其改变成1的过程不能被任何进程中断 。多数CPU结构通过特殊指令提供对此方式的支持,同时我们可以在一个非缓冲主存中实现这个流言锁 。

当控制进程离开临界区时它将递减此Buzz锁 。任何处于自旋状态的进程都可以读取它,它们中最快的那个将递增此值并进入临界区 。


11.6信号灯
信号灯被用来保护临界区中的代码和数据 。请记住每次对临界区数据, 如描叙某个目录VFS inode的访问, 是通过代表进程的核心代码来进行的 。允许某个进程擅自修改由其他进程使用的临界区数据是非常危险的 。防止此问题发生的一种方式是在被存取临界区周围使用buzz锁,但这种简单的方式将降低系统性能 。Linux使用信号灯来迫使某个时刻只有唯一进程访问临界区代码和数据,其他进程都必须等待资源被释放才可使用 。这些等待进程将被挂起而系统中其他进程可以继续运行 。

一个Linux semaphore结构包含了以下信息:


count
此域用来保存希望访问此资源的文件个数 。当它为正数时表示资源可用 。负数和0表示进程必须等待 。当它初始值为1时表示一次仅允许一个进程来访问此资源 。当进程需要此资源时它们必须将此count 值减1并且在使用完后将其加1 。
waking
这是等待此资源的进程个数,同时也是当资源可利用时等待被唤醒的进程个数 。
wait queue
当进程等待此资源时,它们被放入此等待队列 。
lock
访问waking域时使用的buzz锁 。
假设此信号灯的初始值为1,第一个使用它的进程看到此记数为正数,然后它将其减去1而得到0 。现在此进程 拥有了这些被信号灯保护的段代码和资源 。当此进程离开临界区时它将增加此信号灯的记数值,最好的情况 是没有其他进程与之争夺临界区的控制权 。Linux将信号灯设计成能在多数情况下有效工作 。

如果此时另外一个进程希望进入此已被别的进程占据的临界区时,它也将此记数减1 。当它看到此记数值为-1 则它知道现在不能进入临界区, 必须等待到此进程退出使用临界区为止 。在这个过程中Linux将让这个等待 进程睡眠 。等待进程将其自身添加到信号灯的等待队列中然后系统在一个循环中检验waking域的值并当waking非0时调用调度管理器 。

临界区的所有者将信号灯记数值加1,但是如果此值仍然小于等于0则表示还有等待此资源的进程在睡眠 。在 理想情况下此信号灯的记数将返回到初始值1而无需做其他工作 。所有者进程将递增waking记数并唤醒在此 信号灯等待队列上睡眠的进程 。当等待进程醒来时,它发现waking记数值已为1,那么它知道现在可以进入临界区了 。然后它将递减waking记数,将其变成0并继续 。所有对信号灯waking域的访问都将受到使用信号灯 的buzz锁的保护 。

推荐阅读