C Linux programming - Pipe makes child process exit - c

I am having a hard time understanding the behavior of the following code. Closing the file descriptor p[0] makes the program exit (since otherwise the parent process just waits on the child processes in all eternity). It doesn't make sense to me because the child processes are running an infinite while loop, why would they just exit because the read end of the pipe is closed? Sure, you won't be able to write to the pipe but the while loop is not dependent on whether the read end of the parent process is open or not. I tried removing the exit() functions in the child processes and the program exits anyways, so why is the child process killing itself as soon as it notices that the read end is closed?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main()
{
int run=10, fd[2]; pipe(fd); srand(time(0)); char ch, x='x', o='o';
if(!fork())
{
close(fd[0]);
while(run)
{
sleep(1+rand()%6); write(fd[1],&x,1);
}
exit(0); //This exit doesn't happen
}
if(!fork())
{
close(fd[0]);
while(run)
{
sleep(1+rand()%3); write(fd[1],&o,1);
}
exit(0); //This exit doesn't happen
}
close(fd[1]);
while(run--)
{
read(fd[0],&ch,1);
printf("%d %c\n",run,ch);
sleep(1);
}
close(fd[0]); //closing this file descriptor results in that the program can exit
wait(0);
wait(0);
exit(0);
}

That's standard behavior. You close the read-end of the pipe, so there is nowhere to write. That results in a SIGPIPE signal being sent to the writing process.
The default behavior of SIGPIPE is to terminate the process receiving the signal.
If you want your process to survive the signal, you have to catch or ignore it. Then you must check the result of write as it will return with an error when the pipes read-end is closed.

Your problem is not with pipe. Two header files missing in your codes and for which it is crashing when calling wait() function. Add below two header files in your program and it will fix your problem. Also your declaration of main function is old c type declaration make it like void main().
<sys/types.h>
<sys/wait.h>

Related

Closing a pipe does not send EOF to other end

I would like to run an external command from a C program. Let's say, as minimal working example, that I want to run the 'cat' command. I use use fork() and execl() to spawn the new process, and I communicate with it via pipes.
Now that's where my problem is. In a terminal I would tell 'cat' that I am done with my input by pressing CTRL-D. Here I am trying to do so by closing the file descriptor -- see the line with close(outpipefd[1]) in the code below -- but this does not seem to work. My code stalls as 'cat' is waiting for more input.
My code is as follows... What am I doing wrong? Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
int main(void)
{
pid_t pid=0;
int inpipefd[2];
int outpipefd[2];
/*
We create the pipes for communicating with the child process
*/
pipe(inpipefd);
pipe(outpipefd);
if((pid=fork())==0)
{
/*
Child
*/
dup2(outpipefd[0],STDIN_FILENO);
dup2(inpipefd[1],STDOUT_FILENO);
dup2(inpipefd[1],STDERR_FILENO);
/*
We spawn the process
*/
execl("/bin/cat","cat",(char *)(NULL));
/*
Nothing below this line should be executed by child process.
If so, it means that the execl function wasn't successfull, so lets exit!
*/
exit(1);
}
/*
Parent.
Close unused pipe ends.
*/
close(outpipefd[0]);
close(inpipefd[1]);
/*
Now we can write to outpipefd[1] and read from inpipefd[0]
*/
char *greeting="Hello world!\n";
write(outpipefd[1],greeting,strlen(greeting));
/*
Here I believe that closing the pipe should be equivalent to
pressing CTRL-D in a terminal, therefore terminating the cat command...
This is unfortunately not the case!
*/
close(outpipefd[1]);
while(1)
{
char buf[256];
for(int c=0;c<256;c++)
buf[c]=0;
if(read(inpipefd[0], buf, 256)<=0)
break;
printf("OUTPUT: %s\n", buf);
}
/*
Send SIGKILL signal to the child process
*/
int status;
kill(pid, SIGKILL);
waitpid(pid, &status, 0);
return 0;
}
The child still has both ends of both pipes opened, because you never closed any of your FDs in it. Until every FD referring to the write end of a pipe is closed, it won't return EOF.
You have also to close the unused pipe ends in the child, or there will be still things open that block the other end. close what you don't use in parent and child, and you will get the EOFs.

Read system call blocked sharing a pipe

