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

时间:2018-05-09 02:13:37

标签: c multiprocessing

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

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

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

2 个答案:

答案 0 :(得分:10)

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

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

在读取第六个数字并退出第六个子节点后,父节点将从文件中读取另一个缓冲区。在Linux上(或者更准确地说,使用GNU C库),您会得到一些奇怪的效果。请参阅Why does forking my process cause the file to be read infinitely?中的讨论以查看详细信息。但是,退出的子项会将文件描述符的读取位置调整回到开头,因此父级可以再次读取更多数据。

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

GLIBC Bug 23151

GLIBC Bug 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()之后,存在两个句柄,其中之前存在一个句柄。 [...]

请参阅Why does forking my process cause the file to be read infinitely?对此进行广泛讨论。

答案 1 :(得分:0)

读取文本文件中每个字符的次数等于创建的进程数。进程总数= 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,将正整数返回到父进程。 这里有两个输出,因为父进程和子进程同时运行。因此,我们不知道操作系统是否首先控制父进程或子进程将关闭的进程。

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

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