在fork和(failed)exec之后更改C文件指针 [英] C file pointer changing after fork and (failed) exec

查看:55
本文介绍了在fork和(failed)exec之后更改C文件指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我制作了制作叉子的程序,我认为孩子不会影响父母.

I made program which make fork and I think child does not affect parent.

但是文件指针已更改,尽管我未对父级进行任何更改.

But file pointer is changed although I did not made any changes in the parent.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void) {
    FILE *fp = fopen("sm.c", "r");
    char buf[1000];
    char *args[] = {"invailid_command", NULL};

    fgets(buf, sizeof(buf), fp);
    printf("I'm one %d %ld\n", getpid(), ftell(fp));
    if (fork() == 0) {
        execvp(args[0], args);
        exit(EXIT_FAILURE);
    }
    wait(NULL);
    printf("I'm two %d %ld\n", getpid(), ftell(fp));
} 

此输出

I'm one 21500 20
I'm two 21500 -1

我想使文件指针在两个 printf 调用之间不改变.

And I want to make file pointer not change between two printf calls.

为什么文件指针改变了,即使 execvp 失败了,我也可以使文件指针不变吗?

Why does the file pointer change and can I make the file pointer unchangeable even though execvp fails?

推荐答案

感谢乔纳森·莱夫勒(Jonathan Leffler)为我们指明正确的方向.

Credit to Jonathan Leffler for pointing us in the right direction.

尽管您的程序在CentOS 7/GCC 4.8.5/GLIBC 2.17上不会对我产生相同的意外行为,但您可能会观察到不同的行为.根据POSIX(您依赖于 fork 的依赖),您的程序的行为实际上是 undefined .以下是摘录自相关部分(强调)的摘录:

Although your program does not produce the same unexpected behavior for me on CentOS 7 / GCC 4.8.5 / GLIBC 2.17, it is plausible that you observe different behavior. Your program's behavior is in fact undefined according to POSIX (on which you rely for fork). Here are some excerpts from the relevant section (emphasis added):

可以通过文件描述符访问打开的文件描述,使用诸如 open() pipe()之类的函数创建,或通过使用诸如 fopen() popen()之类的函数创建的流.文件描述符或流在打开时称为句柄"它所引用的文件描述;打开的文件说明可能具有几个句柄.

An open file description may be accessed through a file descriptor, which is created using functions such as open() or pipe(), or through a stream, which is created using functions such as fopen() or popen(). Either a file descriptor or a stream is called a "handle" on the open file description to which it refers; an open file description may have several handles.

[...]

涉及任何一个句柄的函数调用的结果(活动句柄")在POSIX.1-2017的此卷的其他地方定义,但是如果使用了两个或多个句柄,其中任何一个都是流,申请应确保他们的行为被协调为如下面所描述的.如果不这样做,结果是不确定的.

The result of function calls involving any one handle (the "active handle") is defined elsewhere in this volume of POSIX.1-2017, but if two or more handles are used, and any one of them is a stream, the application shall ensure that their actions are coordinated as described below. If this is not done, the result is undefined.

[...]

要使句柄成为活动句柄,应用程序应确保在上一次使用之间执行以下操作句柄(当前活动的句柄),第二个的第一次使用句柄(将来的活动句柄).然后,第二个手柄变为活动手柄.[...]

For a handle to become the active handle, the application shall ensure that the actions below are performed between the last use of the handle (the current active handle) and the first use of the second handle (the future active handle). The second handle then becomes the active handle. [...]

句柄不必在同一过程中应用这些规则.

The handles need not be in the same process for these rules to apply.

请注意,在 fork()之后,存在两个句柄,其中一个以前存在.如果两个句柄都可以使用,则应用程序应确保访问时,它们都处于另一种可能成为活动句柄优先.[在符合上述条件的前提下,]应用程序应准备 fork()就像是改变了主动手柄一样.(如果唯一的操作由进程之一执行的是exec函数之一,或者 _exit()(不是 exit()),在该过程中永远不会访问该句柄.)

Note that after a fork(), two handles exist where one existed before. The application shall ensure that, if both handles can ever be accessed, they are both in a state where the other could become the active handle first. [Where subject to the preceding qualification, the] application shall prepare for a fork() exactly as if it were a change of active handle. (If the only action performed by one of the processes is one of the exec functions or _exit() (not exit()), the handle is never accessed in that process.)

对于第一个手柄,适用以下第一个适用条件.[大量不适用于OP情况的替代方案...]

For the first handle, the first applicable condition below applies. [An impressively long list of alternatives that do not apply to the OP's situation ...]

  • 如果使用允许读取的模式打开流,并且基础打开文件描述是指能够执行以下操作的设备:寻找,应用程序应执行 fflush()或流应关闭.
  • If the stream is open with a mode that allows reading and the underlying open file description refers to a device that is capable of seeking, the application shall either perform an fflush(), or the stream shall be closed.

第二个句柄:

  • 如果显式更改了文件偏移的函数已使用了先前的任何活动句柄,则上述要求除外.第一个句柄,应用程序应执行 lseek() fseek()(如适当的手柄类型)到合适的位置.
  • If any previous active handle has been used by a function that explicitly changed the file offset, except as required above for the first handle, the application shall perform an lseek() or fseek() (as appropriate to the type of handle) to an appropriate location.

因此,为了使OP的程序能够在父级和子级中访问相同的流,POSIX要求在分叉之前,父级 fflush() stdin 启动后 fseek().然后,在等待子进程终止之后,父进程必须 fseek()流.鉴于我们知道孩子的exec将会失败,但是,通过让孩子使用 _exit()(不访问流)而不是 exit().

Thus, for the OP's program to access the same stream in both parent and child, POSIX demands that the parent fflush() stdin before forking, and that the child fseek() it after starting. Then, after waiting for the child to terminate, the parent must fseek() the stream. Given that we know the child's exec will fail, however, the requirement for all the flushing and seeking can be avoided by having the child use _exit() (which does not access the stream) instead of exit().

遵守POSIX的规定将产生以下结果:

Complying with POSIX's provisions yields the following:

遵循这些规则时,无论句柄顺序如何使用时,实现应确保一个应用程序,甚至一个由多个过程组成,应产生正确的结果:无数据写入时应丢失或重复,所有数据应按顺序编写,除非寻求者要求.

When these rules are followed, regardless of the sequence of handles used, implementations shall ensure that an application, even one consisting of several processes, shall yield correct results: no data shall be lost or duplicated when writing, and all data shall be written in order, except as requested by seeks.

但是,值得注意的是,

是实现定义了是否以及在什么条件下所有输入只能看到一次.

It is implementation-defined whether, and under what conditions, all input is seen exactly once.


我很高兴仅听到您对程序行为的期望没有相关标准证明是合理的,但这确实是全部.父进程和子进程确实具有一些相关的共享数据,它们具有共同的打开文件描述的形式(与它们具有单独的句柄相关联),对于意外的(和未定义)的行为,但没有依据来预测您看到的特定行为,也没有依据我在同一程序中看到的不同行为.


I appreciate that it may be somewhat unsatisfying to hear merely that your expectations for program behavior are not justified by the relevant standards, but that's really all there is. The parent and child processes do have some relevant shared data in the form of a common open file description (with which they have separate handles associated), and that seems likely to be the vehicle for the unexpected (and undefined) behavior, but there's no basis for predicting the specific behavior you see, nor the different behavior I see for the same program.

这篇关于在fork和(failed)exec之后更改C文件指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