C文件指针在叉和(失败)后更改

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (24)

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

但是文件指针被更改了,尽管我没有在父文件中做任何更改。

#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打电话。

为什么文件指针会改变,而我可以使文件指针即使是不可更改的?execvp失败了?

提问于
用户回答回答于

虽然在CentOS 7/GCC 4.8.5/GLIBC 2.17上,程序不会产生相同的意外行为,但可以观察到不同的行为。

可以通过文件描述符访问打开的文件描述,该描述符是使用以下函数创建的open()pipe(),或者通过一个流创建,该流是使用以下函数创建的fopen()popen().文件描述符或流在它所引用的打开的文件描述上称为“句柄”;一个打开的文件描述可能有几个句柄。..涉及任何一个句柄(“活动句柄”)的函数调用的结果在POSIX1.2017本卷的其他部分中定义,但如果使用了两个或多个句柄,且其中任何一个句柄是流,应用程序应确保它们的操作协调如下所述。如果未完成此操作,则结果未定义.若要使句柄成为活动句柄,应用程序应确保在句柄的最后一次使用(当前活动句柄)和第二句柄第一次使用(未来活动句柄)之间执行以下操作。然后,第二个句柄变成活动句柄。..句柄不需要处于应用这些规则的相同过程中。fork()应用程序应确保,如果两个句柄都可以被访问,则两个句柄都处于一种状态,而另一个句柄可以首先成为活动句柄。在符合上述资格的情况下,申请须拟备fork()就好像是活动句柄的变化。如果其中一个进程执行的唯一操作是其中一个exec函数,则为_exit()(不是exit()**),在这个过程中,句柄永远不会被访问。*)对于第一个句柄,下面的第一个适用条件适用。一长串令人印象深刻的不适用于“任择议定书”情况的备选方案.

  • 如果流是以允许读取的模式打开的,并且底层打开的文件描述是指能够查找的设备,则应用程序应执行fflush(),否则溪流将关闭。

对于第二个手柄:

  • 如果任何先前的活动句柄已被显式更改文件偏移量的函数所使用,除非上述对第一个句柄有要求,则应用程序应执行lseek()fseek()(根据手柄的类型)到适当的位置。

因此,为了使OP的程序在父流和子流中访问相同的流,POSIX要求父程序fflush()stdin在分叉之前,那个孩子fseek()然后,在等待孩子终止后,父母必须fseek()考虑到我们知道孩子的主管会失败,那么通过让孩子使用,就可以避免对所有的冲洗和寻觅的要求。_exit()(它不访问流)而不是exit()

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

当遵循这些规则时,无论使用的句柄顺序如何,实现都应确保应用程序,即使是由多个进程组成的应用程序,也应产生正确的结果:在写入时不应丢失或重复任何数据,并且所有数据都应按顺序写入,除非请求执行。

然而,值得注意的是,

它是实现定义的,是否和在什么条件下,所有的输入都被精确地看到一次。

用户回答回答于

我用GCC 5.4.0在Ubuntu16.04上复制了这个结果。exit结合创建子进程的方式。

手册页exit声明如下:

EXIT()函数将导致正常进程终止,并且Status&0377的值将返回给父进程(请参见等待(2)。_出口(3)被调用,其登记顺序相反。(这些函数中的一个可以在退出(3)或ON上使用)_Exit(3)注册在退出处理期间要执行的附加函数;新注册被添加到待调用的函数列表的前面。如果其中一个函数不返回(例如,它调用_退出(2),或用信号杀死自己),则不再调用其余的函数,并放弃进一步的退出处理(特别是Stdio(3)流的刷新)。如果一个函数已多次使用atexport(3)或ON注册。_退出(3),那么它被调用的次数和注册的次数一样多。所有打开的Stdio(3)流都被刷新和关闭。由tmpfile(3)创建的文件被删除。C标准指定两个常量,Exit_成功与退出_Failure,可以传递给EXIT()以分别指示成功或不成功的终止。

exit在孩子身上,它关闭了FILE代表fp

通常,当创建子进程时,它会获得父进程的文件描述符的副本。但是,在这种情况下,子进程的内存似乎仍然物理地指向父进程。exit关闭FILE这影响了父母。

如果你把孩子改为打电话_exit,它将关闭子文件描述符,但不尝试FILE对象和对ftell在父母那里会成功的。这是一个很好的做法_exit在一个非执教的孩子身上因为它可以防止atexit处理程序在子节点中被调用。

所属标签

可能回答问题的人

  • 天使的炫翼

    17 粉丝531 提问36 回答
  • 富有想象力的人

    3 粉丝0 提问30 回答
  • 学生

    3 粉丝476 提问28 回答
  • o o

    4 粉丝494 提问27 回答

扫码关注云+社区

领取腾讯云代金券