Linux SysFs文件系统分析3(基于Linux6.6)---sysfs目录创建介绍
一、sysfs目录相关的文件操作接口定义
在 Linux 6.x 内核中,sysfs
提供了一种与用户空间交互的机制,其中包括了创建和删除目录、文件的接口。对于 sysfs
目录操作的接口,内核使用 kobject
和一些辅助函数来实现这些操作。以下是针对 Linux 6.x 内核的 sysfs
目录操作接口的定义和实现概述。
1. kobject_create_and_add
:创建 kobject
目录
kobject_create_and_add
是一个常用的接口,用来创建一个新的 kobject
并将其添加到 sysfs
文件系统中。
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
- name:目录的名称,最终会在
/sys
文件系统中创建一个以此名称命名的目录。 - parent:父
kobject
,指定新创建的kobject
目录的父目录,通常可以是kernel_kobj
或其他已有的kobject
。
示例代码:
struct kobject *example_kobj;
example_kobj = kobject_create_and_add("example", kernel_kobj);
if (!example_kobj) {
pr_err("Failed to create kobject\n");
}
这段代码会在 /sys/kernel/example
下创建一个名为 example
的目录。
2. kobject_add
:将已创建的 kobject
添加到 sysfs
如果你已经有了一个 kobject
实例,并希望将其添加到 sysfs
文件系统中,可以使用 kobject_add
函数。
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);
- kobj:要添加的
kobject
。 - parent:父
kobject
,决定了新目录的位置。 - fmt:目录名称的格式化字符串。
示例代码:
int ret;
ret = kobject_add(example_kobj, kernel_kobj, "example");
if (ret) {
pr_err("Failed to add kobject to sysfs\n");
}
3. sysfs_create_dir
:手动创建目录
在某些情况下,你可能希望手动创建一个空的目录。此时可以使用 sysfs_create_dir
来创建目录。
int sysfs_create_dir(struct kobject *kobj);
- kobj:要创建的目录对应的
kobject
。
示例代码:
int ret = sysfs_create_dir(example_kobj);
if (ret) {
pr_err("Failed to create sysfs directory\n");
}
4. sysfs_remove_dir
:删除 sysfs
目录
一旦不再需要某个目录时,可以通过 sysfs_remove_dir
来删除该目录及其所有的子文件。
int sysfs_remove_dir(struct kobject *kobj);
- kobj:要删除的目录对应的
kobject
。
示例代码:
sysfs_remove_dir(example_kobj);
5. kobject_put
:释放 kobject
在删除 kobject
之前,确保释放它的引用。这通常通过 kobject_put
来完成,它会递减 kobject
的引用计数,并在引用计数为零时删除 kobject
。
void kobject_put(struct kobject *kobj);
示例代码:
kobject_put(example_kobj);
6. kobject_uevent
:发送 uevent 通知
sysfs
目录和文件的创建和删除通常伴随有事件通知。kobject_uevent
函数用于发送 uevent 通知,供用户空间的 udev
或其他程序处理。
int kobject_uevent(struct kobject *kobj, enum kobject_action action);
- kobj:目标
kobject
。 - action:事件类型,可以是
KOBJ_ADD
,KOBJ_REMOVE
,KOBJ_CHANGE
等。
示例代码:
kobject_uevent(example_kobj, KOBJ_ADD);
7. 删除目录和属性
为了确保删除目录和属性时不发生内存泄漏,在释放 kobject
之前,通常要删除所有与该目录相关的文件或属性。这可以通过 sysfs_remove_file
来完成。
int sysfs_remove_file(struct kobject *kobj, const struct attribute *attr);
- kobj:要从中删除文件的目录。
- attr:指向要删除的
attribute
。
完整示例代码
以下是一个简单的示例,展示如何创建 sysfs
目录,并为其添加属性文件:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/init.h>
static struct kobject *example_kobj;
static ssize_t example_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
return sprintf(buf, "Hello, Sysfs!\n");
}
static struct kobj_attribute example_attribute = __ATTR(example_file, 0444, example_show, NULL);
static int __init sysfs_example_init(void)
{
int retval;
// 创建 kobject 和 sysfs 目录
example_kobj = kobject_create_and_add("example", kernel_kobj);
if (!example_kobj)
return -ENOMEM;
// 创建属性文件
retval = sysfs_create_file(example_kobj, &example_attribute.attr);
if (retval)
kobject_put(example_kobj); // 失败时清理 kobject
return retval;
}
static void __exit sysfs_example_exit(void)
{
// 删除属性文件
sysfs_remove_file(example_kobj, &example_attribute.attr);
// 删除 kobject 及其所有属性
kobject_put(example_kobj);
}
module_init(sysfs_example_init);
module_exit(sysfs_example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of creating sysfs directories and files");
二、SYSFS提供给kernel模块的目录创建接口
针对sysfs的目录相关的操作接口与变量,均在文件dir.c中实现,针对目录操作,主要介绍目录创建这几个接口。
3.1、目录创建
首先介绍目录创建接口,即sysfs_create_dir接口,该接口主要用于创建目录,其处理流程如下所示。
该接口主要完成的功能如下:
1.根据传递的kobject,获取该kobject的父目录对应的sysfs_dirent类型的变量:
a. 若找到则记为parent_sd;
b. 若没有找到,则以sysfs_root记为父目录对应的sysfs_dirent(即本次在sysfs文件系统的根目录下创建子目录)。
2.若父目录支持kobject namespace,则调用该kobject的ktype->namespace接口,获取namespace变量以及kobject_ns_type。
(在sysfs中,针对net device,为保证在同一个目录下支持多个相同名称的net device,sysfs文件系统中引入了namespace的概念,作为sysfs 目录/文件的tag。在创建目录及文件并将sysfs_dirent类型的变量插入到红黑树节点时,在判断是否存在相同的文件及目录时,也会根据kobject->name与sysfs_dirent->s_ns进行判断(sysfs目前仅支持net type一种namespace类型,关于sysfs namespace的详细内容,可在Documentation/sysfs-tagging.txt中有详细说明))
3.调用sysfs_new_dirent接口创建sysfs_dirent类型的变量,并设置其相应的namespace相关的成员;
4.调用sysfs_add_one(sysfs_add_one->__sysfs_add_one->sysfs_link_sibling),将该sysfs_dirent类型的变量插入到其父目录sysfs_dirent的子目录的红黑树节点中。至此完成目录的创建。
仅在上述第4步执行完成后,方可标识一个sysfs目录或文件的创建完成,因为在sysfs的readdir接口中,其根据父目录对应sysfs_dirent类型变量的s_dir.children.rb_node节点查找子节点,因此若一个sysfs_dirent变量未插入到一个父节点的s_dir.children.rb_node中,则尚未完成创建操作。
sysfs提供了sysfs_create_dir接口后,主要供kobject相关的接口kobject_add_internal使用,该接口实现了根据kobject变量创建对应目录及其属性文件的功能,该接口的处理流程如下。
1.调用kobj_kset_join,将该kobject插入到其所指向的kset的kob子链表中。
2.调用create_dir接口,创建该kobject对应的目录及属性文件。
kobject_add_internal接口主要由kobject_add_varg接口调用,而kobject_add_varg则由kobject_add、kobject_init_and_add。
3.2、举例应用
在 Linux 内核中,sysfs
提供了一种机制,允许内核模块创建目录、文件和属性,用于与用户空间进行交互。通常,内核模块会通过 kobject
和相关的 API 来创建 sysfs
目录和文件。
1. kobject_create_and_add()
创建一个 kobject
并将其与一个目录挂钩,是创建 sysfs
目录的关键步骤。kobject
是一个内核对象,它为 sysfs
中的目录提供了基本的支持。创建的目录会出现在 /sys
文件系统中。
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
- name:指定目录的名称,最终会在
/sys
下显示。 - parent:父级
kobject
,通常是kernel_kobj
或其他现有的kobject
,表示创建的目录是哪个父目录的子目录。
示例:
struct kobject *example_kobj;
example_kobj = kobject_create_and_add("example", kernel_kobj);
if (!example_kobj) {
pr_err("Failed to create kobject\n");
}
上述代码将在 /sys/kernel/example
下创建一个名为 example
的目录,kernel_kobj
是父目录。
2. sysfs_create_file()
一旦创建了一个 kobject
,就可以为它添加属性文件(文件接口),这些属性文件可以供用户空间读取或写入。通过 sysfs_create_file()
来创建文件。
int sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
- kobj:指向
kobject
的指针,属性文件将被添加到这个kobject
目录下。 - attr:一个
struct attribute
结构体,它定义了属性文件的名称和操作。
struct attribute
结构体:
struct attribute {
const char *name;
umode_t mode;
};
- name:文件名。
- mode:文件的权限,例如:
S_IRUGO
(只读)、S_IWUSR
(可写)等。
sysfs
属性的读取和写入通常通过 kobj_attribute
结构体来定义。
struct kobj_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buf);
ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count);
};
- show:当用户从文件读取时调用。
- store:当用户向文件写入时调用。
3. 创建属性文件的完整示例
以下是一个创建 sysfs
目录并为其添加属性文件的完整示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/init.h>
static struct kobject *example_kobj;
static ssize_t example_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
return sprintf(buf, "Hello, Sysfs!\n");
}
static struct kobj_attribute example_attribute = __ATTR(example_file, 0444, example_show, NULL);
static int __init sysfs_example_init(void)
{
int retval;
// 创建 kobject 和 sysfs 目录
example_kobj = kobject_create_and_add("example", kernel_kobj);
if (!example_kobj)
return -ENOMEM;
// 创建属性文件
retval = sysfs_create_file(example_kobj, &example_attribute.attr);
if (retval)
kobject_put(example_kobj); // 失败时清理 kobject
return retval;
}
static void __exit sysfs_example_exit(void)
{
kobject_put(example_kobj); // 删除 kobject 及其所有属性
}
module_init(sysfs_example_init);
module_exit(sysfs_example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of creating sysfs directories and files");
4. 操作说明
-
初始化阶段:在
sysfs_example_init
中,首先通过kobject_create_and_add
创建一个kobject
,它表示/sys/example
目录。然后,使用sysfs_create_file()
创建了一个名为example_file
的只读文件。 -
文件读取:
example_show
函数定义了文件读取时的行为,返回字符串Hello, Sysfs!\n
。 -
退出阶段:在
sysfs_example_exit
中,通过kobject_put()
删除kobject
,这会清理目录及其中的所有文件。
5. 总结
kobject_create_and_add()
是创建sysfs
目录的核心接口。sysfs_create_file()
用于创建与该目录关联的属性文件。- 属性文件的读取和写入通过
show
和store
函数进行定义,允许内核模块与用户空间交互。