linux文件系统二 VFS读写流程

一、文件系统框架:

  从文件系统一种我们了解了linux文件系统的框架,这里我们首先再通过下面简洁的流程图

来展示linux文件系统文件读写的大框架:

  从上图中可以看出linux文件系统的读写通过调用虚拟文件系统(VFS)的对应接口,从而

调用到实际文件系统的读写接口,来进行emmc的操作,这样可以实现多文件系统兼容,如android

中的boot/system分区是ext4的格式,但cache/userdata我们可以配置为f2fs的文件系统格式,但

VFS层的调用接口是不变的。对应的相关结构体直接的关系:

二、抓取调用trace方法:

1、linux应用层操作方法:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(void)
{
	int i,f;
	FILE *fp;
	char string[24];

	fp = fopen("test.dat","w+");
	return 0;
}

gcc  file_open.c  -o  file_open

strace  ./file_open   可以抓取到应用层调用的trace:

mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7562000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7562940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb770c000, 8192, PROT_READ)   = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
mprotect(0xb774c000, 4096, PROT_READ)   = 0
munmap(0xb7712000, 78940)               = 0
brk(0)                                  = 0x81d4000
brk(0x81f5000)                          = 0x81f5000
open("test.dat", O_RDWR|O_CREAT|O_TRUNC, 0666) = -1 EACCES (Permission denied)
exit_group(0)                           = ?
+++ exited with 0 +++

2、kernel  ftrace抓取:

# function.sh
#!/bin/bash
debugfs=/sys/kernel/debug
echo nop > $debugfs/tracing/current_tracer
echo 0 > $debugfs/tracing/tracing_on
echo $$ > $debugfs/tracing/set_ftrace_pid
echo function_graph > $debugfs/tracing/current_tracer
echo vfs_open > $debugfs/tracing/set_graph_function
# echo test_proc_write > $debugfs/tracing/set_graph_function
echo 1 > $debugfs/tracing/tracing_on

exec $@

./function.sh  file_open

cat /sys/kernel/debug/tracing/trace > open.txt

# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 1)               |  vfs_open() {
 1)               |    do_dentry_open() {
 1)               |      path_get() {
 1)   0.148 us    |        mntget();
 1)   0.034 us    |        _raw_spin_lock();
 1)   0.812 us    |      }
 1)   0.036 us    |      try_module_get();
 1)               |      security_file_open() {
 1)   0.112 us    |        apparmor_file_open();
 1)   0.025 us    |        __fsnotify_parent();
 1)   0.107 us    |        fsnotify();
 1)   1.074 us    |      }
 1)               |      ext4_file_open() {
 1)               |        dquot_file_open() {
 1)   0.025 us    |          generic_file_open();
 1)   0.276 us    |        }
 1)   0.798 us    |      }
 1)   0.298 us    |      file_ra_state_init();
 1)   4.738 us    |    }
 1)   5.484 us    |  }

三、调用流程解析:

1、open的文件的代码流程可以用如下图表示:

(1)应用层的open会调用到C库中的open函数,通过系统系统调用,调用到:

//kernel-4.9/fs/open.c
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
	if (force_o_largefile())
		flags |= O_LARGEFILE;

	return do_sys_open(AT_FDCWD, filename, flags, mode);
}
static int do_dentry_open(struct file *f,
			  struct inode *inode,
			  int (*open)(struct inode *, struct file *),
			  const struct cred *cred)
{
...
	if (!open)
		open = f->f_op->open;
...
}

(2)VFS层经过层层调用后会调用到具体的文件系统的接口ext4_file_open,从open

const struct file_operations ext4_file_operations = {
	.llseek		= ext4_llseek,

	.mmap		= ext4_file_mmap,
	.open		= ext4_file_open, //f->f_op->open
}

2、上面从user_mode的open,经过VFS层的转换,再调用到具体的问题系统的打开流程,其中文件的读写流程

与上面的open流程相似,也是C库中的read/write通过系统调用到kernel mode的vfs read/write,最后调用到具体

的文件系统的read/write。后面将详细介绍ext4文件系统的open/read/write的流程。

# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 0)               |    __vfs_read() {
 0)               |      new_sync_read() {
 0)               |        generic_file_read_iter() {
 0)   0.027 us    |          _cond_resched();
 0)               |          pagecache_get_page() {
 0)   0.191 us    |            find_get_entry();
 0)   0.634 us    |          }
 0)               |          page_cache_sync_readahead() {
 0)               |            ondemand_readahead() {
 0)               |              __do_page_cache_readahead() {
 0)               |                __alloc_pages_nodemask() {
 0)   0.027 us    |                  _cond_resched();
 0)   0.032 us    |                  next_zones_zonelist();
 0)               |                  get_page_from_freelist() {
 0)   0.040 us    |                    next_zones_zonelist();
 0)   0.026 us    |                    next_zones_zonelist();
 0)   0.047 us    |                    __zone_watermark_ok();
 0)   0.026 us    |                    __mod_zone_page_state();
 0)   2.132 us    |                  }
 0)   3.274 us    |                }
 0)   0.112 us    |                blk_start_plug();
 0)               |                ext4_readpages() {
 0)   0.029 us    |                    bio_add_page();
 0)   0.038 us    |                    put_page();
 0)               |                    submit_bio()
 0)               |                      generic_make_request() {
 0)   0.085 us    |                        blk_queue_enter();
 0)               |                        blk_queue_bio();
                                   }
 0)   0.037 us    |                put_pages_list();
 0)               |                blk_finish_plug()

write的流程比较长,用思维导图绘制如下:

作者:frank_zyp 
您的支持是对博主最大的鼓励,感谢您的认真阅读。 
本文无所谓版权,欢迎转载。

发布了59 篇原创文章 · 获赞 80 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/frank_zyp/article/details/88579802