有關月度分享上 shell 提到的 syscall 效率

32 views
Skip to first unread message

DaboD

unread,
Jan 25, 2015, 2:30:38 PM1/25/15
to sh...@googlegroups.com
聽完了 shell 今天的分享又學了不少東西. 關於 syscall 方面, google 了一下, 目前瞭解的是
  1. syscall 成本很高
  2. syscall 在 x86 上是以 int 0x80 讓 kernel trap 從而切換 user space 到 kernel space
  3. 新的 CPU 提供了更高效的 syscall 執行方式, 但不向前兼容
  4. 部份 syscall 的執行, 可在 user space 完成
關於第 4 點很有意思. 一些被執行的頻率非常的高又不需要特別權限的簡單 syscall, 在 linux 上以 vDSO (Virtual Dynamic Shared Object) 的方式被實現. 這玩意是由 kernel 提供的, 在 build kernel source tree 時會一併產生, 會跟我們的程序 link 在一塊, 可在 build 過的 kernel source tree 下執行以下命令找到相應的 vDSO
find arch/$ARCH/ -name '*vdso*.so*' -o -name '*gate*.so*'

通常是由如 glibc 來包裝并調用. 執行 ldd 時能看到程序是有跟它 link 在一塊的 (不同 HW platform 名字會有差異), 例如
$ ldd /bin/ls
        linux-vdso.so.1 (0x00007fff205fc000)
        libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fd891751000)
        libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007fd891548000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd89119e000)
        libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007fd890f30000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fd890d2c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fd89199e000)
        libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007fd890b26000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fd890909000)

程序在啟動時, 有部份 kernel page 會以 read only 的方式被 map 到 user space, 這樣 vDSO 就可以不做 context switch 的情況讀取到 kernel 中的資料完成如 gettimeofday(), getpid() 等 syscall.

實驗 1
$ cat test.c 
int main()
{
    int i = 0;
    for(; i < 1000000000; i ++) {}

    return 0;
}

Compile & run
$ gcc -o test test.c && time ./test

real    0m2.127s
user    0m2.104s
sys     0m0.000s

實驗 2
$ cat test2.c
#include <sys/types.h>
#include <unistd.h>

int main()
{
    int i = 0;
    for(; i < 1000000000; i ++) {
        getpid();
    }

    return 0;
}

Compile & run
$ gcc -o test2 test2.c && time ./test2

real    0m3.499s
user    0m3.464s
sys     0m0.000s

$ perf record -F 371 ./test
$ perf report
  60.41%  test  test               [.] main
  30.35%  test  libc-2.19.so       [.] getpid
   9.01%  test  test               [.] getpid@plt

調用了 getpid() 這個 syscall 但時間并沒有花在 kernel space 中. 

實驗 3
$ cat test3.c
#include <sys/types.h>
#include <unistd.h>

int main()
{
    int i = 0;
    for(; i < 100000000; i ++) {
        close(0);
    }

    return 0;
}

Compile & run
$ gcc -o test3 test3.c && time ./test3

real    0m59.033s
user    0m19.532s
sys     0m39.524s

close() 想必會更新 kernel 中的 data structure, 用 vDSO 實現應該會很危險, 必須在 kernel space 中完成.

主要 CPU 時間花用的部份
$ perf record -F 371 ./test3
$ perf report
  28.27%  test  libc-2.19.so       [.] __GI___libc_close
  21.32%  test  [kernel.kallsyms]  [k] system_call
  16.61%  test  [kernel.kallsyms]  [k] system_call_after_swapgs
  14.83%  test  [kernel.kallsyms]  [k] _raw_spin_lock
  10.00%  test  [kernel.kallsyms]  [k] __close_fd
   3.56%  test  [kernel.kallsyms]  [k] sysret_check
   2.94%  test  [kernel.kallsyms]  [k] sys_close
   0.61%  test  test               [.] close@plt
   0.60%  test  [kernel.kallsyms]  [k] ret_from_sys_call
   0.59%  test  test               [.] main

28.27+0.61+0.59=29.47% 是花在 user space 中, 21.32+16.61+14.83+10+3.56+2.94=69.26% 花在 kernel space 中.

