分类: LINUX
2015-04-15 18:38:31
现代计算机一般都有多个处理器,这些处理器通常并发执行着某些程序,而这就会导致一些问题,比如说当多个处理器上的进程同时读写某些资源时(比如说变量),就有可能导致一些错误,而解决这个问题的最好方法就是使用一些同步机制来对处理器上的进程进行排队,那么,怎么排?很简单,我们可以只让第一个访问数据的处理器来处理数据,而后来的处理器则让他们执行一个几乎什么都不做的循环,等当前处理器处理完之后,再让下一个处理器进行处理即可。而实现这个同步机制的,就是我们这里要讲的自旋锁。
自旋锁是一种解决多处理器之间的并发问题而诞生的同步机制,他对单处理器的系统没有任何意义。他的原理很简单:给每个需要保护的资源上一把锁,每当有进程访问这个资源时都要先取得这个锁,如果顺利取到了就可以开始访问资源了,如果没取到,那就会进入一个不停检测锁是否被释放的循环等待锁被释放。
在 Linux 中使用 spinlock_t 结构体来描述一个自旋锁,其定义如下:
自旋锁的 raw_lock 成员是一个 raw_spinlock_t 结构体变量,他只有 1 个 lock 成员,这个 lock 成员就是这个自旋锁的锁值。当他为 0 时,就说明这个自旋锁是空闲的,我们取得锁后就可以访问资源了;当他为 1 时,则说明这个自旋锁已经被其他进程获得了,那么我们必须循环等待这个自旋锁被释放(这个循环就是所谓的自旋)。
自旋锁的操作
我们可以通过 DEFINE_SPINLOCK 来静态定义一个自旋锁,也可以通过其他内存分配函数分配一个自选锁然后通过 spinlock_init() 函数来初始化他,刚初始化的自旋锁都是空闲的自旋锁。
我们可以调用 spin_lock 宏来得到一个自旋锁。
spin_lock 宏的 lock参数就是需要得到的那个自旋锁的指针。宏会被转换成对 _spin_lock() 函数的调用。
在获得自旋锁后不允许在释放自旋锁前切换进程,否则一旦有另外一个进程也来获得这个自旋锁,那么就有可能形成死锁。为什么会形成死锁?这是因为内核态的时钟中断是不会执行调度的(除非发生抢占),所以另一个取锁的进程会不停的自旋而不会被调度出去,而调度不出去就意味着锁的拥有者无法得到 CPU 控制权去释放锁,这也就形成死锁了。作为开发者,当然可以确保在获得自旋锁后不主动调用 schedule(),但是因抢占而产生的调度是我们无法预测的,所以为了防止获得自旋锁后发生抢占,在 181 行会先禁止抢占。
182 行会进行一些调试工作(一般未配置,可看做空函数),然后在 183 行使用 _raw_spin_lock 宏来执行真正的取锁操作。
_raw_spin_lock 宏把自旋锁的锁值字段的指针传递给 __raw_spin_lock() 并由他来执行最后的操作。