[转]浅析/sys/class目录的创建流程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]浅析/sys/class目录的创建流程相关的知识,希望对你有一定的参考价值。

浅析/sys/class目录的创建流程

/sys/class目录创建成功之后,其他的driver就可以调用
struct class *class_create(struct module *owner, const char *name);
int class_register(struct class *cls);
成功向class添加自己的目录和attr文件了.

   因为sysfs是一个内存文件系统,所以文件的物理存储关系就需要使用sd来维护,因此sysfs_dirent即sd就类似于硬盘中的磁道.
   
sysfs文件系统是一个排它式的文件系统,不论被mount多少次都只产生一个sb超级块,
如果尝试再次mount,即尝试再次调用sysfs_get_sb获取另一个sb超级块,那么将执行atomic_inc(old->s_active);增加
已被mount的引用计数,然后如果s已经执行了alloc_super,那么调用destroy_super将其销毁,然后返回这个已被mount了的
super_block超级块old,
这样就实现了sysfs文件系统不论被mount多少次都只产生一个sb超级块的效果,所以取名为get_sb_single[luther.gliethttp]


int __init classes_init(void)
{
    class_kset = kset_create_and_add("class", NULL, NULL);//parent_kobj为NULL,"class"目录将创建到sysfs_root-sysfs的根‘/‘下面.
    if (!class_kset)
        return -ENOMEM;
//到这里/sys/class目录就已经创建成功了,并且当sysfs被mount到/sys目录之后,/sys/class就可以通过ls可见了[luther.gliethttp]
    /* ick, this is ugly, the things we go through to keep from showing up
     * in sysfs... */
    kset_init(&class_obj_subsys);
    kobject_set_name(&class_obj_subsys.kobj, "class_obj");
    if (!class_obj_subsys.kobj.parent)
        class_obj_subsys.kobj.parent = &class_obj_subsys.kobj;
    return 0;
}

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

    kset = kzalloc(sizeof(*kset), GFP_KERNEL);//申请内存空间
    if (!kset)
        return NULL;
    kobject_set_name(&kset->kobj, name);//kmalloc内存,然后拷贝name,最后kobj->name = name;
    kset->uevent_ops = uevent_ops;//uevent处理函数
    kset->kobj.parent = parent_kobj;//

    /*
     * 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管理的所有属性文件的read和write通用实现函数,show和store
    kset->kobj.kset = NULL;//该kset就是父,不再有其他kset来管理本kset

    return kset;
}
========================================
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
{
    va_list args;
    int retval;

    va_start(args, fmt);
    retval = kobject_set_name_vargs(kobj, fmt, args);
    va_end(args);

    return retval;
}
static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
                 va_list vargs)
{
    va_list aq;
    char *name;

    va_copy(aq, vargs);
    name = kvasprintf(GFP_KERNEL, fmt, vargs);//kmalloc之后赋值,返回到name指针
    va_end(aq);

    if (!name)
        return -ENOMEM;

    /* Free the old name, if necessary. */
    kfree(kobj->name);//释放原有的name,如果有

    /* Now, set the new name */
    kobj->name = name;//设置name

    return 0;
}
char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
{
    unsigned int len;
    char *p;
    va_list aq;

    va_copy(aq, ap);
    len = vsnprintf(NULL, 0, fmt, aq);
    va_end(aq);

    p = kmalloc(len+1, gfp);//申请内存空间
    if (!p)
        return NULL;

    vsnprintf(p, len+1, fmt, ap);//拷贝数据

    return p;//返回拷贝完数据之后的内存空间
}
========================================
//定义属性文件的read和write实现函数,show和store
static struct kobj_type kset_ktype = {
    .sysfs_ops    = &kobj_sysfs_ops,
    .release = kset_release,
};
struct sysfs_ops kobj_sysfs_ops = {
    .show    = kobj_attr_show,
    .store    = kobj_attr_store,
};
========================================
int kset_register(struct kset *k)//注册本kset到其父目录下
{
    int err;

    if (!k)
        return -EINVAL;

    kset_init(k);//初始化内部结构
    err = kobject_add_internal(&k->kobj);
    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);
}
static int kobject_add_internal(struct kobject *kobj)
{
    int error = 0;
    struct kobject *parent;

    if (!kobj)//必须存在
        return -ENOENT;

    if (!kobj->name || !kobj->name[0]) {//kobj必须有名字,因为他要作为目录或者文件显示在sysfs中
        pr_debug("kobject: (%p): attempted to be registered with empty "
             "name!\n", kobj);
        WARN_ON(1);
        return -EINVAL;
    }

    parent = kobject_get(kobj->parent);//获取其父kobj结构体,我们的class_kset它的parent为NULL

    /* join kset if set, use it as parent if we do not already have one */
    if (kobj->kset) {//我们的class_kset它的kset为NULL
        if (!parent)
            parent = kobject_get(&kobj->kset->kobj);//如果没有为该kobj指定parent,那么kobj所在的kset将作为
//本kobj的父目录
        kobj_kset_join(kobj);//将kobj链接到管理kobj的kset上去
        kobj->parent = parent;
    }

    pr_debug("kobject: ‘%s‘ (%p): %s: parent: ‘%s‘, set: ‘%s‘\n",
         kobject_name(kobj), kobj, __FUNCTION__,
         parent ? kobject_name(parent) : "",
         kobj->kset ? kobject_name(&kobj->kset->kobj) : "");

    error = create_dir(kobj);//在sysfs中为该kobj创建目录
    if (error) {
        kobj_kset_leave(kobj);
        kobject_put(parent);
        kobj->parent = NULL;

        /* be noisy on error issues */
        if (error == -EEXIST)
            printk(KERN_ERR "%s failed for %s with "
             "-EEXIST, don‘t try to register things with "
             "the same name in the same directory.\n",
             __FUNCTION__, kobject_name(kobj));
        else
            printk(KERN_ERR "%s failed for %s (%d)\n",
             __FUNCTION__, kobject_name(kobj), error);
        dump_stack();
    } else
        kobj->state_in_sysfs = 1;

    return error;
}
static void kobj_kset_join(struct kobject *kobj)
{
    if (!kobj->kset)
        return;

    kset_get(kobj->kset);
    spin_lock(&kobj->kset->list_lock);
    list_add_tail(&kobj->entry, &kobj->kset->list);//将该kobj添加到所属kset的链表上
    spin_unlock(&kobj->kset->list_lock);
}
static int create_dir(struct kobject *kobj)
{
    int error = 0;
    if (kobject_name(kobj)) {//返回kobj的name指针
        error = sysfs_create_dir(kobj);//在sysfs文件系统上创建目录
        if (!error) {
            error = populate_dir(kobj);//向创建的目录添加该kobj需要创建的文件
            if (error)
                sysfs_remove_dir(kobj);
        }
    }
    return error;
}
static inline const char *kobject_name(const struct kobject *kobj)
{
    return kobj->name;
}
int sysfs_create_dir(struct kobject * kobj)
{
    struct sysfs_dirent *parent_sd, *sd;
    int error = 0;

    BUG_ON(!kobj);

    if (kobj->parent)//如果该kobj有parent,那么在parent下面创建目录
        parent_sd = kobj->parent->sd;
    else
        parent_sd = &sysfs_root;//那么从sysfs的根‘/‘下面创建该kobj对应的目录[luther.gliethttp]

    error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);//ok,&sd现在包含了kobj在sysfs文件系统中的文件描述信息
    if (!error)
        kobj->sd = sd;//ok,现在该kobj已经在sysfs中,在parent目录下给自己建立了一个可见的目录,sd包含描述该目录的inode信息,因为sysfs是一个内存文件系统,所以文件的物理存储关系就需要使用sd来维护,因此sysfs_dirent即sd就类似于硬盘中的磁道.
    return error;
}
static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
         const char *name, struct sysfs_dirent **p_sd)
{
    umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;//目录属性标识
    struct sysfs_addrm_cxt acxt;
    struct sysfs_dirent *sd;
    int rc;

    /* allocate */
    sd = sysfs_new_dirent(name, mode, SYSFS_DIR);//申请sysfs_dirent结构体内存
    if (!sd)
        return -ENOMEM;
    sd->s_dir.kobj = kobj;

    /* link in */
    sysfs_addrm_start(&acxt, parent_sd);//准备acxt结构体中需要使用到的信息对象
    rc = sysfs_add_one(&acxt, sd);
    sysfs_addrm_finish(&acxt);//收尾工作,刷新parent目录时间

    if (rc == 0)
        *p_sd = sd;
    else
        sysfs_put(sd);

    return rc;
}
struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
{
    char *dup_name = NULL;
    struct sysfs_dirent *sd;

    if (type & SYSFS_COPY_NAME) {
        name = dup_name = kstrdup(name, GFP_KERNEL);
        if (!name)
            return NULL;
    }

    sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);//从sysfs_dir_cachep申请一个slab对象内存空间
    if (!sd)
        goto err_out1;

    if (sysfs_alloc_ino(&sd->s_ino))//从radix树上,顺序申请下一个空闲的唯一整数,作为sysfs文件系统上文件的inode唯一节点索引号[luther.gliethttp]
        goto err_out2;

    atomic_set(&sd->s_count, 1);
    atomic_set(&sd->s_active, 0);

    sd->s_name = name;
    sd->s_mode = mode;
    sd->s_flags = type;

    return sd;

 err_out2:
    kmem_cache_free(sysfs_dir_cachep, sd);
 err_out1:
    kfree(dup_name);
    return NULL;
}
static int sysfs_alloc_ino(ino_t *pino)
{
    int ino, rc;

 retry:
    spin_lock(&sysfs_ino_lock);
    rc = ida_get_new_above(&sysfs_ino_ida, 2, &ino);//从radix树上,顺序申请下一个空闲的唯一整数
    spin_unlock(&sysfs_ino_lock);

    if (rc == -EAGAIN) {
        if (ida_pre_get(&sysfs_ino_ida, GFP_KERNEL))
            goto retry;
        rc = -ENOMEM;
    }

    *pino = ino;
    return rc;
}
//准备acxt结构体中需要使用到的信息对象
void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
         struct sysfs_dirent *parent_sd)
{
    struct inode *inode;

