Reputation: 4567
I am trying to understand pipes in Linux. Wrote a basic code which will try to write to writing end of the file descriptor twice and then perform read twice. On the second read it is blocking.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
int filedes[2];
char buffer1[] = "hello pipe1";
char buffer2[] = "hello pipe2";
char readbuffer[30] = {};
if (pipe(filedes) == 0) {
printf("Pipe successful\n");
printf("read from %d, write to %d\n", filedes[0], filedes[1]);
write(filedes[1], buffer1, sizeof(buffer1));
perror("write");
write(filedes[1], buffer2, sizeof(buffer2));
perror("write");
read(filedes[0], readbuffer, sizeof(readbuffer));
printf("read:%s\n", readbuffer);
read(filedes[0], readbuffer, sizeof(readbuffer));
printf("read:%s\n", readbuffer);
close(filedes[1]);
close(filedes[0]);
} else {
perror("pipe failed");
}
return 0;
}
I get output as "hello pipe1" and then in the second read call it blocks What happened to the second buffer data. Is it lost?
Upvotes: 2
Views: 1326
Reputation: 754
The first read
actually consumes all the data from both write
s. The second call to read
blocks because it didn't find any data available and is waiting for some to appear.
Why doesn't printf()
display all of the data? Each call to write()
sends a null terminator (\0
). printf
's s
specifier stops printing when it first encounters null.
To see this in detail, start by looking closely at the data in each buffer:
char buffer1[] = "hello pipe1";
printf("strlen(buffer1): %zu\n", strlen(buffer1));
printf("sizeof(buffer1): %zu\n", sizeof(buffer1));
strlen(buffer1): 11
sizeof(buffer1): 12
The buffer actually contains:
hello pipe1\0
The compiler allocates 12 bytes: 11 bytes of text and 1 byte for the null terminator.
Checking the return value of write
show that it actually writes all 12 bytes:
ssize_t n_written = write(filedes[1], buffer1, sizeof(buffer1));
printf("Number of bytes written: %zd\n", n_written);
n_written: 12
It does this because write
's count
argument tells it to:
write(filedes[1], buffer1, sizeof(buffer1));
// ^^^^^^^^^^^^^^^ write the entire buffer, including null
You can change this to write only the contents of the string:
write(filedes[1], buffer1, strlen(buffer1));
// ^^^^^^^^^^^^^^^ write only the string's contents,
// excluding null
but then you need to be sure that readbuffer
is null terminated. Something like this is a start:
ssize_t nread = read(filedes[0], readbuffer, sizeof(readbuffer)-1);
if (nread > 0)
readbuffer[nread] = '\0';
Note that read
's count
argument is saving space for the null terminator.
Upvotes: 1
Reputation: 12296
Your readbuffer is 30 bytes, so this call:
read(filedes[0], readbuffer, sizeof(readbuffer));
will read up to 30 bytes.
You are writing 24 bytes (11 chars for each string plus 1 byte for the nul terminator). The read
will read all those bytes. Nothing is lost.
The issue occurs here:
printf("read:%s\n", readbuffer);
readbuffer looks like this (where \0
is the nul terminator):
hello pipe1\0hello pipe2\0
When printf hits the first nul, it treats that as the end of the string, so only "hello pipe1" will be printed.
If you change the readbuffer size to sizeof(buffer1)
, this should work as expected (assuming both output strings are the same size).
Upvotes: 2