一、文件系统框架:
从文件系统一种我们了解了linux文件系统的框架,这里我们首先再通过下面简洁的流程图
来展示linux文件系统文件读写的大框架:
从上图中可以看出linux文件系统的读写通过调用虚拟文件系统(VFS)的对应接口,从而
调用到实际文件系统的读写接口,来进行emmc的操作,这样可以实现多文件系统兼容,如android
中的boot/system分区是ext4的格式,但cache/userdata我们可以配置为f2fs的文件系统格式,但
VFS层的调用接口是不变的。对应的相关结构体直接的关系:
二、抓取调用trace方法:
1、linux应用层操作方法:
#include
#include
#include
#include
#include
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
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。