在x86内核中获取TSC速率 [英] Getting TSC rate in x86 kernel

查看:705
本文介绍了在x86内核中获取TSC速率的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个运行在Atom上的嵌入式Linux系统,这是一个足够新的CPU,具有不变的TSC(时间戳记计数器),内核在启动时测量其频率.我在自己的代码中使用TSC来节省时间(避免内核调用),而我的启动代码会测量TSC速率,但我宁愿仅使用内核的度量值.有什么办法可以从内核中检索到它吗?它不在/proc/cpuinfo中.

I have an embedded Linux system running on an Atom, which is a new enough CPU to have an invariant TSC (time stamp counter), whose frequency the kernel measures on startup. I use the TSC in my own code to keep time (avoiding kernel calls), and my startup code measures the TSC rate, but I'd rather just use the kernel's measurement. Is there any way to retrieve this from the kernel? It's not in /proc/cpuinfo anywhere.

推荐答案

BPFtrace

以root身份,您可以使用bpftrace检索内核的TSC速率:

BPFtrace

As root, you can retrieve the kernel's TSC rate with bpftrace:

# bpftrace -e 'BEGIN { printf("%u\n", *kaddr("tsc_khz")); exit(); }' | tail -n

(在CentOS 7和Fedora 29上进行了测试)

(tested it on CentOS 7 and Fedora 29)

这是在或者,您也可以以root身份从/proc/kcore读取它,例如:

Alternatively, also as root, you can also read it from /proc/kcore, e.g.:

# 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上进行了测试)

(tested it on CentOS 7 and Fedora 29)

如果系统没有bpftrace或gdb可用,但是有SystemTap,您可以像这样(以root用户身份)获取它:

If the system doesn't have bpftrace nor gdb available but SystemTap you can get it like this (as 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

当然,您也可以编写一个小的内核模块,该模块通过/sys伪文件系统提供对tsc_khz的访问.更好的是,有人已经这样做了,并且 tsc_freq_khz模块在GitHub上可用.因此,以下方法应该起作用:

Of course, you can also write a small kernel module that provides access to tsc_khz via the /sys pseudo file system. Even better, somebody already did that and a tsc_freq_khz module is available on GitHub. With that the following should work:

# modprobe tsc_freq_khz
$ cat /sys/devices/system/cpu/cpu0/tsc_freq_khz

(在Fedora 29上进行测试,读取sysfs文件不需要root)

(tested on Fedora 29, reading the sysfs file doesn't require root)

如果以上都不是选项,则可以从内核日志中解析TSC速率.但这很快就会变得很丑陋,因为您会在不同的硬件和内核上看到不同类型的消息,例如在Fedora 29 i7系统上:

In case nothing of the above is an option you can parse the TSC rate from the kernel logs. But this gets ugly fast because you see different kinds of messages on different hardware and kernels, e.g. on a Fedora 29 i7 system:

$ 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上:

But on a Fedora 29 Intel Atom just:

kernel: tsc: Detected 2200.000 MHz processor

在CentOS 7 i5系统上:

While on a CentOS 7 i5 system:

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-也位于

Perf Values

The Linux Kernel doesn't provide an API to read the TSC rate, yet. But it does provide one for getting the mult and shift values that can be used to convert TSC counts to nanoseconds. Those values are derived from tsc_khz - also in arch/x86/kernel/tsc.c - where tsc_khz is initialized and calibrated. And they are shared with userspace.

使用perf API并访问共享页面的示例程序:

Example program that uses the perf API and accesses the shared page:

#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用户.

Tested in on Fedora 29 and it works also for non-root users.

这些值可用于将TSC计数转换为十亿分之一秒,例如:

Those values can be used to convert a TSC count to nanoseconds with a function like this one:

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;
}

这篇关于在x86内核中获取TSC速率的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