linux设备模型:sysfs(kobject)解析

本文围绕Linux的sysfs和kobject展开。sysfs是基于ramfs的内存文件系统,用于导出内核数据到用户空间,kobject注册时会在sysfs创建对应目录。还介绍了sysfs目录结构、kobject属性导出方式,以及ksysfs_init函数、源码结构、部分结构定义和扩展函数/变量等内容。

  sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核数据结构及其属性,以及它们之间的关联到用户空间的方法。任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递内核的对象层次到用户空间。sysfs中的顶层目录代表着内核对象层次的共同祖先;例如:某些对象属于某个子系统。


  sysfs 在与其目录关联的 kernfs_node 对象中内部保存一个指向实现目录的 kobject 的指针。以前,这个 kobject 指针被 sysfs 直接用于kobject 文件打开和关闭的引用计数。而现在的 sysfs 实现中,kobject引用计数只能通过 sysfs_schedule_callback() 函数直接修改。


  kobject 的属性可在文件系统中以普通文件的形式导出。sysfs 为属性定义了面向文件 I/O 操作的方法,以提供对内核属性的读写。属性应为 ASCII 码文本文件。以一个文件只存储一个属性值为宜。但一个文件只包含一个属性值可能影响效率,所以一个包含相同数据类型的属性值数组也被广泛地接受。混合类型、表达多行数据以及一些怪异的数据格式会遭到强烈反对。这样做是很丢脸的,而且其代码会在未通知作者的情况下被重写。


  一个单独的属性结构并不包含读写其属性值的方法。子系统最好为增删特定对象类型的属性定义自己的属性结构体和封装函数。例如:驱动程序模型定义的 device_attribute 结构体。当一个子系统定义一个新的属性类型时,必须实现一系列的 sysfs 操作,以帮助读写调用实现属性所有者的显示和储存方法。子系统应已经定义了一个 struct kobj_type 结构体作为这个类型的描述符,并在此保存 sysfs_ops 的指针 (更多的信息参见 kobject 的文档)。sysfs 会为这个类型调用适当的方法。当一个文件被读写时,这个方法会将一般的kobject 和 attribute 结构体指针转换为适当的指针类型后调用相关联的函数。


  sysfs 目录的安排显示了内核数据结构之间的关系,顶层 sysfs 目录如下:
      block/
      bus/
      class/
      dev/
      devices/
      firmware/
      net/
      fs/


  devices/ 包含了一个设备树的文件系统表示。他直接映射了内部的内核设备树,反映了设备的层次结构。


  bus/ 包含了内核中各种总线类型的平面目录布局。每个总线目录包含两个子目录:
     devices/
     drivers/


  devices/ 包含了系统中出现的每个设备的符号链接,他们指向 root/ 下的设备目录。


  drivers/ 包含了每个已为特定总线上的设备而挂载的驱动程序的目录(这里假定驱动没有跨越多个总线类型)。


  fs/ 包含了一个为文件系统设立的目录。现在每个想要导出属性的文件系统必须在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.rst)。


  dev/ 包含两个子目录: char/ 和 block/。在这两个子目录中,有以: 格式命名的符号链接。这些符号链接指向 sysfs 目录中相应的设备。/sys/dev 提供一个通过一个 stat(2) 操作结果,查找设备 sysfs 接口快捷的方法。


  sysfs 目录结构以及其中包含的属性定义了一个内核与用户空间之间的 ABI。对于任何 ABI,其自身的稳定和适当的文档是非常重要的。所有新的 sysfs属性必须在 Documentation/ABI 中有文档。详见 Documentation/ABI/README。


       内容来自:Documentation/translations/zh_CN/filesystems/sysfs.txt


kobject

kobject大致结构图

目录


1. 函数分析

1.1 ksysfs_init

2. 源码结构

3. 部分结构定义

4. 扩展函数/变量


1. 函数分析

1.1 ksysfs_init

  ksysfs子系统是以kobj结构对象为基础,并在基础上扩展出各种特征,如关联kobj_ktype结构,用于对文件的权限、读写操作等,而文件以kernfs结构为基础生成(以attribute相关的属性结构关联/绑定),它是一种虚拟文件(存在于内存中),显示在/sys等目录,kernfs它以父、子节点表示目录或文件(如果有子级应该可以称为目录,应该也和它的属性函数有关系),kernfs首先创建一个父节点,然后基于父节点创建属性组及组内的有效属性节点

