sysfs目录结构 与 kobject 功能分析

kobject

/sys 下的文件夹,比如 "dev"、"kernel" 是通过 kobject_create_and_add 创建

一个 struct kobject 类型变量,对应 /sys 内的一个文件夹,"dev" 是 kobject 的名字,也是文件夹的名字

 

 创建 "block" 和 "char" 指定了父 kobject 为 "dev",所以 "dev" 是它们的父文件夹

 

如果 kobj->name = "dev",则 kobj->sd 表示的就是 "dev" 这个文件夹的句柄,kobj->sd->priv 就是 kobj 本身

ktype

每个kobject对象都内嵌有一个ktype,该结构定义了kobject在创建和删除时所采取的行为,可以理解为对象的属性。每个kobject必须关联一个ktype。由于通用性,多个Kobject可能共用同一个属性操作集,因此把Ktype独立出来了。

// include/linux/kobject.h
struct kobj_type {
    /* 处理对象终结的回调函数。该接口应该由具体对象负责填充。 */
    void (*release)(struct kobject *kobj);
    /* 该类型kobj的sysfs操作接口。 */
    const struct sysfs_ops *sysfs_ops;
    /* 该类型kobj自带的缺省属性(文件),这些属性文件在注册kobj时,直接pop为该目录下的文件。 */
    struct attribute **default_attrs;
    /*child_ns_type/namespace 是 文件系统命名空间相关)略*/
    const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
    const void *(*namespace)(struct kobject *kobj);
};

// linux/sysfs.h
struct sysfs_ops {
    ssize_t (*show)(struct kobject *, struct attribute *, char *);
    ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};

struct attribute {
    const char      *name;
    umode_t         mode;
};

 

当kobject的引用计数为0时,通过release方法来释放相关的资源。

attribute为属性,每个属性在sysfs中都有对应的属性文件。

sysfs_op的两个方法用于实现读取和写入属性文件时应该采取的行为。

kset

创建 /sys 内的文件夹还可以用 kset_create_and_add

创建 struct kset 类型变量,同时也创建了其成员变量 kobject,命名为 "slab",同时指定 kobject 的 parent 为 kernel_kobj,所以 "slab" 的父文件夹为 "kernel"

 

如果用 kobject_create 创建一个 kobject 后,指定其成员变量 kset 为 slab_kset,再使用 kobject_add 把该 kobject 添加到系统,该 kobject 的父 kobject 可以为 "slab" ,并且把该 kobject 加到 slab_kset 的链表里,后面就可以通过 slab_kset 遍历其内的所有 kobject

 

kobject与kset的关系

下面这张图非常经典。最下面的kobj都属于同一个kset,同时这些kobj的父对象就是kset内嵌的kobj

sysfs角度而言,kset代表一个文件夹,而下面的kobj就是这个文件夹里面的内容,而内容有可能是文件也有可能是文件夹。

 

kobj状态变动需要向用户空间发生消息,可以调用kobj所属kset.uevent_ops的函数,比如设备和驱动匹配上了通知用户空间的udev在/dev文件夹下创建设备节点。

/sys/ 下的文件

kobject已经被映射为文件目录。所有的对象层次一个不少的映射成sys下的目标结构。

sysfs仅仅是一个漂亮的树,但是没有提供实际数据的文件。

默认的文件集合是通过Kobject的ktype字段提供的。因此所有具有相同类型的kobject在它们对应的sysfs目录下都拥有相同的默认文件集合。

kobj_type字段含有一个字段——default_attrs,它是一个attribute结构体数组。这些属性负责将内核数据映射成sysfs中的文件。

struct sysfs_ops中包含show和store两个函数指针,它们分别在sysfs文件读和文件写时调用。

kobj_type.sysfs_ops 一般为 kobj_sysfs_ops, 其内的两个函数 kobj_attr_show() 和 kobj_attr_store() 是linux 内核提供的两个函数,函数内调用不同属性自定义的 show() 和 store()

