linux sysfs文件系统(二)初始化

sysfs文件系统是linux系统启动的时候第一个初始化的文件系统。

1 sysfs_init

start_kernel

    --------->vfs_caches_init

         ------------>mnt_init

                -------------->sysfs_init

在sysfs_init函数中,对sysfs文件系统根目录的dentry,super_block,inode,挂载点实例mount 等这些重要的数据结构做初始化。

int __init sysfs_init(void)
{
	int err = -ENOMEM;
        //创建用于分配sysfs_dirent的slab
	sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
					      sizeof(struct sysfs_dirent),
					      0, 0, NULL);
	if (!sysfs_dir_cachep)
		goto out;

	err = sysfs_inode_init();
	if (err)
		goto out_err;
        //注册sysfs文件系统结构
	err = register_filesystem(&sysfs_fs_type);
	if (!err) {
		sysfs_mnt = kern_mount(&sysfs_fs_type); //调用文件系统的mount函数挂载文件系统
		if (IS_ERR(sysfs_mnt)) {
			printk(KERN_ERR "sysfs: could not mount!\n");
			err = PTR_ERR(sysfs_mnt);
			sysfs_mnt = NULL;
			unregister_filesystem(&sysfs_fs_type);
			goto out_err;
		}
	} else
		goto out_err;
out:
	return err;
out_err:
	kmem_cache_destroy(sysfs_dir_cachep);
	sysfs_dir_cachep = NULL;
	goto out;
}

register_filesystem函数就是把sysfs_fs_type注册进file_systems全局结构中,每个文件系统都会有一个file_system_type结构,并且这些结构最终都会放入file_systems全局结构,以便于查找。sysfs的结构如下:

static struct file_system_type sysfs_fs_type = {
	.name		= "sysfs",
	.mount		= sysfs_mount,
	.kill_sb	= sysfs_kill_sb,
	.fs_flags	= FS_USERNS_MOUNT,
};

mount函数用于挂载特定的文件系统,而kill_sb则用于释放相关的操作。接着调用kern_mount,kern_mount调用完以后返回vfsmount结构,该结构包含于挂载点实例mount中,通过该结构可以方便的找到mount。

sysfs_init

     --------->kern_mount

         ----------->kern_mount_data

               ------------>vfs_kern_mount

struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
	struct mount *mnt;
	struct dentry *root;

	if (!type)
		return ERR_PTR(-ENODEV);

	mnt = alloc_vfsmnt(name); //分配一个mount结构,并对其进行初始化
	if (!mnt)
		return ERR_PTR(-ENOMEM);

	if (flags & MS_KERNMOUNT)
		mnt->mnt.mnt_flags = MNT_INTERNAL;

	root = mount_fs(type, flags, name, data); //挂载的核心函数,并返回该文件系统的root dentry
	if (IS_ERR(root)) {
		free_vfsmnt(mnt);
		return ERR_CAST(root);
	}

	mnt->mnt.mnt_root = root; // root dentry
	mnt->mnt.mnt_sb = root->d_sb; //super block
	mnt->mnt_mountpoint = mnt->mnt.mnt_root; //挂载点
	mnt->mnt_parent = mnt;
	br_write_lock(&vfsmount_lock);
	list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);//把挂载点放入s_mounts链表
	br_write_unlock(&vfsmount_lock);
	return &mnt->mnt;
}

可以看到,核心函数是mount_fs:

struct dentry *
mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
{
	struct dentry *root;
	struct super_block *sb;
	char *secdata = NULL;
	int error = -ENOMEM;

	if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
		secdata = alloc_secdata();
		if (!secdata)
			goto out;

		error = security_sb_copy_data(data, secdata);
		if (error)
			goto out_free_secdata;
	}
//调用特定文件系统相关的mount 函数
	root = type->mount(type, flags, name, data);
	if (IS_ERR(root)) {
		error = PTR_ERR(root);
		goto out_free_secdata;
	}
	sb = root->d_sb;
	BUG_ON(!sb);
	WARN_ON(!sb->s_bdi);
	WARN_ON(sb->s_bdi == &default_backing_dev_info);
	sb->s_flags |= MS_BORN;
//安全相关的函数,可以暂且跳过
	error = security_sb_kern_mount(sb, flags, secdata);
	if (error)
		goto out_sb;

	/*
	 * filesystems should never set s_maxbytes larger than MAX_LFS_FILESIZE
	 * but s_maxbytes was an unsigned long long for many releases. Throw
	 * this warning for a little while to try and catch filesystems that
	 * violate this rule.
	 */
	WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
		"negative value (%lld)\n", type->name, sb->s_maxbytes);

	up_write(&sb->s_umount);
	free_secdata(secdata);
	return root;
