I am implementing a shell-like program in C++. It has a loop that reads from cin, forks, and waits for the child.
This works fine if the input is interactive or if it's piped from another program. However, when the input is a bash heredoc, the program rereads parts of the input (sometimes indefinitely).
I understand that the child process inherits the parent's file descriptors, including shared file offset. However, the child in this example does not read anything from cin, so I think it shouldn't touch the offset. I'm kind of stumped about why this is happening.
test.cpp:
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
std::string line;
while (std::getline(std::cin, line)) {
pid_t pid = fork();
if (pid == 0) { // child
break; // exit immediately
}
else if (pid > 0) { // parent
waitpid(pid, nullptr, 0);
}
else { // error
perror("fork");
}
std::cout << getpid() << ": " << line << "\n";
}
return 0;
}
I compile it as follows:
g++ test.cpp -std=c++11
Then I run it with:
./a.out <<EOF
hello world
goodbye world
EOF
Output:
7754: hello world
7754: goodbye world
7754: goodbye world
If I add a third line foo bar
to the input command, the program gets stuck in an infinite loop:
13080: hello world
13080: goodbye world
13080: foo bar
13080: o world
13080: goodbye world
13080: foo bar
13080: o world
[...]
Versions:
- Linux kernel: 4.4.0-51-generic
- Ubuntu: 16.04.1 LTS (xenial)
- bash: GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu)
- gcc: g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
std::ios::sync_with_stdio(false);
at the beginning and explicitly flush after your write to stdout? (e.g. change'\n'
tostd::endl
) – Okraexit(0);
but not_exit(0);
. – Preemptionfclose(stdin)
etc at the beginning of the child, to prevent exit from seeking in the first place. – Softheartedcin
after forking (I don't remember if you can do that; if not, you could set its badbit or something)? What if you rewrite the program to use the C io routines? – Okracin
buffer, maybe twice. – Okra_exit
'ing orquick_exit
'ting from the forked child if the child doesn'texec
. The parent builds upcout
buffer state and the children inherit it. If the children exit regularly, they will attempt to flush their copy of thecout
buffer which should be getting flushed in the parent. If this happens, you will get duplicates in your output. – Vertebratefork
if this is a concern. – Sharkeystd::cin.setstate(std::ios::failbit);
to the beginning of the child. It also happens when I rewrite the program to use getline and printf (compiled with both c++11 and c11). And the problem goes away when I make the child sleep instead of calling exit. So it seems like exit is the culprit here. – Softheartedclose(0)
before exiting. – Sharkey