static int __init ksysfs_init(void)
{
        int error;

        kernel_kobj = kobject_create_and_add("kernel", NULL); // 分配并初始化sysfs对象(kobj对象),关联kobj_ktype,为kobj对象赋值父对象指针,并加入到kset容器(链表)中,然后通create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点......

kobject_create_and_add

error = sysfs_create_group(kernel_kobj, &kernel_attr_group); // 获取kobj对象的sysfs所有权数据,创建kernfs_node节点及命名空间 (父级kn,如目录),然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点
// kernel_attr_group kernel属性组

kernel_attr_group

if (notes_size > 0) {
                notes_attr.size = notes_size;
                error = sysfs_create_bin_file(kernel_kobj, &notes_attr); // 从kernfs_node_cache缓存中分配kn节点及初始化,包括关联kernfs_ops对象,将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点
                // ops = &sysfs_bin_kfops_ro;
                if (error)
                        goto group_exit;
}

return 0;

group_exit:
        sysfs_remove_group(kernel_kobj, &kernel_attr_group);
kset_exit:
        kobject_put(kernel_kobj);
exit:
        return error;
}

core_initcall(ksysfs_init);

notes_attr


2. 源码结构

  dynamic_kobj_ktype 动态注册的sysfs的ktype(用于释放对象,访问sysfs文件等等)

static struct kobj_type dynamic_kobj_ktype = {
        .release        = dynamic_kobj_release, // 释放kobject对象 (每个 kobject 都必须有一个release() 方法,并且 kobject 必须持续(处于一致状态),直到调用该方法。如果不满足这些约束,则代码有缺陷)
        .sysfs_ops      = &kobj_sysfs_ops, // sysfs操作结构对象
};

dynamic_kobj_release
kobj_sysfs_ops

  kobj_sysfs_ops sysfs操作

const struct sysfs_ops kobj_sysfs_ops = {
        .show   = kobj_attr_show,  // 读取(显示)
        .store  = kobj_attr_store, // 写入(存储)
};
EXPORT_SYMBOL_GPL(kobj_sysfs_ops);

kobj_attr_show
kobj_attr_store

  kernel_attr_group kernel属性组

static const struct attribute_group kernel_attr_group = {
        .attrs = kernel_attrs, // kennel属性列表
};

kobj_attr_store

  kernel_attrs kennel属性列表(数组)

static struct attribute * kernel_attrs[] = {
	&fscaps_attr.attr,  // 定义sysfs文件属性结构对象fscaps_attr
	&uevent_seqnum_attr.attr,
#ifdef CONFIG_UEVENT_HELPER
	&uevent_helper_attr.attr,
#endif
#ifdef CONFIG_PROFILING
	&profiling_attr.attr,
#endif
#ifdef CONFIG_KEXEC_CORE
	&kexec_loaded_attr.attr,
	&kexec_crash_loaded_attr.attr,
	&kexec_crash_size_attr.attr,
#endif
#ifdef CONFIG_CRASH_CORE
	&vmcoreinfo_attr.attr,
#endif
#ifndef CONFIG_TINY_RCU
	&rcu_expedited_attr.attr,
	&rcu_normal_attr.attr,
#endif
	NULL
};

fscaps_attr.attr

  notes_attr 二进制属性

static struct bin_attribute notes_attr __ro_after_init  = {
	.attr = {
		.name = "notes",
		.mode = S_IRUGO, // (S_IRUSR|S_IRGRP|S_IROTH)  0444
	},
	.read = &notes_read, // 读数据
};

notes_read


3. 部分结构定义

  kobject 表示一个sysfs对象或称为一个内核对象,内核模块以它为基础进行构造

struct kobject {
	const char		*name;    // 名称
	struct list_head	entry; // 链表,用于遍历
	struct kobject		*parent;  // 父指针
	struct kset		*kset; // sysfs对象容器,属于特定子系统的一组特定类型的kobject
	const struct kobj_type	*ktype; // 描述特定类型的 kobject,每个 kobject 都需要有一个关联的kobj_type结构
	struct kernfs_node	*sd; /* sysfs目录条目 */
	struct kref		kref; // 泛型引用计数对象
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
	struct delayed_work	release;
#endif
	unsigned int state_initialized:1; // 0表示未初始化,非0表示已初始化
	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;
};

  kobj_type 此结构用于描述特定类型的 kobject (或者更准确地说,包含对象)。每个 kobject 都需要有一个关联的kobj_type结构;调用 kobject_init()或 kobject_init_and_add()时,必须指定指向该结构的指针

