c - 使用 mmap 和/proc/mtrr 访问不可缓存区域

标签 c linux memory mmap perf

我正在尝试使用 mmap 和/proc/mtrr 来深入分析物理内存。这是我正在尝试做的事情的基本想法以及我到目前为止所做的事情的总结。我使用的是 Ubuntu 内核版本 3.5.0-54-generic。

我基本上是映射到一个特定的物理地址(使用来自/proc/iomem 的提示)并测量对该物理地址范围的访问延迟。这是我到目前为止所做的:

  1. 在/proc/mtrr 中创建了一个条目,使我将映射的物理地址范围不可缓存。
  2. 使用/dev/mem 映射到特定地址。为了从/dev/mem 读取超过 1 MB 的数据,我不得不放宽安全限制。

虽然我能够毫无问题地执行该程序,但我对不可缓存部分是否真的有效存在一些疑问。这是我正在使用的代码片段。请注意,我使用了先前研究论文中的伪代码来创建此代码。

  int main(int argc, char *argv[]) {  
    int fd; // file descriptor to open /dev/mem
    struct timespec time1, time2;
    fd = open("/dev/mem", O_RDWR|O_SYNC);
    if (fd == -1) {
        printf("\n Error opening /dev/mem");
        return 0;
    }
    struct timespec t1, t2;
    char *addr = (char*)mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0x20000);
    if (addr == MAP_FAILED) {
      printf("\n mmap() failed");
    } 
    // Begin accessing 
    char *addr1 = addr;
    char *addr2 = addr1 + 64; // add cache line

    unsigned int i = 0;
    unsigned int j = 0;
    // Begin accessing uncached region
    while(j < 8192){
        i = 0;
        while(i < 500) {
            *addr1 = *addr2 + i;
            *addr2 = *addr1 + i;
            i = i+1;
        }
        j = j + 64;
        addr2 = addr1 + j;
    }
    if (munmap(addr, 8192) == -1) {
         printf("\n Unmapping failed");
         return 0;
    }
    printf("\n Success......");
    return 0;
}

我使用基于输出/proc/iomem 的偏移量 0x20000,如下所示(仅显示相关信息):

00000000-0000ffff : reserved
**00010000-0009e3ff : System RAM**
0009e400-0009ffff : RAM buffer
000a0000-000bffff : PCI Bus 0000:00
000a0000-000b0000 : PCI Bus 0000:20
000c0000-000effff : PCI Bus 0000:00

以下是/proc/mtrr 中的条目:

reg00: base=0x0d3f00000 ( 3391MB), size=    1MB, count=1: uncachable
reg01: base=0x0d4000000 ( 3392MB), size=   64MB, count=1: uncachable
reg02: base=0x0d8000000 ( 3456MB), size=  128MB, count=1: uncachable
reg03: base=0x0e0000000 ( 3584MB), size=  512MB, count=1: uncachable
reg04: base=0x000020000 (    0MB), size=    8KB, count=1: uncachable

如您所见,最后一个条目使感兴趣的地址区域不可缓存。

虽然我运行代码没有问题,但我有以下问题:

  1. 选择表示为系统 RAM 的特定物理地址范围进行读/写是否正确?我的理解是那个地址范围是用来存放数据和代码的。除了使用 hexdump 读取/dev/mem 之外,我还观察到地址区域未初始化(设置为 0)。
  2. 为了检查对未缓存区域的访问是否实际上未缓存,我执行了 perf stat -e cache-misses:u 来测量发生了多少缓存未命中。我得到一个在 128,200 范围内的数字。对我来说,这证实了地址没有被缓存并且像在循环中一样进入 RAM,我正在做 (8192/64)*500*2 = 128,000 次访问。我用另一段类似的代码做了同样的 perf 练习,将 mmap 替换为相同长度的字符数组的动态内存分配。在这种情况下,perf stat 报告的缓存未命中率要低得多。
  3. 为了重新检查我是否确实绕过缓存并进入内存,我将偏移量更改为系统 RAM 范围内的另一个值(例如 0x80000)并运行 perf 命令以测量发生了多少缓存未命中。这里的混淆是它报告的缓存未命中次数与前一种情况几乎相同(大约 128,200)。我期望的东西要少得多,因为我没有使该物理地址区域不可缓存。

任何关于此的建议/反馈都有助于理解这一观察结果。

谢谢

最佳答案

我想我明白了。手册页中的 MAP_PRIVATE 表示更改未反射(reflect)到基础文件中。将其更改为 MAP_SHARED,并启用/proc/mtrr 中的条目后,缓存未命中数和命中数的变化会发生显着变化。

关于c - 使用 mmap 和/proc/mtrr 访问不可缓存区域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26825420/

相关文章:

c - rand() 数字沉迷于 C 函数

c - 实现我自己的 ps 命令

linux - 关于linux内核jiffies

linux - 在具有 x64 操作系统 (redhat 5.6) 的 x64 CPU (Xeon 7650) 上运行的 X64 应用程序的 2GB 障碍 - 为什么 + 要检查的内容

c++ - 离开循环时 vector 数据丢失

c++ - Arduino:如果比较参数

c - 返回指针的函数中的段错误

c - 为什么数组在C中的字符串后面有路径?

python - Selenium:单击的元素,使用 xpath 识别,在 Windows 上工作正常,但在 Linux 上失败

java - 如何解决 PermGen 内存问题