unsigned long flags;
read_lock_irqsave(&xxx_lock, flags);
... critical section that only reads the info ...
read_unlock_irqrestore(&xxx_lock, flags);
write_lock_irqsave(&xxx_lock, flags);
... read and write exclusive access to the info ...
write_unlock_irqrestore(&xxx_lock, flags);
我们来看看read这组函式的源代码是怎幺样子的:
这二个函式和 spin_lock_irqsave() 和
read_unlock_irqrestore() 的 差别只在于一个是呼叫 spin_lock() 和 spin_unlock(),另一个则是呼叫
read_lock() 和 read_unlock()。
#define read_lock_irqsave(lock, flags) do { local_irq_save(flags);
read_lock(lock); } while (0)
#define read_unlock_irqrestore(lock, flags) do { read_unlock(lock);
local_irq_restore(flags); } while (0)
我们再来看看 read_lock() 和 read_unlock() 这两个函式,在 UP 环境底下是这个样子的:
啊哈,跟 UP 底下的 spin_lock() 和
spin_unlock() 完全是一模相同的,所以,事实上在 UP 的环境下,使用 rwlock 和 spinlock 是没有差别的。其实,各位能够自己去看
write_lock_irqsave() 和 write_unlock_irqsave() 的程式,扩展开来跟上面两组函式都是相同的。原因其实很简单,在 UP
的环境下,虽然 Linux 号称多任务的系统,但由于只有一颗 CPU,在同一时间只有一个行程在执行,其他的行程都会被 suspend,唯一会中断 Kernel
执行的只有 interrupt 了。所以,事实上,要做好 critical section 的保护只要暂时将中断 disable 掉就行了。 Kernel
之所以要提 供上面这些函式其实是要给 SMP 的系统使用的,除此之外,他另一个用途就是增加 portability。 程式只要用 spinlock
来写的话,那不管是在 SMP 或 UP 环境下都能够直接 compile 并执行,不用再重新修改程式码。
#define read_lock(lock) (void)(lock) /* Not "unused variable". */
#define read_unlock(lock) do while(0)
至于 SMP 底下 rwlock 的实作方式我就不再赘述,基本上他们的实作方式都是差不多的,只有一点要特别说的是,由于 rwlock 能够容许多个 reader,但却只能有一个 writer,所以,他不会只用到 rwlock_t.lock 的第 0 个 bit 而已。事实上,rwlock_t.lock 是个 32bit 的 unsigned int 型别的变数,因此,他用第 0 到 30 个 bit 当作 reader 的 counter,而第 31 个 bit 则是用来给 writer 使用的。当第 31 个 bit 为 1 时,表示现在 rwlock 被 writer 锁住,此时前 30 个 bit 都应该是 0,表示此时没有任何的 reader。因此,能够推断 rwlock 同一时间最多能够有 2 的 30 次方个 reader。
第三种使用 spinlock 的方式
我们能够看到以上两种的使用机制都是以 disable 中断的方式来做的,虽然 disable 中断很简单,只要一个指令就行了,但事实上,这个指令的 cost 对 CPU 来讲是蛮大的。所以, Kernel 还提供另一组的函式,他不 disable 中断,所以,他的执行速度会比上面两种来得有效率一些。 但是,上帝是公平的,他让您速度快,相对的他也提供的某些限制。这个限制就是就假如您确定 interrupt handler 不会用到这个受保护的资料结构时,那您就能够考虑用这一组的函式, 以加快程式的执行。其实,这一组函式我们已在上面见过了。




