让我感到困扰的是,linux需要很长时间才能列出大型目录的所有文件,因此我创建了一个小的测试脚本,以递归方式列出目录中的所有文件:
#include <stdio.h>
#include <dirent.h>
int list(char *path) {
int i = 0;
DIR *dir = opendir(path);
struct dirent *entry;
char new_path[1024];
while(entry = readdir(dir)) {
if (entry->d_type == DT_DIR) {
if (entry->d_name[0] == '.')
continue;
strcpy(new_path, path);
strcat(new_path, "/");
strcat(new_path, entry->d_name);
i += list(new_path);
}
else
i++;
}
closedir(dir);
return i;
}
int main() {
char *path = "/home";
printf("%i\n", list(path));
return 0;
用gcc -O3
编译它时,程序运行大约15秒(我运行了几次程序并且它大致不变,所以fs缓存不应该在这里发挥作用):
$ /usr/bin/time -f "%CC %DD %EE %FF %II %KK %MM %OO %PP %RR %SS %UU %WW %XX %ZZ %cc %ee %kk %pp %rr %ss %tt %ww %xx" ./a.out
./a.outC 0D 0:14.39E 0F 0I 0K 548M 0O 2%P 178R 0.30S 0.01U 0W 0X 4096Z 7c 14.39e 0k 0p 0r 0s 0t 1692w 0x
因此,它在内核空间中花费S = 0.3秒,在用户空间中花费U = 0.01秒,并且具有7 + 1692个上下文切换。 上下文切换大约需要2000nsec *(7 + 1692)= 3.398msec [1] 但是,还剩下10秒以上,我想知道这个程序在这个时候做了什么。 有没有其他工具来调查程序一直在做什么? gprof告诉我(用户空间)调用图的时间,而gcov没有列出每行所花费的时间,而只列出执行时间的频率......
[1] http://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html
答案 0 :(得分:2)
oprofile
是一个不错的采样分析器,它可以分析用户和内核模式代码。
然而,根据您的数字,大约有14.5秒的时间用于睡眠,oprofile
并没有真正记录下来。也许更有用的是ftrace
结合内核代码的读取。 ftrace
在内核中提供跟踪点,可以在消息发生时记录消息和堆栈跟踪。对于确定进程正在休眠的原因似乎最有用的事件是sched_switch
事件。我建议你启用内核模式堆栈和sched_switch
事件,设置一个足够大的缓冲区来捕获进程的整个生命周期,然后运行你的进程并在之后立即停止跟踪。通过查看跟踪,您将能够看到每次进程进入休眠状态,无论是可运行还是不可运行,高分辨率时间戳以及指示使其进入休眠状态的调用堆栈。
ftrace
通过debugfs
进行控制。在我的系统上,它安装在/sys/kernel/debug
,但你的可能会有所不同。以下是我将如何捕获此信息的示例:
# Enable stack traces
echo "1" > /sys/kernel/debug/tracing/options/stacktrace
# Enable the sched_switch event
echo "1" > /sys/kernel/debug/tracing/events/sched/sched_switch/enable
# Make sure tracing is enabled
echo "1" > /sys/kernel/debug/tracing/tracing_on
# Run the program and disable tracing as quickly as possible
./your_program; echo "0" > /sys/kernel/debug/tracing/tracing_on
# Examine the trace
vi /sys/kernel/debug/tracing/trace
结果输出将包含如下所示的行:
# tracer: nop
#
# entries-in-buffer/entries-written: 22248/3703779 #P:1
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
<idle>-0 [000] d..3 2113.437500: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/0:0 next_pid=878 next_prio=120
<idle>-0 [000] d..3 2113.437531: <stack trace>
=> __schedule
=> schedule
=> schedule_preempt_disabled
=> cpu_startup_entry
=> rest_init
=> start_kernel
kworker/0:0-878 [000] d..3 2113.437836: sched_switch: prev_comm=kworker/0:0 prev_pid=878 prev_prio=120 prev_state=S ==> next_comm=your_program next_pid=898 next_prio=120
kworker/0:0-878 [000] d..3 2113.437866: <stack trace>
=> __schedule
=> schedule
=> worker_thread
=> kthread
=> ret_from_fork
您关注的行将是您的程序显示为prev_comm
任务的行,这意味着调度程序正在从您的程序切换到运行其他程序。 prev_state
会表明您的程序仍然可以运行(R
)或被阻止(S
,U
或其他一些字母,请参阅ftrace来源)。如果被阻止,您可以检查堆栈跟踪和内核源代码以找出原因。