您的位置 首页 培训

Linux设备驱动的并发操控

Linux设备驱动的并发控制-Linux 设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发的访问会导致竞态。

Linux 设备驱动中有必要处理的一个问题是多个进程对同享资源的并发拜访,并发的拜访会导致竞态。

中止屏蔽、原子操作、自旋和信号量都是处理并发问题的机制。中止屏蔽很少独自被运用,原子操作只能针对整数进行,因而自旋锁和信号量运用最为广泛。
自旋锁会导致死循环,确定期间不答应堵塞,因而要求确定的临界区小。信号量答应临界区堵塞,能够适用于临界区大的状况。
读写自旋锁和读写信号量别离是放宽了条件的自旋锁和信号量,它们答应多个履行单元对同享资源的并发读。

中止屏蔽

拜访同享资源的代码区域称为临界区( critical secTIons),在单 CPU 范围内防止竞态的一种简略而省劲的办法是在进入临界区之前屏蔽体系的中止。中止屏蔽将使得中止与进程之间的并发不再产生,并且,因为 Linux 内核的进程调度等操作都依靠中止来完结,内核抢占进程之间的并发也得以防止了。

local_irq_disable(); /* 屏蔽中止 */ … criTIcal secTIon /* 临界区*/ … local_irq_enable(); /* 开中止 */

可是因为 Linux 的异步 I/O、进程调度等许多重要操作都依靠于中止,长期屏蔽中止是很风险的;并且中止屏蔽只对本 CPU 内的中止有用,因而也并不能处理 SMP 多 CPU 引发的竞态。在实践运用中并不引荐直接运用,适合与下文的自旋锁结合运用。

原子操作

Linux 内核供给了一系列函数来完结内核中的原子操作,这些函数又分为两类,别离针对位和整型变量进行原子操作。它们的共同点是在任何状况下操作都是原子的,内核代码能够安全地调用它们而不被打断。

整型原子操作

设置原子变量的值

#include void atomic_set(atomic_t *v, int i); /* 设置原子变量的值为 i */atomic_t v = ATOMIC_INIT(0); /* 界说原子变量 v 并初始化为 0 */

获取原子变量的值

int atomic_read(atomic_t *v); /* 回来原子变量的值*/

原子变量加/减

void atomic_add(int i, atomic_t *v); /* 原子变量添加 i */void atomic_sub(int i, atomic_t *v); /* 原子变量削减 i */void atomic_inc(atomic_t *v); /* 原子变量自增 1 */void atomic_dec(atomic_t *v); /* 原子变量自减 1 *//* 操作完结果==0, return true */int atomic_inc_and_test(atomic_t *v);int atomic_dec_and_test(atomic_t *v);int atomic_sub_and_test(int i, atomic_t *v);/* 操作完结果 <0, return true */int atomic_add_negaTIve(int i, atomic_t *v);/* 操作并回来成果 */int atomic_add_return(int i, atomic_t *v);int atomic_sub_return(int i, atomic_t *v);int atomic_inc_return(atomic_t *v);int atomic_dec_return(atomic_t *v);

位原子操作

位原子操作恰当快,一般只需一个机器指令,不需关中止。

set/clear/toggle

#include /* 更改指针addr所指数据的第nr位 */void set_bit(nr, void *addr);void clear_bit(nr, void *addr);void change_bit(nr, void *addr);

test

int test_bit(nr, void *addr); /* 回来第nr位 */

测验并操作

/* 操作第nr位,并回来操作前的值 */int test_and_set_bit(nr, void *addr);int test_and_clear_bit(nr, void *addr);int test_and_change_bit(nr, void *addr);

自旋锁(spinlock)

