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

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.

Related

Why does read() block and wait forever in parent process despite the writing end of pipe being closed?

I'm writing a program with two processes that communicate through a pipe. The child process reads some parameters from the parent, executes a shell script with them and returns the results to the parent process line by line.
My code worked just fine until I wrote the while(read()) part at the end of the parent process. The child would execute the shell script, read its echo's from popen() and print them to standard output.
Now I tried to write the results to the pipe as well and read them in the while() loop at the parent's end but it blocks and neither will the child process print the result to standard output. Apparently it won't even reach the point after reading the data from the pipe sent by the parent.
If I comment out the while() at the parent process, the child will print the results and return, and the program will end smoothly.
Why does the while(read()) block even if I closed the writing end of the pipe in both parent and child processes?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
int read_from_file(char **directory, int *octal) {
FILE *file = fopen("input", "r");
if (file == NULL) {
perror("error opening file");
exit(1);
}
fscanf(file, "%s %d", *directory, octal);
}
int main(int argc, char *argv[]) {
char *directory = malloc(256);
int *octal = malloc(sizeof *octal);
pid_t pid;
int pfd[2];
char res[256];
if (pipe(pfd) < 0) {
perror("Error opening pipe");
return 1;
}
if ((pid = fork()) < 0)
perror("Error forking");
if (pid == 0) {
printf("client here\n");
if (read(pfd[0], directory, 256) < 0)
perror("error reading from pipe");
if (read(pfd[0], octal, sizeof(int)) < 0)
perror("error reading from pipe");
// This won't get printed:
printf("client just read from pipe\n");
// close(pfd[0]);
char command[256] = "./asd.sh ";
strcat(command, directory);
char octal_c[5];
sprintf(octal_c, " %d", *octal);
strcat(command, octal_c);
FILE *f = popen(command, "r");
while (fgets(res, 256, f) != NULL) {
printf("%s", res);
if (write(pfd[1], res, 256) < 0)
perror("Error writing res to pipe");
}
fclose(f);
close(pfd[1]);
close(pfd[0]);
fflush(stdout);
return 1;
}
read_from_file(&directory, octal);
if (write(pfd[1], directory, 256) < 0)
perror("Error writing dir to pipe");
if (write(pfd[1], octal, sizeof(int)) < 0)
perror("error writing octal to pipe");
int r;
close(pfd[1]);
while (r = read(pfd[0], res, 256)) {
if (r > 0) {
printf("%s", res);
}
}
close(pfd[0]);
while (wait(NULL) != -1 || errno != ECHILD);
}
Since the child demonstrably reaches ...
printf("client here\n");
... but seems not to reach ...
printf("client just read from pipe\n");
... we can suppose that it blocks indefinitely on one of the two read() calls between. With the right timing, that explains why the parent blocks on its own read() from the pipe. But how and why does that blocking occur?
There are at least three significant semantic errors in your program:
pipes do not work well for bidirectional communication. It is possible, for example, for a process to read back the bytes that it wrote itself and intended for a different process. If you want bidirectional communication then use two pipes. In your case, I think that would have avoided the apparent deadlock, though it would not, by itself, have made the program work correctly.
write and read do not necessarily transfer the full number of bytes requested, and short reads and writes are not considered erroneous. On success, these functions return the number of bytes transferred, and if you want to be sure to transfer a specific number of bytes then you need to run the read or write in a loop, using the return values to track progress through the buffer being transferred. Or use fread() and fwrite() instead.
Pipes convey undifferentiated streams of bytes. That is, they are not message oriented. It is not safe to assume that reads from a pipe will be paired with writes to the pipe, so that each read receives exactly the bytes written by one write. Yet your code depends on that to happen.
Here's a plausible failure scenario that could explain your observations:
The parent:
fork()s the child.
after some time performs two writes to the pipe, one from variable directory and the other from variable octal. At least the first of those is a short write.
closes its copy of the write end of the pipe.
blocks attempting to read from the pipe.
The child:
reads all the bytes written via its first read (into its copy of directory).
blocks on its second read(). It can do this despite the parent closing its copy of the write end, because the write end of the pipe is still open in the child.
You then have a deadlock. Both ends of the pipe are open in at least one process, the pipe is empty, and both processes are blocked trying to read bytes that can never arrive.
There are other possibilities that arrive at substantially the same place, too, some of them not relying on a short write.
The parent process was trying to read from the pipe before the child could have read from it and write the results to it. Using two different pipes for the two-way communication solved the problem.

Why child process is waiting while using fork and pipes for stdin from parent's output?

