自旋锁的使用场景
当多个执行单元同时、并行 地执行,并且并发的执行单元对共享资源进行访问,则会导致竞态。这些执行单元多为多处理器下的不同核下的进程或中断,或者单处理器下允许内核抢占的两个不同进程或中断。要避免竞态的产生,则需要对共享资源的代码段互斥访问,这一互斥的操作则有自旋锁来完成。
自旋锁的实现原理
linux内核提供了位和整形变量的原子操作函数,这些函数的实现也是以来cpu的原子操作,与cpu底层架构有关。就arm处理器而言,底层的实现细节主要通过汇编指令ldrex和strex来完成。原子操作可以保证对位和整形数据的操作是排他性的,所以为临界区代码的互斥访问奠定了基础。自旋锁就是通过执行一个原子操作,测试并设置某个内存变量,由于它是原子操作,所以对该变量的访问是排他性的,如何测试到锁为空闲,则获取该自旋锁,否则重复执行该原子操作,直至该锁可用为止。
自旋锁的使用方法
假设在一个单处理内核可抢占的进程中,该进程获得了一个自旋锁,但是突然产生了中断,并且在中断处理函数中唤醒了比当前进程优先级更高的进程,可想而知,此时会产生一个进程调度,将当前进程切换为优先级高的进程,倘若这个新的进程再次申请此自旋锁,则会因为申请不到而导致cpu一直执行那个原子操作,这就产生了死锁。所以,应该避免该情况的产生,在使用自旋锁的时候禁止进程的切换,即禁止进入中断,因为中断对于系统来说十分重要,所以自旋锁临界区的操作应该尽可能短。自旋锁获取和释放函数为spin_lock、spin_unlock,若在获取锁的情况下同时禁止中断,则可使用spin_lock_irp、spin_unlock_irq。另外,还有一些其他会产生进程调度的函数,比如copy_from_user()、copy_to_user()、kmalloc()、msleep等会产生阻塞的函数,都不应该在自旋锁的临界区使用。
其他互斥操作
信号量:信号量用在pv操作中比较常见,用于进度调度中的同步互斥问题,解决进程间的逻辑依赖关系,比如生产者-消费者问题。
互斥体:其与自旋锁类似,但是其临界区开销可以很大。互斥体的临界区开销是比较大的,其允许进程阻塞。从严格意义上说,互斥体和自旋锁属于不同层次的互斥手段,前者的实现依赖于后者。从互斥本身的实现上,为了保证互斥体结构存取的原子性,需要自旋锁来互斥。所以自旋锁属于更底层。
网友评论