struct kobj_type {
	void (*release)(struct kobject *kobj);
	const struct sysfs_ops *sysfs_ops;
	const struct attribute_group **default_groups;
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
	void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};

  kobj_attribute sysfs文件属性,在声明属性时,必须指定show()或store()方法,以实现属性的读或写

struct kobj_attribute {
	struct attribute attr; // 属性结构对象即可表示sysfs文件的名称、权限等,也可以用于通过container_of等方法获取到kobj_attribute对象,它的作用相对较为重要
	ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf); // 参数由一个对象、一个属性和一个指针(数据)构成,用于读取/显示(sysfs文件)信息
	ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count); // 多了一个数据长度参数,用于写入(sysfs文件)信息
};

  attribute 属性结构

struct attribute {
	const char		*name;  // 属性的名字(它出现在 kobject 的 sysfs 目录中)
	umode_t			mode; // 应用到这个属性的保护位(权限), S_IRUSR读,S_IWUSR 写
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	bool			ignore_lockdep:1;
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};

  kernfs_node

/*
* kernfsnode-kernfs层次结构的构建块
* 每个kernfs节点都由单个kernfs_node表示
* 大多数字段对kernfs是私有的,kernfs用户不应该直接访问
*
* 只要保持计数引用,kernfs_node本身就可以访问
* 取消引用元素或任何其他外部实体需要主动引用。
*/
struct kernfs_node {
	atomic_t		count;
	atomic_t		active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
	/*
	 * 使用kernfs_get_parent()和kernfs_name/path(),而不是直接访问以下两个字段
	 * 如果从未将节点移动到不同的父节点,则直接访问父节点是安全的
	 */
	struct kernfs_node	*parent;
	const char		*name;

	struct rb_node		rb;

	const void		*ns;	/* 命名空间标签 */
	unsigned int		hash;	/* ns + name hash */
	union {
		struct kernfs_elem_dir		dir;
		struct kernfs_elem_symlink	symlink;
		struct kernfs_elem_attr		attr;
	};

	void			*priv;

	/*
	 * 64位唯一ID
	 * 在64位ino设置中,ID是ino
	 * 在32位上,低32位是ino和上一代
	 */
	u64			id;

	unsigned short		flags;
	umode_t			mode;
	struct kernfs_iattrs	*iattr;
};

4. 扩展函数/变量

  kobject_create_and_add 分配并初始化sysfs对象(kobj对象),关联kobj_ktype,为kobj对象赋值父对象指针,并加入到kset容器(链表)中,然后通create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点…

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
        struct kobject *kobj;
        int retval;

        kobj = kobject_create(); // 分配并初始化sysfs对象或称为内核对象,关联kobj_ktype

kobject_create

retval = kobject_add(kobj, parent, "%s", name); 计算出kobj对象 name,为kobj对象赋值父对象指针,并加入到kset容器(链表)中,然后通create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点......
        if (retval) {
                pr_warn("%s: kobject_add error: %d\n", __func__, retval);
                kobject_put(kobj);
                kobj = NULL;
        }
        return kobj;
}

kobject_add

  kobject_create 分配并初始化sysfs对象或称为内核对象,关联kobj_ktype

static struct kobject *kobject_create(void)
{
        struct kobject *kobj;

        kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); // 分配kobj对象,必须分配干净的内存,如state_initialized等变量如果不为0,表示已经初始化过或失败
        if (!kobj)
                return NULL;

        kobject_init(kobj, &dynamic_kobj_ktype); // 初始化sysfs对象的引用计数、链表对象及一些变量标志,关联kobj_ktype对象
        // 动态注册的sysfs的ktype(用于释放对象,访问sysfs文件等等) 
        return kobj;
}

kobject_init
dynamic_kobj_ktype

  kobject_init 初始化sysfs对象的引用计数、链表对象及一些变量标志,关联kobj_ktype对象

void kobject_init(struct kobject *kobj, const struct kobj_type *ktype)
{
        char *err_str;
		...
		kobject_init_internal(kobj); // 初始化sysfs对象的引用计数、链表对象及一些变量标志
		// kobj->state_in_sysfs = 0;
        // kobj->state_add_uevent_sent = 0;
        // kobj->state_remove_uevent_sent = 0;
        // kobj->state_initialized = 1;
		
        kobj->ktype = ktype; // 赋值为dynamic_kobj_ktype
        return;

error:
        pr_err("kobject (%p): %s\n", kobj, err_str);
        dump_stack();
}
EXPORT_SYMBOL(kobject_init);

  kobj_attr_show sysfs对象默认的读取函数实现,用于读取/显示(注册的sysfs文件)信息

