为什么 strace 根据 environment/kernel 忽略某些系统调用(随机)?

Why does strace ignore some syscalls (randomly) depending on environment/kernel?

如果我编译以下程序:

$ cat main.cpp && g++ main.cpp
#include <time.h>
int main() {
    struct timespec ts;
    return clock_gettime(CLOCK_MONOTONIC, &ts);
}

然后 运行 它在“标准”Kubuntu 中的 strace 下,我得到这个:

strace -tt --trace=clock_gettime ./a.out
17:58:40.395200 +++ exited with 0 +++

如您所见,没有 clock_gettime(完整的strace输出是here)。

另一方面,如果我在 qemu 下自定义构建的 linux 内核中 运行 相同的应用程序,我会得到以下输出:

strace -tt --trace=clock_gettime ./a.out
18:00:53.082115 clock_gettime(CLOCK_MONOTONIC, {tv_sec=101481, tv_nsec=107976517}) = 0
18:00:53.082331 +++ exited with 0 +++

哪个更值得期待——clock_gettime.

所以,我的问题是

第一个问题的答案

来自vdso man

strace(1)、seccomp(2) 和 vDSO

当使用 strace(1) 跟踪系统调用时,vDSO 导出的符号(系统调用)不会出现在跟踪输出中。这些系统调用同样对 seccomp(2) 过滤器不可见。

Answer转第二题:

在vDSO中,clock_gettimeofday及相关函数依赖于特定的时钟模式;参见 __arch_get_hw_counter。

如果时钟模式是VCLOCK_TSC,则在没有系统调用的情况下读取时间,使用RDTSC;如果它是 VCLOCK_PVCLOCK 或 VCLOCK_HVCLOCK,则从特定页面读取它以从管理程序检索信息。 HPET 未声明时钟模式,因此它以默认 VCLOCK_NONE 结束,vDSO 发出系统调用以检索时间

确实:

在默认内核中(来自 Kubuntu):

$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
tsc hpet acpi_pm
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc

自定义内核:

$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
hpet

More info 关于各种时钟源。特别是:

The documentation of Red Hat MRG version 2 states that TSC is the preferred clock source due to its much lower overhead, but it uses HPET as a fallback. A benchmark in that environment for 10 million event counts found that TSC took about 0.6 seconds, HPET took slightly over 12 seconds, and ACPI Power Management Timer took around 24 seconds.

这可能是因为 clock_gettime() 是优化系统调用的一部分。看这篇answer.
中描述的vdso机制 考虑到 clock_gettime(),在某些体系结构上(例如 ARM v7 32 位上的 Linux),VDSO 实现仅支持可用时钟标识符的子集。对于其他人,有一个回退到实际的系统调用。这是 ARM v7 Linux 内核中 clock_gettime() 的 VDSO 实现的源代码(源代码树中的文件 arch/arm/vdso/vgettimeofday.c ):

notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts)
{
      struct vdso_data *vdata;
      int ret = -1;

      vdata = __get_datapage();

      switch (clkid) {
      case CLOCK_REALTIME_COARSE:
            ret = do_realtime_coarse(ts, vdata);
            break;
      case CLOCK_MONOTONIC_COARSE:
            ret = do_monotonic_coarse(ts, vdata);
            break;
      case CLOCK_REALTIME:
            ret = do_realtime(ts, vdata);
            break;
      case CLOCK_MONOTONIC:
            ret = do_monotonic(ts, vdata);
            break;
      default:
            break;
      }

      if (ret)
            ret = clock_gettime_fallback(clkid, ts);

      return ret;
}

上面的源代码显示了一个 switch/case 支持的时钟标识符,在默认情况下,有一个回退到实际的系统调用。
在这样的架构上,监视像 systemd 这样的软件,它使用 clock_gettime()CLOCK_MONOTONICCLOCK_BOOTTIMEstrace 只显示带有后一个标识符的调用,因为它不是 VDSO 模式下支持的案例的一部分。

比照。这个link供参考