Linux设备驱动模型浅析4--sys文件系统的代码实现

一相关数据结构
         sys 文件系统中使用数据结构sysfs_dirent来记录sys文件系统中的每一个目录项,这些目录项包括了属性文件,目录,符号链接文件等。sysfs_dirent数据结构中记录了目录项与子目录项的关系,引用计数等,每一个kobject数据结构中都包含有一个sysfs_dirent数据结构。在有实际磁盘载体的文件系统中,目录内容都记录在对应的磁盘块里,对于sys文件系统这个虚拟的文件系统来说,sysfs_dirent就相当于存放内容的磁盘块了,记录相关信息。
struct sysfs_dirent {
atomic_t s_count;//目录项的引用计数
atomic_t s_active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
struct sysfs_dirent *s_parent;//指向其父目录项
struct sysfs_dirent *s_sibling;//将该目录项链接到父目录项的子目录项链表中
const char *s_name;//目录项的名字


const void *s_ns; //namespace的标签
union {
struct sysfs_elem_dir s_dir;//串接子目录项
struct sysfs_elem_symlink s_symlink;//记录了其指向的sysfs_dirent
struct sysfs_elem_attr s_attr;//若该目录项代表的是属性文件,
//保存相关的属性
struct sysfs_elem_bin_attr s_bin_attr;
};
unsigned int s_flags;//相关标志位
unsigned short s_mode;//读写等权限
ino_t s_ino;//目录项对应的inode的索引号
struct sysfs_inode_attrs *s_iattr;//该目录项的一些基本属性,比如时间戳等
};


sysfs_root用来表示sys文件系统根的sysfs_dirent:
struct sysfs_dirent sysfs_root = {
.s_name = "",
.s_count = ATOMIC_INIT(1),//引用计数为1
.s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),//该目录项为目 //录
.s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,//设置读写执行的权限
.s_ino = 1,//索引节点为1
};


二sys文件系统的创建
sysfs_mount()函数用来创建sysfs文件系统的主入口函数,在该函数中调用了sysfs_fill_super()来初始化sys fs对应的超级块数据结构:


static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;


sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = SYSFS_MAGIC;
sb->s_op = &sysfs_ops;//设置sys fs超级快对应的文件系统操作
sb->s_time_gran = 1;


/* get root inode, initialize and unlock it */
mutex_lock(&sysfs_mutex);
//sysfs_root代表了sys fs最基本的目录项
//对sysfs_root目录项产生一个对应的inode,并将inode和sysfs_dirent关联起来
inode = sysfs_get_inode(sb, &sysfs_root);
mutex_unlock(&sysfs_mutex);
if (!inode) {
pr_debug("sysfs: could not get root inode\n");
return -ENOMEM;
}


/* instantiate and link root dentry */
//生成根目录的dentry,该dentry是vfs中使用的dentry
root = d_alloc_root(inode);
if (!root) {
pr_debug("%s: could not get root dentry!\n",__func__);
iput(inode);
return -ENOMEM;
}
root->d_fsdata = &sysfs_root;
/指向sys文件系统根目录项的dentry
sb->s_root = root;
return 0;
}


三往sys fs文件系统中添加属性文件
用户可以在sys文件系统中创建一个目录(比如新加一个device)或者添加一个文件(比如为device新加一个属性),这里仅仅描述添加一个属性的过程。
在device_add()函数中会调用device_create_file()以在该设备目录下创建一个uenvet属性文件,而device_create_file()最终调用sysfs_create_fil e()来创建属性文件。
      error = device_create_file(dev, &uevent_attr);
static struct device_attribute uevent_attr =
__ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
    
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
//kobj->sd应该代表着该kobj在sys文件系统中的目录项
return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);



