See Stack Overflow Q&A: * https://stackoverflow.com/questions/50110992/why-does-forking-my-process-cause-the-file-to-be-read-infinitely * https://stackoverflow.com/questions/50244579/unwanted-child-processes-being-created-while-file-reading As the accepted answer to the "why does forking my process" question shows, when the child process exits, it seeks on the file descriptor towards the beginning of the file, thus changing the position of the read position in the (shared) open file description, potentially leading to an infinite loop in the parent process as it never encounters EOF. The output from strace (strace -ff -o st-out -- neof97) for the children shows: $ cat st-out.80833 lseek(0, -63, SEEK_CUR) = 21 exit_group(0) = ? +++ exited with 0 +++ $ cat st-out.80834 lseek(0, -42, SEEK_CUR) = -1 EINVAL (Invalid argument) exit_group(0) = ? +++ exited with 0 +++ $ cat st-out.80835 lseek(0, -21, SEEK_CUR) = 0 exit_group(0) = ? +++ exited with 0 +++ $ cat st-out.80836 exit_group(0) = ? +++ exited with 0 +++ $ One version of the code that shows the problem is (neof97.c). It limits the 'infinite input' to 30 lines. #include "posixver.h" #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> enum { MAX = 100 }; int main(void) { if (freopen("input.txt", "r", stdin) == 0) return 1; char s[MAX]; for (int i = 0; i < 30 && fgets(s, MAX, stdin) != NULL; i++) { // Commenting out this region fixes the issue int status; pid_t pid = fork(); if (pid == 0) { exit(0); } else { waitpid(pid, &status, 0); } // End region printf("%s", s); } return 0; } The file input.txt can be any text; 4 lines of 20 random characters each is sufficient to show the bug. If the file is explicitly closed in the child, the lseek() does not occur which avoids the problem. The problem was reproduced in a Ubuntu 16.04 LTS system running as a VM under VMWare Fusion on a MacBook Pro running macOS 10.13.4 (High Sierra). When the same code was recompiled and run on the macOS host operating system, the problem was not exhibited.
Please read <http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05_01>, especially this paragraph: Note that after a fork(), two handles exist where one existed before. The application shall ensure that, if both handles can ever be accessed, they are both in a state where the other could become the active handle first. The application shall prepare for a fork() exactly as if it were a change of active handle. (If the only action performed by one of the processes is one of the exec functions or _exit() (not exit()), the handle is never accessed in that process.)