Linux驱动总结3- unlocked_ioctl和堵塞(waitqueue)读写函数的实现 【转】
转自:http://blog.chinaunix.net/uid-20937170-id-3033633.html
学习了驱动程序的设计,感觉在学习驱动的同时学习linux内核,也是很不错的过程哦,做了几个实验,该做一些总结,只有不停的作总结才能印象深刻。
- //头文件
- #include
- /*定义一系列的命令*/
- /*幻数,主要用于表示类型*/
- #define MAGIC_NUM 'k'
- /*打印命令*/
- #define MEMDEV_PRINTF _IO(MAGIC_NUM,1)
- /*从设备读一个int数据*/
- #define MEMDEV_READ _IOR(MAGIC_NUM,2,int)
- /*往设备写一个int数据*/
- #define MEMDEV_WRITE _IOW(MAGIC_NUM,3,int)
- /*最大的序列号*/
- #define MEM_MAX_CMD 3
- /*确定命令的方向*/
- _IOC_DIR(nr)
- /*确定命令的类型*/
- _IOC_TYPE(nr)
- /*确定命令的序号*/
- _IOC_NR(nr)
- /*确定命令的大小*/
- _IOC_SIZE(nr)
- /*检查类型,幻数是否正确*/
- if(_IOC_TYPE(cmd)!=MAGIC_NUM)
- return -EINVAL;
- /*检测命令序号是否大于允许的最大序号*/
- if(_IOC_NR(cmd)> MEM_MAX_CMD)
- return -EINVAL;
- if(_IOC_DIR(cmd) & _IOC_READ)
- err = !access_ok(VERIFY_WRITE,(void *)args,_IOC_SIZE(cmd));
- else if(_IOC_DIR(cmd) & _IOC_WRITE)
- err = !access_ok(VERIFY_READ,(void *)args,_IOC_SIZE(cmd));
- if(err)/*返回错误*/
- return -EFAULT;
- /*根据命令执行相应的操作*/
- switch(cmd)
- {
- case MEMDEV_PRINTF:
- printk("<--------CMD MEMDEV_PRINTF Done------------>\n\n");
- ...
- break;
- case MEMDEV_READ:
- ioarg = &mem_devp->data;
- ...
- ret = __put_user(ioarg,(int *)args);
- ioarg = 0;
- ...
- break;
- case MEMDEV_WRITE:
- ...
- ret = __get_user(ioarg,(int *)args);
- printk("<--------CMD MEMDEV_WRITE Done ioarg = %d--------->\n\n",ioarg);
- ioarg = 0;
- ...
- break;
- default:
- ret = -EINVAL;
- printk("<-------INVAL CMD--------->\n\n");
- break;
- }
- /*添加该模块的基本文件操作支持*/
- static const struct file_operations mem_fops =
- {
- /*结尾不是分号,注意其中的差别*/
- .owner = THIS_MODULE,
- .llseek = mem_llseek,
- .read = mem_read,
- .write = mem_write,
- .open = mem_open,
- .release = mem_release,
- /*添加新的操作支持*/
- .unlocked_ioctl = mem_ioctl,
- };
- wait_event和wait_event_interruptible的实现都是采用宏的方式,都是一个重新调度的过程,如下所示:
- #define wait_event_interruptible(wq, condition) \
- ({ \
- int __ret = 0; \
- if (!(condition)) \
- __wait_event_interruptible(wq, condition, __ret); \
- __ret; \
- })
- #define __wait_event_interruptible(wq, condition, ret) \
- do { \
- /*此处存在一个声明等待队列的语句,因此不需要再重新定义一个等待队列节点*/
- DEFINE_WAIT(__wait); \
- \
- for (;;) { \
- /*此处就相当于add_wait_queue()操作,具体参看代码如下所示*/
- prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
- if (condition) \
- break; \
- if (!signal_pending(current)) { \
- /*此处是调度,丢失CPU,因此需要wake_up函数唤醒当前的进程
- 根据定义可知,如果条件不满足,进程就失去CPU,能够跳出for循环的出口只有
- 1、当条件满足时2、当signal_pending(current)=1时。
- 1、就是满足条件,也就是说wake_up函数只是退出了schedule函数,
- 而真正退出函数还需要满足条件
- 2、说明进程可以被信号唤醒。也就是信号可能导致没有满足条件时就唤醒当前的进程。
- 这也是后面的代码采用while判断的原因.防止被信号唤醒。
- */
- schedule(); \
- continue; \
- } \
- ret = -ERESTARTSYS; \
- break; \
- } \
- finish_wait(&wq, &__wait); \
- } while (0)
- void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
- {
- unsigned long flags;
- wait->flags &= ~WQ_FLAG_EXCLUSIVE;
- spin_lock_irqsave(&q->lock, flags);
- if (list_empty(&wait->task_list))
- /*添加节点到等待队列*/
- __add_wait_queue(q, wait);
- set_current_state(state);
- spin_unlock_irqrestore(&q->lock, flags);
- }
- 唤醒的操作也是类似的。
- #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
- #include<linux/wait.h>
- struct mem_dev
- {
- char *data;
- unsigned long size;
- /*添加一个并行机制*/
- spinlock_t lock;
- /*添加一个等待队列t头*/
- wait_queue_head_t rdqueue;
- wait_queue_head_t wrqueue;
- };
- /*初始化函数*/
- static int memdev_init(void)
- {
- ....
- for(i = 0; i < MEMDEV_NR_DEVS; i)
- {
- mem_devp[i].size = MEMDEV_SIZE;
- /*对设备的数据空间分配空间*/
- mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
- /*问题,没有进行错误的控制*/
- memset(mem_devp[i].data,0,MEMDEV_SIZE);
- /*初始化定义的互信息量*/
- //初始化定义的自旋锁ua
- spin_lock_init(&(mem_devp[i].lock));
- /*初始化两个等待队列头,需要注意必须用括号包含起来,使得优先级正确*/
- init_waitqueue_head(&(mem_devp[i].rdqueue));
- init_waitqueue_head(&(mem_devp[i].wrqueue));
- }
- ...
- }
- /*read函数的实现*/
- static ssize_t mem_read(struct file *filp,char __user *buf, size_t size,loff_t *ppos)
- {
- unsigned long p = *ppos;
- unsigned int count = size;
- int ret = 0;
- struct mem_dev *dev = filp->private_data;
- /*参数的检查,首先判断文件位置*/
- if(p >= MEMDEV_SIZE)
- return 0;
- /*改正文件大小*/
- if(count > MEMDEV_SIZE - p)
- count = MEMDEV_SIZE - p;
- /*添加一个等待队列节点到当前进程中*/
- DECLARE_WAITQUEUE(wait_r,current);
- /*将节点添加到等待队列中*/
- add_wait_queue(&dev->rdqueue,&wait_r);
- /*添加等待队列,本来采用if即可,但是由于信号等可能导致等待队列的唤醒,因此采用循环,确保不会出现误判*/
- #endif
- while(!havedata)
- {
- /*判断用户是否设置为非堵塞模式读,告诉用户再读*/
- if(filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- /*依据条件havedata判断队列的状态,防止进程被信号唤醒*/
- wait_event_interruptible(dev->rdqueue,havedata);
- }
- spin_lock(&dev->lock);
- /*从内核读数据到用户空间,实质就通过private_data访问设备*/
- if(copy_to_user(buf,(void *)(dev->data p),count))
- {
- /*出错误*/
- ret = -EFAULT;
- }
- else
- {
- /*移动当前文件光标的位置*/
- *ppos = count;
- ret = count;
- printk(KERN_INFO "read %d bytes(s) from %d\n",count,p);
- }
- spin_unlock(&dev->lock);
- /*将等待队列节点从读等待队列中移除*/
- remove_wait_queue(&dev->rdqueue,&wait_r);
- #endif
- /*更新条件havedate*/
- havedata = false;
- /*唤醒写等待队列*/
- wake_up_interruptible(&dev->wrqueue);
- return ret;
- }
- /*write函数的实现*/
- static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
- {
- unsigned long p = *ppos;
- unsigned int count = size;
- int ret = 0;
- /*获得设备结构体的指针*/
- struct mem_dev *dev = filp->private_data;
- /*检查参数的长度*/
- if(p >= MEMDEV_SIZE)
- return 0;
- if(count > MEMDEV_SIZE - p)
- count = MEMDEV_SIZE - p;
- /*定义并初始化一个等待队列节点,添加到当前进程中*/
- DECLARE_WAITQUEUE(wait_w,current);
- /*将等待队列节点添加到等待队列中*/
- add_wait_queue(&dev->wrqueue,&wait_w);
- #endif
- /*添加写堵塞判断*/
- /*为何采用循环是为了防止信号等其他原因导致唤醒*/
- while(havedata)
- {
- /*如果是以非堵塞方式*/
- if(filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- /*分析源码发现,wait_event_interruptible 中存在DECLARE_WAITQUEUE和add_wait_queue的操作,因此不需要手动添加等待队列节点*/
- wait_event_interruptible(&dev->wrqueue,(!havedata));
- }
- spin_lock(&dev->lock);
- if(copy_from_user(dev->data p,buf,count))
- ret = -EFAULT;
- else
- {
- /*改变文件位置*/
- *ppos = count;
- ret = count;
- printk(KERN_INFO "writted %d bytes(s) from %d\n",count,p);
- }
- spin_unlock(&dev->lock);
- #if 0
- /*将该等待节点移除*/
- remove_wait_queue(&dev->wrqueue,&wait_w);
- #endif
- /*更新条件*/
- havedata = true;
- /*唤醒读等待队列*/
- wake_up_interruptible(&dev->rdqueue);
- return ret;
- }
Linux驱动总结3- unlocked_ioctl和堵塞(waitqueue)读写函数的实现 【转】的更多相关文章
- Linux代码的重用与强行卸载Linux驱动
(一)Linux代码的重用 重用=静态重用(将要重用的代码放到其他的文件的头文件中声明)+动态重用(使用另外一个Linux驱动中的资源,例如函数.变量.宏等) 1.编译是由多个文件组成的Linux驱动 ...
- Linux 驱动学习笔记05--字符驱动实例,实现一个共享内存设备的驱动
断断续续学驱动,好不容易有空,做了段字符驱动的例子.主要还是跟书上学习在此记录下来,以后说不定能回过头来温故知新. 首先上驱动源码 gmem.c: /************************* ...
- 嵌入式Linux驱动开发日记
嵌入式Linux驱动开发日记 主机硬件环境 开发机:虚拟机Ubuntu12.04 内存: 1G 硬盘:80GB 目标板硬件环境 CPU: SP5V210 (开发板:QT210) SDRAM: 512M ...
- linux驱动初探之杂项设备(控制两个GPIO口)
关键字:linux驱动.杂项设备.GPIO 此驱动程序控制了外接的两个二极管,二极管是低电平有效. 上一篇博客中已经介绍了linux驱动程序的编写流程,这篇博客算是前一篇的提高篇,也是下一篇博客(JN ...
- linux驱动初探之字符驱动
关键字:字符驱动.动态生成设备节点.helloworld linux驱动编程,个人觉得第一件事就是配置好平台文件,这里以字符设备,也就是传说中的helloworld为例~ 此驱动程序基于linux3. ...
- 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)
这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- 嵌入式Linux驱动笔记(十八)------浅析V4L2框架之ioctl【转】
转自:https://blog.csdn.net/Guet_Kite/article/details/78574781 权声明:本文为 风筝 博主原创文章,未经博主允许不得转载!!!!!!谢谢合作 h ...
- linux驱动---等待队列、工作队列、Tasklets【转】
转自:https://blog.csdn.net/ezimu/article/details/54851148 概述: 等待队列.工作队列.Tasklet都是linux驱动很重要的API,下面主要从用 ...
随机推荐
- 自学Zabbix3.10.2.1 linux如何配置使用sendEmail发送邮件
点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 自学Zabbix3.10.2.1 linux如何配置使用sendEmail发送邮件 sendEma ...
- [Cqoi2014]数三角形——组合数
Description: 给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个.下图为4x4的网格上的一个三角形. 注意三角形的三点不能共线. Hint: 1<=m,n<=1000 ...
- des加密解密JAVA与.NET互通实例
JAVA版本 import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFacto ...
- caffe 错误
一些caffe错误 训练时很快梯度爆炸,loss猛增至nan 如果找不到数据上的原因的话,可以怀疑caffe框架有问题,换用其它版本试试.比如我遇到的问题是在训练时使用了Accuracy层,而该层的实 ...
- python 正则括号的使用及踩坑
直接先上结论: 若匹配规则里有1个括号------返回的是括号所匹配到的结果, 若匹配规则里有多个括号------返回多个括号分别匹配到的结果, 若匹配规则里没有括号------就返回整条语句所匹配到 ...
- CodeForces-915C Permute Digits
C. Permute Digits time limit per test 1 second memory limit per test 256 megabytes input standard in ...
- CircleList-使用UGUI实现的圆形列表
CircleList CircleList是一个通过UGUI实现的圆形列表,通过缩放.平移和层级的改变模拟一个3D的圆形列表. 效果 添加与旋转 间距调整 椭圆形的旋转 参数 CenterX: 椭圆圆 ...
- java类的回顾
1.类是某一批对象的抽象,对象才是一个具体的存在的实体,你我他都是人的实例,而不是人的类.2.类可以包含三种最常见的成员:构造器,成员变量,方法3.4.java世界里,属性,如某个类具有age属性,通 ...
- 03-Windows Server 2016 IIS的安装与配置
1. 打开服务器管理器,点击[添加角色和功能选项]. 2. 进入“添加角色和功能向导”页面,点击下一步. 3. 安装类型选择[基于角色或基于功能的安装],点击下一步. 4. 进入服务器选 ...
- Prezento – 轻量、简单的 jQuery 幻灯片插件
Prezento 是一个超级简单的 jQuery 幻灯片插件.可以让你网页以新颖的交互方式呈现.另外,Prezento 支持响应式设计,配置项也很灵活,可以根据你需要的效果配置. 您可能感兴趣的相关文 ...