13

我刚开始研究系统调用。我想知道进行系统调用时导致开销的原因。

例如,如果我们考虑 getpid(),当对 getpid() 进行系统调用时,我的猜测是,如果控件当前在子进程中,则必须进行上下文切换才能进入父进程以获取 pid . 这会增加开销吗?

另外当getpid()被调用时,会有一些元数据跨越用户空间边界进出内核。那么用户空间和内核之间不断的切换会不会也造成一些开销呢?

4

3 回答 3

16

我在 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)应该最准确地反映平均开销,因为内核中不应该有太多的工作要做。

作为比较,具有小请求(<64 字节 => 无系统调用)的malloc+free对的成本约为70-80ns(请参阅我在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对如何最小化系统调用开销有一些有趣的想法。

于 2017-01-21T19:14:44.337 回答
6

例如,如果我们考虑 getpid(),当对 getpid() 进行系统调用时,我的猜测是,如果控件当前在子进程中,则必须进行上下文切换才能进入父进程以获取 pid .

这里不需要切换到子进程的上下文——内核应该拥有自己可用的所有必要数据。在大多数情况下,内核只会将上下文切换到调度程序中的用户空间进程,或者从系统调用返回时。

此外,当调用 getpid() 时,将有一些元数据跨用户空间边界传输并进入和退出内核。那么用户空间和内核之间的不断切换是否也会造成一些开销呢?

是的,如果getpid()经常被调用,开销肯定会增加。有一些方法可以避免这种简单的“getter”系统调用的开销,比如getpid()and gettimeofday(); 在 Linux 下曾经使用过的一种方法是将系统调用的(已知)结果存储在一个特殊的内存页面中。(这种机制被称为vsyscall。)

于 2014-05-12T02:35:56.820 回答
4

请原谅概括(而不是限定每个句子)。

对系统服务的调用(例如返回进程信息)具有用户模式 ​​shell。此 shell 触发一个异常,该异常通过调用内核模式系统服务的系统调度表进行路由。

切换到内核模式需要类似于进程上下文切换的东西。例如,它需要从用户堆栈更改为紧排堆栈(以及其他与系统相关的更改)。

调用进程提供用户模式返回缓冲区。出于安全目的,系统系统服务将在写入响应数据之前检查以确保它是有效的用户模式缓冲区。

像 getpid 这样只返回当前进程信息的库函数可能不需要切换到内核模式。

于 2014-05-12T04:47:13.780 回答