Linux内核大讲堂 (三) 解不开的"/"情结
最近比较忙,一直都没有抽时间写文章,还好没有事先和出版社签订协议,否则哥哥就没时间去跳舞,就没时间和朋友喝酒了,人生在世几十年,生活总才是最重要的。以失去生活为代价的学习,哥是不干的,哥是有追求的人。呵呵,扯远了,个人低下的人生观,让各位见笑了。
言归正传。前阵在BLOG中放了置顶的帖子做调查,很多同志都卡在文件系统上面,哥当年也卡过,后面是经过学习与思考之后,最终才搞清楚来龙去脉,其实文件系统要真正搞清楚我觉得要从两个方面着手。
一者,从上电启动后,文件系统到底是怎么一步一步挂载的?有位哥们说最首先挂载的是根文件系统?另一位哥们又问,那根文件系统又挂载在哪呢?最首先那个”/”到底是怎么来的呢?等等。反正啊,问完这一个又有另一个疑问,永远问不完的问题。
二者,虚拟文件系统与各种实际的文件系统的关系。这个又有很多同志开始问问题了。我们的硬盘上有没有文件系统呢?等等。
先告诉大家,你心中的疑惑十有八九全都在这一大节当中,看你能领会多少了。
我们首先来解决一个源始的问题,注意是源始,呵呵。就是开始,文件系统的头在哪?根源在哪?
话说我们上电以后,我们的CPU从一个固定的地址取下代码然后一步一步执行,经过千辛万苦,终于跑到了一个叫 start_kernel的地方,然后这个函数又调用了vfs_caches_init_early。
fs/dcache.c
void __init vfs_caches_init_early(void)
{
dcache_init_early();
inode_init_early();
}
static void __init dcache_init_early(void)
{
int loop;
/* If hashes are distributed across NUMA nodes, defer
* hash allocation until vmalloc space is available.
*/
if (hashdist)
return;
dentry_hashtable =
alloc_large_system_hash("Dentry cache",
sizeof(struct dcache_hash_bucket),
dhash_entries,
13,
HASH_EARLY,
&d_hash_shift,
&d_hash_mask,
0);
for (loop = 0; loop < (1 << d_hash_shift); loop++)
INIT_HLIST_BL_HEAD(&dentry_hashtable[loop].head);
}
void __init inode_init_early(void)
{
int loop;
/* If hashes are distributed across NUMA nodes, defer
* hash allocation until vmalloc space is available.
*/
if (hashdist)
return;
inode_hashtable =
alloc_large_system_hash("Inode-cache",
sizeof(struct hlist_head),
ihash_entries,
14,
HASH_EARLY,
&i_hash_shift,
&i_hash_mask,
0);
for (loop = 0; loop < (1 << i_hash_shift); loop++)
INIT_HLIST_HEAD(&inode_hashtable[loop]);
}
为了别把大家搞晕,这dcache和inode后面讲虚拟文件系统的时候再讲。这两个函数大概的意思就是整两张hash表并初始化啦。要真正搞清楚,就得讲内存管理了,所以暂时先放他一马。^_^
在分配了并初始化了这两张表后,我们的start_kernel又调用了vfs_caches_init()。
void __init vfs_caches_init(unsigned long mempages)
{
unsigned long reserve;
/* Base hash sizes on available memory, with a reserve equal to
150% of current kernel size */
reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);
mempages -= reserve;
names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
dcache_init();
inode_init();
files_init(mempages);
mnt_init();
bdev_cache_init();
chrdev_init();
}
static void __init dcache_init(void)
{
int loop;
/*
* A constructor could be added for stable state like the lists,
* but it is probably not worth it because of the cache nature
* of the dcache.
*/
dentry_cache = KMEM_CACHE(dentry,
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD);
register_shrinker(&dcache_shrinker);
/* Hash may have been set up in dcache_init_early */
if (!hashdist)
return;
dentry_hashtable =
alloc_large_system_hash("Dentry cache",
sizeof(struct dcache_hash_bucket),
dhash_entries,
13,
0,
&d_hash_shift,
&d_hash_mask,
0);
for (loop = 0; loop < (1 << d_hash_shift); loop++)
INIT_HLIST_BL_HEAD(&dentry_hashtable[loop].head);
}
void __init inode_init(void)
{
int loop;
/* inode slab cache */
inode_cachep = kmem_cache_create("inode_cache",
sizeof(struct inode),
0,
(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
SLAB_MEM_SPREAD),
init_once);
register_shrinker(&icache_shrinker);
/* Hash may have been set up in inode_init_early */
if (!hashdist)
return;
inode_hashtable =
alloc_large_system_hash("Inode-cache",
sizeof(struct hlist_head),
ihash_entries,
14,
0,
&i_hash_shift,
&i_hash_mask,
0);
for (loop = 0; loop < (1 << i_hash_shift); loop++)
INIT_HLIST_HEAD(&inode_hashtable[loop]);
}
咦,打住,你前面在vfs_caches_init_early()中不是已经搞过两张表了吗?怎么这里又搞两张表,并且都指向同一个变量dentry_hashtable和inode_hashtable。
其实这里是只初始化一次的,原因就在于hashdist这个变量。
在vfs_caches_init_early()和vfs_caches_init()这两个函数中hashdist是一个取正,一个取反的。所以肯定有一个判断是真的并且返回。所以只可能初始化一次。
对于文件系统的理解vfs_caches_init_early()这个函数你完全可以当他不存在。
让我们继续回到dcache_init()函数中。
dentry_cache = KMEM_CACHE(dentry,
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD);
register_shrinker(&dcache_shrinker);
这两行依然是和内存管理相关的,一个是叫缓存,一个是叫”压缩机”。很牛B也很唬人的两大概念。前者道上的哥们都知道缓存就是用来命中的,命中了,就不用往下跑了,自然速度就提上去了,后者压缩机是当你内存不够时,会由系统自动触发执行的。内存管理的东西不是本节的重点,这里先不讲了,想获得更多了解的同志可以看看alloc_page_buffers()这个函数。如果跟晕了就别怪我哦。呵呵。
接下来的inode_init()基本做了一样的操作,不浪费笔墨了。
接下来就是files_init(mempages)了。
void __init files_init(unsigned long mempages)
{
unsigned long n;
filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
/*
* One file with associated inode and dcache is very roughly 1K.
* Per default don't use more than 10% of our memory for files.
*/
n = (mempages * (PAGE_SIZE / 1024)) / 10;
files_stat.max_files = max_t(unsigned long, n, NR_FILE);
files_defer_init();
lg_lock_init(files_lglock);
percpu_counter_init(&nr_files, 0);
}
这个传入的参数在vfs_caches_init()函数运行之初是有动过手脚的:
reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);
mempages -= reserve;
这个又是一个动态的小算法了,至于为什么是页面尺寸除以1024等这些细节, Linux内核的原版解释是:
/* Base hash sizes on available memory, with a reserve equal to
150% of current kernel size */
意思是说被保留的页面数等于当前已经被内核所占页面数的1.5倍,这绝对是个经验值。不用过份的追究。经过上面这么一算,mempages等于总的内存页面数减去内核占的页面数的2.5倍。接下来我们回到files_init()函数中,files_init()首先创建了一块名为filp_cachep的缓存,我顶他的肺,又是内存相关的,内存管理相关的东西无处不在啊。改天一定得好好教训教训他。接下来都是一些初始化的东西,可以先不过份追究。
接下来就是重量级的mnt_init()。
void __init mnt_init(void)
{
unsigned u;
int err;
init_rwsem(&namespace_sem);
mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);
if (!mount_hashtable)
panic("Failed to allocate mount hash table/n");
printk("Mount-cache hash table entries: %lu/n", HASH_SIZE);
for (u = 0; u < HASH_SIZE; u++)
INIT_LIST_HEAD(&mount_hashtable[u]);
br_lock_init(vfsmount_lock);
err = sysfs_init();
if (err)
printk(KERN_WARNING "%s: sysfs_init error: %d/n",
__func__, err);
fs_kobj = kobject_create_and_add("fs", NULL);
if (!fs_kobj)
printk(KERN_WARNING "%s: kobj create error/n", __func__);
init_rootfs();
init_mount_tree();
}
在经过一些初始化之后我们又看到了一个老朋友sysfs_init()。
太熟悉了,熟悉得不能再熟悉了,是你,让我能直观的感受了驱动模型,是你让我失去了一个做菜鸟的机会。这么好的朋友,我们没有道理不和他打个招呼。说不定老朋友会给我们意外的惊喜呢。以前老师告诉我们交朋友的话要交好朋友,不要和怪叔叔说话。^_^
int __init sysfs_init(void)
{
int err = -ENOMEM;
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;
err = register_filesystem(&sysfs_fs_type);
if (!err) {
sysfs_mnt = kern_mount(&sysfs_fs_type);
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;
}
前面创建缓存的就不多说了,现在说也是白说,我们看看sysfs_inode_init()。
int __init sysfs_inode_init(void)
{
return bdi_init(&sysfs_backing_dev_info);
}
static struct backing_dev_info sysfs_backing_dev_info = {
.name = "sysfs",
.ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
};
又出现了一个叫backing_dev_info,这个东西中文名叫后台设备信息。先买个关子,这可是一位大侠,这位大侠和雷峰叔叔一样伟大,傻傻的牺牲自已,帮助别人,只能说*大人太会忽悠了,*大人自已坐家里吃红烧肉,忽悠雷峰叔叔把自已不多的薪资全用来帮助别人,哎,什么世道。我们这位后台设备信息大人和雷峰叔叔是一样的,被文件系统这个高来高往的大人忽悠着干活。
static struct file_system_type sysfs_fs_type = {
.name = "sysfs",
.mount = sysfs_mount,
.kill_sb = sysfs_kill_sb,
};
register_filesystem(&sysfs_fs_type)
这个函数引入了一个重要的概念,那就是sruct file_system_type,这个东西叫文件系统对象,我们后面会讲到的,register_filesystem()会将传入的文件系统对象链入一个叫file_systems的全局文件系统对象链表中,当然,一个文件系统对象想两次链入到file_systems的话那就no way了。
接下来调用sysfs_mnt = kern_mount(&sysfs_fs_type);
#define kern_mount(type) kern_mount_data(type, NULL)
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
{
return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
}
上面一直都是小打小闹,这回终于来了一个长点的函数。
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
struct vfsmount *mnt;
struct dentry *root;
char *secdata = NULL;
int error;
if (!type)
return ERR_PTR(-ENODEV);
error = -ENOMEM;
mnt = alloc_vfsmnt(name);
if (!mnt)
goto out;
if (flags & MS_KERNMOUNT)
mnt->mnt_flags = MNT_INTERNAL;
if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
secdata = alloc_secdata();
if (!secdata)
goto out_mnt;
error = security_sb_copy_data(data, secdata);
if (error)
goto out_free_secdata;
}
if (type->mount) {
root = type->mount(type, flags, name, data);
if (IS_ERR(root)) {
error = PTR_ERR(root);
goto out_free_secdata;
}
mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
} else {
error = type->get_sb(type, flags, name, data, mnt);
if (error < 0)
goto out_free_secdata;
}
BUG_ON(!mnt->mnt_sb);
WARN_ON(!mnt->mnt_sb->s_bdi);
WARN_ON(mnt->mnt_sb->s_bdi == &default_backing_dev_info);
mnt->mnt_sb->s_flags |= MS_BORN;
error = security_sb_kern_mount(mnt->mnt_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. This warning should be either removed or
* converted to a BUG() in 2.6.34.
*/
WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
"negative value (%lld)/n", type->name, mnt->mnt_sb->s_maxbytes);
mnt->mnt_mountpoint = mnt->mnt_root;
mnt->mnt_parent = mnt;
up_write(&mnt->mnt_sb->s_umount);
free_secdata(secdata);
return mnt;
out_sb:
dput(mnt->mnt_root);
deactivate_locked_super(mnt->mnt_sb);
out_free_secdata:
free_secdata(secdata);
out_mnt:
free_vfsmnt(mnt);
out:
return ERR_PTR(error);
}
首先调用了。alloc_vfsmnt()。
struct vfsmount *alloc_vfsmnt(const char *name)
{
struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
if (mnt) {
int err;
err = mnt_alloc_id(mnt);
if (err)
goto out_free_cache;
if (name) {
mnt->mnt_devname = kstrdup(name, GFP_KERNEL);
if (!mnt->mnt_devname)
goto out_free_id;
}
#ifdef CONFIG_SMP
mnt->mnt_pcp = alloc_percpu(struct mnt_pcp);
if (!mnt->mnt_pcp)
goto out_free_devname;
this_cpu_add(mnt->mnt_pcp->mnt_count, 1);
#else
mnt->mnt_count = 1;
mnt->mnt_writers = 0;
#endif
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_FSNOTIFY
INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
#endif
}
return mnt;
#ifdef CONFIG_SMP
out_free_devname:
kfree(mnt->mnt_devname);
#endif
out_free_id:
mnt_free_id(mnt);
out_free_cache:
kmem_cache_free(mnt_cache, mnt);
return NULL;
}
可以看出这个函数纯粹是分配一块内存然后初始化,没别的。
如果name不等于NULL的话就执行:
mnt->mnt_devname = kstrdup(name, GFP_KERNEL);
就这一句最关键了。
OK,回到vfs_kern_mount()函数中。
接下来那一堆secxxx我们可以忽略不看,从字面上理解是和安全相关的,我这里对应的宏也关掉了,我也没有细看过,不发表言论。
接下来就到了:
if (type->mount) {
root = type->mount(type, flags, name, data);
if (IS_ERR(root)) {
error = PTR_ERR(root);
goto out_free_secdata;
}
mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
}
我们回顾一下我传入的type的定义:
fs/sysfs/mount.c
static struct file_system_type sysfs_fs_type = {
.name = "sysfs",
.mount = sysfs_mount,
.kill_sb = sysfs_kill_sb,
};
很明显我们会进入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;
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_current(type);
sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info);
if (IS_ERR(sb) || sb->s_fs_info != info)
kfree(info);
if (IS_ERR(sb))
return ERR_CAST(sb);
if (!sb->s_root) {
sb->s_flags = flags;
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);
}
在经过一系列的处理后我们调用了sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info);
这个是得到一个超级块,超级块干嘛的我们后面再说。
接下来
if (!sb->s_root) {
sb->s_flags = flags;
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;
}
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;
sb->s_time_gran = 1;
/* get root inode, initialize and unlock it */
mutex_lock(&sysfs_mutex);
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 */
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;
sb->s_root = root;
return 0;
}
首先:
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;
sb->s_time_gran = 1;
这个没啥好说的,关键的就一个魔数和一个 sysfs_ops。另外块的设置就和内存的设置是一样的。sysfs_ops也没啥东西,都是“骗人”的。呵呵。
接下来就是inode = sysfs_get_inode(sb, &sysfs_root);
struct sysfs_dirent sysfs_root = {
.s_name = "",
.s_count = ATOMIC_INIT(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,
};
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_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;
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值钱的东西全在这里。各种各样的对象操作集全在这里,我们这里就随便挑一个讲一下。
inode->i_fop = &sysfs_file_operations;
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,
};
static ssize_t
sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct sysfs_buffer * buffer = file->private_data;
ssize_t retval = 0;
mutex_lock(&buffer->mutex);
if (buffer->needs_read_fill || *ppos == 0) {
retval = fill_read_buffer(file->f_path.dentry,buffer);
if (retval)
goto out;
}
pr_debug("%s: count = %zd, ppos = %lld, buf = %s/n",
__func__, count, *ppos, buffer->page);
retval = simple_read_from_buffer(buf, count, ppos, buffer->page,
buffer->count);
out:
mutex_unlock(&buffer->mutex);
return retval;
}
ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos,
const void *from, size_t available)
{
loff_t pos = *ppos;
size_t ret;
if (pos < 0)
return -EINVAL;
if (pos >= available || !count)
return 0;
if (count > available - pos)
count = available - pos;
ret = copy_to_user(to, from + pos, count);
if (ret == count)
return -EFAULT;
count -= ret;
*ppos = pos + count;
return count;
}
看到copy_to_user我想你一切都明白了。呵呵。
我们再回到sysfs_fill_super()函数中并往下走。
root = d_alloc_root(inode);
我的评价是:其貌不扬的一个函数。但就这个其貌不扬的函数中包含了我们本节的最终答案。
struct dentry * d_alloc_root(struct inode * root_inode)
{
struct dentry *res = NULL;
if (root_inode) {
static const struct qstr name = { .name = "/", .len = 1 };
res = d_alloc(NULL, &name);
if (res) {
res->d_sb = root_inode->i_sb;
d_set_d_op(res, res->d_sb->s_d_op);
res->d_parent = res;
d_instantiate(res, root_inode);
}
}
return res;
}
我们来分析一下:
static const struct qstr name = { .name = "/", .len = 1 };
“/”,伟大的”/”原来就在这里。
struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
{
struct dentry *dentry;
char *dname;
dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
if (!dentry)
return NULL;
if (name->len > DNAME_INLINE_LEN-1) {
dname = kmalloc(name->len + 1, GFP_KERNEL);
if (!dname) {
kmem_cache_free(dentry_cache, dentry);
return NULL;
}
} else {
dname = dentry->d_iname;
}
dentry->d_name.name = dname;
dentry->d_name.len = name->len;
dentry->d_name.hash = name->hash;
memcpy(dname, name->name, name->len);
dname[name->len] = 0;
dentry->d_count = 1;
dentry->d_flags = DCACHE_UNHASHED;
spin_lock_init(&dentry->d_lock);
seqcount_init(&dentry->d_seq);
dentry->d_inode = NULL;
dentry->d_parent = NULL;
dentry->d_sb = NULL;
dentry->d_op = NULL;
dentry->d_fsdata = NULL;
INIT_HLIST_BL_NODE(&dentry->d_hash);
INIT_LIST_HEAD(&dentry->d_lru);
INIT_LIST_HEAD(&dentry->d_subdirs);
INIT_LIST_HEAD(&dentry->d_alias);
INIT_LIST_HEAD(&dentry->d_u.d_child);
if (parent) {
spin_lock(&parent->d_lock);
/*
* don't need child lock because it is not subject
* to concurrency here
*/
__dget_dlock(parent);
dentry->d_parent = parent;
dentry->d_sb = parent->d_sb;
d_set_d_op(dentry, dentry->d_sb->s_d_op);
list_add(&dentry->d_u.d_child, &parent->d_subdirs);
spin_unlock(&parent->d_lock);
}
this_cpu_inc(nr_dentry);
return dentry;
}
这个不用多说了,我们之前的”/”就被放置在了dentry->d_iname中,最后函数返回了dentry的地址。
到这里我们的”/”终于有了,有了他,一切都变得美好了,你挂载文件系统的梦想也可以实现了。
别和我说我没有编译sysfs,但是我照样有"/",这是怎么回事呢?
你只要把眼光往下看一点,在接下来的代码中就可以找到答案了。
OK,本节先到这里,抽根烟,下节见。呵呵。^_^
版权声明:本文为博主原创文章,未经博主允许不得转载。