kobject device model and sys file system introduction

一、Linux Device Model

1.linux设备模型简介

在内核2.5的开发周期中需要完成一个目标:为内核建立一个统一的设备模型,目的是为了对计算机上的所有设备进行统一地表示和操作,包括设备本身和设备之间的连接关系。这个模型是在分析了PCI和USB的总线驱动过程中得到的,这两个总线类型能代表当前系统中的大多数设备类型,它们都有完善的热插拔机制和电源管理的支持,也都有级连机制的支持,以桥接的PCI/USB总线控制器的方式可以支持更多的PCI/USB设备。为了给所有设备添加统一的电源管理的支持,而不是让每个设备中去独立实现电源管理的支持,人们考虑的是如何尽可能地重用代码;而且在有层次模型的PCI/USB总线中,必须以合理形式展示出这个层次关系,这也是电源管理等所要求的必须有层次结构。如在一个典型的PC系统中,中央处理器(CPU)能直接控制的是PCI总线设备,而USB总线设备是以一个PCI设备(PCI-USB桥)的形式接入在PCI总线设备上,外部USB设备再接入在USB总线设备上;当计算机执行挂起(suspend)操作时,Linux内核应该以 “外部USB设备->USB总线设备->PCI总线设备” 的顺序通知每一个设备将电源挂起;执行恢复(resume)时则以相反的顺序通知;反之如果不按此顺序则将有设备得不到正确的电源状态变迁的通知,将无法正常工作。总的来说,Linux设备模型是一个复杂的数据结构,它将系统中的设备层次关系进行了抽象的表述。

2.基本数据结构体

kobject是组成设备模型的最基本结构,通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject类似于C++中的基类(C语言中不允许直接描述继承关系,因此使用了在一个结构中嵌入另一个结构的技术),它嵌入于更大的对象中--所谓的容器--用来描述设备模型的组件。如bus,devices, drivers都是典型的容器。这些容器就是通过kobject连接起来了,形成了一个树状结构。这个树状结构就与/sys相对应。

kobject结构为一些大的数据结构和子系统提供了基本的对象管理,避免了类似任务的重复实现。这些任务包括

-对象引用计数:通常一个内核对象被创建时,不知道该对象的存活时间,跟踪此对象生命周期的一个方法是使用引用计数。当内核中没有代码持有该对象的引用时,该对象将结束自己的有效生命周期,并且可以被删除。

-维护对象链表:从整体上看,设备模型是一个友好而复杂的数据结构,通过其间的大量连接而构成一个多层次的体系结构。kobject实现了该结构并把他们链接在一起。

-热插拔事件处理:当系统中的硬件被热插拔时,在kobject子系统控制下,将产生事件以通知用户空间。

-在用户空间的表示:在sysfs中显示的每一个对象,都对应一个kobject,它被用来与内核交互并创建它的可见表述。

&1. Kobject结构定义为:

kernel/include/linux/kobject.h

struct kobject {

const char *name;指向设备名称的指针

struct list_head

struct kobject *parent;指向父节点的指针

struct kset *kset;所属kset的指针

struct kobj_type *ktype;指向其对象类型描述符的指针

struct sysfs_dirent *sd;指向的sysfs中的目录实体

struct kref kref;该对象的引用计数

unsigned int state_initialized:1;

unsigned int state_in_sysfs:1;

unsigned int state_add_uevent_sent:1;

unsigned int state_remove_uevent_sent:1;

unsigned int uevent_suppress:1;

};

kobject作为Linux设备模型中最基本的对象,它的功能是提供引用计数和维持父子(parent)结构、平级(sibling)目录关系,下面的device、device_driver等各对象都是在kobject基础功能之上实现的。

struct kref内含一个atomic_t类型用于引用计数,parent是单个指向父节点的指针,entry用于父kset以链表头结构将kobject结构维护成双向链表。

kobject相关的操作函数

void kobject_init(struct kobject * kobj);kobject初始化函数。

int kobject_set_name(struct kobject *kobj, const char *format, ...);设置指定kobject的名称。

struct kobject *kobject_get(struct kobject *kobj);将kobj对象的引用计数加1,同时返回该对象的指针。

void kobject_put(struct kobject * kobj);将kobj对象的引用计数减1,如果引用计数降为0,则调用kobject release()释放该kobject对象。

int kobject_add(struct kobject * kobj);将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录各级kobject的引用计数,在其parent指向的

目录下创建文件节点。

void kobject_del(struct kobject * kobj);从Linux设备层次(hierarchy)中删除kobj对象。

§2 Kobj type

struct kobj_type {

void (*release)(struct kobject *kobj);当kobject的引用计数为0的时候,本函数将被调用来释放kobject资源。

const struct sysfs_ops *sysfs_ops;指向sysfs操作表

struct attribute default_attrs;执行sysfs文件系统缺省属性列表

const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);用来标记kobjects和sysfs入口

