读文件

读文件其实就是读出目录中的目录项,首先假定文件在磁盘上且已经打开。用户进程有如下语句:

read(fd, data, len);

即读取 fd 对应文件,读取长度为 len,存入 data 中。下面来分析一下读文件的实现。

通用文件访问接口层的处理流程

先进入通用文件访问接口层的处理流程,即进一步调用如下用户态函数:read->sys_read->syscall,从而引起系统调用进入到内核态。到了内核态以后,通过中断处理例程,会调用到 sys_read 内核函数,并进一步调用 sysfile_read 内核函数,进入到文件系统抽象层处理流程完成进一步读文件的操作。

文件系统抽象层的处理流程

  1. 检查错误,即检查读取长度是否为 0 和文件是否可读。

  2. 分配 buffer 空间,即调用 kmalloc 函数分配 4096 字节的 buffer 空间。

  3. 读文件过程

[1] 实际读文件

循环读取文件,每次读取 buffer 大小。每次循环中,先检查剩余部分大小,若其小于 4096 字节,则只读取剩余部分的大小。然后调用 file_read 函数(详细分析见后)将文件内容读取到 buffer 中,alen 为实际大小。调用 copy_to_user 函数将读到的内容拷贝到用户的内存空间中,调整各变量以进行下一次循环读取,直至指定长度读取完成。最后函数调用层层返回至用户程序,用户程序收到了读到的文件内容。

[2] file_read 函数

这个函数是读文件的核心函数。函数有 4 个参数,fd 是文件描述符,base 是缓存的基地址,len 是要读取的长度,copied_store 存放实际读取的长度。函数首先调用 fd2file 函数找到对应的 file 结构,并检查是否可读。调用 filemap_acquire 函数使打开这个文件的计数加 1。调用 vop_read 函数将文件内容读到 iob 中(详细分析见后)。调整文件指针偏移量 pos 的值,使其向后移动实际读到的字节数 iobuf_used(iob)。最后调用 filemap_release 函数使打开这个文件的计数减 1,若打开计数为 0,则释放 file。

SFS 文件系统层的处理流程

vop_read 函数实际上是对 sfs_read 的包装。在 sfs_inode.c 中 sfs_node_fileops 变量定义了.vop_read = sfs_read,所以下面来分析 sfs_read 函数的实现。

sfs_read 函数调用 sfs_io 函数。它有三个参数,node 是对应文件的 inode,iob 是缓存,write 表示是读还是写的布尔值(0 表示读,1 表示写),这里是 0。函数先找到 inode 对应 sfs 和 sin,然后调用 sfs_io_nolock 函数进行读取文件操作,最后调用 iobuf_skip 函数调整 iobuf 的指针。

在 sfs_io_nolock 函数中,先计算一些辅助变量,并处理一些特殊情况(比如越界),然后有 sfs_buf_op = sfs_rbuf,sfs_block_op = sfs_rblock,设置读取的函数操作。接着进行实际操作,先处理起始的没有对齐到块的部分,再以块为单位循环处理中间的部分,最后处理末尾剩余的部分。每部分中都调用 sfs_bmap_load_nolock 函数得到 blkno 对应的 inode 编号,并调用 sfs_rbuf 或 sfs_rblock 函数读取数据(中间部分调用 sfs_rblock,起始和末尾部分调用 sfs_rbuf),调整相关变量。完成后如果 offset + alen > din->fileinfo.size(写文件时会出现这种情况,读文件时不会出现这种情况,alen 为实际读写的长度),则调整文件大小为 offset + alen 并设置 dirty 变量。

sfs_bmap_load_nolock 函数将对应 sfs_inode 的第 index 个索引指向的 block 的索引值取出存到相应的指针指向的单元(ino_store)。它调用 sfs_bmap_get_nolock 来完成相应的操作。sfs_rbuf 和 sfs_rblock 函数最终都调用 sfs_rwblock_nolock 函数完成操作,而 sfs_rwblock_nolock 函数调用 dop_io->disk0_io->disk0_read_blks_nolock->ide_read_secs 完成对磁盘的操作。

results matching ""

    No results matching ""