Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C, how does the read function "know" when there is nothing left to read?

In the below program (which is not mine, but one which I have modified), the child process makes two writes to the pipe. When I run the program, I get the following output:

Received string: Hello, world!
This is the child process.

How is it that the read performed by the parent process captures both of these strings from the pipe buffer? What (if anything) prevents the parent process from assuming after it has read the first string (or the first char of the first string for that matter) that there is nothing else to read from the buffer, and exiting?

Program in question:

int main(void)
{
    int     fd[2], nbytes;
    pid_t   childpid;
    char    string[] = "Hello, world!\n";
    char    string2[] = "This is the child process.\n";
    char    readbuffer[80];

    pipe(fd);

    if((childpid = fork()) == -1)
    {
            perror("fork");
            return 1;
    }

    if(childpid == 0)
    {
            /* Child process closes pipe's input file descriptor */
            close(fd[0]);

            /* Send "string" through the output side of pipe */
            write(fd[1], string, (strlen(string)));
            write(fd[1], string2, (strlen(string2)+1));
            return 1;
    }
    else
    {
            /* Parent process closes pipe's output file descriptor */
            close(fd[1]);

            /* Read in a string from the pipe */
            nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
            printf("Received string: %s", readbuffer);
    }

    return 0;
}
like image 323
sav0h Avatar asked Aug 08 '15 22:08

sav0h


People also ask

How does read know when to stop?

The rule is quite simple. For a blocking socket, the read will wait until there is some data to read, that could be 1 byte or 1MB. The kernel will then copy over as much data at the kernel has in its internal buffer, or the number of bytes requested, whichever is the smaller.

How does read function work in C?

The read() function reads data previously written to a file. If any portion of a regular file prior to the end-of-file has not been written, read() shall return bytes with value 0. For example, lseek() allows the file offset to be set beyond the end of existing data in the file.

What does the read function return?

Definition and Usage. The read() method returns the specified number of bytes from the file. Default is -1 which means the whole file.


2 Answers

The key here is the read() buffer size 80.

Usually read() block the process which is calling it (is set in a sleeping status) until certain conditions doesn't happen, for example:

  • the requested buffer is full
  • there's less data than requested (EOF) or an error occurred
  • a signal wake the process (as ^C), this may be your case, the child process exit and the system send a broken pipe signal to the parent (the process wake and read() get the whole buffer)

Note that those conditions depends on the subsystem you are reading from, in your case a pipe. Subsystem which may have different properties, like buffer size. An absurd example: if pipe buffer size on kernel side was less than or equal to your first write, the reading process would have wake up earlier returning a truncated buffer.

like image 179
Alex Avatar answered Nov 29 '22 09:11

Alex


I love this question. It makes me remember old time ...

Meta-answer: It does only know, if the stream is closed

Answer: your process reads all it can/has to read, no end stops at '\n'

But I suppose, you want to write/read records.

Despite others I try to answer without distinguishing types of streams. I make a disclaimer here:

it all depends on OS and stream type - and the options opening on both (!) sides

Basic:

Lets say one process writes an other reads - easy enough.

No is not.

Imagine - always ;-) - they put there chars (bytes) into the stream as they want and can - slowly - fast - one by one or all they buffered.

and you read it - no - not you and not your program - in between - byte by byte or on block or how the layer in between likes.

So if you want a record, there a only three possibilities:

  1. make an end char(byte) - block seperator,
  2. make the records fix length,
  3. the old two-byte-length then read the rest

Answer again:

in your case the child puts all chars in the stream the parent read them and over - why should one of them make a break?

Solution:

Depending on the OS and language and libs, you sometimes can tell the OS to make an / accept an end char (like \n) - sometimes your write read functions do that for you

like image 45
halfbit Avatar answered Nov 29 '22 09:11

halfbit