文件系统和模块设备的Page cache问题
ahcoder 2025-06-09 19:44 3 浏览
一,普通文件的address space
文件系统读取文件一般会使用do_generic_file_read(),mapping指向普通文件的address space。如果一个文件的某一块不在page cache中,在find_get_page函数中会创建一个page,并将这个page根据index插入到这个普通文件的address space中。这也是我们熟知的过程。
static ssize_t do_generic_file_read(struct file *filp, loff_t *ppos,
struct iov_iter *iter, ssize_t written)
{
struct address_space *mapping = filp->f_mapping;
struct inode *inode = mapping->host;
struct file_ra_state *ra = &filp->f_ra;
pgoff_t index;
pgoff_t last_index;
pgoff_t prev_index;
unsigned long offset; /* offset into pagecache page */
unsigned int prev_offset;
int error = 0;
index = *ppos >> PAGE_CACHE_SHIFT;
prev_index = ra->prev_pos >> PAGE_CACHE_SHIFT;
prev_offset = ra->prev_pos & (PAGE_CACHE_SIZE-1);
last_index = (*ppos + iter->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
offset = *ppos & ~PAGE_CACHE_MASK;
for (;;) {
struct page *page;
pgoff_t end_index;
loff_t isize;
unsigned long nr, ret;
cond_resched();
find_page:
page = find_get_page(mapping, index);
if (!page) {
page_cache_sync_readahead(mapping,
ra, filp,
index, last_index - index);
page = find_get_page(mapping, index);
if (unlikely(page == NULL))
goto no_cached_page;
}
......//此处省略约200行
}
更多Linux内核视频教程文档资料后台私信【内核大礼包】自行获取。
二,块设备的address space
但是在读取文件系统元数据的时候,元数据对应的page会被加入到底层模块设备的address space中。下面代码的bdev_mapping指向块设备的address space,调用find_get_page_flags()后,一个新的page(如果page不在这个块设备的address space)就被创建并且插入到这个块设备的address space。
static struct buffer_head *
__find_get_block_slow(struct block_device *bdev, sector_t block)
{
struct inode *bd_inode = bdev->bd_inode;
struct address_space *bd_mapping = bd_inode->i_mapping;
struct buffer_head *ret = NULL;
pgoff_t index;
struct buffer_head *bh;
struct buffer_head *head;
struct page *page;
int all_mapped = 1;
index = block >> (PAGE_CACHE_SHIFT - bd_inode->i_blkbits);
page = find_get_page_flags(bd_mapping, index, FGP_ACCESSED);
if (!page)
goto out;
......//此处省略几十行
}
三,两份缓存
前面提到的情况是正常的操作流程,属于普通文件的page放在文件的address space,元数据对应的page放在块设备的address space中,大家井水不犯河水,和平共处。但是世事难料,总有一些不按套路出牌的人。文件系统在块设备上欢快地跑着,如果有人绕过文件系统,直接去操作块设备上属于文件的数据块,这会出现什么情况?如果这个数据块已经在普通文件的address space中,这次直接的数据块修改能够立马体现到普通文件的缓存中吗?
答案是直接修改块设备上块会新建一个对应这个块的page,并且这个page会被加到块设备的address space中。也就是同一个数据块,在其所属的普通文件的address space中有一个对应的page。同时,在这块设备的address space中也会有一个与其对应的page,所有的修改都更新到这个块设备address space中的page上。除非重新从磁盘上读取这一块的数据,否则普通文件的文件缓存并不会感知这一修改。
四,实验
口说无凭,实践是检验真理的唯一标准。我在这里准备了一个实验,先将一个文件的数据全部加载到page cache中,然后直接操作设备修改这个文件的数据块,再读取文件的内容,看看有没有被修改。
为了确认一个文件的数据是否在page cache中,我先介绍一个有趣的工具---vmtouch,这个工具可以显示出一个文件有多少内容已经被加载到page cache。大家可以在github上获取到它的源码,并自行编译安装
https://github.com/hoytech/vmtouch
现在开始我们的表演:
首先,我们找一个测试文件,就拿我家目录下的read.c来测试,这个文件的内容就是一些凌乱的c代码。
~ cat read.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
char buf[4096] = {0};
int main(int argc, char *argv[])
{
int fd;
if (argc != 2) {
printf("argument error.\n");
return -1;
}
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open failed:");
return -1;
}
read(fd, buf, 4096);
//read(fd, buf, 4096);
close(fd);
}
~
接着运行vmtouch,看看这个文件是否在page cache中了,由于这个文件刚才被读取过,所以文件已经全部保存在page cache中了。
~ vmtouch read.c
Files: 1
Directories: 0
Resident Pages: 1/1 4K/4K 100%
Elapsed: 0.000133 seconds
~
然后我通过debugfs找到read.c的数据块,并且通过dd命令直接修改数据块。
Inode: 3945394 Type: regular Mode: 0644 Flags: 0x80000
Generation: 659328746 Version: 0x00000000:00000001
User: 0 Group: 0 Project: 0 Size: 386
File ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x5ad2f108:60154d80 -- Sun Apr 15 14:28:24 2018
atime: 0x5ad2f108:5db2f37c -- Sun Apr 15 14:28:24 2018
mtime: 0x5ad2f108:5db2f37c -- Sun Apr 15 14:28:24 2018
crtime: 0x5ad2f108:5db2f37c -- Sun Apr 15 14:28:24 2018
Size of extra inode fields: 32
EXTENTS:
(0):2681460
~ dd if=/dev/zero of=/dev/sda2 seek=2681460 bs=4096 count=1
1+0 records in
1+0 records out
4096 bytes (4.1 kB, 4.0 KiB) copied, 0.000323738 s, 12.7 MB/s
修改已经完成,我们看看直接读取这个文件会怎么样。
~ cat read.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
char buf[4096] = {0};
int main(int argc, char *argv[])
{
int fd;
if (argc != 2) {
printf("argument error.\n");
return -1;
}
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open failed:");
return -1;
}
read(fd, buf, 4096);
//read(fd, buf, 4096);
close(fd);
}
~ vmtouch read.c
Files: 1
Directories: 0
Resident Pages: 1/1 4K/4K 100%
Elapsed: 0.00013 seconds
文件依然在page cache中,所以我们还是能够读取到文件的内容。然而当我们drop cache以后,再读取这个文件,会发现文件内容被清空。
~ vmtouch read.c
Files: 1
Directories: 0
Resident Pages: 1/1 4K/4K 100%
Elapsed: 0.00013 seconds
~ echo 3 > /proc/sys/vm/drop_caches
~ vmtouch read.c
Files: 1
Directories: 0
Resident Pages: 0/1 0/4K 0%
Elapsed: 0.000679 seconds
~ cat read.c
~
总结
普通文件的数据可以保存在它的地址空间中,同时直接访问块设备中此文件的块,也会将这个文件的数据保存在块设备的地址空间中。这两份缓存相互独立,kernel并不会为这种非正常访问同步两份缓存,从而避免了同步的开销。
相关推荐
- MySQL的数据类型和存储引擎介绍(mysql数据存储种类)
-
一.MySQL数据类型1.整数类型注:MySQL可以为整数类型指定宽度,比如int(3)、int(5),这个限制不是限制value的合法范围,所以对绝大数应用没有任何意义,对于存储而言,int(...
- Linux目录管理(linux目录权限详解)
-
创建、复制、删除、移动、查看、编辑、压缩、查找内容提要:目录结构如图1-1所示bin普通用户使用的命令/bin/ls,/bin/datesbin管理员使用的命令/sbin/server如图1-2所...
- C语言中的变量,在内存中如何存储?
-
在C语言中,提供了char,int这样的变量类型,可以定义一个变量,然后,在变量中存储数据。例如:charx=68;就是定义了一个char类型的变量,变量的名称是x,在x这个变量中存放68...
- Redis进阶之底层存储数据结构及内存优化
-
前言Redis作为高性能缓存中间件,除了拥有高性能的特点之后,相比于其他缓存而言还支持多种数据结构,而如String、List、Set、SortedSet和Hash都是redis对外支持的数据结构,而...
- 在Centos8 中使用Stratis管理本地存储(一)
-
Stratis是RHEL8/Centos8中提供的一个新的本地存储管理工具,它将有助于在块设备上创建分层存储。在RHEL8/Centos8中,可以通过安装两个软件包获得Stratis。在RHEL7,我...
- Linux基金会2015 Vault大会:专攻存储、文件系统
-
IT之家(www.ithome.com):Linux基金会2015Vault大会:专攻存储、文件系统谈到云计算,我们常常想到微软、Azure等词语。其实也不然,Linux在云计算适用性更强,例如运行...
- 安卓16大升级!Linux终端存储突破42.3GB,手机变电脑真香了?
-
IT之家4月20日消息,谷歌Pixel手机用户迎来重磅升级!安卓16Beta4版本中,Linux终端应用彻底解除存储限制,用户可自由调用手机全部存储空间,实测扩容至42.3GB,让手机秒变生产力工...
- Linux下ISCSI存储部署教程(iscsi存储优缺点)
-
在Linux系统下部署iSCSI存储是一项常见的任务,它能够为系统提供远程块级存储,使多个服务器可以通过网络访问共享的存储设备。iSCSI是一种基于TCP/IP的协议,通过IP网络来传输SCSI指令,...
- 如何在 Linux 系统中永久禁用交换分区 ?
-
Linux操作系统中的交换分区或交换文件充当硬盘上的临时存储区域,当物理内存(RAM)满时,系统使用该存储区域。它用于交换较少使用的内存页,这样系统就不会因为运行应用程序而耗尽物理内存。随着技术的发...
- 如何使用 Chmod 更改 Linux 上的文件和文件夹权限?
-
在Linux的世界里,权限就是秩序的基础。一个系统的安全性、文件访问策略、用户协作边界,无一不依赖于文件和目录权限的精细控制。而chmod(ChangeMode)命令,正是你手中调整权限的利剑...
- 掌控进程优先级:深入理解 Linux 的 Nice 与 Renice 命令
-
在日常的Linux系统管理中,资源调度是一个绕不开的话题。当系统负载过高、响应变慢,我们往往会寻找高资源占用的“罪魁祸首”,但我们还能做得更多——「调整这些进程的“优先级”」,让关键任务跑得更快,...
- Linux修改uboot启动延时方法详细攻略,触觉智能RK3568开发板演示
-
本文介绍Linux系统修改uboot启动延时方法,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持开源鸿蒙OpenHarmon...
- Linux基本命令—修改命令别名(linux怎么修改命令)
-
输入命令后系统的执行过程:alias(别名)—>内部命令—>hash表(记录外部命令的路径)—>$PATH(变量)—>命令找不到命令别名修改临时:aliascdnet=...
- 文件系统和模块设备的Page cache问题
-
一,普通文件的addressspace文件系统读取文件一般会使用do_generic_file_read(),mapping指向普通文件的addressspace。如果一个文件的某一块不在page...
- LINUX MMC子系统分析(四) MMC HOST模块分析
-
本章主要介绍mmchost相关的内容,包括mmc子系统提供的mmchost相关的接口,以及mmchost与mmccard等子模块的关联等内容。我们按如下几部分进行介绍:一、相关的数据结构体及关...
- 一周热门
- 最近发表
- 标签列表
-
- linux 远程 (37)
- u盘 linux (32)
- linux 登录 (34)
- linux 路径 (33)
- linux 文件命令 (35)
- linux 是什么 (35)
- linux 界面 (34)
- 查看文件 linux (35)
- linux 语言 (33)
- linux代码 (32)
- linux 查看命令 (33)
- 关闭linux (34)
- root linux (33)
- 删除文件 linux (35)
- linux 主机 (34)
- linux与 (33)
- linux 函数 (35)
- linux .ssh (35)
- cpu linux (35)
- 查看linux 系统 (32)
- linux 防火墙 (33)
- linux 手机 (32)
- linux 镜像 (34)
- linux ip地址 (34)
- linux 用户查看 (33)