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:
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.
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
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.