我刚开始研究系统调用。 我想知道在进行系统调用时导致开销的原因。
例如, 如果我们考虑getpid(),当对getpid()进行系统调用时,我的猜测是如果控件当前在子进程中,则必须进行上下文切换以进入父进程以获取pid。这会导致开销吗?
当调用getpid()时,会在用户空间边界上进行一些元数据传输并进入和退出内核。那么用户空间和内核之间的不断切换是否会导致一些开销?
答案 0 :(得分:6)
我在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 对具有小尺寸请求(< 64字节=>无系统调用)的成本约为 70-80ns (请参阅我的回答at Cost of static memory allocation vs dynamic memory allocation in C)。
https://softwareengineering.stackexchange.com/questions/311165/why-isnt-there-generic-batching-syscall-in-linux-bsd/350173有一些关于如何最小化系统调用开销的有趣想法。
答案 1 :(得分:4)
请原谅(而不是限定每一句话)。
对系统服务的调用(例如返回进程信息)具有用户模式shell。此shell触发一个异常,该异常通过调用内核模式系统服务的系统调度表进行路由。
切换到内核模式需要类似于进程上下文切换的东西。例如,它需要从用户堆栈更改为kern堆栈(以及其他依赖于系统的更改)。
调用进程提供用户模式返回缓冲区。在出于安全目的编写响应数据之前,系统系统服务将检查以确保它是有效的用户模式缓冲区。
像getpid这样的库函数只返回有关当前进程的信息,可能不需要切换到内核模式。
答案 2 :(得分:3)
例如,如果我们考虑getpid(),当对getpid()进行系统调用时,我的猜测是如果控件当前在子进程中,则必须进行上下文切换以进入父进程得到pid。
此处不需要上下文切换到子进程 - 内核应该拥有自己可用的所有必要数据。在大多数情况下,内核只会将上下文切换到调度程序中的用户空间进程,或者从系统调用返回时。
当调用getpid()时,将在用户空间边界上进行一些元数据传输,并进入和退出内核。那么用户空间和内核之间的不断切换也会导致一些开销吗?
是的,如果经常调用getpid()
,那么开销肯定会增加。有一些方法可以避免这种简单的“getter”系统调用(如getpid()
和gettimeofday()
;在Linux下使用的一种方法是将系统调用的(已知)结果存储在特殊的内存页面中。 (这种机制称为vsyscall。)