简体   繁体   中英

Does read() wait in linux?

I'm learning about pipe() system call in Linux. Here is the code snippet I have question about:

#define MSGSIZE 16
char* msg1 = "hello, world #1";
char* msg2 = "hello, world #2";
char* msg3 = "hello, world #3";

int main()
{
    char inbuf[MSGSIZE];
    int p[2], i;

    if (pipe(p) < 0)
        exit(1);

    int pid=fork();
    if(pid==0){
        sleep(2);  ///Making parent process sleep so that child process moves ahead
        write(p[1], msg1, MSGSIZE);
        write(p[1], msg2, MSGSIZE);
        write(p[1], msg3, MSGSIZE);
        printf("Done writing");
    }
    else{
        printf("child process\n");
        for (i = 0; i < 3; i++) {
            read(p[0], inbuf, MSGSIZE);
            printf("%s\n", inbuf);
        }
    }
    return 0;
}

I get output as follows

child process
hello, world #1
hello, world #2
hello, world #3
Done writing

As we see child process has started before write calls where completed. Here it seems that read call is waiting for something.I couldn't find docs in man pages regarding this behaviour. Can someone explain whats happening here in easy terms?

By default, read() blocks until there is input available, or an end of input occurs.

POSIX (IEEE Std 1003-1) standard says that a pipe can hold at least 512 bytes of data. (This means that write() will succeed immediately, even though the data is still in the pipe, and not yet read by the other end.)

Your main process writes the data to the pipe and immediately exits. Because the child process has the read end still open, it can read the data written by the main process later on.

Keyword: Synchronization

The output of your terminal is shared by two processes. How the terminal is handling the synchronization internally, who knows, but you could synchronize your output of your processes (eg by shared memory and a mutex or semaphore). So do not rely on the sequence of your output on what was called first and who is waiting for whom.

Or simply use a timestamp for each invocation of printf .

I've answered a similar question here: How Multi-threaded program works in C? .

I took the liberty to polish your code a bit, in particular:

Parent and child processes

I modified your IF statement to correcly identify the parent and child process. From the fork() man page, concerning it's return values:

On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately.

In this case it wasn't a big deal but it could definately lead to some problems down the road.

Closing pipes

I closed the pipe ends both when unused and when done reading/writing. See why it's important here: Why should you close a pipe in linux

Exiting and waiting

I made the parent wait for the child's termination in order not to leave an orphaned process which (in more complex and extreme situations) could starve the system for resources.

Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define MSGSIZE 16

char* msg1 = "hello, world #1";
char* msg2 = "hello, world #2";
char* msg3 = "hello, world #3";

int main()
{
  char inbuf[MSGSIZE];
  int p[2], i;

  if (pipe(p) < 0)
    exit(1);

  int pid = fork();
  if (pid > 0) {
    // Parent process
    
    close(p[0]);  // Closing unused pipe

    //sleep(2);  
    write(p[1], msg1, MSGSIZE);
    write(p[1], msg2, MSGSIZE);
    write(p[1], msg3, MSGSIZE);
    close(p[1]);  // Closing pipe after all writes are done
    printf("Parent done writing\n");

    wait(NULL);
  } else if (pid == 0) {
    // Child process

    close(p[1]);  // Closing unused pipe
    printf("Child process\n");
    for (i = 0; i < 3; i++) {
      read(p[0], inbuf, MSGSIZE);
      printf("%s\n", inbuf);
    }
    close(p[0]);

    exit(0);  // Child has terminated succesfully, exit.

  } else {
    fprintf(stderr, "Fork error.\n");
    return 1;
  }

  return 0;
}

The simple answer is that read waits until some input is available.

More precisely, files can be opened in blocking or non-blocking mode. Blocking mode is the default. You can choose non-blocking mode by passing O_NONBLOCK in the flags argument to open (eg open(filename, O_RDONLY | O_NONBLOCK) ) or by calling fcntl(fd, F_SETFL, O_NONBLOCK) after opening the file.

In blocking mode, read normally waits until it has read at least one byte. Note that it does not wait until it has read as many bytes as requested, even if that many bytes are available. How many bytes are returned in a single read call can depend on the whims of the process scheduler. “Normally” means that you can write a loop like

do n = read(…);
while (n > 0 || (n < 0 && errno == EINTR));

and be confident that it isn't going to be a tight loop. Note the case errno == EINTR , which indicates that read was interrupted before read anything. (This isn't conveyed by returning 0, which would be more logical, because returning 0 indicates the end of the file.)

If the file is in nonblocking mode, another case where nothing has been read is errno == EAGAIN . In non-blocking mode, if no data is available, read returns immediately, so a loop similar to the one above would be bad.

All of this applies to write as well, except for 0 meaning end-of-file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM