第13章 写一个块设备驱动

块设备驱动实战基础篇一 (170行代码构建一个逻辑块设备驱动) 1   内核块设备驱动基础学习与实战 1.1 设备驱动IO架构初探   操作系统是如何将数据读到缓冲区的,发生了什么?我们带着这样的问题,粗略走一下read调用系统过程,希望这个初探,可以唤起大家研究操作系统内核的好奇心和兴趣,并以此为例,让我们先初步对请求在过滤块设备驱动中的处理过程有个大概印象和了解。   块设备在整个Linux中应用的总体结构图如下:   从上图可以看出,块 阅读详情

第13章

+---------------------------------------------------+
|                 写一个块设备驱动                  |
+---------------------------------------------------+
| 作者:赵磊                                        |
| email: zhaoleidd@hotmail.com                      |
+---------------------------------------------------+
| 文章版权归原作者所有。                            |
| 大家可以自由转载这篇文章,但原版权信息必须保留。  |
| 如需用于商业用途,请务必与原作者联系,若因未取得  |
| 授权而收起的版权争议,由侵权者自行负责。          |
+---------------------------------------------------+

没有最好的代码,是因为我们总能把代码改得更好。
因此我们现在打算做一个小的性能改进,这次我们准备拿free_diskmem()函数下刀。

本质上说,这个改进的意义不大,这是因为free_diskmem()函数仅仅是在模块卸载时被调用,
而对这种执行次数即少又不在关键路径上的函数来说,最好是尽量让他简单以增加可靠性和可读性,
除非它的耗时已经慢到能让人有所感觉,否则0.01秒和0.00001秒是差不多的,毕竟在现实中尼奥不太可能用我们的程序。

但我们仍然打算继续这一改进,一是为了示范什么是没有意义的改进,二是为了通过这一改进示范使用radix_tree_gang_lookup()函数和page->index的技巧。

首先我们看看原先的free_diskmem()函数:
void free_diskmem(void)
{
        int i;
        struct page *page;

        for (i = 0; i < (simp_blkdev_bytes + SIMP_BLKDEV_DATASEGSIZE - 1)
                >> SIMP_BLKDEV_DATASEGSHIFT; i++) {
                page = radix_tree_lookup(&simp_blkdev_data, i);
                radix_tree_delete(&simp_blkdev_data, i);
                /* free NULL is safe */
                __free_pages(page, SIMP_BLKDEV_DATASEGORDER);
        }
}

它遍历所有的内存块索引,在基树中找到这个内存块的page指针,然后释放内存,顺带着释放掉基数中的这个节点。
考虑到这个函数不仅会在模块卸载时被调用,也会在模块加载时、申请内存中途掉链子时用来擦屁股,因此也需要考虑内存没有完全申请的情况。
所幸的是这种情况下radix_tree_lookup()函数会返回NULL指针,而radix_tree_delete()和__free_pages()函数都能对NULL指针做出我们最期待的处理:就是什么也不做。

这段代码很小很直接,逻辑简单而清晰,性能也差不到哪里去,完全符合设计要求,
不幸的是我们还是打算做一些没必要的优化,借此还可以顺便读一读基树的内核代码。
首先看radix_tree_lookup()函数,它在基数中查找指定索引对应的指针,为了获得这一指针的值,基本上它需要把基树从上到下找一遍。
而对于free_diskmem()函数而言,我们仅仅是需要遍历基树中的所有节点,使用逐一查找的方法进行遍历未免代价太大了。
就像是我们要给在场的所有同学每人发一个糖果,只需要让他们排好队,每人领一个即可,而不需要按照名单找出每个人再发。
为了实现这一思想,我们跑到linux/lib/radix-tree.c中找函数,找啊找,找到了radix_tree_gang_lookup()函数。

radix_tree_gang_lookup()函数虽然不是我们理想中的遍历函数,但也有了八九不离十的功能。
就像在酒吧里找不到D Cup,带回去个C Cup也总比看A片强。
通过radix_tree_gang_lookup()函数,我们可以一次从基树中获取多个节点的信息:
unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, unsigned long first_index, unsigned int max_items);
具体的参数嘛,RTFSC吧。

