2012-07-06 85 views
5

我想知道在内存中缓存大文件的哪一部分。我使用的是fincore的一些代码,它的工作方式如下:文件是mmaped,然后fincore遍历地址空间并用mincore检查页面,但由于文件大小很长(几分钟) )。Linux:识别内存中的页面

有没有办法循环使用的RAM页面?它会更快,但这意味着我应该从某处获得已使用页面的列表......但是,我无法找到一个方便的系统调用来实现。

这里谈到的代码:

#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
/* } */ 

#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <sys/mman.h> 
#include <sys/sysinfo.h> 


void 
fincore(char *filename) { 
    int fd; 
    struct stat st; 

    struct sysinfo info; 
    if (sysinfo(& info)) { 
    perror("sysinfo"); 
    return; 
    } 

    void *pa = (char *)0; 
    char *vec = (char *)0; 
    size_t pageSize = getpagesize(); 
    register size_t pageIndex; 

    fd = open(filename, 0); 
    if (0 > fd) { 
     perror("open"); 
     return; 
    } 

    if (0 != fstat(fd, &st)) { 
     perror("fstat"); 
     close(fd); 
     return; 
    } 

    pa = mmap((void *)0, st.st_size, PROT_NONE, MAP_SHARED, fd, 0); 
    if (MAP_FAILED == pa) { 
     perror("mmap"); 
     close(fd); 
     return; 
    } 

    /* vec = calloc(1, 1+st.st_size/pageSize); */ 
    /* 2.2 sec for 8 TB */ 
    vec = calloc(1, (st.st_size+pageSize-1)/pageSize); 
    if ((void *)0 == vec) { 
     perror("calloc"); 
     close(fd); 
     return; 
    } 

    /* 48 sec for 8 TB */ 
    if (0 != mincore(pa, st.st_size, vec)) { 
     fprintf(stderr, "mincore(%p, %lu, %p): %s\n", 
       pa, (unsigned long)st.st_size, vec, strerror(errno)); 
     free(vec); 
     close(fd); 
     return; 
    } 

    /* handle the results */ 
    /* 2m45s for 8 TB */ 
    for (pageIndex = 0; pageIndex <= st.st_size/pageSize; pageIndex++) { 
     if (vec[pageIndex]&1) { 
     printf("%zd\n", pageIndex); 
     } 
    } 

    free(vec); 
    vec = (char *)0; 

    munmap(pa, st.st_size); 
    close(fd); 

    return; 
} 

int main(int argc, char *argv[]) { 
    fincore(argv[1]); 

    return 0; 
} 
+2

映射一个8 TB文件需要20亿4k页面。 “mincore”的48秒运行时间表示正在检查44.7 Mpages/sec。你认为这可以走多快?用printf()打印数百或数十亿行也不是世界上最快的事情。 – 2012-07-06 15:07:44

+0

我不希望让mmap/mincore比这更快;我想要的是减少循环的长度,可能通过扫描较少的页面... – wazoox 2012-07-06 15:42:29

+0

'printf'通常是非常慢的操作。将它替换为'activePages ++'之类的东西,看看处理循环需要多少时间。注意'vec'仍然是2 GiB,甚至调用'mincore'可能会改变缓存的内容,因为在分配给'vec'的虚拟地址空间内正在触摸物理内存。 – 2012-07-06 15:50:29

回答

0

由谁缓存?

考虑引导后文件位于磁盘上。没有任何部分是在记忆中。

现在打开文件并执行随机读取。

文件系统(例如内核)将被缓存。

C标准库将被缓存。

内核将缓存在内核模式内存中,即用户模式内存中的C标准库。

如果您可以发出查询,也可能是在查询之后立即发出 - 在它返回给您之前 - 从缓存中删除有问题的缓存数据。

+0

这是一个iscsi目标,所以它被内核缓存在磁盘缓存中。我想随时监控缓存的使用情况和进化。 – wazoox 2012-07-06 15:44:11

1

对于悲观的情况,当所有页面或几乎所有页面都在RAM中时,比位图高出许多 - 每个条目至少64比1比特。如果有这样一个API,当查询它关于你的20亿页时,你将不得不准备在答复中获得16 GB的数据。此外,处理可变长度结构(如列表)比处理固定长度数组更复杂,因此库函数(尤其是低级系统函数)往往避免了麻烦。我也不是很确定实现(在这种情况下操作系统如何与TLB和Co进行交互),但很可能填充位图的操作(甚至大小差异)可以比创建由于从中提取信息的操作系统和硬件级结构而产生的列表。

如果你不关心非常精细的粒度,你可以看看/proc/<PID>/smaps。对于每个映射区域,它显示一些统计信息,包括装入内存的多少(Rss字段)。如果出于调试的目的,用一个单独的mmap()调用映射某个文件的某些区域(除用于执行实际任务的主映射外),您可能会在smaps中获得单独的条目,从而查看这些区域的单独统计信息。你几乎肯定无法在不杀死你的系统的情况下进行数十亿次映射,但是如果文件结构合理,也许只有几十个精心挑选的区域有单独的统计数据可以帮助你找到你正在寻找的答案。

+0

我确实尝试了smaps。但由于某些原因,它不会报告其他进程映射的内存页面,所以在这种情况下它不会帮助我。我真正想要的是访问MMU虚拟到物理地址表:) – wazoox 2012-07-06 22:03:41