1. 前言
本文主要总结f2fs文件的打开过程,以touch test为例
2.打开文件总体流程
在打开文件时,获取了file描述符,创建了file, 而file->op_s来源于file所对应的inode->i_fop
而inode是如何被创建的呢?
[<7f003770>] (f2fs_create+0x24/0x1fc [f2fs]) from [<800c1724>] (vfs_create+0x80/0xb4)
[<800c1724>] (vfs_create+0x80/0xb4) from [<800c462c>] (do_last.isra.45+0x794/0xb98)
[<800c462c>] (do_last.isra.45+0x794/0xb98) from [<800c4ad8>] (path_openat+0xa8/0x47c)
[<800c4ad8>] (path_openat+0xa8/0x47c) from [<800c5170>] (do_filp_open+0x2c/0x80)
[<800c5170>] (do_filp_open+0x2c/0x80) from [<800b6f40>] (do_sys_open+0xe4/0x170)
[<800b6f40>] (do_sys_open+0xe4/0x170) from [<8000e140>] (ret_fast_syscall+0x0/0x30)
打开文件的过程中,会调用vfs_create,vfs_create会调用具体文件系统的create回调,对于f2fs文件系统就是通过f2fs_create来创建了inode, 并对inode进行初始化,其中:
inode->i_fop = &f2fs_file_operations; f2fs_file_operations定义在f2fs/file.c中
之后将通过如下的调用来将inode->i_fop赋值给file->f_op,如果file->f_op->open不为空,则将得到执行
do_last
finish_open
do_dentry_open
3. f2fs_create
通过f2fs_create创建了inode,同时初始化如下的操作函数集:
138 inode->i_op = &f2fs_file_inode_operations;
139 inode->i_fop = &f2fs_file_operations;
140 inode->i_mapping->a_ops = &f2fs_dblock_aops;
f2fs_file_operations将用来初始化file->f_op
f2fs_dblock_aops主要用于与page cache的交互相关
3. mknod
字符设备文件的创建
mknod系统调用->
vfs_mknod->
dir->i_op->mknod(dir, dentry, mode, dev)->
f2fs_mknod (struct inode * dir, struct dentry *dentry,int mode, dev_t rdev)->
f2fs_new_inode
init_special_inode
字符设备文件与块设备文件创建的流程基本是一致的,都是通过mknod系统调用。以f2fs为例它通过调用f2fs_mknod。首先通过f2fs_new_inode来创建inode
-
f2fs_new_inode:创建新的inode
-
init_special_inode
为新创建的inode进行初始化,其中针对字符设备会指定inode->i_fop为def_chr_fops,它会在文件打开的时候赋值给新创建的file->f_ops,并调用其中的chrdev_open
4. 字符设备文件的打开
参考打开文件的流程,对于字符设备file->fops为def_chr_fops,它的open函数为chrdev_open
因此将按如下路程执行:
do_last
finish_open
do_dentry_open
chrdev_open
5. 块设备文件的创建
mknod系统调用->
vfs_mknod->
dir->i_op->mknod(dir, dentry, mode, dev)->
f2fs_mknod (struct inode * dir, struct dentry *dentry,int mode, dev_t rdev)->
f2fs_new_inode
init_special_inode
块设备文件创建与字符设备文件创建的流程基本是一致的,都是通过mknod系统调用。以f2fs为例它通过调用f2fs_mknod。首先通过f2fs_new_inode来创建次inode,这其中也包含了f2fs自己的inode,之后对次inode执行初始化,其中初始化次inode->f_ops为def_blk_fops
-
f2fs_new_inode:创建新的inode,这个就是次inode,次inode的内容就是设备号,并没有占用额外的磁盘空间,将来次inode就是通过设备号建立了与block_inode的关联;
-
init_special_inode:为新创建的次inode进行初始化,其中针对块设备会指定inode->i_fop为def_blk_fops
5. 块设备文件的打开
参考打开文件的流程,对于块设备file->fops为def_blk_fops,它的open函数为blkdev_open,它的执行流程为:
do_last
finish_open
do_dentry_open
blkdev_open->
bd_acquire(inode)//次inode
bdget
blkdev_get(bdev, filp->f_mode, filp)
-
blkdev_open
在文件打开的时候将次inode->i_fop赋值给新创建的file->f_ops,并调用其中的blkdev_open,blkdev_open来源于创建块设备文件时,它被初始化为次inode的i_fop即def_blk_fops,blkdev_open调用bd_acquire -
bd_acquire
获取主inode的block_device, 会通过bdget首先来查找是否已经创建了与传入设备号相同的inode,如果已经创建则直接返回,否则调用blockdev_superblock的ops来创建bdev文件系统下的块设备文件的bdev_inode,它包含了主inode和block_device,并建立起了与次inode的关系,注意到address_space是内嵌在inode中,因此address_space也准备好了。
bdget也会对新创建的主inode进行初始化,其中会初始化inode内嵌的address_space->a_ops为def_blk_aops,它会在读写块设备文件时被调用 -
blkdev_get
建立起block_device与底层块设备的关系,这样就可以通过通过读写块设备文件来达到操作块设备的目的。上一步bdget获取了block_device,通过block_device可以获取到gendisk,进一步获取各个分区hd_struct,从而可以实现对磁盘的操作