You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用libcheck对调用fork/exec的程序进行单元测试?

解决libcheck测试fork/exec程序时的信号终止问题

问题回顾

我用libcheck测试一个调用fork()和exec()的程序,正常运行程序时能创建子进程、等待片刻后用SIGTERM终止子进程,完全没问题。但在测试里执行相同操作时:

  • 测试失败,提示“Received signal 15 (Terminated)”,疑似测试进程被终止;
  • 用strace跑测试,看不到kill操作执行;
  • 设置CK_FORK=no后用strace跑,kill会执行,但程序在wait阶段中止;
  • 测试成功率和系统负载相关:CPU空闲时约50%成功,负载高时几乎全失败;
  • 编译命令:gcc test.c -lcheck_pic -pthread -lrt -lm -lsubunit

核心原因

libcheck默认通过fork子进程运行测试用例,你的测试逻辑又嵌套了fork操作,导致两层进程的信号处理、进程调度出现冲突:

  1. 默认模式下,libcheck测试子进程和你自己fork的子进程共享信号上下文,SIGTERM可能误发给libcheck的测试进程;
  2. CK_FORK=no时,测试用例在主进程运行,你的wait逻辑和libcheck内部进程管理逻辑冲突,高负载下进程调度延迟加剧了竞态条件。

解决方案

1. 隔离测试逻辑到独立进程

在测试用例中手动fork一个独立进程执行你的核心逻辑,避免和libcheck的测试进程干扰:

void test_my_program() {
    pid_t wrapper_pid = fork();
    if (wrapper_pid == 0) {
        // 这里放原测试逻辑:fork子进程、exec、等待、发送SIGTERM
        pid_t child_pid = fork();
        if (child_pid == 0) {
            execvp("your_program", argv);
            exit(EXIT_FAILURE);
        }
        // 等待片刻后发送SIGTERM
        sleep(1);
        kill(child_pid, SIGTERM);
        // 等待子进程退出
        int status;
        waitpid(child_pid, &status, 0);
        exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
    } else {
        // 测试进程等待独立子进程完成
        int status;
        waitpid(wrapper_pid, &status, 0);
        ck_assert(WIFEXITED(status) && WEXITSTATUS(status) == 0);
    }
}

2. 重置子进程信号处理

在你自己fork的子进程中,重置信号处理为默认,避免继承libcheck的信号掩码:

pid_t child_pid = fork();
if (child_pid == 0) {
    // 重置SIGTERM处理为默认
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = SIG_DFL;
    sa.sa_flags = 0;
    sigaction(SIGTERM, &sa, NULL);
    
    execvp("your_program", argv);
    exit(EXIT_FAILURE);
}

3. 优化wait逻辑避免竞态

用waitpid()指定目标PID,并且处理被信号中断的情况:

int status;
// 循环等待,处理EINTR中断
while (waitpid(child_pid, &status, 0) == -1) {
    if (errno != EINTR) {
        perror("waitpid failed");
        ck_abort_msg("Failed to wait for child process");
    }
}

调试技巧

  • 启用verbose日志:CK_VERBOSE=1 ./test,查看测试进程的详细执行流程;
  • 打印进程ID:在关键位置加入printf("Test PID: %d, Child PID: %d\n", getpid(), child_pid);,确认进程层级;
  • 跟踪系统调用:CK_FORK=no strace -f -e fork,execve,kill,waitpid ./test,跟踪所有进程的fork/exec/kill/wait操作,定位异常点;

内容的提问来源于stack exchange,提问作者Caulder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 22:37:31