Linux驱动总结3- unlocked_ioctl和堵塞(waitqueue)读写函数的实现
学习了驱动程序的设计,感觉在学习驱动的同时学习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驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数
1. 前言 很多朋友在调试驱动的时候,都会遇到这样一个场景:修改一个参数,然后调用某个内核中的函数. 比如将某个gpio的值拉高/拉低,修改某个寄存器的值等等. 如果每一个参数都通过字符设备的ioct ...
- Linux驱动:内核的中断机制之二--request_threaded_irq函数使用
内核开始支持中断线程(threaded interrupt handler),使用接口request_threaded_irq:原来的request_irq也继续支持.使用时可根据实际情况选择合适的接 ...
- 【正点原子MP157连载】第二十章 字符设备驱动开发-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
- Linux驱动设计ioctl函数的cmd参数不能为2
Linux驱动程序设计的时候偶然发现的ioctl()函数的cmd参数不能为2,如果为2,ioctl()函数返回-1,网上说就是这样的,正常,不知道为什么,stack overflow上有一个外国学友的 ...
- 嵌入式linux应用层中断函数,嵌入式LINUX驱动开发(中断处理函数)
嵌入式LINUX驱动开发(中断处理函数) 2020年08月11日 | 萬仟网网络运营 | 我要评论 嵌入式LINUX驱动学习之7中断相关(一)中断处理函数一.函数.头文件及说明二.编译举例:一.函数. ...
- Linux驱动总结3- unlocked_ioctl和堵塞(waitqueue)读写函数的实现 【转】
转自:http://blog.chinaunix.net/uid-20937170-id-3033633.html 学习了驱动程序的设计,感觉在学习驱动的同时学习linux内核,也是很不错的过程哦,做 ...
- 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)
这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...
- Linux设备驱动开发-linux驱动中的阻塞访问方式
阻塞与非阻塞是设备访问的两种不同的模式.什么是阻塞操作呢?其是指在执行设备操作的时候,如果不能获得资源,则挂起进程直到满足可操作的条件后再进行操作.而非阻塞操作则是在进程不能进行设备操作时,并不挂起到 ...
- 嵌入式Linux驱动笔记(十八)------浅析V4L2框架之ioctl【转】
转自:https://blog.csdn.net/Guet_Kite/article/details/78574781 权声明:本文为 风筝 博主原创文章,未经博主允许不得转载!!!!!!谢谢合作 h ...
- linux wait函数头文件_手把手教Linux驱动9-等待队列waitq
在上一篇<手把手教Linux驱动8-Linux IO模型>我们已经了解了阻塞.非阻塞.同步和异步等相关概念,本文主要讲解如何通过等待队列实现对进程的阻塞. 应用场景: 当进程要获取某些资源 ...
最新文章
- 集中化监控SQL Server数据库
- teamviewer 无法连接 原因未知
- 从Java到Kotlin(五)
- Shell——read读取控制台输入和函数
- NYOJ 题目77 开灯问题(简单模拟)
- 《数据库SQL实战》查找入职员工时间排名倒数第三的员工的所有信息
- 前端学习(871):attachment注册事件
- 域内,如何限制一台电脑只能指定的域用户登录
- 程序员该如何选择工作以及如何做好职业规划?
- [Unity3D]ml-agent入门案例
- 毕向东java视频js_js foteach 传智播客毕向东老师 新版JAVASE基础学习视频教程 ...(8)...
- centos6使用df命令,设备名称太长导致换行问题
- 如何将工作流程“简单化”,从而提高工作效率?
- 2022低压电工考试题及答案
- 矩阵方程的计算求解(Matlab实现)
- [DAX] IF函数
- mysql 5.7 压缩包解压安装过程
- 数据结构c语言课程设计报告,(数据结构c语言课程设计报告.doc
- 正确安装破解后,打开Matlab R2018a 报错License Manager Error-8
- 学海灯塔课后题答案模块上线