[Linux Kernel] sysfs 与 kobject 介绍 & 使用

sysfs

sysfs 简介

   sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核数据结构及其属性,以及它们之间的关联到用户空间的方法。

   sysfs 始终与 kobject 的底层结构紧密相关。请阅读 Documentation/kobject.txt 文档以获得更多关于 kobject 接口的信息。

   只要内核配置中定义了 CONFIG_SYSFS ,sysfs 总是被编译进内核。你可通过以下命令挂载它:

  • mount -t sysfs sysfs /sys

创建目录

   任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的共同祖先;例如:某些对象属于某个子系统。

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

属性

   kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义了面向文件 I/O 操作的方法,以提供对内核属性的读写。

   属性应为 ASCII 码文本文件。以一个文件只存储一个属性值为宜。但一个文件只包含一个属性值可能影响效率,所以一个包含相同数据类型的属性值数组也被广泛地接受。

   混合类型、表达多行数据以及一些怪异的数据格式会遭到强烈反对。这样做是很丢脸的,而且其代码会在未通知作者的情况下被重写。

  • 一个简单的属性结构定义如下:
struct attribute {
        char              * name;
        struct module	  * owner;
        umode_t             mode;
};

int  sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);

   一个单独的属性结构并不包含读写其属性值的方法。子系统最好为增删特定对象类型的属性定义自己的属性结构体和封装函数。

  • 例如:驱动程序模型定义的 device_attribute 结构体如下:
struct device_attribute {
	struct attribute	attr;
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};

int  device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);

为了定义设备属性,同时定义了一下辅助宏:

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

例如:声明

static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);

等同于如下代码:

static struct device_attribute dev_attr_foo = {
        .attr	= {
		.name = "foo",
		.mode = S_IWUSR | S_IRUGO,
		.show = show_foo,
		.store = store_foo,
	},
};

子系统特有的回调函数

   当一个子系统定义一个新的属性类型时,必须实现一系列的 sysfs 操作,以帮助读写调用实现属性所有者的显示和储存方法。

struct sysfs_ops {
        ssize_t (*show) (struct kobject *, struct attribute *, char *);
        ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};

[子系统应已经定义了一个 struct kobj_type 结构体作为这个类型的描述符,并在此保存 sysfs_ops 的指针。更多的信息参见 kobject 的文档]

   sysfs 会为这个类型调用适当的方法。当一个文件被读写时,这个方法会将一般的kobject 和 attribute 结构体指针转换为适当的指针类型后调用相关联的函数。

示例:

#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)

static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
                             char *buf)
{
        struct device_attribute *dev_attr = to_dev_attr(attr);
        struct device *dev = to_dev(kobj);
        ssize_t ret = -EIO;

        if (dev_attr->show)
                ret = dev_attr->show(dev, dev_attr, buf);
        if (ret >= (ssize_t)PAGE_SIZE) {
                printk("dev_attr_show: %pS returned bad count\n",
                                dev_attr->show);
        }
        return ret;
}

读写属性数据

   在声明属性时,必须指定 show() 或 store() 方法,以实现属性的读或写。这些方法的类型应该和以下的设备属性定义一样简单。

ssize_t (*show) (struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
                 const char *buf, size_t count);

   也就是说,他们应只以一个处理对象、一个属性和一个缓冲指针作为参数。

   sysfs 会分配一个大小为 (PAGE_SIZE) 的缓冲区并传递给这个方法。Sysfs 将会为每次读写操作调用一次这个方法。这使得这些方法在执行时会出现以下的行为:

  • 在读方面(read(2)),show() 方法应该填充整个缓冲区。回想属性应只导出了一个属性值或是一个同类型属性值的数组,所以这个代价将不会不太高。

    这使得用户空间可以局部地读和任意的向前搜索整个文件。如果用户空间向后搜索到零或使用‘0’偏移执行一个pread(2)操作,show()方法将再次被调用,以重新填充缓存。

  • 在写方面(write(2)),sysfs 希望在第一次写操作时得到整个缓冲区。之后 Sysfs 传递整个缓冲区给 store() 方法。

    当要写 sysfs 文件时,用户空间进程应首先读取整个文件,修该想要改变的值,然后回写整个缓冲区。

    在读写属性值时,属性方法的执行应操作相同的缓冲区。

