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,下面主要从用 ...
随机推荐
- Change upload file limit in specified Webapllication in SharePoint (PowerShell)
http://gallery.technet.microsoft.com/office/Change-upload-file-limit-89502ff2 http://sharepointrela ...
- emWin 文字图形同时刷新导致图形显示异常
@2018-7-10 实现目标 一 BUTTON 控制文字图形的刷新切换,具体为 BUTTON 初次按下,文字显示为 “开始” .填充圆显示为绿色,再次按下,文字显示为 “停止” .填充圆显示为红色 ...
- yml实例
producer.yml apiVersion: v1kind: Podmetadata:name: producer-consumerspec:containers:- image: busybox ...
- hadoop文件配置
伪分布式配置: core-site.xml <configuration> <property> <name>fs.defaultFS</name> & ...
- N皇后问题(DFS)
题目:在N*N的国际象棋棋盘上放置N个皇后彼此不受攻击(即在棋盘的任一行,任一列和任意对角线上不能放置2个皇后),求解所有摆放方案的总数. 样例输入: 1 8 样例输出: 1 92 解题思路:由于皇后 ...
- BFS求解迷宫的最短路径问题
题目:给定一个大小为N*M的迷宫,迷宫由通道('.')和墙壁('#')组成,其中通道S表示起点,通道G表示终点,每一步移动可以达到上下左右中不是墙壁的位置.试求出起点到终点的最小步数.(本题假定迷宫是 ...
- Android下载管理DownloadManager功能扩展和bug修改
http://www.trinea.cn/android/android-downloadmanager-pro/ 本文主要介绍如何修改Android系统下载管理,以支持更多的功能及部分bug修改和如 ...
- P1339 热浪 最短路径模板题
这么naive的题面一看就是最短路模板题~~~ ok.首先是floyd算法,tts,记得把k放在最外面就行了. #include <cstdio> #include <cstring ...
- 一个程序如何在调试时退出调试或退出while循环
1.退出调试 按Ctrl+C 2.退出while循环 比如 #include <stdio.h> #include <stdlib.h> int main() { long a ...
- centos7 修改 PATH环境变量(注意,不是添加!!!TMD)
起因都是,参照阿里云的Java环境配置,MMP~ 现在我们分析一下这几句话.JAVA_HOME和JRE_HOME都是没问题的 CLASSPATH:注意 [ lib$:JRE ]这部分,Linux环 ...