文件读取时创建了不需要的子进程

我正在创建一个多进程程序。 当我尝试使用if(f == 0) break;在for循环中调用fork() if(f == 0) break; 。 我得到了所需数量的子进程。

但是现在,我正在处理输入文件,并且最初不知道所需的进程数。 这是我的代码中最小的可能示例。

 FILE* file = fopen("sample_input.txt", "r"); while(fscanf(file, "%d", &order) == 1){ f = fork(); if(f == 0){ break; } } 

示例sample_input.txt

 5 2 8 1 4 2 

现在正在创建数千个子进程(我想要6,文件中的整数),可能是什么原因? 它与文件指针有关吗?

编辑:我用控制台输出做了一些调试,子进程确实打破了循环。 但是,父母一直在阅读一个小文件。 如果我删除fork() ,循环按预期执行6次。

编辑2:我有一个理论,我无法certificate它也许你可以帮助我。 可能是这样的情况:文件指针在进程之间共享,当子进程退出时,它关闭文件,当父进程再次尝试读取时,它只是从头开始(或其他一些奇怪的行为)。 可能是这样吗?

当第一个进程读取第一个数字时,它实际上将整行读入内存。 过程分叉。

子进程打破了循环; 接下来发生的事情未指定,但可能会退出。 父进程现在读取第二个数字并再次分叉。 再次,孩子退出,父母读取第三个数字,叉子等。

读取第六个数字并退出第六个子节点后,父节点将从文件中读取另一个缓冲区。 在Linux上(或者更准确地说,使用GNU C库),您会得到一些奇怪的效果。 请参阅为什么分支我的进程导致文件被无限读取的讨论? 看到细节。 但是,退出的子项将文件描述符的读取位置调整回开始,因此父级可以再次读取更多数据。

我对另一个问题的回答表明,如果子进程在退出之前关闭文件,则不会发生此行为。 (无论如何不应该发生,但根据经验确实如此。)


GLIBC错误23151

GLIBC 错误23151 – 具有未关闭文件的分叉进程在退出之前执行lseek并且可能导致父I / O中的无限循环。

该错误创建于2019-05-08美国/太平洋,并于2018-05-09关闭为无效。 给出的理由是:

请阅读http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05_01 ,尤其是本段:

请注意,在fork() ,存在两个句柄,其中之前存在一个句柄。 […]

请参阅为什么分叉我的进程会导致文件被无限读取? 对此进行了广泛的讨论。

读取文本文件中每个字符的次数等于创建的进程数。 进程总数= 2n,其中n是fork系统调用的数量。 所以这里n = 3,2 ^ 3 = 8

让我们为这三行添加一些标签名称:

 fork (); // Line 1 fork (); // Line 2 fork (); // Line 3 L1 // There will be 1 child process / \ // created by line 1. L2 L2 // There will be 2 child processes / \ / \ // created by line 2 L3 L3 L3 L3 // There will be 4 child processes // created by line 3 

例:

 int main() { fork(); fork(); fork(); printf("Gwapo ko\n"); return 0; } 

输出:

 Gwapo ko Gwapo ko Gwapo ko Gwapo ko Gwapo ko Gwapo ko Gwapo ko Gwapo ko 

检查另一个例子:

 void forkexample() { // child process because return value zero if (fork()==0) printf("Hello from Child!\n"); // parent process because return value non-zero. else printf("Hello from Parent!\n"); } int main() { forkexample(); return 0; } 

输出:

 1. Hello from Child! Hello from Parent! (or) 2. Hello from Parent! Hello from Child! 

创建子进程,fork()在子进程中返回0,将正整数返回到父进程。 这里,两个输出是可能的,因为父进程和子进程同时运行。 因此,我们不知道操作系统是否首先控制父进程或子进程将关闭的进程。

重要:父进程和子进程正在运行相同的程序,但这并不意味着它们是相同的。 OS为这两个进程分配不同的数据和状态,并且这些进程的控制流也可以不同。

理论:可能是封闭的流程是子流程而不是父流程,而是父流程和其他子流程。