注记:

  • 写操作导致的 show() 方法重载,会忽略当前文件位置。

  • 缓冲区应总是 PAGE_SIZE 大小。对于i386,这个值为4096。

  • show() 方法应该返回写入缓冲区的字节数,也就是 snprintf()的返回值。

  • show() 应始终使用 snprintf()。

  • store() 应返回缓冲区的已用字节数。如果整个缓存都已填满,只需返回count 参数。

  • show() 或 store() 可以返回错误值。当得到一个非法值,必须返回一个错误值。

  • 一个传递给方法的对象将会通过 sysfs 调用对象内嵌的引用计数固定在内存中。尽管如此,对象代表的物理实体(如设备)可能已不存在。如有必要,应该实现一个检测机制。

一个简单的(未经实验证实的)设备属性实现如下:

static ssize_t show_name(struct device *dev, struct device_attribute *attr,
                         char *buf)
{
	return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name);
}

static ssize_t store_name(struct device *dev, struct device_attribute *attr,
                          const char *buf, size_t count)
{
	snprintf(dev->name, sizeof(dev->name), "%.*s",
            (int)min(count, sizeof(dev->name) - 1), buf);
	return count;
}

static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);

(注意:真正的实现不允许用户空间设置设备名。)

顶层目录布局

sysfs 目录的安排显示了内核数据结构之间的关系。

顶层 sysfs 目录如下:

block/
bus/
class/
dev/
devices/
firmware/
net/
fs/

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

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

devices/
drivers/

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

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

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

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

更多有关 driver-model 的特性信息可以在Documentation/driver-api/driver-model/ 中找到。

TODO: 完成这一节。

当前接口

以下的接口层普遍存在于当前的sysfs中:

  • 设备 (include/linux/device.h)

结构体:

struct device_attribute {
	struct attribute	attr;
	ssize_t (*show) (struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};

声明:

DEVICE_ATTR(_name, _mode, _show, _store);/删属性:

int  device_create_file(struct device *dev, const struct device_attribute * attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);
  • 总线驱动程序 (include/linux/device.h)

结构体:

struct bus_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct bus_type *, char * buf);
        ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};

声明:

BUS_ATTR(_name, _mode, _show, _store)/删属性:

int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
  • 设备驱动程序 (include/linux/device.h)

结构体:

struct driver_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct device_driver *, char * buf);
        ssize_t (*store)(struct device_driver *, const char * buf,
                         size_t count);
};

声明:

DRIVER_ATTR(_name, _mode, _show, _store)/删属性:

int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);

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

kobject

kobject 简介

   理解驱动程序模型(以及在其基础上构建的kobject抽象)的部分困难在于没有明显的起点。处理kobjects需要理解几种不同的类型,所有这些类型都相互参照。为了使事情变得更容易,我们将采取一种多步骤的方法,从模糊的术语开始,并在进行过程中添加细节。为此,这里有一些我们将要使用的术语的快速定义。

  • kobject 是struct kobject类型的对象。Kobjects有一个名称和一个引用计数。kobject还有一个父指针(允许将对象排列成层次结构)、一个特定类型,通常还有sysfs虚拟文件系统中的表示。

    kobjects 本身并没有什么感兴趣的内容;相反,它们通常嵌入到包含代码真正感兴趣的内容的其他结构中。

    任何结构中都不应嵌入多个kobject。如果这样做了,对象的引用计数肯定会被弄乱和不正确,并且您的代码将是错误的。所以不要这样做。

  • ktype 是嵌入kobject的对象类型。嵌入kobject的每个结构都需要相应的ktype。ktype控制kobject在创建和销毁时发生的情况。

  • kset 是一组kobjects。这些kobject可以是相同的k类型,也可以属于不同的k类型。kset是kobjects集合的基本容器类型。kset包含自己的kobject,但是可以安全地忽略该实现细节,因为kset核心代码会自动处理这个kobject。

    当您看到一个包含其他目录的sysfs目录时,通常每个目录都对应于同一kset中的一个kobject。

我们将学习如何创建和操作所有这些类型。我们将采取自下而上的方法,所以我们将回到kobjects。

嵌入 kobject

   内核代码很少创建独立的kobject,下面解释了一个主要的异常。相反,kobjects用于控制对更大的、特定于域的对象的访问。为此,kobjects将嵌入到其他结构中。如果您习惯于用面向对象的术语来思考问题,那么kobjects可以看作是一个顶级的抽象类,从中派生出其他类。kobject实现了一组功能,这些功能本身并不特别有用,但在其他对象中很好使用。C语言不允许直接表示继承,因此必须使用其他技术(如结构嵌入)。

   (顺便提一下,对于熟悉内核链表实现的人来说,这类似于“list_head”结构本身很少有用,但总是嵌入到感兴趣的较大对象中。)

   因此,例如,drivers/UIO/UIO.c 中的 UIO 代码有一个定义与UIO设备关联的内存区域的结构:

