10.按键之互斥、阻塞机制(详解)

  • 本节目标:
  1. 学习原子操作和互斥信号量,实现互斥机制,同一时刻只能一个应用程序使用驱动程序
  2. 学习阻塞和非阻塞操作

当设备被一个程序打开时,存在被另一个程序打开的可能,如果两个或多个程序同时对设备文件进行写操作,这就是说我们的设备资源同时被多个进程使用,对共享资源(硬件资源、和软件上的全局变量、静态变量等)的访问则很容易导致竞态。

显然这不是我们想要的,所以本节引入互斥的概念:实现同一时刻,只能一个应用程序使用驱动程序

互斥其实现很简单,就是采用一些标志,当文件被一个进程打开后,就会设置该标志,使其他进程无法打开设备文件。

1.其中的标志需要使用函数来操作,不能直接通过判断变量来操作标志

比如:

if (-- canopen != 0)  //当canopen==0,表示没有进程访问驱动,当canopen<0:表示有进程访问

    编译汇编来看,分了3段: 读值、减1、判断

如果刚好在读值的时候发生了中断,有另一个进程访问时,那么也会访问成功,也会容易导致访问竞态。

1.1所以采用某种函数来实现,保证执行过程不被其他行为打断,有两种类型函数可以实现:

原子操作(像原子一样不可再细分不可被中途打断)

当多个进程同时访问同一个驱动时,只能有一个进程访问成功,其它进程会退出

互斥信号量操作

比如:A、B进程同时访问同一个驱动时,只有A进程访问成功了,B进程进入休眠等待状态,当A进程执行完毕释放后,等待状态的B进程又来访问,保证一个一个进程都能访问

2. 原子操作详解

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

原子操作函数如下:

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

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

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

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

5)int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。

2.1修改驱动程序

定义原子变量:

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

在.open成员函数里添加:

/*自减操作后测试其是否为0,为0则返回true,否则返回false   */
if(!atomic_dec_and_test(&canopen))     
 {
  atomic_inc(&canopen);       //++,复位
  return -1;
 }

在. release成员函数里添加:

  atomic_inc(&canopen);       //++,复位

2.2修改测试程序:

int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;     
  fd=open("/dev/buttons",O_RDWR);          
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);
       return -1;}
   while(1)
   { 
     read( fd, &ret, 1);              //读取驱动层数据
     printf("key_vale=0X%x\r\n",ret);   
   }  

   return 0;

}

2.3 测试效果

如下图,可以看到第一个进程访问驱动成功,后面的就再也不能访问成功了

3.互斥信号量详解

互斥信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。

当获取不到信号量时,进程进入休眠等待状态。

信号量函数如下:

/*注意: 在2.6.36版本后这个函数DECLARE_MUTEX修改成DEFINE_SEMAPHORE了*/
1)static DECLARE_MUTEX(button_lock);     //定义互斥锁button_lock,被用来后面的down和up用
2)void down(struct semaphore * sem);            // 获取不到就进入不被中断的休眠状态(down函数中睡眠)
3)int down_interruptible(struct semaphore * sem);  //获取不到就进入可被中断的休眠状态(down函数中睡眠)
4)int down_trylock(struct semaphore * sem);       //试图获取信号量,获取不到则立刻返回正数
5)void up(struct semaphore * sem);               //释放信号量

3.1修改驱动程序(以down函数获取为例)

(1)定义互斥锁变量:

/*定义互斥锁button_lock,被用来后面的down()和up()使用 */
static DECLARE_MUTEX(button_lock);  

(2)在.open成员函数里添加:

/* 获取不到就进入不被中断的休眠状态(down函数中睡眠) */
          down(&button_lock);

(3)在. release成员函数里添加:

 /*         释放信号量          */
          up(&button_lock);               

3.2修改测试程序:

 int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;      
  fd=open("/dev/buttons",O_RDWR);          
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);
       return -1;}
  else
       {
       printf("can open,PID=%d\n",getpid());    //打开成功,打印pid进程号
       }
   while(1)
   { 
     read( fd, &ret, 1);              //读取驱动层数据
     printf("key_vale=0X%x\r\n",ret);  
   }  
   return 0;
}

3.3 测试效果

如下图所示,3个进程同时访问时,只有一个进程访问成功,其它2个进程进入休眠等待状态

如下图所示,多个信号量访问时, 会一个一个进程来排序访问

4.阻塞与非阻塞

4.1阻塞操作 

进程进行设备操作时,使用down()函数,若获取不到资源则挂起进程,将被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

在read读取按键时, 一直等待按键按下才返回数据

4.2非阻塞操作

进程进行设备操作时,使用down_trylock()函数,若获取不到资源并不挂起,直接放弃。

在read读取按键时, 不管有没有数据都要返回

4.3 怎么来判断阻塞与非阻塞操作?

在用户层open时,默认为阻塞操作,如果添加了” O_NONBLOCK”,表示使open()、read()、write()不被阻塞

实例:

fd=open("/dev/buttons",O_RDWR);                                          //使用阻塞操作
fd = open("/dev/buttons ", O_RDWR | O_NONBLOCK);                         //使用非阻塞操作

然后在驱动设备中,通过file_operations成员函数.open、.read、.write带的参数file->f_flags 来查看用户层访问时带的参数

实例:

  if(  file->f_flags & O_NONBLOCK )   //非阻塞操作,获取不到则退出
  {
     ... ...
  }
  else   //阻塞操作,获取不到则进入休眠
  {
     ... ...
  }

4.4修改应用程序,通过判断file->f_flags来使用阻塞操作还是非阻塞操作

(1)定义互斥锁变量:

/*定义互斥锁button_lock,被用来后面的down()和up()使用 */
static DECLARE_MUTEX(button_lock); 

(2)在.open成员函数里添加:

if( file->f_flags & O_NONBLOCK )   //非阻塞操作
  {
   if(down_trylock(&button_lock) )       //尝试获取信号量,获取不到则退出
            return -1;
  }else   //阻塞操作
  {
     down(&button_lock);         //获取信号量,获取不到则进入休眠
  }

 (3)在. release成员函数里添加:

        /*释放信号量*/
          up(&button_lock);     

4.5 写阻塞测试程序 fifth_blocktext.c

代码如下:

int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;       
  fd=open("/dev/buttons",O_RDWR);           //使用阻塞操作
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);      
       return -1;}
  else
       {
       printf("can open,PID=%d\n",getpid());    //打开成功,打印pid进程号
       } 

   while(1)
   { 
    val=read( fd, &ret, 1);              //读取驱动层数据
     printf("key_vale=0X%x,retrun=%d\r\n",ret,val);  
   }
   return 0;

}

4.6 非阻塞测试效果

如下图所示:      

4.7写阻塞测试程序 fifth_nonblock.c

代码如下:

int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;      
  fd=open("/dev/buttons",O_RDWR | O_NONBLOCK);   //使用非阻塞操作 
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);
       return -1;}
  else
       {
       printf("can open,PID=%d\n",getpid());    //打开成功,打印pid进程号
       }
   while(1)
   { 
              val=read( fd, &ret, 1);                              //读取驱动层数据
            printf("key_vale=0X%x,retrun=%d\r\n",ret,val); 
                 sleep(3);         //延时3S
   }  
   return 0;}

4.8 阻塞测试效果

如下图所示:      

接下来学习按键驱动使用定时器防抖 

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏battcn

利用 Nginx 实现限流

Nginx为我们提供了请求限制模块( ngx_http_limit_req_module)、基于令牌桶算法的流量限制模块( ngx_stream_limit_c...

562
来自专栏CaiRui

nginx的worker_processes优化

nginx的worker_processes参数 来源: http://bbs.linuxtone.org/thread-1062-1-1.html 分享一: ...

4177
来自专栏携程技术中心

干货 | 长连接/websocket/SSE等主流服务器推送技术比较

作者简介 本文由携程市场营销研发部武艺嫱和王宇星以及张子祥共同撰写,武艺嫱在市场营销研发部负责前端,王宇星和张子祥在市场营销研发部负责java后端。 最近做的...

4788
来自专栏我是攻城师

Logstash与Kafka集成

3495
来自专栏皮振伟的专栏

[kvm][virt]PIO技术分析

前言: 基于KVM的设备虚拟化,就从这里开始吧。 分析: 1,PIO Port IO,所谓端口IO,x86上使用in、out指令进行访问。和内存的地址空间完...

2857
来自专栏智能大石头

NewLife.Net——开始网络编程

1143
来自专栏java相关

Redis学习笔记01---配置文件

1114
来自专栏点滴积累

Ubuntu14.04双网卡主备配置

近日有个需求,交换机有两台,做了堆叠,服务器双网卡,每个分别连到一台交换机上。这样就需要将服务器的网卡做成主备模式,以增加安全性,使得当其中一个交换机不通的时候...

3116
来自专栏jeremy的技术点滴

重新理解HTTP中的“持久连接”

3314
来自专栏智能大石头

NewLife.Net——开始网络编程

网络编程的重要性就不说了,先上源码:https://github.com/nnhy/NewLife.Net.Tests

720

扫码关注云+社区