kset

1.kset结构

struct kset { /**<  内核的某个子系统中的同一类型的内核对象的集合 */

 struct list_head list;        /** 用于连接该kset中所有kobject以形成环形链表的链表头  */

 spinlock_t list_lock;         /**< Spinlock */

 struct kobject kobj;          /**< 这个KSet中嵌入的 kobject ,内嵌的kobject。这样kset本身也是一个kobject也被表现为一个目录*/

 struct kset_uevent_ops *uevent_ops; /**< 操作函数,支持热插拔事件的函数集 */

struct kobj_type    * ktype; /*指向该kset对象类型的指针*/

};

list: 包含在kset中的所有kobject被组织成一个双向循环链表的表头。

list_lock: 遍历这个kset中的所有kobject所需持有的锁。

kobject: 数据结构中内嵌的kobject对象(由kobj域表示),所有属于这个kset 的kobject对象的parent域均指向这个内嵌的对象。此外,kset的引用计数实际上就是内嵌的 kobject对象的引用计数。

kset_uevent_ops热插拔事件

热插拔事件是用内核空间发送到用户空间的通知。表明内核中的某些配置已经发生变化。用户空间则会根据这些信息做相应的处理。例如,U盘插入USB系统时,会产生一个热插拔事件,内核会捕捉到这个热插拔事件,然后调用/sbin/hotplug程序,该程序通知加载驱动程序来相应U盘的插入动作。

热插拔函数集的定义在include/linux/koject.h中

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

        //uevent函数可在热插拔程序执行前,向环境变量写值

};

filter()函数用于过滤掉不需要导出到用户空间的事件;uevent()函数用于导出一些环境变量给用户的热插拔事件处理程序;

以下宏用以查找指定kobject的kobj_type 指针: 

struct kobj_type *get_ktype(struct kobject *kobj);

这个函数其实就是从以上提到的这两个地方返回kobj_type指针,源码如下: 

static inline struct kobj_type * get_ktype(struct kobject * k)

{

    if (k->kset && k->kset->ktype)

        return k->kset->ktype;

    else 

        return k->ktype;

}

2.注册一个kset

int kset_register(struct kset *k)

{

    int err;

    if (!k)

        return -EINVAL;

    kset_init(k);

    err = kobject_add_internal(&k->kobj);    //将kset中的kobject添加进sysfs,函数将在后面讲解

    if (err)

        return err;

    kobject_uevent(&k->kobj, KOBJ_ADD);

    return 0;

}

void kset_init(struct kset *k)

{

    kobject_init_internal(&k->kobj);

    INIT_LIST_HEAD(&k->list);

    spin_lock_init(&k->list_lock);

}

我们使用函数kset_create_and_add()还可以一次性将kset创建并注册进sysyfs

struct kset *kset_create_and_add(const char *name,

                 struct kset_uevent_ops *uevent_ops,

                 struct kobject *parent_kobj)

{

    struct kset *kset;

    int error;

    kset = kset_create(name, uevent_ops, parent_kobj);    //根据参数创建一个kset

    if (!kset)

        return NULL;

    error = kset_register(kset);                //将kset注册进sysfs,函数在上面已经分析过

    if (error) {

        kfree(kset);

        return NULL;

    }

    return kset;

}

static struct kset *kset_create(const char *name,

                struct kset_uevent_ops *uevent_ops,

                struct kobject *parent_kobj)

{

    struct kset *kset;

    int retval;

    kset = kzalloc(sizeof(*kset), GFP_KERNEL);

    if (!kset)

        return NULL;

    retval = kobject_set_name(&kset->kobj, name);    //设置kobject名称

    if (retval) {

        kfree(kset);

        return NULL;

    }

    kset->uevent_ops = uevent_ops;

    kset->kobj.parent = parent_kobj;            //设置kset的kobject的父对象

    /*

     * The kobject of this kset will have a type of kset_ktype and belong to

     * no kset itself. That way we can properly free it when it is

     * finished being used.

     */

    kset->kobj.ktype = &kset_ktype;    //设置kset的kobject的默认属性

    kset->kobj.kset = NULL;

    return kset;

}

kset中嵌入了一个kobject,所以还有一些和kobject相似的函数如,

增加kset的引用,实际是调用kobject_get增加kset中的kobject的引用

static inline struct kset *kset_get(struct kset *k)

{

    return k ? to_kset(kobject_get(&k->kobj)) : NULL;    

}

减少kset的引用,实际是调用kobject_put减少kset中的kobject的引用

static inline void kset_put(struct kset *k)

{

    kobject_put(&k->kobj);

}

2.kset属性

默认属性

当创建kobject 时, 每个 kobject 都被给定一系列默认属性。这些属性保存在 kobj_type 结构中: 

struct kobj_type {

void (*release)(struct kobject *);

struct sysfs_ops *sysfs_ops;/*提供实现以下属性的方法*/

struct attribute **default_attrs; /*用于保存类型属性列表(指针的指针) */

}; 

struct attribute {

char *name;/*属性的名字( 在 kobject 的 sysfs 目录中显示)*/

struct module *owner;/*指向模块的指针(如果有), 此模块负责实现这个属性*/

mode_t mode; /*属性的保护位,modes 的宏定义在 :例如S_IRUGO 为只读属性等等*/

}; /*default_attrs 列表中的最后一个元素必须用 0 填充*/

sysfs 读写这些属性是由 kobj_type->sysfs_ops 成员中的函数完成的: 

struct sysfs_ops {

ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buffer);

ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size);

};

当用户空间读取一个属性时,内核会使用指向 kobject 的指针(kobj)和正确的属性结构(*attr)来调用show 方法,该方法将给定属性值编码进缓冲(buffer)(注意不要越界( PAGE_SIZE 字节)), 并返回实际数据长度。sysfs 的约定要求每个属性应当包含一个单个人眼可读值; 若返回大量信息,需将它分为多个属性.