struct uio_map {
	struct kobject kobj;
	struct uio_mem *mem;
};

   如果您有一个 struct uio_map 结构,那么找到它的嵌入kobject只是使用kobj成员的问题。使用kobjects的代码通常会遇到相反的问题:给定struct kobject指针,指向包含结构的指针是什么?您必须避免使用技巧(例如假设kobject位于结构的开头),而是使用 container_of() 宏,可以在<linux/kernel.h>中找到:

  container_of(pointer, type, member)

其中:

  • “pointer” 是指向嵌入 kobject 的指针,
  • “type” 是包含结构的类型,并且
  • “member” 是 “pointer” 指向的结构字段的名称。

   container_of() 的返回值是指向相应容器类型的指针。因此,例如,指向嵌入在结构 uio_map 中的结构 kobject 的指针 “kp” 可以转换为指向包含 uio_map 结构的指针,该结构的指针为:

  struct uio_map *u_map = container_of(kp, struct uio_map, kobj);

   为了方便起见,程序员通常定义一个简单的宏,用于“反向转换”指向包含类型的kobject指针。在前面的drivers/uio/uio.c中,您可以看到:

struct uio_map {
	struct kobject kobj;
	struct uio_mem *mem;
};

#define to_map(map) container_of(map, struct uio_map, kobj)

   其中宏参数“map”是指向有问题的 struct kobject 的指针。该宏随后使用以下命令调用:

  struct uio_map *map = to_map(kobj);

kobjects 的初始化

   当然,创建kobject的代码必须初始化该对象。一些内部字段是通过(必须)调用 kobject_init() 来设置的:

   void kobject_init(struct kobject *kobj, struct kobj_type *ktype);

   要正确创建kobject,需要ktype,因为每个kobject都必须具有关联的 kobj_type。调用 kobject_init() 后,若要向sysfs注册kobject,必须调用函数 kobject_add():

  int kobject_add(struct kobject *kobj, struct kobject *parent,
	              const char *fmt, ...);

   这将正确设置 kobject 的父对象和 kobject 的名称。如果要将 kobject 与特定 kset 关联,则必须在调用 kobject_add() 之前分配 kobj->kset。如果 kset 与 kobject 相关联,那么 kobject 的父对象可以在对 kobject_add() 的调用中设置为 NULL,然后 kobject 的父对象将是 kset 本身。

   由于 kobject 的名称是在添加到内核时设置的,因此不应直接操作 kobject 的名称。如果必须更改 kobject 的名称,请调用 kobject_rename() :

    int kobject_rename(struct kobject *kobj, const char *new_name);

   kobject_rename 不执行任何锁定,也不知道哪些名称是有效的,因此调用者必须提供自己的健全性检查和序列化。

   有一个名为 kobject_set_name() 的函数,但它是遗留的cruft,正在被删除。如果您的代码需要调用此函数,则它是不正确的,需要修复。

要正确访问kobject的名称,请使用函数 kobject_name() :

 const char *kobject_name(const struct kobject * kobj);

   有一个 helper 函数可以同时初始化 kobject 并将其添加到内核中,它被称为令人惊讶的 kobject_init_and_add() :

 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
                         struct kobject *parent, const char *fmt, ...);

参数与上面描述的单个 kobject_init() 和 kobject_add() 函数相同。

Uevents

   在 kobject 注册到 kobject 核心之后,您需要向世界宣布它已经创建。这可以通过调用 kobject_uevent() 来完成:

  int kobject_uevent(struct kobject *kobj, enum kobject_action action);

   当 kobject 首次添加到内核时,使用 KOBJ_ADD 操作。只有在正确初始化 kobject 的任何属性或子对象之后,才应该执行此操作,因为当调用发生时,用户空间将立即开始查找这些属性或子对象。

   当 kobject 从内核中移除时(下面详细介绍如何执行此操作),KOBJ_REMOVE 的 uevent 将由 kobject core 自动创建,因此调用方不必担心手动操作。

