在kernal start函数中,创建sys文件系统的函数调用栈:
start_kernel
>vfs_caches_init
>mnt_init
>kernfs_init
>sysfs_init
当前分析位置:kernfs_init
start_kernel
>vfs_caches_init
>mnt_init
>kernfs_init <====当前分析位置
>sysfs_init
//为全局变量struct kmem_cache *kernfs_node_cache申请内存空间并初始化部分成员值
void __init kernfs_init(void)
{
/*
* the slab is freed in RCU context, so kernfs_find_and_get_node_by_ino
* can access the slab lock free. This could introduce stale nodes,
* please see how kernfs_find_and_get_node_by_ino filters out stale
* nodes.
*/
kernfs_node_cache = kmem_cache_create("kernfs_node_cache",
sizeof(struct kernfs_node),
0,
SLAB_PANIC | SLAB_TYPESAFE_BY_RCU,
NULL);
}
struct kmem_cache *
kmem_cache_create(const char *name, size_t size, size_t offset,
unsigned long flags, void (*ctor)(void *))
{
struct kmem_cache *ret = malloc(sizeof(*ret));
pthread_mutex_init(&ret->lock, NULL);
ret->size = size;
ret->nr_objs = 0;
ret->objs = NULL;
ret->ctor = ctor;
return ret;
}
当前分析位置:sysfs_init
start_kernel
>vfs_caches_init
>mnt_init
>kernfs_init
>sysfs_init <====当前分析位置
相关数据结构:
/*
通过kernfs_root创建的基数树并维护目录kernfs_node与id的关联,
可以通过树查找到id对应的kernfs_node,而目录kernfs_node之间的
层级父子关系则通过kernfs_node中的rb和dir维护
*/
struct kernfs_root {
/* published fields */
struct kernfs_node *kn;//树的第一个节点?
unsigned int flags; /* KERNFS_ROOT_* flags */
/* private fields, do not use outside kernfs proper */
struct idr ino_idr;//关键成员,基数树,维护目录kernfs_node与id的关联
u32 next_generation;//基数树已申请的节点总数,也是下一代将新添加的节点的序号
struct kernfs_syscall_ops *syscall_ops;//文件系统相关的操作函数集
/* list of kernfs_super_info of this root, protected by kernfs_mutex */
struct list_head supers;
wait_queue_head_t deactivate_waitq;
};
/*
* kernfs_node - the building block of kernfs hierarchy. Each and every
* kernfs node is represented by single kernfs_node. Most fields are
* private to kernfs and shouldn't be accessed directly by kernfs users.
*
* As long as s_count reference is held, the kernfs_node itself is
* accessible. Dereferencing elem or any other outer entity requires
* active reference.
*/
struct kernfs_node {
atomic_t count;
atomic_t active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
/*
* Use kernfs_get_parent() and kernfs_name/path() instead of
* accessing the following two fields directly. If the node is
* never moved to a different parent, it is safe to access the
* parent directly.
*/
struct kernfs_node *parent;//关键
const char *name;
struct rb_node rb;//被挂接在其父节点的所有子节点所挂载的红黑树中
const void *ns; /* namespace tag */
unsigned int hash; /* ns + name hash */ //关键
union {
struct kernfs_elem_dir dir;//关键
struct kernfs_elem_symlink symlink;
struct kernfs_elem_attr attr;
};
void *priv;//关键
union kernfs_node_id id;//关键
unsigned short flags;
umode_t mode;
struct kernfs_iattrs *iattr;
};
struct kernfs_elem_dir {
unsigned long subdirs;
/* children rbtree starts here and goes through kn->rb */
struct rb_root children;//指向本节点的所有子节点所挂载的红黑树的根节点的kernfs_node.rb
/*
* The kernfs hierarchy this directory belongs to. This fits
* better directly in kernfs_node but is here to save space.
*/
struct kernfs_root *root;
};
static struct kernfs_root *sysfs_root;//代表sys文件系统的根
struct kernfs_node *sysfs_root_kn;//代表sys文件系统的根的节点
int __init sysfs_init(void)
{
int err;
//创建sys系统根
sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
NULL); <=======当前分析位置
if (IS_ERR(sysfs_root))
return PTR_ERR(sysfs_root);
//sys系统根的目录
sysfs_root_kn = sysfs_root->kn;
将待注册的文件系统添加到已注册的文件系统列表中
err = register_filesystem(&sysfs_fs_type);
if (err) {
kernfs_destroy_root(sysfs_root);
return err;
}
return 0;
}
sysfs_init
>kernfs_create_root <=======当前分析位置
struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
unsigned int flags, void *priv)
{
struct kernfs_root *root;
struct kernfs_node *kn;
root = kzalloc(sizeof(*root), GFP_KERNEL);
if (!root)
return ERR_PTR(-ENOMEM);
idr_init(&root->ino_idr);
INIT_LIST_HEAD(&root->supers);
root->next_generation = 1;
kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO,
KERNFS_DIR); <=======当前分析位置
if (!kn) {
idr_destroy(&root->ino_idr);
kfree(root);
return ERR_PTR(-ENOMEM);
}
kn->priv = priv;//此时为空
kn->dir.root = root;//记录创建的kn所属的根
root->syscall_ops = scops;//添加相应的回调函数
root->flags = flags;
root->kn = kn;//第一个创建的kn是树根对应的kn目录
init_waitqueue_head(&root->deactivate_waitq);
if (!(root->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
kernfs_activate(kn);
return root;
}
sysfs_init
>kernfs_create_root
>__kernfs_new_node <=======当前分析位置
//在根文件系统上创建并添加目录kernfs_node
static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
const char *name, umode_t mode,
unsigned flags)
{
struct kernfs_node *kn;
u32 gen;
int cursor;
int ret;
name = kstrdup_const(name, GFP_KERNEL);
if (!name)
return NULL;
kn = kmem_cache_zalloc(kernfs_node_cache, GFP_KERNEL);
if (!kn)
goto err_out1;
idr_preload(GFP_KERNEL);
spin_lock(&kernfs_idr_lock);
//获取下一个申请的id
cursor = idr_get_cursor(&root->ino_idr);
//在基数树上申请id,并将kn绑定到id对应的树节点上,便于通过id查找kn
ret = idr_alloc_cyclic(&root->ino_idr, kn, 1, 0, GFP_ATOMIC);
//root->next_generation++记录当前节点是树中添加的第几个节点
if (ret >= 0 && ret < cursor)
root->next_generation++;
gen = root->next_generation;
spin_unlock(&kernfs_idr_lock);
idr_preload_end();
if (ret < 0)
goto err_out2;
//将id信息记录到kn中
kn->id.ino = ret;
kn->id.generation = gen;
/*
* set ino first. This barrier is paired with atomic_inc_not_zero in
* kernfs_find_and_get_node_by_ino
*/
smp_mb__before_atomic();
atomic_set(&kn->count, 1);
atomic_set(&kn->active, KN_DEACTIVATED_BIAS);
RB_CLEAR_NODE(&kn->rb);
kn->name = name;
kn->mode = mode;
kn->flags = flags;
return kn;
err_out2:
kmem_cache_free(kernfs_node_cache, kn);
err_out1:
kfree_const(name);
return NULL;
}
返回kernfs_create_root
sysfs_init
>kernfs_create_root <=======当前分析位置
struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,
unsigned int flags, void *priv)
{
struct kernfs_root *root;
struct kernfs_node *kn;
root = kzalloc(sizeof(*root), GFP_KERNEL);
if (!root)
return ERR_PTR(-ENOMEM);
idr_init(&root->ino_idr);
INIT_LIST_HEAD(&root->supers);
root->next_generation = 1;
kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO,
KERNFS_DIR);
if (!kn) {
idr_destroy(&root->ino_idr);
kfree(root);
return ERR_PTR(-ENOMEM);
}
<=======当前分析位置
kn->priv = priv;//此时为空
kn->dir.root = root;//记录创建的kn所属的根
root->syscall_ops = scops;//添加相应的回调函数
root->flags = flags;
root->kn = kn;//第一个创建的kn是树根对应的kn目录
init_waitqueue_head(&root->deactivate_waitq);
if (!(root->flags & KERNFS_ROOT_CREATE_DEACTIVATED))
kernfs_activate(kn);
return root;
}
返回sysfs_init
sysfs_init <=======当前分析位置
int __init sysfs_init(void)
{
int err;
//创建sys系统根
sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
NULL);
if (IS_ERR(sysfs_root))
return PTR_ERR(sysfs_root);
//sys系统根的目录
sysfs_root_kn = sysfs_root->kn;
将待注册的文件系统添加到已注册的文件系统列表中
err = register_filesystem(&sysfs_fs_type); <=======当前分析位置
if (err) {
kernfs_destroy_root(sysfs_root);
return err;
}
return 0;
}
sysfs_init
>kernfs_create_root
>register_filesystem <=======当前分析位置
//将待注册的文件系统添加到已注册的文件系统列表中,直到文件系统被注销时才是空闲
int register_filesystem(struct file_system_type * fs)
{
int res = 0;
struct file_system_type ** p;
BUG_ON(strchr(fs->name, '.'));
if (fs->next)
return -EBUSY;//根据本函数最终的注册效果是将带注册系统添加到已注册列表可知,这里表示该注册的系统已经被注册
write_lock(&file_systems_lock);
//由于已注册链表最后一个元素的next为空,所以这里需要再查询一下
p = find_filesystem(fs->name, strlen(fs->name));
if (*p)
res = -EBUSY;该注册的系统已经被注册
else
*p = fs;
write_unlock(&file_systems_lock);
return res;
}
返回sysfs_init
sysfs_init <=======当前分析位置
int __init sysfs_init(void)
{
int err;
//创建sys系统根
sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
NULL);
if (IS_ERR(sysfs_root))
return PTR_ERR(sysfs_root);
//sys系统根的目录
sysfs_root_kn = sysfs_root->kn;
将待注册的文件系统添加到已注册的文件系统列表中
err = register_filesystem(&sysfs_fs_type);
if (err) {
kernfs_destroy_root(sysfs_root);
return err;
}
return 0;
}
总结:
1、 为全局变量struct kmem_cache *kernfs_node_cache申请内存空间并初始化部分成员值。
2、创建sys文件系统根struct kernfs_root *sysfs_root,包含文件系统根的信息,并为sysfs_root添加一个kernfs_node *sysfs_root_kn代表sys文件系统的根的节点。
3、将待注册的文件系统sysfs_fs_type 添加到已注册的文件系统列表struct file_system_type *file_systems中。
static struct file_system_type sysfs_fs_type = {
.name = "sysfs",
.mount = sysfs_mount,
.kill_sb = sysfs_kill_sb,
.fs_flags = FS_USERNS_MOUNT,
}