=========驱动程序中的同步互斥 阻塞 =================================
同一时间只能有一个 应用打开驱动程序
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