我正在使用read()
中的C
阅读不同大小的文件(1KB - 1GB)。但每次我使用page-faults
检查perf-stat
时,它总是给我相同(几乎)的值。
我的计算机 (虚拟机上的fedora 18,RAM - 1GB,磁盘空间 - 20 GB)
uname -a
Linux localhost.localdomain 3.10.13-101.fc18.x86_64 #1 SMP Fri Sep 27 20:22:12 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
mount | grep "^/dev"
/dev/mapper/fedora-root on / type ext4 (rw,relatime,seclabel,data=ordered)
/dev/sda1 on /boot type ext4 (rw,relatime,seclabel,data=ordered)
我的代码:
10 #define BLOCK_SIZE 1024
. . .
19 char text[BLOCK_SIZE];
21 int total_bytes_read=0;
. . .
81 while((bytes_read=read(d_ifp,text,BLOCK_SIZE))>0)
82 {
83 write(d_ofp, text, bytes_read); // writing to /dev/null
84 total_bytes_read+=bytes_read;
85 sum+=(int)text[0]; // doing this just to make sure there's
// no lazy page loading by read()
// I don't care what is in `text[0]`
86 }
87 printf("total bytes read=%d\n", total_bytes_read);
88 if(sum>0)
89 printf("\n");
Perf-stat输出: (显示文件大小,读取文件的时间和页面错误数量)
[read]: f_size: 1K B, Time: 0.000313 seconds, Page-faults: 150, Total bytes read: 980
[read]: f_size: 10K B, Time: 0.000434 seconds, Page-faults: 151, Total bytes read: 11172
[read]: f_size: 100K B, Time: 0.000442 seconds, Page-faults: 150, Total bytes read: 103992
[read]: f_size: 1M B, Time: 0.00191 seconds, Page-faults: 151, Total bytes read: 1040256
[read]: f_size: 10M B, Time: 0.050214 seconds, Page-faults: 151, Total bytes read: 10402840
[read]: f_size: 100M B, Time: 0.2382 seconds, Page-faults: 150, Total bytes read: 104028372
[read]: f_size: 1G B, Time: 5.7085 seconds, Page-faults: 148, Total bytes read: 1144312092
问题:
1.大小为1KB&的文件read()
的页面错误如何? 1GB是一样的吗?由于我也在读数据(代码行#84),我确保数据实际读取。
2.我能想到它没有遇到很多页面错误的唯一原因是因为数据已经存在于主存储器中。如果是这种情况,我该如何刷新它,这样当我运行我的代码时它实际上显示了 true 页面错误?否则,我永远无法衡量read()
的 true 性能。
EDIT1:
echo 3 > /proc/sys/vm/drop_caches
没有帮助,输出仍然保持不变。
EDIT2:
对于mmap
,perf-stat
的输出为:
[mmap]: f_size: 1K B, Time: 0.000103 seconds, Page-faults: 14
[mmap]: f_size: 10K B, Time: 0.001143 seconds, Page-faults: 151
[mmap]: f_size: 100K B, Time: 0.002367 seconds, Page-faults: 174
[mmap]: f_size: 1M B, Time: 0.007634 seconds, Page-faults: 401
[mmap]: f_size: 10M B, Time: 0.06812 seconds, Page-faults: 2,688
[mmap]: f_size: 100M B, Time: 0.60386 seconds, Page-faults: 25,545
[mmap]: f_size: 1G B, Time: 4.9869 seconds, Page-faults: 279,519
答案 0 :(得分:5)
我认为你不明白究竟是什么页面错误。 pagefault,according to Wikipedia,是一种“陷阱”(异常),一种中断,当程序试图访问某些东西时由CPU本身生成,该内存未加载到物理内存中(但通常已经在虚拟内存中注册)其页面标记为“不存在”P: Present bit = 0)。
Pagefault很糟糕,因为它迫使CPU停止执行用户程序并切换到内核。并且内核模式中的页面故障并不常见,因为内核可以在访问之前检查页面存在。如果内核函数想要将内容写入新页面(在您的情况下是read
系统调用),它将通过显式调用页面分配器来分配页面,而不是通过尝试访问它并导致页面错误。使用显式内存管理执行的中断更少,代码更少。
---读案例---
您的阅读材料由sys_read
472 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
479 ret = vfs_read(f.file, buf, count, &pos);
vvv
353 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
368 ret = file->f_op->read(file, buf, count, pos);
vvv
处理。这是调用链(可能不精确):
626 const struct file_operations ext4_file_operations = {
628 .read = do_sync_read,
... do_sync_read -> generic_file_aio_read -> do_generic_file_read
1100 static void do_generic_file_read(struct file *filp, loff_t *ppos,
1119 for (;;) {
1120 struct page *page;
1127 page = find_get_page(mapping, index);
1128 if (!page) {
1134 goto no_cached_page;
// osgx - case when pagecache is empty ^^vv
1287 no_cached_page:
1288 /*
1289 * Ok, it wasn't cached, so we need to create a new
1290 * page..
1291 */
1292 page = page_cache_alloc_cold(mapping);
233 static inline struct page *page_cache_alloc_cold(struct address_space *x)
235 return __page_cache_alloc(mapping_gfp_mask(x)|__GFP_COLD);
vvv
222 static inline struct page *__page_cache_alloc(gfp_t gfp)
224 return alloc_pages(gfp, 0);
read()
因此,我可以通过直接调用跟踪alloc_pages
系统调用在页面分配(read()
)中的结束。分配页面后,read()
系统调用会将数据从HDD传输到新页面,然后返回给用户(考虑到文件未在页面缓存中缓存的情况)。如果数据已经在页面缓存中,do_generic_file_read
(read()
)将通过创建其他映射来重用页面缓存中的现有页面,而无需实际读取HDD。
mmap()
返回后,所有数据都在内存中,对它的读取权限不会产生页面错误。
--- mmap case ---
如果您重写测试以执行文件的text[offset]
,然后访问(perf stat
)文件的非当前页面(它不在页面缓存中),则会发生真正的页面错误
所有页面故障计数器(/proc/$pid/stat
和1224 dotraplinkage void __kprobes
1225 do_page_fault(struct pt_regs *regs, unsigned long error_code)
1230 __do_page_fault(regs, error_code);
vvv
1001 /*
1002 * This routine handles page faults. It determines the address,
1003 * and the problem, and then passes it off to one of the appropriate
1004 * routines.
1005 */
1007 __do_page_fault(struct pt_regs *regs, unsigned long error_code)
/// HERE is the perf stat pagefault event generator VVV
1101 perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
)仅在CPU生成真正的页面故障陷阱时更新。这是页面错误include/linux/pagemap.h的x86处理程序,它将起作用
vma->vm_ops->fault(vma, &vmf);
稍后某个页面故障处理程序将调用arch/x86/mm/fault.c - > handle_mm_fault
- > handle_pte_fault
以fault
结尾。
此mmap
虚拟函数已在__alloc_page
中注册,我认为它是__do_fault
。如果是空的pagecache,这个函数将进行实际页面分配(echo 3 > /proc/sys/vm/drop_caches
)和磁盘读取(这将被视为“主要”页面错误,因为它需要外部I / O)或者将从页面缓存重新映射页面(如果数据已被预取或已经在页面缓存中,被视为“次要”页面错误,因为它是在没有外部I / O且通常更快的情况下完成的。
PS:在虚拟平台上进行实验可能会改变一些事情;例如,即使在客户Fedora中清除磁盘缓存(pagecache){{1}}之后,虚拟硬盘驱动器中的数据仍然可以被主机操作系统缓存。