Linux驱动之同步、互斥、阻塞的应用
同步、互斥、阻塞的概念:
同步:在并发程序设计中,各进程对公共变量的访问必须加以制约,这种制约称为同步。
互斥机制:访问共享资源的代码区叫做临界区,这里的共享资源可能被多个线程需要,但这些共享资源又不能被同时访问,因此临界区需要以某种互斥机制加以保护,以确保共享资源被互斥访问。
阻塞与非阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程,而是直接返回。
在按键驱动的例子中,如果有多个应用程序调用按键驱动的设备文件,这时候就要利用同步与互斥的概念对这个种情况进行处理:
1、利用原子变量标志来判断设备文件是否被打开,原子变量在操作的时候不能被打断,它是利用关闭中断的方式实现的,一旦关闭了中断,内核将不能对进程进行调度,这就保证了原子性。
直接修改驱动代码,先定义一个原子变量
static atomic_t open_flag = ATOMIC_INIT(); //定义原子变量open_flag 并初始化为1
接着修改打开文件的函数与关闭文件的函数,初始化时open_flag 为1,一旦打开函数被调用则会减1变为0。关闭函数被调用后会加1又变成1。
a、在sixth_drv_open 中利用atomic_dec_and_test函数判断是否已经被调用,如果返回值为0,说明已经被调用。调用atomic_inc函数,并且返回。
b、在sixth_drv_close中第调用atomic_inc。
static int sixth_drv_open (struct inode * inode, struct file * file)
{
int ret; if(atomic_dec_and_test(&open_flag)==)//自检后是否为0,不为0说明已经被人调用
{
atomic_inc(&open_flag);//原子变量+1
return -EBUSY;
} ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "s1", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 1\n");
return -;
}
ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "s2", (void * )& pins_desc[]);
if(ret)
{
printk("open failed 2\n");
return -;
}
ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "s3", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 3\n");
return -;
}
ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "s4", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 4\n");
return -;
} return ;
} static int sixth_drv_close(struct inode * inode, struct file * file)
{
atomic_inc(&open_flag);//原子变量+1 free_irq(IRQ_EINT0 ,(void * )&pins_desc[]); free_irq(IRQ_EINT2 ,(void * )& pins_desc[]); free_irq(IRQ_EINT11 ,(void * )&pins_desc[]); free_irq(IRQ_EINT19 ,(void * )&pins_desc[]); return ;
}
static DECLARE_MUTEX(button_lock); //定义互斥锁
接着更改按键驱动中打开文件的函数与关闭文件的函数:
a、在sixth_drv_open函数中如果文件打开方式非阻塞的,那么调用down_trylock函数获取信号量,此函数如果获取不到信号量,直接返回;如果打开文件的方式是阻塞的,那么调用down函数,如果获取不到信号量,则将进程休眠直到获取信号量为止。
b、在sixth_drv_close函数利用up函数直接释放掉信号量。
static int sixth_drv_open (struct inode * inode, struct file * file)
{
int ret; if(file->f_flags & O_NONBLOCK)//非阻塞方式
{
if(down_trylock(&button_lock))//获取信号量失败则返回
return -EBUSY;
}
else
down(&button_lock);//获得信号量 ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "s1", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 1\n");
return -;
}
ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "s2", (void * )& pins_desc[]);
if(ret)
{
printk("open failed 2\n");
return -;
}
ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "s3", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 3\n");
return -;
}
ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "s4", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 4\n");
return -;
} return ;
} static int sixth_drv_close(struct inode * inode, struct file * file)
{
up(&button_lock);//释放信号量 free_irq(IRQ_EINT0 ,(void * )&pins_desc[]); free_irq(IRQ_EINT2 ,(void * )& pins_desc[]); free_irq(IRQ_EINT11 ,(void * )&pins_desc[]); free_irq(IRQ_EINT19 ,(void * )&pins_desc[]); return ;
}
将完整的按键驱动的源代码贴出
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h> //含有iomap函数iounmap函数
#include <asm/uaccess.h>//含有copy_from_user函数
#include <linux/device.h>//含有类相关的处理函数
#include <asm/arch/regs-gpio.h>//含有S3C2410_GPF0等相关的
#include <linux/irq.h> //含有IRQ_HANDLED\IRQ_TYPE_EDGE_RISING
#include <asm-arm/irq.h> //含有IRQT_BOTHEDGE触发类型
#include <linux/interrupt.h> //含有request_irq、free_irq函数
#include <linux/poll.h>
#include <asm-generic/errno-base.h> //含有各种错误返回值
//#include <asm-arm\arch-s3c2410\irqs.h> static struct class *sixth_drv_class;//类
static struct class_device *sixth_drv_class_dev;//类下面的设备
static int sixthmajor; static unsigned long *gpfcon = NULL;
static unsigned long *gpfdat = NULL;
static unsigned long *gpgcon = NULL;
static unsigned long *gpgdat = NULL; struct fasync_struct *sixth_fasync; static unsigned int key_val; struct pin_desc
{
unsigned int pin;
unsigned int key_val;
}; static struct pin_desc pins_desc[] =
{
{S3C2410_GPF0,0x01},
{S3C2410_GPF2,0x02},
{S3C2410_GPG3,0x03},
{S3C2410_GPG11,0x04}
}; static unsigned int ev_press;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//注册一个等待队列button_waitq static atomic_t open_flag = ATOMIC_INIT(); //定义原子变量open_flag 并初始化为1 static DECLARE_MUTEX(button_lock); //定义互斥锁 /*
*0x01、0x02、0x03、0x04表示按键被按下
*/ /*
*0x81、0x82、0x83、0x84表示按键被松开
*/ /*
*利用dev_id的值为pins_desc来判断是哪一个按键被按下或松开
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
unsigned int pin_val;
struct pin_desc * pin_desc = (struct pin_desc *)dev_id;//取得哪个按键被按下的状态 pin_val = s3c2410_gpio_getpin(pin_desc->pin); if(pin_val) //按键松开
key_val = 0x80 | pin_desc->key_val;
else
key_val = pin_desc->key_val; wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
ev_press = ; kill_fasync(&sixth_fasync, SIGIO, POLL_IN);//发生信号给进程 return IRQ_HANDLED;
} static int sixth_drv_open (struct inode * inode, struct file * file)
{
int ret; // if(atomic_dec_and_test(&open_flag)==0)//自检后是否为0,不为0说明已经被人调用
// {
// atomic_inc(&open_flag);//原子变量+1
// return -EBUSY;
// }
if(file->f_flags & O_NONBLOCK)//非阻塞方式
{
if(down_trylock(&button_lock))//获取信号量失败则返回
return -EBUSY;
}
else
down(&button_lock);//获得信号量 ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "s1", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 1\n");
return -;
}
ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "s2", (void * )& pins_desc[]);
if(ret)
{
printk("open failed 2\n");
return -;
}
ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "s3", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 3\n");
return -;
}
ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "s4", (void * )&pins_desc[]);
if(ret)
{
printk("open failed 4\n");
return -;
} return ;
} static int sixth_drv_close(struct inode * inode, struct file * file)
{
// atomic_inc(&open_flag);//原子变量+1
up(&button_lock);//释放信号量 free_irq(IRQ_EINT0 ,(void * )&pins_desc[]); free_irq(IRQ_EINT2 ,(void * )& pins_desc[]); free_irq(IRQ_EINT11 ,(void * )&pins_desc[]); free_irq(IRQ_EINT19 ,(void * )&pins_desc[]); return ;
} static ssize_t sixth_drv_read(struct file * file, char __user * userbuf, size_t count, loff_t * off)
{
int ret; if(count != )
{
printk("read error\n");
return -;
} if(file->f_flags & O_NONBLOCK)//非阻塞方式
{
if(!ev_press)//判断是否有按键按下,如果没有直接返回
{
key_val = ;
copy_to_user(userbuf, &key_val, );
return -EBUSY;
}
}
else//如果没有按键动作,直接进入休眠
wait_event_interruptible(button_waitq, ev_press);//将当前进程放入等待队列button_waitq中 ret = copy_to_user(userbuf, &key_val, );
ev_press = ;//按键已经处理可以继续睡眠 if(ret)
{
printk("copy error\n");
return -;
} return ;
} static unsigned int sixth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int ret = ;
poll_wait(file, &button_waitq, wait);//将当前进程放到button_waitq列表 if(ev_press)
ret |=POLLIN;//说明有数据被取到了 return ret;
} static int sixth_drv_fasync(int fd, struct file * file, int on)
{
int err;
printk("fansync_helper\n");
err = fasync_helper(fd, file, on, &sixth_fasync);//初始化sixth_fasync
if (err < )
return err;
return ;
} static struct file_operations sixth_drv_ops =
{
.owner = THIS_MODULE,
.open = sixth_drv_open,
.read = sixth_drv_read,
.release = sixth_drv_close,
.poll = sixth_drv_poll,
.fasync = sixth_drv_fasync, }; static int sixth_drv_init(void)
{
sixthmajor = register_chrdev(, "buttons", &sixth_drv_ops);//注册驱动程序 if(sixthmajor < )
printk("failes 1 buttons_drv register\n"); sixth_drv_class = class_create(THIS_MODULE, "buttons");//创建类
if(sixth_drv_class < )
printk("failes 2 buttons_drv register\n");
sixth_drv_class_dev = class_device_create(sixth_drv_class, NULL, MKDEV(sixthmajor,), NULL,"buttons");//创建设备节点
if(sixth_drv_class_dev < )
printk("failes 3 buttons_drv register\n"); gpfcon = ioremap(0x56000050, );//重映射
gpfdat = gpfcon + ;
gpgcon = ioremap(0x56000060, );//重映射
gpgdat = gpgcon + ; printk("register buttons_drv\n");
return ;
} static void sixth_drv_exit(void)
{
unregister_chrdev(sixthmajor,"buttons"); class_device_unregister(sixth_drv_class_dev);
class_destroy(sixth_drv_class); iounmap(gpfcon);
iounmap(gpgcon); printk("unregister buttons_drv\n");
} module_init(sixth_drv_init);
module_exit(sixth_drv_exit); MODULE_LICENSE("GPL");
接着改写测试程序,测试加入阻塞方式打开文件,在fd = open(filename, O_RDWR|O_NONBLOCK)函数中加入O_NONBLOCK即可以按阻塞方式打开。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h> static int fd; //static void fifth_testsignal(int signum)
//{
// unsigned char key_val;
//
// printf("signal = %d\n",signum); // read(fd, &key_val, 1);
// printf("signumkey_val: 0x%x\n\n",key_val);
//} /*
*usage ./buttonstest
*/
int main(int argc, char **argv)
{
char* filename="dev/buttons";
int oflags,ret;
unsigned char key_val; fd = open(filename, O_RDWR|O_NONBLOCK);//打开dev/firstdrv设备文件,非阻塞方式打开
if (fd < )//小于0说明没有成功
{
printf("error, can't open %s\n", filename);
return ;
} if(argc !=)
{
printf("Usage : %s ",argv[]);
return ;
}
// signal(SIGIO, fifth_testsignal);//注册一个信号,函数为fifth_testsignal
//
// fcntl(fd, F_SETOWN, getpid()); // 告诉内核,发给谁
//
// oflags = fcntl(fd, F_GETFL); //取得当前的状态
//
// fcntl(fd, F_SETFL, oflags | FASYNC); // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct while()
{
ret = read(fd, &key_val, );
printf("ret = %d,key_val: 0x%x\n",ret,key_val);
sleep();
} return ;
}
Linux驱动之同步、互斥、阻塞的应用的更多相关文章
- 入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖
文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键, ...
- Linux之同步互斥阻塞20160703
主要介绍一下Linux下的互斥与阻塞方面的知识: 1. 原子操作 原子操作指的是在执行过程中不会被别的代码路径所中断的操作. 常用原子操作函数举例: atomic_t v = ATOMIC_INIT( ...
- 【线程】linux之多线程同步互斥技术
1.同步机制 线程同步机制主要有:互斥量/信号量/条件变量/读写锁等. 2.技术示例 创建2个计数线程A和B,每次计数加1,当为偶数时,A线程计数:当为奇数时,B线程计数. 源码: ...
- linux 进程间同步互斥
参考链接: https://www.oschina.net/code/snippet_237505_8646 http://www.cnblogs.com/xilentz/archive/2012/1 ...
- linux驱动编写之阻塞与非阻塞
一.概念 应用程序使用API接口,如open.read等来最终操作驱动,有两种结果--成功和失败.成功,很好处理,直接返回想要的结果:但是,失败,是继续等待,还是返回失败类型呢? 如果继续等待,将进 ...
- linux下的同步与互斥
linux下的同步与互斥 谈到linux的并发,必然涉及到线程之间的同步和互斥,linux主要为我们提供了几种实现线程间同步互斥的 机制,本文主要介绍互斥锁,条件变量和信号量.互斥锁和条件变量包含在p ...
- Linux并发与同步专题 (4) Mutex互斥量
关键词:mutex.MCS.OSQ. <Linux并发与同步专题 (1)原子操作和内存屏障> <Linux并发与同步专题 (2)spinlock> <Linux并发与同步 ...
- Linux同步互斥(Peterson算法,生产者消费者模型)
同步 两个或两个以上随时间变化的量在变化过程中保持一定的相对关系. 互斥 对一组并发进程,一次只有一个进程能够访问一个给定的资源或执行一个给定的功能. 互斥技术可以用于解决诸如资源争用之类的冲突,还可 ...
- Linux 驱动层实现阻塞和非阻塞
linux应用层的函数默认是阻塞型的,但是要想真正实现阻塞,还需要驱动的支持才行. 例:open().scanf().fgets().read().accept() 等 1.默认情形,驱动层不实现阻塞 ...
随机推荐
- Maven项目中在properties 中使用 ${} 来引用pom文件中的属性
比如在pom文件中定义了属性如下: <jdbc.host.global>127.0.0.1</jdbc.host.global> <jdbc.databasename.g ...
- python 常用的模块
面试的过程中经常被问到使用过那些python模块,然后我大脑就出现了一片空白各种模块一顿说,其实一点顺序也没有然后给面试官造成的印象就是自己是否真实的用到这些模块,所以总结下自己实际工作中常用的模块: ...
- Docker入门与实战讲解
转载自:http://blog.csdn.net/relax_hb/article/details/69668815 简述 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包 ...
- erlang遍历目录
{ok, Cwd} = file:get_cwd(). Filelist = filelib:fold_files( Cwd, ".*", true, fun(File, Acc) ...
- Android之微信布局篇
一.准备工作: 1. 下载好相关的图片: 2.创建一个名WeiChat的项目,将图片复制到res----->drawable-hdpi目录下. 二.编写代码: 1. 最终效果: 2.微信可划分为 ...
- CSS预处理语言
CSS预处理语言 Less,Sass,Stylus 安装 Less yarn add less 运行命令 ./node_modules/.bin/lessc 嵌套规则 Less.Sass嵌套规则一样 ...
- 502 Bad Gateway
状态码解释: 502 Bad Gateway:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应. 502 原因分析: 将请求提交给网关如php-fpm执行,但是由于某些原因没有 ...
- Mysql数据表去重
查询不重复元素个数 select count(distinct domain) from black_botnet_domian; 查询表中元素个数大于等于2的元素 SELECT goods_id,g ...
- JDK1.7 ConcurrentHashMap--解决高并发下的HashMap使用问题
高并发下也可以使用HashTable .Collections.synchronizedMap因为他们是线程安全的,但是却牺牲了性能,无论是读操作.写操作都是给整个集合加锁,导致同一时间内其他操作均为 ...
- python3中一句话定义函数
import math as marea=lambda r:r**2*m.pi #定义一个计算圆的面积的函数area(8) 显示结果 201.06192982974676