运行 GNU time (
/usr/bin/time
) 并检查内存消耗时,其输出是否考虑了目标程序的子进程的内存使用情况?
在 GNU 的时间手册页中找不到任何内容。
是的。
您可以轻松查看:
$ /usr/bin/time -f '%M' sh -c 'perl -e "\$y=q{x}x(2*1024*1024)" & wait'
8132
$ /usr/bin/time -f '%M' sh -c 'perl -e "\$y=q{x}x(8*1024*1024)" & wait'
20648
wait4
系统调用(通过 wait3
glibc 包装器),虽然没有记录,但它在 struct rusage
中返回的资源使用情况还包括等待的进程的后代。您可以在 wait4
中查看
kernel/exit.c
的内核实现以了解所有详细信息:
$ grep -C2 RUSAGE_BOTH include/uapi/linux/resource.h
#define RUSAGE_SELF 0
#define RUSAGE_CHILDREN (-1)
#define RUSAGE_BOTH (-2) /* sys_wait4() uses this */
#define RUSAGE_THREAD 1 /* only the calling thread */
wait6
系统调用,它返回等待的进程及其后代的单独信息。他们还清楚地记录了 wait3
和 wait4
返回的 rusage 还包括孙子。
答案部分取决于正在计时的程序的行为。是的,
time
确实使用了wait4()
系统调用,并且该系统调用返回子进程及其孙子进程的资源使用情况(包括消耗的CPU时间),但仅适用于已启用wait()
的孙子进程。如果孙子们从来没有被侍候过,那么他们的资源使用量就不会被计算在内。
这意味着,如果
time
正在监视的程序创建了子进程,但 wait()
没有在这些子进程上运行,则 time
不包括这些子进程使用的 CPU 时间。
我们可以通过一个小示例程序来看到这一点,该程序创建一个消耗一些 CPU 的子进程,并且可以选择在终止之前等待子进程(父进程消耗的 CPU 时间可以忽略不计):
/* burner.c */
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
int nloops = argc > 1 ? atoi(argv[1]) : 1;
/* Use a pipe to ensure that parent can wait until child loop has completed. */
int pfd[2];
pipe(pfd);
if (fork() == 0) {
/* CHILD */
for (volatile int j = 0; j < nloops; j++)
for (volatile int k = 0; k < 1000000000; k++)
continue;
close(pfd[1]);
exit(EXIT_SUCCESS);
}
/* PARENT */
close(pfd[1]);
char ch;
read(pfd[0], &ch, 1); // Wait for child to close pipe
/* If a second command-line argument was supplied, wait() on child */
if (argc > 2)
wait(NULL);
exit(EXIT_SUCCESS);
}
如果在没有第二个命令行参数的情况下运行,程序不会等待其子进程:
$ /bin/time -p ./burner 10
real 2.68
user 0.00
sys 0.00
请注意,
user
和 sys
CPU 时间为 0。
如果使用第二个命令行参数运行,程序会等待其子进程,我们看到
time
确实显示了子进程消耗的 CPU 时间:
$ /bin/time -p ./burner 10 w
real 2.66
user 2.66
sys 0.00