const void *(*namespace)(struct kobject *kobj);

};

struct sysfs_ops {

ssize_t (*show)(struct kobject *, struct attribute *,char *);当用户态读取属性时,此函数编码指定属性值存入buffer中返回给用户态。

ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);此函数用于存储用户态传入的属性值。

};

struct attribute {

const char *name;它以文件的形式输出到sysfs的目录当中,在kobject对应的目录下面。name就是属性文件的名字

mode_t mode;

};

§3. kset

kset最重要的是建立上层和下层的关联性。kobject也会利用它来找到自己属于那一个类型,之后在/sys下建立正确的目录位置。而kset的优先权比较高,kobject会利用自已的*kset找到自已所属的kset。

Kobject通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合,在内核数据结构上它也是由内嵌一个kboject实现,因而它同时也是一个kobject (面向对象OOP概念中的继承关系),具有

kobject的全部功能,在内核中kset的数据结构定义为:

struct kset {

struct list_head list;用于连接该kset中所有kobject的链表头

spinlock_t list_lock;

struct kobject kobj;嵌入的kobject

const struct kset_uevent_ops *uevent_ops;关于uevent的操作函数

};

kset的相关操作函数:

void kset_init(struct kset *kset);完成指定kset的初始化

int kset_register(struct kset *kset);完成kset的注册

void kset_unregister(struct kset *kset);完成kset的注销

void kset_put(struct kset *k)减少kset对象的引用计数

kset *kset_get(struct kset *k)增加kset对象的引用计数

包含在kset中的所有kobject被组织成一个双向循环链表,list域正是该链表的头,Kset数据结构还内嵌了一个kobject对象(由kobj域表示),所有属于这个kset的kobject对象的parent域均指向这个内嵌的对象。

此外,kset还依赖于kobj维护引用计数:kset的引用计数实际上就是内嵌的kobject对象的引用计数。

kset与kobject的简要关系图如下所示:

§4.bus

系统中总线由struct bus_type描述,定义为:

kernel/include/linux/device.h

struct bus_type {

const char *name;总线类型的名字

struct bus_attribute *bus_attrs;总线属性

struct device_attribute *dev_attrs;设备属性

struct driver_attribute *drv_attrs;驱动程序属性

int (*match)(struct device *dev, struct device_driver *drv);

int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

int (*probe)(struct device *dev);

int (*remove)(struct device *dev);

void (*shutdown)(struct device *dev);

int (*suspend)(struct device *dev, pm_message_t state);

int (*resume)(struct device *dev);

const struct dev_pm_ops *pm;

struct subsys_private *p;

};

其中subsys_private这个结构是链接bus对象的一个关键结构,

(kernel/drivers/base/base.h)

其定义为:

truct subsys_private {

struct kset subsys;此总线所属的kset

struct kset *devices_kset;指向device所属的kset的指针

struct kset *drivers_kset;指向drivers所属的kset的指针

struct klist klist_devices;设备链表

struct klist klist_drivers;驱动链表

struct blocking_notifier_head bus_notifier;

unsigned int drivers_autoprobe:1;

struct bus_type *bus;

struct list_head class_interfaces;

struct kset glue_dirs;

struct mutex class_mutex;

struct class *class;

};

每个bus_type对象内嵌一个subsys_private结构体,此结构体代替了之前的subsystem对象,将与总线以及创建kobject层次结构相关的对象信息都放在此结构中。每个bus_type对象对应/sys/bus目录下的一个子目录,如PCI

总线类型对应于/sys/bus/pci。在每个这样的目录下都存在两个子目录:devices和drivers(分别对应于subsys_private结构中的devices_kset和drivers_kset域)。其中klist_devices描述连接在该总线上的所有设备,

而klist_drivers则描述与该总线关联的所有驱动程序。与device_driver对象类似,bus_type结构还包含几个函数(match()、uevent()等)处理设备与驱动的匹配和uevent事件,而很关键的dev_pm_ops结构体中的函数用来

操作电源管理事件。

§5 device

系统中的任一设备在设备模型中都由一个device对象描述,其对应的数据结构struct device

定义为:

struct device {

struct device *parent;此设备的父设备

struct device_private *p;这个数据结构很重要,用来定位device所处的层次结构

struct kobject kobj;设备内嵌的kobject

const char *init_name;设备的名字

struct device_type *type;设备的类型

struct mutex mutex;同步调用它的驱动

struct bus_type *bus;设备所在的总线类型

struct device_driver *driver;分配给这个设备的驱动

void *platform_data;具体平台数据,设备核心接触不到它

struct dev_pm_info power;电源管理相关函数

struct dev_archdata archdata;与arch相关的数据

dev_t devt;创建sysfs下的dev目录

struct list_head devres_head;将设备对象加到链表中

struct class *class;所属的类

struct klist_node knode_class;

void (*release)(struct device *dev);

/* some fields omitted*/

};