/* default kobject attribute operations */
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);
    if (kattr->show)
        ret = kattr->show(kobj, kattr, buf);
    return ret;
}

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

const struct sysfs_ops kobj_sysfs_ops = {
    .show    = kobj_attr_show,
    .store    = kobj_attr_store,
};

由于 默认的 show() 和 store() 内 使用了 container_of(attr, struct kobj_attribute, attr),所以可以把属性文件定义为:

static struct kobj_attribute delimiters_attribute =
    __ATTR(delimiters, 0644, punc_show, punc_store);

struct kobj_attribute {
    struct attribute attr;
    ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
            char *buf);
    ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
             const char *buf, size_t count);
};
 
struct attribute {
    const char      *name;
    umode_t         mode;
}

 

从而可以为每个属性文件指定各自 show() 和 store() ,上面代码指定了属性文件 delimiters 的 show() 和 store() 分别为 punc_show() 和 punc_store()

创建/删除新属性

一些特别情况下会碰到特殊的kobject实例,它希望(甚至必须)有自己的属性——也许是通用属性没包含那些需要的数据或者函数。

因此使用sysfs_create_file()接口在默认集合上添加新属性:

#include <linux/sysfs.h>
int sysfs_create_file(struct kobject *kobj,
                      const struct attribute *attr);
int sysfs_create_files(struct kobject *kobj,
                       const struct attribute **attr);

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

创建/删除软链接

除了添加文件外,可能还要创建符号连接。在sysfs中创建一个符号连接方式。

int  sysfs_create_link(struct kobject *kobj, struct kobject *target,
                       const char *name);
int sysfs_create_link_nowarn(struct kobject *kobj,
                             struct kobject *target,
                             const char *name);

该函数创建的符号连接名由name指定,连接则由kobj对应的目录映射到target指定的目录。如果成功,则返回零,如果失败,返回负的错误码。

 

sysfs目录结构

sysfs是一个基于ramfs的文件系统,在2.6内核开始引入,用来导出内核对象(kernel object)的数据、属性到用户空间。与同样用于查看内核数据的proc不同,sysfs只关心具有层次结构的设备信息,比如系统中的总线,驱动以及已经加载的模块等,而诸如PID等信息还是使用proc来管理。本质上,sysfs文件的层次结构就是基于内核中kset与kobject逻辑结构来组织的。从驱动开发的角度,/sysfs为我们提供了除了设备文件/dev/proc之外的另外一种通过用户空间访问内核数据的方式。想要使用sysfs,编译内核的时候需要定义CONFIG_SYSFS,可以通过mount -t sysfs sysfs /sys命令来挂载sysfs到"/sys"目录。

/sys/block/

块设备的存放目录,这是一个过时的接口,按照sysfs的设计理念,所有的设备都存放在"sys/devices/"同时在"sys/bus/"或(和)"sys/class/"存放相应的符号链接,所以现在这个目录只是为了提高兼容性的设计,里面的文件已经被全部替换成了符号链接,只有在编译内核的时候勾选CONFIG_SYSFS_DEPRECATED才会有这个目录。

/sys/bus/

bus包含了系统中所有的总线,为了使一个设备在sysfs中只有一个实例,很多目录都是使用符号链接的形式

/sys/class/

按照设备功能对系统设备进行分类的结果放在这个目录,如系统所有输入设备都会出现在 "/sys/class/input"之下。和sys/bus一样,sys/class最终的文件都是符号链接,这样可以保证整个系统中每一个设备都只有一个实例。

/sys/dev/

按照设备号对字符设备和块设备进行分类的结果放在这个目录,同样,文件依然是使用符号链接的形式链接到"sys/devices/"中的相应文件

/sys/devices/

如前所述,所有的设备文件实例都在"sys/devices/"目录下