I'm new in Unix systems programming and I'm struggling to understand file descriptors and pipes. Let's consider this simple code:
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
int main() {
int fd[2], p;
char *m = "123456789\n", c;
pipe(fd);
p = fork();
if (p == 0) {
// child
while(read(fd[0], &c, 1) > 0) write(1, &c, 1);
}
else {
// parent
write(fd[1], m, strlen(m));
close(fd[1]);
wait(NULL);
}
exit (0);
}
When I compile and run the code, it outputs 123456789 but the process never ends unless I issue ^C. Actually, both processes appear as stopped in htop.
If the child closes fd[1] prior to read() then it seems to work OK but I don't understand why. The fd are shared between both processes and the parent closes fd[1] after writing. Why then the child doesn't get the EOF when reading?
Thank you in advance!
Well, first of all your parent process is waiting for the child to terminate in the wait(2) system call, whyle your child is blocked in the pipe to read(2) for another character. Both processes are blocked... so you need to act externally to take them off. The problem is that the child process doesn't close it's writing descriptor of the pipe (and also the parent doesn't close its reading descriptor of the pipe, but this doesn't affect here) Simply the pipe blocks any reader while at least one such writing descriptor is still open. Only when all writing descriptors are closed, the read returns 0 to the reader.
When you did the fork(2) both pipe descriptors (fd[0] and fd[1]) were dup()ed on the child process, so you have a pipe with two open file descriptors (one in the parent, one in the child) for writing, and two open descriptors (again, one in the parent, one in the child) for reading, so as one writer remains with the pipe open for writing (the child process in this case) the read made by the child still blocks. The kernel cannot detect this as an anomaly, because the child could still write on the pipe if another thread (or a signal handler) should want to.
By the way, I'm going to comment some things you made bad in your code:
first is that you consider only two cases from fork() for the parent, and for the child, but if the fork fails, it will return -1 and you'll have a parent process writing on a pipe with no reading process, so probably it should block (as I say, this is not your case, but it is an error either) You have always to check for errors from system calls, and don't assume your fork() call is never to fail (think that -1 is considered != 0 and so it falls through the parent's code). There's only one system call that you can execute without checking it for errors, and it is close(2) (although there's much controversy on this)
This same happens with read() and write(). A better solution to your problem would be to have used a larger buffer (not just one char, to reduce the number of system calls made by your program and so speed it up) and use the return value of read() as a parameter on the write() call.
Your program should (it does on my system, indeed) work with just inserting the following line:
close(fd[1]);
just before the while loop in the child code, as shown here:
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
int main() {
int fd[2], p;
char *m = "123456789\n", c;
pipe(fd);
p = fork();
if (p == 0) {
// child
close(fd[1]); // <--- this close is fundamental for the pipe to work properly.
while(read(fd[0], &c, 1) > 0) write(1, &c, 1);
}
else if (p > 0) {
// parent
// another close(fd[0]); should be included here
write(fd[1], m, strlen(m));
close(fd[1]);
wait(NULL);
} else {
// include error processing for fork() here
}
exit (0);
}
If the child closes fd[1] prior to read() then it seems to work OK but I don't understand why.
That's what you need to do. There's not much more to it than that. A read from the read end of a pipe won't return 0 (signaling EOF) until the kernel is sure that nothing will ever write to the write end of that pipe again, and as long as it's still open anywhere, including the process doing the reading, it can't be sure of that.

Feeding stdout to a child process which will execv() sort

I am trying to find out how I can send output of one process into a child process. I have gone down a journey learning of file descriptors and pipes. I think I am almost there but am missing a key component.
This is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd[2];
pid_t sort_pid;
/* Create the pipe */
if(pipe(fd) == -1) {
fprintf(stderr, "Pipe failed\n");
exit(EXIT_FAILURE);
}
/* create child process that will sort */
sort_pid = fork();
if(sort_pid < 0) { // failed to fork
fprintf(stderr, "Child Fork failed\n");
exit(EXIT_FAILURE);
}
else if(sort_pid == 0) { // child process
close(0); // close stdin
dup2(fd[0], 0); // make stdin same as fd[0]
close(fd[1]); // don't need this end of the pipe
execlp("D:/Cygwin/bin/sort", "sort", NULL);
}
else { // parent process
close(1); // close stdout
dup2(fd[1], 1); // make stdout same as fd[1]
close(fd[0]); // don't need this end of the pipe
printf("Hello\n");
printf("Bye\n");
printf("Hi\n");
printf("G'day\n");
printf("It Works!\n");
wait(NULL);
}
return EXIT_SUCCESS;
}
This doesn't work, as it seems to go into an endless loop or something. I tried combinations of the wait() but that doesnt help either.
I am doing this to learn how to apply this idea in my actual program. In my actual program I read files, parse them line by line and save the processed data to a static array of structs. I want to be able to then generate output based on these results and use the fork() and execv() syscalls to sort the output.
This is ultimately for a project in uni.
These are similar examples which I dissected to get to the stage I am at so far:
pipe() and fork() in c
How to call UNIX sort command on data in pipe
Using dup,pipe,fifo to communicate with the child process
Furthermore I read the manual pages on the relevant syscalls to try and understand them. I will admit my knowledge of pipes and using them is still basically nothing, as this is my first every try with them.
Any help is appreciated, even further sources of information I could look into myself. I seem to have exhausted most of the useful stuff a google search give me.
sort will read until it encounters end-of-file. You therefore have to close the write-end of the pipe if you want it to complete. Because of the dup2, you have two copies of the open file description, so you need
close(fd[1]); anytime after the call to dup2
close(1); after you're done writing to (the new) stdout
Make sure to fflush(stdout) before the second of these to ensure that all your data actually made it into the pipe.
(This is a simple example of a deadlock: sort is waiting on the pipe to close, which will happen when the parent exits. But the parent won't exit until it finishes waiting on the child to exit…)

Am I using close() wrongly?

I'm trying to make a program that runs any given Linux command until the users inputs "stop".
The father should make a child, which should also make a child.
So let's say A is the father, B is the son and C is the grandson.
A -> reads the command from user input and sends it to B
B -> reads the command from a pipe and sends it to C
C -> reads the command from the pipe and will use execl. The output should be sent to a pipe which is sent to B, and then B will display the output to the screen.
So I will have 3 pipes: 1 form A to B, 1 from B to C, and 1 form C to B.
pipe p1 : A to B
pipe p2 : B to C
pipe p3 : C to B
Here's my code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int p1[2], p2[2],p3[3];
pipe(p1);
pipe(p2);
pipe(p3);
char cmd[20],buffer[128];
while(1<2)
{
printf("Type a cmd: \n");
scanf("%s20",cmd);
if(strcmp(cmd,"stop")==0)
{
break;
}
else
{
close(p3[1]);
close(p3[0]);
close(p1[0]);
close(p2[1]);
close(p2[0]);
write(p1[1],&cmd,sizeof(char)*20);
if(fork()==0)
{
close(p1[1]);
close(p2[0]);
close(p3[1]);
read(p1[0],&cmd,sizeof(char)*20);
write(p2[1],&cmd,sizeof(char)*20);
if(fork()==0)
{
close(p1[0]);
close(p1[1]);
close(p2[1]);
close(p3[0]);
read(p2[0],&cmd,sizeof(char)*20);
char bin[20]="/bin/";
strcat(bin,cmd);
dup2(p3[1],1);
execl(bin,bin,NULL);
exit(1);
}
wait(0);
dup2(p3[0],0);
while(read(p3[0],&buffer,sizeof(char)))
{
printf("%s",buffer);
}
exit(1);
}
}
wait(0);
}
return 0;
}
I think I'm using close() wrongly because I don't understand exactly what it does. AFAIK it's used because every time I use fork(), the FD is duplicated.
So far, it does compile and run, but if I input "ls", the program terminates.
If I remove all calls to close(), "ls" will work, but it will block, not allowing me to type a second command.
Your program creates three pipes, then closes two of them (p2 and p3) completely before calling fork(). It also closes the read end of p1 at the same time. Your child processes will not get any usable file handles.
pipe(p1);
pipe(p2);
pipe(p3);
char cmd[20],buffer[128];
while(1<2) {
...
else {
close(p3[1]);
close(p3[0]);
close(p1[0]);
close(p2[1]);
close(p2[0]);
...
write(p1[1],&cmd,sizeof(char)*20);
After the fork, the child closes the single remaining pipe file handle (that's not a problem, really), and then tries to read from the file handle already closed by the parent (that is):
if(fork()==0) {
...
close(p1[1]);
...
read(p1[0],&cmd,sizeof(char)*20);
Usually, you'd want to fork first, then close the unneeded file handles after that, i.e. for parent to child communication, only the parent should close the read end, and only the child should close the write end.
Of course, if the parent closes one end of the pipe, then in the next iteration of the loop, that file handle will not be available to a new child process, so you'd need to create a new pipe at that point. Or keep all the fd's open in the main program (but I can't remember if that will cause issues with pipes).
As for the fd's you pass from child to grandchild, you'll need to treat them the same way when doing that fork. Though I would suggest doing a two-process experiment first.

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

Resources