系统调用费用

Posted

技术标签:

【中文标题】系统调用费用【英文标题】:System call cost 【发布时间】:2016-08-08 22:21:53 【问题描述】:

我目前正在处理操作系统运营开销。

我实际上正在研究进行系统调用的成本,并且我开发了一个简单的 C++ 程序来观察它。

#include <iostream>
#include <unistd.h>
#include <sys/time.h>

uint64_t
rdtscp(void) 
  uint32_t eax, edx;

  __asm__ __volatile__("rdtscp" //! rdtscp instruction
               : "+a" (eax), "=d" (edx) //! output
               : //! input
               : "%ecx"); //! registers

  return (((uint64_t)edx << 32) | eax);


int main(void) 
  uint64_t before;
  uint64_t after;
  struct timeval t;
  for (unsigned int i = 0; i < 10; ++i) 
    before = rdtscp();
    gettimeofday(&t, NULL);
    after = rdtscp();
    std::cout << after - before  << std::endl;
    std::cout << t.tv_usec << std::endl;
  

  return 0;

这个程序非常简单。

rdtscp 函数只是调用 RTDSCP 指令(将 64 位周期计数加载到两个 32 位寄存器中的处理器指令)的包装器。该函数用于计时。 我重复了 10 次。在每次迭代中,我都会调用 gettimeofday 并确定执行它所花费的时间(以 CPU 周期数表示)。

结果出乎意料:

8984
64008
376
64049
164
64053
160
64056
160
64060
160
64063
164
64067
160
64070
160
64073
160
64077

输出中的奇数行是执行系统调用所需的周期数。甚至行是 t.tv_usec 中包含的值(由 gettimeofday 设置,我正在研究的系统调用)。

我真的不明白这是怎么可能的:循环次数急剧减少,从近 10,000 次减少到 150 次左右!但是 timeval 结构仍然在每次调用时更新!

我尝试过不同的操作系统(debian 和 macos),结果相似。

即使使用了缓存,我也看不出它是怎么可能的。进行系统调用应该会导致从用户模式切换到内核模式的上下文切换,我们仍然需要读取时钟以更新时间。

有人有想法吗?

【问题讨论】:

我不知道答案,但为什么你认为缓存不能解释它? 这将是动态链接器延迟符号解析(延迟绑定)。尝试使用LD_BIND_NOW=1 (Linux) 或DYLD_BIND_AT_LAUNCH=1 (MacOS) 在启动时强制链接器解析所有符号。第一次调用gettimeofday() 的循环计数应该会显着下降。 好主意,它已显着下降,但在第一次调用后仍继续下降(第一次调用时约 770 个循环计数,之后仍为约 150 个)。 【参考方案1】:

答案?尝试另一个系统调用。 linux 上有 vsyscalls,它们会加速某些系统调用的速度: What are vdso and vsyscall?

简短版本:不执行系统调用,而是内核映射一个内存区域,进程可以在其中访问时间信息。成本 ?不多(没有上下文切换)。

【讨论】:

正是我想要的!

以上是关于系统调用费用的主要内容,如果未能解决你的问题,请参考以下文章

Linux系统调用跟我学(1)

LINUX系统调用

Linux 内核进程管理 ( 系统调用简介 | 进程相关系统调用源码 )

系统调用的理解

系统调用与函数调用

深入理解系统调用