6

I tried to see what happens if I read something from keyboard while I have multiple processes with fork() (in my case there are two children and a parent) and I discovered the following problem: I need to tell the parent to wait for children's processes, otherwise the program behaves strangely.
I did a research and I found that the problem is with the parent, he needs to wait for the child's process to end because if the parent's process ends first somehow he closes the STDIN, am I right? But also I found that every process has a copy of STDIN so my question is:

Why it works this way and why only the parent has the problem with STDIN and the children not, I mean why if the child's process ends first doesn't affect STDIN but if the parent's process ends first it does affect STDIN?

  • Here are my tests:

    1. I ran the program without wait() and after I typed a number the program stopped, but then I pressed enter two more times and the other two messages from printf() appeared. Here is the picture.

    2. When I ran the program with wait() everything worked fine, every process called scanf() separately and read a different number. Here is the picture.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
IonutC
  • 75
  • 4
  • in general, when preparing to 'fork()' a new process, it is advisable to also call `dup()` for stdin, stdout, stderr. OF course, that is not mandatory, but will avoid the problem your seeing. ALSO, closing the parent first makes the child process into a zombie. Zombies are very difficult to 'kill' without rebooting the computer. The parent can use `wait()` (or better, `waitpid()`) to wait for the child to complete. The typical practice is for the parent to handle stdin, stdout, etc and pass messages to child processes via `popen()` (or similar) connection to the child – user3629249 Dec 27 '16 at 17:43
  • @user3629249 That is completely bogus. – o11c Dec 27 '16 at 18:35
  • The process that has 'focus' will get the input from the keyboard. That is why it is better for the child to receive their input from the parent. – user3629249 Dec 27 '16 at 18:39
  • Well I'd not say bogus, but completely irrelevant. – Tanmay Dec 27 '16 at 18:42

2 Answers2

6

Well, a lot of stuff is going on here. I will try to explain it step by step.


When you start your terminal, the terminal creates a special file having path /dev/pts/<some number>. Then it starts your shell (which is bash in this case) and links the STDIN, STDOUT and STDERR of the bash process to this special file. This file is called a special file because it doesn't actually exist on your hard disk. Instead, whatever you write to this file, it goes directly to the terminal and the terminal renders it on the screen. (Similarly, whenever you try to read from this file, the read blocks until someone types something at the terminal).

Now when you launch your program by typing ./main, bash calls the fork function in order create a new process. The child process execs your executable file, while the parent process waits for the child to terminate. Your program then calls fork twice and we have three processes trying to read their STDINs, ie the same file /dev/pts/something. (Remember that calling fork and exec duplicates and preserves the file descriptors respectively).

The three processes are in race condition. When you enter something at the terminal, one of the three processes will receive it (99 out of 100 times it would be the parent process since the children have to do more work before reaching scanf statement).

So, parent process prints the number and exits first. The bash process that was waiting for the parent to finish, resumes and puts the STDIN into a so called "non-canonical" mode, and calls read in order to read the next command. Now again, three processes (Child1, Child2 and bash) are trying to read STDIN.

Since the children are trying to read STDIN for a longer time, the next time you enter something it will be received by one of the children, rather than bash. So you think of typing, say, 23. But oops! Just after you press the 2 key, you get Your number is: 2. You didn't even press the Enter key! That happened because of this so called "non-canonical" mode. I won't be going into what and why is that. But for now, to make things easier, use can run your program on sh instead of bash, since sh doesn't put STDIN into non-canonical mode. That will make the picture clear.


TL;DR

  • No, parent process closing its STDIN doesn't mean that its children or other process won't be able to use it.

  • The strange behavior you are seeing is because when the parent exits, bash puts the pty (pseudo terminal) into non-canonical mode. If you use sh instead, you won't see that behavior. Read up on pseudo terminals, and line discipline if you want to have a clear understading.

  • The shell process will resume as soon as the parent exits.

  • If you use wait to ensure that parents exits last, you won't have any problem, since the shell won't be able to run along with your program.

  • Normally, bash makes sure that no two foreground processes read from STDIN simultaneously, so you don't see this strange behavior. It does this by either piping STDOUT of one program to another, or by making one process a background process.

    Trivia: When a background process tries to read from its STDIN, it is sent a signal SIGTTIN, which stops the process. Though, that's not really relevant to this scenario.

Community
  • 1
  • 1
Tanmay
  • 821
  • 7
  • 9
  • Could you explain what non-canonical mode is? – Mad Physicist Dec 27 '16 at 19:28
  • @Tanmay, excellent answer, now I have a better idea of how this works, it helped me a lot. I used `sh` and it worked as you said. But I have two more questions that I want to clarify. First, you said that after the parent exits there will be three processes (Child1, Child2 and bash) that are trying to read STDIN. Is it correct that these three processes are running in foreground and it is possible in a special case for the `bash` to read STDIN before the two children? – IonutC Dec 28 '16 at 00:51
  • And secondly, you said that normally, `bash` makes sure that no two foreground processes read from STDIN simultaneously. Here you meant in general, and you told me how he does it, right? I mean, in my example, `bash` takes care alone to not let two processes read from STDIN simultaneously, I don't need to do anything extra for it, am I right? – IonutC Dec 28 '16 at 00:59
  • @Zamolxe Yes, the three processes (Child1, Child2 and bash) are running in the foreground, and who will be able to read depends on the implementation of pseudo terminal, which is implemented in a FIFO way in case of linux. – Tanmay Dec 28 '16 at 09:14
  • And yes, I meant that in general when you run multiple commands on bash simultaneously (eg `a | b` or `a & b`), bash makes sure that no two foreground commands are reading from `STDIN` simultaneously. But in this case, its your responsibility to make sure that the children cooperate with the parent and avoid reading from `STDIN` simultaneously. – Tanmay Dec 28 '16 at 09:18
1

There are several issues that can happen when multiple processes try to do I/O to the same TTY. Without code, we can't tell which may be happening.

  • Trying to do I/O from a background process group may deliver a signal: SIGTTIN for input (usually enabled), or SIGTTOU for output (usually disabled)

  • Buffering: if you do any I/O before the fork, any data that has been buffered will be there for both processes. Under some conditions, using fflush may help, but it's better to avoid buffering entirely. Remember that, unlike output buffering, it is impossible to buffer input on a line-by-line basis (although you can only buffer what is available, so it may appear to be line-buffered at first).

  • Race conditions: if more than one process is trying to read the same pipe-like file, it is undefined which one will "win" and actually get the input each time it is available.

o11c
  • 15,265
  • 4
  • 50
  • 75
  • SIGTTIN is sent if a background process tries to read STDIN. That's not the case here. – Tanmay Dec 27 '16 at 19:09
  • @Tanmay Not in the question - image links don't count. – o11c Dec 27 '16 at 20:05
  • "image links don't count." What do you mean? Can you cite a meta post or rule? – Tanmay Dec 28 '16 at 09:25
  • 1
    @Tanmay It's very well known. The MCVE close reason includes, emphasis original, "must include the ... code ... **in the question itself**" – o11c Dec 28 '16 at 16:29