花在 kernel 中 close() 的實現部份只有 10%, 花在 glibc 的 close() 包裝 28.27%, 沒研究 source code 不瞭解為什麼. system_call_after_swapgs 是實際 syscall 調用的 entry point, 在 check 了 syscall number 後進行相應功能的調用 (http://www.crashcourse.ca/wiki/index.php/System_calls), 從 syscall 的返回也花了 0.60%.

參考資料

Chaos Eternal

unread,
Jan 25, 2015, 7:41:55 PM1/25/15
to sh...@googlegroups.com
歪个楼
这纯属架构问题,按wang yin的说法,早用lisp machine 就没这些事了。
> --
> -- You received this message because you are subscribed to the Google Groups
> Shanghai Linux User Group group. To post to this group, send email to
> sh...@googlegroups.com. To unsubscribe from this group, send email to
> shlug+un...@googlegroups.com. For more options, visit this group at
> https://groups.google.com/d/forum/shlug?hl=zh-CN
> ---
> 您收到此邮件是因为您订阅了Google网上论坛上的“Shanghai Linux User Group”群组。
> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到shlug+un...@googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout

DaboD

unread,
Jan 25, 2015, 9:29:03 PM1/25/15
to sh...@googlegroups.com
喔? 有參考資料嗎?
> 要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到shlug+unsubscribe@googlegroups.com
> 要查看更多选项,请访问https://groups.google.com/d/optout

DaboD

unread,
Jan 25, 2015, 9:40:21 PM1/25/15
to sh...@googlegroups.com
更新一下實驗 3, 同樣調用 getpid(), 但調用 glibc 的 syscall() 跳過 vDSO
$ cat test3.c
#define _GNU_SOURCE
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>

int main()
{
    int i = 0;
    for(; i < 1000000000; i ++) {
        syscall(SYS_getpid);
    }

    return 0;
}

Compile & run
$ gcc -o test test.c && time ./test

real    0m50.049s
user    0m19.060s
sys     0m31.012s

$ perf record -F 371 ./test
$ perf report
  26.49%  test  libc-2.19.so       [.] syscall
  25.79%  test  [kernel.kallsyms]  [k] system_call
  19.29%  test  [kernel.kallsyms]  [k] system_call_after_swapgs
  13.56%  test  [kernel.kallsyms]  [k] pid_vnr
   5.53%  test  test               [.] main
   5.03%  test  [kernel.kallsyms]  [k] sysret_check
   2.02%  test  [kernel.kallsyms]  [k] sys_getpid
   0.89%  test  [kernel.kallsyms]  [k] system_call_fast_compare_end
   0.66%  test  test               [.] syscall@plt

Shell Xu

unread,
Jan 25, 2015, 9:58:19 PM1/25/15
to shlug
呵呵,你的死法和我一模一样。
有看过glibc源码么?glibc-2.19/nptl/sysdeps/unix/sysv/linux/getpid.c

我的错误和修正:

看到又有人栽在了我栽的坑里,我顿时感觉好欣慰。

2015-01-26 3:30 GMT+08:00 DaboD <daid...@gmail.com>:

--
-- You received this message because you are subscribed to the Google Groups Shanghai Linux User Group group. To post to this group, send email to sh...@googlegroups.com. To unsubscribe from this group, send email to shlug+un...@googlegroups.com. For more options, visit this group at https://groups.google.com/d/forum/shlug?hl=zh-CN
---
您收到此邮件是因为您订阅了Google网上论坛上的“Shanghai Linux User Group”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到shlug+un...@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout



--
彼節者有間,而刀刃者無厚;以無厚入有間,恢恢乎其於游刃必有餘地矣。
blog: http://shell909090.org/blog/

DaboD

unread,
Jan 25, 2015, 10:37:28 PM1/25/15
to sh...@googlegroups.com
大家一起進來, 坑裡暖活些 ;)
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到shlug+unsubscribe@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout

DaboD

unread,
Jan 25, 2015, 10:46:29 PM1/25/15
to sh...@googlegroups.com
比較搞不懂的是 __GI___libc_close 這玩意是什麼東西, 怎麼會花這麼多時間在上面, 照理說不就是 @plt 轉 call 一個 syscall() 的 wrapper function, 然後執行 syscall() 接著就進到 kernel 中了?


On Monday, January 26, 2015 at 10:58:19 AM UTC+8, shell909090 wrote:
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到shlug+unsubscribe@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout

py_zhu

unread,
Jan 27, 2015, 9:22:20 AM1/27/15
to sh...@googlegroups.com
不要以为能在man 2中找到的API就肯定会执行syscall啊,得看具体C库实现。

要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到shlug+un...@googlegroups.com
要查看更多选项,请访问https://groups.google.com/d/optout



--
zhu

DaboD

unread,
Jan 27, 2015, 9:40:37 AM1/27/15
to sh...@googlegroups.com
喔? 能舉個例子嗎?
不要以为能在man 2中找到的API就肯定会执行syscall啊,得看具体C库实现。
zhu
Reply all
Reply to author
Forward
0 new messages