/* 默认kobject属性操作 */
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
                              char *buf)
{
        struct kobj_attribute *kattr;
        ssize_t ret = -EIO;

        kattr = container_of(attr, struct kobj_attribute, attr); // 通过属性对象attr获取kobj_attribute(sysfs文件)对象
        if (kattr->show)
                ret = kattr->show(kobj, kattr, buf); // 对应的读函数
        return ret;
}

  kobj_attr_store sysfs对象默认的写函数实现,用于写入(注册的sysfs文件)信息

static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
			       const char *buf, size_t count)
{
	struct kobj_attribute *kattr;
	ssize_t ret = -EIO;

	kattr = container_of(attr, struct kobj_attribute, attr);
	if (kattr->store)
		ret = kattr->store(kobj, kattr, buf, count); // 对应的写函数
	return ret;
}

  dynamic_kobj_release sysfs对象默认的释放函数实现

static void dynamic_kobj_release(struct kobject *kobj)
{
	pr_debug("kobject: (%p): %s\n", kobj, __func__);
	kfree(kobj);
}

  kobject_add 计算出kobj对象 name,为kobj对象赋值父指针,并加入到kset容器(链表)中,然后通create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点(父级kn,如目录),加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点,然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点(属性列表中的节点),最后启用目录下的命名空间,设置sysfs初始化完成(kobj->state_in_sysfs = 1)

int kobject_add(struct kobject *kobj, struct kobject *parent,
                const char *fmt, ...)
{
        va_list args;
        int retval;

        if (!kobj)
                return -EINVAL;

        if (!kobj->state_initialized) { // 如果kobj对象没有初始化
                pr_err("kobject '%s' (%p): tried to add an uninitialized object, something is seriously wrong.\n",
                       kobject_name(kobj), kobj);
                dump_stack();
                return -EINVAL;
        }
        va_start(args, fmt); // args指向首个%对应的字符地址
        retval = kobject_add_varg(kobj, parent, fmt, args); // 计算出kobj对象 name,为kobj对象赋值父对象指针,并加入到kset容器(链表)中
        // kobj->parent = parent;
        va_end(args); // args指向NULL

        return retval;
}
EXPORT_SYMBOL(kobject_add);

kobject_add_internal

  kobject_add_internal kobj对象关联父指针,并加入到kset容器(链表)中,然后通create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点(父级kn,如目录),加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点,然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点(属性列表中的节点),最后启用目录下的命名空间,设置sysfs初始化完成(kobj->state_in_sysfs = 1)