    memset(acxt, 0, sizeof(*acxt));//先清0所有数据
    acxt->parent_sd = parent_sd;//填入parent_sd

    /* Lookup parent inode. inode initialization and I_NEW
     * clearing are protected by sysfs_mutex. By grabbing it and
     * looking up with _nowait variant, inode state can be
     * determined reliably.
     */
    mutex_lock(&sysfs_mutex);

    inode = ilookup5_nowait(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test,
                parent_sd);
//从存储着所有文件系统yaffs2,ext3,fat和sysfs等文件系统中被读入内存之后建立的内存inode节点映像的
//hash表inode_hashtable中查找parent_sd->s_ino节点号对应的inode节点指针[luther.gliethttp].
    if (inode && !(inode->i_state & I_NEW)) {
//ok,该inode确实已经存在,并且不是正在创建中.
        /* parent inode available */
        acxt->parent_inode = inode;//填入该parent的inode映像指针.

        /* sysfs_mutex is below i_mutex in lock hierarchy.
         * First, trylock i_mutex. If fails, unlock
         * sysfs_mutex and lock them in order.
         */
        if (!mutex_trylock(&inode->i_mutex)) {
            mutex_unlock(&sysfs_mutex);
            mutex_lock(&inode->i_mutex);
            mutex_lock(&sysfs_mutex);
        }
    } else
        iput(inode);//释放NEW的inode索引
}
//从存储着所有文件系统yaffs2,ext3,fat和sysfs等文件系统中被读入内存之后建立的内存inode节点映像的
//hash表inode_hashtable中查找parent_sd->s_ino节点号对应的inode节点指针[luther.gliethttp].
struct inode *ilookup5_nowait(struct super_block *sb, unsigned long hashval,
        int (*test)(struct inode *, void *), void *data)
{
    struct hlist_head *head = inode_hashtable + hash(sb, hashval);

    return ifind(sb, head, test, data, 0);
}
ifind
=>find_inode
=>if (inode->i_sb != sb) continue;//比较该inode是否属于该sb超级块,所以这样就可以区分不同超级块管理的相同节点号[luther.gliethttp]
=>if (!test(inode, data)) continue;//如果在hash短链上的inode属于指定的sb超级块,那么调用test函数进一步定位inode的正确性,对于sysfs文件系统,使用了sysfs_ilookup_test来进一步判断
static int sysfs_ilookup_test(struct inode *inode, void *arg)
{
    struct sysfs_dirent *sd = arg;
    return inode->i_ino == sd->s_ino;//只需要保证该sd的s_ino和inode->i_ino相同即说明s_ino号对应的节点已经在sysfs文件系统创建了[luther.gliethttp]
}
int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
{
    if (sysfs_find_dirent(acxt->parent_sd, sd->s_name)) {//检查欲填入的sd是否已经在parent目录下存在了
        printk(KERN_WARNING "sysfs: duplicate filename ‘%s‘ "
         "can not be created\n", sd->s_name);
        WARN_ON(1);
        return -EEXIST;
    }

    sd->s_parent = sysfs_get(acxt->parent_sd);//为新建的sd指定有效的parent目录结构体

    if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
        inc_nlink(acxt->parent_inode);

    acxt->cnt++;

    sysfs_link_sibling(sd);//sibling们,按inode节点号,从小到大顺序链接在一起

    return 0;
}
//因为sysfs是一种内存文件系统,所以所有文件数据信息都在内存中,文件的级联关系是使用单向链表来完成的,
//所以遍历parent目录结构体挂载的所有文件孩子和目录孩子的目录结构逐一比较name即可[luther.gliethttp].
struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
                 const unsigned char *name)
{
    struct sysfs_dirent *sd;

    for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)
        if (!strcmp(sd->s_name, name))
            return sd;
    return NULL;
}
//sibling们,按inode节点号,从小到大顺序链接在一起[luther.gliethttp]
static void sysfs_link_sibling(struct sysfs_dirent *sd)
{
    struct sysfs_dirent *parent_sd = sd->s_parent;
    struct sysfs_dirent **pos;

    BUG_ON(sd->s_sibling);

    /* Store directory entries in order by ino. This allows
     * readdir to properly restart without having to add a
     * cursor into the s_dir.children list.
     */
    for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) {
        if (sd->s_ino < (*pos)->s_ino)//如果新加入的sd的节点号小于该pos节点号,那么break.
            break;
    }
    sd->s_sibling = *pos;//将自己插入到pos前面
    *pos = sd;
}
//在classes_init
//=>kset_create_and_add
//=>kset_create
//=>kset->kobj.ktype = &kset_ktype;
static int populate_dir(struct kobject *kobj)
{
    struct kobj_type *t = get_ktype(kobj);
    struct attribute *attr;
    int error = 0;
    int i;

    if (t && t->default_attrs) {//在classes_init中对应的kset_ktype没有属性文件要创建,所以该执行将直接返回
        for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
            error = sysfs_create_file(kobj, attr);
            if (error)
                break;
        }
    }
    return error;
}
//向sysfs文件系统kobj所在目录添加文件
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
{
    BUG_ON(!kobj || !kobj->sd || !attr);

    return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);

}
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
         int type)
{
    umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;//普通文件属性标识
    struct sysfs_addrm_cxt acxt;
    struct sysfs_dirent *sd;
    int rc;

    sd = sysfs_new_dirent(attr->name, mode, type);//同create_dir
    if (!sd)
        return -ENOMEM;
    sd->s_attr.attr = (void *)attr;

    sysfs_addrm_start(&acxt, dir_sd);//同create_dir
    rc = sysfs_add_one(&acxt, sd);//同create_dir
    sysfs_addrm_finish(&acxt);

    if (rc)
        sysfs_put(sd);

    return rc;
}

以上是关于[转]浅析/sys/class目录的创建流程的主要内容,如果未能解决你的问题,请参考以下文章

bootrom启动流程转

(转)虚拟文件系统(VFS)浅析

OkHttp 流程浅析 - NoHarry的博客

ESXI中,Centos7虚拟机扩容根目录

epoll监测不到/sys/class目录下的读写事件?

epoll监测不到/sys/class目录下的读写事件?