首页 > 代码库 > 同步互斥按键驱动

同步互斥按键驱动

目标:实现同一时刻只能有一个进程使用同一个设备,例如:只能有一个进程,在同一时刻里使用/dev/buttons这个设备。

使用linux互斥机制实现同一时刻只能有一个进程使用某个设备。

linux互斥机制有原子变量、互斥锁、信号量、自旋锁、读写锁等等

 

一、原子操作:

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

整型原子操作:

1.设置原子变量的值

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

2.获取原子变量的值

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

 

3.原子变量加/减

void atomic_add(int i, atomic_t *v); //原子变量增加i
void atomic_sub(int i, atomic_t *v); //原子变量减少i

 

4.原子变量自增/自减

void atomic_inc(atomic_t *v); //原子变量增加1
void atomic_dec(atomic_t *v); //原子变量减少1

 

5.操作并测试

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,为0 则返回true,
否则返回false。

 

6.操作并返回

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

上述操作对原子变量进行加/减和自增/自减操作,并返回新的值。

 

实现同一时刻只能有一个进程使用同一个设备,在上一个驱动程序中加入:

一个全局原子变量

static atomic_t canopen = ATOMIC_INIT(1);     //定义原子变量并初始化为1

open函数中:

if (!atomic_dec_and_test(&canopen))
    {
        atomic_inc(&canopen);
        return -EBUSY;
    }
...

close函数中:

  atomic_inc(&canopen);

 

加载驱动执行./a.out

 

# ./a.out &
# driver: buttons_fasync

#
# ps
PID Uid VSZ Stat Command

802 0 1308 S ./a.out

# ./a.out &
# can‘t open!

 

二、信号量

信号量(semaphore)是用于保护临界区(访问共享资源的代码区域称为临界区(critical sections),临界区需要以某种互斥机制加以保护。)的一种常用方法,只有得到信号量的进程才能执行临界区代码。当获取不到信号量时,进程不会原地打转而是进入休眠等待状态。!



Linux 系统中与信号量相关的操作主要有如下4 种。

 

1.定义信号量

下列代码定义名称为sem 的信号量。

 

struct semaphore sem;

2.初始化信号量

void sema_init (struct semaphore *sem, int val);

该函数初始化信号量,并设置信号量sem 的值为val。尽管信号量可以被初始化为大于1 的值从而成
为一个计数信号量,但是它通常不被这样使用。

 

void init_MUTEX(struct semaphore *sem);

该函数用于初始化一个用于互斥的信号量,它把信号量sem的值设置为1,等同于sema_init (struct semaphore
*sem, 1)。

 

void init_MUTEX_LOCKED (struct semaphore *sem); 

该函数也用于初始化一个信号量,但它把信号量sem 的值设置为0,等同于sema_init (struct semaphore
*sem, 0)。

此外,下面两个宏是定义并初始化信号量的“快捷方式”。

DECLARE_MUTEX(name)
DECLARE_MUTEX_LOCKED(name)

前者定义一个名为name 的信号量并初始化为1,后者定义一个名为name 的信号量并初始化为0。

 

3.获得信号量

void down(struct semaphore * sem);

该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文使用

 

int down_interruptible(struct semaphore * sem);

该函数功能与down()类似,不同之处为,因为down()而进入睡眠状态的进程不能被信号打断,而因为
down_interruptible()而进入睡眠状态的进程能被信号打断,信号也会导致该函数返回,这时候函数的返回值
非0。

在使用 down_interruptible()获取信号量时,对返回值一般会进行检查,如果非0,通常立即返回
-ERESTARTSYS,如:

if (down_interruptible(&sem))
{
return - ERESTARTSYS; }

 

int down_trylock(struct semaphore * sem);

该函数尝试获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,返回非0 值。
它不会导致调用者睡眠,可以在中断上下文使用

 

4.释放信号量

void up(struct semaphore * sem);

该函数释放信号量sem,唤醒等待者。
信号量一般这样被使用,如下所示:

 

 

信号量一般这样被使用,如下所示:

//定义信号量
DECLARE_MUTEX(mount_sem);
down(&mount_sem);//获取信号量,保护临界区
...
critical section //临界区
...
up(&mount_sem);//释放信号量

 

 

驱动代码中:

定义信号量

static DECLARE_MUTEX(button_lock); 

  

在open函数中获取信号量

/* 获取信号量 */
        down(&button_lock);

当第一次执行open函数时就会获取到信号量 

当第二次执行open函数是就会不能获取到信号量就会陷入到休眠。

 

在close函数中释放掉信号量

up(&button_lock);

当第一个程序用完后close后就会将信号量进行释放掉  这时候其他进程就可以使用 。 

结果

技术分享

 其中pid 805处于僵死状态 

第二次无法打开的时候就会在open函数中的down(&button_lock);时陷入休眠 ,当第一个应用程序释放掉这个信号量时才会唤醒这个进程的。

 

同步互斥按键驱动