免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 39985 | 回复: 14

Linux设备驱动模型中的热插拔机制及实验 [复制链接]

论坛徽章:
0
发表于 2012-03-03 18:11 |显示全部楼层
本帖最后由 MagicBoy2010 于 2012-03-03 18:13 编辑

本人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,略有改动)

论坛徽章:
0
发表于 2012-03-03 18:17 |显示全部楼层
免责声明:本人对按照上述实验进行导致的U盘数据丢失、损坏等不负任何责任。如果实验现象没有达到预期,请想本版其他网友提问。

论坛徽章:
1
CU十二周年纪念徽章
日期:2013-10-24 15:41:34
发表于 2012-03-03 22:15 |显示全部楼层
好文好文,
PS:深入Linux设备驱动程序内核机制,讲的是哪个内核版本呢?

弄明白一版内核的某些模块,新版内核很可能又发生了变化,不知道您是怎么跟上这个步伐的?

论坛徽章:
0
发表于 2012-03-04 12:47 |显示全部楼层
好文好文,
PS:深入Linux设备驱动程序内核机制,讲的是哪个内核版本呢?

弄明白一版内核的某些模块,新版内核很可能又发生了变化,不知道您是怎么跟上这个步伐的?

================================

很荣幸楼上的仁兄把你在CU上的处女贴给了我,
PS:书刚开始写时,基于2.6.35,到中晚期时,更新到了2.6.39,我也努力地把之前写好的文字及代码做了更新。但是肯定会有某些遗漏不周的地方。。。
我最熟悉的是2.4的内核,但是很明显到了2.6变化很大,我无法跟上这个步伐,但是之前的积累可以让我在遇到新的问题时可以很快速地定位到特定的代码中,比如语感。

论坛徽章:
0
发表于 2012-03-06 10:23 |显示全部楼层
支持阿,另外,我决定了,楼主大人帮我解决这个问题我就买你的书
http://bbs.chinaunix.net/thread-2041616-1-2.html

我是windows coder,linux用的少,内核调试环境搭不起来,感觉就没法搞了,真是个悲剧阿!

论坛徽章:
0
发表于 2012-03-06 11:53 |显示全部楼层
支持阿,另外,我决定了,楼主大人帮我解决这个问题我就买你的书
http://bbs.chinaunix.net/thread-2041616-1-2.html

我是windows coder,linux用的少,内核调试环境搭不起来,感觉就没法搞了,真是个悲剧阿!

==================================

不带这么讨价还价的,楼主还有别的事要做 ,你那个虚拟机的玩意我一看就比较棘手,我平时几乎不用虚拟机和硬件模拟器这些东西。你直接搞个真实的环境吧。书还是要买滴,哈哈

论坛徽章:
0
发表于 2012-03-06 11:54 |显示全部楼层
感谢千里马同学,终于让我有了一个精华的主题,真想让5楼去拥抱你一下,呵呵

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2012-03-06 12:07 |显示全部楼层
本帖最后由 Godbach 于 2012-03-06 12:08 编辑
MagicBoy2010 发表于 2012-03-06 11:54
感谢千里马同学,终于让我有了一个精华的主题,真想让5楼去拥抱你一下,呵呵



呵呵,客气了。是 MagicBoy2010 兄的文章精彩啊。

最近论坛在线的时间都没有保证,没能更好的为大家服务,真的很惭愧啊。

论坛徽章:
0
发表于 2012-03-07 17:35 |显示全部楼层
Godbach 发表于 2012-03-06 12:07
呵呵,客气了。是 MagicBoy2010 兄的文章精彩啊。

最近论坛在线的时间都没有保证,没能更好的为大 ...

千里马同学辛苦,大家平时都比较忙,可以理解,呵呵

论坛徽章:
0
发表于 2012-07-10 08:25 |显示全部楼层
:wink::wink::wink::wink:
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP