一个forkchild能判断是fork还是vfork吗?

Can a fork child determine whether it is a fork or a vfork?

在 child 进程中,有什么方法可以确定它是作为具有覆盖内存的 fork 还是作为具有共享内存的 vfork 启动的?

基本上,我们的日志引擎在 vfork 中需要更加小心(而不是记录 activity 的 类)。在 fork 中,它需要以在 vfork 中没有的方式与 parent 进程合作。我们知道如何做这两件事,但不知道如何决定。

我知道我可能可以拦截 fork/vfork/clone 调用,并将 fork/vfork/mapping 状态存储为标志,但如果有 API 调用child 可以判断自己的状态。

加分:理想情况下,我还需要在库中选择任何已经完成 fork 或 vfork 的地方,然后回调到我们的代码中。这怎么可能发生?我们至少有一个库提供了 popen-like API,其中客户端 call-back 在 exec 之前从 fork child 调用。显然 call-back 的效用在 vfork 中受到很大限制。

所有不是专门为在 vfork() 下工作而设计的代码在 vfork() 下都不起作用。

从技术上讲,您可以通过调用 mmap() 并检查内存映射是否由 parent 进程继承来检查您是否处于 vfork() child /proc。不要写这段代码。这是一个非常糟糕的主意,没有人应该使用它。确实,判断您是否在 vfork() child 中的最佳方法是传递该信息。但重点来了。你打算用它做什么?

作为 vfork() child,您不能做的事情包括调用 fprintf()puts()fopen() 或任何其他标准 I/O 函数,也不是 malloc() 。除非代码经过精心设计,否则您最好根本不要调用您的日志框架,如果经过精心设计,您也不需要知道。更好的设计很可能是在首先调用 vfork() 之前记录您的意图。

您在评论中询问有关调用 fork() 的库,然后返回到您的代码中。那已经有点糟糕了。但是任何库都不应该 曾经 调用 vfork() 并返回到您的代码中,而没有明确记录这样做。 vfork() 是一个受限环境,调用不希望出现在该环境中的东西确实不应该发生。

如果您是由 vfork 创建的,您的 parent 将等待您终止。不然还是运行。这是一些非常难看的代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

void doCheck()
{
    char buf[512];
    sprintf(buf, "/proc/%d/wchan", (int) getppid());
    int j = open(buf, O_RDONLY);
    if (j < 0) printf("No open!\n");
    int k = read(j, buf, 500);
    if (k <= 0) printf("k=%d\n", k);
    close(j);
    buf[k] = 0;
    char *ptr = strstr(buf, "vfork");
    if (ptr != NULL)
       printf("I am the vfork child!\n");
    else
       printf("I am the fork child!\n");   
}

int main()
{
    if (fork() == 0)
    {
       doCheck();
       _exit(0);
    }
    sleep(1);
    if (vfork() == 0)
    {
        doCheck();
        _exit(0);
    }
    sleep(1);
}

这并不完美,parent 可能正在等待后续 vfork 调用完成。

一个简单的解决方案可以使用 pthread_atfork()。注册到此服务的回调仅在 fork() 时触发。因此,函数的第三个参数(在 fork 之后的 child 进程中调用)可以更新全局变量。 child 可以检查变量,如果被修改,那么它已经分叉了:

/*
  Simple program which demonstrates a solution to
  make the child process know if it has been forked or vforked
*/
#include <pthread.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

pid_t forked;

void child_hdl(void)
{
  forked = getpid();
}


int main(void)
{
pid_t pid;

  pthread_atfork(0, 0, child_hdl);

  pid = fork();
  if (pid == 0) {
    if (forked != 0) {
      printf("1. It is a fork()\n");
    }
    exit(0);
  }

  // Father continues here
  wait(NULL);

  pid = vfork();
  if (pid == 0) {
    if (forked != 0) {
      printf("2. It is a fork()\n");
    }
    _exit(0);
  }

  // Father continues here
  wait(NULL);

  return 0;
}

Build/execution:

$ gcc fork_or_vfork.c
$ ./a.out
1. It is a fork()

我今天遇到 kcmp,看起来它可以回答基本问题 - 即两个 tids 或 pids 是否共享同一个 VM。如果你知道它们代表分叉的 parent/child pids,这也许可以告诉你它们是否是 vfork()ed。

当然,如果它们是同一进程组中的 tids,那么根据定义它们将共享 VM。

https://man7.org/linux/man-pages/man2/kcmp.2.html

int syscall(SYS_kcmp, pid_t pid1, pid_t pid2, int type,
               unsigned long idx1, unsigned long idx2);

KCMP_VM 检查进程是否共享相同的地址space。 忽略参数 idx1 和 idx2。见 clone(2).

中 CLONE_VM 标志的讨论