引用计数

   kobject 的一个关键功能是充当其嵌入对象的引用计数器。只要存在对该对象的引用,该对象(以及支持它的代码)就必须继续存在。用于操作 kobject 的引用计数的低级函数有:

 struct kobject *kobject_get(struct kobject *kobj);
 void kobject_put(struct kobject *kobj);

   成功调用 kobject_get() 将增加 kobject 的引用计数器并返回指向 kobject 的指针。

   当释放引用时,对 kobject_put() 的调用将减少引用计数,并可能释放该对象。请注意,kobject_init() 将引用计数设置为1,因此设置kobject的代码最终需要执行 kobject_put() 以释放该引用。

   因为 kobjects 是动态的,所以不能静态地或在堆栈上声明它们,而是始终动态地分配它们。内核的未来版本将包含对静态创建的 kobjects 的运行时检查,并警告开发人员此不正确用法。

   如果您只想使用 kobject 来为您的结构提供一个引用计数器,请使用 struct kref;kobject 将是过度的。有关如何使用 struct kref 的更多信息,请参见文件 Documentation/kref.txt 文件在 Linux 内核源代码树中。

Creating “simple” kobjects

   有时,开发人员只需要在 sysfs 层次结构中创建一个简单的目录,而不必处理 kset、显示和存储函数以及其他细节的整个复杂过程。这是应该创建单个 kobject 的一个例外。要创建这样的条目,请使用函数:

    struct kobject *kobject_create_and_add(char *name, struct kobject *parent);

   此函数将创建一个 kobject 并将其放在 sysfs 中指定父kobject下面的位置。要创建与此 kobject 关联的简单属性,请使用:

  int sysfs_create_file(struct kobject *kobj, struct attribute *attr);

或者:

 int sysfs_create_group(struct kobject *kobj, struct attribute_group *grp);

   这里使用的两种类型的属性(使用 kobject_create_and_add() 创建的 kobject)都可以是 kobj_attribute 类型,因此不需要创建特殊的自定义属性。

有关简单 kobject 和 attributes 的实现,请参见示例模块samples/kobject/kobject_example.c 。

Ktypes 和释放方法

   讨论中还缺少一件重要的事情,那就是当 kobject 的引用计数为零时,它会发生什么。创建 kobject 的代码通常不知道什么时候会发生这种情况;如果发生了,那么首先使用 kobject 就没什么意义了。当引入 sysfs 时,甚至可预测的对象生命周期也变得更加复杂,因为内核的其他部分可以获得对系统中注册的任何kobject的引用。

   最终的结果是,受 kobject 保护的结构在其引用计数变为零之前无法释放。引用计数不受创建 kobject 的代码的直接控制。因此,每当对某个 kobject 的最后一次引用消失时,必须异步通知该代码。

   一旦通过 kobject_add() 注册了kobject,就不能使用 kfree() 直接释放它。唯一安全的方法是使用 kobject_put() 。在 kobject_init() 之后始终使用 kobject_put() 是一种好的做法,以避免错误悄悄进入。

此通知是通过 kobject 的 release() 方法完成的。通常这种方法的形式如下:

void my_object_release(struct kobject *kobj)
{
	struct my_object *mine = container_of(kobj, struct my_object, kobj);
	
	/* Perform any additional cleanup on this object, then... */
	kfree(mine);
}

   有一点是不能夸大的:每个 kobject 都必须有一个 release() 方法,并且 kobject 必须保持(一致的状态)直到调用该方法。如果不满足这些约束,则代码有缺陷。请注意,如果忘记提供 release() 方法,内核将发出警告。不要试图通过提供 “空的” 释放函数来消除此警告。

   如果清理函数只需要调用 kfree() ,则必须创建一个包装函数,该函数使用 container_of() 向上转换为正确的类型(如上面的示例所示),然后对整个结构调用 kfree() 。

   注意,kobject 的名称在 release 函数中可用,但不能在此回调中更改。否则,kobject 内核会出现内存泄漏,这会让人们不高兴。

   有趣的是,release() 方法并不存储在 kobject 本身中;相反,它与 ktype 相关联。那么,让我们介绍一下struct kobj_type:

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);
};

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

   struct kobj_type 中的 release 字段当然是指向此类型 kobject 的 release() 方法的指针。其他两个字段(sysfs_ops 和 default_attrs)控制此类型的对象在 sysfs 中的表示方式;它们超出了本文档的范围。

   默认属性指针是一个默认属性列表,将自动为使用此 ktype 注册的任何 kobject 创建这些属性。

ksets

   kset 仅仅是希望相互关联的 kobject 的集合。没有限制它们是同一个 ktype,但是如果不是,就要非常小心。

