Read from pipe even if the write end is closed - c

my teacher said that if the writing end of a pipe is closed, the child process can no longer read from the read end of the pipe and a read would generate a BROKEN _PIPE error. However, I can't get this code to generate any error while reading on the closed tube :
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
int main(void) {
int pipefd[2];
char c;
pipe(pipefd);
if (fork() == 0) {
close(pipefd[1]);
sleep(5);
// The parent has already closed pipefd[1]
while (read(pipefd[0], &c, 1)) {
printf("%c", c);
}
close(pipefd[0]);
return 0;
}
close(pipefd[0]);
char str[] = "foo";
write(pipefd[1], str, 4);
close(pipefd[1]);
return 0;
}
The output on stdout after 5 seconds is foo. So what I understand is that closing the write end just add EOF after the characters already there and DOES NOT send EOF on any forthcoming read (so the child can read all the characters already sent). Am I right ?

As you have found out, your teacher is wrong.
You do not get a broken pipe "error" (which is actually a combination of a signal, SIGPIPE, and an error EPIPE if that is ignored), when you try to read from a broken pipe, but when you attempt to write to a broken pipe.
For Linux systems, you can read more about this here, or you can take a look at the BSD man page pipe(2).

Related

FILE * streams with dup2()-ed descriptors — stdin not working

