我刚开始学习系统调用。 我想知道发生系统调用时会导致什么开销。
例如,如果我们考虑 getpid() 函数,当调用 getpid() 时,我的猜测是,如果控制权当前在子进程中,则必须进行上下文切换才能进入父进程以获取 pid。这是否会对开销造成贡献?
此外,当调用 getpid() 时,将在用户空间边界之间传输一些元数据并进入并退出内核。因此,频繁切换用户空间和内核之间是否也会造成一些开销?
我刚开始学习系统调用。 我想知道发生系统调用时会导致什么开销。
例如,如果我们考虑 getpid() 函数,当调用 getpid() 时,我的猜测是,如果控制权当前在子进程中,则必须进行上下文切换才能进入父进程以获取 pid。这是否会对开销造成贡献?
此外,当调用 getpid() 时,将在用户空间边界之间传输一些元数据并进入并退出内核。因此,频繁切换用户空间和内核之间是否也会造成一些开销?
我在一台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
最便宜的"syscalls"是通过辅助向量的那些(~20-30ns)。中间的调用(~250-310ns)应该最准确地反映平均开销,因为内核中不需要做太多的工作。
相比之下,小尺寸请求(<64字节=>没有系统调用)的malloc+free对成本约为70-80ns(请参见我在C中静态内存分配与动态内存分配的成本比较回答)。
https://softwareengineering.stackexchange.com/questions/311165/why-isnt-there-generic-batching-syscall-in-linux-bsd/350173提供了一些有关如何将syscall开销最小化的有趣想法。
getpid()
已不再被缓存,这个答案提交后不久就发布了。getpid(2)
的手册页面提供了有关此主题的一些附加上下文,并指出在版本2.3.4到2.24之间存在缓存。 - jmikolageteuid
是真正的系统调用开始的地方。一个真正的系统调用不可能比这个快多少。 - Petr Skocik请见谅我有些概括(而不是每个句子都详细说明)。
调用系统服务(例如返回进程信息)会有一个用户模式壳。这个壳会触发异常,然后通过系统调度表路由到调用内核模式系统服务。
切换到内核模式需要类似于进程上下文切换的操作。例如,需要从用户栈切换到内核栈(和其他系统相关的更改)。
调用进程提供用户模式返回缓冲区。出于安全原因,系统服务将在写入响应数据之前检查其是否为有效的用户模式缓冲区。
像只返回有关当前进程信息的getpid这样的库函数可能不需要切换到内核模式。