自旋锁(spinlock)是一种典型的对临界资源进行互斥拜访的手法,其称号来源于它的作业方法。为了取得一个自旋锁, 在某 CPU 上运转的代码需先履行一个原子操作,该操作测验并设置( test-and-set) 某个内存变量,因为它是原子操作,所以在该操作完结之前其他履行单元不行能拜访这个内存变量。假如测验成果表明锁现已闲暇,则程序取得这个自旋锁并持续履行; 假如测验成果表明锁仍被占用,程序将在一个小的循环内重复这个“ 测验并设置” 操作,即进行所谓的“ 自旋”,浅显地说便是“在原地打转”。 当自旋锁的持有者经过重置该变量开释这个自旋锁后,某个等候的“测验并设置” 操作向其调用者陈述锁已开释。

Basic

界说/初始化

#include /* 静态初始化 */spinlock_t my_lock = SPIN_LOCK_UNLOCKED;/* 动态初始化 */void spin_lock_init(spinlock_t *lock);

获取/开释

/* 根本操作 */void spin_lock(spinlock_t *lock);void spin_unlock(spinlock_t *lock);/* 保存中止状况并封闭 == spin_lock() + local_irq_save() */void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);void spin_unlock_irqsave(spinlock_t *lock, unsigned long flags);/* 疏忽操作前中止状况 */void spin_lock_irq(spinlock_t *lock);void spin_unlock_irq(spinlock_t *lock);/* 封闭中止底部(即封闭软件中止,翻开硬件中止,详见后续中止的解说) */void spin_lock_bh(spinlock_t *lock);void spin_unlock_bh(spinlock_t *lock);/* 非堵塞获取,成功回来非0 */int spin_trylock(spinlock_t *lock);int spin_trylock_bh(spinlock_t *lock);

Reader/Writer Spinlocks

粒度更小,可多Reader一起读,但Writer只能独自,且读与写不能一起,适用于写很少读许多的状况。

界说/初始化

rwlock_t my_rwlock = RW_LOCK_UNLOCKED; /* 静态初始化 */rwlock_t my_rwlock;rwlock_init(&my_rwlock); /* 动态初始化 */

void read_lock(rwlock_t *lock);void read_lock_irqsave(rwlock_t *lock, unsigned long flags);void read_lock_irq(rwlock_t *lock);void read_lock_bh(rwlock_t *lock);void read_unlock(rwlock_t *lock);void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);void read_unlock_irq(rwlock_t *lock);void read_unlock_bh(rwlock_t *lock);

void write_lock(rwlock_t *lock);void write_lock_irqsave(rwlock_t *lock, unsigned long flags);void write_lock_irq(rwlock_t *lock);void write_lock_bh(rwlock_t *lock);void write_unlock(rwlock_t *lock);void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);void write_unlock_irq(rwlock_t *lock);void write_unlock_bh(rwlock_t *lock);

seqlock

次序锁(seqlock)是对读写锁的一种优化,采用了重读机制,读写不彼此堵塞。

界说/初始化

#include seqlock_t lock1 = SEQLOCK_UNLOCKED; /* 静态 */seqlock_t lock2;seqlock_init(&lock2); /* 动态 */

/* 读之前先获取个次序号,读完与当时次序号比照,如不共同则重读 */unsigned int seq;do { seq = read_seqbegin(&the_lock); /* Do what you need to do */} while read_seqretry(&the_lock, seq);/* 假如这个锁或许会出现在中止程序中获取,则在这儿应运用关中止版别 */unsigned int read_seqbegin_irqsave(seqlock_t *lock,unsigned long flags);int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq,unsigned long flags);

void write_seqlock(seqlock_t *lock);void write_seqlock_irqsave(seqlock_t *lock, unsigned long flags);void write_seqlock_irq(seqlock_t *lock);void write_seqlock_bh(seqlock_t *lock);int write_tryseqlock(seqlock_t *lock);void write_sequnlock(seqlock_t *lock);void write_sequnlock_irqrestore(seqlock_t *lock, unsigned long flags);void write_sequnlock_irq(seqlock_t *lock);void write_sequnlock_bh(seqlock_t *lock);