I understood how fork and pipes work but i have a doubt regarding the flow of the child and parent process.Since we are using fork the order of execution of parent and child process is undefined but why child process is waiting for stdin from parent process.What happens if child process executes first? it must print empty in console? but it is not happening can i know why?
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main () {
int fds[2]; pid_t pid;
/* File descriptors for the two ends of the pipe are placed in fds. */
pipe (fds);
/* Fork a child process. */
pid = fork ();
if (pid == (pid_t) 0) {
/* Child proces -. Close the write end of the file descriptor. */
close (fds[1]);
/* Connect the read end of the pipe to standard input. */
dup2 (fds[0], STDIN_FILENO);
/* Replace the child process with the “rev” program. */
execlp("rev", "rev", 0); }
else {
/* This is the parent process. */
FILE* stream;
/* Close the read end of the file descriptor. */
close (fds[0]);
/* Convert the write file descriptor to a FILE object */
stream = fdopen (fds[1], "w");
fprintf (stream, ",ereh ot ereht morF\n");
fprintf (stream, ",ereht ot ereh dna\n");
fprintf (stream, ".erehwyreve era sgniht ynnuf\n");
fprintf (stream, "ssueS .rD - \n");
fflush (stream);
close (fds[1]);
/* Wait for the child process to finish. */
waitpid (pid, NULL, 0);
}
return 0;
}
You aren't closing enough file descriptors in the child.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD
In this case, the child needs to close fds[1] after duplicating it. Because it is still open, rev will never receive EOF because there is a process (the rev child process) that could, in theory, write to the input.
You should use fclose(stream) instead of close(fds[1]) because the output is buffered and fclose() knows to flush the buffers, but close() hasn't got a clue. However, by using fflush(stream) before the misguided close(), you do avoid problems.
That leads to:
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
int fds[2];
pid_t pid;
pipe(fds);
pid = fork();
if (pid == (pid_t)0)
{
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
close(fds[1]);
execlp("rev", "rev", 0);
}
else
{
FILE *stream;
close(fds[0]);
stream = fdopen(fds[1], "w");
fprintf(stream, ",ereh ot ereht morF\n");
fprintf(stream, ",ereht ot ereh dna\n");
fprintf(stream, ".erehwyreve era sgniht ynnuf\n");
fprintf(stream, "ssueS .rD - \n");
fclose(stream);
waitpid(pid, NULL, 0);
}
return 0;
}
which produces the output:
From there to here,
and here to there,
funny things are everywhere.
- Dr. Seuss

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.

Open a cmd program with full functionality (i/o)

I tried popen() and it is working well for output with "r" passed as a second argument; I know you can use "w" as writing mode and it worked for me (the program was just one scanf()). My question is how to use the append ("a") mode. You can both write and read, how do you know when the program is outputting something and when it's requesting for user input?
popen uses a pipe (that's the "p" in "popen") and pipes are unidirectional. You can either read or write from one end of a pipe, not both. To get both read/write access you should use a socketpair instead. I use this in my programs when I want something like popen, but for read/write:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
FILE *sopen(const char *program)
{
int fds[2];
pid_t pid;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
return NULL;
switch(pid=vfork()) {
case -1: /* Error */
close(fds[0]);
close(fds[1]);
return NULL;
case 0: /* child */
close(fds[0]);
dup2(fds[1], 0);
dup2(fds[1], 1);
close(fds[1]);
execl("/bin/sh", "sh", "-c", program, NULL);
_exit(127);
}
/* parent */
close(fds[1]);
return fdopen(fds[0], "r+");
}
Note that since it doesn't return the child's pid, you'll have a a zombie process after the child program exits. (Unless you set up SIGCHLD...)

Dead lock linux pipe

I want to learn how Linux pipes work! I wrote a small and easy program that use a pipe to communicate a string between parent and child process. However, the program results in a dead lock that I have not understood what is its cause.
Here is the code :
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SIZE 100
int
main(int argc, char *argv[])
{
int pfd[2];
int read_pipe=0, write_pipe=0;
pid_t cpid;
char buf[SIZE];
/* PIPE ***************************************
* pipe() creates a pair of file descriptors, *
* pointing to a pipe inode, and places them *
* in the array pointed to by filedes. *
* filedes[0] is for reading, *
* filedes[1] is for writing *
**********************************************/
if (pipe(pfd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
read_pipe=pfd[0];
write_pipe=pfd[1];
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
char * hello = "I am a child process\n";
sleep(1);
// wait until there is some data in the pipe
while (read(read_pipe, buf, SIZE) > 0);
printf("Parent process has written : %s\n", buf);
write(write_pipe, hello, strlen(hello));
close(write_pipe);
close(read_pipe);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
char * hello = "I am a parent process\n";
write(write_pipe, hello, strlen(hello));
while (read(read_pipe, buf, SIZE) > 0);
printf("Child process has written : %s\n", buf);
close(write_pipe);
close(read_pipe);
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
In this link you'll find the proper mannipulation of PIPEs between parent and child. Your problem here is that the communication is not being correctly set-up.
The PIPE should be used to communicate in only one direction, so one process has to close the read descriptor and the other has to close the write descriptor. Otherwise what will happen is that the call to 'read'(both on the father and the son), since it can detect that there is another process with an open write descriptor on the PIPE, will block when it finds that the PIPE is empty (not return 0), until someone writes something in it. So, both your father and your son are getting blocked on their respective read.
There are two solutions to this:
.You create two PIPEs, one for the communication in each direction, and perform the initialization as explained in the link above. Here you have to remember to close the write descriptor when you are done sending the message, so the other process' read will return, or condition the loop to the count of bytes read (not to the return of read), so you won't perform another call when you read the whole message. For example:
int bread = 0;
while(bread < desired_count)
{
bread += read(read_pipe, buf + bread, SIZE - bread);
}
.You create one PIPE as you did, and modify the flags on the read descriptor, using fcntl to also have O_NONBLOCK, so the calls to read won't block when there's no information in the PIPE. Here you need to check on the return value of the read to know you received something, and go adding up until you get the full length of the message. Also you will have find a way to synchronize the two processes so they won't read messages that are not meant for them. I don't recommend you to use this option, but you can try it if you want using condition variables.
Maybe you can tell if you see any of yout printf() outputs?
Anyway, if you want to establish a two way communication between your paent and child, yout should use two pipes, one for writing data form parent to child an the other for writing from child to parent. Furthermore, your read loops may be dangerous: if the data comes in two or more chunks the second read() overwrites the first portion (I've never seen tha happen with local pipes, but for example with sockets). And of course, yout is not automatically null terminated after read(), so just printing int with "%s" may also cause problems.
I hope that gives you some ideas to try.

Resources