字符设备驱动学习笔记---并发

来源:互联网 发布:java gc 编辑:程序博客网 时间:2024/06/11 17:10

 

=========驱动程序中的同步互斥 阻塞 =================================

同一时间只能有一个 应用打开驱动程序

linux中处理并发的几种解决方法:信号量和互斥体自旋锁 原子操作

 

  a 原子操作:

  atomic_t 定义在<asm/atomic.h>中

   常用操作:

                   void atomic_set(atomic_t *v,int i);

                   atomic_t v=ATMIC_INIT(0);//初始化原子变量

                  

                   int atomic_read(atomic_t *v);//返回v的当前值

                  

                   void atomic_add(int i,atomic_t *v);//将i累加到v指定的原子变量

                  

                   void atomic_sub(int i,atomic_t *v);//从*v中减去i

                  

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

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

                  

                   int atomic_inc_and_test(atomic_t *v);// 自加操作后测试其返回结果是否为0

b 信号量:

 semaphore 定义在<linux/semaaphore.h>中

       常用操作:

                     structsemaphore sem;//定义一个信号量

                     voidsema_init(struct semaphore *sem,int val);//初始化一个信号量  

            

             //声明和初始化互斥体(linux 3.x 中可能被删除)

             DECLARE_MUTEX(name);//1

             DECLARE_MUTEX_LOCKED(name);//0

            

             //初始化互斥体 (linux 3.x 中可能被删除)

             voidinit_MUTEX(struct semaphore *sem);//

             voidinti_MUTEX_LOCKED(struct semaphore *sem);//

            

             //获得信号量

             voiddown(struct semaphore *sem);

             intdown_interruptible(struct semaphore *sem);

             intdown_trylock(struct semaphore *sem);

            

             //释放信号量

             voidup(struct semaphore *sem);

  c 阻塞和非阻塞

      阻塞:在执行设备操作时若不能获得资源则挂起,直到满足条件后再理行操作

      非阻塞:进程在不进行设备操作时并不挂起,或者放弃,或者不停查询,直到可以进行操作

       目标:同一时间只能有一个应用打开驱动程序

//阻塞方式

       //fd=open("/dev/sixthdrv-sixthdrv",O_RDWR);

       //非阻塞方式

       fd=open("/dev/sixthdrv-sixthdrv",O_RDWR| O_NONBLOCK);

 

 

驱动示例代码如下;

/****************************************

*第七个驱动程序   异步通知方式实现按键驱动 原子同步

*****************************************/

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <linux/poll.h>    

#include <linux/irq.h>

#include <asm/irq.h>

#include <asm/io.h>

#include <linux/interrupt.h>

#include <asm/uaccess.h>

#include <mach/hardware.h>

#include <linux/platform_device.h>

#include <linux/cdev.h>

#include <linux/miscdevice.h>

#include <linux/device.h>

 

#include <mach/map.h>

#include <mach/regs-clock.h>

#include <mach/regs-gpio.h>

 

#include <plat/gpio-cfg.h>

#include <mach/gpio-bank-n.h>

#include <mach/gpio-bank-l.h>

#include <asm/atomic.h>

#include <linux/semaphore.h>

 

#define DEVICE_NAME"seventh_button_dev"

 

static struct fasync_struct *button_async;

static struct class*seventh_button_dev_class;

 

int major;

staticDECLARE_WAIT_QUEUE_HEAD(button_waitq);

/*中断事件标志,中断处理函数将其置1,third_drv_read将其置0*/

static volatile int ev_press=0;

 

/*自定义中断结构体*/

struct button_irq_desc{

       intirq;//按键中断号

       intnumber;//

       char*name;//按键名

};

 

 

//按键数组

static struct button_irq_descbutton_irqs[]={

       {IRQ_EINT(0),0,"K0"},

      {IRQ_EINT(1),1,"K1"},

       {IRQ_EINT(2),2,"K2"},

       {IRQ_EINT(3),3,"K3"},

       {IRQ_EINT(4),4,"K4"},

       {IRQ_EINT(5),5,"K5"},

       {IRQ_EINT(19),6,"K6"},