struct device_private {除了驱动核心之外,没有什么地方会使用这些域,所以称为私有private。

struct klist klist_children;该设备所管理的所有子设备

struct klist_node knode_parent;在兄弟链表中的node

struct klist_node knode_driver;在driver链表中的node

struct klist_node knode_bus;在bus链表中的node

void *driver_data;指向driver具体信息的私有指针

struct device *device;回指device结构

};

内核提供了相应的函数用于操作device对象,device相关的操作函数如下:

int device_register(struct device *dev);将一个新的device对象插入设备模型,并在sys/devices下面创建一个对应的目录。

void device_unregister(struct device *dev);完成与device_register相反的动作,注销设备对象。

void device_initialize(struct device *dev);对device的结构进行初始化,可以作为device_register的第一部分

int device_add(struct device *dev);通过调用kobject_add将devcie添加到kobject的层次结构中,并添加到它的全局以及兄弟设备链表中,配置到相应的子系统。它是device_register的第二部分。

void device_del(struct device *dev);实现与device_add相反的操作。

int device_for_each_child(struct device *dev, void *data,int (*fn)(struct device *dev, void *data));遍历每个孩子设备,对他们执行同样的操作。

struct device *device_find_child(struct device *dev, void *data,int (*match)(struct device *dev, void *data));遍历设备链表,为了找到某个具体的设备。

int device_rename(struct device *dev, const char *new_name);为设备重新命名

int device_move(struct device *dev, struct device *new_parent,enum dpm_order dpm_order);把设备重新放到其他的parent下。

通常device结构不单独使用,而是包含在更大的结构中作为一个子结构使用,比如platform设备的struct platform_device,其中的device域就是一个device对象。

§6. driver

系统中的每个驱动程序由一个device_driver对象描述,对应的数据结构定义为:

struct device_driver {

const char *name;设备驱动程序的名称

struct bus_type *bus;该驱动所管理的设备挂接的总线类型

struct module *owner;

const char *mod_name;被用来内建module

bool suppress_bind_attrs;通过sysfs来禁用bind/unbind

int (*probe) (struct device *dev);指向设备探测函数,用于探测设备是否可以被该驱动程序管理

int (*remove) (struct device *dev);用于移除设备的函数

void (*shutdown) (struct device *dev);

int (*suspend) (struct device *dev, pm_message_t state);

int (*resume) (struct device *dev);

const struct attribute_group groups;

const struct dev_pm_ops *pm;关于电源管理的各种操作函数

struct driver_private *p;确定驱动所代表的kobject在kobject层次结构中所处的位置

};

driver_private
{

struct kobject_kobj; driver 所内嵌的kobject对象

struct klist klist_devices;该驱动所管理的设备链表头

struct klist_node knode_bus;在bus链表中的node

struct module_kobject *mkobj;

struct device_driver *driver;指向device_driver设备

};

与driver相关的一些操作函数:

int driver_register(struct device_driver *drv);向设备模型插入新的driver对象,同时在sysfs文件系统中创建对应的目录。

void driver_unregister(struct device_driver *drv);注销驱动,实现与driver_ register相反的操作

struct device_driver *get_driver(struct device_driver *drv);增加driver对象的引用计数

void put_driver(struct device_driver *drv);减少driver对象的引用计数

struct device_driver *driver_find(const char *name,struct bus_type *bus);在某个bus类型上找到名字为name的driver

int driver_probe_done(void);确定driver探测是否完成

void wait_for_device_probe(void);等候设备探测函数

§7. sysfs_dirent在上面的kobject象中可以看到有指向sys_dirent的指针,在sysfs中每一个node都是用sysfs_dirent来统一表示的。

在每个文件系统的私有数据上(dentry->d_fsdata),使用了称为struct sysfs_dirent的结构用于表示/sys中的每一个目录项。

struct sysfs_dirent {
atomic_t s_count;
atomic_t s_active;
struct sysfs_dirent *s_parent;
struct sysfs_dirent *s_sibling;
const char *s_name;

const void *s_ns; 命名空间的tag
union {
struct sysfs_elem_dir s_dir;
struct sysfs_elem_symlink s_symlink;
struct sysfs_elem_attr s_attr;
struct sysfs_elem_bin_attr s_bin_attr;
};

unsigned int s_flags;
unsigned short s_mode;
ino_t s_ino;
struct sysfs_inode_attrs *s_iattr;
};

