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.默认情形,驱动层不实现阻塞 ...
随机推荐
- Fabric的settings用法
http://fabric-chs.readthedocs.io/zh_CN/chs/api/core/context_managers.html?highlight=with%20settings# ...
- MQTT研究之mosquitto:【环境搭建】
环境信息: 1. Linux Centos7.2 环境,CPU 2核,内存8G. 2. mosquitto版本:mosquitto-1.5.4 官网:http://mosquitto.org/down ...
- 域名到站点的负载均衡技术一览(主要是探讨一台Nginx抵御大并发的解决方案)(转)https://www.cnblogs.com/EasonJim/p/7823410.html
一.问题域 Nginx.LVS.Keepalived.F5.DNS轮询,往往讨论的是接入层的这样几个问题: 1)可用性:任何一台机器挂了,服务受不受影响 2)扩展性:能否通过增加机器,扩充系统的性能 ...
- oData 排序字段生成
跟踪SQL 发现生成的SQL中所有的字段都进行了排序,查看OData原代码,发现如果实体有Key,就按照Key asc 加上指定字段进行排序 属性 EnsureStableOrdering可以控制是否 ...
- AI与RPA
RPA(机器人流程自动化)是一类自动化软件工具,它可以通过用户界面使用和理解企业已有的应用,将基于规则的常规操作自动化,例如读取邮件和系统,计算,生成文件和报告,检查文件等.因此,RPA的应用范围非常 ...
- LSTM/RNN中的Attention机制
一.解决的问题 采用传统编码器-解码器结构的LSTM/RNN模型存在一个问题,不论输入长短都将其编码成一个固定长度的向量表示,这使模型对于长输入序列的学习效果很差(解码效果很差). 注意下图中,ax ...
- js类的继承,es5和es6的方法
存在的差异:1. 私有数据继承差异 es5:执行父级构造函数并且将this指向子级 es6:在构造函数内部执行super方法,系统会自动执行父级,并将this指向子级2. 共有数据(原型链方法)继承的 ...
- VS2015密匙--VS2015打开丢失msvcp140.dll--cannot find one or more components ,please reinstall the application
win7旗舰版 64位 + vs2015 专业版 1.安装VS2015过程中可能需要用到的VS2015专业版钥匙:(测试,可用) HMGNV-WCYXV-X7G9W-YCX63-B98R2 2.VS2 ...
- python3 列表去除重复项保留原序
l1 = ['a',1,'c','b',2,'b','c','d','a'] l2= sorted(set(l1),key=l1.index) print('l2:',l2) print('l1:', ...
- kudu导入文件(基于impala)
kudu是cloudera开源的运行在hadoop平台上的列式存储系统,拥有Hadoop生态系统应用的常见技术特性,运行在一般的商用硬件上,支持水平扩展,高可用,集成impala后,支持标准sql语句 ...