一个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 标志的讨论
在 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 标志的讨论