static int kobject_add_internal(struct kobject *kobj)
{
        int error = 0;
        struct kobject *parent;
		...
		parent = kobject_get(kobj->parent); // 递增父对象指针的引用计数(+1)

        /*如果已设置,则连接kset,如果尚未设置,则将其用作父级 */
        if (kobj->kset) { // 如果存在kobj对象容器
                if (!parent) // 并且父对象指针不存在
                        parent = kobject_get(&kobj->kset->kobj); // 从容器中获取父对象指针
                kobj_kset_join(kobj); // 将kob对象添加到它的kset容器(链表)中
                kobj->parent = parent; // 为kobj对象指针赋值父对象指针
        }

		error = create_dir(kobj); // create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点(父级kn,如目录),加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点,然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点(属性列表中的节点),最后启用目录下的命名空间

create_dir

		...
		kobj->state_in_sysfs = 1; // sysfs初始化完成

        return error;
}

  fscaps_attr.attr 定义sysfs文件属性结构对象fscaps_attr

 /* fscaps_attr扩展后的形式
  * static struct kobj_attribute fscaps_attr = {
  *      .attr = { .name = fscaps, .mode = 0444 }, // 对象名称和操作权限
  *      .show = fscaps_show, // 读函数
  * };

KERNEL_ATTR_RO(fscaps); fscaps_attr由这个宏注册生成
||
\/
___________________________________________________________________________________
#define KERNEL_ATTR_RO(_name) \
static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
||
\/
static struct kobj_attribute fscaps_attr = __ATTR_RO(_name)
||
\/
#define __ATTR_RO(_name) {                                              \
        .attr   = { .name = __stringify(_name), .mode = 0444 },         \
        .show   = _name##_show,                                         \
}
||
\/
#define __ATTR_RO(fscaps) {                                              \
        .attr   = { .name = __stringify(fscaps), .mode = 0444 },         \
        .show   = fscaps_show,                                         \
}
___________________________________________________________________________________

  sysfs_add_file_mode_ns 分配kernfs_node节点及初始化,包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点

int sysfs_add_file_mode_ns(struct kernfs_node *parent,
                const struct attribute *attr, umode_t mode, kuid_t uid,
                kgid_t gid, const void *ns)
{
        struct kobject *kobj = parent->priv;
        const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
        struct lock_class_key *key = NULL;
        const struct kernfs_ops *ops = NULL;
        struct kernfs_node *kn;

		if (mode & SYSFS_PREALLOC) { // 预分配
                if (sysfs_ops->show && sysfs_ops->store) // profiling, kexec_crash_size
                        ops = &sysfs_prealloc_kfops_rw; 
                else if (sysfs_ops->show) // fscaps, uevent_seqnum, kexec_loaded, kexec_crash_loaded, vmcoreinfo
                        ops = &sysfs_prealloc_kfops_ro;
                else if (sysfs_ops->store)
                        ops = &sysfs_prealloc_kfops_wo;
        } else {
                if (sysfs_ops->show && sysfs_ops->store)
                        ops = &sysfs_file_kfops_rw;
                else if (sysfs_ops->show)
                        ops = &sysfs_file_kfops_ro;
                else if (sysfs_ops->store)
                        ops = &sysfs_file_kfops_wo;
        }

        if (!ops)
                ops = &sysfs_file_kfops_empty;

#ifdef CONFIG_DEBUG_LOCK_ALLOC
        if (!attr->ignore_lockdep)
                key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endif

        kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
                                  PAGE_SIZE, ops, (void *)attr, ns, key); // 从kernfs_node_cache缓存中分配kn节点及初始化,将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点

  create_dir 获取kobj对象的sysfs所有权数据,分配kernfs节点(父级kn,如目录),加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点,然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点(属性列表中的节点),最后启用目录下的命名空间

  先通过sysfs_create_dir_ns创建父级kn,然后通过sysfs_create_groups属性组子级kn(多个节点)

static int create_dir(struct kobject *kobj)
{
        const struct kobj_type *ktype = get_ktype(kobj); // kobj->ktype
        const struct kobj_ns_type_operations *ops;
        int error;

        error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); // 获取kobj对象的sysfs所有权数据,分配kernfs节点(目录),加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点

sysfs_create_dir_ns

if (ktype) {
                error = sysfs_create_groups(kobj, ktype->default_groups); //  获取kobj对象的sysfs所有权数据,创建kernfs_node节点及命名空间 (父级kn,如目录),然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点(属性列表中的节点)
                if (error) {
                        sysfs_remove_dir(kobj);
                        return error;
                }
        }

sysfs_create_groups

 		ops = kobj_child_ns_ops(kobj); // struct kobj_ns_type_operations 
        if (ops) {
			...
			BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
            BUG_ON(ops->type >= KOBJ_NS_TYPES);
            BUG_ON(!kobj_ns_type_registered(ops->type));

            sysfs_enable_ns(kobj->sd); // 启用目录下的命名空间
            // kn->flags |= KERNFS_NS;
        }

        return 0;
}

  sysfs_create_dir_ns // 获取kobj对象的sysfs所有权数据,分配kernfs节点,加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点

int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
{               
        struct kernfs_node *parent, *kn;
        kuid_t uid;     
        kgid_t gid;     
		...
		if (kobj->parent) // 父指针为NULL
                parent = kobj->parent->sd;
        else
                parent = sysfs_root_kn; // struct kernfs_node *sysfs_root_kn;

		kobject_get_ownership(kobj, &uid, &gid); // 获取kobj对象的sysfs所有权数据

kobject_get_ownership

kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 0755, uid, gid,
                                  kobj, ns); // 分配kernfs节点,加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点

kernfs_create_dir_ns

		kobj->sd = kn; // 关联kn
        return 0;
}

  kobject_get_ownership 获取kobj对象的sysfs所有权数据

/*
* kobject_get_ownership() - 获取kobj对象的sysfs所有权数据
* @kobj: kobject有问题
* @uid: sysfs对象的内核用户ID
* @gid: sysfs对象的内核组ID
*
* 返回创建给定kobject的sysfs表示时应该使用的初始uid/gid对
* 通常用于调整容器中对象的所有权
* /
void kobject_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid)
{       
        *uid = GLOBAL_ROOT_UID;  // 初始化uid (0)
        *gid = GLOBAL_ROOT_GID; // 初始化gid (0)

        if (kobj->ktype->get_ownership)
                kobj->ktype->get_ownership(kobj, uid, gid);
}

  kernfs_create_dir_ns 分配kernfs节点,加入安全策略(父节点存在的情况下),将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点

struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
                                         const char *name, umode_t mode,
                                         kuid_t uid, kgid_t gid,
                                         void *priv, const void *ns)
{
        struct kernfs_node *kn;
        int rc;

        /* allocate */
        kn = kernfs_new_node(parent, name, mode | S_IFDIR,
                             uid, gid, KERNFS_DIR); // 从kernfs_node_cache缓存中分配kn节点,为kn分配私有idr号,并且如果kn的uid和gid不是初始值(0),将设置内部的iattr结构的ia_valid变量为大于0的值,如果父节点存在,加入SELINUX(安全)策略等等

