brvah树状结构默认展开第一个_kobject内核对象(1)—基本数据结构

Linux内核基于kobject内核对象机制将系统中总线类型、设备和驱动分别用bus_type、device和device_driver对象描述,并且用各种类别(class)的设备及其接口(class_interface)对其管理,最终通过sysfs文件系统在用户态进行展示。本文主要介绍kobject的基本数据结构以及如何在sysfs中运用。

1.基本数据结构

  • kobject
  • name kobject对象的名字;
  • entry 通过它链接到kset上;
  • parent 指向一个kobject;
  • kref kobject的引用计数;
  • state_initialized 为1表示已经被初始化过
  • state_in_sysfs 为1表示已经添加到sysfs文件系统中
  • state_add_uevent_sent 为1表示发送过添加事件到用户态
  • state_remove_uevent_sent 为1表示删除过添加事件到用户态
  • uevent_suppress 为1屏蔽发送事件到用户态
struct kobject {
	const char		*name;
	struct list_head	entry;
	struct kobject		*parent;
	struct kset		*kset;
	struct kobj_type	*ktype;
	struct kernfs_node	*sd; /* sysfs directory entry */
	struct kref		kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
	struct delayed_work	release;
#endif
	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;
};
  • ktype

同一类型的某些成员和方法是相同的,例如某类内核对象的默认属性以及属性的读/写实现方法等,可以将这些共有的方法抽象出来,因此就有可kobj_type类型结构。

  • release 实现这类kobject释放自己的方法,配合引用计数使用;
  • sysfs_ops 实现这类kobject的读写方法,sysfs_ops 在下面会有详细描述,sysfs文件系统的读写操作对应kobject的show和store操作;
  • default_attrs 表示该类对象的默认属性;
struct kobj_type {
	void (*release)(struct kobject *kobj);
	const struct sysfs_ops *sysfs_ops;
	struct attribute **default_attrs;
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};
  • kset

表示内核对象的集合,kset本身可以被当做一个kobject对象对待,用kobj成员表示,list成员将kobject组织成一个链表。kset的存在是为了在它之下的kobject有相同的操作方式。

  • list 表头,用来链接所有的kobject对象;
  • list_lock 遍历这个kset上所有kobject的自旋锁;
  • kobj 对于kset而言也是一个kobject对象;
  • uevent_ops uevent操作函数,当内核对象发生了特定事件需要通知到用户空间时,需要调用
    kset_uevent_ops中的回调函数,如热插拔特性就是依靠kobject_event函数来通知的。与之对应的有kobj_uevent_env数据结构。
struct kset {
	struct list_head list;
	spinlock_t list_lock;
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
};
  • 三者之间的关系

e30ce42662e86816b4bca3e1669e129e.png

1.kset集成了kobject,本身也可以作为kobject对待;

2.每个object有一个parent指针,通常情况下kset下包含的kobject通过它指向kset内嵌的kobject,不过也可以指向其他的object或者置为null;

3.kset下的kobject通过list双向链表串起来;

4.kobject也可以单独存在,即其kset域为NULL;

尽管内核版本在不断演进,但是kobject这套机制从2.6开始一直没有很大的变化,而基于此机制之上的如sysfs倒是有很大的改动。

kobject的释放通常通过引用计数来完成,引用计数在内核中提供如下数据结构:

struct kref {
          atomic_t refcount;
};

struct kref一般都会内嵌在实际构造的内核对象中。

下面介绍下如何将kobject添加到sysfs中。

1.创建初始化内核对象

kobject_create-->kobject_init-->kobject_init_internal

kobject_create中负责创建一个kobject对象;

kobject_init中负责检查这个kobject是否重复创建;

kobject_init_internal中初始化kobject的各个字段;

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

        kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
        if (!kobj)
                return NULL;

        kobject_init(kobj, &dynamic_kobj_ktype);
        return kobj;
}

2.将内核对象添加到sysfs文件系统

sysfs是一种表示内核对象、对象属性,以及对象关系的一种机制,一般内核对象、属性、以及对象关系组织成树状形式。其中内核对象被映射为用户态的目录;对象属性被映射为用户态的文件,文件在目录下;对象关系被映射成用户空间的符号链接。

如果koibject需要添加到sysfs中,则必须要调用kobject_add函数

fe0f5fa5c99bf1bef70d499526ce7915.png

e649d330802cbb0d4c5eb81393841bdd.png
  • kobjset_set_name_vargs

解析fmt传入的参数,设置kobject的name,并指向传进来的parent;

  • kobject_add_internal
    1. 对parent的引用计数加1,确保parent不会退出;
    2. 调用create_dir接口,完成sysfs的注册,其中sysfs_create_dir中负责创建目录,populate_dir中负责创建file;
    3. 设置state_in_sysfs,表示已经添加到sysfs中
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) {
                printk(KERN_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);
        retval = kobject_add_varg(kobj, parent, fmt, args);
        va_end(args);

        return retval;
}

populate_dir中逐个处理内核对象所属对象类型的默认属性,对每个属性,调用sysfs_create_file函数在内核对象的目录下创建以属性名为名字的文件。kobject_add中只会为默认属性自动创建文件。

3.创建内核对象集kset

这里不再展开与创建kobject类似。

4.发送内核对象变化事件到用户空间

当内核对象创建好以后,如果其发生了特定的事件变化,需要通知到用户态,则可以通过调用kobject_uevent函数完成,一种典型的运用就是热插拔技术。

int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
        return kobject_uevent_env(kobj, action, NULL);
}
enum kobject_action {
        KOBJ_ADD,  // 内核对象被添加 比如热插拔一个设备
        KOBJ_REMOVE, // 内核对象被删除,同上
        KOBJ_CHANGE, // 内核对象属性发生变化,比如分区表发生变化
        KOBJ_MOVE, // 内核对象位置发生改变,比如设备名改变
        KOBJ_ONLINE, // 内核对象在线,比如cpu在线或离线
        KOBJ_OFFLINE,
        KOBJ_MAX // 事件种类的最大值
};

kobject_action指定了内核对象发生的事件类型。

5.举个列子

可以参考内核samples/kobject/kobject-example.c写的列子

  1. 首先在sys/kernel目录下创建一个kobject_example目录
example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
  1. 对这个kobject_eaample对象设置了三个属性,其属性名分别是bar、baz、foo
static struct attribute *attrs[] = { 
        &foo_attribute.attr,
        &baz_attribute.attr,
        &bar_attribute.attr,
        NULL,   /* need to NULL terminate the list of attributes */
};
  1. 设置文件的读写函数,对应的内核对象的show/store函数

列子中比较简单就是将一个整数写入,当尝试写入字符"aaa"时会报如下错误:

0caaf3a6b70d228df15707dc66b4cd09.png

81d5c2e4676855534df47db99d2c4fcd.png

6.参考

  • 《存储技术原理分析》
  • 《linux内核探秘》
  • linux kernel 4.19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值