在main函数之前调用fork函数

14

POSIX标准允许在main()之前调用fork()吗?比如在C++的静态实例中或__attribute__((constructor))的C函数中?


9
__attribute__并非标准C语言或POSIX规范,而C++是一种不同的语言。 - too honest for this site
1
为什么你甚至想在main()之前调用fork()函数? - EOF
3个回答

7

fork命令的文档中没有说明它是被禁止的,我也想不到任何理由会这样。

实际上,对于POSIX来说,main 没有任何特殊之处。只是C语言选择以该名称的函数开始程序,而C ++则几乎类似地做了同样的事情。但就POSIX而言,一旦进程启动,进程就启动了。 它可以用任何一种旧的语言编写,fork仍然必须工作。

在C中(而不是C++!),你无法编写在main之前执行的代码(因为在该上下文中,static变量的初始化器必须是常量),因此对于C来说,这有点无关紧要。 但是,暂时离开C抽象,仍然没有POSIX阻止编译器供应商在C运行时中包含在进入 main之前执行 fork的代码。请记住,"真正"的入口点实际上不是 main ; "真正"的入口点执行一些库初始化等操作,然后调用main来开始程序的部分。


5
POSIX标准允许在main()之前调用fork()吗?这取决于你的意思。对于C程序,POSIX通过纳入C标准规定C程序启动时,环境(操作系统)会调用main()。由此产生的POSIX定义语义从那一点开始;根据定义,它们不包括先前对任何其他函数的调用。如果实际上发生了任何这样的调用,则它们在这个意义上不是“程序”的一部分,因此该程序不能在main()之前调用fork()。另一方面,POSIX并没有明确禁止在定义的C语义开始之前,运行C程序所在的进程执行fork()或任何其他函数。在未被禁止的情况下,POSIX允许这样做。如果你的问题是关于GCC的__attribute__((constructor))或类似设施是否违反了POSIX,那么答案是否定的,尽管它们的行为没有被POSIX定义。第三方面,POSIX并没有肯定地允许它,并且没有定义任何C源代码指定它应该发生的方式。如果你的问题是关于POSIX是否定义了一个特定的设施,__attribute__((constructor))或类似设施利用该设施提供其广告行为,或者它是否要求符合规范的系统必须提供这样的设施操作机制,那么答案也是否定的。不提供这样的机制不会本质上导致系统不符合POSIX。对于C++程序,POSIX是以shell命令语言和ISO C为基础定义的。它不要求实现提供任何C++支持,但当然也不禁止它。因此,前一节的第二段和第三段适用于C++程序的所有方面。值得一提的是,与C一样,C++指定程序从main()开始。这在C++中具有类似的含义,但与C不同,C++实现可以提供机制,通过该机制可以使用户提供的代码在执行main()的第一条语句之前运行。具有静态持续时间的变量的构造函数可能在执行main()的第一条语句之前运行,但是否这样做是实现定义的(C++11,3.6.2/4)。在这个意义上,因此,在执行main()的第一条语句之前调用fork()是否包括在C++程序语义中是实现定义的。
请注意,C++标准的措辞非常谨慎,以避免涉及此类初始化是否可以在进入main()之前进行 - 虽然这可能不重要,但至少有几种方法可以区分它们。
其他程序
POSIX对于其他类型的程序比它对C++程序更少的涉及,除了shell脚本。当然,shell脚本通常根本没有main(),它们也无法直接访问C库来显式地fork()

2

在libc完全加载和初始化后,您可以使用fork。

一个简单的测试程序可以证明这是真的:

 #include <stdio.h>

 void before(void) __attribute__ (( constructor ));
 void after(void) __attribute__ (( destructor ));

 int main(void)
 {
   puts("main");

   return 0;
 }

 void before(void)
 {
   puts("before");
 }

 void after(void)
 {
   puts("after");
 }

输出结果为:
 $ LD_DEBUG=files ./a.out
      26032:    
      26032:    file=libc.so.6 [0];  needed by ./a.out [0]
      26032:    file=libc.so.6 [0];  generating link map
      26032:      dynamic: 0x00007f58c7703ba0  base: 0x00007f58c7341000   size: 0x00000000003c8a00
      26032:        entry: 0x00007f58c7361950  phdr: 0x00007f58c7341040  phnum:                 10
      26032:    
      26032:    
      26032:    calling init: /lib/x86_64-linux-gnu/libc.so.6
      26032:    
      26032:    
      26032:    initialize program: ./a.out
      26032:    
 before
      26032:    
      26032:    transferring control: ./a.out
      26032:    
 main
      26032:    
      26032:    calling fini: ./a.out [0]
      26032:    
 after

库已加载,初始化完成,因此可以安全地使用标记构造函数的fork。


问题是关于 POSIX 允许 什么。你的程序在特定环境下表现出所期望的行为并不说明 POSIX 是否允许该行为,也绝不证明它所展示的实践在任何有意义的方面都是“安全”的。 - John Bollinger
@JohnBollinger:问题更简单,问题是“在使用由gcc或clang定义的构造函数属性之前,我可以安全地在POSIX环境中使用库(POSIX或stdc或其他)函数吗?”这更多关于加载机制而不是POSIX问题。 - Picodev
考虑到问题文本以“POSIX标准是否允许[...]”开头,并且标记为[language-lawyer],我有点不知道您如何将其解释为与POSIX的规定无关。 - John Bollinger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接