       {IRQ_EINT(20),7,"K7"},

};

 

//static volatile char key_values[]={'0','0','0','0','0','0','0','0'};

static volatile int keyValue=0;

 

//使用原子量来处理并发

//static atomic_t canOpen =ATOMIC_INIT(1);

 

//使用信号量来处理并发

//linux 3.x中可能被删除,不能使用以下方法初始化

//static DECLEAR_MUTEX(button_lock);

static struct semaphore button_lock;

 

 

static irqreturn_t buttons_irq(int irq,void *dev_id)

{

       printk("irq=%d\n",irq);

       //确定按键值

       structbutton_irq_desc *button_irqs=(struct button_irq_desc *)dev_id;

       intnumber;

       intdown;

       unsignedtmp;

      

              number = button_irqs->number;

       //检查哪个键按下

       switch(number){

       case0: case 1: case 2: case 3: case 4: case 5:

              tmp = readl(S3C64XX_GPNDAT);

              down = !(tmp & (1<<number));

              break;

       case6: case 7:

              tmp = readl(S3C64XX_GPLDAT);

              down = !(tmp & (1 << (number +5)));

              break;

       default:

              down = 0;

       }

       printk("number=%d\n",number);

       printk("down=%d\n",down);

       /*按下down=1*/

        if (down) {

 

              keyValue=10+number;

              printk("key %d down,key value= %d\n",number,keyValue);

             

           }else{//松开

              keyValue=number;

              printk("key %d up,keyvalue = %d\n",number,keyValue);

       }

       ev_press= 1;

       wake_up_interruptible(&button_waitq);

       //发送通知

       kill_fasync(&button_async,SIGIO, POLL_IN);

       returnIRQ_RETVAL(IRQ_HANDLED);

}

 

static int seventh_button_dev_open(structinode *inode, struct file *file)

{

 

       //if(!atomic_dec_and_test(&canOpen)){

       //     atomic_inc(&canOpen);

       //     return -EBUSY;    

       //}

 

       if(file->f_flags& O_NONBLOCK){ //非阻塞方式

              if(down_trylock(&button_lock)){

                     return-EBUSY;

              }

       }else{

             //获取信号量

               down(&button_lock);  

       }

      

 

       printk("seventh_button_dev_open!\n");

       //采用中断的方式

       //注册中断处理函数    

       inti;

       interr=0;

       for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++){

              if(button_irqs[i].irq<0){

                     continue;

              }                  

              err=request_irq(button_irqs[i].irq,buttons_irq,IRQ_TYPE_EDGE_BOTH,button_irqs[i].name,(void*)&button_irqs[i]);

              if(err)

                     break;

       }

       return0;

}

 

