linux驱动——并发控制

今天的内容回顾的是linux驱动开发中,并发控制的使用,那么问题来了,为什么我们需要进行并发控制?想要了解并发控制的话,那么我们先来认识一下什么是并发。

什么是并发呢?就是多个执行单元同时进行,但是这里要注意,我们所说的同时,只是宏观上面的同时,微观上还是有先后的顺序执行的。

说完并发,那么问题就接踵而至,同时对资源进行访问时,会产生什么情况呢?那就是传说中的竟态了,何谓竟态,就是并发执行的单元对共享资源的访问导致的竞争状态。

并发通常会发生在多CPU之间,单CPU之间进程间的并发,单CPU上进程和中断之间,还有单CPU上中断之间都会出现并发的情况。

现在,你大概知道了我们为什么要进行并发控制了,就是为了提高工作的效率。同时访问我们的共享资源的时候会出现一系列的问题,所以我们要进行相关的限制,那么并发控制有几种呢?

下面我们将对几种常见的并发控制机制进行相关的介绍:

常见的有以下几种:中断屏蔽,自旋锁,信号量,互斥锁,还有原子操作。

1.中断屏蔽

中断屏蔽不会产生进程调度,当前的进程会一直的执行,一般我们要求中断屏蔽的时间要尽可能的短,这样可以提高我们的效率,有些复杂的中断我们还将分为中断上半部和中断下半部,上半部放一些不能被打断执行的操作,下半部我们放一些可以被中断的操作,这样会使中断的利用率更高,关于中断的操作我会在之后继续的更新,这里只是简单的提一下。

如何在linux下实现中断屏蔽,我们经常使用以下几个函数来实现中断屏蔽:

//关闭中断   然后去访问共享的资源

local_irq_disable();

//使能中断

local_irq_enable();


//保存当前的CPSR值并且关闭中断    然偶访问共享资源

local_irq_save();

//恢复中断之前的状态

local_irq_restore();

2.自旋锁

自旋锁是一种满目等待的锁,使用自旋锁的时候,他会关闭抢占,此时不会有进程的调度,在多CPU使用自旋锁时,会有个全局的标志位来控制进程间的并发,使用自旋锁的时候不允许有关可以睡眠的函数使用,因为这样会很浪费我们的CPU资源,自旋锁使用的场合通常是代码量很少的场合,他会不断的在那里等待条件的满足。

如何实现自旋锁:

//定义自旋锁

spinlock_t  lock;

//初始化自旋锁

void spin_lock_init(spinlock_t  * lock);

//获得自旋锁

void spin_lock(spinlock_t *lock) ;  或者 int  spin_trylock(spinlock_t *lock);//尝试获得锁,若获得返回真,否则返回假。

//   ...   ...    执行相关的操作

//释放自旋锁

void spin_unlock(spinlock_t  *lock);


3.信号量(PV操作)

就是通常所说的PV操作,即申请资源和释放资源,他和自旋锁类似,只有得到信号量的进程,才可以访问共享资源,不同的是,当获取不到信号量时,信号量不会进行自旋的等待而是进入休眠等待状态,使用信号量时,当有些资源不能使用的时候,会引起进程的阻塞,我们需要注意的是,信号量不能使用在中断处理函数中。

如何实现信号量:

//定义信号量

struct semaphore sem;


//初始化信号量

sema_init(struct  semaphore *sem,int  value);


//获取信号量   有两种方式  先来说所第一种   如不能获取信号则进入不可中断的等待状态

void down(struct semaphore *sem);

//第二种获取信号量的方式   若不能获得信号量 进入可以被中断的等待状态      

void  down_interrupt(struct semaphore *sem);


//释放信号量   唤醒所有等待当前信号量的进程

void  up(struct semaphore *sem);

4.互斥锁

互斥锁是一种睡眠锁,他比信号量的效率更高,它的使用方法和信号量一样,唯一不同的是,信号量常用于P,V操作,当然也可以用作互斥,而互斥锁只是用来进行互斥的。

如何使用互斥锁:

//定义互斥锁

struct  mutex_lock mlock;

//初始化互斥锁

mutex_init(struct  mutex_lock * lock);


//获得互斥锁

mutex_lock(struct  mutex_lock  * lock);


//释放互斥锁

mutex_unlock(struct mutex_lock *lock);


5.原子操作

原子操作一般针对的是单个变量值的修改,使用这些方式会让变量的值修改的时候由原子特性。我们经常会在linux内核中使用,他提供了好多的函数,有些是把修改变量和判断放在一个函数中去做,另外原子操作是不能被中断的操作。

如何实现原子操作:

//定义原子变量

typedef struct {  int  counter;  } atomic_t;

atomic_t  v;


//设置原子变量的值为 i

void  atomic_set(atomic_t *v,int i);


//获得原子变量的值

#define atomic_read(v)   (*(volatile int * )&(v)->counter)


//原子变量的加和减

void  atomic_add(int  i ,atomic_t *v); //原子变量的值加 i

void  atomic_dec(int  i ,atomic_t  *v);//原子变量的值减i


//原子变量的自增与自jian

void  atomic_inc(atomic_t *v);

void  atomic_dec(atomic_t *v);


//操作并测试   函数操作后会测试原子变量的值是否为0,为0返回true ,否则返回false

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_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);


好了,以上就是对linux下驱动中的并发控制的简单介绍,我们可以再闭上眼睛回忆一下都有哪些并发控制呢?

对的,回答对了,有中断屏蔽,自旋锁,信号量,互斥锁,还有原子操作。


你可能感兴趣的:(驱动,驱动,linux驱动开发,并发控制,信号量,原子操作)