把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 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

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设备驱动的更多相关文章

  1. 支持阻塞操作和轮询操作的globalfifo设备驱动代码分析以及测试代码

    #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include ...

  2. 蜕变成蝶~Linux设备驱动中的阻塞和非阻塞I/O

    今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合 ...

  3. platform设备驱动全透析

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://21cnbao.blog.51cto.com/109393/337609 1.1 ...

  4. 《Linux设备驱动开发具体解释(第3版)》进展同步更新

    本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTE ...

  5. 《linux设备驱动开发详解》笔记——12linux设备驱动的软件架构思想

    本章重点讲解思想.思想.思想. 12.1 linux驱动的软件架构 下述三种思想,在linux的spi.iic.usb等复杂驱动里广泛使用.后面几节分别对这些思想进行详细说明. 思想1:驱动与设备分离 ...

  6. 七、设备驱动中的阻塞与非阻塞 IO(二)

    7.2 轮询 7.2.1 介绍 在用户程序中的 select() 和 poll() 函数最终会使设备驱动中的 poll() 函数被执行. 设备驱动程序中的轮询函数原型: /** 用于询问设备是否可以非 ...

  7. 七、设备驱动中的阻塞与非阻塞 IO(一)

    7.1 阻塞与非阻塞 IO 阻塞操作是指在执行设备操作的时候,若不能获取资源,则挂起进程直到满足可操作的条件后再进行操作.被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足. 非 ...

  8. Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...

  9. 《连载 | 物联网框架ServerSuperIO教程》- 3.设备驱动介绍

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

随机推荐

  1. Bootstrap简易使用指南

    1.框架 1.1全局样式 使用HTML5的doctype,scaffolding.less中定义全局样式,从2开始使用normalize.css,并使用reset.less进行简化 1.2默认栅格系统 ...

  2. WPF 显示模态窗口和窗体

    <WPF编程宝典——使用C# 2008和.NET 3.5(第2版)>第25章与Windows窗体的互操作,本章将介绍用于集成Windows窗体和WPF内容的不同策略.还将分析如何在应用程序 ...

  3. hadoop2.4.0 安装配置 (2)

    hdfs-site.xml 配置如下: <?xml version="1.0" encoding="UTF-8"?> <?xml-styles ...

  4. SHELL学习笔记----IF条件判断,判断条件

    SHELL学习笔记----IF条件判断,判断条件 前言: 无论什么编程语言都离不开条件判断.SHELL也不例外.  if list then           do something here   ...

  5. MySQL注入中load_file()函数的应用

    常用的一些Load_File()函数攻击手法:

  6. XSS高级实战教程

    1.[yueyan科普系列]XSS跨站脚本攻击--yueyan 2.存储型XSS的成因及挖掘方法--pkav 3.跨站脚本攻击实例解析--泉哥 4.XSS高级实战教程--心伤的瘦子 5.XSS利用与挖 ...

  7. jsp接收相同复合参数 request.getParameterValues()用法

    在网站中往往需要用户选择复选框,此时需要创建多个复选框让用户进行选择: <head> <meta http-equiv="Content-Type" conten ...

  8. javaweb学习总结(三十七)——获得MySQL数据库自动生成的主键

    测试脚本如下: 1 create table test1 2 ( 3 id int primary key auto_increment, 4 name varchar(20) 5 ); 测试代码: ...

  9. spring声明式事务 同一类内方法调用事务失效

    只要避开Spring目前的AOP实现上的限制,要么都声明要事务,要么分开成两个类,要么直接在方法里使用编程式事务 [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring ...

  10. IEHelper - Internet Explorer Helper Class

    http://www.codeproject.com/Articles/4411/IEHelper-Internet-Explorer-Helper-Class Discussions (81) IE ...