您的位置:首页 > 其它

锁(一) 中断屏蔽 原子操作

2017-04-27 10:38 141 查看
中断屏蔽

单cpu,在单cpu范围内避免竞态的简单方法是在进入临界区之前屏蔽系统的中断。cpu一般都具备屏蔽中断和打开中断

的功能,这项功能可以保证正在执行的内核执行路径不被中断处理程序抢占,防止某些竞态条件的发生。由于linux内核

的进程调度等操作都依赖中断来实现,内核抢占进程之间的并发也得以避免。

中断屏蔽的使用方法为:

local_irq_disable()    //屏蔽中断

...

critical section            //临界区

...

local_irq_enable()    //开中断

由于linux的异步I/O,进程调度等很多重要操作都依赖于中断,中断对于内核的运行非常重要,在屏蔽中断期间所有的中

断都无法得到处理,因此长时间屏蔽中断是很危险的,有可能造成数据丢失乃至系统崩溃等后果。在就要求在屏蔽中断

之后,当前的内核执行路径应当尽快地执行完临界区的代码。

local_irq_disable()和local_irq_enable()只能屏蔽使能本cpu(单个cpu)的中断,因此不适合多cpu的竞态处理。因此

单独使用中断屏蔽通常不是一种值得推荐的避免竞态的方法,它适合于自旋锁联合使用。

与local_irq_disable()不同的是,local_irq_save(flags)除了进行禁止中断的操作以外,还保存目前CPU的中断位信息。

如果只是想禁止使能中断的底半部,应使用local_bh_disable(),local_bh_enable()。

原子操作

原子操作指的是在执行过程中不会被别的代码路径所中断的操作。linux内核提供了一系列函数来实现内核中的原子操作,

这些函数分为两类,分别针对(位和整形变量)进行原子操作。特点咋任何情况下操作都是原子的,内核代码可以安全地

调用它们而不被打断。位和整形变量原子操作都依赖底层CPU的原子操作来实现,因此所有这些函数都与cpu架构相关。

自旋锁

自旋锁(spin lock)是一种典型的对临界资源进行互斥访问的手段,为了获得自旋锁在某个cpu上运行的代码需先执行一个

原子操作,该操作测试并设置某个内存变量,由于它是原子操作,所以在该操作完成之前其他执行单元不可能访问这个内存

变量。如果测试操作结果表明锁已经空闲,则程序获得这个自旋锁并继续执行;如果结果表明锁仍被占用,程序将在一个小

的循环内重复这个“测试并设置”操作,即进行所谓的“自旋”(原地打转)。当自旋锁的持有这通过重置该变量释放自旋锁后,

某个等待的“测试并设置”操作向其调用者报告锁已释放。



定义自旋锁:spin_lock_t lock;

初始化自旋锁:spin_lock_init(lock);

获得自旋锁:spin_lock(lock);如果获取到自旋锁就立即返回,否则它将自旋在哪里,直到该自旋锁的持有者释放。

                        spin_trylock(lock);尝试获得自旋锁,如果立即获得锁,就返回真,否则立即返回假,不再“原地打转”。

释放自旋锁:spin_unlock(lock);

自旋锁使用中需要注意的问题:

1,只有在占用锁的时间极短的情况下,使用自旋锁才是合理的。当临界区很大或有共享设备的时候,需要较长时间占用锁,

使用自旋锁会降低效率。原因是自旋锁实际上是忙等锁,当锁不可用时cpu一直循环执行“测试并设置”该锁直到可用而取得该

锁,cpu在等待自旋锁时不做任何有用的工作,仅仅是等待。

2,自旋锁可能导致系统死锁。比如递归调用一个自旋锁,即如果一个已经拥有某个自旋锁的cpu想第二次获得这个自旋锁,则

该cpu将死锁。

3,自旋锁锁定期间不能调用可能引起进程调度的函数。如果进程获得自旋锁之后再阻塞,如调用copy_from_user(),copy_to_user(),

kmalloc()和msleep()等函数,则可能导致内核的崩溃。

自旋锁实现设备只能被一个进程打开

int x_count = 0;
static int x_open(struct inode *inode,struct file *filp)
{
...
spinlock(&x_lock);
if(x_count){
spin_unlock(&x_lock);
return -EBUSY;
}
x_count++;
spin_unlock(&x_lock);
...
return 0;
}

static int x_release(struct inode *inode,struct file *filp)

{
...
spinlock(&x_lock);
x_count--;
spin_unlock(&x_lock);

return 0;
}

读写自旋锁

自旋锁不支持并行读,但是自旋锁的衍生锁读写自旋锁支持读并行。

    自旋锁不关心锁定的临界区究竟进行怎样的操作,不管是读还是写都一样对待,即便多个执行单元同时读取临界资源也会被锁住。

实际上对共享资源并发访问时,多个执行单元同时读取它是不会有问题的,自旋锁的衍生锁读写自旋锁(rwlock)可允许读的并发,

但是在写操作方面某一时刻只能最多有1个写进程,在读操作方面某一时刻可以有多个读执行单元,读写某一时刻最多只能有一个。

顺序锁

    顺序锁(seqlock)是对读写锁的一种优化,如使用顺序锁读执行单元不会被写执行单元阻塞,也就是说读执行单元在写执行单元

对被顺序锁保护的共享资源进行写操作时仍然可以继续读,而不必等待写执行单元完成写操作,写执行单元也不需要等待所有读执行

完成读操作才去进行写操作。

    但是,写执行单元也写执行单元之间仍然是互斥的,A没有释放,B只能自旋在哪里直到A释放顺序锁。如果读执行单元在读操作期

间,写执行单元已经发生了写操作,那么读执行单元必须重新读取数据,以便确保得到的数据是完整的。这种锁对于读写同时进行的

概率比较小的情况,性能是非常好的,而且它允许读写同时进行,因而更大的提高了并发性。

    顺序锁的限制,它必须要求被保护的共享资源不含有指针,因为写执行单元可能使得指针失效,但读执行单元如果正要去访问该指

针,将导致oops。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息