sysfs单独提出来说比较难,因为它离不开kobject。如果遇到kobject的话,我会笼统的解释一下。
这编涉及源代码,版本为linux-3.2.36
sysfs刚开始是为了方便电源管理而提供的一种设备拓扑结构,现在发展为设备结构树导出的一个文件系统,先透露一下kobject建立了设备的层次结构(设备结构树),sysfs就是利用vfs的接口去读写kobject的层次结构建立起来的文件系统。
创建目录
我们用dentry和inode来描述一个文件
当你在/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出错是权限问题,一般的文件系统用vfs的generic_permission(inode, mask);检查权限,而sysfs的多判断了 if (mask & MAY_NOT_BLOCK) return -ECHILD;//未给出的过程。
sysfs的inode没提供create方法,那sysfs目录是如何创建的?先说一个结构struct sysfs_dirent,dirent = directory entry(目录实体),dentry(目录项)与kobject联系就是通过sysfs_dirent。所以目录的创建主要是sysfs_dirent的创建。所有的dentry用d_parent和d_child联系起来的。sysfs_dirent的是靠红黑树联系的。
下面看sysfs的create
每个设备模型会调用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时,调用printk和panic打印错误
if (kobj->parent)
parent_sd = kobj->parent->sd;
else//没父kobject节点就让parent_sd指向sysfs_root。sysfs_root的引索号为1,
/*
在/下执行ls –i看出/sys为1,所以就代表/sys,sysfs_root.i_ino = 1,i_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还会调用inode的lookup去生成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的内核只有file的inode滞后生成
我现在的3.2.36dir和file都滞后生成。
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;
}
可以看出没有生成inode,inode生成滞后到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.name的sysfs_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->ops(ops = 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结构的数据。
write和read差不多
sysfs_write_file()->flush_write_buffer()->ops->store()
store和show都是kobject中一个结构体的成员。
小结:
读完这篇文章,应该能理解/sysfs的目录和文件的创建和读写,以及/sysfs中文件的实体是什么,读写最终访问的是什么。
sysfs就说到这,有疑问或发现错误的话,请回复,我会第一时间解答或修改。谢谢!!!