kset 具有以下功能:

  • 它是一个装有一组物体的袋子。内核可以使用kset来跟踪“所有块设备”或“所有PCI设备驱动程序”
  • kset也是sysfs中的一个子目录,其中可以显示与kset相关联的kobject。每个kset都包含一个kobject,可以将其设置为其他kobject的父对象;sysfs层次结构的顶层目录是这样构造的。
  • kset可以支持kobjects的“热插拔”,并影响uevent事件向用户空间报告的方式。

   在面向对象的术语中,“kset”是顶级容器类;kset包含自己的kobject,但该kobject由kset代码管理,不应被任何其他用户操作。

   kset将其子级保存在标准的内核链接列表中。Kobjects通过kset字段指向其包含的kset。在几乎所有的情况下,属于kset的kobject在其父对象中都有kset(或者严格地说,它的嵌入kobject)。

   由于kset中包含一个kobject,因此应该始终动态创建它,而不应静态地或在堆栈上声明它。要创建新的kset,请使用:

struct kset *kset_create_and_add(const char *name,
						struct kset_uevent_ops *u,
						struct kobject *parent);

使用完 kset 后,请调用:

void kset_unregister(struct kset *kset);

   去摧毁它。这将从 sysfs 中删除 kset 并减少其引用计数。当引用计数为零时,将释放 kset。由于对 kset 的其他引用可能仍然存在,因此释放可能在 kset_unregister() 返回之后发生。

使用kset的示例可以在内核树中的 samples/kobject/kset example.c 文件中看到。

   如果 kset 希望控制与其关联的 kobjects 的 uevent 操作,它可以使用结构 kset_uevent_ops 来处理它:

struct kset_uevent_ops {
	int (*filter)(struct kset *kset, struct kobject *kobj);
	const char *(*name)(struct kset *kset, struct kobject *kobj);
	int (*uevent)(struct kset *kset, struct kobject *kobj,
				struct kobj_uevent_env *env);
};

   filter 函数允许 kset 阻止将 uevent 发送到特定 kobject 的用户空间。如果函数返回 0,则不会发出 uevent。

   将调用 name 函数来覆盖 uevent 发送给用户空间的 kset 的默认名称。默认情况下,名称将与 kset 本身相同,但此函数(如果存在)可以重写该名称。

   当要将 uevent 发送到用户空间时,将调用 uevent 函数,以允许向 uevent 添加更多环境变量。

   有人可能会问,如果没有提供执行该函数的函数,那么如何将 kobject 确切地添加到 kset 中。答案是此任务由 kobject_add() 处理。当kobject传递给 kobject_add() 时,其 kset 成员应指向 kobject 将属于的 kset。kobject_add() 将处理其余部分。

   如果属于 kset 的 kobject 没有 parent kobject set,它将被添加到 kset 的目录中。并非 kset 的所有成员都必须存在于 kset 目录中。如果在添加 kobject 之前指定了显式的 parent kobject,则 kobject 将注册到 kset,但添加到 parent kobject 的下面。

Kobject removal

   在 kobject 成功注册到 kobject core 之后,必须在代码完成时清除它。为此,请调用 kobject_put() 。通过这样做,kobject core 将自动清除这个 kobject 分配的所有内存。如果已为对象发送 KOBJ_ADD uevent,则将发送相应的 KOBJ_REMOVE uevent,并将为调用方正确处理任何其他 sysfs 内务处理。

   如果需要对 kobject 执行两步删除(假设需要销毁对象时不允许睡眠),则调用 kobject_del() ,这将从 sysfs 中注销 kobject。这使得 kobject “不可见”,但它没有被清理,并且对象的引用计数仍然相同。稍后调用 kobject_put() 完成与 kobject 相关联的内存清理。

   如果构造了循环引用,则可以使用 kobject_del() 删除对父对象的引用。在某些情况下,父对象引用子对象是有效的。必须通过显式调用 kobject_del() 来中断循环引用,以便调用释放函数,并且前一个循环中的对象彼此释放。

Example code to copy from

有关正确使用 kset 和 kobjects 的更完整示例,请参见示例程序 samples/kobject/{kobject-example.c,kset-example.c},如果您选择 CONFIG_SAMPLE_KOBJECT,它将被构建为可加载模块。

参考文献

  • linux-5.4.37/Documentation/translations/zh_CN/filesystems/sysfs.txt
  • linux-5.4.37/Documentation/kobject.txt
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值