也可对所有 kobject 关联的属性使用同一个 show 方法,用传递到函数的 attr 指针来判断所请求的属性。有的 show 方法包含对属性名字的检查。有的show 方法会将属性结构嵌入另一个结构, 这个结构包含需要返回属性值的信息,这时可用container_of 获得上层结构的指针以返回属性值的信息。

store 方法将存在缓冲(buffer)的数据( size 为数据的长度,不能超过 PAGE_SIZE )解码并保存新值到属性(*attr), 返回实际解码的字节数。store 方法只在拥有属性的写权限时才能被调用。此时注意:接收来自用户空间的数据一定要验证其合法性。如果到数据不匹配, 返回一个负的错误值。

非默认属性

虽然 kobject 类型的 default_attrs 成员描述了所有的 kobject 会拥有的属性,倘若想添加新属性到 kobject 的 sysfs 目录属性只需简单地填充一个attribute结构并传递到以下函数: 

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

/*若成功,文件以attribute结构中的名字创建并返回 0; 否则, 返回负错误码*/

/*注意:内核会调用相同的 show() 和 store() 函数来实现对新属性的操作,所以在添加一个新非默认属性前,应采取必要的步骤确保这些函数知道如何实现这个属性*/

若要删除属性,调用: 

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

/*调用后, 这个属性不再出现在 kobject 的 sysfs 入口。若一个用户空间进程可能有一个打开的那个属性的文件描述符,在这个属性已经被删除后,show 和 store 仍然可能被调用*/

二进制属性

sysfs 通常要求所有属性都只包含一个可读文本格式的值,很少需要创建能够处理大量二进制数据的属性。但当在用户空间和设备间传递不可改变的数据时(如上传固件到设备)就需要这个特性。二进制属性使用一个 bin_attribute 结构来描述: 

struct bin_attribute {

    struct attribute    attr;/*属性结构体*/

    size_t            size;/*这个二进制属性的最大大小(若无最大值则为0)*/

    void            *private;

    ssize_t (*read)(struct kobject *, char *, loff_t, size_t);

    ssize_t (*write)(struct kobject *, char *, loff_t, size_t);

/*read 和 write 方法类似字符驱动的读写方法;,在一次加载中可被多次调用,每次调用最大操作一页数据,且必须能以其他方式判断操作数据的末尾*/

    int (*mmap)(struct kobject *, struct bin_attribute *attr,

         struct vm_area_struct *vma);

};

/*二进制属性必须显式创建,不能以默认属性被创建,创建一个二进制属性调用:*/

int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);

/*删除二进制属性调用:*/

int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);

符号链接

sysfs 文件系统具有树型结构, 反映 kobject之间的组织层次关系。为了表示驱动程序和所管理的设备间的关系,需要额外的指针,其在 sysfs 中通过符号链接实现。 

/*在 sysfs 创建一个符号链接:*/

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

/*函数创建一个链接(name)指向target的 sysfs 入口作为 kobj 的一个属性,是一个相对连接,与它在sysfs 系统中的位置无关*/

/*删除符号连接调用:*/

void sysfs_remove_link(struct kobject *kobj, char *name); 

4.热插拔事件产生
一 个热插拔事件是一个从内核空间发送到用户空间的通知, 表明系统配置已经改变. 无论 kobject 被创建或删除,都会产生这种事件。热插拔事件会导致对 /sbin/hotplug 的调用, 它通过加载驱动程序, 创建设备节点, 挂载分区或其他正确动作响应事件。
热插拔事件的实际控制是通过一套存储于 kset_uevent_ops (《LDD3》中介绍的struct kset_hotplug_ops * hotplug_ops;在2.6.22.2中已经被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, char **envp,
            int num_envp, char *buffer, int buffer_size);
};

可以在 kset 结构的uevent_ops 成员中找到指向kset_uevent_ops结构的指针。
若在 kobject 中不包含指定的 kset , 内核将通过 parent 指针在分层结构中进行搜索,直到发现一个包含有kset的 kobject ; 接着使用这个 kset 的热插拔操作。
kset_uevent_ops 结构中的三个方法作用如下:
(1) filter 函数让 kset 代码决定是否将事件传递给用户空间。如果 filter 返回 0,将不产生事件。以磁盘的 filter 函数为例,它只允许kobject产生磁盘和分区的事件,源码如下:
static int block_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
 struct kobj_type *ktype = get_ktype(kobj);
    return ((ktype == &ktype_block) || (ktype == &ktype_part));
}
(2) 当调用用户空间的热插拔程序时,相关子系统的名字将作为唯一的参数传递给它。name 函数负责返回合适的字符串传递给用户空间的热插拔程序。
(3)热插拔脚本想得到的任何其他参数都通过环境变量传递。uevent 函数的作用是在调用热插拔脚本之前将参数添加到环境变量中。函数原型:
int (*uevent)(struct kset *kset, struct kobject *kobj, /*产生事件的目标对象*/
 char **envp,/*一个保存其他环境变量定义(通常为 NAME=value 的格式)的数组*/
 int num_envp, /*环境变量数组中包含的变量个数(数组大小)*/
 char *buffer, int buffer_size/*环境变量被编码后放入的缓冲区的指针和字节数(大小)*/
/*若需要添加任何环境变量到 envp, 必须在最后的添加项后加一个 NULL 入口,使内核知道数组的结尾*/
        );
/*返回值正常应当是 0,若返回非零值将终止热插拔事件的产生*/
热插拔事件的产生通常是由在总线驱动程序层的逻辑所控制。 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值