read

SYSCALL_DEFINE3(read
    ksys_read()
        vfs_read()
            ext4_file_read_iter()
                generic_file_read_iter()
                |    kiocb_write_and_wait()
                |        filemap_write_and_wait_range()
                |            __filemap_fdatawrite_range()
                |                filemap_fdatawrite_wbc()
                |                    do_writepages()
                |            __filemap_fdatawait_range()
                |                filemap_get_folios_tag()
                |                folio_wait_writeback()
                |    file_accessed()
                |    mapping->a_ops->direct_IO()
                |
                |    filemap_read()
                |        filemap_get_pages()
                |        folio_mark_accessed()
                |        copy_folio_to_iter()
                |        file_accessed()

generic_file_read_iter() 是通用文件系统读路径。

如果是从 file 直接读取数据,需要先调用 do_writepages() 将脏页回写到文件中,然后 调用 folio_wait_writeback() 等待回写完成。 后面才能够利用 mapping->a_ops->direct_IO() 接口从 file 直接读取数据到用户空间 buffer。

如果是从 pagecache 复制数据到用户空间 buffer,需要先调用 filemap_get_pages() 先从 address_space->xarray 查找合适的 pagecache,并且保存在 fbatch 中。 再调用 folio_mark_accessed() 设置每一个 folio 的 referenced/active 属性, 在必要时将 folio 从 LRU inactive list 移动到 LRU active list 中。 最后再通过 copy_folio_to_iter() 将 fbatch 中每一个 folio 的内容复制到用户空间 buffer 中。

filemap_get_pages()
    filemap_get_read_batch()
        folio_batch_add()

    page_cache_sync_readahead()
    filemap_get_read_batch()
        folio_batch_add()

    filemap_create_folio()
        filemap_alloc_folio()
        filemap_add_folio()
            __filemap_add_folio()
            workingset_refault()
            folio_add_lru()
        filemap_read_folio()
        folio_batch_add()

    filemap_readahead()
        page_cache_async_ra()

调用 filemap_get_read_batch() 先从 address_space->xarray 查找合适的 pagecache, 如果找到合适的 pagecache,进入下一步。

如果找不到,调用 page_cache_sync_readahead() 继续尝试从 file 进行同步预读数据到 address_space->xarray 中, 再调用 filemap_get_read_batch() 从 address_space->xarray 查找合适的 pagecache。 如果找到合适的 pagecache,进入下一步。

如果还是找不到,调用 filemap_create_folio() 直接申请一个 folio,添加到 address_space->xarray 中,并且 从 file 读取对应的数据到 folio 中,同时将 folio 加入到对应的 LRU 链表中,最后直接返回。

(下一步)如果找到的 pagecache 有 readahead 标志,代表此页是之前通过预读操作读到的数据, 于是调用 filemap_readahead() 继续启动异步预读文件内容到 pagecache 中。最后返回之前找到合适的 pagecache。

Last updated

Was this helpful?