linux 驱动——并发和竞态

来源:互联网 发布:图像数据挖掘研究综述 编辑:程序博客网 时间:2024/06/08 19:20

一、并发造成的原因

l 中断——中断几乎可以在任何时刻异步发生,也就可能随时打断当前正在执行的代码。
2 睡眠及与用户空间的同步——在内核执行的进程可能会睡眠,这就会唤醒调度程序,从而导致调度一个新的用户进程执。
3 对称多处理——两个或多个处理器可以同时执行代码。
4内核抢占——因为内核具有抢占性,所以内核中的任务可能会被另一任务抢占(在2.6内核引进的新能力)。
5 多个用户空间进程组合访问代码

要在内核代码中避免使用全局变量
进程上下文:实际上是进程执行活动全过程的静态描述。我们把已执行过的进程指令和数据在相关寄存器与堆栈中的内容称为上文,把正在执行的指令和数据在寄存器和堆栈中的内容称为正文,把待执行的指令和数据在寄存器与堆栈中的内容称为下文。

二、信号量与互斥体

信号量: 是P、V操作,访问临界区时,进程条用P,信号量大于零时,信号量值减一,可获得临界区资源,进程继续;信号量不大于零时,进程等待其他人获取临界区资源。释放临界区资源时,调用V操作,信号量值增加,并唤醒等待此资源的进程。
互斥体:当信号量初始化为1时,只能被一个进程同时拥有,此时可称为互斥体。

使用内核态中, 信号量几乎都用于互斥。与互斥体相同。
用户态: 信号量:是进程间(线程间)同步用的,一个进程(线程)完成了某一个动作就通过信号量告诉别的进程(线程),别的进程(线程)再进行某些动作。有二值和多值信号量之分。
互斥锁:是线程间互斥用的,一个线程占用了某一个共享资源,那么别的线程就无法访问,直到这个线程离开,其他的线程才开始可以使用这个共享资源。可以把互斥锁看成二值信号量。

 信号量的初始化及操作。
void sema_init(struct semaphore *sem, int val);void down(struct semaphore *sem);int down_interruptible(struct semaphore *sem);int down_trylock(struct semaphore *sem);void up(struct semaphore *sem);

在新的内核中推荐使用互斥体。信号量不被推荐。

互斥体初始化及其操作

宏DEFINE_MUTEX静态定义和初始化一个互斥锁static DEFINE_MUTEX(mymutex); 动态初始化void mutex_init(struct mutex *mutex); void mutex_lock(struct mutex *lock); ————>  睡眠时,不可被信号中断 int mutex_lock_interruptible(struct mutex *lock); ————> 睡眠时,可被信号中断 int mutex_trylock(struct mutex *lock); ————> 获取不到信号时,会返回 void mutex_unlock(struct mutex *lock);

非信号中断可以理解为建立不可杀的进程。一般采用mutex_lock_interruptible作为使用

读取者/写入者信号量

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);void downgrade_write(struct rw_semaphore *sem);

三、自旋锁 (spinlock)

使用 : 可在中断处理例程中获得更高的效能。

自旋锁死锁造成的原因: 获得自旋锁时,在临界区运行时,若调用了可能造成休眠的函数如copy_to_user, kmalloc, 此时进程可能休眠,也可能发生内核抢占,或是发生中断,此时拥有锁的进程会丢到处理器;之后,其他进程可能会调用此自旋锁,此时就会造成死锁。
拥有自旋锁时,但内核抢占被禁止,SMP下的该核的抢占调度也被禁止。但可能受到硬件中断核软件中断的影响,所以需要使用软中断。
自旋锁定义及使用

void spin_lock_init(spinlock_t *lock);void spin_lock(spinlock_t *lock);  ————>没有禁止中断void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);————>禁止中断,返回时,中断状态与禁止中断一致void spin_lock_irq(spinlock_t *lock);————>禁止中断,返回时,中断状态开void spin_lock_bh(spinlock_t *lock);————>禁止软中断void spin_unlock(spinlock_t *lock);void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);void spin_unlock_irq(spinlock_t *lock);void spin_unlock_bh(spinlock_t *lock);int spin_trylock(spinlock_t *lock);int spin_trylock_bh(spinlock_t *lock);

四、自旋锁与互斥锁的使用原则

1、自旋锁时忙等待、互斥锁是休眠,所以大的临界区用互斥锁,小的用自旋锁
2、互斥锁保护的临界区可以包含引起阻塞的函数、代码如copy_to_user、kmalloc、msleep;
而自旋锁禁止,若引起阻塞,新的进程可能调用此自旋锁,就会造成死锁。
3、被中断调用的临界区需要使用自旋锁,若要使用互斥锁,则只能使用mutex_trylock();

五、锁之外的方法

1、循环队列
循环缓冲区及两个索引, 要保证两个索引不重叠

2、原子操作

void atomic_set(atomic_t *v, int i);atomic_t v = ATOMIC_INIT(0);int atomic_read(atomic_t *v);void atomic_add(int i, atomic_t *v);void atomic_sub(int i, atomic_t *v);void atomic_inc(atomic_t *v);void atomic_dec(atomic_t *v);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);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);atomic_sub(amount, &first_atomic);atomic_add(amount, &second_atomic);

3、 位操作

void set_bit(nr, void *addr);void clear_bit(nr, void *addr);void change_bit(nr, void *addr);test_bit(nr, void *addr);int test_and_set_bit(nr, void *addr);int test_and_clear_bit(nr, void *addr);int test_and_change_bit(nr, void *addr);

4、顺序锁读写更新

还可参考更详细的文章: http://blog.csdn.net/zqixiao_09/article/details/50898854

阅读全文
0 0