2

Let us say I have .c file and corresponding executable (let us say foo) that takes in some input from stdin and then makes a sys call to /bin/sh. Feeding input to this executable like following:

python -c "<some script to feed input>" | ./foo

I observed that the shell invoked by foo closes immediately and foo terminates. However, someone on IRC recommended that executing like this:

(python -c "<some script to feed input>"; cat) | ./foo

keeps the subshell invoked by ./foo still running. I would like to know what is going on here.

My Speculation: For the first case, Does the stdout of the python script close as soon as it's done thus sending EOF to the stdin of the process ./foo terminating ./foo? But that does not make sense to me as the sys call should be a blocking one leading to ./foo not terminating. I would appreciate help and a pointer to resources to fix erroneous understanding. Thanks!

0

1 Answer 1

3

There is no such thing as “sending EOF”. End-of-file is reached when there is no more data to read.

In the first snippet, once the Python script exits, there is no more process that has the pipe open for writing. Therefore, once foo has finished reading all the data that was sent on the pipe, it detects an end-of-file condition on its standard input.

In the second snippet, once the Python script exits, the pipe is still open by the subshell. Therefore, even if foo finishes reading all the data produced by the Python script, it will keep waiting for more input. The subshell launches cat, which reads and forwards data from its own standard input. Once cat exits, the situation is the same as before: there is no more process that has the pipe open for writing and foo will reach the end of its input.

When a process is blocked in a read system call, there are several reasons why that system call might return:

  • If data is available, read returns a positive value and stores that many bytes in the buffer passed to it.
  • If the end of the file has been reached, read returns 0. For a regular file, this happens when the file position has reached the end of the file. For a pipe, this happens when the last process that had the file open dies and the pipe's buffer has been fully consumed.
  • If some error occurs, read returns -1.
4
  • Ok that makes sense. Does each subshell get it's own standard input stream and output stream? Also in the first case, ./foo calls system(/bin/sh). Shouldn't this sys call block in the first case? Also, why is the stdin of the process ./foo the same as the shell invoked by it?
    – iart
    Jun 9, 2014 at 0:20
  • @RohitRamprasad Standard input, standard output and other file descriptors are inherited by subprocesses. They change only when a process changes them explicitly; for example, the redirection and pipe operators in the shell cause the shell to manipulate descriptors just before invoking the command. Jun 9, 2014 at 0:27
  • This makes a lot of sense. Thanks! What about the system call to /bin/sh. Shouldn't it also block in the first case? This is the last thing that is confusing me!
    – iart
    Jun 9, 2014 at 0:30
  • @RohitRamprasad The fact that foo calls some other program (such as a shell) doesn't change anything. The shell reads from standard input, so it will block if the end of file has not been reached, and return immediately if the end of file has been reached. Jun 9, 2014 at 0:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .