Linux的设备模型在设计上采用了许多面向对象的思想,但是由于C语言的限制它们只能通过一些技巧和对高级语言的模仿来实现。这些技巧使得代码结构变得更加复杂,并使得对代码的理解变得困难。本文尝试完全以面向对象的角度来分析整个设备模型的结构。所参考的代码为Linux 3.10。
如何用C模拟面向对象
从代码来看基本有两种方法:
1. 通过内嵌结构体实现继承。例如kset结构体内嵌了kobject结构,可以理解为从kobject派生了kset类。
2. 通过对结构体内的函数指针进行赋值,实现类似对纯虚父类的虚函数重载的效果。例如在bus_type结构体中含有match函数指针,变量usb_bus_type变量对其赋值,这样usb_bus_type变量可以看成是对bus_type父类的继承,并重载了虚函数match。
struct bus_type {
int (*match)(struct device *dev, struct device_driver *drv);
}
struct bus_type usb_bus_type = {
<span style="white-space:pre"> </span>.match = usb_device_match,
}
The Big Picture
上面是一张用UML表示的设备模型关系图。最上面两行的kobject, kobj_type, attribute和kset是最基本的类。从devices_kset和bus_kset开始到usb_bus_type这行(不包含)之间是设备无关层。这一层最重要的三个结构就是分别表示bus, device, driver的but_type, device和device_driver类。usb_bus_type及其之下是usb设备相关的结构。
kobject
每一个/sys下的目录(注意是目录不是文件)都有一个相对应的kobject,目录的名字就是kobject的name字段。parent指向父目录,例如/sys/bus/usb的父目录是/sys/bus。
kobject_add()用于将一个kobject加入到系统中(加入到/sys中)。每一个继承自kobject的类的实例在register的时候最终都会调用到kobject_add(),例如通过bus_register()将一个bus_type(代表一种总线)注册到系统中时,kobject_add()就会被调用。
kobj_type
kobject的指针ktype指向一个kobj_type结构。该