Linux下同步机制
当Linux刚刚开始支持多处理器/SMP时,内核态需要获得BKL(Big Kernel Lock)。现在,只有很少的地方需要BKL进行保护了。
处理器数据(percpu data)(我看不懂。。。)
严格地说不算时同步机制,它可以解决一些本来需要同步机制来处理的问题,从而提高性能。
典型例子有:伙伴内存分配器中buddy system
里维护的每个CPU可分配页的表(pcp list)(我不知道是什么东西);还有slab分配器中的每个CPU的可用和已被分配列表(我也不知道是什么东西)
将原先需要加锁保护的数据结构分离到每个CPU,可以在大多数情况下全速地处理本CPU地数据,不必考虑同步(为什么?我真的不懂)
可抢占内核
Linux 2.6支持抢占内核。利用task_struct
中的一个计数preempt_count
来控制某个程序是否可以被抢占。
调用preempt_disable
、preempt_enable
来控制进程能否被抢占
在内核获得自旋锁、读写锁、或者屏蔽中断的时候,抢占是关掉的(否则会进入死锁)
内核在区的CPU逻辑ID前通常需要关闭抢占,然后在使用完ID后再打开抢占
否则,在取得CPU逻辑ID和使用逻辑ID之间,若发生了抢占,进程回来的时候已经不在原来的CPU上了,获得的数据就将发生错误。
开关中断
内核使用local_irq_disable()
和local_irq_enable()
来开关中断。
local_irq_disable()
是通过设置CPU寄存器来阻塞中断,当local_irq_enanle()
启动后,中断会重新到达,并不会丢失。
而函数disable_irq()
是通过对中断控制器进行编程来阻断中断的发生(从源头)
关闭中断,以便保护可能被中断修改的数据。
注意:
- 注意关闭中断的时间,长时间的关闭会影响系统的反应性
- 中断关闭以后不可发生调度。因为中断关闭后也关闭了抢占,因此进程不会被动调度除去,但是主动调用
schedule
也是被禁止的,因为Linux的调度本身是依赖于中断的(时间片等),关闭中断的情况下进行调度很有可能不会有机会发生下一次调度。
软中断:local_bh_disable()
、local_bh_enable()
用来保护可能能软中断处理的数据。多鉴于网络协议栈。
原子操作
利用不同体系结构的处理器特性来实现原子操作的原语。例如atomic_read
、atomic_set
、atomic_add
、atomic_sub
……
利用它们可以构建更复杂的同步原语(例如自旋锁等)
注意,原子操作并不是没有开销的!
只有在应当使用的时候使用。
为什么要锁总线:test_and_set为原子操作,但是,在多CPU系统中,一条指令可能被拆分到多个CPU执行,从而干扰原子性。汇编时,在指令前缀加上LOCK,处理器就会把总线锁住,直到该指令执行完毕。原子操作是用多条指令完成的。
// 自增举例
long __stdcall Increment(long volatile*