我制作了制作叉子的程序,我认为孩子不会影响父母。
但文件指针已更改,但我没有对父进行任何更改。
I'm one 21500 20
I'm two 21500 -1
此输出
printf
我想让两个execvp
调用之间的文件指针不变。
为什么文件指针会发生变化,即使configuration.node.global should be a boolean.
失败,我也可以使文件指针不变;
答案 0 :(得分:3)
感谢Jonathan Leffler指出我们正确的方向。
虽然您的程序在CentOS 7 / GCC 4.8.5 / GLIBC 2.17上不会产生相同的意外行为,但您可能会发现不同的行为。根据POSIX(您依赖fork
),您的程序的行为实际上是 undefined 。以下是the relevant section的一些摘录(强调补充):
可以通过文件描述符访问打开的文件描述, 这是使用
open()
或pipe()
等功能创建的 流,使用fopen()
或popen()
等函数创建的。 文件描述符或流被称为"句柄"在开放 它所引用的文件描述;一个打开的文件描述可能有 几个把手。[...]
涉及任何一个句柄的函数调用的结果("活动的 handle")在本卷POSIX.1-2017的其他地方定义,但是如果 使用两个或更多个句柄,其中任何一个都是一个流,即 申请应确保其行为得到协调 如下面所描述的。 如果不这样做,结果是未定义的。
[...]
要使手柄成为活动手柄,应用程序应确保 以下操作是在最后一次使用之间执行的 handle(当前活动句柄)和第二次使用 handle(未来的活动句柄)。然后第二个手柄成为 主动手柄。 [...]
处理这些规则的手柄不需要在同一个过程中。
请注意,在
fork()
之后,存在两个句柄,其中之前存在一个句柄。 如果两个手柄都可以,应用程序应确保 访问,他们都处于另一个可能成为的状态 积极处理第一。 [在符合前述资格的情况下,]申请应准备fork()
就好像它是活动句柄的变化一样。 (如果唯一的行动 由其中一个进程执行的是exec函数之一或_exit()
(不是exit()
),永远不会在该过程中访问句柄。)对于第一个句柄,适用以下第一个适用条件。 [令人印象深刻的一系列替代方案不适用于OP的情况......]
- 如果使用允许读取的模式打开流,则底层打开文件描述指的是能够读取的设备 寻求,申请应执行
fflush()
,或 溪流应该关闭。对于第二个句柄:
- 如果显式更改了文件偏移量的函数使用了任何先前的活动句柄,则上面的要求除外 第一个句柄,应用程序应执行
lseek()
或fseek()
(如 适合于手柄的类型)到适当的位置。
因此,为了让OP程序在父级和子级中访问相同的流,POSIX要求父级fflush()
stdin
在分叉之前,以及子级{{1}它开始后。然后,在等待孩子终止后,父母必须fseek()
流。然而,鉴于我们知道孩子的执行失败,可以通过让孩子使用fseek()
(不访问流)而不是{{1}来避免对所有刷新和寻求的要求。 }}
遵守POSIX的规定会产生以下结果:
遵循这些规则,无论句柄顺序如何 使用,实现应确保应用程序,甚至一个 由几个过程组成,应产生正确的结果:没有数据 写作时应丢失或重复,所有数据均为 除非被搜查要求,否则按顺序书写。
但值得注意的是
是的 实现 - 定义是否以及在什么条件下所有输入 只看到一次。
我理解,仅仅听到您对计划行为的期望没有得到相关标准的证明,这可能有点令人不满意,但这确实存在。父进程和子进程确实具有一些相关的共享数据,这些共享数据采用公共开放文件描述的形式(它们具有相关的单独句柄),并且这似乎可能是意外的车辆(和未定义的行为,但是没有基础可以预测您看到的具体行为,也没有我在同一程序中看到的不同行为。
答案 1 :(得分:0)
我能够使用gcc 5.4.0在Ubuntu 16.04上重现这一点。这里的罪魁祸首是exit
以及子进程的创建方式。
exit
的手册页说明了以下内容:
exit()函数导致正常的进程终止和值 地位与0377将返回父级(请参阅wait(2))。
使用atexit(3)和on_exit(3)注册的所有函数都是 按照其注册的相反顺序调用。 (有可能的 对于其中一个函数,使用atexit(3)或on_exit(3) 注册在退出处理期间执行的附加功能; 新注册被添加到列表的前面 剩下要调用的函数。)如果其中一个函数有 不返回(例如,它调用_exit(2),或用a杀死自己 信号),然后没有剩余的功能被调用,进一步 退出处理(特别是刷新stdio(3)流) 被遗弃了。如果功能已多次注册 使用atexit(3)或on_exit(3),然后调用它多次 它被注册了。
刷新并关闭所有打开的stdio(3)流。创建的文件 删除tmpfile(3)。
C标准指定了两个常量,EXIT_SUCCESS和 EXIT_FAILURE,可以传递给exit()以表示成功或 不成功终止。
因此,当您在孩子中致电exit
时,会关闭FILE
所代表的fp
。
通常,在创建子进程时,它会获取父进程的文件描述符的副本。然而,在这种情况下,孩子的记忆似乎仍然在物理上指向父母的记忆。因此,当exit
关闭FILE
时,它会影响父级。
如果您将子项更改为_exit
,则会关闭子项的文件描述符,但设法不触及FILE
对象,并且父项中对ftell
的第二次调用将成功。最好在非执行的孩子中使用_exit
,因为它会阻止在孩子中调用atexit
个处理程序。