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

C 读取文件时创建不需要的子进程,c,multiprocessing,C,Multiprocessing,我正在创建一个多进程程序。当我尝试在for循环中使用if(f==0)break调用fork()时。我得到了所需数量的子进程 但是现在,我正在处理一个输入文件,并且最初不知道所需的进程数。下面是我的代码的最小可能示例 FILE* file = fopen("sample_input.txt", "r"); while(fscanf(file, "%d", &order) == 1){ f = fork(); if(f == 0){ break;

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

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

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

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

编辑:我对控制台输出进行了一些调试,子进程确实打破了循环。然而,家长不断地反复阅读一个小文件。如果删除
fork()
,循环将按预期执行6次


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

读取文本文件中每个字符的次数等于创建的进程数。进程总数=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
1.
Hello from Child!
Hello from Parent!
     (or)
2.
Hello from Parent!
Hello from Child!
请参见另一个示例:

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; 
}
输出:

Gwapo ko
Gwapo ko
Gwapo ko
Gwapo ko
Gwapo ko
Gwapo ko
Gwapo ko
Gwapo ko
1.
Hello from Child!
Hello from Parent!
     (or)
2.
Hello from Parent!
Hello from Child!
创建子进程后,fork()在子进程中返回0,并向父进程返回正整数。 在这里,两个输出是可能的,因为父进程和子进程同时运行。所以我们不知道操作系统是否首先控制关闭父进程或子进程的进程

重要提示:父进程和子进程运行相同的程序,但这并不意味着它们是相同的。操作系统为这两个进程分配不同的数据和状态,并且这些进程的控制流也可能不同


理论:可能是关闭的进程是子进程而不是父进程,留下了父进程和其他子进程。

当第一个进程读取第一个数字时,它实际上会将整行数据读取到内存中。这个过程是分岔的

子进程打破了循环;接下来会发生什么并没有指定,但它可能会退出。父进程现在读取第二个数字并再次分叉。同样,子级退出,父级读取第三个数字、forks等

读取第六个数字并且第六个子项退出后,父项将从文件中读取另一个缓冲区。在Linux上(或者更准确地说,使用GNUC库),您会得到一些奇怪的效果。有关详细信息,请参见中的讨论。但是,退出的子级将文件描述符的读取位置调整回起始位置,以便父级可以再次读取更多数据

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


GLIBC Bug 23151 GLIBC—具有未关闭文件的分叉进程在退出前不执行lseek,并可能导致父I/O中出现无限循环

该漏洞创建于2019-05-08美国/太平洋地区,并于2018-05-09年因无效而关闭。理由是:

请阅读 , 特别是这一段:

请注意,在一个
fork()
之后,在一个句柄之前存在的地方存在两个句柄。[……]


有关此问题的详细讨论,请参阅。

@Someprogrammerdude您所说的最终错误是什么意思?您可以尝试在循环结束时重置errno,并在循环开始时进行检查,以查看当循环读取超过6次时,是否有任何结果复制了您的行为。但是,如果我用
fclose
取消对该行的注释,它将正常工作。除非在循环后需要文件,否则只需关闭它就可以“修复”问题。直接解决第二个理论:文件句柄指向同一内核空间结构的子进程退出时不会导致任何不希望出现的/异常的/&c。行为。@charlesduff:直到上周,我都会同意你的。但是,请参见,其中显示了Linux上的GNU C库的特殊行为。这与文件I/O的while循环有何关系?代码中的循环在子进程中有一个
中断。子进程本身不会调用
fork
并创建新进程。我添加了一些细节。我认为这不会有什么帮助所有这些父子关系与循环文件I/O有什么关系?我认为这基本上回答了这个问题。但是,找到问题的根源,也许有助于解决问题,从一开始就可以防止这种奇怪的效果。我想最简单的方法是使用您的代码和/或其他问答中的代码针对GNU C库创建一个bug。理论上,我们应该根据bug列表验证这是一个新bug,但不管怎样,仅仅进入bug是很有诱惑力的。bug报告可以合理地交叉引用这两个问题,但应该包括至少一个说明问题的程序的代码。bug 23151已被视为无效而拒绝。我已经把关于它的讨论和解释放在了,并把它作为它的复制品结束了。这都是一系列不明显的后果。在这段代码中,您可能应该在
fork()之前执行
fflush(file)
fflush(NULL)