设备模型3之sysfs

sysfs单独提出来说比较难,因为它离不开kobject。如果遇到kobject的话,我会笼统的解释一下。

这编涉及源代码,版本为linux-3.2.36

sysfs刚开始是为了方便电源管理而提供的一种设备拓扑结构,现在发展为设备结构树导出的一个文件系统,先透露一下kobject建立了设备的层次结构(设备结构树),sysfs就是利用vfs的接口去读写kobject的层次结构建立起来的文件系统。

 

创建目录

我们用dentryinode来描述一个文件

当你在/sys执行

[root@localhost sys]# mkdir a

mkdir: 无法创建目录a:不允许的操作

[root@localhost sys]# touch a

touch: 无法触碰a:权限不够

why? inode_operations中有个create.

Vfs通过系统调用create()open()来调用改函数从而为dentry对象创建一个新的引所节点。

看一下ext2

const struct inode_operations ext2_dir_inode_operations = {

        .create         = ext2_create,

        .lookup         = ext2_lookup,

        .link           = ext2_link,

        .unlink         = ext2_unlink,

        .symlink        = ext2_symlink,

        .mkdir          = ext2_mkdir,

        .rmdir          = ext2_rmdir,

        .mknod          = ext2_mknod,

        .rename         = ext2_rename,

#ifdef CONFIG_EXT2_FS_XATTR

        .setxattr       = generic_setxattr,

        .getxattr       = generic_getxattr,

        .listxattr      = ext2_listxattr,

        .removexattr    = generic_removexattr,

#endif

        .setattr        = ext2_setattr,

        .get_acl        = ext2_get_acl,

};//可以看出有create\mkdir\rename等我们熟悉的操作。

再看Sysfs

const struct inode_operations sysfs_dir_inode_operations = {

        .lookup         = sysfs_lookup,

        .permission     = sysfs_permission,

        .setattr        = sysfs_setattr,

        .getattr        = sysfs_getattr,

        .setxattr       = sysfs_setxattr,

};//查找、权限检查。没有create\mkdir等操作

上面的mkdir报错应该是没有实现mkdir方法

touch出错是权限问题,一般的文件系统用vfsgeneric_permission(inode, mask);检查权限,而sysfs的多判断了 if (mask & MAY_NOT_BLOCK) return -ECHILD;//未给出的过程。

sysfs的inode没提供create方法,那sysfs目录是如何创建的?先说一个结构struct sysfs_direntdirent = directory entry(目录实体)dentry(目录项)kobject联系就是通过sysfs_dirent。所以目录的创建主要是sysfs_dirent的创建。所有的dentryd_parentd_child联系起来的。sysfs_dirent的是靠红黑树联系的。

下面看sysfscreate

每个设备模型会调用kobject_add_varg()(老的是kobject_add),kobject_add_internal()->create_dir()->sysfs_create_dir()->create_dir()

先不管kobject

int sysfs_create_dir(struct kobject * kobj)

{

        enum kobj_ns_type type;

        struct sysfs_dirent *parent_sd, *sd;     

        const void *ns = NULL;

        int error = 0;

 

        BUG_ON(!kobj);//在没定义HAVE_ARCH_BUG时,调用printkpanic打印错误

 

        if (kobj->parent)

                parent_sd = kobj->parent->sd;

        else//没父kobject节点就让parent_sd指向sysfs_rootsysfs_root的引索号为1

/*

/下执行ls –i看出/sys1,所以就代表/syssysfs_root.i_ino = 1i_ino是索引节点号,在一个文件系统中是惟一的,内核只要根据i_ino,就能计算出它对应的inode在介质上的位置。

*/

                parent_sd = &sysfs_root;

 

        if (sysfs_ns_type(parent_sd))//(sd->s_flags & SYSFS_NS_TYPE_MASK) >> //SYSFS_NS_TYPE_SHIFT;//namespace类型

                ns = kobj->ktype->namespace(kobj);

        type = sysfs_read_ns_type(kobj);

 

        error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);

        if (!error)

                kobj->sd = sd;

        return error;

}

 

static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,

        enum kobj_ns_type type, const void *ns, 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_get_dentry从用缓存中找,我们这是新建,缓存中没有,所以现在直接新建,sysfs_get_dentry还会调用inodelookup去生成inode。现在的代码在这没有生成inode。滞后生成

        if (!sd)

                return -ENOMEM;

 

        sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT);

        sd->s_ns = ns;

        sd->s_dir.kobj = kobj;

 

        /* link in */

        sysfs_addrm_start(&acxt, parent_sd);// acxt->parent_sd = parent_sd;将父节点拷入acxt

        rc = sysfs_add_one(&acxt, sd);

        sysfs_addrm_finish(&acxt);

 

        if (rc == 0)

                *p_sd = sd;

        else

                sysfs_put(sd);

 

        return rc;

}

 

看似create_dir()sysfs_create_dir()形成嵌套。但后面的create_dir和前面的不是一个函数。我贴的是后面的。

create_dir主要是sysfs_add_one最终调用__sysfs_add_one

int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)

