分类: LINUX
2013-11-03 22:35:06
内核设备模型分析
Sysfs文件系统
内核设备模型主要的模块和用户之间能看到的相关部分就是sysfs文件系统了。内核在启动的时候会注册sysfs文件系统,并且在启动系统的初期。通过mount命令挂载sysfs文件系统到/sys挂载点。
Mount -t sysfs sysfs /sys
那么sysfs文件系统的作用是什么呢。概括的说有三点:
1、建立系统中总线、驱动、设备三者之间的桥梁
2、像用户空间展示内核中各种设备的拓扑图
3、提供给用户空间对设备获取信息和操作的接口,部分取代ioctl功能。
Kobject
Sysfs文件系统中最基本的结构就是kobject,kobject可以代表一个设备,一条总线等。在sys目录下直观的以一个目录表示出来。
struct kobject |
Kobject结构中还有一个 kobj_type数据结构成员,该成员保护了kobject的属性和属性相关的操作函数。一个属性对应于kobject目录下的一个文件。在用户态可以通过读写来操作这个属性。
struct kobj_type |
Kset
Kset可以认为是一组kobject的集合,kset首先自己是一个kobject。Kset处理将同类型的kobject组合到一起外,还有一个重要的作用就是当kset中的某些kobject对象发生了状态的改变需要通知到用户空间时,就会调用其中uevent相关的函数向用户空间发送消息来完成。
struct kset |
下图描述了kset下挂接多个kobject时相应的数据结构连接。
Uevent机制
上面的分析其实只是对linux设备模型做了一些基础性的了解。也就是一个穿针引线的作用,如果要细致了解,需要仔细阅读代码。有了上面对于sysfs的基础。接下来我们来比较详细的了解一下uevent机制。
什么是uevent机制。这个得从热插拔设备开始说起。最简单的一个例子就是U盘了。当我们在计算机上插上一个U盘的时候,系统的USB hub会检测到U盘设备接入,并且完成设备枚举过程(从设备上读出相应的设备信息),并在内核中创建相应的设备结构体。但是,usb设备千奇百态,内核不可能预先将所有usb设备驱动都增加到内存中来。也就是当插入U盘设备的时候,内核中不一定存在对应这个设备的usb驱动。这个时候USB驱动也许以模块的形式保存在硬盘上。载入驱动必然只能从用户态来进行,那这时候应该怎么办呢?
看到这里的时候,有人一定会想,人工敲入命令载入驱动,呵呵。这必然是一种方法,但是是一种很古老的方法。Linux对类似的情况设计了一种uevent的机制。当有新的设备加入的时候,将设备的信息发送消息到用户态。而用户态有一个udev的进程监听这个信息。当收到信息后做一定的解析,根据解析到的结果和用户程序的配置做一些处理,也包括加载驱动程序。
上图就是usb设备插入到主机后,usb hub检测到设备并添加设备,会调用到device_add函数,该函数会调用到kobject_uevent函数,想用户态发送KOBJ_ADD设备接入的消息。
int kobject_uevent(struct kobject *kobj, enum kobject_action action) |
具体调用到kobject_uevent_env函数完成了消息的拼装和具体的发送。下面直接将这段代码贴出来并且附上关键部分的分析,函数长但是逻辑比较简单。
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, |
Uevent的用户态部分是一个udev的后台进程,它随系统启动后监听内核发送到消息。主要执行的工作有两个:
1、自动加载驱动模块
2、根据uevent消息在/dev目录下自动添加或者删除设备节点
Udev进程相关的文档比较多,这里不再做详细的叙述。Udev进程在etc目录下面有一个uevent规则文件/etc/udev/rules.d/50-udev.rules类似的以.rules为后缀的文件。Udev程序收到uevent消息后,在这些规则文件中寻找匹配的规则,如果找到了就执行匹配的shell命令。
例如:
ACTION=="add", SUBSYSTEM=="?*", ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}"
等udev收到add事件后,shell能自动去加载MODALIAS定义的模块。
关于如何写udev的匹配规则,这里也不做原理类的内容来叙述。或许有时间的时候专门写和试验的小程序来玩一玩udev机制。