/*
** arm、linux2.6.29
** 该文件不是内核中的文件,只是为了方便分析单独从 linux2.6 ramfs机制解析 中提取出来
** 主要分析的是start_kernel()函数中的vfs_caches_init_early()和vfs_caches_init(num_physpages)
** 他们实现了rootfs的初始化
*/
asmlinkage void __init start_kernel(void)
{
...
vfs_caches_init_early(); // note 1
...
vfs_caches_init(num_physpages); // note 2
...
}
/** note 1 **/
void __init vfs_caches_init_early(void)
{
dcache_init_early(); // 对目录项哈希表进行早期的初始化 note 1-1
inode_init_early(); // 对文件节点哈希表进行早期初始化 note 1-2
}
/**** note 1-1 ****/
// fs/dcache.c
static void __init dcache_init_early(void)
{
int loop;
/* If hashes are distributed across NUMA nodes, defer
* hash allocation until vmalloc space is available.
* 如果hashes需要布局(NUMA),那么就需要在vmalloc空间有效之后再分配hash
*/
if (hashdist) // note 1-1-1
return;
/* 返回hash表头地址
* hash table 名字是Dentry cache
* 哈希表入口项数目numentries值2的对数,保存到d_hash_shif变量。
* d_hash_mask保存入口项的掩码。
*/
dentry_hashtable = // dentry_hashtable是fs/dcache.c中的全局变量 // note 1-1-2
alloc_large_system_hash("Dentry cache",
sizeof(struct hlist_head),
dhash_entries, // note 1-1-3
13,
HASH_EARLY,
&d_hash_shift,
&d_hash_mask,
0);
// 对哈希表的每项的链表头进行初始化,->NULL
for (loop = 0; loop < (1 << d_hash_shift); loop++)
INIT_HLIST_HEAD(&dentry_hashtable[loop]);
}
/****** note 1-1-1 ******/
// hashdist的历史
// include/linux/bootmem.h
...
/* Only NUMA needs hash distribution.
只有NUMA才需要hash布局
* IA64 and x86_64 have sufficient vmalloc space.
IA64和x86_64拥有充足的vamalloc空间
*/
#if defined(CONFIG_NUMA) && (defined(CONFIG_IA64) || defined(CONFIG_X86_64))
#define HASHDIST_DEFAULT 1
#else
#define HASHDIST_DEFAULT 0
#endif
...
// mm/page_allloc.c定义了hashdist
int hashdist = HASHDIST_DEFAULT;
/*
该值可以采用默认值HASHDIST_DEFAULT,也可以通过cmdline来传递,hashdist=
*/
// page_alloc.c
#ifdef CONFIG_NUMA
static int __init set_hashdist(char *str)
{
if (!str)
return 0;
hashdist = simple_strtoul(str, &str, 0);
return 1;
}
__setup("hashdist=", set_hashdist);
#endif
/****** note 1-1-1 ******/
/****** note 1-1-2 ******/
// fs/dcache.c z中的静态变量
static struct hlist_head *dentry_hashtable __read_mostly;
// include/liunx/list.h
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
/****** note 1-1-2 ******/
/****** note 1-1-3 ******/
static __initdata unsigned long dhash_entries;
static int __init set_dhash_entries(char *str)
{
if (!str)
return 0;
dhash_entries = simple_strtoul(str, &str, 0);
return 1;
}
__setup("dhash_entries=", set_dhash_entries);
/*
这个dhash_entries变量表示目录项哈希表入口数目,默认为0。
可以通过命令行传递参数dhash_entries= 0xxx来指定。
*/
/****** note 1-1-3 ******/
/**** note 1-1 ****/
/**** note 1-2 ****/
// fs/inode.c
static __initdata unsigned long ihash_entries;
static int __init set_ihash_entries(char *str)
{
if (!str)
return 0;
ihash_entries = simple_strtoul(str, &str, 0);
return 1;
}
__setup("ihash_entries=", set_ihash_entries);
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;
// alloc_large_system_hash() // note 1-2-1
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]);
// #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
}
// 与对目录项哈希表早期初始化类似,只不过这里是对文件节点inode哈希表初始化
// 该hash的入口项可以通过cmdline参数传递进来,ihash_entries=
/****** note 1-2-1 ******/
/*
参数含义:
tablename 哈希表名字
bucketsize 这个是每个元素的大小
numentries 哈希表入口数,可以取0,或者由cmdline中传入。这时可能即使你给了值,系统也会把它变为最接
近的2的幂
scale 取值有 13、14、15、17 之类的,如果numentries不为0,这个参数没有作用。
lags 可取HASH_EARLY或0,分配内存的地方根据这个有不同
_hash_shift 用于返回元素个数的以2为底的对数,也就是表示元素个数这个数值所用的比特数
_hash_mask 用于返回*_hash_shift个比特所能表示的最大数-1
limit 哈希表表元数上限,不是分配内存的总尺寸,不要弄混了。
如果给个0值,那么系统使用1/16内存所能容纳的元素数作为哈希表表元数。
参数numentries和limit有关系的,参数numentries的值不能超出limit的值
*/
// 下面函数注释中的所有数据均代表创建Inode-cache时所用的数据,所传参数如下:
// ("Inode-cache", 4, 0, 14, HASH_EARLY, &i_hash_shift, &i_hash_mask, 0)
void *__init alloc_large_system_hash(const char *tablename,
unsigned long bucketsize,
unsigned long numentries,
int scale,
int flags,
unsigned int *_hash_shift,
unsigned int *_hash_mask,
unsigned long limit)
{
unsigned long long max = limit;
unsigned long log2qty, size;
void *table = NULL;
/* allow the kernel cmdline to have a say */
// 如果传递进来的参数numentries==0,进入if体内执行
if (!numentries) {
/* round applicable memory size up to nearest megabyte */
/*
这个nr_kernel_pages 变量在free_area_init_core() 函数中赋值,
表示不包含高端内存的系统内存共有的内存页面数。
PAGE_SHIFT=12
计算不包含高端内存的系统内存共有多少MB(不足1MB,以1MB 计算)。
*/
numentries = nr_kernel_pages;
numentries += (1UL << (20 - PAGE_SHIFT)) - 1; // +上1MB中包含的页数
numentries >>= 20 - PAGE_SHIFT; // 向上对1MB取整
//取得不足1MB,以1MB计算后的页面数目
numentries <<= 20 - PAGE_SHIFT;
/* limit to 1 bucket per 2^scale bytes of low memory */
// scale = 14 对numentries进行缩放
if (scale > PAGE_SHIFT)
numentries >>= (scale - PAGE_SHIFT); // numentries >> 2
else
numentries <<= (PAGE_SHIFT - scale);
/* Make sure we've got at least a 0-order allocation.. */
// 如果numentries个hash入口所占空间不足一个页面的空间,以一个页面的空间来计算出numentries
if (unlikely((numentries * bucketsize) < PAGE_SIZE))
numentries = PAGE_SIZE / bucketsize;
}
// 向上取最接近目前numentries的2的幂次方的值给numentries
numentries = roundup_pow_of_two(numentries);
/* limit allocation size to 1/16 total memory by default */
//这个max等于limit,如果等于0,就计算numentries的最大值max
if (max == 0) {
/*
这个nr_all_pages 变量表示所有内存的页面,即哈希表的内存最大为整个内存的1/16的空间。
*/
max = ((unsigned long long)nr_all_pages << PAGE_SHIFT) >> 4;
//do_div()宏, 使max除以bucketsize,并把结果赋值给max变量
do_div(max, bucketsize);
}
// 变量numentries不能够大于max变量,即哈希表的入口不能够大于max变量
if (numentries > max)
numentries = max;
log2qty = ilog2(numentries); //对numentries取2的对数
do {
size = bucketsize << log2qty; /*取得要分配空间的大小*/
if (flags & HASH_EARLY)
// 当flags为HASH_EARLY标志时,表示内核没有启动完毕,使用启动内存分配器来分配空间
table = alloc_bootmem_nopanic(size);
else if (hashdist)
// 如果hashdist不等于0 ,从vmalloc空间分配内存
table = __vmalloc(size, GFP_ATOMIC, PAGE_KERNEL);
else {
// 下面的在系统启动完毕后,从buddy内存管理器上分配页面
unsigned long order = get_order(size);
table = (void*) __get_free_pages(GFP_ATOMIC, order);
/*
* If bucketsize is not a power-of-two, we may free
* some pages at the end of hash table.
*/
if (table) {
unsigned long alloc_end = (unsigned long)table +
(PAGE_SIZE << order);
unsigned long used = (unsigned long)table +
PAGE_ALIGN(size);
split_page(virt_to_page(table), order);
while (used < alloc_end) {
free_page(used);
used += PAGE_SIZE;
}
}
}
} while (!table && size > PAGE_SIZE && --log2qty);
if (!table) /* 如果table等于NULL,表示没有足够的空间来进行分配 */
panic("Failed to allocate %s hash table\n", tablename);
printk(KERN_INFO "%s hash table entries: %d (order: %d, %lu bytes)\n",
tablename,
(1U << log2qty),
ilog2(size) - PAGE_SHIFT,
size);
// 返回哈希表入口项数目numentries值2的对数
if (_hash_shift)
*_hash_shift = log2qty;
if (_hash_mask)
*_hash_mask = (1 << log2qty) - 1;
return table; // 返回哈希表入口地址
}
/****** note 1-2-1 ******/
/**** note 1-2 ****/
/*
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
总结:
inode_hashtable就是一数组,每个数组元素都是一个struct hlist_head结构体,该结构体实际上就只有一个
指向struct hlist_node对象的指针first,所有指针在arm体系上都是4字节,所以这个函数实际上只是为数组
inode_hashtable分配了一段内存空间,该数组是实际上就是一个指针数组,每个元素都是指向struct hlist_node
对象的指针。然后最后将这些指针全部置成NULL。至于后续怎么使用还没涉及到。
同样对于Dentry cache哈希表(dentry_hashtable)也是一样的,只是创建了一个指针数组而已。
参考:http://qgjie456.blog.163.com/blog/static/3545136720081126102615685/
*/
/** note 1 **/
++++++++++++++++++++++++++++++++ vfs_caches_init(num_physpages) +++++++++++++++++++++
/** note 2 **/
// fs/dcache.c
// 注意:该版本代码是2.6.29 ,和2.6.14的代码该处关于内存分配器有一些差别,就像前文说的,2.6.29引入了
// slub分配器,而2.6.14还是使用的slab,但是接口被保留,实现的功能一样。以下还是以slab称呼
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);
// 创建slab高速缓存组names_cache,其中缓存的结构体对象大小是PATH_MAX = 4096
// 这里与2.6.14版本不同的是,filp高速缓存组没有被创建。
// 返回描述该高速缓存的一个结构体指针names_cachep
dcache_init(); // slab高速缓存组创建,注册dcache页面回收函数 note 2-1
inode_init(); // slab高速缓存组创建,注册icache页面回收函数 note 2-2
files_init(mempages); // note 2-3
mnt_init(); /* 重要函数,挂载rootfs文件系统 */ // note 2-4
bdev_cache_init();
chrdev_init();
}
/**** note 2-1 ****/
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);
/*
static struct kmem_cache *dentry_cache __read_mostly;
#define KMEM_CACHE(__struct, __flags) kmem_cache_create(#__struct,\
sizeof(struct __struct), __alignof__(struct __struct),\
(__flags), NULL)
可以看出,这里是创建了一个名字为dentry的slab高速缓存组,由指针dentry_cache指向的结构体描述
*/
register_shrinker(&dcache_shrinker);
// 注册dcache页面回收函数shrink_dcache_memory,由页面回收算法(PFRA)来执行
/* Hash may have been set up in dcache_init_early */
if (!hashdist)
return;
... // 该部分的工作在dcache_init_early中完成
}
/**** note 2-1 ****/
/**** note 2-2 ****/
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);
// 创建了一个名字为inode_cache的slab高速缓存组,由指针inode_cachep指向的结构体描述
register_shrinker(&icache_shrinker);
// 注册icache页面回收函数shrink_icache_memory,由页面回收算法(PFRA)来执行
/* Hash may have been set up in inode_init_early */
if (!hashdist)
return;
... // 该部分的工作在icache_init_early中完成
}
/**** note 2-2 ****/
/**** note 2-3 ****/
void __init files_init(unsigned long mempages)
{
int n;
filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
// 创建名为filp的slab高速缓存组,格式化成struct file,由指针filp_cachep指向的结构体描述该高缓
/*
* 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 = n;
if (files_stat.max_files < NR_FILE)
files_stat.max_files = NR_FILE;
files_defer_init(); // 这两个函数做什么用的,暂时不管
percpu_counter_init(&nr_files, 0);
}
/**** note 2-3 ****/
/**** note 2-4 ****/
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);
// 创建名为mnt_cache的slab高速缓存,格式化成struct vfsmount,由指针mnt_cache指向的结构体描述
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);
// HASH_SIZE = 512
for (u = 0; u < HASH_SIZE; u++)
INIT_LIST_HEAD(&mount_hashtable[u]);
// 初始化next和prev均指向其自身
err = sysfs_init(); /* sysfs文件系统注册 in fs/sysfs/mount.c*/
if (err)
printk(KERN_WARNING "%s: sysfs_init error: %d\n",
__func__, err);
fs_kobj = kobject_create_and_add("fs", NULL);
// 在sysfs目录下创建fs目录,主要是创建名为fs的kobject及相关初始化
if (!fs_kobj)
printk(KERN_WARNING "%s: kobj create error\n", __func__);
init_rootfs(); /* rootfs文件系统注册 in fs/ramfs/inode.c*/
init_mount_tree();/* 为VFS建立根目录 "/" */ // note 2-4-1
/* 挂载rootfs文件系统。它的挂载点默认为"/",最后切换进程的根目录和当前目录为"/" */
}
/**** note 2-4 ****/
/****** note 2-4-1 init_mount_tree()******/
static void __init init_mount_tree(void)
{
struct vfsmount *mnt;
struct mnt_namespace *ns;
struct path root;
mnt = do_kern_mount("rootfs", 0, "rootfs", NULL); // note 2-4-1-1
/* 重要函数 挂载前面已经注册进内核的rootfs文件系统*/
if (IS_ERR(mnt))
panic("Can't create rootfs");
ns = kmalloc(sizeof(*ns), GFP_KERNEL);
if (!ns)
panic("Can't allocate initial namespace");
atomic_set(&ns->count, 1);
// 设置引用计数
INIT_LIST_HEAD(&ns->list);
init_waitqueue_head(&ns->poll);
ns->event = 0;
list_add(&mnt->mnt_list, &ns->list);
// 将该vfsmount结构体挂接到namespace结构体的链表中
ns->root = mnt;
// 该命名空间的root目录为刚刚建立的vfsmount结构体
mnt->mnt_ns = ns;
// 该vfsmount结构体的命名空间为ns
init_task.nsproxy->mnt_ns = ns;
get_mnt_ns(ns);
/*
init_mount_tree() 函数会为系统最开始的进程(即 init_task 进程)
准备它的进程数据块中的namespace 域,主要目的是将 do_kern_mount()
函数中建立的 mnt 和 dentry 信息记录在了 init_task 进程的进程数据块中,
这样所有以后从 init_task 进程 fork 出来的进程也都先天地继承了这一信息
*/
root.mnt = ns->root;
root.dentry = ns->root->mnt_root;
set_fs_pwd(current->fs, &root);
set_fs_root(current->fs, &root);
/*
最后两行便是将 do_kern_mount() 函数中建立的 mnt 和 dentry 信息记录在了当前进程的 fs结构中
*/
}
/******** note 2-4-1-1 do_kern_mount() ********/
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
struct file_system_type *type = get_fs_type(fstype); // 获取全局链表file_system中名为fstype的文件系统
// 结构体指针
struct vfsmount *mnt;
if (!type)
return ERR_PTR(-ENODEV);
mnt = vfs_kern_mount(type, flags, name, data); // note 2-4-1-1-1
// 重要函数
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
!mnt->mnt_sb->s_subtype)
mnt = fs_set_subtype(mnt, fstype);
put_filesystem(type); // wake_up_process()
return mnt;
}
/********** note 2-4-1-1-1 vfs_kern_mount() **********/
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
struct vfsmount *mnt;
char *secdata = NULL;
int error;
if (!type)
return ERR_PTR(-ENODEV); // 参数验证
error = -ENOMEM;
mnt = alloc_vfsmnt(name);
/*在slab高速缓存组mnt_cache中分配一个vfsmount对象,并对其进行初始化*/
if (!mnt)
goto out;
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;
}
error = type->get_sb(type, flags, name, data, mnt); // note 2-4-1-1-1-1 ,以rootfs为例
/* 文件系统超级块的回调函数 */
/* rootfs_get_sb
一般的伪文件系统或叫内存文件系统分配超级块都是调用get_sb_nodev()或者
get_sb_single(); 而实际文件系统如磁盘文件系统,在分配超级块的时候则调
用get_sb_bdev()需要在具体块设备上建立超级块。
因为rootfs是内存文件系统所以调用get_sb_nodev();
*/
if (error < 0)
goto out_free_secdata;
BUG_ON(!mnt->mnt_sb);
error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
if (error)
goto out_sb;
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);
up_write(&mnt->mnt_sb->s_umount);
deactivate_super(mnt->mnt_sb);
out_free_secdata:
free_secdata(secdata);
out_mnt:
free_vfsmnt(mnt);
out:
return ERR_PTR(error);
}
/************ note 2-4-1-1-1-1 超级块回调函数 ************/
// rootfs文件系统的超级块回调函数是rootfs_get_sb()
static int rootfs_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
return get_sb_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super,
mnt); // note 2-4-1-1-1-1-1
}
// ramfs_fill_super()是超级块填充函数
/************** note 2-4-1-1-1-1-1 get_sb_nodev() **************/
int get_sb_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int),
struct vfsmount *mnt)
{
int error;
struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);
/* 在内存中分配一个超级块,并做相应初始化*/
// set_anon_super为回调函数
if (IS_ERR(s))
return PTR_ERR(s);
s->s_flags = flags; // flags = MS_NOUSER
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); // note 2-4-1-1-1-1-1-1
// 执行填充超级块的回调函数 ramfs_fill_super
if (error) {
up_write(&s->s_umount);
deactivate_super(s);
return error;
}
s->s_flags |= MS_ACTIVE;
return simple_set_mnt(mnt, s); /* 超级块和文件系统挂载结构体简单关联 */
// mnt->mnt_sb = sb;
// mnt->mnt_root = dget(sb->s_root);
}
/**************** note 2-4-1-1-1-1-1-1 fill_super = ramfs_fill_super() ****************/
static int ramfs_fill_super(struct super_block * sb, void * data, int silent)
{
struct inode * inode;
struct dentry * root;
sb->s_maxbytes = MAX_LFS_FILESIZE; /* 文件的最大值 */
sb->s_blocksize = PAGE_CACHE_SIZE; /* 以字节为单位的块的大小 */
sb->s_blocksize_bits = PAGE_CACHE_SHIFT; /* 以位为单位的块的大小 */
sb->s_magic = RAMFS_MAGIC; /* 文件系统的魔数 0x858458f6 */
sb->s_op = &ramfs_ops; /* 超级块的方法 ,在处理inode的时候会有用 */
sb->s_time_gran = 1;
inode = ramfs_get_inode(sb, S_IFDIR | 0755, 0);/* 建立根目录索引节点 */ // note 2-4-1-1-1-1-1-1-1
if (!inode)
return -ENOMEM;
root = d_alloc_root(inode);/* 建立根目录目录对象,并关联上inode */ // note 2-4-1-1-1-1-1-1-2
if (!root) {
iput(inode);
return -ENOMEM;
}
sb->s_root = root; /* 超级块的s_root指向刚建立的根目录对象 */
return 0;
}
/****************** note 2-4-1-1-1-1-1-1-1 ramfs_get_inode() ******************/
struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
{
struct inode * inode = new_inode(sb);
/* 在索引节点高速缓存inode_cachep中创建一个inode, 并初始化话相应的域*/
if (inode) {
inode->i_mode = mode;/* 文件的类型 */
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;//文件的块数
inode->i_mapping->a_ops = &ramfs_aops;
inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {//判断文件类型
default:
init_special_inode(inode, mode, dev);
//特殊文件;如:字符~块设备文件,FIFO,SOCKET文件
break;
case S_IFREG://普通文件
inode->i_op = &ramfs_file_inode_operations;//索引节点的操作方法
inode->i_fop = &ramfs_file_operations;// 缺省普通文件的操作方法
break;
case S_IFDIR:// 目录文件
inode->i_op = &ramfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations; // 目录文件的操作方法
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inode->i_nlink++;
break;
case S_IFLNK: //符号链接
inode->i_op = &page_symlink_inode_operations;
break;
}
}
return inode; //返回创建的inode与对应的目录项对象关联
}
/****************** note 2-4-1-1-1-1-1-1-1 ramfs_get_inode() ******************/
/****************** note 2-4-1-1-1-1-1-1-2 d_alloc_root() ******************/
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; //指向该文件系统的超级块
res->d_parent = res; //根目录的父亲当然是它自己了
d_instantiate(res, root_inode); // note 2-4-1-1-1-1-1-1-2-1
//将dentry 和 inode关联上
}
}
return res;
}
/******************** note 2-4-1-1-1-1-1-1-2-1 d_instantiate() ********************/
void d_instantiate(struct dentry *entry, struct inode * inode)
{
if (!list_empty(&entry->d_alias)) BUG();
//一个目录项对象只能关联一个索引节点
spin_lock(&dcache_lock);
if (inode)
list_add(&entry->d_alias, &inode->i_dentry);
//一个索引节点可以关联多个目录项对象
entry->d_inode = inode;
spin_unlock(&dcache_lock);
security_d_instantiate(entry, inode);
}
/******************** note 2-4-1-1-1-1-1-1-2-1 d_instantiate() ********************/
/****************** note 2-4-1-1-1-1-1-1-2 d_alloc_root() ******************/
/**************** note 2-4-1-1-1-1-1-1 fill_super = ramfs_fill_super() ****************/
/************** note 2-4-1-1-1-1-1 get_sb_nodev() **************/
/************ note 2-4-1-1-1-1 超级块回调函数 ************/
/********** note 2-4-1-1-1 vfs_kern_mount() **********/
/******** note 2-4-1-1 do_kern_mount() ********/
/****** note 2-4-1 init_mount_tree()******/
/** note 2 **/