out_sb:
	dput(root);
	deactivate_locked_super(sb);
out_free_secdata:
	free_secdata(secdata);
out:
	return ERR_PTR(error);
}

对于sysfs文件系统,其特定的mount函数是sysfs_mount:

static struct dentry *sysfs_mount(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
{
	struct sysfs_super_info *info;
	enum kobj_ns_type type;
	struct super_block *sb;
	int error;

	if (!(flags & MS_KERNMOUNT) && !current_user_ns()->may_mount_sysfs)
		return ERR_PTR(-EPERM);

	info = kzalloc(sizeof(*info), GFP_KERNEL);
	if (!info)
		return ERR_PTR(-ENOMEM);

	for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
		info->ns[type] = kobj_ns_grab_current(type);
        //分配super_block,并初始化super_block
	sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info);
	if (IS_ERR(sb) || sb->s_fs_info != info)
		free_sysfs_super_info(info);
	if (IS_ERR(sb))
		return ERR_CAST(sb);
	if (!sb->s_root) {  
                //为该文件系统的根目录初始化dentry和inode
		error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
		if (error) {
			deactivate_locked_super(sb);
			return ERR_PTR(error);
		}
		sb->s_flags |= MS_ACTIVE;
	}

	return dget(sb->s_root);
}

先看sget,该函数的主要作用就是为特定文件系统分配一个super block结构,并初始化该结构,最后返回一个super block结构。然后调用sysfs_fill_super进一步初始化。

sysfs_mount

     --------->sysfs_fill_super

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;//设置super block的操作函数
	sb->s_time_gran = 1;

	/* get root inode, initialize and unlock it */
	mutex_lock(&sysfs_mutex);
	inode = sysfs_get_inode(sb, &sysfs_root); //分配并初始化该文件系统根目录的inode
	mutex_unlock(&sysfs_mutex);
	if (!inode) {
		pr_debug("sysfs: could not get root inode\n");
		return -ENOMEM;
	}

	/* instantiate and link root dentry */
	root = d_make_root(inode); //分配并初始化该文件系统根目录的dentry
	if (!root) {
		pr_debug("%s: could not get root dentry!\n",__func__);
		return -ENOMEM;
	}
	root->d_fsdata = &sysfs_root; //把根目录的sysfs_dirent放入dentry的d_fsdata中
	sb->s_root = root;//在super block中记录下该根目录的dentry
	sb->s_d_op = &sysfs_dentry_ops;
	return 0;
}

上面函数比较重要的是sysfs_get_inode,该函数分配并初始化了该文件系统根目录的inode结构,然后d_make_root函数为该文件系统的根目录分配了dentry结构,最后一个很重要的操作是root->d_fsdata = &sysfs_root;把根目录的sysfs_dirent放入dentry的d_fsdata,前面已经说过在,在sysfs文件系统中,不管是目录还是文件,都有一个sysfs_dirent结构相对应,而在vfs中,所有的目录和文件都需要由一个dentry结构,所以在这里就把根目录的dentry和sysfs_dirent关联起来。下面先来看一下sysfs_get_inode函数:

sysfs_mount

     --------->sysfs_fill_super

          ----------->sysfs_get_inode

struct inode * sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd)
{
	struct inode *inode;

	inode = iget_locked(sb, sd->s_ino);  //获取一个inode,如果之前没有过,则需要重新创建一个
	if (inode && (inode->i_state & I_NEW))
		sysfs_init_inode(sd, inode); //初始化该inode结构

	return inode;
}

主要关注sysfs_init_inode函数:

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

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

	/* initialize inode according to type */
	switch (sysfs_type(sd)) {
	case SYSFS_DIR:
		inode->i_op = &sysfs_dir_inode_operations;//该操作函数很重要,后面在从上级目录,查找下级目录的dentry的时候,如果该dentry还没有建立,则需要调用indoe操作函数的lookup函数来新建一个dentry
		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);
}

sysfs_init_inode函数比较重要的是根据是目录还是文件为其赋值不同的inode操作函数,操作函数会在目录搜索,或者文件操作中会用到。接着看d_make_root:

sysfs_mount

     --------->sysfs_fill_super

          ----------->d_make_root

struct dentry *d_make_root(struct inode *root_inode)
{
	struct dentry *res = NULL;

	if (root_inode) {
		static const struct qstr name = QSTR_INIT("/", 1);//该dentry的名字为‘/’,是该文件系统的root dentry

		res = __d_alloc(root_inode->i_sb, &name);//分配并初始化dentry
		if (res)
			d_instantiate(res, root_inode);
		else
			iput(root_inode);
	}
	return res;
}