int seventh_button_dev_close(struct inode*inode, struct file *file){

      

       //atomic_inc(&canOpen);     

      

 

       //注销中断处理程序

       inti;

           for (i = 0; i <sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {

              if (button_irqs[i].irq < 0) {

                         continue;

              }

              free_irq(button_irqs[i].irq, (void*)&button_irqs[i]);

     }    

 

       /*使用信号量 释放信号量*/

       up(&button_lock);

       return0;

}    

 

static ssize_tseventh_button_dev_read(struct file *file,const char __user *buf,size_tsize,loff_t * ppos)

{

       if(size!=1){

              return -EINVAL;

       }

       if(file->f_flags& O_NONBLOCK){//非阻塞

              //检查有没有按键发生

              if(!ev_press){

                     return-EAGAIN;

              }

       }else{

              //如果没有按键动作发生就休眠

               wait_event_interruptible(button_waitq,ev_press);

       }

      

       //如果有动作发生直接返回

       copy_to_user(buf,&keyValue,1);

       ev_press=0;

       return1;

}

 

static unsignedseventh_button_dev_poll(struct file *file, poll_table *wait)

{

       unsignedint mask=0;

       //不会产即休眠

       poll_wait(file,&button_waitq, wait);

       if(ev_press)

       {

              mask |= POLLIN | POLLWRNORM;

       }

       returnmask;

}

 

//在此确定要接收通知的应用

//应用程序设用此方法,将pid告诉驱动程序

static int seventh_button_dev_fasync(intfd, struct file *filp, int on)

{

       printk("seventh_button_dev_fasync!\n");

       returnfasync_helper(fd, filp, on, &button_async);

}

 

 

static struct file_operationsseventh_button_dev_fops = {

   .owner      =   THIS_MODULE,

   .open       =   seventh_button_dev_open,

   .release    =   seventh_button_dev_close,

   .read       =   seventh_button_dev_read,

   .poll =   seventh_button_dev_poll,

   .fasync     =   seventh_button_dev_fasync,

};

 

/*注册驱动程序*/

static int __initseventh_button_dev_init(void){

       /*major设备的主设备号,name是驱动程序的名称,fops默认的是file_operations结构*/

       //如果主设备号为0,系统会自动分配

       major=register_chrdev(0,DEVICE_NAME,&seventh_button_dev_fops);

       seventh_button_dev_class= class_create(THIS_MODULE,DEVICE_NAME);

       //创建设备节点

       device_create(seventh_button_dev_class,//

                            NULL,//

                            MKDEV(major,0),//

                            NULL,//                      

                            DEVICE_NAME);//

       //初始化信号量

       sema_init(&button_lock,1);

       return0;

}

 

static void __exitseventh_button_dev_exit(void){

       //删除设备节点

       device_destroy(seventh_button_dev_class,MKDEV(major,0));

       if(seventh_button_dev_class){

              class_destroy(seventh_button_dev_class);

       }

 

       /*major和name必须和注册时的值一致*/

       unregister_chrdev(major,DEVICE_NAME);

       return;

}

 

 

module_init(seventh_button_dev_init);

module_exit(seventh_button_dev_exit);

 

MODULE_AUTHOR("RETACN");

MODULE_DESCRIPTION("SEVENTH BUTTONdriver");

MODULE_LICENSE("GPL");

 

测试代码如下

#include <sys/types.h>

#include <sys/stat.h>

#include <stdio.h>

#include <fcntl.h>

#include <poll.h>

#include <signal.h>

#include <unistd.h>

 

int fd;

//信号处理函数

void my_signal_fun(int signum){

       unsignedchar key_val;

       read(fd,&key_val,1);

      printf("key_val:%d\n",key_val);

}

             

 

/*测试添加pull机制后的中断方式按键驱动*/

int main(int argc,char **argv){    

       intret;

       intOflags;

       //测试阻塞和非阻塞不使用信号

       //signal(SIGIO,my_signal_fun);

       //阻塞

       //fd=open("/dev/seventh_button_dev",O_RDWR);

       //非阻塞

       fd=open("/dev/seventh_button_dev",O_RDWR| O_NONBLOCK);  

       if(fd<0){

              printf("can not open!\n");

              return -1;

       }           

             

       //fcntl(fd,F_SETOWN, getpid());

       //Oflags= fcntl(fd, F_GETFL);   

       //fcntl(fd,F_SETFL, Oflags | FASYNC);

                           

       unsignedchar key_val;

       while(1){

              read(fd,&key_val,1);

             printf("key_val:%d\n",key_val);

              //sleep(1000);

       }

       return0;

}

 

驱动下载到开发板:

 

测试原子量:

后台运行测试程序:

[root@FriendlyARM/tmp]# ./seventh_button_test &

再次运行程序:

[root@FriendlyARM/tmp]# ./seventh_button_test &

Can not open!

 

 

 

测试信号量:

后台运行测试程序:[root@FriendlyARM /tmp]# ./seventh_button_test &

再次运行程序 [root@FriendlyARM /tmp]# ./seventh_button_test &

 

 1000 root     1504 S    ./seventh_button_test

     //睡眠状态,在第一个程序关闭,释放信号量后才会被唤醒

 1001 root     1504 D    ./seventh_button_test

 

关闭第一个测试程序:

[root@FriendlyARM/tmp]# kill -9 1000

[root@FriendlyARM/tmp]# seventh_button_dev_fasync!

seventh_button_dev_open!

seventh_button_dev_fasync!

 

[root@FriendlyARM/tmp]# ps

 1001 root     1504 S    ./seventh_button_test

 


0 0