After forking a child and dub2()-ing its stdin descriptor to the read-end of a pipe (its write-end is in the parent process) reading with read(0,...) (descriptor based) works fine. But reading with fgets(stdin,...) (stream based) does not work. Why?
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(){
char string[]="MY TEST STRING";
pid_t pid;
int bufSize=80;
char rBuf[bufSize];
int downlink[2], wrlen=0, rdlen=0, status;
memset(rBuf,0,bufSize);
if (pipe (downlink) == -1){
printf("Error with pipe()\n");
exit(4);
}
pid=fork();
if (pid>0){ //parent
wrlen=wrlen+write(downlink[1], string, strlen(string)+1);
//dprintf(downlink[1],"%s", string);
sleep(6);
}
else if (pid == 0){ // child
dup2(downlink[0],STDIN_FILENO);
//rdlen=read(downlink[0], rBuf, bufSize); //works
//rdlen=read(STDIN_FILENO, rBuf, bufSize); //works
//scanf("%s", rBuf);fflush(stdin); //doesn't work, reads up to first blank
//scanf(stdin,"%s", rBuf);fflush(stdin); //doesn't work, reads up to first blank
fgets(rBuf, bufSize, stdin);fflush(stdin); //doesn't work
printf("c: %s", rBuf), fflush(stdout);
//status =execl("/usr/bin/octave","octave","--no-gui",NULL);
//status =execl("/usr/bin/man","man",NULL);
//printf("c: status%d", status), fflush(stdout);
}
else{ //error
printf("Error with fork()\n");
exit(4);
}
return 0;
}
In this code the fork()ed child is supposed to read from stdin (which is dub2()ed to downlink[0](=read-end of pipe from writing parent)) and printf() to stdout the received contets.
If the reading happens with read() (descriptor based) everything works fine. When reading with fgets() or scanf() (stream based) no data is printed.
What am I missing here?
fgets() reads a line, but your parent process never sends a line of text. So you need to add a newline to your string
char string[]="MY TEST STRING\n";
read() however just reads whatever is in the pipe when it becomes available - it does not try to read all the data it can up till a newline character, which is why you get data back when using read()
Even when you do not send a newline, fgets() would return when the write end of the pipe gets closed. However the pipe you create in your parent process gets copied into the child process.
That means that when the parent process exits, its write end of the pipe is closed - but not the write end of the pipe in the child process - leading to the pipe still being open when the parent exit.
So make sure you close() the write end of the pipe in your child process, as you don't need it:
else if (pid == 0){ // child
close(downlink[1]);
dup2(downlink[0],STDIN_FILENO);

Unix : What happens to a pipe(half-duplex) if the process dies

I am trying to prove one of my doubts, that two non-related processes can share the fd of half-duplex pipe and have communication.
I have created two programs for that. But then I had this another question, that what happens to the pipe if process dies?Because my reader got some garbage message, when I printed out the message.
Writer
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2];
char str[] = "hello\n";
if(pipe(fd) < 0)
perror("Pipe creation failed\n");
//Since i am a writer, i should close the reading end as a best practice
close(fd[0]);
/*
The processes need not to be related processes, in order to use the half duplex pipes. fd is just a number/identifier
which can be shared across different processes
*/
printf("Hey there !!! use this file descriptor for reading : %d\n", fd[0]);
//writing message
write(fd[1],str,strlen(str)+1);
return 0;
}
Reader
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd,bytesRead;
char buffer[1024];
printf("please enter the fd :");
scanf("%d",&fd);
bytesRead = read(fd,buffer,1024);
printf("Bytes Read : %d\nMessage : %s\n", bytesRead, buffer);
return 0;
}
You can't do this.
The table of file descriptors is per-process; every process has its own separate set of open file descriptors (note the distinction between open file descriptors, which are per-process, and open file descriptions, which are system-wide, discussed in open(2)). If you want to share a file descriptor between processes, you need to either inherit it over a fork(2) or pass it through a unix(7) domain socket via sendmesg(2) with SCM_RIGHTS in the cmesg(3) header.
(On Linux, you can also pass around paths to /proc/[PID]/fd/..., and other systems may have their own non-portable equivalents).
What is happening in your case is that the read is failing (you're giving it a file descriptor which is not open), leaving your buffer with uninitialized garbage. Since you don't check the return value, you never know that it failed.
In pipe man page,
pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe.
The pipe is mainly used for the related process(parent and child). You are not able to use the pipe for non related process.
In related process, one end is closed. In other end process gets the SIGPIPE signal.
Example program using pipe :-
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
The above program creates a pipe, and then fork(2)s to create a child process; the child inherits a duplicate set of file descriptors that refer to the same pipe. After the fork(2), each process closes the descriptors that it doesn't need for the pipe (see pipe(7)). The parent then writes the string contained in the program's command-line argument to the pipe, and the child reads this string a byte at a time from the pipe and echoes it on standard output.

fork and pipe confusion from quiz

This is a quiz from my class, and it invovles concept around fork and pipe. I just have a several confusions about this code.
1) What does if((pid = fork() == 0) means? is it just checking fork using pid(process id), why does loop start with this?
2)close (p[1]); what does this part mean? closing the first integer of array P?
3)The while loop start after close, does it mean it read into p[0]'s size if it is not zero?
4.The two write lines, what does that mean, and why are they both named 1? are they happening at the same time?
#include <stdio.h>
#include <stdlib>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int p[2];
int i, pid, status;
char buffer[20];
pipe(p);
if((pid = fork() == 0) {
close (p[1]);
while (( i = read (p[0], buffer, sizeof("abcdefghi"))) != 0)
{ buffer [i] = '\0';
printf("read %d bytes: %s\n", i, buffer);
}
close(p[0]);
exit (0);
}
write(p[1], "abcdefghi', sizeof("abcdefghi"));
write(p[1], "123456789', sizeof("123456789"));
close(p[0]);
close(p[1]);
while(wait(&status)!= pid);
return(0);
}
You really should RTFM but :-
fork() creates an identical copy of the current procedure running from the same line of code. The only difference between the two copies is the return code from fork(). This will be 0 if you are in the newly created copy or the process id of the newly created copy if you are in the original executable (or -1 if something went wrong).
pipe(p) creates a pipe and returns two file handles in the array "p". the first handle is the output from the pipe opened for reading, the second handle is the input to the pipe open for writing. So close(p[1]) closes the input to the pipe ( this is in the new process which reads from the pipe, it is considered good practice to close the file descriptor you are not using!)
The while loop is checking "i" the return code from the read from the pipe file, this will return 0 when there is nothing to read.

regarding the relation of close() and read()

int main()
{
char *msg="hello";
char buff[MAX];
int p[2];
pipe(p);
int i,pid=fork();
if(pid>0){
//close(p[1]);
read(p[0],buff, MAX);
}
else
{
printf("child exiting\n");
}
}
Why does the above code end up blocking ? But then if we remove the comment and place
close(p[1])
then why does the code end immediately ?
Once you create a pipe, it gets four ends:
A reading end p[0] in the parent process
A writing end p[1] in the parent process
A reading end p[0] in the child process
A writing end p[1] in the child process
UNIX will not deliver EOF to the reader unless both writing ends have been closed, because it knows that the pipe is still writeable.
When the child process exits, it closes both ends of the pipe on its side. However, the parent still has one writeable end open, so reading from the pipe blocks instead of delivering an EOF to the parent. That is why UNIX manual instructs to close the unused ends of the pipe right away:
An application that uses pipe(2) and fork(2) should use suitable close(2) calls to close unnecessary duplicate file descriptors; this ensures that end-of-file and SIGPIPE/EPIPE are delivered when appropriate.
Here is an example of how to make your program not block without closing p[1] on the parent side:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void* write_pipe(void* pp) {
int* p = (int*)pp;
char msg[] = "Hello from another thread!";
write(p[1], msg, sizeof(msg));
return NULL;
}
int main()
{
char buff[100];
int p[2];
pipe(p);
int pid=fork();
if(pid>0){
pthread_t thread1;
pthread_create (&thread1, NULL, &write_pipe, (void *)p);
read(p[0],buff, 100);
printf("%s\n", buff);
printf("parent exiting\n");
}
else
{
printf("child exiting\n");
}
return 0;
}
The code above writes to the writing end of the pipe from a thread within the parent process, instead of writing to it from the child process. This is a legitimate use of a pipe, too, illustrating why UNIX cannot deliver EOF unless the parent's writing end of the pipe is closed.
Read is a blocking call and it returns only when it receives EOF . If you wont close the write end of the pipe, read end wont get the EOF and hence,program will remain blocked

C: dup2, pipe and fork not working as expected

I'm trying to do a simple fork -> execute another program -> say "hello" to that child process -> read back something -> print what received.
The program used as child just waits for any line of input and prints something to the stdout like "hello there!"
This is my "host" program (that is not working):
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#define IN 0
#define OUT 1
#define CHILD 0
main ()
{
pid_t pid;
int pipefd[2];
FILE* output;
char buf[256];
pipe(pipefd);
pid = fork();
if (pid == CHILD)
{
printf("child\n");
dup2(pipefd[IN], IN);
dup2(pipefd[OUT], OUT);
execl("./test", "test", (char*) NULL);
}
else
{
sleep(1);
printf("parent\n");
write(pipefd[IN], "hello!", 10); // write message to the process
read(pipefd[OUT], buf, sizeof(buf));
printf("received: %s\n", buf);
}
}
I get this:
child
[.. waits 1 second ..]
parent
received:
What am I missing? Thanks!
EDIT (test.c):
By request, this is the child program:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int getln(char line[])
{
int nch = 0;
int c;
while((c = getchar()) != EOF)
{
if(c == '\n') break;
line[nch] = c;
nch++;
}
if(c == EOF && nch == 0) return EOF;
return nch;
}
main()
{
char line[20];
getln(line);
printf("hello there!", line);
fflush(stdout);
return 0;
}
You're always suppose to read from file-descriptor 0, and write to file-descriptor 1 with pipes ... you have this relationship reversed in the parent process. For what you're wanting to-do, you may end up needing two pipes for two-way communication between the parent and child that avoids situations where the parent ends up reading the contents it wrote to the pipe since process scheduling is non-deterministic (i.e., the child is not guaranteed to read what the parent wrote to the pipe if the parent is also reading from the same pipe since the parent could just end up writing and then reading with no interleaving of the child process to read what the parent wrote).
Change your code to the following:
main ()
{
pid_t pid;
int pipe_to_child[2];
int pipe_from_child[2];
FILE* output;
char buf[256];
pipe(pipe_to_child);
pipe(pipe_from_child);
pid = fork();
if (pid == CHILD)
{
printf("child\n");
//child process not using these ends of the pipe, so close them
close(pipe_to_child[1]);
close(pipe_from_child[0]);
dup2(pipe_to_child[0], fileno(stdin));
dup2(pipe_from_child[1], fileno(stdout));
execl("./test", "test", (char*) NULL);
}
else
{
sleep(1);
printf("parent\n");
write(pipe_to_child[1], "hello!\n", 10); // write message to the process
read(pipe_from_child[0], buf, sizeof(buf));
printf("received: %s\n", buf);
}
}
You need two pipes for this: one for the child process's stdin, and one for its stdout. You cannot reuse the two ends of a pipe as two pipes.
Also, this line of the parent program
write(pipefd[IN], "hello!", 10); // write message to the process
does not write a newline, so getln in the child will never return. (Furthermore, "hello!" has only six characters, but you are writing ten.)
You probably should use wait or waitpid.
It looks like you have your pipe descriptors mixed up. After calling pipe(), pipefd[0] is the read end of the pipe, and pipefd[1] is the write end of the pipe. You're writing to the read end, and reading from the write end.
Also, you're trying to use one pipe for both stdin and stdout of the child process. I don't think this is really what you want to do (you will need two pipes).
Looks like you have your IN/OUT backwards for the pipe -- pipefd[0] is the read end of the pipe, so writing to it (as the parent does) is nonsensical and will fail. Similarly pipefd[1] is the write end so reading from it (as the parent does) will also fail. You should ALWAYS check the return values of the read and write calls, to see if you're getting any errors
Others are saying that the pipe is mono-directional, which is what I thought at first. But actually that's not what my man page says:
A read from fildes[0] accesses the data written to fildes[1]
on a first-in-first-out (FIFO) basis and a read from
fildes[1] accesses the data written to fildes[0] also on a
FIFO basis.
However, this does mean that if the parent is writing to pipefd[0], then the child should read from pipefd[1], so you are associating the wrong side of the pipe with the child's stdin and stdout.
From the man page, it does seem like you can do this with one pipe. But it might be clearer code to use two.
It seems like you are thinking of each element of pipefd as a separate pipe, but that's not the case.

Resources