大家好,我是极客范的本期栏目编辑小友,现在为大家讲解学会使用linux热插拔之udev的使用方法问题。
有很多与Udev相关的文章。本文的主要目的不是提供一个完整的教学文档,而是使用它,只是给出网上可用的主要资源。本文着重分析其基本工作原理和自述文件中没有明确说明的一些问题。
1基本概念
Udev文件系统针对2.6内核,提供了基于用户空间的动态设备节点管理和命名的解决方案。网上关于为什么要使用udev文件系统,udev文件系统和devfs文件系统的比较等文章很多。想了解这方面,请直接搜索相关关键词。
Udev的官方网站:http://www.kernel.org/pub/Linux/utils/kernel/hotplug/udev.html
src代码的下载地址:http://www.us.kernel.org/pub/linux/uTIls/kernel/hotplug/
此外,网上还有很多关于udev规则编写的文章。如果想得到最准确的版本,可以在src代码的代码树中找到wriTIng_udev_rules的帮助文档。事实上,本文并没有逐一介绍规则的所有关键词。结合manodev和udev的一些规则文件,可以了解如何编写自己需要的规则文件。
2安装和启动
2.1安装
代码树中有很多版本的Udev,我下载的最新版本是udev-117,可以和内核版本2.6.21一起正常使用。网上很多文章可能会介绍更早的版本,有些步骤,包括udev的README文档,似乎不是很准确。
基本上这个版本的udev需要注意的是,安装只需要UDEV和udevadm两个文件,其他必要的文件,包括udevtrigger,都只是UDEV ADM的一个符号链接,Udevstart是不需要的。当然,像Udev.conf这样的配置文件还是一样的。
2.2启动
可以在启动脚本中用ude VDd参数启动udev文件系统的守护进程,然后使用udevtrigger创建buildin的设备驱动节点,以后插入和移除模块时会自动处理节点的管理。
能够正常加载udev的前提基本包括以下操作:
设置路径变量
挂载sysfs文件系统
基于ram加载一个可写/dev目录(事实上,只需提供一个可写目录,目录路径本身是可配置的)
/dev目录中需要创建的控制台节点和空节点。
脚本类似:
#设置路径
PATH=/bin :/sbin :/usr/bin :/usr/sbin
导出路径
#装载proc和devpts文件系统
/bin/mount -a
MK nod/dev/控制台c 5 1
mknod /dev/null c 1 3
/sbin/udevd -d
/sbin/udevtrigger
M
ount使用的fstab文件类似:none /tmp ramfs defaults 0 0
udev /dev ramfs defaults 0 0
none /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
当然,你的系统上可能还会需要预先创建一些其它的设备节点,比如串口的ttySx 才能正常启动shell,完成以上脚本的执行,那就要看具体情况了。
3 使用中的一些问题的思考
3.1 关于规则的多次匹配
帮助文档中说一个设备可以被多条规则多次匹配,不过,需要明确的一点是:
多次匹配只能添加多个Symlink,不能创建多个Name:
例如:
KERNEL=="mtdblock4", NAME+="mtdbb4"
KERNEL=="mtdblock4", NAME+="%k"
就只会创建 /dev/mtdbb4 而不会创建/dev/mtdblock4
而类似:
KERNEL=="mtdblock4", NAME+="mtdbb4"
KERNEL=="mtdblock4", SYMLINK+="mtdbb4link"
是可以正常工作的。
3.2 关于udev.conf的语法
可能大家会发现,似乎没有什么详细文档描述udev.conf的写法,实际上从udevd的代码里可以看出:
udev.conf文件里面只会解析这三个参数:
udev_root 定义udev的目录路径
udev_rules 定义udev的规则文件的目录路径
udev_log 定义log的级别
也许以后会添加一些别的配置参数?
4 基本工作原理方面的问题
这部分主要是分析了一下udev的source code,对一些自己关心的问题的理解
4.1 Udevd如何获取内核的这些模块动态变化的信息
设备节点的创建,是通过sysfs接口分析dev文件取得设备节点号,这个很显而易见。那么udevd是通过什么机制来得知内核里模块的变化情况,如何得知设备的插入移除情况呢?当然是通过hotplug机制了,那hotplug又是怎么实现的?或者说内核是如何通知用户空间一个事件的发生的呢?
答案是通过netlink socket通讯,在内核和用户空间之间传递信息。
内核调用kobject_uevent函数发送netlink message给用户空间,这部分工作通常不需要驱动去自己处理,在统一设备模型里面,在子系统这一层面,已经将这部分代码处理好了,包括在设备对应的特定的Kobject创建和移除的时候都会发送相应add和remove消息,当然前提是你在内核中配置了hotplug的支持。
Netlink socket作为一种内核与用户空间的通信方式,不仅仅用在hotplug机制中,同样还应用在其它很多真正和网络相关的内核子系统中。
Udevd通过标准的socket机制,创建socket连接来获取内核广播的uevent事件 并解析这些uevent事件。
4.2 Udevd如何监控规则文件的变更
如果内核版本足够新的话,在规则文件发生变化的时候,udev也能够自动的重新应用这些规则,这得益于内核的inoTIfy机制, inoTIfy是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知。
在udevd中,对inotify和udev的netlink socket文件描述符都进行了select的等待操作。有事件发生以后再进一步处理。
4.3 Udevtrigger的工作机制?
运行udevd以后,使用udevtrigger的时候,会把内核中已经存在的设备的节点创建出来,那么他是怎么做到这一点的? 分析udevtrigger的代码可以看出:
udevtrigger通过向/sysfs 文件系统下现有设备的uevent节点写"add"字符串,从而触发uevent事件,使得udevd能够接收到这些事件,并创建buildin的设备驱动的设备节点以及所有已经insmod的模块的设备节点。
所以,我们也可以手工用命令行来模拟这一过程:
/ # echo"add">/sys/block/mtdblock2/uevent
/ #
/ # UEVENT[178.415520] add /block/mtdblock2 (block)
但是,进一步看代码,你会发现,实际上,不管你往uevent里面写什么,都会触发add事件,这个从kernel内部对uevent属性的实现函数可以看出来,默认的实现是:
static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
kobject_uevent(&dev->kobj, KOBJ_ADD);
return count;
}
所以不管写的内容是什么,都是触发add操作,真遗憾,我还想通过这个属性实验remove的操作。 不知道这样限制的原因是什么。
而udevstart的实现方式和udevtrigger就不同了,它基本上是重复实现了udevd里面的机制,通过遍历sysfs,自己完成设备节点的创建,不通过udevd来完成。
4.4 其它
Ø udevd创建每一个节点的时候,都会fork出一个新的进程来单独完成这个节点的创建工作。
Ø Uevent_seqnum 用来标识当前的uevent事件的序号(已经产生了多少uevent事件),你可以通过如下操作来查看:
$ cat /sys/kernel/uevent_seqnum
2673