Linux的同步访问技术

时间:2021-11-21 03:22:38

1.中断屏蔽:

单CPU范围内避免竞态的一种简单方法:在进入临界区之前屏蔽系统的中断。中断屏蔽将使得中断与进程之间的并发不再发生,而且Linux内核的进程调度等操作都依赖中断来实现,内核抢占式进程之间的并发也就得以避免。

操作步骤:

local_irq_disable()  //屏蔽中断

critical section()    //临界区

local_irq_enable()   //开启中断

中断对于内核的运行非常重要,在屏蔽中断期间的终端都无法得到处理,因此长时间的屏蔽中断是很危险的,有可能造成数据丢失甚至是系统崩溃,所以在中断屏蔽后,当前的内核执行路径应当尽快的执行完临界区的代码。

2.原子操作:

原子操作->是在执行的过程中不会被别的代码路径所中断的操作。

Linux内核提供了一系列函数来实现内核中的原子操作,这些函数分为两类:

1>针对位进行原子操作;2>针对整理变量进行原子操作。

共同点:在任何情况下操作都是原子的,内核代码可以安全地调用它们而不会被打断,都是依赖底层CPU的原子操作来实现的。

3.自旋锁:

由于中断屏蔽会使得中断得不到响应,从而导致系统性能变差;原子操作受限于CPU,只能实现有限几种基本数据类型的排他操作;Linux设计了自旋锁以实现共享资源的同步访问。

自旋锁(spin lock)是一种对临界资源进行互斥访问的典型手段。为了获得一个自旋锁,在某CPU上运行的代码需先执行一个原子操作,该操作测试并设置(test-and-set)某个内存变量,由于是原子操作,所以该操作完成之前其他操作执行单元不可能访问这个内存变量。若测试结果表明这个锁已经空闲,则程序获得这个自旋锁并继续执行,若测试结果表明这个锁仍被占用,程序将在一个小的循环内重复这个“测试并设置”操作,即进行所谓的“自旋”。当自旋锁的持有者通过重置该变量释放这个自旋锁后,某个等待的变量“测试并设置”操作向其调用者报告锁已释放。

Linux与自旋锁有关的操作主要有以下4种:

(1)spinlock_t spin;     //定义自旋锁

(2)spin_lock_init(lock);     //动态初始化自旋锁lock

(3)spin_lock(lock);     //获得自旋锁  直到获得  “可能会一直原地打转”

spin_trylock(lock) ;    //获得自旋锁   只尝试一次  “不会一直原地打转”

(4)spin_unlock(lock);    //释放自旋锁

中断处理程序不能使用自旋锁,因为中断处理程序中申请的自旋锁被其他执行程序路径所占有,会导致系统其他中断得不到响应。

自旋锁其实是忙等锁,当锁不可用时,CPU一直循环执行“测试并设置”该锁直到可用而取得该锁,CPU在等待自旋锁时不做任何有用的工作,仅仅是等待,因此只有在占用锁的时间极短的情况下使用自旋锁才是合理的。当临界区很大或有共享设备的时候,需要较长时间占有锁,使用锁会降低系统的性能。

自旋锁还可能会导致系统死锁。递归使用一个锁即如果一个已经拥有自旋锁的CPU想要第二次获得这个锁,则该CPU将产生死锁。此外还有进程获得锁后再阻塞,也可能导致死锁。copy_from_user()、copy_to_user()、kmalloc()等函数都可能因为内存缺页而阻塞,因此在自旋锁占用期间不能调用这些函数。