sysfs_add_file()又调用了sysfs_add_file_mode():
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
  int type)
{
return sysfs_add_file_mode(dir_sd, attr, type, attr->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;
int rc;


//为属性创建一个新的目录项
sd = sysfs_new_dirent(attr->name, mode, type);
if (!sd)
return -ENOMEM;
//将属性记入
sd->s_attr.attr = (void *)attr;
sysfs_dirent_init_lockdep(sd);
//acxt专门用来将目录项插入到sysfs中,start里面
//主要记录了该目录项的父目录项
sysfs_addrm_start(&acxt, dir_sd);
//将该目录项加入到sysfs中
rc = sysfs_add_one(&acxt, sd);
sysfs_addrm_finish(&acxt);
if (rc)
sysfs_put(sd);
return rc;
}
在把属性文件加入的过程,简化来说,仅仅是把文件目录项对应的sysfs_dirent加入到了父目录项sysfs_dirent的链表中。在此过程中不会创建vfs的inode数据结构,对应的inode创建在sysfs_get_inode()函数中实现,该函数在sysfs_lookup()函数中被调用,而sysfs_lookup函数属于sys文件系统inode的inode_operations中的lookup()方法,即在目录中查找一个文件的方法。可见sys文件系统目录项对应的inode是在解析文件路径时形成的。


四open  sys文件系统中的文件
    在这里简要描述一下使用系统调用open来open sys文件系统中文件的过程,假设该文件已经存在,这个流程也是通用的vfs open系统调用的流程。
open()系统调用的服务例程是sys_open()函数,执行如下操作:
1用getname()读取从进程地址空间传过来的文件路径名。
2调用get_unused_fd()在current->files->fd中查找一个空的位置。
3调用filep_open()函数:
a把访问模式标志copy到namei_flags标志中。
b调用open_namei(),参数为路径名,nameidata数据结构地址,修改的访问模式标志。open_namei()会去解析路径名,对路径名上的每一个目录项,都会去查找对应的 dentry结构,若dentry结构不存在,则会创建一个dentry结构,创建对应目录项的inode数据结构。该过程在sysfs_lookup()中实现。(注意,此处的dentry是vfs中的目录项 dentry,而不是sys fs特有的sysfs_dentry);
c调用dentry_open()函数,传递给的参数是访问模式标志,目录项对象的地址,和相关的已安装文件系统对象,执行如下操作:
(1)分配文件对象,根据传递给系统调用的访问模式标志初始化文件对象的f_flags和f_mode字段。
(2)初始化文件对象的f_fentry和f_vfsmnt字段。
(3)把f_op字段设置为相应索引节点对象i_fop字段的内容。
(4)把文件对象插入到文件系统超级快的s_files字段链表中。
(5)调用文件操作的open方法。
(6)返回文件对象的地址。
4把current->files->fd[fd]置为由dentry_open()返回的文件对象的地址。
5返回fd.


static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
struct dentry *ret = NULL;
struct dentry *parent = dentry->d_parent;//指向要查找目录项的父目录项
struct sysfs_dirent *parent_sd = parent->d_fsdata;//指向父目录项的sysfs_dirent结构
struct sysfs_dirent *sd;
struct inode *inode;
enum kobj_ns_type type;
const void *ns;


mutex_lock(&sysfs_mutex);


type = sysfs_ns_type(parent_sd);
ns = sysfs_info(dir->i_sb)->ns[type];


//在父目录项的sysfs_dirent中查找是否存在此子目录项
sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name);


/* no such entry */
if (!sd) {
    ret = ERR_PTR(-ENOENT);
goto out_unlock;
}


//为该目录项创建对应的inode,将inode和sysfs_dirent关联,并
//初始化inode
inode = sysfs_get_inode(dir->i_sb, sd);
if (!inode) {
ret = ERR_PTR(-ENOMEM);
goto out_unlock;
}


/* instantiate and hash dentry */
ret = d_find_alias(inode);
if (!ret) {
d_set_d_op(dentry, &sysfs_dentry_ops);
//该目录项对应的dentry->d_fsdata 指向目录项对应的sysfs_dirent
dentry->d_fsdata = sysfs_get(sd);
//将目录项的dentry和inode关联
d_add(dentry, inode);
} else {
d_move(ret, dentry);
iput(inode);
}


 out_unlock:
mutex_unlock(&sysfs_mutex);
return ret;
}
struct inode * sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd)
{
struct inode *inode;


inode = iget_locked(sb, sd->s_ino);
if (inode && (inode->i_state & I_NEW))
sysfs_init_inode(sd, inode);


return inode;
}
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
{
struct bin_attribute *bin_attr;


//inode->i_private指向目录项的sysfs_dirent
inode->i_private = sysfs_get(sd);
inode->i_mapping->a_ops = &sysfs_aops;
inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
inode->i_op = &sysfs_inode_operations;


set_default_inode_attr(inode, sd->s_mode);
sysfs_refresh_inode(sd, inode);


//根据不同的文件类型,设置inode->i_op不同的操作描述符,该描述符将被赋值给对应的file文件对象
switch (sysfs_type(sd)) {
case SYSFS_DIR:
inode->i_op = &sysfs_dir_inode_operations;
inode->i_fop = &sysfs_dir_operations;
break;
case SYSFS_KOBJ_ATTR:
inode->i_size = PAGE_SIZE;
inode->i_fop = &sysfs_file_operations;
break;
case SYSFS_KOBJ_BIN_ATTR:
bin_attr = sd->s_bin_attr.bin_attr;
inode->i_size = bin_attr->size;
inode->i_fop = &bin_fops;
break;
case SYSFS_KOBJ_LINK:
inode->i_op = &sysfs_symlink_inode_operations;
break;
default:
BUG();
}


unlock_new_inode(inode);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值