相信自己,快乐每一天
分类: LINUX
2014-02-19 10:26:43
原文地址:sysfs文件系统的初始化 作者:tq08g2z
首先,我们从文件系统的角度来看sysfs文件系统,看它的文件系统注册,文件系统的挂载,各个虚拟文件系统对象实例的建立等。
mnt_init()函数中,在建立了vfsmount对象缓冲,为vfsmount哈希表分配空间并初始化哈希表之后,紧接着就调用了sysfs_init()函数来初始化了sysfs文件系统。此时此刻,根文件系统可都还没有挂载呢。sysfs_init()定义为:
---------------------------------------------------------------------
fs/sysfs/mount.c
87
int __init sysfs_init(void)
88 {
89
int err = -ENOMEM;
90
91
sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",
92 sizeof(struct sysfs_dirent),
93
0, 0, NULL);
94
if (!sysfs_dir_cachep)
95 goto out;
96
97
err = sysfs_inode_init();
98
if (err)
99 goto out_err;
100
101 err =
register_filesystem(&sysfs_fs_type);
102 if (!err) {
103 sysfs_mount =
kern_mount(&sysfs_fs_type);
104 if (IS_ERR(sysfs_mount)) {
105 printk(KERN_ERR
"sysfs: could not mount!\n");
106 err =
PTR_ERR(sysfs_mount);
107 sysfs_mount = NULL;
108
unregister_filesystem(&sysfs_fs_type);
109 goto out_err;
110 }
111 } else
112 goto out_err;
113
out:
114 return err;
115
out_err:
116 kmem_cache_destroy(sysfs_dir_cachep);
117 sysfs_dir_cachep = NULL;
118 goto out;
119
}
---------------------------------------------------------------------
这个函数也在继续着建立各种slab缓存的过程。
1、调用kmem_cache_create()来建立sysfs_dirent结构的缓存,由全局变量sysfs_dir_cachep指向这个缓存。至于说这个结构到底是干什么用的,待我们稍后再说。
2、调用sysfs_inode_init(),
---------------------------------------------------------------------
fs/sysfs/inode.c
47
int __init sysfs_inode_init(void)
48
{
49 return
bdi_init(&sysfs_backing_dev_info);
50
}
---------------------------------------------------------------------
3、调用register_filesystem(&sysfs_fs_type)来注册sysfs文件系统。这个没什么好说的,无非就是将file_system_type对象sysfs_fs_type添加进file_systems指向的file_system_type对象链表中而已。这好像还是系统中注册的第一个文件系统呢。
4、调用kern_mount(&sysfs_fs_type)来挂载sysfs文件系统。kern_mount()是一个宏。其定义如下:
---------------------------------------------------------------------
iclude/linux/fs.h
1796
#define kern_mount(type) kern_mount_data(type, NULL)
---------------------------------------------------------------------
这就是一个函数调用的别名而已。再来看kern_mount_data()函数定义:
---------------------------------------------------------------------
fs/super.c
1032
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
1033
{
1034 return vfs_kern_mount(type,
MS_KERNMOUNT, type->name, data);
1035
}
---------------------------------------------------------------------
它只是根据传递进来的参数,选择适当的参数来调用vfs_kern_mount()。当是复习好了,我们再来仔仔细细的看下这个vfs_kern_mount()。它的定义为:
---------------------------------------------------------------------
fs/super.c
927
struct vfsmount *
928
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
*data)
929
{
930 struct vfsmount *mnt;
931 char *secdata = NULL;
932 int error;
933
934 if (!type)
935 return ERR_PTR(-ENODEV);
936
937 error = -ENOMEM;
938 mnt = alloc_vfsmnt(name);
939 if (!mnt)
940 goto out;
941
942 if (flags & MS_KERNMOUNT)
943 mnt->mnt_flags =
MNT_INTERNAL;
944
945 if (data &&
!(type->fs_flags & FS_BINARY_MOUNTDATA)) {
946 secdata = alloc_secdata();
947 if (!secdata)
948 goto out_mnt;
949
950 error =
security_sb_copy_data(data, secdata);
951 if (error)
952 goto out_free_secdata;
953 }
954
955 error = type->get_sb(type, flags, name,
data, mnt);
956 if (error < 0)
957 goto out_free_secdata;
958 BUG_ON(!mnt->mnt_sb);
959 WARN_ON(!mnt->mnt_sb->s_bdi);
960
961 error =
security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
962 if (error)
963 goto out_sb;
964
965 /*
966 * filesystems should never set
s_maxbytes larger than MAX_LFS_FILESIZE
967 * but s_maxbytes was an unsigned long
long for many releases. Throw
968 * this warning for a little while to
try and catch filesystems that
969 * violate this rule. This warning
should be either removed or
970 * converted to a BUG() in
971 */
972 WARN((mnt->mnt_sb->s_maxbytes
< 0), "%s set sb->s_maxbytes to "
973 "negative value
(%lld)\n", type->name, mnt->mnt_sb->s_maxbytes);
974
975 mnt->mnt_mountpoint =
mnt->mnt_root;
976 mnt->mnt_parent = mnt;
977 up_write(&mnt->mnt_sb->s_umount);
978 free_secdata(secdata);
979 return mnt;
980
out_sb:
981 dput(mnt->mnt_root);
982
deactivate_locked_super(mnt->mnt_sb);
983
out_free_secdata:
984 free_secdata(secdata);
985
out_mnt:
986 free_vfsmnt(mnt);
987
out:
988 return ERR_PTR(error);
989
}
---------------------------------------------------------------------
vfs_kern_mount()的四个参数分别是文件系统类型,挂载标志,设备名和文件系统私有数据。在挂载sysfs中,这四个参数的实例分别为sysfs_fs_type、MS_KERNMOUNT、"sysfs"和NULL。没有挂载目录,可见这个函数完成的都是与vfsmount和super_block有关的工作。来看下在我们挂载sysfs文件系统的过程中vfs_kern_mount()的操作:
a.调用alloc_vfsmnt(name)在vfsmount缓冲mnt_cache中分配vfsmount对象,分配内核缓冲区,将设备名复制到该内核缓冲区,并使vfsmount对象的mnt_devname字段指向这个缓冲区,然后初始化它的部分其他字段,并将其地址保存在局部变量mnt中。
b.设置mnt的挂载标志字段mnt->mnt_flags为MNT_INTERNAL。
c.调用sysfs_fs_type的get_sb()方法来分配并初始化一个超级块结构,一个sysfs的超级块结构。这才是让我们牵肠挂肚的那条语句啊。在这里我们也来看一下sysfs_fs_type的定义:
---------------------------------------------------------------------
fs/sysfs/mount.c
75
static int sysfs_get_sb(struct file_system_type *fs_type,
76 int flags, const char *dev_name, void
*data, struct vfsmount *mnt)
77
{
78 return get_sb_single(fs_type, flags, data,
sysfs_fill_super, mnt);
79
}
81
static struct file_system_type sysfs_fs_type = {
82 .name = "sysfs",
83 .get_sb = sysfs_get_sb,
84 .kill_sb = kill_anon_super,
85
};
---------------------------------------------------------------------
上面的那个get_sb()方法也即是sysfs_get_sb()函数。sysfs_get_sb()函数也是以传递进来的参数为依据,来形成适当的参数调用get_sb_single()而已。get_sb_single()的几个参数分别为sysfs_fs_type、MS_KERNMOUNT、NULL、sysfs_fill_super和刚刚获得的mnt。
---------------------------------------------------------------------
fs/super.c
899
int get_sb_single(struct file_system_type *fs_type,
900 int flags, void *data,
901 int (*fill_super)(struct super_block
*, void *, int),
902 struct vfsmount *mnt)
903
{
904 struct super_block *s;
905 int error;
906
907 s = sget(fs_type, compare_single,
set_anon_super, NULL);
908 if (IS_ERR(s))
909 return PTR_ERR(s);
910 if (!s->s_root) {
911 s->s_flags = flags;
912 error = fill_super(s, data,
flags & MS_SILENT ? 1 : 0);
913 if (error) {
914
deactivate_locked_super(s);
915 return error;
916 }
917 s->s_flags |= MS_ACTIVE;
918 } else {
919 do_remount_sb(s, flags, data, 0);
920 }
921 simple_set_mnt(mnt, s);
922 return 0;
923
}
---------------------------------------------------------------------
(1).调用sget()函数分配新的super_block对象,传递set_anon_super函数的地址作为参数。sget()函数会调用alloc_super(type)来分配一个super_block对象,alloc_super()如下处理新分配的super_block对象的各个字段:
INIT_LIST_HEAD(&s->s_files);
INIT_LIST_HEAD(&s->s_instances);
INIT_HLIST_HEAD(&s->s_anon);
INIT_LIST_HEAD(&s->s_inodes);
INIT_LIST_HEAD(&s->s_dentry_lru);
init_rwsem(&s->s_umount);
mutex_init(&s->s_lock);
lockdep_set_class(&s->s_umount,
&type->s_umount_key);
/*
* The locking rules for
s_lock are up to the
* filesystem. For example
ext3fs has different
* lock ordering than usbfs:
*/
lockdep_set_class(&s->s_lock,
&type->s_lock_key);
/*
* sget() can have s_umount
recursion.
*
* When it cannot find a
suitable sb, it allocates a new
* one (this one), and tries
again to find a suitable old
* one.
*
* In case that succeeds, it
will acquire the s_umount
* lock of the old one. Since
these are clearly distrinct
* locks, and this object
isn't exposed yet, there's no
* risk of deadlocks.
*
* Annotate this by putting
this lock in a different
* subclass.
*/
down_write_nested(&s->s_umount,
SINGLE_DEPTH_NESTING);
s->s_count
= S_BIAS;
atomic_set(&s->s_active,
1);
mutex_init(&s->s_vfs_rename_mutex);
mutex_init(&s->s_dquot.dqio_mutex);
mutex_init(&s->s_dquot.dqonoff_mutex);
init_rwsem(&s->s_dquot.dqptr_sem);
init_waitqueue_head(&s->s_wait_unfrozen);
s->s_maxbytes
= MAX_NON_LFS;
s->dq_op
= sb_dquot_ops;
s->s_qcop
= sb_quotactl_ops;
s->s_op
= &default_op;
s->s_time_gran
= 1000000000;
接下来,sget()函数会调用set_anon_super函数,用合适的方式设置超级块的s_dev字段:主设备号为0,次设备号不同于其他已安装的特殊文件系统。并设置super_block对象的s_bdi字段:
s->s_dev
= MKDEV(0, dev & MINORMASK);
s->s_bdi
= &noop_backing_dev_info;
随后sget()将设置super_block对象的s_type指向文件系统类型,文件系统名称type->name复制给super_block对象的s_id字段,将超级块对象添加进超级块对象链表super_blocks,将超级块对象添加进文件系统的同文件系统类型超级块链表type->fs_supers中。增加文件系统所在的文件系统的模块的引用计数。
(2).设置超级块的挂载标志字段为传递进来的挂载标志,也就是MS_KERNMOUNT。调用传递进来的方法fill_super()来进一步设置超级块对象,这个方法主要完成创建sysfs文件系统根目录目录项的工作。后面详细说明。
设置超级块挂在标志的MS_ACTIVE位。
(3).调用 simple_set_mnt(mnt, s)来将超级块对象和vfsmount对象mnt联系起来,其本质上是设置mnt->mnt_sb为超级块的地址,设置mnt->mnt_root为指向超级块的文件系统根目录的目录项,并增加该目录项引用计数。这个目录项在特定文件系统的fill_super()中构建。
(4).返回0。
d. vfs_kern_mount()中设置mnt->mnt_mountpoint为mnt->mnt_root,设置mnt->mnt_parent指向mnt。
e.返回mnt。在此,总结一下vfsmount对象个字段的值:
alloc_vfsmnt()分配
atomic_set(&mnt->mnt_count, 1);
INIT_LIST_HEAD(&mnt->mnt_hash);
INIT_LIST_HEAD(&mnt->mnt_child);
INIT_LIST_HEAD(&mnt->mnt_mounts);
INIT_LIST_HEAD(&mnt->mnt_list);
INIT_LIST_HEAD(&mnt->mnt_expire);
INIT_LIST_HEAD(&mnt->mnt_share);
INIT_LIST_HEAD(&mnt->mnt_slave_list);
INIT_LIST_HEAD(&mnt->mnt_slave);
#ifdef
CONFIG_SMP
mnt->mnt_writers = alloc_percpu(int);
if (!mnt->mnt_writers)
goto out_free_devname;
#else
mnt->mnt_writers = 0;
mnt->mnt_flags
= MNT_INTERNAL;
mnt->mnt_sb
= sb
mnt->mnt_root
= sb->s_root
mnt->mnt_mountpoint
= mnt->mnt_root
mnt->mnt_parent
= mnt