Linux 6.6内核内存管理深度解析(五):page cache(页缓存)— 读缓存在内存、写延迟回写

〇、全景:磁盘和用户之间的缓存层

前面几篇讲了物理内存怎么管理(#1 memblock/buddy)、虚拟地址怎么映射到物理页(#3 页表缺页)。但缺页分配出来的页,数据从哪来 ?匿名页(堆/栈)是凭空清零,文件页则来自磁盘------而内核不会每次都去读磁盘,它在内存里维护了一层 page cache(页缓存):

  • 读 :先查 page cache,命中就直接拷给用户(copy_folio_to_iter);没命中才读盘填进去;
  • 写 :先写进 page cache 标脏,不立刻落盘(延迟写 write-back),由后台 flush 线程异步刷回磁盘。

#mermaid-svg-ZJwJrUpeUXz69QCx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZJwJrUpeUXz69QCx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZJwJrUpeUXz69QCx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZJwJrUpeUXz69QCx .error-icon{fill:#552222;}#mermaid-svg-ZJwJrUpeUXz69QCx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZJwJrUpeUXz69QCx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZJwJrUpeUXz69QCx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZJwJrUpeUXz69QCx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZJwJrUpeUXz69QCx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZJwJrUpeUXz69QCx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZJwJrUpeUXz69QCx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZJwJrUpeUXz69QCx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZJwJrUpeUXz69QCx .marker.cross{stroke:#333333;}#mermaid-svg-ZJwJrUpeUXz69QCx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZJwJrUpeUXz69QCx p{margin:0;}#mermaid-svg-ZJwJrUpeUXz69QCx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZJwJrUpeUXz69QCx .cluster-label text{fill:#333;}#mermaid-svg-ZJwJrUpeUXz69QCx .cluster-label span{color:#333;}#mermaid-svg-ZJwJrUpeUXz69QCx .cluster-label span p{background-color:transparent;}#mermaid-svg-ZJwJrUpeUXz69QCx .label text,#mermaid-svg-ZJwJrUpeUXz69QCx span{fill:#333;color:#333;}#mermaid-svg-ZJwJrUpeUXz69QCx .node rect,#mermaid-svg-ZJwJrUpeUXz69QCx .node circle,#mermaid-svg-ZJwJrUpeUXz69QCx .node ellipse,#mermaid-svg-ZJwJrUpeUXz69QCx .node polygon,#mermaid-svg-ZJwJrUpeUXz69QCx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZJwJrUpeUXz69QCx .rough-node .label text,#mermaid-svg-ZJwJrUpeUXz69QCx .node .label text,#mermaid-svg-ZJwJrUpeUXz69QCx .image-shape .label,#mermaid-svg-ZJwJrUpeUXz69QCx .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZJwJrUpeUXz69QCx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZJwJrUpeUXz69QCx .rough-node .label,#mermaid-svg-ZJwJrUpeUXz69QCx .node .label,#mermaid-svg-ZJwJrUpeUXz69QCx .image-shape .label,#mermaid-svg-ZJwJrUpeUXz69QCx .icon-shape .label{text-align:center;}#mermaid-svg-ZJwJrUpeUXz69QCx .node.clickable{cursor:pointer;}#mermaid-svg-ZJwJrUpeUXz69QCx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZJwJrUpeUXz69QCx .arrowheadPath{fill:#333333;}#mermaid-svg-ZJwJrUpeUXz69QCx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZJwJrUpeUXz69QCx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZJwJrUpeUXz69QCx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZJwJrUpeUXz69QCx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZJwJrUpeUXz69QCx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZJwJrUpeUXz69QCx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZJwJrUpeUXz69QCx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZJwJrUpeUXz69QCx .cluster text{fill:#333;}#mermaid-svg-ZJwJrUpeUXz69QCx .cluster span{color:#333;}#mermaid-svg-ZJwJrUpeUXz69QCx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZJwJrUpeUXz69QCx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZJwJrUpeUXz69QCx rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZJwJrUpeUXz69QCx .icon-shape,#mermaid-svg-ZJwJrUpeUXz69QCx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZJwJrUpeUXz69QCx .icon-shape p,#mermaid-svg-ZJwJrUpeUXz69QCx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZJwJrUpeUXz69QCx .icon-shape .label rect,#mermaid-svg-ZJwJrUpeUXz69QCx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZJwJrUpeUXz69QCx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZJwJrUpeUXz69QCx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZJwJrUpeUXz69QCx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 命中 → 拷贝给用户

miss → 读盘填进去
预读 readahead

提前读后续页
写进缓存标脏

不立刻落盘
脏页超阈值

balance_dirty_pages 限速
异步刷回磁盘

清除脏标记
page cache

每个文件一个 address_space

用 xarray 按页偏移索引 folio
读 read()

filemap_read
写 write()

generic_perform_write
回写 writeback

wb_workfn flush 线程

一句话主线:page cache 是"磁盘 ↔ 用户"之间的一层内存缓存------每个文件对应一个 address_space,用 xarray 按页偏移索引 folio;读 = 先查缓存(命中直接拷、miss 读盘 filemap_read_folio 填进去),写 = 写进缓存标脏(write_end)、由后台 flush 线程 wb_workfn 异步回写,脏页太多时 balance_dirty_pages 限速写进程;预读 readahead 提前把后续页读进来 。它和上一篇 #3 缺页是同一枚硬币的两面:文件页缺页 do_fault 本质就是"从 page cache 里找不到这一页,去磁盘读进来"。


一、核心数据结构:address_space + xarray + folio

1.1 一个文件一个 address_space

每个 inode(文件)对应一个 address_space,它就是这个文件的 page cache 的"账簿":

c 复制代码
// include/linux/fs.h (v6.6, line 470)
struct address_space {
	struct inode		*host;         // 归属哪个文件(inode)
	struct xarray		i_pages;       // 按页偏移索引 folio 的树
	struct rb_root_cached	i_mmap;        // 映射这个文件的所有 vma
	unsigned long		nrpages;       // 缓存了多少页
	pgoff_t			writeback_index; // 回写进度(写到哪一页了)
	const struct address_space_operations *a_ops; // 读/写/回写的回调
	// ...
};
  • i_pages(xarray) :核心索引结构,key 是"页偏移"(文件第几个页),value 是 struct folio *。旧内核用 radix tree,v6.6 已换成更通用的 xarray。
  • a_ops(address_space_operations) :具体文件系统提供的回调------read_folio(读一页)、readahead(预读一批)、write_begin/write_end(写)、writepages(回写落盘)。page cache 是通用框架,具体怎么读盘/写盘交给文件系统。

1.2 folio:页缓存的单位(不再是单 struct page)

v6.6 的 page cache 基本单位是 struct folio (include/linux/mm_types.h:293),不再是单个 struct page。一个 folio 可以包含多个物理页(order 0 单页,或 order 9 = 2MB 大页),把"一组连续页"当整体管理,减少了逐页加锁/记数的开销,也让 THP 大页能直接作为缓存单位。

1.3 xarray 的 tag:标记"脏"和"回写中"

xarray 除了存 folio,还能给每个条目打 tag。page cache 用两个 tag 标记状态:

c 复制代码
// include/linux/fs.h (v6.6, line 498)
#define PAGECACHE_TAG_DIRTY	XA_MARK_0     // 这页被改过,要回写
#define PAGECACHE_TAG_WRITEBACK	XA_MARK_1     // 正在回写中

回写时,内核用 xa_marked 扫出所有打了 DIRTY tag 的 folio 拿去落盘,省得遍历整个文件的所有页。


二、读路径:查缓存 → 预读 → 读盘

read(fd, buf, n) 走 generic_file_read_iter(filemap.c:2790),它对 buffered 读(非 O_DIRECT)最后落到 filemap_read(filemap.c:2617):

c 复制代码
// mm/filemap.c (v6.6, line 2790,节选)
ssize_t generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
	// O_DIRECT 直接读盘,绕过 page cache(:2798 direct_IO)
	if (iocb->ki_flags & IOCB_DIRECT) { ... }
	// 普通 buffered 读 → 走 page cache
	return filemap_read(iocb, iter, retval);   // :2831
}

filemap_read(filemap.c:2617)是一个循环:每次用 filemap_get_pages(filemap.c:2535)取一批 folio,再 copy_folio_to_iter 拷给用户。核心在 filemap_get_pages,它按"页在不在缓存里"分三种情况:

c 复制代码
// mm/filemap.c (v6.6, line 2535,节选)
	filemap_get_read_batch(mapping, index, last_index - 1, fbatch); // ① 查 xarray
	if (!folio_batch_count(fbatch)) {                               // 缓存 miss
		page_cache_sync_readahead(mapping, ra, filp, index, ...);  // ② 同步预读
		filemap_get_read_batch(mapping, index, last_index - 1, fbatch);
	}
	if (!folio_batch_count(fbatch)) {                               // 还是没有
		err = filemap_create_folio(filp, mapping, ...);            // ③ 分配 + 读盘
		// ...
	}
  • ① 命中 :filemap_get_read_batch(filemap.c:2329)从 xarray 批量捞出 folio,直接拷给用户,零磁盘 I/O;
  • ② 预读 :miss 时先 page_cache_sync_readahead 把当前及后续一批页读进来(见 §五);
  • ③ 读盘 :还 miss,filemap_create_folio(filemap.c:2478)分配一个新 folio、filemap_add_folio 挂进 xarray、filemap_read_folio(filemap.c:2365)调用 a_ops->read_folio 真正从磁盘读;对"预读已部分填好但还没读完"的 folio,则走 filemap_update_page(filemap.c:2422)补读并等待 I/O 完成。

注意 generic_file_read_iter 里的 O_DIRECT 分支(:2798):用 O_DIRECT 打开的文件绕过 page cache ,direct_IO 直接读盘。这是"要不要缓存"的选择------默认要缓存(page cache),数据库等自己管缓存的场景才用 O_DIRECT 绕过。


三、写路径:写进缓存 + 标脏(延迟写)

write(fd, buf, n) 走 generic_file_write_iter(filemap.c:4081),buffered 写落到 generic_perform_write(filemap.c:3932):

c 复制代码
// mm/filemap.c (v6.6, line 3932,节选)
	do {
		status = a_ops->write_begin(file, mapping, pos, bytes, ...); // ① 准备页
		// ...
		copied = copy_page_from_iter_atomic(page, offset, bytes, i); // ② 拷用户数据
		// ...
		status = a_ops->write_end(file, mapping, pos, bytes, copied, ...); // ③ 标脏
		// ...
		balance_dirty_pages_ratelimited(mapping);   // ④ 脏页多了就限速
	} while (iov_iter_count(i));
  • ① write_begin:文件系统拿到(或分配)page cache 里的目标页,必要时读盘先填满要改的部分;
  • ② 拷贝 :copy_page_from_iter_atomic 把用户缓冲区数据拷进 page cache 页;
  • ③ write_end :标记这页为 dirty (打上 PAGECACHE_TAG_DIRTY),但绝不写盘;
  • ④ 限速 :balance_dirty_pages_ratelimited(page-writeback.c:2062)检查脏页是否超阈值,超了就慢下来(见 §四)。

这就是"延迟写(write-back)" :write() 返回时数据只进了内存缓存,还没到磁盘。好处是快(攒一批再落盘、合并连续写),代价是掉电/崩溃会丢还没回写的数据(所以有 fsync/O_SYNC 强制落盘)。


四、回写 writeback:balance_dirty_pages 限速 + flush 线程

脏页不能无限攒,内核用两条腿把它刷回磁盘:一条是"写进程侧"的限速,一条是"后台 flush 线程"的刷盘。

4.1 三个阈值:free-run → 后台回写 → 节流

脏页占"可脏内存"的比例由两个可配置阈值(默认 dirty_background_ratio = 10、vm_dirty_ratio = 20,page-writeback.c:74 / :91)和派生的 freerun 上限一起分档:

脏页比例 内核动作
< bg_thresh(10%) 写进程全速,无动作
> bg_thresh(10%) 启动后台回写 wb_start_background_writeback
> freerun ceiling((thresh+bg_thresh)/2 = 15%) 写进程开始被节流 (wb_position_ratio 算比例,pause)
> thresh(20%) 严重节流,写进程基本停下来等回写

balance_dirty_pages(page-writeback.c:1669)是限速的核心,它在一个循环里反复判断:

c 复制代码
// mm/page-writeback.c (v6.6, line 1669,节选)
	for (;;) {
		// ① 脏页超过后台阈值 → 启动后台回写
		if (!laptop_mode && nr_reclaimable > gdtc->bg_thresh &&
		    !writeback_in_progress(wb))
			wb_start_background_writeback(wb);          // :1754
		// ② 还没到 freerun 上限 → 写进程全速,跳出(if 在 :1768)
		if (dirty <= dirty_freerun_ceiling(thresh, bg_thresh) && ...)
			break;                                       // :1783
		// ③ 超过 thresh → 算 pos_ratio,按比例 pause 写进程
		wb_position_ratio(gdtc);                         // :1813
		// ... pause 一段时间后回到循环重新判断 ...
	}

dirty_freerun_ceiling(page-writeback.c:813)就是 (thresh + bg_thresh) / 2。核心思想是"让写盘速度追上写内存速度":脏页涨到 bg_thresh 就派后台线程回写;回写还追不上、脏页继续涨到 15%/20%,就直接把写进程摁住(节流),等脏页降下来再放行------保证脏页不会把可回收内存吞光。

4.2 flush 线程:wb_workfn 真正落盘

后台刷盘由 writeback 工作队列的 worker 执行------每个 backing device 一个 bdi_writeback 上下文,worker 显示名就是 ps 里看到的 flush-<设备名> 线程,入口 wb_workfn(fs/fs-writeback.c:2245):

c 复制代码
// fs/fs-writeback.c (v6.6, line 2245,节选)
void wb_workfn(struct work_struct *work)
{
	struct bdi_writeback *wb = container_of(..., struct bdi_writeback, dwork);
	do {
		pages_written = wb_do_writeback(wb);   // → wb_writeback(:2022)
	} while (!list_empty(&wb->work_list));     // 还有活就继续
	// ...
}

wb_writeback(fs-writeback.c:2022)扫出打 PAGECACHE_TAG_DIRTY 的 folio,调用 a_ops->writepages(具体文件系统,如 ext4)把它们写回磁盘,完成后清除 DIRTY tag 和脏位。这整个过程是异步 的------写进程早 write() 完事了,flush 线程在后台慢慢刷。


五、预读 readahead:提前把后续页读进来

顺序读文件时,如果每次都等"缺这一页才去读盘",磁盘 I/O 延迟会卡住读进程。预读就是"读第 N 页时,顺手把 N+1、N+2... 也读进来",用空间换延迟。两条触发路径:

  • 同步预读 page_cache_sync_ra (readahead.c:671):读 miss 时(§二 ②),把请求页及后续一批页读进来;
  • 异步预读 page_cache_async_ra (readahead.c:699):读到一个打了 PG_readahead 标记的页时,说明"预读的页快被消费完了",后台再预读下一批。

预读窗口记在 struct file_ra_state(fs.h:968)里------start(窗口起点)、size(本次预读多少)、async_size(异步预读的提前量)、ra_pages(最大预读页数):

c 复制代码
// include/linux/fs.h (v6.6, line 968)
struct file_ra_state {
	pgoff_t start;           // 预读窗口起点
	unsigned int size;       // 本次预读页数
	unsigned int async_size; // 异步预读提前量
	unsigned int ra_pages;   // 最大预读页数
	// ...
};

两条 *_ra 最终都进 ondemand_readahead → page_cache_ra_unbounded(readahead.c:205)→ read_pages(readahead.c:146),read_pages 调 a_ops->readahead(文件系统批量读)或逐页 a_ops->read_folio 提交 I/O。预读的页先加进 page cache 但标记 PG_readahead/!uptodate,读进程走到那一页时若还没读完,就 filemap_update_page 等它(§二 ③)。


六、与 mmap 缺页的关系:filemap_fault

上一篇 #3 讲文件页缺页 do_fault → __do_fault → vm_ops->fault。对普通文件,这个 fault 回调就是 filemap_fault (filemap.c:3255)------它做的事情和 read() 的读路径几乎一样:在 page cache 里找这一页,找不到就读盘填进去,然后映射进页表。所以 mmap 的文件映射,底层数据也是存在 page cache 里的:

  • read()/write() 通过 filemap_read/generic_perform_write 操作 page cache;
  • mmap 后的内存访问通过缺页 filemap_fault 操作同一个 page cache;
  • 两者看到的是一致的缓存,这正是 mmap 和 read/write 能共享同一份文件缓存的原因。

七、为什么这样设计(Why 层)

7.1 为什么读/写都要过 page cache,而不是直接读盘

因为磁盘慢、内存快 ,且文件访问有强时间局部性和重复性(同一文件反复读、反复写)。page cache 把"最近访问过的文件页"留在内存,命中就免掉磁盘 I/O,性能提升数量级。这也是为什么刚读过的文件再次读会快得多、为什么内存里总有大量"可回收的"文件页(见 #8 页回收)。

7.2 为什么写用"延迟写(write-back)"而不是"同步写(write-through)"

同步写每 write() 一次就落盘一次,磁盘 I/O 立刻成为瓶颈。延迟写让写进程只写内存、立刻返回,把"落盘"这个慢动作攒成一批、交给后台线程异步做------合并连续写、批量下盘 ,吞吐量大幅提升。代价是崩溃可能丢数据,所以内核提供 fsync/O_SYNC 给需要持久化保证的调用者显式强制落盘。"默认快(延迟写)+ 需要时慢(fsync)"是通用折中。

7.3 为什么要 balance_dirty_pages 限速写进程

如果写进程全速往 page cache 灌、回写线程又刷不过来,脏页会无限上涨,最终把可回收内存(NR_FILE_DIRTY)全部吞掉、触发 OOM。限速的本质是"让写内存的速度和写盘的速度匹配":脏页一超阈值就摁住写进程,逼迫整体吞吐收敛到磁盘带宽------这是"生产者(写进程)vs 消费者(回写线程)"的背压(backpressure)机制。

7.4 为什么要预读 readahead

顺序读是文件 I/O 的常态(日志、数据文件、模型权重)。如果只按需读,每次读盘都要等磁盘寻道/延迟,CPU 空转。预读用"读一页、顺手读一批"把未来的缺页 I/O 提前、合并成顺序大 I/O(磁盘顺序读远快于随机读),把延迟摊销掉。预读是"用多读一点内存的代价,换磁盘顺序 I/O"的投机优化。

7.5 为什么用 xarray 而不是简单数组

文件的页偏移是稀疏的------一个 1GB 的文件,用户可能只访问头尾两页。简单数组要为整个文件每个页都建条目,稀疏访问时浪费巨大;xarray(radix tree 的通用化)按"用到的偏移"建树,只占实际访问到的部分,和 #3 多级页表"稀疏地址空间只占实际用到的部分"是同一个思想。


八、与已有博客的交叉引用

已有博客 本篇覆盖的关系
内存 #3 页表缺页 文件页缺页 do_fault → __do_fault → vm_ops->fault = filemap_fault(:3255),本质是"从 page cache 读"。本篇 §六 讲清了这条线;page cache 是文件缺页的"数据源"
内存 #2 buddy page cache 的 folio 从哪来?filemap_alloc_folio 底层就是 buddy 分配物理页(__alloc_pages)。page cache 是 buddy 的又一个大消费者
内存 #4 大页 folio 可以是 order=9 的大页,THP 大页能直接作为 page cache 的缓存单位(CONFIG_READ_ONLY_THP_FOR_FS,fs.h:476)
IOMMU 系列 page cache 管的是"CPU/文件"侧缓存;设备 DMA 读写文件(如 GPU 直读 SSD)绕不绕 page cache 是另一套(O_DIRECT / GUP,见后续 \[blog-memory-expansion] GUP 篇)

附:本篇关键源码索引

符号 位置
struct address_space(host/i_pages/a_ops/nrpages/writeback_index) include/linux/fs.h:470
PAGECACHE_TAG_DIRTY / PAGECACHE_TAG_WRITEBACK include/linux/fs.h:498 / :499
struct file_ra_state(start/size/async_size/ra_pages) include/linux/fs.h:968
struct folio(页缓存单位) include/linux/mm_types.h:293
generic_file_read_iter(读入口,O_DIRECT 分流) mm/filemap.c:2790
filemap_read(读主循环) mm/filemap.c:2617
filemap_get_pages(查缓存/预读/读盘三态) mm/filemap.c:2535
filemap_get_read_batch(xarray 批量查) mm/filemap.c:2329
filemap_create_folio(分配 + 读盘) mm/filemap.c:2478
filemap_update_page(补读 + 等 I/O) mm/filemap.c:2422
filemap_read_folio(→ a_ops->read_folio) mm/filemap.c:2365
filemap_fault(mmap 文件缺页) mm/filemap.c:3255
generic_perform_write(写主循环) mm/filemap.c:3932
generic_file_write_iter(写入口) mm/filemap.c:4081
balance_dirty_pages(限速核心) mm/page-writeback.c:1669
balance_dirty_pages_ratelimited mm/page-writeback.c:2062
dirty_background_ratio / vm_dirty_ratio(默认 10 / 20) mm/page-writeback.c:74 / :91
dirty_freerun_ceiling((thresh+bg_thresh)/2) mm/page-writeback.c:813
wb_workfn(flush 线程) fs/fs-writeback.c:2245
wb_writeback(回写落盘) fs/fs-writeback.c:2022
page_cache_sync_ra / page_cache_async_ra(同步/异步预读) mm/readahead.c:671 / :699
page_cache_ra_unbounded / read_pages(预读执行) mm/readahead.c:205 / :146
相关推荐
研发小能1 小时前
金融行业 DevOps 流水线解决方案:从环境准备到发布门禁的全链路提效
运维·金融·devops
海宇大数据1 小时前
零信任架构实战:基于海宇企业综合风险报告构建自动化供应链保理对公尽调网关
运维·人工智能·架构·自动化
星恒随风1 小时前
Linux进程信号(一):从Ctrl+C到kill、raise、alarm,深入理解信号的概念与产生原理
linux·c语言·笔记·学习·状态模式
LMT_Lanmantech1 小时前
LS-DYNA许可降本增效:不增购,也能让现有Token发挥更大价值
运维·软件工程·软件许可证·ladyna
ZeroNews内网穿透1 小时前
内网穿透公网暴露防护:边缘网关 IP 黑白名单运维实践
运维·网络协议·tcp/ip·内网穿透·api安全·企业网络安全·运维实践
honsor1 小时前
机房动环监控:以太网温湿度传感器选型与部署全解
运维·服务器·网络·物联网·网络协议·tcp/ip
idcardwang1 小时前
智嵌物联,串口-wifi网关的配置
运维·服务器·网络
一条泥憨鱼2 小时前
【从0开始学习计算机网络】| 从一次 ping 出发:ICMP、路由协议与网关边界
服务器·网络·ip
MinterFusion2 小时前
如何在开放麒麟(openKylin)下安装GTK(v0.1.0)
linux·操作系统·软件安装·gtk·明德融创·openkylin·linux图形软件开发