https://zhuanlan.zhihu.com/p/517122285
文件系统接口写脏的page cache, 因为主动调用了syscall, os知道该dirty page并在inode上进行标记, 之后合适的时间会进行writeback, 那么对于mmap后直接对地址进行写的数据, os是否一样能主动writeback, 而不需要应用主动触发?
使用MAP_SHARED映射一个文件, 并对返回地址直接赋值.
fd = open("4k", O_RDWR);
addr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, fd, 0);
printf("addr: %p\n", addr);
while (1) {
*addr = 'a';
c = getchar();
}
执行如下, 可以通过敲键盘重新dirty page
$ ./mem
addr: 0x7f985beff000
在敲完键盘后, 马上执行
$ sudo ./page-types -f /mnt/4k
flags page-count MB symbolic-flags long-symbolic-flags
0x0000000000000838 1 0 ___UDl_____M_______________________________ uptodate,dirty,lru,mmap
total 1 0
可以看到页面是dirty的, 过一会会写回, 配置不同时间会不一样
$ sudo ./page-types -f /mnt/4k
flags page-count MB symbolic-flags long-symbolic-flags
0x0000000000000828 1 0 ___U_l_____M_______________________________ uptodate,lru,mmap
total 1 0
已经没有dirty标志了, 同样页可以通过进程的虚拟地址来看
$ sudo ./page-types -p `pgrep mem` -a $((0x7f985beff000/4096))
跟一下进程在内核的执行情况
$ sudo trace-cmd record -p function_graph -P `pgrep mem`
有如下调用链
do_page_fault()
handle_mm_fault()
do_wp_page()
可知触发了写保护, 应该是某处对该mmap的page进行了写保护, 简单起见, 直接使用了address做判断, 这并不是充分的条件, 对于调试是够了
#!/usr/bin/bpftrace
kprobe:page_mkclean_one /arg2 == 0x7f985beff000/ {
@[kstack] = count();
}
interval:s:1 {
print(@); clear(@);
}
拿到这样的栈
@[
page_mkclean_one+1
rmap_walk+72
page_mkclean+161
clear_page_dirty_for_io+161
mpage_submit_page+36
mpage_process_page_bufs+255
mpage_prepare_extent_to_map+494
ext4_writepages+987
do_writepages+67
__writeback_single_inode+68
writeback_sb_inodes+561
__writeback_inodes_wb+86
wb_writeback+605
wb_workfn+918
process_one_work+475
worker_thread+77
kthread+260
ret_from_fork+34
]: 1
page_mkclean_one会执行如下操作
entry = pte_wrprotect(entry);
set_pte_at(vma->vm_mm, address, pte, entry);