RCU(Read-Copy-Update)

关于被 RCU 维护的同享数据结构,读履行单元不需求取得任何锁就能够拜访它,因而读履行单元没有任何同步开支。运用 RCU 的写履行单元在拜访它前需首要复制一个副本,然后对副本进行修正,最终运用一个回调机制在恰当的机遇把指向本来数据的指针从头指向新的被修正的数据,这个机遇便是一切引证该数据的 CPU 都退出对同享数据的操作的时分。写履行单元的同步开支则取决于运用的写履行单元间同步机制。RCU在驱动中很少运用,这儿暂不胪陈。

注意事项

自旋锁实践上是忙等锁,当锁不行用时, CPU 一向循环履行“测验并设置”该锁直到可用而取得该锁, CPU 在等候自旋锁时不做任何有用的作业,仅仅是等候。 因而,只要在占用锁的时刻极短的状况下,运用自旋锁才是合理的。 当临界区很大,或有同享设备的时分,需求较长期占用锁,运用自旋锁会下降体系的功能。

自旋锁或许导致体系死锁。引发这个问题最常见的状况是递归运用一个自旋锁,即假如一个现已具有某个自旋锁的 CPU 想第2次取得这个自旋锁,则该 CPU 将死锁。

自旋锁确定期间不能调用或许引起进程调度而导致休眠的函数。假如进程取得自旋锁之后再堵塞, 如调用 copy_from_user()、 copy_to_user()、 kmalloc()和 msleep()等函数,则或许导致内核的溃散。

信号量 semaphore

运用方法和自旋锁相似,不同的是,当获取不到信号量时,进程不会原地打转而是进入休眠等候状况。

界说/初始化

#include struct semaphore sem;void sema_init(struct semaphore *sem, int val);/* 一般咱们将val的值置1,即运用互斥形式 */DECLARE_MUTEX(name);DECLARE_MUTEX_LOCKED(name);void init_MUTEX(struct semaphore *sem);void init_MUTEX_LOCKED(struct semaphore *sem);

取得信号量

void down(struct semaphore * sem); /* 信号量减1, 会导致睡觉,因而不能在中止上下文运用 */int down_interruptible(struct semaphore * sem); /* 与down不同的是,进入睡觉后的进程可被打断回来非0 */ int down_trylock(struct semaphore * sem); /* 非堵塞版别,取得回来0,不会导致睡觉,可在中止上下文运用 */

开释信号量

void up(struct semaphore * sem);

Reader/Writer Semaphores

读写信号量与信号量的联系与读写自旋锁和自旋锁的联系相似,读写信号量或许引起进程堵塞,但它可答应 N 个读履行单元一起拜访同享资源, 而最多只能有 1 个写履行单元。因而,读写信号量是一种相对放宽条件的粒度稍大于信号量的互斥机制。

界说/初始化

#include struct rw_semaphore;void init_rwsem(struct rw_semaphore *sem);

void down_read(struct rw_semaphore *sem);int down_read_trylock(struct rw_semaphore *sem);void up_read(struct rw_semaphore *sem);

/* 写比读优先级高,写时一切读只能等候 */void down_write(struct rw_semaphore *sem);int down_write_trylock(struct rw_semaphore *sem);void up_write(struct rw_semaphore *sem);

完结量 completion

轻量级,用于一个履行单元等候另一个履行单元履行完某事。

界说/初始化

#include /* 静态 */DECLARE_COMPLETION(name);/* 动态 */struct completion my_completion;init_completion(struct completion *c);INIT_COMPLETION(struct completion c); /* 从头初始化现已界说并运用过的 completion */

等候完结

void wait_for_completion(struct completion *c);

完结信号

void complete(struct completion *c); /* 唤醒1个 */void complete_all(struct completion *c); /* 唤醒一切waiter */void complete_and_exit(struct completion *c, long retval); /* call complete() and exit(retval) */

 

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/changshang/peixun/89600.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部