kernel管理的设备千差万别,但最终都归于统一设备模型的管理, 即sysfs,理解了sysfs,就可以管窥蠡豹,对整个DeviceDriver子系统有一个清晰的认识。sysfs的实质是以文件系统的形式展示内核中的Device Driver信息,因此,理解sysfs可以分为2个维度的问题:
- sysfs的Inside Tree的组织思想
- sysfs的Outside Tree的目录结构
本文主要讨论内部树的基础,kobject,kset和ktype的定义,联系及其常用API
kobject
list_head是内核所有链式存储数据结构的基础,可以看作这些struct的父类,基于同样的思想,在Device Driver Subsystem中, 这个全局父类的角色由kobject担任,当然,kobject本身也是一个list_head的子类。kobject为Device Driver管理的对象提供了最高层次的抽象,无论是Device对象还是Driver对象,都可以看作kobject的父类,sysfs通过kobject,就可以管理系统中所有的Device对象和Driver对象。
//include/linux/kobject.h 63 struct kobject { 64 const char *name; 65 struct list_head entry; 66 struct kobject *parent; 67 struct kset *kset; 68 struct kobj_type *ktype; 69 struct kernfs_node *sd; 70 struct kref kref; 71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE 72 struct delayed_work release; 73 #endif 74 unsigned int state_initialized:1; 75 unsigned int state_in_sysfs:1; 76 unsigned int state_add_uevent_sent:1; 77 unsigned int state_remove_uevent_sent:1; 78 unsigned int uevent_suppress:1; 79 };
–64–>kobject对象的名字
–65–>kobject对象之间的连接件
–66–>该kobject对象的父kobject对象
–67–>该kobject所属的kset
–68–>该kobject附属的ktype
–69–>该kobject在sysfs中的形式,比如符号链接,目录以及属性等信息
–70–>引用计数成员,本质是一个原子变量,用于决定何时释放对象
–74–>1bit,如果该kobject对象已经被初始化,则为1
–75–>1bit,如果该kobject对象已经被添加到sysfs中,则为1
–76–>1bit,如果该kobject对象已经发送过uevent add事件到用户空间,则为1
–77–>1bit,如果该kobject对象已经发送过uevent remove事件到用户空间,则为1
–78–>1bit,如果该kobject对象”抑制”发送事件到用户空间,则为1
kobj_init()
//lib/kobject.c 314 void kobject_init(struct kobject *kobj, struct kobj_type *ktype) 315 { 316 char *err_str; ... 333 kobject_init_internal(kobj); 334 kobj->ktype = ktype; 335 return; 340 } //lib/kobject.c 187 static void kobject_init_internal(struct kobject *kobj) 188 { 189 if (!kobj) 190 return; 191 kref_init(&kobj->kref); 192 INIT_LIST_HEAD(&kobj->entry); 193 kobj->state_in_sysfs = 0; 194 kobj->state_add_uevent_sent = 0; 195 kobj->state_remove_uevent_sent = 0; 196 kobj->state_initialized = 1; 197 }
–334–>初始化ktype成员
–191–>初始化引用计数
–192–>初始化list_head连接件
–193-196–>初始化kobject状态
kobject_add()
添加kobject实例到内核数据结构的接口
//lib/kobject.c 382 int kobject_add(struct kobject *kobj, struct kobject *parent, 383 const char *fmt, ...) 384 { 385 va_list args; 386 int retval; ... 398 va_start(args, fmt); 399 retval = kobject_add_varg(kobj, parent, fmt, args); 400 va_end(args); 401 402 return retval; 403 } 343 static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, 344 const char *fmt, va_list vargs) 345 { 346 int retval; 348 retval = kobject_set_name_vargs(kobj, fmt, vargs); ... 353 kobj->parent = parent; 354 return kobject_add_internal(kobj); 355 } 200 static int kobject_add_internal(struct kobject *kobj) 201 { 202 int error = 0; 203 struct kobject *parent; 213 214 parent = kobject_get(kobj->parent); 215 216 /* join kset if set, use it as parent if we do not already have one */ 217 if (kobj->kset) { 218 if (!parent) 219 parent = kobject_get(&kobj->kset->kobj); 220 kobj_kset_join(kobj); 221 kobj->parent = parent; 222 } 228 229 error = create_dir(kobj); 230 if (error) { ... 245 } else 246 kobj->state_in_sysfs = 1; 248 return error; 249 }
–382–>给kobject命名的格式字符串
–348–>初始化kobject->name成员
–353–>初始化kobject->parent成员
–214–>将kobject->parent的引用计数,同时暂存kobject->parent
–219–>将kobject的parent设为kset->kobj
–220–>kobj->kset引用计数+1,将kobj通过kobj->entry链接到kobj->kset->list队列尾,入队用spinlock
–221–>初始化kobject->parent
–229–>在sysfs下创建相应目录
–246–>如果sysfs目录创建成功,设置kobject标志位
kobject_uevent()
当在一个kobject发生某些事件,希望上报uevent事件到用户空间的时候,就需要调用这个函数,
这个函数是对kobject_uevent_env()的再次封装。
uevent事件的本质将一组环境变量值封装到kobj_uevent_env并通过netlink或udevhelper来发送给用户空间
支持的事件如下:
//include/linux/kobject.h 53 enum kobject_action { 54 KOBJ_ADD, 55 KOBJ_REMOVE, 56 KOBJ_CHANGE, 57 KOBJ_MOVE, 58 KOBJ_ONLINE, 59 KOBJ_OFFLINE, 60 KOBJ_MAX 61 };
–54–>一个kobject对象被添加到内核,比如hotpulg一个设备
–55–>一个kobject对象从内核被移除,比如hotunplug一个设备
–56–>一个kobject对象的属性发生了改变,eg, hd_part分区表发生了改变
–57–>一个kobject对象的位置发生了改变,或者设备名发生了改变
–58-59–>一个kobject对象在线/离线,比如一个CPU
–60–>不是一个事件,仅表示事件的个数
//include/linux/kobject.h 123 struct kobj_uevent_env { 124 char *argv[3]; 125 char *envp[UEVENT_NUM_ENVP]; 126 int envp_idx; 127 char buf[UEVENT_BUFFER_SIZE]; 128 int buflen; 129 };
–124–>XXX
–125–>每一对KV的起始地址,UEVENT_NUM_ENVP==32,即一共最多32对KV
–126–>每增加一对KV,envp_idx++,所以这个域表示当前对象中的KV对个数
–127–>存储所有KV的buf,一共2048个字节,环境信息使用”KV键值对”的形式表示,所有的KV对都被封装在同一个buf中,使用”\0”分隔。
–128–>实际使用的buf长度
//lib/kobject_uevent.c 164 int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, 165 char *envp_ext[]) 166 { 167 struct kobj_uevent_env *env; 168 const char *action_string = kobject_actions[action]; 169 const char *devpath = NULL; 170 const char *subsystem; 171 struct kobject *top_kobj; 172 struct kset *kset; 173 const struct kset_uevent_ops *uevent_ops; 174 int i = 0; 175 int retval = 0; 176 #ifdef CONFIG_NET 177 struct uevent_sock *ue_sk; 178 #endif 179 180 pr_debug("kobject: '%s' (%p): %s\n", 181 kobject_name(kobj), kobj, __func__); 182 183 /* search the kset we belong to */ 184 top_kobj = kobj; 185 while (!top_kobj->kset && top_kobj->parent) 186 top_kobj = top_kobj->parent; 187 188 if (!top_kobj->kset) { 192 return -EINVAL; 193 } 194 195 kset = top_kobj->kset; 196 uevent_ops = kset->uevent_ops; 197 198 /* skip the event, if uevent_suppress is set*/ 199 if (kobj->uevent_suppress) { 203 return 0; 204 } 205 /* skip the event, if the filter returns zero. */ 206 if (uevent_ops && uevent_ops->filter) 207 if (!uevent_ops->filter(kset, kobj)) { 211 return 0; 212 } 213 214 /* originating subsystem */ 215 if (uevent_ops && uevent_ops->name) 216 subsystem = uevent_ops->name(kset, kobj); 217 else 218 subsystem = kobject_name(&kset->kobj); 219 if (!subsystem) { 223 return 0; 224 } 225 226 /* environment buffer */ 227 env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); 230 231 /* complete object path */ 232 devpath = kobject_get_path(kobj, GFP_KERNEL); 237 238 /* default keys */ 239 retval = add_uevent_var(env, "ACTION=%s", action_string); 242 retval = add_uevent_var(env, "DEVPATH=%s", devpath); 245 retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem); 248 249 /* keys passed in from the caller */ 250 if (envp_ext) { 251 for (i = 0; envp_ext[i]; i++) { 252 retval = add_uevent_var(env, "%s", envp_ext[i]); 255 } 256 } 257 258 /* let the kset specific function add its stuff */ 259 if (uevent_ops && uevent_ops->uevent) { 260 retval = uevent_ops->uevent(kset, kobj, env); 267 } 275 if (action == KOBJ_ADD) 276 kobj->state_add_uevent_sent = 1; 277 else if (action == KOBJ_REMOVE) 278 kobj->state_remove_uevent_sent = 1; 279 280 mutex_lock(&uevent_sock_mutex); 281 /* we will send an event, so request a new sequence number */ 282 retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum); 288 #if defined(CONFIG_NET) 289 /* send netlink message */ 290 list_for_each_entry(ue_sk, &uevent_sock_list, list) { 291 struct sock *uevent_sock = ue_sk->sk; 292 struct sk_buff *skb; 293 size_t len; 294 295 if (!netlink_has_listeners(uevent_sock, 1)) 296 continue; 297 298 /* allocate message with the maximum possible size */ 299 len = strlen(action_string) + strlen(devpath) + 2; 300 skb = alloc_skb(len + env->buflen, GFP_KERNEL); 301 if (skb) { 302 char *scratch; 303 304 /* add header */ 305 scratch = skb_put(skb, len); 306 sprintf(scratch, "%s@%s", action_string, devpath); 307 308 /* copy keys to our continuous event payload buffer */ 309 for (i = 0; i < env->envp_idx; i++) { ... 313 } 314 315 NETLINK_CB(skb).dst_group = 1; 316 retval = netlink_broadcast_filtered(uevent_sock, skb,0, 1, GFP_KERNEL,kobj_bcast_filter,kobj); 322 retval = 0; 323 } else 324 retval = -ENOMEM; 325 } 326 #endif 327 mutex_unlock(&uevent_sock_mutex); 328 329 #ifdef CONFIG_UEVENT_HELPER ... 354 #endif 355 356 exit: 359 return retval; 360 }
–185–>逐级向上,直到找到kobject对象的kset或已经没有parent了
–188–>对上一级的结果进行判断,如果最终是没有kset,返回错误
–195-196–>保存kset及其kset_uevent_ops
–198-226–>逐项检查是否具备发送uevent条件
–198–>如果uevent_suppress位被设置,则直接返回
–206–>如果所属的kset->kset_uevent_ops->filter()返回0,则直接返回
–215–>试图获取所属的kset的name作为subsystem的名字,获取失败则使用kobject本身的name
–216–>具备发送uevent条件,分配env buf
–232–>获取kobject路径,即在sysfs中的位置
–238-245–>env->buf中填入ACTION、DEVPATH、SUBSYSTEM的key及其value
–250–>调用者自定义的KV值
–259–>添加所属kset的相关信息
–275–>如果ACTION是ADD或REMOVE,要对kobject中的相应bit置位
–282–>填入SEQNUM的key及其value,这个值是事件序号
–288–>env在前文已经准备就绪,如果系统中使用netlink,就在这里发送uevent事件
–290–>遍历所有注册到系统中的netlink_socket
–295–>如果没有进程监视这个netlink_sock,continue
–300–>分配skbuf接收环境变量
–316–>广播这个skbuf,listener将会侦听到这个netlink_message
–329–>对于使用uevent_helper机制的内核,在这里向用户空间上报uevent
综上,有以下情况不会上报uevent事件:
- kobjetc->uevent_suppress为1
- kset->filter()返回0
- NULL==kset->name
- kset->name()返回NULL。
kset
在sysfs中,用户空间看到一个目录对应内核空间的一个kobject对象,一个文件对应kobject的一个关联attribute(ktype),kset是对kobject的封装。
//include/linux/kobject.h 167 struct kset { 168 struct list_head list; 169 spinlock_t list_lock; 170 struct kobject kobj; 171 const struct kset_uevent_ops *uevent_ops; 172 };
–168–>kset对象和它附属的所有kobject对象的连接件
–169–>操作附属kobject需要的锁
–170–>内嵌的kobject,附属的kobject的parent指向kset的kobject,通过kobject对象之间的parent-child关系组织成属性结构供sysfs使用(其实与kset无关)
–171–>针对该kset附属的所有的kobject,一旦有事件发生(eg,add or remove)都会调用这个接口,eg. 这个接口可以影响热插拔上报到用户空间的表现
ktype
kobj_type用来表征一个kobject对象的”类型”
//include/linux/sysfs.h 184 struct sysfs_ops { 185 ssize_t (*show)(struct kobject *, struct attribute *, char *); 186 ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); 187 }; //include/linux/kobject.h 115 struct kobj_type { 116 void (*release)(struct kobject *kobj); 117 const struct sysfs_ops *sysfs_ops; 118 struct attribute **default_attrs; 119 const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); 120 const void *(*namespace)(struct kobject *kobj); 121 };
–116–>附属到的kobject被释放时回调的方法
–117–>读写相应kobject时回调的方法,读->show,写->store
–118–>该kobj_type默认的属性,每一个属性都对应到用户空间sysfs下的一个文件,都可以有自己的show/store方法(也可以没有),存储在container_of(attribute, struct kobj_attribute,attr)的kobj_attribute中。通常一个ktype都不是一个属性,所以是二级指针,实际中使用的常常是结构体指针数组。
__ATTR()
构造kobj_type的核心是构造”kobj_attribute”。如前文所述,内核提供了一组API来供我们快速的构造kobj_attribute并与kobject(实质为kobject->ktype)绑定,这种绑定会直接体现在sysfs文件系统中, 关于syfs的构造, 将是另外一篇文章要讨论的内容
//include/linux/sysfs.h 75 __ATTR(_name, _mode, _show, _store) 82 __ATTR_PREALLOC(_name, _mode, _show, _store) 89 __ATTR_RO(_name) 94 __ATTR_WO(_name) 99 __ATTR_RW(_name) 105 __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) 115 __ATTRIBUTE_GROUPS(_name) 121 ATTRIBUTE_GROUPS(_name) 125 __ATTRIBUTE_GROUPS(_name)
–75–125–>这些宏用于快速构造attribute结构体
最后, 附一张全图: