关于 gettimeofday 是否真的不涉及到系统调用进行记录

在《Linux多线程服务端编程》一书 5.1 节中提到过,在 x86-64 的 Linux 上,gettimeofday 不是系统调用,不会陷入内核。其实我是对这句话存有疑问的。众所周知想要获得准确的当前时间,就必须要通过内核的全局的计时变量(ticks 或者 jiffies),但是用 strace 查看调用了 gettimeofday 的进程,发现的确没有涉及到系统调用,没有陷入内核。这就奇了怪了,故上网查找,得到以下信息:

首先, gettimeofday 的确是一个系统调用,因为它所需求的数据是内核所持有的,但它没有走传统的 trap 这条路进入内核,而是利用了 linux 的 vdso(virtual dynamic shared object)机制帮我们做到了在调用这个系统调用时不陷入内核,从而提高了性能。

这个实现方法其实也是很直觉的(intuitive),在程序装载阶段,装载程序观察到程序有需要 vdso 的相关系统调用,就为其做一下内存映射(将对应的系统调用处理函数所需的指令和数据映射到用户空间中去)。那么用户在调用这些函数时自然就不需要陷入内核啦~~

如何调用到这些代码呢?直接调用这些系统调用对应的 glibc 包装函数就可以,因为这些 glibc 包装函数默认会使用 vdso。如果你执意通过 syscall 函数/ syscall 指令/int 0x80 来调用这些系统调用,vdso 是无法生效的,还是会陷入内核。

当然 vdso 也不保证一定不会陷入内核,有些情况下是会 fallback 的,以 clock_gettime 为例,下面是 linux 4.16 版本中该系统调用在 vdso 中的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
{

switch (clock) {

case CLOCK_REALTIME:
if (do_realtime(ts) == VCLOCK_NONE)
goto fallback;
break;
case CLOCK_MONOTONIC:
if (do_monotonic(ts) == VCLOCK_NONE)
goto fallback;
break;
case CLOCK_REALTIME_COARSE:
do_realtime_coarse(ts);
break;
case CLOCK_MONOTONIC_COARSE:
do_monotonic_coarse(ts);
break;
default:
goto fallback;
}

return 0;
fallback:
return vdso_fallback_gettime(clock, ts);
}

其中do_realtimedo_monotonic如果返回值为VCLOCK_NONE的话,就会调用vdso_fallback_gettime,而这个函数是会陷入内核的。另外,clock_gettimeclock参数可不止上面代码中switch里面的4个case,如果我们传入的是CLOCK_BOOTTIME/CLOCK_PROCESS_CPUTIME_ID/CLOCK_THREAD_CPUTIME_ID的话,就会走到default分支,还是会调用vdso_fallback_gettime陷入内核。

综上所诉,gettimeofdayclock_gettime实际上都是系统调用,但是调用得当的话,可以避免陷入内核,从而提高性能。是否陷入了内核,可以利用 strace 来判断。

参考

  1. gettimeofday和clock_gettime是不是系统调用?
  2. muduo