__d_alloc函数不展开分析了,就是分配一个dentry,并初始化他,然后在d_instantiate函数把dentry和inode关联起来:dentry->d_inode = inode;

初始化完以后各个结构之间的关系大概如下:

每个文件系统,都有一个‘/’目录相对应,从上面的初始化可以看出,sysfs文件系统的根目录'/'是没有object结构相对应的。

2 kobject_create_and_add

接着会调用:

fs_kobj = kobject_create_and_add("fs", NULL);

该函数在当前文件系统的根目录下生成fs目录,后面注册的文件系统,应该都会在该fs下面添加新的目录,所以先要把该目录初始好。

start_kernel

    --------->vfs_caches_init

         ------------>mnt_init

                -------------->kobject_create_and_add

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
	struct kobject *kobj;
	int retval;

	kobj = kobject_create();//新建并初始化一个kobject结构
	if (!kobj)
		return NULL;

	retval = kobject_add(kobj, parent, "%s", name);//创建object目录文件的核心结构
	if (retval) {
		printk(KERN_WARNING "%s: kobject_add error: %d\n",
		       __func__, retval);
		kobject_put(kobj);
		kobj = NULL;
	}
	return kobj;
}

主要关注kobject_add函数:

kobject_create_and_add

    -------------->kobject_add_varg

           ----------------->kobject_add_internal

在kobject_add_varg中,先把kobj->parent设置为null,因为该文件系统顶层根目录是没有object结构的。

static int kobject_add_internal(struct kobject *kobj)
{
	int error = 0;
	struct kobject *parent;

	if (!kobj)
		return -ENOENT;

	if (!kobj->name || !kobj->name[0]) {
		WARN(1, "kobject: (%p): attempted to be registered with empty "
			 "name!\n", kobj);
		return -EINVAL;
	}

	parent = kobject_get(kobj->parent);

	/* join kset if set, use it as parent if we do not already have one */
	if (kobj->kset) {  //没有设置kset,不会走到这边
		if (!parent)
			parent = kobject_get(&kobj->kset->kobj);
		kobj_kset_join(kobj);
		kobj->parent = parent;
	}

	pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
		 kobject_name(kobj), kobj, __func__,
		 parent ? kobject_name(parent) : "<NULL>",
		 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

	error = create_dir(kobj);
	if (error) {
		kobj_kset_leave(kobj);
		kobject_put(parent);
		kobj->parent = NULL;

		/* be noisy on error issues */
		if (error == -EEXIST)
			WARN(1, "%s failed for %s with "
			     "-EEXIST, don't try to register things with "
			     "the same name in the same directory.\n",
			     __func__, kobject_name(kobj));
		else
			WARN(1, "%s failed for %s (error: %d parent: %s)\n",
			     __func__, kobject_name(kobj), error,
			     parent ? kobject_name(parent) : "'none'");
	} else
		kobj->state_in_sysfs = 1;

	return error;
}

核心函数为create_dir:

create_dir

       --------->sysfs_create_dir

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

	if (kobj->parent)
		parent_sd = kobj->parent->sd;
	else
		parent_sd = &sysfs_root; //由于kobject 没有设置kobj->parent,所以设置sysfs_dirent为sysfs_root

	if (!parent_sd)
		return -ENOENT;

	if (sysfs_ns_type(parent_sd))
		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;
}

create_dir

       --------->sysfs_create_dir

            -------------->create_dir

最终调用dir.c下面的create_dir函数:

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_dirent 结构
	if (!sd)
		return -ENOMEM;

	sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT);
	sd->s_ns = ns;
	sd->s_dir.kobj = kobj;//把kobject和新建的sysfs_dirent关联起来

	/* link in */
        //acxt结构只是个中间结构,用来传递sysfs_dirent数据
	sysfs_addrm_start(&acxt, parent_sd);
	rc = sysfs_add_one(&acxt, sd);
	sysfs_addrm_finish(&acxt);

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

	return rc;
}

在sysfs_add_one函数中,把新建的sysfs_dirent结构的s_parent设置为sysfs_root,并把该结构link到sysfs_root的孩子节点上。至此初始化完全,可以看一下相互结构关系:

可以看到,新建的fs目录,没有dentry和inode结构,和vfs文件系统对接,应该是需要dentry和inode结构。其实这两个结构,在遍历目录的时候会根据上层目录的inode节点的look_up操作函数会动态的创建,后面章节再具体分析该过程。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值