Linux 确定进程正在等待的系统调用或子系统

Linux 确定进程正在等待的系统调用或子系统,linux,performance,blocking,perf,systemtap,Linux,Performance,Blocking,Perf,Systemtap,我正在寻找方法来了解进程或线程花费时间等待的系统调用或子系统,即阻塞和不计划在CPU上运行 具体来说,如果我有一些未知的过程,或者我们只知道“它很慢”的过程,我希望能够学习以下内容: “它将80%的时间花在fd 13上的sys_write(),fd 13是/some/file” “从网络套接字读取()需要花费大量时间” “它正在epoll_wait()中睡眠,等待FD[4,5,6]上的活动,这些FD[4,5,6]是[file/boo],[socket 10.1.1.:42],[notifyfd

我正在寻找方法来了解进程或线程花费时间等待的系统调用或子系统,即阻塞和不计划在CPU上运行

具体来说,如果我有一些未知的过程,或者我们只知道“它很慢”的过程,我希望能够学习以下内容:

  • “它将80%的时间花在fd 13上的
    sys_write()
    ,fd 13是/some/file”
  • “从网络套接字读取()需要花费大量时间”
  • “它正在epoll_wait()中睡眠,等待FD[4,5,6]上的活动,这些FD[4,5,6]是[file/boo],[socket 10.1.1.:42],[notifyfd blah]。”
换句话说,当我的程序没有在CPU上运行时,它在做什么

使用
perf
回答这一问题非常困难,因为它似乎没有任何方法来记录从sys\u enter到sys\u exit的系统调用的持续时间,或者跟踪事件的持续时间。大概是因为它的取样性质

我知道一些针对Linux 4.6及更高版本的eBPF的实验工作可能会对Brendan Gregg有所帮助。但是,在操作和支持4.6内核的悲惨世界中,很少有独角兽值得珍惜

现实世界的选择是什么

ftrace、systemtap等是否提供了任何见解?

您可以使用。首先,您可能希望获得每种类型系统调用的成本的高级摘要。您可以通过运行
strace-c
获得此摘要。例如,一个可能的输出如下:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 90.07    0.000263          26        10           getdents
  3.42    0.000010           0      1572           read
  3.42    0.000010           0       762         1 stat
  3.08    0.000009           0      1574         6 open
  0.00    0.000000           0        11           write
  0.00    0.000000           0      1569           close
  0.00    0.000000           0        48           fstat
%time
值与总体内核时间有关,而不是总体执行时间(内核+用户)。此摘要告诉您最昂贵的系统调用是什么。但是,如果需要确定哪些系统调用的特定实例最昂贵,以及传递给它们的参数,则可以运行
strace-i-T
-i
选项显示执行系统调用的指令的指令地址,
-T
选项显示在系统调用中花费的时间。输出可能如下所示:

[00007f97f1b37367] open("myfile", O_RDONLY|O_CLOEXEC) = 3 <0.000020>
[00007f97f1b372f4] fstat(3, {st_mode=S_IFREG|0644, st_size=159776, ...}) = 0 <0.000018>
[00007f97f1b374ba] mmap(NULL, 159776, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f97f1d19000 <0.000019>
[00007f97f1b37467] close(3)             = 0 <0.000018>
[00007f97f1b37367]打开(“myfile”,O|RDONLY | O|u CLOEXEC)=3
[00007f97f1b372f4]fstat(3,{st_mode=S_IFREG | 0644,st_size=159776,…})=0
[00007f97f1b374ba]mmap(空,159776,PROT_READ,MAP_PRIVATE,3,0)=0x7f97f1d19000
[00007f97f1b37467]关闭(3)=0
第一列显示指令地址,第二列显示带参数的系统调用,第三列显示返回值,最后一列显示在该系统调用中花费的时间。此列表按系统调用的动态发生顺序排列。您可以使用
grep
-e
选项筛选此输出。指令地址可以帮助您找到这些系统调用在源代码中的位置。例如,如果一长串系统调用具有相同的地址,那么很有可能在包含系统调用的代码中的某个地方有一个循环。如果您的可执行二进制文件不是,则动态地址与由
objdump
显示的静态地址相同。但即使使用PIE,动态地址的相对顺序也是相同的。我不知道是否有一种简单的方法可以将这些系统调用映射到源代码行

如果您想了解“它80%的时间都花在fd 13上的sys_write()上,即/some/file”然后,您需要编写一个脚本,首先提取所有
open
调用的返回值和相应的文件名参数,然后汇总
sys\u write
调用的次数,这些调用的
fd
参数等于某个值