"sys/class/","sys/bus/","sys/devices"是设备开发中最重要的几个目录。他们之间的关系可以用下图表示

 

 

/sys/fs

这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点,但目前只有 fuse,gfs2 等少数文件系统支持 sysfs 接口,一些传统的虚拟文件系统(VFS)层次控制参数仍然在 sysctl (/proc/sys/fs) 接口中

/sys/kernel

这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于 sysctl (/proc/sys/kernel) 接口中

/sys/module

这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在 /sys/module 中:编译为外部模块(ko文件)在加载后会出现对应的/sys/module/<module_name>/, 并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等;编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的 /sys/module/<module_name>, 这些模块的可用参数会出现在 /sys/modules//parameters/<param_name> 中,如 /sys/module/printk/parameters/time 这个可读写参数控制着内联模块 printk 在打印内核消息时是否加上时间前缀;所有内联模块的参数也可以由 "<module_name>.<param_name>="的形式写在内核启动参数上,如启动内核时加上参数 "printk.time=1" 与 向"/sys/module/printk/parameters/time" 写入1的效果相同;没有非0属性参数的内联模块不会出现于此。

/sys/power

这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

/sys/slab

(对应 2.6.23 内核,在 2.6.24 以后移至/sys/kernel/slab) 从2.6.23 开始可以选择 SLAB 内存分配器的实现,并且新的 SLUB(Unqueued Slab Allocator)被设置为缺省值;如果编译了此选项,在 /sys 下就会出现 /sys/slab ,里面有每一个 kmem_cache 结构体的可调整参数。对应于旧的 SLAB 内存分配器下的/proc/slabinfo 动态调整接口, 新式的 /sys/kernel/slab/<slab_name> 接口中的各项信息和可调整项显得更为清晰

 

例子

代码来自:samples/kobject/kobject-example.c

/*
 * Sample kobject implementation
 *
 * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
 * Copyright (C) 2007 Novell Inc.
 *
 * Released under the GPL version 2 only.
 *
 */
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>

/*
 * This module shows how to create a simple subdirectory in sysfs called
 * /sys/kernel/kobject-example  In that directory, 3 files are created:
 * "foo", "baz", and "bar".  If an integer is written to these files, it can be
 * later read out of it.
 */

static int foo;
static int baz;
static int bar;

/*
 * The "foo" file where a static variable is read from and written to.
 */
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
            char *buf)
{
    return sprintf(buf, "%d\n", foo);
}

static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
             const char *buf, size_t count)
{
    sscanf(buf, "%du", &foo);
    return count;
}

static struct kobj_attribute foo_attribute =
    __ATTR(foo, 0664, foo_show, foo_store);

/*
 * More complex function where we determine which variable is being accessed by
 * looking at the attribute for the "baz" and "bar" files.
 */
static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr,
              char *buf)
{
    int var;

    if (strcmp(attr->attr.name, "baz") == 0)
        var = baz;
    else
        var = bar;
    return sprintf(buf, "%d\n", var);
}

static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
               const char *buf, size_t count)
{
    int var;

    sscanf(buf, "%du", &var);
    if (strcmp(attr->attr.name, "baz") == 0)
        baz = var;
    else
        bar = var;
    return count;
}

static struct kobj_attribute baz_attribute =
    __ATTR(baz, 0664, b_show, b_store);
static struct kobj_attribute bar_attribute =
    __ATTR(bar, 0664, b_show, b_store);


/*
 * Create a group of attributes so that we can create and destroy them all
 * at once.
 */
static struct attribute *attrs[] = {
    &foo_attribute.attr,
    &baz_attribute.attr,
    &bar_attribute.attr,
    NULL,    /* need to NULL terminate the list of attributes */
};

/*
 * An unnamed attribute group will put all of the attributes directly in
 * the kobject directory.  If we specify a name, a subdirectory will be
 * created for the attributes with the directory being the name of the
 * attribute group.
 */