{

        struct sysfs_inode_attrs *ps_iattr;

 

        if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) {//父子的namespace是否一样

                WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",

                        sysfs_ns_type(acxt->parent_sd)? "required": "invalid",

                        acxt->parent_sd->s_name, sd->s_name);

                return -EINVAL;

        }

 

        if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name))//检查是否存在

                return -EEXIST;

 

        sd->s_parent = sysfs_get(acxt->parent_sd);// atomic_inc(&sd->s_count);引用加一

 

        sysfs_link_sibling(sd);//

 

        /* Update timestamps on the parent */

        ps_iattr = acxt->parent_sd->s_iattr;

        if (ps_iattr) {

                struct iattr *ps_iattrs = &ps_iattr->ia_iattr;

                ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;//创建时间

        }

 

        return 0;

}

sysfs_link_sibling(sd);编历文件系统拓扑结构(红黑树),把sd加入这个树(新文件create)。不再贴源码了。目录(实质是struct sysfs_dirent创建完毕。

创建inode

简单说一下

2.6.10的内核只有fileinode滞后生成

我现在的3.2.36dirfile都滞后生成。

const struct inode_operations sysfs_dir_inode_operations = {

        .lookup         = sysfs_lookup,

        .permission     = sysfs_permission,

        .setattr        = sysfs_setattr,

        .getattr        = sysfs_getattr,

        .setxattr       = sysfs_setxattr,

};

在查找时,sysfs_lookup()->sysfs_get_inode()->iget_locked()-> alloc_inode()生成

再调用sysfs_init_inode()初始化。

inode只是一个形式。文件的实质还是在kobj中。

 

创建sysfs文件

这个小标题是一个函数的翻译。sysfs_create_file

在创建目录里,说过kobject_add_varg()会调用create_dir(),我没有贴,现在贴一点

                error = sysfs_create_dir(kobj);//我们上面说的创建目录

                if (!error) {//成功

                        error = populate_dir(kobj);

populate_dir()->sysfs_create_file()->sysfs_add_file()->sysfs_add_file_mode()

上面的逻辑就是先建目录,成功后建文件。下面看sysfs_add_file_mode

 

int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,

                        const struct attribute *attr, int type, mode_t amode)

{

        umode_t mode = (amode & S_IALLUGO) | S_IFREG;

        struct sysfs_addrm_cxt acxt;

        struct sysfs_dirent *sd;

        const void *ns;

        int rc;

 

        rc = sysfs_attr_ns(dir_sd->s_dir.kobj, attr, &ns);//做一些判断,不细说了

        if (rc)

                return rc;

        //下面的代码和上面建目录的create_dir()里的打同小异

        sd = sysfs_new_dirent(attr->name, mode, type);

        if (!sd)

                return -ENOMEM;

 

        sd->s_ns = ns;

        sd->s_attr.attr = (void *)attr;

        sysfs_dirent_init_lockdep(sd);

 

        sysfs_addrm_start(&acxt, dir_sd);

        rc = sysfs_add_one(&acxt, sd);

        sysfs_addrm_finish(&acxt);

 

        if (rc)

                sysfs_put(sd);

 

        return rc;

}

可以看出没有生成inodeinode生成滞后到lookup

 

读写

读目录

先看目录文件的操作方法

const struct file_operations sysfs_dir_operations = {

       .read           = generic_read_dir,

       .readdir        = sysfs_readdir,

       .release        = sysfs_dir_release,

       .llseek         = generic_file_llseek,

};

readdir()->getdents()//用户态

sys32 readdir()->vfs readdir()->sysfs readdir()//核心态

sysfs readdir代码不贴了,简单说一下,

dentry->d_fsdata指向的sysfs_dirent开始(dentry->d_fsdata就是对应dentry->d_name.namesysfs_dirent数据,d_name:目录项名字),编历dentry->dirent下的所有子sysfs_dirent。读出名字,回调函数filldir()会把文件名,文件类型等信息,按照一定的格式写入某个缓冲区。

 

读写普通文件

普通文件的操作方法

const struct file_operations sysfs_file_operations = {

       .read           = sysfs_read_file,

       .write          = sysfs_write_file,

       .llseek         = generic_file_llseek,

       .open           = sysfs_open_file,

       .release        = sysfs_release,

       .poll           = sysfs_poll,

};

 

读写之前要先要open()

open()//用户态

sys_open() -> filp_open()-> dentry_open() -> sysfs_open_file()//核心态

sysfs_open_file()

检查权限,创建一个 sysfs的缓冲区 sysfs_buffer buffer,并设置其sysfs_ops sysfs_buffer->opsops = kobj->ktype->sysfs_ops;就是我们kobject中的ops)。最后让file->private_data = buffer

 

read()//用户态

sys_read()->vfs_read()->sysfs_read_file()//核心态

sysfs_read_file()->fill_read_buffer()->ops->show()

到这就可以了show()就是kobject中一个结构体的成员。现在只要知道sysfs文件的实体就是kobject结构的数据。

 

writeread差不多

sysfs_write_file()->flush_write_buffer()->ops->store()

storeshow都是kobject中一个结构体的成员。

 

小结:

读完这篇文章,应该能理解/sysfs的目录和文件的创建和读写,以及/sysfs中文件的实体是什么,读写最终访问的是什么。

sysfs就说到这,有疑问或发现错误的话,请回复,我会第一时间解答或修改。谢谢!!!

你可能感兴趣的:(设备模型3之sysfs)