本人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机制的:
- <drivers/base/core.c>
- int device_add(struct device *dev)
- {
- ...
- kobject_uevent(&dev->kobj, KOBJ_ADD);
- ...
- }
下面给出kobject_uevent_env函数的核心框架:
- int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
- char *envp_ext[])
- {
- ...
- #if defined(CONFIG_NET)
- /* send netlink message */
- ...
- #endif
-
- /* call uevent_helper, usually only enabled during early boot */
- if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
- char *argv [3];
-
- argv [0] = uevent_helper;
- argv [1] = (char *)subsystem;
- argv [2] = NULL;
- retval = add_uevent_var(env, "HOME=/");
- if (retval)
- goto exit;
- retval = add_uevent_var(env,
- "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
- if (retval)
- goto exit;
-
- retval = call_usermodehelper(argv[0], argv,
- env->envp, UMH_WAIT_EXEC);
- }
-
- ...
- }
下面看看uevent_helper[0]来自何处:
- <lib/kobject_uevent.c>
- char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
- <linux-3.1.6/.config>
- #
- # Generic Driver Options
- #
- CONFIG_UEVENT_HELPER_PATH=""
<kernel/ksysfs.c>
- static ssize_t uevent_helper_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t count)
- {
- if (count+1 > UEVENT_HELPER_PATH_LEN)
- return -ENOENT;
- memcpy(uevent_helper, buf, count);
- uevent_helper[count] = '\0';
- if (count && uevent_helper[count-1] == '\n')
- uevent_helper[count-1] = '\0';
- return count;
- }
有个uevent_helper文件不是?那么我们现在可以把我们用户空间的程序给打进去了,我打算做个最简单的脚本/sbin/myhotplug,这个脚本只干一件事,在/home/dennis目录下生成一个hotplug文件:
</sbin/myhotplug>
- #!/bin/sh
- cd /home/dennis
- touch hotplug
- root@build-server:/sys/kernel# echo "/sbin/myhotplug" > uevent_helper
- root@build-server:/sys/kernel# cat uevent_helper
- /sbin/myhotplug
最后,对于PCI设备而言,Linux系统在启动过程中会扫描系统中所有PCI设备,对发现的每一个设备都会调用device_add函数,正如你前面看到的那样,udev将会被通知,它负责找到对应的驱动模块并加载。当然,如果你愿意,你也可以去捕捉这些事件。
(原文首发:http://www.embexperts.com/forum.php?mod=viewthread&tid=551&page=1&extra=#pid4649,略有改动)
本文探讨了Linux系统中热插拔(hotplug)机制的工作原理,特别是设备加入系统时内核如何通知用户空间,以及如何利用uevent机制自行捕捉设备事件。
5354

被折叠的 条评论
为什么被折叠?



