4

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

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

我知道我可能会拦截 fork/vfork/clone 调用,并将 fork/vfork/mapping 状态存储为一个标志,但如果有一个 API 调用孩子可以进行以确定其自己的状态,它会使生活变得更简单。

额外标记:理想情况下,我还需要在库中找到任何已完成 fork 或 vfork 的地方,然后将其回调到我们的代码中。那怎么会发生呢?我们拥有的至少一个库提供了一个类似 popen 的 API,其中客户端回调在 exec 之前从 fork 子调用。显然,该回调的实用性在 vfork 中受到很大限制。

4

3 回答 3

6

所有不是专门设计用于工作的代码vfork()都不能在vfork().

从技术上讲,您可以vfork()通过调用mmap()并检查内存映射是否由/proc. 不要编写此代码。这是一个非常糟糕的主意,没有人应该使用它。真的,判断你是否怀孕的最好方法vfork()是传递这些信息。但这里的妙语来了。你打算用它做什么?

您小时候不能做的事情vfork()包括调用fprintf()puts()fopen()或任何其他标准 I/O 函数,也不能malloc()就此而言。除非代码设计得非常仔细,否则你最好不要调用你的日志框架,如果它是精心设计的,你不需要知道。更好的设计很可能是vfork()在首先调用之前记录您的意图。

您在评论中询问有关库调用的内容fork(),然后返回您的代码。这已经有点糟糕了。但是,任何库都不应该在没有明确记录的情况下调用vfork()并返回到您的代码中。vfork()是一个受限制的环境,调用不应该在那个环境中的事情真的不应该发生。

于 2021-03-09T17:04:25.100 回答
1

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

/*
  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;
}

构建/执行:

$ gcc fork_or_vfork.c
$ ./a.out
1. It is a fork()
于 2021-03-09T21:23:55.203 回答
-3

如果您是由 创建的vfork,您的父母将等待您终止。否则,它仍在运行。这是一些非常丑陋的代码:

#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);
}

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

于 2021-03-09T17:11:39.387 回答