C语言 子进程是否在fork()后共享来自父进程的stdio流(FILE *)偏移量?

fjaof16o  于 2023-04-19  发布在  其他
关注(0)|答案(1)|浏览(121)

所以我知道,正如fork手册中所述,forked子进程拥有父进程的打开fd的副本,因此父进程和子进程共享文件偏移量。
Stdio流构建在fd函数之上,但它们包含每个进程唯一的缓冲区,因此在分叉时,子进程继承了那些FILE*,但它们是否共享相同的缓冲区和相同的文件偏移量?
我已经搜索了手册和这个论坛,但没有找到任何关于FILE* 的信息,只有关于文件描述符的信息。如果这个问题是重复的,很抱歉,但我找不到它。

c86crjj0

c86crjj01#

正如fork()manual和你在帖子中所说:
子文件继承父文件的打开文件描述符集的副本。子文件中的每个文件描述符引用与父文件中相应文件描述符相同的打开文件描述(参见open(2))。这意味着两个文件描述符共享打开文件状态标志、文件偏移量和信号驱动的I/O属性(参见fcntl(2)中对F_SETOWN和F_SETSIG的描述)。
FILE是OS文件描述符数据结构之上的数据结构。它基本上是内存中的动态分配,在分叉时从父进程复制到子进程。因此,该数据结构中的任何缓冲数据都将在子进程中复制。并且它们都可以随时将其刷新到目标文件中。由于文件偏移量在父进程和子进程之间共享,这将触发输出上的重复打印。考虑以下程序作为示例:

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

int main(void)
{

  printf("String to be printed on stdout");

  if (fork() == 0) {

    // Child
    fflush(stdout);
    
  } else {

    // Father
    fflush(stdout);

    wait(NULL);
  }

}

当运行它时,我们可以看到最初从父亲调用的单个printf()产生的两个打印结果:

$ gcc try.c
$ ./a.out 
String to be printed on stdoutString to be printed on stdout$

当然,任何将来在子端或父端的打印将在输出上混合,因为它们都将移动相同的底层文件偏移量。

备注:C库中stdio子系统的目标是通过使用一个中间缓冲区来最小化系统调用的数量,I/O首先进入这个缓冲区。(需要刷新缓冲区满以将数据附加到输出文件中,查找操作超出缓冲区当前Map的边界,显式刷新请求...)。因此,如果其中一个进程调用rewind()ftell(),这可能会导致实际的系统调用,这取决于调用进程中缓冲区的状态。只有当系统调用完成时,另一个进程才会受到影响。库调用和等效的系统调用之间不存在1/1对应关系。通常,库调用将多于系统调用(例如,多个fwrite()可能仅触发一个write()系统调用)。

相关问题