【问题标题】:EOF reached before end of fileEOF 在文件结束之前到达
【发布时间】:2026-01-31 02:15:01
【问题描述】:

我正在为学校制作一个程序,其中我有一个多进程程序,其中每个进程读取文件的一部分,它们一起工作以计算文件中的单词数。我遇到了一个问题,如果有超过 2 个进程,那么所有进程在读取文件的一部分之前从文件中读取 EOF。以下是相关代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char *argv[]) {

    FILE *input_textfile = NULL;
    char input_word[1024];
    int num_processes = 0;
    int proc_num = 0; //The index of this process (used after forking)
    long file_size = -1;

    input_textfile = fopen(argv[1], "r");
    num_processes = atoi(argv[2]);

    //...Normally error checking would go here

    if (num_processes > 1) {

        //...create space for pipes

        for (proc_num = 0; proc_num < num_processes - 1; proc_num++) {

            //...create pipes

            pid_t proc = fork();

            if (proc == -1) {
                fprintf(stderr,"Could not fork process index %d", proc_num);
                perror("");
                return 1;
            } else if (proc == 0) {
                break;
            }

            //...link up the pipes
        }
    }

    //This code taken from http://*.com/questions/238603/how-can-i-get-a-files-size-in-c
    //Interestingly, it also fixes a bug we had where the child would start reading at an unpredictable place
    //No idea why, but apparently the offset wasn't guarenteed to start at 0 for some reason
    fseek(input_textfile, 0L, SEEK_END);
    file_size = ftell(input_textfile);
    fseek(input_textfile, proc_num * (1.0 * file_size / num_processes), 0);

    //read all words from the file and add them to the linked list
    if (file_size != 0) {

        //Explaination of this mess of a while loop:
        //  if we're a child process (proc_num < num_processes - 1), then loop until we make it to where the next
        //  process would start (the ftell part)
        //  if we're the parent (proc_num == num_processes - 1), loop until we reach the end of the file
        while ((proc_num < num_processes - 1 && ftell(input_textfile) < (proc_num + 1) * (1.0 * file_size / num_processes))
                || (proc_num == num_processes - 1 && ftell(input_textfile) < file_size)){
            int res = fscanf(input_textfile, "%s", input_word);

            if (res == 1) {
                //count the word
            } else if (res == EOF && errno != 0) {
                perror("Error reading file: ");
                exit(1);
            } else if (res == EOF && ftell(input_textfile) < file_size) {
                printf("Process %d found unexpected EOF at %ld.\n", proc_num, ftell(input_textfile));
                exit(1);
            } else if (res == EOF && feof(input_textfile)){
                continue;
            } else {
                printf("Scanf returned unexpected value: %d\n", res);
                exit(1);
            }
        }
    }

    //don't get here anyway, so no point in closing files and whatnot

    return 0;
}

使用 3 个进程运行文件时的输出:

All files opened successfully
Process 2 found unexpected EOF at 1323008.
Process 1 found unexpected EOF at 823849.
Process 0 found unexpected EOF at 331776.

导致错误的测试文件:https://dl.dropboxusercontent.com/u/16835571/test34.txt

编译:

gcc main.c -o wordc-mp

并运行为:

wordc-mp test34.txt 3

值得注意的是,只有那个特定的文件给我带来了问题,但错误的偏移量不断变化,所以它不是文件的内容。

【问题讨论】:

  • Jonathan 的猜测可能是正确的,但在寻求调试帮助时,您应该始终发帖 Minimal Complete Verifiable Example
  • 好的,我会努力完成的
  • @user3386109 完成。链接在编辑文本中。
  • 我不喜欢成为坏消息的承担者,但是......您需要edit问题,并将所有必要信息放在问题中本身。因此,我将删除您当前拥有的代码,并将其替换为 MCVE 代码。至于文本文件,可能没有必要把它放在问题中。我假设任何旧文本文件(包含单词)都可以。所以留下一个文本文件的链接是可以的。
  • 看起来即使我们在课堂上进行了测试,但问题实际上是在分叉之前打开了文件。我想我们在测试期间很幸运,因为文件太小了。无论如何,将打开文件移到程序的后面就足以解决我的问题(尽管代码仍然不起作用,但现在有其他原因)。谢谢你们的帮助。

标签: c linux scanf


【解决方案1】:

您在分叉之前创建了文件描述符。子进程继承文件描述符,它指向与父进程相同的文件描述,因此,与其中一个子进程一起前进会使所有子进程的光标前进。

从“man fork”,你可以得到确认:

  • 子进程是用一个线程创建的——那个 那 称为 fork()。父级的整个虚拟地址空间是 在孩子中复制,包括互斥体的状态、条件 变量和其他 pthreads 对象; pthread_atfork(3) 的使用 可能有助于处理这可能导致的问题。

  • 子继承父的打开文件描述集的副本- 托尔斯。 child 中的每个文件描述符都指向同一个 open 文件描述(参见 open(2))作为相应的文件描述符 在父母。这意味着两个描述符共享打开的文件 状态标志、当前文件偏移量和信号驱动的 I/O 属性 (参见 fcntl(2) 中对 F_SETOWN 和 F_SETSIG 的描述)。

【讨论】:

  • 我已经做到了,但是我们在课堂上进行了测试,我也测试了自己,无论是孩子还是父母的进步都不会在其他任何人身上进步。另外,您可以看到每个进程到达 EOF 时在文件中的位置,并且它们都不是文件的实际结尾。
  • 当我打开文件并修复它时,实际上继续尝试移动。不知道为什么它在课堂测试中起作用,但无论如何,谢谢你的回答!很抱歉最初把它写下来。
  • 如您所见,这记录在 fork 的手册页中。在分叉保留虚拟内存(制作完整副本)时,文件描述符与系统相关,并且文件描述符(它是指向文件描述的指针)被复制但引用相同的描述(未复制)。
  • 是的,对不起,我在那里看到了,这就是我最初的想法,但他在课堂上展示了这个例子,让我不这么认为。