`
wsql
  • 浏览: 11777032 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

Linux设备驱动模型中的热插拔机制及实验

 
阅读更多
本人CU博客中的文章,本来是希望CU给推荐到首页的,结果周五不知道CU博客出了啥问题,居然没什么更新。所以我干脆把再把它放到这里,因为话题涉及设备驱动模块自动加载,本版已经有同学问过这方面的问题。

热插拔(hotplug,打这个词的时候我常常想到热干面)不一定非要指类似U盘那样的插入拔出,此处的热插拔广义上讲,是指一个设备加入系统,内核如何通知用户空间。举个简单的例子,如果你的电脑中有块PCI网卡,针对该网卡的驱动程序以内核模块的形式被编译(obj-m),那么Linux系统在启动过程中是如何自动加载该网卡的驱动模块呢?大家都知道现在udev负责干这事,其实除了udev,还可以有其他的手法,你自己就可以这样做。

我们先讨论udev,udev最关键的东西是当系统发现一个设备时,它要能够被通知该事件,一旦它知道了这件事,那么余下的事情就都好说了,无非是个如何查找模块并加载的过程。所以我们看到,这里的关键是热插拔事件的通知机制。Linux的设备模型为此提供了非常完美的支持,其原理其实发源于kset这一层,对此在《深入Linux设备驱动程序内核机制》一书中有详细的描述,虽然这部分看起来蛮复杂,貌似挺能吓唬住一些新手,其实说白了,要点就是通过sysfs建立关系,沟通内核与用户空间,然后就是uevent,也就是下面要说的热插拔事件

当然设备驱动程序一般不会和这些太底层的kobject/kset家伙打交道,因为更高层次的device,bus和driver把kobject/kset那一层的细节实现都给封装了起来。所以设备热插拔的uevent事件最终的源头来自于device_add,本帖这里肯定不会讨论device与driver如何绑定那一摊子事情。下面看看device_add的源码,是如何实现uevent机制的:
  1. <drivers/base/core.c>
  2. int device_add(struct device *dev)
  3. {
  4. ...
  5. kobject_uevent(&dev->kobj, KOBJ_ADD);
  6. ...
  7. }
复制代码
热插拔的核心实现就那一个函数调用,这里device_add对应的是KOBJ_ADD,那么移除设备自然对应KOBJ_REMOVE了。kobject_uevent函数最终调用的是kobject_uevent_env,后者才是真正干事的伙计。
下面给出kobject_uevent_env函数的核心框架:
  1. int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
  2. char *envp_ext[])
  3. {
  4. ...
  5. #if defined(CONFIG_NET)
  6. /* send netlink message */
  7. ...
  8. #endif

  9. /* call uevent_helper, usually only enabled during early boot */
  10. if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
  11. char *argv [3];

  12. argv [0] = uevent_helper;
  13. argv [1] = (char *)subsystem;
  14. argv [2] = NULL;
  15. retval = add_uevent_var(env, "HOME=/");
  16. if (retval)
  17. goto exit;
  18. retval = add_uevent_var(env,
  19. "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
  20. if (retval)
  21. goto exit;

  22. retval = call_usermodehelper(argv[0], argv,
  23. env->envp, UMH_WAIT_EXEC);
  24. }

  25. ...
  26. }
复制代码
怎么样,够简洁吧,其实看实际的代码比这要郁闷地多,不过骨架清晰就行了。代码中的netlink message就不用多说了吧,给udev发通知用(有时间的话可以分析分析udev的代码)。本帖重点讨论后半段的if (uevent_helper[0] && !kobj_usermode_filter(kobj))代码,这里的核心调用是call_usermodehelper,这个函数最有意思的地方就在于在内核空间调用用户空间的程序,它的详细实现机制在书中已经讲得很多,这里就不再赘述了。call_usermodehelper在kobject_uevent_env函数中要调用的用户空间程序由uevent_helper[0]来指定,所以如果我们能控制这个uevent_helper[0],就能接收到设备加入系统移出系统等事件。那个if中的kobj_usermode_filter条件一般都会满足(除非这是个特别注意个人隐私的设备,那就不好说了,人家偷偷加入系统就是不想让你知道你也没有办法,但是udev还是能知道的)。

下面看看uevent_helper[0]来自何处:
  1. <lib/kobject_uevent.c>
  2. char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
复制代码
貌似要通过内核配置来指定,我看了一下我系统中Linux目录下的.config文件,找到了下面这行:
  1. <linux-3.1.6/.config>
  2. #
  3. # Generic Driver Options
  4. #
  5. CONFIG_UEVENT_HELPER_PATH=""
复制代码
丫的,居然没指定,那么uevent_helper[0]="",这样的话我们在kobject_uevent_env函数中的那个if语句就没法满足了,看来要重新配置再编译内核了。不过想想sysfs这么强大,内核开发的那帮人好歹给留个用户空间的接口出来吧,一查看还真有:

<kernel/ksysfs.c>
  1. static ssize_t uevent_helper_store(struct kobject *kobj,
  2. struct kobj_attribute *attr,
  3. const char *buf, size_t count)
  4. {
  5. if (count+1 > UEVENT_HELPER_PATH_LEN)
  6. return -ENOENT;
  7. memcpy(uevent_helper, buf, count);
  8. uevent_helper[count] = '\0';
  9. if (count && uevent_helper[count-1] == '\n')
  10. uevent_helper[count-1] = '\0';
  11. return count;
  12. }
复制代码
尼玛,爽得简直是一塌糊涂,虽然俺那台马力强劲的机器编个全新的内核不过几分钟的事情,但是哪里有上面这个方法爽啊。马上进入到/sys/kernel目录下ls一把,截屏如下(点击放大):

uevent1.png

有个uevent_helper文件不是?那么我们现在可以把我们用户空间的程序给打进去了,我打算做个最简单的脚本/sbin/myhotplug,这个脚本只干一件事,在/home/dennis目录下生成一个hotplug文件:

</sbin/myhotplug>
  1. #!/bin/sh
  2. cd /home/dennis
  3. touch hotplug
复制代码
然后把这个脚本程序的文件名给打入到内核空间的uevent_helper[0]上:
  1. root@build-server:/sys/kernel# echo "/sbin/myhotplug" > uevent_helper
  2. root@build-server:/sys/kernel# cat uevent_helper
  3. /sbin/myhotplug
复制代码
好了,现在检查一下你的/home/dennis目录下面有没有hotplug这个文件,有的话就删掉,否则怎么知道是新生成的呢。现在,找个U盘插到你的电脑里,然后再看一下/home/dennis目录,有个hotplug文件对吧?如果你现在删除这个文件,再把U盘给拔了,你会再次发现这个文件。这意味着什么,意味着你可以轻而易举地捕捉到设备加入/移出系统等事件,如果你的脚本足够智能,那么你就会想到很多很有创意的玩法对吧?

最后,对于PCI设备而言,Linux系统在启动过程中会扫描系统中所有PCI设备,对发现的每一个设备都会调用device_add函数,正如你前面看到的那样,udev将会被通知,它负责找到对应的驱动模块并加载。当然,如果你愿意,你也可以去捕捉这些事件。

(原文首发:http://www.embexperts.com/forum.php?mod=viewthread&tid=551&page=1&extra=#pid4649,略有改动)
分享到:
评论

相关推荐

    Linux设备驱动程序学习(15)-Linux设备模型(热插拔、mdev 与 firmware) - Linux设备驱动程序

    Linux设备驱动程序学习(15)-Linux设备模型(热插拔、mdev 与 firmware) - Linux设备驱动程序

    Linux设备驱动程序学习

    Linux设备驱动程序学习(1)-字符设备驱动程序 ·Linux设备驱动程序学习(0)-Hello, world!模块 ·Linux设备驱动程序学习(2)-...·Linux设备驱动程序学习(15)-Linux设备模型(热插拔、mdev 与 firmware) 等等

    linux下驱动模型介绍

    设备操作的函数、file_operations 等,只有一些电源管理,热插拔相关的函数。 platform_device 里也主要是resource 的管理,所以感觉两者根本就没关系,也很奇怪 为什么要弄两套东西来实现,而且两者也对应不起来。...

    Linux 驱动学习笔记pdf文档

    ·Linux设备驱动程序学习(15)-Linux设备模型(热插拔、mdev 与 firmware) ·Linux设备驱动程序学习(16)-USB 驱动程序(一) ·Linux设备驱动程序学习(17)-USB 驱动程序(二) ·Linux设备驱动程序学习(18...

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

    4.3.3 热插拔和冷插拔76 4.3.4 微码下载76 4.3.5 模块自动加载77 4.4 内存屏障78 4.5 电源管理79 4.6 查看源代码79 第5章 字符设备驱动程序81 5.1 字符设备驱动程序基础81 5.2 设备实例:系统cmos...

    LINUX设备驱动第三版_588及代码.rar

    热插拔事件的产生 总线、设备和驱动程序 类 各环节的整合 热插拔 处理固件 快速索引 第十五章 内存映射和DMA Linux的内存管理 mmap设备操作 执行直接I/O访问 直接内存访问 快速参考 第十六章 块设备...

    Linux设备驱动程序第三版2.6.CHM

    本书是经典著作《Linux 设备驱动程序》的第三版。该版本已针对 Linux 内核的 2.6.10 彻底更新过了。...DMA、驱动程序模型和 sysfs、热插拔设备、对常见总线的描述,包括 SCSI、PCI、USB 和 IEEE1394(火线)。

    精通LINUX设备驱动程序开发

    64 4.2.3 设备实例:导航杆 65 4.2.4 softirq和tasklet 68 4.3 linux设备模型 71 4.3.1 udev 71 4.3.2 sysfs、kobject和设备类 73 4.3.3 热插拔和冷插拔 76 4.3.4 微码下载 76 4.3.5 模块自动加载 77 4.4 ...

    linux设备驱动程序第三版

    1. Linux 设备驱动第三版 .................................................................................................................... 5 2. 第 1 章 设备驱动简介 ....................................

    Linux设备驱动第三版

    本书是经典著作《Linux 设备驱动程序》的第三版。该版本已针对 Linux 内核的 2.6.10 彻底更新过了。...DMA、驱动程序模型和 sysfs、热插拔设备、对常见总线的描述,包括 SCSI、PCI、USB 和 IEEE1394(火线)。

    Linux DeviceDrivers 3rd Edition

    热插拔事件的产生 372 总线、设备和驱动程序 374 类 384 各环节的整合 388 热插拔 394 处理固件 401 快速索引 403 第十五章 内存映射和DMA 408 Linux的内存管理 408 mmap设备操作 418 执行直接I/O访问 429...

    基于Linux的USBoverIP系统设计

    备驱动原理,Linux驱动模型,以及sysfs 文件系统。 &lt;2&gt; Linux 系统下的USB驱动结构分析,包括Linux USB主机端驱动和设 备端驱动,着重分析USB主机端驱动。 &lt;3&gt; USB over IP 的实现原理和具体实现方法,由于操作的...

    SPDK官方文档中文版(2019年8月版).pdf

    3.3.6. iSCSI 热插拔 32 3.4. NVMe over Fabrics Target 32 3.5. Vhost Target(略) 37 3.6 块设备用户指南 38 3.6.1 bdev介绍 38 3.6.2 通用RPC命令 38 3.6.3 Ceph RBD 39 3.6.4 压缩虚拟Bdev模块 40 3.6.5 加密...

    SPDK开发手册中文版.docx

    3.3.6. iSCSI 热插拔 32 3.4. NVMe over Fabrics Target 32 3.5. Vhost Target(略) 37 3.6 块设备用户指南 38 3.6.1 bdev介绍 38 3.6.2 通用RPC命令 38 3.6.3 Ceph RBD 39 3.6.4 压缩虚拟Bdev模块 40 3.6.5 加密...

Global site tag (gtag.js) - Google Analytics