globalfifo设备驱动
把globalmem中的全局内存变成一个FIFO,只有当FIFO中有数据的时候(即有进程把数据写到这个FIFO而且没有被读进程读空),读进程才能把数据读出,而且读取后的数据会从globalmem的全局内存中被拿掉;只有当fifo非满时(即还有一些空间未被写,或写满后被读进程读出了数据),写进程才能写入数据。现在将globalmem重命名为“globalfifo",在globalfifo中,读fifo将唤醒写fifo,写fifo也将唤醒读fifo.
(一) 支持阻塞操作的globalfifo设备驱动
在globalfifo设备结构体上需要添加两个等待队列头,分别对应于读和写。
struct globalfifo_dev
{
struct cdev cdev; //cdev结构体
unsigned int current_len; //fifo有效数据长度
unsigned char mem[GLOBALFIFO_SIZE]; //全局内存
struct semaphore sem; //并发控制用的信号量
wait_queue_head_t r_wait; //阻塞读用的等待队列头
wait_queue_head_t w_wait; //阻塞写用的等待dui lie
};
等待队列需要在设备驱动模块加载函数中调用init_waitqueue_head()被初始化。
int gloaalfifo_init(void)
{
int ret;
dev_t devno=MKDEV(globalfifo_major,0);
//申请设备号
if(globalfifo_major)
ret=register_chrdev_region(devno,1,"globalfifo");
else
{ //动态申请设备号
ret=alloc_chrdev_region(&devno,0,1,"globalfifo");
globalfifo_major=MAJOR(devno);
}
if(ret<0)
return ret;
//动态申请设备结构体的内存
globalfifo_devp=kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL);
if(!globalfifo_devp)
{
ret=-ENOMEM;
goto fail_malloc;
}
memset(globalfifo_devp,0,sizeof(struct_globalfifo_dev));
globalfifo_setup_cdev(globalfifo_devp,0);
init_MUTEX(&globalfifo_devp->sem); //初始化信号量
init_waitqueue_head(&globalfifo_devp->r_wait); //初始化读等待队列
init_waitqueue_head(&globalfifo_devp->w_wait); //初始化写等待队列
return 0;
fail_malloc:unregister_chrdev_region(devno,1);
return ret;
}
设备驱动读函数中应该增加等待globalfifo_devp->w_wait被唤醒的语句;而在写操作中唤醒globalfifo_devp->r_wait.
static ssize_t globalfifo_read(struct file *filp,char_user *buf,size_t count,loff_t *ppos)
{
int ret;
struct globalfifo_dev *dev=filp->private_data; //获得设备结构体指针
DECLARE_WAITQUEUE(wait,current); //定义等待队列
down(&dev->sem); //获得信号量
add_wait_queue(&dev->r_wait,&wait); //进入读等待队列头
//等待fifo非空
while(dev->current_len==0)
{
if(filp->f_flags &o_nonblock)
{
ret=-EAGAIN;
goto out;
}
_set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠
up(&dev->sem);
schedule(); //调度其他进程执行
if(signal_pending(current)) //如果是因为信号唤醒
{
ret=-ERESTARTSYS;
goto out2;
}
down(&dev->sem);
}
//拷贝到用户空间
if(count > dev->current_len)
count=dev->current_len;
if(copy_to_user(buf,dev->mem,count)
{
ret=-EFAULT;
goto out;
}
else
{
memcpy(dev->mem,dev->mem+count,dev->current_len-count); //fifo数据前移
dev->current_len-=count; //有效数据长度减少。
printk(KERN_INFO "read %d bytes(s),current_len:%d\n",count,dev->current_len);
wake_up_interruptible(&dev->w_wait); //唤醒写等待队列
ret=count;
}
out:up(&dev->sem); //释放信号量
out2:remove_wait_queue(&dev->w_wait,&wait); //移除等待队列
set_current_state(TASK_RUNNING);
return ret;
}
//globalfifo写操作
static ssize_t globalfifo_write(struct file *filp,const char _ _user *buf,size_t count, loff_t *ppos)
{
struct globalfifo_dev *dev=filp->private_data; //获得设备结构体指针
int ret;
DECLARE_WAITQUEUE(&dev->w_wait,&wait); //进入写等待队列
down(&dev->sem); //获得信号量
add_wait_queue(&dev->w_wait,&wait); //进入写等待队列头
//等待fifo非满
while(dev->current_len=GLOBALFIFO_SIZE)
{
if(filp->f_flags &O_NONBLOCK )
{
//如果是非阻塞访问
ret=-EAGAIN;
goto out;
}
_set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠
up(&dev->sem);
schedule(); //调度其它进程执行
if(signal_pending(current))
{
//如果是因为信号唤醒
ret=-ERESTARTSYS;
goto out2;
}
down(&dev->sem);
}
if(count>GLOBALFIFO_SIZE-dev->current_len)
count=GLOBALFIFO_SIZE-dev->current_len;
if(copy_from_user(dev->mem+dev->current_len,buf,count))
{
ret=-EFAULT;
goto out;
}
else
{
dev->current_len+=count;
printk(KERNEL_INFO"written %d byte(S),current_len:%d\n",count,dev->current_len);
wake_up_interruptible(&dev->r_wait); //唤醒读等待队列
ret=count;
}
out:up(&dev->sem);
out2:remove_wait_queue(&dev->w_wait,&wait);
set_current_state(TASK_RUNNING);
return ret;
}
(二)支持轮询操作的globalfifo驱动
在global的poll()函数中,首先将设备结构体中的r_wait和w_wait等待队列添加到等待列表,然后通过判断dev->current_len是否等于0来获得设备的可读状态,通过判断dev->current_len是否等于GLOBALFIFO_SIZE来获得设备的可写状态。
global设备驱动的poll()函数
static unsigned int globalfifo_poll(struct file *filp,poll_table *wait)
{
unsigned int mask=0;
struct globalfifo_dev *dev=filp->private_data; //获得设备结构体指针
down(&dev-sem); //获得信号量
poll_wait(filp,&dev->r_wait,wait);
poll_wait(filp,&dev->w_wait,wait);
//fifo非空
if(dev->current_len!=0)
mask|=POLLIN|POLLRDNORM; //标示数据可获得
if(dev->current_len!=GLOBALFIFO_SIZE)
mask|=POLLOUT|POLLWRNORM; //标示数据可写入
up(&dev->sem); //释放信号量
return mask;
}
注意要把globalfifo_poll赋给globalfifo_fops的poll成员。
(三) 支持异步通知的globalfifo设备驱动
首先应该将异步通知的结构体指针加入到globalfifo设备的结构体里面去。
{
struct cdev cdev; //cdev结构体
unsigned int current_len; //fifo有效数据长度
unsigned char mem[GLOBALFIFO_SIZE]; //全局内存
struct semaphore sem; //并发控制用的信号量
wait_queue_head_t r_wait; //阻塞读用的等待队列头
wait_queue_head_t w_wait; //阻塞写用的等待dui lie
struct fasync_struct *async_queue; //异步结构体指针,用于读
};
支持异步通知的globalfifo设备驱动fasync()函数
static int globalfifo_fasync(int fd,struct file *filp,int mode)
{
struct globalfifo_dev *dev=filp->private_data;
return fasync_helper(fd,filp,mode,&dev->async_queue);
}
支持异步通知的globalfifo设备驱动写函数
static ssize_t globalfifo_write(struct file *filp,const char _user *buf,size_t count,loff_t *ppos)
{
struct globalfifo_dev *dev=filp->private_data; //获得设备结构体指针
int ret;
DECLARE_WAITQUEUE(wait,current); //定义等待队列
down(&dev->sem); //取得信号量
add_wait_queue(&dev->w_wait,&wait); //进入写等待队列
//等待fifo未满
if(dev->current_len==GLOBALFIFO_SIZE)
{
if(filp->f_flags&O_NOBLOCK)
{ //如果是非阻塞访问
ret=-EAGAIN;
goto out;
}
_set_current_state(TASK_INTERRUPTIBLE) //改变进程状态为睡眠
up(&dev->sem);
schedule();
if(signal_pending(current)
ret=-ERESTARTSYS;
goto out2;
}
down(&dev->sem);
}
//从用户空间拷贝到内核空间
if(count >GLOBALFIFO_SIZE- dev->current_len)
count=GLOBALFIFO_SIZE-dev->current_len;
if(copy_form_user(dev->mem+dev->current_len,buf,count))
{
ret=-EFAULT;
goto out;
}
else
{
dev->current_len+=count;
wake_up_interruptible(&dev->r-wait); //唤醒读等待队列
}
//产生异步读信号
if(dev->async_queue)
kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
ret=count;
}
out:up(&dev->sem);
out2:remove_wait_queue(&dev->w_wait,&wait);
set_current_state(TASK_RUNNING);
return ret;
}
最后记得在release()函数里用globalfifo_fasync()函数将文件从异步通知列表中删除
int globalfifo_release(struct inode *inode,struct file *filp)
{
globalfifo_fasync(-1,filp,0);
return 0;
}
globalfifo设备驱动的更多相关文章
- 支持阻塞操作和轮询操作的globalfifo设备驱动代码分析以及测试代码
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include ...
- 蜕变成蝶~Linux设备驱动中的阻塞和非阻塞I/O
今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合 ...
- platform设备驱动全透析
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://21cnbao.blog.51cto.com/109393/337609 1.1 ...
- 《Linux设备驱动开发具体解释(第3版)》进展同步更新
本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTE ...
- 《linux设备驱动开发详解》笔记——12linux设备驱动的软件架构思想
本章重点讲解思想.思想.思想. 12.1 linux驱动的软件架构 下述三种思想,在linux的spi.iic.usb等复杂驱动里广泛使用.后面几节分别对这些思想进行详细说明. 思想1:驱动与设备分离 ...
- 七、设备驱动中的阻塞与非阻塞 IO(二)
7.2 轮询 7.2.1 介绍 在用户程序中的 select() 和 poll() 函数最终会使设备驱动中的 poll() 函数被执行. 设备驱动程序中的轮询函数原型: /** 用于询问设备是否可以非 ...
- 七、设备驱动中的阻塞与非阻塞 IO(一)
7.1 阻塞与非阻塞 IO 阻塞操作是指在执行设备操作的时候,若不能获取资源,则挂起进程直到满足可操作的条件后再进行操作.被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足. 非 ...
- Linux字符设备驱动框架
字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...
- 《连载 | 物联网框架ServerSuperIO教程》- 3.设备驱动介绍
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
随机推荐
- ECshop网店系统百万级商品量性能优化-加快首页访问速度
如果ECshop的商品数达到几万,十几万的时候,如果首页没有缓存,第一次访问的时候,你会发现其慢无比,原因就是清空了Cache后或者没有Cache的情况下,ECshop会Bulid一些Cache数据, ...
- javascript 通用loading动画效果
由于项目中多处要给ajax提交的时候增加等待动画效果,所以就写了一个简单的通用js方法: 代码如下: /*ajax提交的延时等待效果*/ var AjaxLoding = new Object(); ...
- java创建多线程(转载)
转载自:Java创建线程的两个方法 Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对 ...
- cocos2dx输出信息重定向到控制台
重定向输出到控制台,方便调试,代码: // uncomment below line, open debug console #define USE_WIN32_CONSOLE int APIENTR ...
- nutch 生产者队列的大小如何控制 threadcount * 50
如果topN 设置为1000万 ,不会这1000万都放到QueueFeeder(内存)中,而是从文件系统中(hdfs)中迭代不断填充QueueFeeder.队列中默认存放 threadcount * ...
- 对C语言中sizeof细节的三点分析
转自对C语言中sizeof细节的三点分析 1.sizeof是运算符,跟加减乘除的性质其实是一样的,在编译的时候进行执行,而不是在运行时才执行. 那么如果编程中验证这一点呢?ps:这是前两天朋友淘宝面试 ...
- MyBatis动态SQL语法
[注:摘自MyBatis官网 ] 1.动态SQL的元素: if choose (when, otherwise) trim (where, set) foreach bind 2.if语句: &l ...
- java中获得jar包执行路径的方法
当我们由于某种需要需要的得到jar的路径是可以用下面的方式来获得: basePath = new Solution().getClass().getProtectionDomain().getCode ...
- ANDROID_MARS学习笔记_S01原始版_018_SERVICE之Parcel
一.代码 1.xml(1)activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk ...
- ruby 线程学习
i=1 Thread.start{ while true print "Thread 1 \n" i+=1 if i==5 then Thread.kill Thread.curr ...