这是我们注意到使用这个函数时顾此失彼的一面,虽然我们获得了一组需要释放的指针,但却无法获得这些指针的索引。
而执行释放基树中节点的操作时却恰恰需要使用索引作参数。
然后就是一个技巧了,我们借用page结构的index成员来存储这一索引。
之所以可以这样用,是因为page结构的index成员在该页用作页高速缓存时存储相对文件起始处的以页大小为单位的偏移,
而我们所使用的页面不会被同时用作页高速缓存,因此这里可以借用page.index成员。
按照以上思路,我们写出了修改后的代码:
void free_diskmem(void)
{
        unsigned long long next_seg;
        struct page *seglist[64];
        int listcnt;
        int i;

        next_seg = 0;
        do {
                listcnt = radix_tree_gang_lookup(&simp_blkdev_data,
                        (void **)seglist, next_seg, ARRAY_SIZE(seglist));

                for (i = 0; i < listcnt; i++) {
                        next_seg = seglist[i]->index;
                        radix_tree_delete(&simp_blkdev_data, next_seg);
                        __free_pages(seglist[i], SIMP_BLKDEV_DATASEGORDER);
                }

                next_seg++;
        } while (listcnt == ARRAY_SIZE(seglist));
}

当然,alloc_diskmem()函数中也需要加上page->index = i这一行,用于把基树的索引存入page.index,修改后的代码如下:
int alloc_diskmem(void)
{
        int ret;
        int i;
        struct page *page;

        INIT_RADIX_TREE(&simp_blkdev_data, GFP_KERNEL);

        for (i = 0; i < (simp_blkdev_bytes + SIMP_BLKDEV_DATASEGSIZE - 1)
                >> SIMP_BLKDEV_DATASEGSHIFT; i++) {
                page = alloc_pages(GFP_KERNEL | __GFP_ZERO | __GFP_HIGHMEM,
                        SIMP_BLKDEV_DATASEGORDER);
                if (!page) {
                        ret = -ENOMEM;
                        goto err_alloc;
                }

                page->index = i;
                ret = radix_tree_insert(&simp_blkdev_data, i, page);
                if (IS_ERR_VALUE(ret))
                        goto err_radix_tree_insert;
        }
        return 0;

err_radix_tree_insert:
        __free_pages(page, SIMP_BLKDEV_DATASEGORDER);
err_alloc:
        free_diskmem();
        return ret;
}

