【问题标题】:How to tell if a function is called within a vfork()'ed process?如何判断一个函数是否在 vfork()'ed 进程中被调用?
【发布时间】:2019-09-18 13:12:02
【问题描述】:

我有一个用于 C 的插入器库。无法插入 vfork(),因为 vfork()(插入函数)在调用真正的 vfork() 后无法返回调用函数。但我插入_exit() 因为我需要知道该过程何时结束。当然,我正在插入exec*() 函数。我的问题是,当插入_exit() 时,当_exit() 被正常进程调用但不是当进程是受限制的vfork()'ed 进程时,我想做某些事情。

如何在 C 程序中判断我的进程何时是 vfork()'ed 进程以及何时我无权访问由 vfork() 返回的进程 ID?

转接器库:

/* COMPILE: gcc -shared -ldl -fPIC -o libso_interposer.so so_interposer.c -std=c99 */
/* RUN:     LD_PRELOAD=./libso_interposer.so so_interposer_test */
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>


static void (*_exit_R)(int) = (void *) 0;
static pid_t (*fork_R)(void) = (void *) 0;
static void teardown_interposer() {
    fprintf(stderr, "Destructing so_interposer.\n");
    /* Concurrency protected code to be executed only once
     * when the process finishes! */
    /* Must not be executed if/when vfork() process finishes. */
}

pid_t fork(void) {
    *(pid_t **) (&fork_R) = dlsym(RTLD_NEXT, "fork");
    /* Code to prepare for a new process.
    * More preparation in exec* interposing functions.*/
    pid_t pid = fork_R();
    return pid;
}
__attribute__((noreturn)) void _exit(int status) {
    *(void **) (&_exit_R) = dlsym(RTLD_NEXT, "_exit");
    fprintf(stderr, "Process '%lld' called _exit(%i).\n", (signed long long int) getpid(), status);
    teardown_interposer();
    _exit_R(status);
}

测试二进制文件:

/* COMPILE: gcc -std=c99 -D FORK=vfork -o so_interposer_test so_interposer_test.c */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#if !defined(FORK)
#error "Define FORK: 'fork' or 'vfork'!"
#endif
int main(void) {
    pid_t pid = FORK();
    if(pid == 0) {
        _exit(EXIT_SUCCESS);
    } else if(pid > 0) {
        _exit(EXIT_SUCCESS);
    }
    return EXIT_SUCCESS;
}

【问题讨论】:

  • vfork()无法返回调用函数”是什么意思?当然vfork 返回。也许您正在考虑exec 系列函数?
  • 我不确定你到底想要什么,但vfork() 会为子进程返回 0,就像fork() 一样。
  • 为什么你的函数不能返回?你真的在做什么?你能创建一个minimal reproducible example 给我们看吗?
  • @Someprogrammerdude 我有同样的问题,但the vfork() man page 说“孩子不能从当前函数返回”。所以我认为这里的问题是关于如何在子进程中处理这个问题(尽管它当然需要澄清)。
  • 自己的函数是不是要转发到真实的vfork()?你能不能只转发到fork() 或者比真正的vfork() 限制更少的东西?

标签: c linux vfork


【解决方案1】:

对于足够新的 gcc 版本,您应该能够将 vfork 包装为:

typedef pid_t (*vfork_t)(void);
extern vfork_t wrap_vfork();

pid_t vfork(void) {
    vfork_t f = wrap_vfork();
    return f();
}

wrap_vfork 完成所有 vfork 包装工作,并返回指向真正 vfork 的指针(不调用它)。 Gcc 6.3.0 (-O3) 将其编译为:

    .globl  vfork
    .type   vfork, @function
vfork:
.LFB11:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    xorl    %eax, %eax
    call    wrap_vfork
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    jmp     *%rax
    .cfi_endproc
.LFE11:
    .size   vfork, .-vfork

需要注意的重要一点是它直接跳转到实际的vfork函数而不是调用它,所以本身不需要返回(真正的vfork会直接返回给这个函数的调用者)

如果你不习惯依赖编译器为你做尾部调用优化,你可以直接在 asm 中而不是在 C 中编写这个小例程。

【讨论】:

  • 这只有在编译器支持尾调用优化时才有效。即便如此,我也无法明确要求。相反,如果编译器认为应该优化我的代码。如果 GCC 具有应程序员要求实现尾调用的功能(不取决于它是否是优化),这将是该问题的替代解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 2015-01-02
  • 2012-07-31
  • 1970-01-01
  • 1970-01-01
  • 2011-08-17
相关资源
最近更新 更多