gpt4 book ai didi

linux-kernel - 从 x86 内核获取 TSC 速率

转载 作者:行者123 更新时间:2023-12-04 04:17:26 29 4
gpt4 key购买 nike

我有一个在 Atom 上运行的嵌入式 Linux 系统,这是一个足够新的 CPU,可以有一个不变的 TSC(时间戳计数器),内核在启动时测量其频率。我在自己的代码中使用 TSC 来保持时间(避免内核调用),我的启动代码测量 TSC 速率,但我宁愿只使用内核的测量。有没有办法从内核中检索它?它不在/proc/cpuinfo 的任何地方。

最佳答案

BPFtrace
作为 root,您可以使用 bpftrace 检索内核的 TSC 速率:

# bpftrace -e 'BEGIN { printf("%u\n", *kaddr("tsc_khz")); exit(); }' | tail -n
(在 CentOS 7 和 Fedora 29 上测试过)
这是在 arch/x86/kernel/tsc.c 中定义、导出和维护/校准的值.
广发银行
或者,也可以作为 root,您也可以从 /proc/kcore 读取它,例如:
# gdb /dev/null /proc/kcore -ex 'x/uw 0x'$(grep '\<tsc_khz\>' /proc/kallsyms \
| cut -d' ' -f1) -batch 2>/dev/null | tail -n 1 | cut -f2
(在 CentOS 7 和 Fedora 29 上测试过)
SystemTap
如果系统没有可用的 bpftrace 或 gdb 但 SystemTap 你可以像这样(以 root 身份)获得它:
# cat tsc_khz.stp 
#!/usr/bin/stap -g

function get_tsc_khz() %{ /* pure */
THIS->__retvalue = tsc_khz;
%}
probe oneshot {
printf("%u\n", get_tsc_khz());
}
# ./tsc_khz.stp
当然,你也可以编写一个小的内核模块来提供对 tsc_khz 的访问。通过 /sys伪文件系统。更好的是,有人已经这样做了,还有一个 tsc_freq_khz module is available on GitHub .有了以下应该工作:
# modprobe tsc_freq_khz
$ cat /sys/devices/system/cpu/cpu0/tsc_freq_khz
(在 Fedora 29 上测试,读取 sysfs 文件不需要 root)
内核消息
如果以上都不是一个选项,您可以从内核日志中解析 TSC 速率。但这很快就会变得丑陋,因为您会在不同的硬件和内核上看到不同类型的消息,例如在 Fedora 29 i7 系统上:
$ journalctl --boot | grep 'kernel: tsc:' -i | cut -d' ' -f5-
kernel: tsc: Detected 2800.000 MHz processor
kernel: tsc: Detected 2808.000 MHz TSC
但是在 Fedora 29 Intel Atom 上:
kernel: tsc: Detected 2200.000 MHz processor
在 CentOS 7 i5 系统上:
kernel: tsc: Fast TSC calibration using PIT
kernel: tsc: Detected 1895.542 MHz processor
kernel: tsc: Refined TSC clocksource calibration: 1895.614 MHz
性能值
Linux 内核还没有提供读取 TSC 速率的 API。但它确实提供了一个用于获取 multshift可用于将 TSC 计数转换为纳秒的值。这些值来自 tsc_khz - 也在 arch/x86/kernel/tsc.c - 其中 tsc_khz被初始化和校准。它们与用户空间共享。
使用 perf API 并访问共享页面的示例程序:
#include <asm/unistd.h>
#include <inttypes.h>
#include <linux/perf_event.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
}
实际代码:
int main(int argc, char **argv)
{
struct perf_event_attr pe = {
.type = PERF_TYPE_HARDWARE,
.size = sizeof(struct perf_event_attr),
.config = PERF_COUNT_HW_INSTRUCTIONS,
.disabled = 1,
.exclude_kernel = 1,
.exclude_hv = 1
};
int fd = perf_event_open(&pe, 0, -1, -1, 0);
if (fd == -1) {
perror("perf_event_open failed");
return 1;
}
void *addr = mmap(NULL, 4*1024, PROT_READ, MAP_SHARED, fd, 0);
if (!addr) {
perror("mmap failed");
return 1;
}
struct perf_event_mmap_page *pc = addr;
if (pc->cap_user_time != 1) {
fprintf(stderr, "Perf system doesn't support user time\n");
return 1;
}
printf("%16s %5s\n", "mult", "shift");
printf("%16" PRIu32 " %5" PRIu16 "\n", pc->time_mult, pc->time_shift);
close(fd);
}
在 Fedora 29 上进行了测试,它也适用于非 root 用户。
这些值可用于使用如下函数将 TSC 计数转换为纳秒:
static uint64_t mul_u64_u32_shr(uint64_t cyc, uint32_t mult, uint32_t shift)
{
__uint128_t x = cyc;
x *= mult;
x >>= shift;
return x;
}
CPUID/MSR
获取 TSC 率的另一种方法是 follow DPDK's lead .
x86_64 上的 DPDK 基本上采用以下策略:
  • 通过 cpuid 内部函数读取“时间戳计数器和标称核心 Crystal 时钟信息叶”(不需要特殊权限),如果可用
  • 从 MSR 读取它(需要 rawio capability/dev/cpu/*/msr 的读取权限),如果可能的话
  • 通过其他方式在用户空间校准,否则

  • FWIW,一个快速测试表明 cpuid 叶似乎没有那么广泛可用,例如i7 Skylake 和 goldmont atom 没有。否则,从 DPDK 代码中可以看出,使用 MSR 需要一堆复杂的大小写区分。
    但是,如果程序已经使用 DPDK,那么获取 TSC 速率、获取 TSC 值或转换 TSC 值只是使用正确的 DPDK API 的问题。

    关于linux-kernel - 从 x86 内核获取 TSC 速率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35123379/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com