有没有办法检查处理器缓存最近是否被刷新过?

问题描述 投票:0回答:3

在 i386 Linux 上。如果可能的话,最好在 c/(c/posix std libs)/proc 中。如果没有,是否有任何程序集或第三方库可以做到这一点?

编辑:我正在尝试开发测试内核模块是否清除缓存行或整个处理器(使用 wbinvd())。程序以 root 身份运行,但如果可能的话,我更愿意留在用户空间中。

linux cpu flush hardware-interface cpu-cache
3个回答
12
投票

缓存一致性系统会尽最大努力向您隐藏此类内容。我认为您必须间接观察它,要么使用性能计数寄存器来检测缓存未命中,要么通过高分辨率计时器仔细测量读取内存位置的时间。

这个程序在我的 x86_64 机器上运行,以演示

clflush
的效果。它计算使用
rdtsc
读取全局变量所需的时间。作为直接与 CPU 时钟相关的单个指令,直接使用
rdtsc
非常适合此目的。

这是输出:

花了 81 个刻度
花了 81 个刻度
刷新:花费了 387 个刻度
花了 72 个刻度

您会看到 3 个试验:第一个确保

i
位于缓存中(确实如此,因为它刚刚作为 BSS 的一部分被归零),第二个是读取应该位于缓存中的
i
。然后
clflush
i
踢出缓存(及其邻居),并显示重新读取它需要更长的时间。最终读取验证它是否已返回缓存。结果的重现性非常好,并且差异足够大,足以轻松查看缓存未命中情况。如果您愿意校准
rdtsc()
的开销,您可以使差异更加明显。

如果您无法读取要测试的内存地址(尽管即使

mmap
/dev/mem
也可以用于这些目的),如果您知道缓存行大小和缓存的关联性,您也许能够推断出您想要的内容。然后,您可以使用可访问的内存位置来探测您感兴趣的集合中的活动。

源代码:

(如果使用较新的 gcc,请使用

static inline
此处引用的其他方法)

#include <stdio.h>
#include <stdint.h>

inline void
clflush(volatile void *p)
{
    asm volatile ("clflush (%0)" :: "r"(p));
}

inline uint64_t
rdtsc()
{
    unsigned long a, d;
    asm volatile ("rdtsc" : "=a" (a), "=d" (d));
    return a | ((uint64_t)d << 32);
}

volatile int i;

inline void
test()
{
    uint64_t start, end;
    volatile int j;

    start = rdtsc();
    j = i;
    end = rdtsc();
    printf("took %lu ticks\n", end - start);
}

int
main(int ac, char **av)
{
    test();
    test();
    printf("flush: ");
    clflush(&i);
    test();
    test();
    return 0;
}

3
投票

我不知道有什么通用命令可以获取缓存状态,但有一些方法:

  1. 我想这是最简单的:如果你得到了内核模块,只需将其反汇编并查找缓存失效/刷新命令(atm。我只想到了 3 个:WBINDVD、CLFLUSH、INVD)。
  2. 你刚才说它是针对 i386 的,但我猜你的意思不是 80386。问题是有很多不同的扩展和功能。例如。最新的英特尔系列包含一些针对缓存系统的性能/分析寄存器,您可以使用它们来评估缓存未命中/命中/传输次数等。
  3. 与2类似,很大程度上取决于您所使用的系统。但是,当您具有多处理器配置时,您可以观看第一个缓存一致性协议 (MESI) 与第二个缓存一致性协议。

您提到了 WBINVD - 据我所知,它将始终刷新完成,即所有缓存行


0
投票

这可能不是您具体问题的答案,但您是否尝试过使用缓存分析器(例如 Cachegrind)?它只能用于分析用户空间代码,但您仍然可以使用它,例如如果函数的代码不依赖于任何特定于内核的接口,请将其移至用户空间。

它实际上可能比尝试向处理者询问可能存在或可能不存在的信息更有效,并且这些信息可能会受到你仅仅询问它的影响 - 是的,海森堡远远超前于他的时代:-)

© www.soinside.com 2019 - 2024. All rights reserved.