在union共用体中,包含四种不同的结构,分别是目录、富豪链接文件、属性文件、二进制属性文件;其中目录类型可以对应kobject,在相应的s_dir中也有对kobject的指针,因此在内核数据结构中,kobject与sysfs_dirent是互相引用的。

3.建立设备模型时的组织结构

在对kobject、kset等上述结构分析以后,我们想要知道的是在对设备进行统一管理时,他们内部的组织结构如何,如下图所示:

而对device、device_driver、bus等结构的综合概括如下:

类型所包含的内容对应内核数据结构对应/sys项
设备(Devices)设备是此模型中最基本的类型,以设备本身的连接按层次组织struct device/sys/devices/*/*/.../
设备驱动(Device Drivers)在一个系统中安装多个相同设备,只需要一份驱动程序的支持struct device_driver/sys/bus/pci/drivers/*/
总线类型(Bus Types)在整个总线级别对此总线上连接的所有设备进行管理struct bus_type/sys/bus/*/
设备类别(Device Classes)这是按照功能进行分类组织的设备层次树;如USB接口和PS/2接口的鼠标都是输入设备,都会出现在/sys/class/input/下struct class/sys/class/*/


在/sys根目录之下的都是kset,它们组织了/sys的顶层目录视图;
在部分kset根目录下有二级或更深层次的kset;
每个kset目录下再包含着一个或多个kobject,这表示一个kset集合所包含的kobjcet结构体

在kobject下有属性(attrs)文件和属性组(attr_group),属性组就是组织属性的一个目录,它们一起向用户层提供了表示和操作这个kobject的属性特征的接口;

在kobject下还有一些符号链接文件,指向其他的kobject,这些符号链接文件用于组织上面的device,device_driver,bus_type,class,module之间的关系;

不同类型的设备或驱动的kobject都有不同的属性,不同驱动程序支持的sysfs接口也有不同的属性文件;而相同类型的设备上有很多相同的属性文件。

二、sys文件系统

1.sys文件系统简介

sysfs是Linux内核中设计较新的一种虚拟的基于内存的文件系统,它的作用与proc有些类似,但除了与proc相同的具有查看和设定内核参数功能之外,还有为Linux统一设备模型作为管理之用。相比于proc文件系统,使用sysfs导出内核数据的方式更为统一,并且组织的方式更好,它的设计从proc中吸取了很多教训。sysfs的挂载点在/sys目录结构、其与Linux统一设备模型关系存在着密切的关系,可以说sysfs是在Linux统一设备模型的开发过程中的一项副产品,为了将这些有层次结构的设备以用户程序可见的方式表达出来,人们很自然想到了利用文件系统的目录树结构(UNIX方式思考问题的基础,一切都是文件!)来进行表述。

2.sys文件系统目录介绍 /sys: block class devices fs kernel power bus dev firmware module等

这些目录展示了内核对各种设备进行统一管理的模型和方式:

/sys/devices
这是内核对系统中所有设备的分层次表达模型,此目录下存放着向系统注册的所有设备,也是/sys文件系统管理设备的最重要的目录结构。

/sys/dev

这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(sys/devices)下的符号链接文件。

/sys/bus

内核设备是按总线类型分层放置的目录结构,devices下的所有设备以及相应的驱动都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接以及相应的驱动。

/sys/class

这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在/sys/class/input之下,而不论它们是以何种总线连接到系统。

/sys/firmware

系统加载固件机制的对用户空间的接口

/sys/fs

这里按照设计是用于描述系统中的所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点,但目前只有fuse等极少数文件系统支持sysfs接口,一些传统的虚拟文件系统(VFS)层次控制参数仍然在sysctl

(proc/sys/fs)接口中;

/sys/kernel

这里是内核所有可调整参数的位置,目前只有uevent_helper,kexe_loaded,mm 和新式的slab分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于sysctl(proc/sys/kernel)接口中;

/sys/module
这里有系统中所有模块的信息,不论这些模块是以内连inline方式编译到内核映像文件中还是编译为外部模块(ko文件),都可能会出现在/sys/module中。
*编译为外部模块(ko文件)在加载后会出现对应的/sys/module/<module_name>,并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等;
*编译为内联方式的模块则只有当它有非0属性的模块参数时,会出现对应的 /sys/module/<module_name> ,这些模块的可用参数会出现在/sys/module/<module_name>/parameters/<param_name>中;

o如/sys/module/printk/parameters/time 这个可读写参数控制着内联模块printk在打印内核消息时是否加上时间前缀;

o所有内联模块的参数也可以由"<module_name>.<param_name>=<value>"的形式写在内核启动参数上,如启动内核时加上参数"printk.time=1" 与向/sys/module/printk/parameters/time 写入1的

效果相同;

*没有非0属性参数的内联模块不会出现于此。

/sys/pwoer

这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

  1. sysfs与kobject设备模型的映射关系

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值