kernfs_root

		kn->dir.root = parent->dir.root;
        kn->ns = ns;  // kobject namespace
        kn->priv = priv;
        
        /* link in */
        rc = kernfs_add_one(kn); // 将kn链接到同级rbtree,更新哈希值及时间戳,并激活这个节点
        if (!rc)
                return kn;

        kernfs_put(kn);
        return ERR_PTR(rc);
}

  kernfs_root 获取目标对象的kernfs根节点(目录)

static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn)
{
        /* if parent exists, it's always a dir; otherwise, @sd is a dir */
        if (kn->parent)
                kn = kn->parent;
        return kn->dir.root; // struct kernfs_elem_dir -> root
}

  sysfs_create_groups 获取kobj对象的sysfs所有权数据,创建kernfs_node节点及命名空间 (父级kn,如目录),然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点(属性列表中的节点)

int sysfs_create_groups(struct kobject *kobj,
                        const struct attribute_group **groups)
{
        return internal_create_groups(kobj, 0, groups);
}
EXPORT_SYMBOL_GPL(sysfs_create_groups);
||
\/
static int internal_create_groups(struct kobject *kobj, int update,
                                  const struct attribute_group **groups)
{
        int error = 0;
        int i;
		...
		for (i = 0; groups[i]; i++) {
                error = internal_create_group(kobj, update, groups[i]); // 获取kobj对象的sysfs所有权数据,创建kernfs_node节点及命名空间 (父级kn,如目录),然后为属性组->属性列表中的属性分配kernfs_node节点及初始化(子级kn,如目录/文件),包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点
				...
				}
        return error;
}

create_files

  create_files 为属性组->属性列表中的属性分配kernfs_node节点(目录)及初始化,包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点

static int create_files(struct kernfs_node *parent, struct kobject *kobj,
                        kuid_t uid, kgid_t gid,
                        const struct attribute_group *grp, int update)
{
        struct attribute *const *attr;
        struct bin_attribute *const *bin_attr;
        int error = 0, i;

        if (grp->attrs) { // 属性组中的属性列表(数组)存在
                for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
                        umode_t mode = (*attr)->mode;
						...
						mode &= SYSFS_PREALLOC | 0664;
                        error = sysfs_add_file_mode_ns(parent, *attr, mode, uid,
                                                       gid, NULL); // 分配kernfs_node节点及初始化,包括关联kernfs_ops对象,将kernfs_node链接到同级rbtree,更新哈希值及时间戳,并激活这个节点
				}

sysfs_add_file_mode_ns

if (grp->bin_attrs) { // 二进制属性,默认没有初始化赋值
                for (i = 0, bin_attr = grp->bin_attrs; *bin_attr; i++, bin_attr++) {
                        umode_t mode = (*bin_attr)->attr.mode;
                        ...
                }
}

  notes_read 从__start_notes + off 地址读取数据

extern const void __start_notes __weak;
extern const void __stop_notes __weak;
#define	notes_size (&__stop_notes - &__start_notes)

static ssize_t notes_read(struct file *filp, struct kobject *kobj,
			  struct bin_attribute *bin_attr,
			  char *buf, loff_t off, size_t count)
{
	memcpy(buf, &__start_notes + off, count);
	return count;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坤昱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值