在《Linux多线程服务端编程》一书5.1节中提到过,在x86-64的Linux上,gettimeofday不是系统调用,不会陷入内核。其实这种说法有点小问题,因为gettimeofday确实是个系统调用,但是linux的vdso(virtual dynamic shared object)机制帮我们做到了在调用这些系统调用时不陷入内核,从而提高了性能。

vdso机制说白了就是在用户空间帮我们实现了一些特定的系统调用,用户进程启动时这些代码会被自动映射到进程地址空间的用户空间中。这样的话,当我们利用vdso调用到这些系统调用时,就不会陷入内核了。如何调用到这些代码呢?直接调用这些系统调用对应的glibc包装函数就可以,因为这些glibc包装函数默认会使用vdso。如果你执意通过syscall函数/syscall指令/int 0x80来调用这些系统调用,vdso是无法生效的,还是会陷入内核。

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

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来判断。

(为什么我会写这篇文章呢?因为我之前一直顾虑clock_gettime是个系统调用,虽然精度比gettimeofday高,但性能差不敢多用。为什么我会觉得clock_gettime性能差呢?因为之前用strace排查公司代码性能问题的时候总是能看到一大堆一大堆的clock_gettime,总耗时加起来占所有系统调用总耗时比例不低。那看了今天的文章,clock_gettime不是在vdso中有实现么,为什么性能差?那是因为既然在strace中有输出,就说明这些clock_gettime还是陷入了内核。为什么陷入了内核中呢?猜测这些clock_gettime是英伟达显卡驱动通过syscall函数调用或者直接使用汇编调用的。)

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