static struct attribute_group attr_group = {
    .attrs = attrs,
};

static struct kobject *example_kobj;

static int __init example_init(void)
{
    int retval;

    /*
     * Create a simple kobject with the name of "kobject_example",
     * located under /sys/kernel/
     *
     * As this is a simple directory, no uevent will be sent to
     * userspace.  That is why this function should not be used for
     * any type of dynamic kobjects, where the name and number are
     * not known ahead of time.
     */
    example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
    if (!example_kobj)
        return -ENOMEM;

    /* Create the files associated with this kobject */
    retval = sysfs_create_group(example_kobj, &attr_group);
    if (retval)
        kobject_put(example_kobj);

    return retval;
}

static void __exit example_exit(void)
{
    kobject_put(example_kobj);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");

 

代码来自:samples/kobject/kset-example.c

/*
 * Sample kset and ktype implementation
 *
 * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
 * Copyright (C) 2007 Novell Inc.
 *
 * Released under the GPL version 2 only.
 *
 */
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>

/*
 * This module shows how to create a kset in sysfs called
 * /sys/kernel/kset-example
 * Then tree kobjects are created and assigned to this kset, "foo", "baz",
 * and "bar".  In those kobjects, attributes of the same name are also
 * created and if an integer is written to these files, it can be later
 * read out of it.
 */


/*
 * This is our "object" that we will create a few of and register them with
 * sysfs.
 */
struct foo_obj {
    struct kobject kobj;
    int foo;
    int baz;
    int bar;
};
#define to_foo_obj(x) container_of(x, struct foo_obj, kobj)

/* a custom attribute that works just for a struct foo_obj. */
struct foo_attribute {
    struct attribute attr;
    ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
    ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
};
#define to_foo_attr(x) container_of(x, struct foo_attribute, attr)

/*
 * The default show function that must be passed to sysfs.  This will be
 * called by sysfs for whenever a show function is called by the user on a
 * sysfs file associated with the kobjects we have registered.  We need to
 * transpose back from a "default" kobject to our custom struct foo_obj and
 * then call the show function for that specific object.
 */
static ssize_t foo_attr_show(struct kobject *kobj,
                 struct attribute *attr,
                 char *buf)
{
    struct foo_attribute *attribute;
    struct foo_obj *foo;

    attribute = to_foo_attr(attr);
    foo = to_foo_obj(kobj);

    if (!attribute->show)
        return -EIO;

    return attribute->show(foo, attribute, buf);
}

/*
 * Just like the default show function above, but this one is for when the
 * sysfs "store" is requested (when a value is written to a file.)
 */
static ssize_t foo_attr_store(struct kobject *kobj,
                  struct attribute *attr,
                  const char *buf, size_t len)
{
    struct foo_attribute *attribute;
    struct foo_obj *foo;

    attribute = to_foo_attr(attr);
    foo = to_foo_obj(kobj);

    if (!attribute->store)
        return -EIO;

    return attribute->store(foo, attribute, buf, len);
}

/* Our custom sysfs_ops that we will associate with our ktype later on */
static const struct sysfs_ops foo_sysfs_ops = {
    .show = foo_attr_show,
    .store = foo_attr_store,
};

/*
 * The release function for our object.  This is REQUIRED by the kernel to
 * have.  We free the memory held in our object here.
 *
 * NEVER try to get away with just a "blank" release function to try to be
 * smarter than the kernel.  Turns out, no one ever is...
 */
static void foo_release(struct kobject *kobj)
{
    struct foo_obj *foo;

    foo = to_foo_obj(kobj);
    kfree(foo);
}

/*
 * The "foo" file where the .foo variable is read from and written to.
 */
static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
            char *buf)
{
    return sprintf(buf, "%d\n", foo_obj->foo);
}

static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
             const char *buf, size_t count)
{
    sscanf(buf, "%du", &foo_obj->foo);
    return count;
}

