写一个块设备驱动 13

第 13章

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

没有最好的代码,是因为我们总能把代码改得更好

因此我们现在打算做一个小的性能改进,这次我们准备拿free_diskmem()函数下刀

本质上说,这个改进的意义不大,这是因为 free_diskmem()函数仅仅是在模块卸载时被调用,

----------------------- Page 91-----------------------

而对这种执行次数即少 不在关键路径上的函数来说,最好是尽量让他简单以增加可靠性和可读性,

除非它的耗时已经慢到能让人有所感觉,否则0.01秒和 0.                    1秒是差不多的,毕竟在现实中尼奥不

太可能用我们的程序

但我们仍然打算继续这一改进,一是为了示范什么是没有意义的改进,二是为了通过这一改进示范使用

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片强

----------------------- Page 92-----------------------

通过 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;

----------------------- Page 93-----------------------

        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

----------------------- Page 94-----------------------

HighFree:       339144 kB
LowTotal:       896356 kB
LowFree:        630920 kB
...
#
这里显示现在剩余339M高端内存和 630M低端内存

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

然后看看卸载模块后的内存情况:

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

本章的改动不大,就算是暂作休整,以留住忍耐至今忍无可忍认为无需再忍而开始打包收拾行李准备溜

之大吉的读者们

不过下一章中倒是预备了一个做起来让人比较有成就感的功能

<未完,待续>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值