Kobject 与 sysfs 文件系统框架的分析
时间:2018-05-14作者:华清远见
1、设备模型 2.6内核增加了一个引人注目的新特性------统一设备模型 设备模型提供了一个独立的机制专门来表示设备,并描述在系统中的拓扑结构 (1)代码重复最小化 (2)可以列出系统中所有的设备,观察到他们的状态,并且查看他们连接的总线 (3) 可以将设备和其对应的驱动联系起来 (4) 可以按照设备类型进行分类 2、kobject 设备模型的核心部分就是kobject
struct kobject { const char *name; /*指向kobject的名字*/ struct list_head entry; struct kobject *parent/*指向父kobject 实现层次结构*/ struct kset *kset; struct kobj_type *ktype; struct sysfs_dirent *sd;/*指向sysfs_dirent结构体 这个结构体就表示kobject对象的层次结构*/ struct kref kref;/*提供引用计数 其核心成员是一原子型变量,用来表示内核对象的引用计数 内核通过该成员追踪内核对象生命周期*/ unsigned int state_initialized:1; unsigned int state_in_sysfs:1;/*是否已经加入sysfs*/ unsigned int state_add_uevent_sent:1; unsigned int state_remove_uevent_sent:1; }; 3、ktype
kobject对象被关联到一种特殊的类型 即ktype(kernel object type的缩写) ktype由kobj_type结构体来表示,定义于头文件 struct kobj_type { void (*release)(struct kobject *kobj); struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; }; 1)ktype的存在是为了描述一族kobject所具有的普遍特性,实现同类的kobject都能共享相同的特性 2)release指针指向在kobject引用计数减至零时调用析构函数,该函数负责释放所有kobject使用的内存和其他相关的清理工作 3)defaults_attrars 结构体数组定义了所有具有相同类型的kobject对象的属性 4)sysfs文件系统根据对应的kobject属性来创建文件 4、kset 1)kset是kobject对象的集合体,将相关的kobject集合到一起 2)具有相同ktype的kobject可以被分组到不同的kset 5、管理和操作kobject kobject一般都被嵌入到设备结构体中 (1)kobject的初始化一个kobject对象 void kobject_init(struct kobject *kobj,struct kobj_type *ktype) (2)定义并初始化 struct kobject *kobject_create(void) 6、引用计数 1)kobject通过引用计数控制对象的有效生命周期 (1)初始化后kobject的引用计数置为1 (2)当引用计数为0时,则表示设备已经卸载,不能在操作对应的设备 2)操作引用计数的接口 (1)增加一个引用计数 struct kobject *kobject_get(struct kobject *kobj) (2)减少一个引用计数 void kobject_put(struct kobject *kobj) 3)描述引用计数的结构kref kobject计数是通过kref实现 struct kref{ atomic_t refcount } (1)初始化 void kref_init(struct kref *kref) (2)增加一个计数 void kref_get(struct kref *kref) (3)减一个计数 void kref_put(struct kref *kref) 代码: (1)创建一个kobject对象并初始化 (2) 定制自己的kobject对象创建与释放函数 7、sysfs sysfs文件系统是一个处于内存中的虚拟文件系统,用文件系统的方式提供kobject对象层次结构的视图 bus:提供一个系统总线视图 dev:提供已经注册设备节点的视图 device:系统中设备结构体视图 class:给用户的视图 通过对device实际设备目录的符号链接 kernel:包含内核配置项和状态信息 fs:已经注册文件系统的视图 class与devices 一个是高层概念 给用户的视图 一个底层物理设备 给内核的视图 8、sysfs添加和删除kobject 将kobject对象映射到sysfs文件系统中,产生对应的文件 int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) 从sysfs文件系统中删除一个kobject对应文件目录,需要使用函数kobject_decl() void kobject_del(struct kobject *kobj) int kobject_set_name(struct kobject *kobj, const char *fmt, ...)//设置kobject对象在sysfs中的名字 int sysfs_create_dir(struct kobject * kobj)//在sysfs文件系统中创建目录 void sysfs_remove_dir(struct kobject * kobj)//在sysfs文件系统中删除目录 struct kobject *kobject_get(struct kobject *kobj)//可以用来获取kobj-parent 代码3:将对应的kobject映射到对应sysfs文件系统中,增加my_kobject_add()函数my_kobject_del()函数 代码4:将当前的kobject对象增加parent节点 增加my_kobject_create_and_add()函数 9、向sysfs添加文件 1)默认的文件集合通过kobject和kset中的ktype字段提供 2)具有相同属性的kobject导入到sysfs文件系统中的文件也具有相同的属性 3)默认文件属性由kobj_type结构来描述 struct kobj_type { void (*release)(struct kobject *kobj); struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; }; 4)kobj_type结构中包含默认文件集合中所有文件的属性, default_attrs 默认文件属性结构体数组 struct attribute { const char *name; 属性名称,在sysfs文件系统中显示的名字 struct module *owner;所属模块 如果存在 mode_t mode;权限 }; sysfs 文件系统根据默认文件属性建立对应的文件 5)增加新的文件 (1)根据新文件属性在sysfs文件系统创建文件 int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) { BUG_ON(!kobj || !kobj->sd || !attr); return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR); } 6)对sysfs文件系统中的文件进行读写 struct sysfs_ops { 在读sysfs文件时该方法会调用 ssize_t (*show)(struct kobject *, struct attribute *,char *); 在写sysfs文件时该方法被调用 ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); }; struct kset *kset_create_and_add(const char *name,struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)//创建一个kset集合 10、创建新属性 sysfs_create_link():增加新的链接 sysfs_remove_file():删除新的文件 sysfs_remove_link() 删除链接文件 代码5:在sysfs文件系统中增加文件 代码6:在sysfs文件系统中增加多个文件,并设置新文件的属性 代码7: 在sysfs文件系统中针对每个文件设置相应的的操作方法show()与store() 代码8:底层实现led驱动并关联到kobject对象中 11、uevent 1)kset是属于一组kobject的集合 struct kset { struct list_head list; spinlock_t list_lock; struct kobject kobj; const struct kset_uevent_ops *uevent_ops; }; kset_uevent_ops : 当kset中的kobject对象发生状态变化需要通知用户空间 struct kset_uevent_ops { int (* const filter)(struct kset *kset, struct kobject *kobj); const char *(* const name)(struct kset *kset, struct kobject *kobj); int (* const uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env); }; 2)kset相关的API void kset_init(struct kset *k) 初始化kset对象 int kset_register(struct kset *k) 初始化并向系统注册一个kset对象 static void kobject_init_internal(struct kobject *kobj) 注册kset对象 notes: .kset对象本身也是一个kobject内核对象,在sysfs文件系统中生成一个新的目录 .注册kset对象,内核编译启用了CONFIG_HOTPLUG,则需要就这一事件通知用户空间(内核配置文件中可以查询) .事件通知由kobject_uevent完成 .不属于kset的kobject对象不能完成事件通知 .kobject之间通过parent成员实现层次关系,当kobject的parent为NULL时,就会把kobj->kset->kobj作为kobj的parent struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj) 创建一个kset对象并添加到sysfs文件系统中 3)hotplug(热插拔) 当一个设备动态加入系统时,设备驱动程序可以检查到设备,并通过通知的方式告知用户空间 通知用户空间的方式一般有两种:udev 与 /sbin/hotplug,现在使用更多的是udev udev 的实现基于内核中的网络机制,通过标准的socket接口来监听来自内核的网络广播包,并对接收的包进行分析处理 4)hotplug相关的API int kobject_uevent(struct kobject *kobj, enum kobject_action action) 发送一个 event给用户空间,以网络数据包的形式发送给用户空间应用程序 @action : 发送event类型 enum kobject_action { KOBJ_ADD, KOBJ_REMOVE, KOBJ_CHANGE, KOBJ_MOVE, KOBJ_ONLINE, KOBJ_OFFLINE, KOBJ_MAX }; kobject_uevent_env() sprintf(scratch, "%s@%s", action_string, devpath); @273L 传递给用户空间event数据的内容 "事件类型@设备路径" example: "add@/kset-test/kobject-test/" uevent一般与应用程序结合起来使用,一般可以接收并处理uevent的应用程序有udev or mdev udev工具通过netlink获取内核发出的uevent消息,并且处理,加载相应的驱动或者在/dev/目录下生成对应的设备结点 udev服务启动后,会扫描/sys目录下所有具有uevent属性文件,在进行相应的处理 uevent的触发被封装到设备模型的操作中,当添加设备的时则会发送一个uevent(user event)事件, udev 工具会通过netlink获取uevent消息 ,然后进行加载驱动或者在/dev/目录下生成对应的设备结点 int device_add() { ...... kobject_uevent(&dev->kobj, KOBJ_ADD); ...... } 5)netlink机制 (1)netlink机制的特点 .netlink是一种特殊的socket .netlink可以实现内核与应用程序进行双向传输通讯,并且使用socket()API进行交互 .netlink是一种异步通讯方式,在内核与用户态进行传递的消息保存在socket缓冲区数据队列中,发送消息只是把消息保存在接收者的socket的接收队列中,而不需要等待接收者收到消息 (2)使用socket()创建netlink套接字 int socket(int domain, int type, int protocol); @domain:协议族 AF_NETLINK or PF_NETLINK @type :socket类型 SOCK_DGRAM or SOCK_RAW @protocol : 协议类型 NETLINK_KOBJECT_UEVENT (3)netlink地址结构 struct sockaddr_nl { __kernel_sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* zero */ __u32 nl_pid; /* port ID */ __u32 nl_groups; /* multicast groups mask */ }; @nl_family : AF_NETLINK 协议族 @nl_pad : 填0 @nl_pid : 进程pid ,可以通过getpid()获取 (3)使用setsockopt()设置套接字选项 setsockopt(sd, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); @SO_RCVBUFFORCE:设置或者获取缓冲区的大小 (4)使用bind()绑定地址结构 retval= bind(sd, (void*)&snl, sizeof(struct sockaddr_nl)) (5)读取netlink接收缓冲区的内容 read(sd, buf, sizeof(buf)); 相关资讯
发表评论
|