static struct foo_attribute foo_attribute =
    __ATTR(foo, 0664, foo_show, foo_store);

/*
 * More complex function where we determine which variable is being accessed by
 * looking at the attribute for the "baz" and "bar" files.
 */
static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
              char *buf)
{
    int var;

    if (strcmp(attr->attr.name, "baz") == 0)
        var = foo_obj->baz;
    else
        var = foo_obj->bar;
    return sprintf(buf, "%d\n", var);
}

static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
               const char *buf, size_t count)
{
    int var;

    sscanf(buf, "%du", &var);
    if (strcmp(attr->attr.name, "baz") == 0)
        foo_obj->baz = var;
    else
        foo_obj->bar = var;
    return count;
}

static struct foo_attribute baz_attribute =
    __ATTR(baz, 0664, b_show, b_store);
static struct foo_attribute bar_attribute =
    __ATTR(bar, 0664, b_show, b_store);

/*
 * Create a group of attributes so that we can create and destroy them all
 * at once.
 */
static struct attribute *foo_default_attrs[] = {
    &foo_attribute.attr,
    &baz_attribute.attr,
    &bar_attribute.attr,
    NULL,    /* need to NULL terminate the list of attributes */
};

/*
 * Our own ktype for our kobjects.  Here we specify our sysfs ops, the
 * release function, and the set of default attributes we want created
 * whenever a kobject of this type is registered with the kernel.
 */
static struct kobj_type foo_ktype = {
    .sysfs_ops = &foo_sysfs_ops,
    .release = foo_release,
    .default_attrs = foo_default_attrs,
};

static struct kset *example_kset;
static struct foo_obj *foo_obj;
static struct foo_obj *bar_obj;
static struct foo_obj *baz_obj;

static struct foo_obj *create_foo_obj(const char *name)
{
    struct foo_obj *foo;
    int retval;

    /* allocate the memory for the whole object */
    foo = kzalloc(sizeof(*foo), GFP_KERNEL);
    if (!foo)
        return NULL;

    /*
     * As we have a kset for this kobject, we need to set it before calling
     * the kobject core.
     */
    foo->kobj.kset = example_kset;

    /*
     * Initialize and add the kobject to the kernel.  All the default files
     * will be created here.  As we have already specified a kset for this
     * kobject, we don't have to set a parent for the kobject, the kobject
     * will be placed beneath that kset automatically.
     */
    retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
    if (retval) {
        kobject_put(&foo->kobj);
        return NULL;
    }

    /*
     * We are always responsible for sending the uevent that the kobject
     * was added to the system.
     */
    kobject_uevent(&foo->kobj, KOBJ_ADD);

    return foo;
}

static void destroy_foo_obj(struct foo_obj *foo)
{
    kobject_put(&foo->kobj);
}

static int __init example_init(void)
{
    /*
     * Create a kset with the name of "kset_example",
     * located under /sys/kernel/
     */
    example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
    if (!example_kset)
        return -ENOMEM;

    /*
     * Create three objects and register them with our kset
     */
    foo_obj = create_foo_obj("foo");
    if (!foo_obj)
        goto foo_error;

    bar_obj = create_foo_obj("bar");
    if (!bar_obj)
        goto bar_error;

    baz_obj = create_foo_obj("baz");
    if (!baz_obj)
        goto baz_error;

    return 0;

baz_error:
    destroy_foo_obj(bar_obj);
bar_error:
    destroy_foo_obj(foo_obj);
foo_error:
    kset_unregister(example_kset);
    return -EINVAL;
}

static void __exit example_exit(void)
{
    destroy_foo_obj(baz_obj);
    destroy_foo_obj(bar_obj);
    destroy_foo_obj(foo_obj);
    kset_unregister(example_kset);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");

 

posted @ 2023-03-05 19:40  流水灯  阅读(394)  评论(0编辑  收藏  举报