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

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

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

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"},

};

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

staticvolatile 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 root1504 S ./seventh_button_test

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

1001 root1504 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 root1504 S ./seventh_button_test


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值