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,下面主要从用 ...
随机推荐
- [算法进阶0x10]基本数据结构A作业总结
在线题目\(oj\)评测地址:https://xoj.red/contests/show/1237 T1-Editor(hdu4699) 题目描述 维护一个整数序列的编辑器,有以下5种操作,操作总数不 ...
- matplotlib 28原则
记下各个简易模板,方便ctrl+c和ctrl+v 子图: import numpy as np import matplotlib.pyplot as plt x = np.array(range(1 ...
- android sqlite批量插入数据速度解决方案
转自 http://hi.baidu.com/hfutonline/blog/item/62b1e4de8bdf4b2e5882dd28.html 最近在做android项目的时候遇到一个问题,应用程 ...
- JAVA8给我带了什么——lambda表达
这此年来我一直从事.NET的开发.对于JAVA我内心深处还是很向往的.当然这并不是说我不喜欢.NET.只是觉得JAVA也许才是笔者最后的归处.MK公司是以.NET起家的.而笔者也因为兄弟的原因转行.N ...
- VB|xp风格:终于解决了“图片优化软件”在部分xp系统上无法启动的问题。
一年以来,图片优化软件一直存在一个“兼容”性问题. 因为之前的软件是在windows 2003系统上开发的,制作成安装文件后,经部分用户测试发现,在部分用户的xp系统上安装后,无法正常启动,只能听到p ...
- win+R快捷启动程序
win10: Win+R cmd:命令行程序 notepad:记事本 winword:word文档 calc:记事本 mspaint:画图 wordpad:写字板
- Harbor镜像迁移
目录 背景说明 方案实现 背景说明 在早期生产环境尝试使用docker的时候,虽然使用了harbor作为镜像仓库,但是并没有做好相关存储规划,所有的镜像都直接存储到了harbor本地.随着业务发展,本 ...
- python 数据类型 datatype
python 数据类型 datatype 列表list 元组tuple 集合set 字典dictionary 字符串string 一.列表list list :python的一种数据类型.是可变的, ...
- Xen的入门到放弃
Xen的入门到放弃 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. Xen 是一个开放源代码虚拟机监视器(VMM),由剑桥大学的"Ina Pratt"和" ...
- check nginx配置文件错误:[emerg]: getpwnam(“nginx”) failed
1.错误提示: [root@server include]# /application/nginx/sbin/nginx -t -c /applications/nginx/nginx/nginx.c ...