Linux内核学习:EXT4 文件系统在 Linux 内核系统中的读写过程

目录

1 概述

2 虚拟文件系统 与 Ext4 文件系统

2.1 sys_write( ) 代码跟踪

2.2 sys_write( ) 过程分析

2.3 sys_write( ) 的核心部分 vfs_write( )

2.4 ext4_file_write( )

2.4.1 ext4文件系统的extent

2.4.2 ext4_file_write( ) 

2.5 generic_file_write_iter( )

2.6 __generic_file_write_iter( )

2.7 generic_perform_write( )

2.7.1 ext4文件系统address_space_operations

2.7.2 ext4文件系统delay allocation机制

2.7.3 执行完 generate_write_back( )后


1 概述

用户进程通过系统调用write()往磁盘上写数据,但write()执行结束后,数据是否 立即写到磁盘上?内核读文件数据时,使用到了“提前读”;写数据时,则使用了“延迟写”, 即write()执行结束后,数据并没有立即立即将请求放入块设备驱动请求队列,然后写到 硬盘上。

跟踪的时候通过

dump_stack

重新编译linux内核,跟踪函数执行过程。

2 虚拟文件系统 与 Ext4 文件系统

首先文件系统在内核中的读写过程是在 sys_write( ) 中定义的。

2.1 sys_write( ) 代码跟踪

sys_write( ) 定义在 include/linux/syscalls.h 中:

asmlinkage long sys_write(unsigned int fd, const char __user *buf, 568 size_t count);

sys_write( )的具体实现在 fs/read_write.c 中:

SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
         size_t, count)
{
     struct fd f = fdget_pos(fd);
     ssize_t ret = -EBADF; 
     if (f.file) {
         loff_t pos = file_pos_read(f.file);
         ret = vfs_write(f.file, buf, count, &pos);
         if (ret >= 0)
             file_pos_write(f.file, pos);
         fdput_pos(f);
     } 
     return ret;
}

2.2 sys_write( ) 过程分析

可以看出在实现 sys_write( ) 的时候,分为如下几步:

1) 根据打开文件号 fd找到该已打开文件file结构:

struct fd f = fdget_pos(fd);

2) 读取当前文件的读写位置:

loff_t pos = file_pos_read(f.file);

3) 写入:

ret = vfs_write(f.file, buf, count, &pos);

4) 根据读文件结果,更新文件读写位置 :

file_pos_write(f.file, pos);

2)和  4)可以作为写入之前和之后的对应操作来看,一个是读取当前文件的位置,一个是根据写文件的结果,更新文件的读写位置,主要代码还是在 fs/read_write.c 中:

static inline loff_t file_pos_read(struct file *file)                                                   
{
     return file->f_pos;
}

static inline void file_pos_write(struct file *file, loff_t pos)
{
     file->f_pos = pos;
}

3) 是整个 sys_write( ) 中最为重要的一部分,下面我们仔细分析一下这个函数。

2.3 sys_write( ) 的核心部分 vfs_write( )

ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos){
     ssize_t ret;
 
     if (!(file->f_mode & FMODE_WRITE))
         return -EBADF;
     if (!(file->f_mode & FMODE_CAN_WRITE))
         return -EINVAL;
     if (unlikely(!access_ok(VERIFY_READ, buf, count)))
         return -EFAULT; 
     ret = rw_verify_area(WRITE, file, pos, count);
     if (ret >= 0) {
         count = ret;
         file_start_write(file);
         if (file->f_op->write)
             ret = file->f_op->write(file, buf, count, pos);
         else if (file->f_op->aio_write)
             ret = do_sync_write(file, buf, count, pos);
         else
             ret = new_sync_write(file, buf, count, pos);
         if (ret > 0) {
             fsnotify_modify(file);
             add_wchar(current, ret);
         }
         inc_syscw(current);
         file_end_write(file);
     }
 
     return ret;
}

首先函数在 rw_verify_area(WRITE, file, pos, count); 检查文件是否从当前位置 pos 开始的 count 字节是否对写操作加上了 “强制锁”,这是通过调用函数完成的。

通过合法性检查后,就调用具体文件系统 file_operations中 write 的方法。对于ext4文件系统,file_operations方法定义在 fs/ext4/file.c 中。从定义中可知 write 方法实现函数为 do_sync_write( )。 

下面是ext4文件系统操作的数据结构:

 const struct file_operations ext4_file_operations = {
     .llseek     = ext4_llseek,
     .read       = new_sync_read,
     .write      = new_sync_write,
     .read_iter  = generic_file_read_iter,
     .write_iter = ext4_file_write_iter,
     .unlocked_ioctl = ext4_ioctl,
 #ifdef CONFIG_COMPAT
     .compat_ioctl   = ext4_compat_ioctl,
 #endif
     .mmap       = ext4_file_mmap,
     .open       = ext4_file_open,
     .release    = ext4_release_file,
     .fsync      = ext4_sync_file,
     .splice_read    = generic_file_splice_read,
     .splice_write   = iter_file_splice_write,
     .fallocate  = ext4_fallocate,
 };

下面是do_sync_write( )的具体代码,也在fs/read_write.c中:

 ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
 {
     struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
     struct kiocb kiocb;
     ssize_t ret; 
     init_sync_kiocb(&kiocb, filp);
     kiocb.ki_pos = *ppos;
     kiocb.ki_nbytes = len; 
     ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
     if (-EIOCBQUEUED == ret)
         ret = wait_on_sync_kiocb(&kiocb);
     *ppos = kiocb.ki_pos;
     return ret;
 }
 EXPORT_SYMBOL(do_sync_write);

异步I/O允许用户空间来初始化操作而不必等待它们的完成,因此,一个应用程序可以在他的I/O处理进行中做其他的处理。

块和网络驱动在整个时间是完全异步的,因此只有字符驱动对于明确的异步I/O支持是候选的。实现异步I/O操作的file_operations方法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值