在 C 中测量子进程退出所需的时间

Measuring the time a child process takes to exit in C

提问人:CoolSteve 提问时间:11/13/2023 更新时间:11/13/2023 访问量:59

问:

我正在尝试在 C 中创建一个非常小的单元测试仅标头库。

我有一个非常基本的宏设置,称为分叉进程并执行给定的测试用例,这只是一个函数。我想掩盖测试无法正确退出的情况(例如,使用)。 我想打印出测试用例运行所需的时间,最好以秒为单位。RUN_TEST_CASEforkSIGSEGV

这是我目前拥有的代码——

#include <stdio.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
#include <time.h>

#define __MINTEST_SAFE_BLOCK(block) \
    do                              \
    {                               \
        block                       \
    } while (0)

#define TEST_CASE(test) static void test(void)

#define TEST_SUITE(suite) static void suite(void)

#define __RUN_TEST_CASE(test, caller) __MINTEST_SAFE_BLOCK(                                               \
    pid_t pid;                                                                                            \
    int status;                                                                                           \
    clock_t begin;                                                                                        \
    clock_t end;                                                                                          \
    double time_spent;                                                                                    \
                                                                                                          \
    begin = clock();                                                                                      \
    pid = fork();                                                                                         \
    switch (pid) {                                                                                        \
        case -1:                                                                                          \
            perror("fork");                                                                               \
            break;                                                                                        \
        case 0:                                                                                           \
            test();                                                                                       \
            break;                                                                                        \
        default:                                                                                          \
            waitpid(pid, &status, 0);                                                                     \
            end = clock();                                                                                \
            time_spent = (end - begin);                                                                   \
            if (WIFEXITED(status))                                                                        \
            {                                                                                             \
                printf("%s:%s passed after %fms [%d]\n", caller, #test, time_spent, WEXITSTATUS(status)); \
            }                                                                                             \
            else if (WIFSIGNALED(status))                                                                 \
            {                                                                                             \
                printf("%s:%s failed after %fms [%d]\n", caller, #test, time_spent, WTERMSIG(status));    \
            }                                                                                             \
            break;                                                                                        \
    })

#define RUN_TEST_CASE(test) __RUN_TEST_CASE(test, __func__)

#define RUN_TEST_SUITE(suite) suite()

我正在使用以下 C 代码测试这些宏 -

#include "mintest.h"

TEST_CASE(test_something) {
    printf("In test\n");
    sleep(2);
    *(volatile int*)NULL;
};

TEST_SUITE(test_suite) {
    RUN_TEST_CASE(test_something);
};

int main(void) {
    RUN_TEST_SUITE(test_suite);
    return 0;
}

我希望看到打印出来的测试失败并花了大约 2 秒钟,但我得到的结果非常不一致 -

In test
test_suite:test_something failed after 574.000000ms [11]

In test
test_suite:test_something failed after 462.000000ms [11]

In test
test_suite:test_something failed after 689.000000ms [11]

我做错了什么吗?也许我对什么有错误的理解?waitpid()

C Linux 单元测试 时间

评论

0赞 Charles Duffy 11/13/2023
操作系统调度程序可以根据需要在任意延迟后安排您;不能保证您会在孩子完成的那一刻重新获得控制权。
0赞 Charles Duffy 11/13/2023
如果你想要操作系统内部的精确计时,你最好使用一个专门构建的工具进行测量 - oprofile,或者它更现代的基于ebpf的替代方案之一,或者类似的东西。github.com/iovisor/bpftrace/blob/master/docs/......对于后者来说,这是一个不错的起点。
0赞 Charles Duffy 11/13/2023
(系统调用级别的跟踪也会让你看到为什么你没有得到预期的至少两秒的延迟——尽管你也可以通过实际检查返回值来获得一些进展;它允许提前完成并返回一个人需要休眠的剩余时间,以达到完全请求的超时;参见 pubs.opengroup.org/onlinepubs/009696799/functions/sleep.htmlsleep())
0赞 Craig Estey 11/13/2023
而不是 ,我会使用 And,我可能会将 移动到 上方以忽略/跳过通话的开销。无论哪种方式,它都不准确,但可能包括父母的时间,而不仅仅是孩子的时间。或者,更好的是,让孩子执行这两个调用并将差异发送回父级(通过管道或共享内存,或 SysV IPC)。clock(3)clock_gettime(CLOCK_MONOTONIC,...)begin = ...waitpidforkclockclock*msgsnd/msgrcv

答:

3赞 Chris Dodd 11/13/2023 #1

clock()返回处理器时间(不是经过时间),以时钟周期为单位,而不是毫秒。您需要除以才能转换为秒,但这可能仍然不是您想要的,因为它只是在父项中花费的时间,不包括在 中花费的任何时间。(double)CLOCKS_PER_SECwait

您可以使用代替来获得更有用的数字。clock_gettime


在子项中运行测试后,应调用 (或 );否则,它可能会继续运行测试代码(这意味着它将在孙子项中运行后续测试的额外副本,并且通常会爆炸。在您的特定情况下,它不会发生,因为测试用例在 SEGV 中失败,但其他通过的测试可能会出现此问题。exit_exit