现在试验一下修改后的代码,先看看能不能编译:
# make
make -C /lib/modules/2.6.18-53.el5/build SUBDIRS=/root/test/simp_blkdev/simp_blkdev_step13 modules
make[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686'
  CC [M]  /root/test/simp_blkdev/simp_blkdev_step13/simp_blkdev.o
  Building modules, stage 2.
  MODPOST
  CC      /root/test/simp_blkdev/simp_blkdev_step13/simp_blkdev.mod.o
  LD [M]  /root/test/simp_blkdev/simp_blkdev_step13/simp_blkdev.ko
make[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686'
#

看看当前系统的内存情况:
# cat /proc/meminfo
HighTotal:     1146816 kB
HighFree:       339144 kB
LowTotal:       896356 kB
LowFree:        630920 kB
...
#
这里显示现在剩余339M高端内存和630M低端内存。

然后加载我们的模块,让它吃掉300M内存:
# insmod simp_blkdev.ko size=300M
# cat /proc/meminfo
HighTotal:     1146816 kB
HighFree:       137964 kB
LowTotal:       896356 kB
LowFree:        523900 kB
...
#
正如我们的预期,剩余内存减少300M左右。

然后看看卸载模块后的内存情况:
# rmmod simp_blkdev
# cat /proc/meminfo
HighTotal:     1146816 kB
HighFree:       338028 kB
LowTotal:       896356 kB
LowFree:        631044 kB
...
#
我们发现剩余内存增加了300M,这意味着模块已经把吃掉的内存吐回来了,
从而可以推断出我们修改过的free_diskmem()函数基本上是能够工作的。

本章的改动不大,就算是暂作休整,以留住忍耐至今忍无可忍认为无需再忍而开始打包收拾行李准备溜之大吉的读者们。
不过下一章中倒是预备了一个做起来让人比较有成就感的功能。

正点原子imx6ull-mini-Linux驱动之Linux 块设备驱动实验 块设备是针对存储设备的,比如 SD 卡、EMMC、NAND Flash、Nor Flash、SPI Flash、机 械硬盘、固态硬盘等。因此块设备驱动其实就是这些存储设备驱动,块设备驱动相比字符设备 驱动的主要区别如下:①、块设备只能以块为单位进行读写访问,块是 linux 虚拟文件系统(VFS)基本的数据传输 单位。字符设备是以字节为单位进行数据传输的,不需要缓冲。 阅读详情

相关推荐

Linux驱动开发13个实用案例

对 AI 很强大,但我自己调试好的小案例用起来也很方便,就像自己创建的一个智能体一样,你觉得呢。关注威哥爱编程,全栈开发就你行。

威哥爱编程,华为 HDE,鸿蒙极客、优快云博客专家、《鸿蒙 HarmonyOS 5/6 开发之路》系列图书第一作者 2824

6.块设备驱动程序--二:块设备驱动编写

如何写块设备驱动程序 1., 使用register_blkdev()创建一个块设备 2,以面向对象的思想分配 gendisk 结构体。用 alloc_disk 函数。 3,设置 gendisk 结构体。 ①,分配/设置一个队列:request_queue_t. (提供读写能力)用 blk_init_queue 函 数。 ②,设置 gendisk 其他信息。(提供磁盘属性:磁盘容量,扇区大小等) 4...

weixin_42918587的博客 632

写一个块设备驱动-共15章.pdf

写一个块设备驱动-共15章.pdf 详细分析了一个块设备

写一个块设备驱动-第13章

转自:http://linux.chinaunix.net/bbs/thread-1045283-13-1.html +---------------------------------------------------+|                 写一个块设备驱动                  |+----------------------------------

cnctloveyu的专栏 724

LVDS信号调试必备:5分钟搞懂Xilinx IDELAYE3的TIME模式校准

本文深入解析了在Xilinx Ultrascale系列FPGA中调试LVDS信号时,如何正确使用IDELAYE3的TIME模式进行精密延迟校准。文章重点阐述了IDELAYCTRL、EN_VTC管脚与校准状态机的协同工作机制,揭秘了内置自校准(BISC)中Align_Delay的影响,并提供了从抽头值计算到实际眼图优化的完整实战方案,帮助工程师快速解决高速接口的时序问题。

joy55的博客 216

ANC降噪学习

概述 ANC,英文名称:Active Noise Control,主动降噪。其原理是降噪系统电路产生降噪MIC接收的外界环境噪音相等的反相信号,将噪声抵消。核心算法 ANC降噪实现核心算法为:FxLMS(最小均方差算法)。最小均方差算法以均方误差为代价函数,并使误差降到最小的算法。 具体算法推导这里不做具体介绍,这里直接列出表达式: ...

王小壮的博客 2万+

写一个块设备驱动-第12章

转自:http://linux.chinaunix.net/bbs/thread-1045283-11-1.html +---------------------------------------------------+|                 写一个块设备驱动                  |+----------------------------------

cnctloveyu的专栏 750

从GAN生成失败案例出发:为什么你的AI画不准位置?CoordConv保姆级解决方案

本文深入探讨了GAN生成图像时位置偏移问题的根源,并提供了CoordConv的保姆级解决方案。通过分析传统CNN在空间坐标建模上的缺陷,详细介绍了CoordConv的原理、实现步骤及在StyleGAN2等模型中的实战应用,显著提升了医学图像生成等任务的定位精度。

pp12345的博客 204

【Linux驱动设备开发详解】13.Linux块设备驱动

块设备的I/O方式相比字符设备存在较大不同,因而引入了request_queue,request、bio等一系列数据结构。在整个块设备的I/O操作中吗,贯穿始终的就是"请求",块设备的I/O操作会排队和整合;而字符设备的I/O操作则是直接进行,不绕弯驱动的任务是处理请求,对请求的排队和整合由I/O调度算法解决,因此,块设备驱动的核心就是请求处理函数或"制造请求"函数。

qq_38089448的博客 990

DataHub 架构设计与核心工作原理

DataHub 是一个第三代数据目录系统,专为现代数据栈设计,支持数据发现、协作、治理和端到端可观测性。其架构采用模型优先的设计理念,注重不同工具和系统之间的互操作性。

北斗云的博客 1223

AGM Supra软件从零到一:国产CPLD工程创建全流程实战解析

本文详细解析了AGM Supra软件在国产CPLD开发中的全流程实战操作,从工程创建到代码开发、编译调试,再到高级技巧与疑难排解。特别对比了Supra与Quartus的差异,提供了工程路径管理、Quartus项目转换、HDL代码规范等实用技巧,帮助开发者高效完成CPLD项目开发。

weixin_29193259的博客 501

Linux块设备驱动

第十三章  Linux块设备驱动 本章导读 块设备提供块设备提供设备的存取,设备的存取,可以随机的以固定大小的块传输数据,例如我们最为常见的磁盘设备,当然块设备和字符设备有较大差别,块设备有自己的驱动接口。简单来说,内核决定一个块是固定的4096字节,当然该值可以随着依赖文件系统的变化而改变。块设备驱动采用register_blkdev向系统进行注册,unregister_blkdev取消

zhanjianshinian的专栏 2478

Linux下Catapro酶动力学预测工具保姆级安装指南(PyTorch+CUDA11.8适配版)

本文提供了Linux系统下Catapro酶动力学预测工具的详细安装指南,特别适配PyTorch+CUDA11.8环境。从环境准备、源码获取到虚拟环境创建、依赖包安装和模型配置,逐步指导用户完成安装流程,并包含常见问题排查和性能优化建议,帮助研究者快速部署这一高效酶动力学参数预测工具。

weixin_29278805的博客 211

【驱动】块设备驱动(四)-块设备驱动层

接着,通过调用通用的文件写入函数进行实际的写入操作,并返回写入操作的结果。例如,如果读取出现错误(retval小于0),或者已经读取完所有数据(iov_iter_count(iter)为0),或者已经读取到文件末尾(*ppos >= size),或者文件是DAX文件(IS_DAX(inode)),则跳过剩余的读取操作,并更新文件的访问时间。:调用通用的执行写入操作的函数generic_perform_write,执行标准缓冲区写入操作,并将写入的起始位置保存到pos中,返回写入的状态码。

嵌入式与Linux那些事的博客 1567

SD卡烧写Linux kernel——SD卡分区,并烧写uboot,kernel,DTB及filesystem

Linux Ubuntu环境下对SD卡分区(烧写uboot,kernel,及filesystem) 介绍dd命令的使用,SD卡分区、格式化、复制文件等 fdisk命令

weixin_42408707的博客 5056

【零基础入门unity游戏开发——3D篇】3D物理关节 —— Joint相关组件

【零基础入门unity游戏开发——3D篇】3D物理关节 —— Joint相关组件

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1200

Linux 块设备驱动实验

前面我们都是在学习字符设备驱动,本章我们来学习一下块设备驱动框架,块设备驱动是 Linux 三大驱动类型之一。块设备驱动要远比字符设备驱动复杂得多,不同类型的存储设备又 对应不同的驱动子系统,本章我们重点学习一下块设备相关驱动概念,不涉及到具体的存储设 备。最后,我们使用ALPHA 开发板板载RAM 模拟一个块设备,学习块设备驱动框架的使用。

zhuguanlin121的博客 891

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

第13章--Linux块设备驱动

所有的I/O请求都会先放入一个队列,统一由I/O调度层管理,它决定队列中的请求的排列顺序以及什么时候派发请求到块设备驱动,最终由块设备驱动程序操作到相关的硬件。块设备对于I/O请求有对应的缓冲区,可以调整响应顺序,而字符设备没有缓冲、直接读写。当这个结构代表一个块设备时,bd_disk成员指向设备的gendisk结构。块设备只能以块为单位接收输入和返回输出,而字符设备则以字节为单位。是连接抽象的块设备操作与具体块设备操作之间的枢纽。块设备可以随机访问,而字符设备只能顺序读写。

qq_38500362的博客 158

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

笔记整理—linux驱动开发部分(13)块设备

整理一下学习的内容,个人观点可能存在错误。本章介绍了什么是块设备,对比了块设备与字符设备的区别,并说明块设备更适合驱动内存类设备的原因。

qq_35229394的博客 692

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Linux驱动开发(13)------- 块设备驱动介绍

目录1.正确理解块设备驱动的概念2.块设备驱动框架简介3.块设备驱动案例分析-------->用内存虚拟出来的硬盘4.块设备驱动案例演示 1.正确理解块设备驱动的概念 【1】块设备和字符设备的差异 块和字符是两种不同的访问设备的策略 同一个设备可以同时支持块和字符两种访问策略 设备本身的物理特性决定了哪一种访问策略更适合 块设备本身驱动层支持缓冲区,而字符设备驱动层没有缓冲 块设备驱动最适合存储设备 【2】块设备驱动的特点 字符设备只能顺序访问(如串口发送数据顺序),而块设备可以随机访问(不

big_C的博客 639

写一个块设备驱动-第14章

转自:http://linux.chinaunix.net/bbs/thread-1045283-13-1.html +---------------------------------------------------+|                 写一个块设备驱动                  |+----------------------------------

cnctloveyu的专栏 711

写一个块设备驱动 2

第 2章 +---------------------------------------------------+ |                 写一个块设备驱动                   | +---------------------------------------------------+ | 作者:赵磊

流年偷换的专栏 611

《Linux 设备驱动开发详解:基于最新的 Linux 4.0 内核》 第 13 章 Linux 块设备驱动

参考:宋宝华 著,机械工业出版社,2015年版。

qq_40843427的博客 369

块设备驱动

出处:23.Linux-块设备驱动(详解) 通过分析块设备驱动框架,学习如何写块设备驱动 字符设备驱动: 当我们的应用层读写(read()/write())字符设备驱动时,是按字节/字符来读写数据的,期间没有任何缓存区,因为数据量小,不能随机读取数据,例如:按键、LED、鼠标、键盘等 块设备: 块设备是i/o设备中的一类, 当我们的应用层对该设备读写时,是按扇区大小来读写数据的,若读写的...

weixin_30651273的博客 124
上一篇: 第12章 写一个块设备驱动
下一篇: 第14章 写一个块设备驱动
opendba
博客等级 码龄16年 43粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值