您的位置:首页 > 运维架构 > Linux

linux内核中访问共享资源

2016-03-08 22:04 295 查看
访问共享资源的代码区域称为临界区,临时以某种互斥机制加以保护。中断屏蔽、原子操作

自旋锁和信号量是Linux设备驱动中可采用的互斥途径。

在单CPU范围内避免竞态的一种简单方法是在进入临界区之前屏蔽系统的中断。

CPU一般都具备屏蔽中断和打开中断的功能。

中断屏蔽的使用方法:

Local_irq_disable(); //屏蔽中断--->和它不同的是local_irq_save除了禁止中断操作以外还可以保存目前CPU的中断位信息

......

临界区

......

Local_irq_enable(); //开中断--->local_irq_restore进行的是与local_irq_save相反的操作。

要求:在屏蔽了中断之后,当前的内核执行路径应当尽快地执行完临界区的代码。

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

禁止的底半部应该调用local_bh_enable();

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

内核中有一些函数分为两类,分别针对位和整型变量进行原子操作。共同点:在任何情况下操作都是原子的,内核代码可以安全地调用

它们而不被打断。都是依赖底层CPU的原子操作来实现的。所以这些函数都与CPU的架构密切相关。

SMP:多处理器结构的简称.

自旋锁的作用:

自旋锁spin_lock是一种对临界资源进行互斥访问的典型手段,这个名字是由于它的工作方式。

要获得自旋锁,在CPU上运行的代码需要先执行一个原子操作,该操作测试并设置某个内存变量,

由于它是原子操作,所以在操作完成之前其它执行单元不可能访问这个内存变量。

如果测试结果表明锁已经空闲,则程序获得这个自旋锁并继续执行;如果测试结果表明锁

仍被占用,程序将在一个小循环内重复这个"测试并设置"操作,即进行所谓的“自旋”,通俗的说

就是在原地打转,当自旋锁的持有者通过重新设置该变量释放这个自旋锁后,某个等待的"测试并设置"的操作

向其调用者报告锁已经被释放。

自旋锁操作主要针对SMP或单CPU但内核可抢占的情况,对于单CPU和内核不支持抢占的系统,自旋锁退化为空操作。

在单CPU和内核可抢占的系统中,自旋锁持有期间内核的抢占将被禁止。由于内核抢占的单CPU兄的行为实际

很类似于SMP系统,隐私在这样的单CPU系统中使用自旋锁就十分必要。

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

定义一个自旋锁:

Spinlock_t spin ;

初始化自旋锁:

Spin_lock_init(lock) ;

获得自旋锁:

Spin_lock(lock);

如果能立即获得锁,就马上返回,否则,它将自旋,直到该自旋锁的保持者释放。

Spin_trylock(lock);

尝试获得自旋锁lock,如果可以立即获得,获得并返回真,否则立即返回假,实际上不再原地打转.

释放锁:

Spin_unlock(lock);

与spin_trylock或spin_lock配对使用。

一般这样使用:

Spinlock_t lock ;

Spin_lock_init(&lock);

Spin_lock(&lock);//获取自旋锁,包含临界区

.....//临界区

Spin_unlock(&lock); //解锁

使用自旋锁实际上是忙等锁,当锁不可用时,CPU一直循环执行测试并设置该锁直到可用而

取得该锁。此时CPU在等待自旋锁不做任何有用的工作,仅仅是等待。因此只有在

占用锁极短的情况下,使用自旋锁才是合理的,如果临界区有很大或者有共享设备的时候

,需要较长时间占用锁,会降低系统的性能。

自旋锁可能导致系统死锁,常见情况就是递归一个自旋锁,如果已经拥有某个自旋锁的

CPU想第二次获得这个自旋锁,则该CPU将死锁。如果进程获得自旋锁后再阻塞,也可能

导致死锁的发生。copy_from_user(),copy_to_user()和kmalloc()等函数都有可能引起阻塞。

因此自旋锁的占用期间不能调用这些函数。

读写自旋锁:是一种比自旋锁粒度更小的锁机制,它保留了自旋的概念,但是在写操作方面,

只能最多有一个写进程,在读操作方面,同事可以有多个读执行单元。当然,读和写也不能同事进行。

定义和初始化:

rwlock_t lock = RW_LOCK_LOCKED ; //静态初始化

rwloct_t lock ;

rwlock_init(&lock);

读锁定:

read_lock(rwlock_t *lock);

read_lock_irq(rwlock_t *lock);

read_lock_irqsave(rwlock_t *lock , unsigned long flags);

read_lock_bh(rwlock_t *lock);

读解锁:

read_unlock(rwlock_t *lock);

read_unlock_irq(rwlock_t *lock);

read_unlock_irqrestore(rwlock_t *lock , unsigned long flags);

read_unlock_bh(rwlock_t *lock);

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