系统调用开销
Posted
技术标签:
【中文标题】系统调用开销【英文标题】:System calls overhead 【发布时间】:2014-06-29 05:28:05 【问题描述】:我刚开始研究系统调用。 我想知道进行系统调用时导致开销的原因。
例如, 如果我们考虑 getpid(),当对 getpid() 进行系统调用时,我的猜测是,如果控件当前位于子进程中,则必须进行上下文切换才能进入父进程以获取 pid。这会增加开销吗?
还有当getpid()被调用时,会有一些元数据跨越用户空间边界进出内核,那么用户空间和内核之间不断的切换会不会也造成一些开销?
【问题讨论】:
顺便说一句,在内核中启用 Spectre 缓解的现代 x86 上,明显高于以前,至少对于简单/廉价的系统调用,其中刷新分支预测器占总数的很大一部分时间。 How do SYSCALL/SYSRET instructions perform across x86 CPUs? - 系统调用后的执行需要一段时间才能在内核代码污染缓存后重新启动,并且乱序 exec 可以看到足够远的距离以找到并行性。 【参考方案1】:例如,如果我们考虑 getpid(),当对 getpid() 进行系统调用时,我的猜测是,如果控件当前位于子进程中,则必须进行上下文切换才能进入父进程以获取 pid。
这里不需要切换到子进程的上下文——内核应该拥有对自己可用的所有必要数据。在大多数情况下,内核只会在调度程序中或从系统调用返回时将上下文切换到用户空间进程。
另外,当调用 getpid() 时,会有一些元数据跨用户空间边界传输并进入和退出内核。那么用户空间和内核之间不断的切换会不会也造成一些开销呢?
是的,如果经常调用getpid()
,开销肯定会增加。对于像getpid()
和gettimeofday()
这样的简单“getter”系统调用,有一些方法可以避免这种开销;在 Linux 下曾经使用过的一种方法是将系统调用的(已知)结果存储在一个特殊的内存页面中。 (这种机制被称为vsyscall。)
【讨论】:
【参考方案2】:请原谅概括(而不是限定每个句子)。
对系统服务的调用(例如返回进程信息)具有用户模式外壳。此 shell 触发了一个异常,该异常通过调用内核模式系统服务的系统调度表进行路由。
切换到内核模式需要类似于进程上下文切换的东西。例如,它需要从用户堆栈更改为紧排堆栈(以及其他与系统相关的更改)。
调用进程提供用户模式返回缓冲区。出于安全考虑,系统系统服务会在写入响应数据之前检查以确保它是有效的用户模式缓冲区。
像 getpid 这样只返回当前进程信息的库函数可能不需要切换到内核模式。
【讨论】:
【参考方案3】:我在 x86-64 Linux(使用 -O3 编译)上做了一些更精确的基准测试:
ns relative(rounded) function
4.89 1 regular_function //just a value return
6.05 1 getpid //glibc caches this one (forks invalidate the cached value)
17.7 4 sysconf(_SC_PAGESIZE)
22.6 5 getauxval(AT_EUID)
25.4 5 sysconf(_SC_NPROCESSORS_ONLN)
27.1 6 getauxval(AT_UID)
54.1 11 gettimeofday
235 48 geteuid
261 53 getuid
264 54 getppid
314 64 sysconf(_SC_OPEN_MAX)
622 127 pread@0 // IO funcs benchmarked with 1 bytes quantities
638 130 read // through a 1 Gigabyte file
1690 346 write
1710 350 pwrite@0
最便宜的“系统调用”是通过辅助向量(~20-30ns)的那些。中间的调用(~250–310ns)应该最准确地反映平均开销,因为在内核中应该没有太多工作要做。
为了比较,malloc+free 对与小尺寸请求( 无系统调用)的成本约为 70-80ns(请参阅我在 @987654321 的回答@)。
https://softwareengineering.stackexchange.com/questions/311165/why-isnt-there-generic-batching-syscall-in-linux-bsd/350173 有一些关于如何最小化系统调用开销的有趣想法。
【讨论】:
getpid()
在 glibc 2.25 中是 no longer cached,在提交此答案后不久发布。 getpid(2)
的手册页提供了有关该主题的一些附加上下文,并指出从 2.3.4 到 2.24 版本都存在缓存。
regular_function 是系统调用还是普通函数?
@scottxiao 常规 = 普通。 geteuid
是真正的系统调用开始的地方。真正的系统调用不会比这快多少。以上是关于系统调用开销的主要内容,如果未能解决你的问题,请参考以下文章