C reading all data from pipe - c

My program creates child process and sets pipes to communicate with it. The problem occurs when i try to read data from the pipe. Since child process has ended (i use wait to ensure that) EOF should be on the end of the data stream thus ending the read (As in the man page for pipe). But instead read just freezes and waits for more data to come.
What am i missing here?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void setfd(int *in, int *out) {
dup2(out[1], 1);
dup2(in[0], 0);
}
int main(int argc, char *argv[]) {
int status;
int pipe2ch[2], pipe2pr[2];
char *newargv[] = {NULL, NULL};
newargv[0] = argv[1];
pipe(pipe2ch);
pipe(pipe2pr);
setfd(pipe2pr, pipe2ch);
int a;
if (!(a = fork())) {
setfd(pipe2ch, pipe2pr);
execve(newargv[0], newargv, NULL);
exit(1);
} else {
printf("hello!\n");
fflush(stdout);
char str;
wait(&status);
while (read(pipe2pr[0], &str, 1) > 0) {
fprintf(stderr, "%c", str);
}
exit(0);
}
}

Since child process has ended (i use wait to ensure that) EOF should be on the end of the data stream thus ending the read (As in the man page for pipe).
I'm not sure what you've read to suggest that. Or maybe it's your wording that I don't understand. EOF is not a character on the stream.
But instead read just freezes and waits for more data to come. What am i missing here?
Several things. The most important one is probably that when a process forks, the child's copies of the parent's open file descriptors refer to the same entries in the kernel's underlying table of open files as the parent's do, each of which remains open until all handles on it are closed. The child closes all its file descriptors when it exits, but both ends of both pipes remain open in the parent, so end-of-file will not be signaled to readers. Each process must close the pipe ends it doesn't use or is finished using.
Additionally, you should read() first, then wait(), for if the child process writes enough data to the pipe then it may block, and if the parent does not read until after the child exits then you'll have a deadlock.
Furthermore, I don't see any reason to dupe either pipe end onto the parent's standard streams (resulting in closing the original ones). Just manipulate the pipes via their file descriptors, as you already half do. If you want a stream interface to those, then use fdopen() to get one.

Related

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.

How to send continuous stream of data from one process to another via EXECL

I am writing C program which constantly generates two string values named stateName and timer (with the rate of five times per second). I need to concatenate and pass them to another process called ProcessNo3_TEST which is responsible for tokenizing and also displaying them.
The problem is I don't know how to pass them continuously via execl. I had a couple of attempts but none of them were successful. Here is my code which works fine for a single pair of values (e.g. UP2 and 98):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define READ 0
#define WRITE 1
int FIFO[2];
char fileDescriptor[10];
char* stringMaker( char *s1,char *s2 );
int main()
{
char lengthInChar[15],msg[200];
int msgLength,i;
char *stateName, *timer;
if (pipe(FIFO) == -1)
{
printf("cannot create pipe\n");
exit(1);
}
sprintf(fileDescriptor, "%d", FIFO[READ]);
stateName = "UP2"; // for instance
timer = "98"; // for instance
msgLength = strlen(stateName) + strlen(timer) +3;
strcpy(msg, stringMaker(stateName, timer) );
write(FIFO[WRITE], msg, msgLength);
switch (fork())
{
case 0:
sprintf(lengthInChar, "%d", msgLength);
execl("ProcessNo3_TEST", "ProcessNo3_TEST", lengthInChar, fileDescriptor, NULL);
exit(1);
case -1:
perror("fork() failed-->");
exit(2);
default:
break;
}
sleep(10);
exit(0);
}
char* stringMaker( char *s1,char *s2 )
{
char *s3;
strcpy(s3,s1);
strcat(s3,"-");
strcat(s3,s2);
strcat(s3,"-");
strcat(s3,"\0");
return s3;
}
Can anyone help on this please?
(I am running CygWin on Windows by the way)
----------UPDATE-------------
As advised in comments below, I found a good example of fdopen() which solved my problem. (Link)
Although one can arrange to pass one pipe end's file descriptor number (in string form) as a program argument, normally one would instead redirect the child process's standard streams to read from (in this case) or write to the pipe. This is usually achieved via dup2(), which you would apply in the child process, after forking but before execl().
Parent and child processes then communicate via the pipe. In this case, the parent writes to the writing end and the child reads from the reading end. Either or both can wrap their file descriptor in a stream by passing it to fdopen(). Then you can use stdio functions with it. Note, too, that after the fork, each process should close the FD for the pipe end it does not intend to use.
If are set on using execl() (often people prefer not to, but sometimes it has it's benefits) than you should use named pipes instead of unanonymous. Anononymous pipe end is lost after execl(). But if you have a named pipe, you can pass it's name as an argument to the execl(), open it in child process and use there.

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

Declaring char array causes execv() to not work

I wrote the following code in order to use pipes in c unix:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
int main ()
{
int fds[2];
pid_t pid;
/* Create a pipe. 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) {
//char abc[10]; - **Uncommenting this cause the program not to work.**
/* This is the child process. Close our copy of the write end of
the file descriptor. */
close (fds[1]);
// Read params
FILE * stream;
stream = fdopen (fds[0], "r");
char* args[4]={"avg3.out","4","3","5"};
/* Replace the child process with the “avg3” program. */
execv("avg3.out", args);
} else {
/* This is the parent process. */
FILE* stream;
/* Close our copy of the read end of the file descriptor. */
close (fds[0]);
/* Convert the write file descriptor to a FILE object, and write
to it. */
dup2(fds[0], STDOUT_FILENO);
stream = fdopen (fds[1], "w");
fprintf (stream, "5 4 3");
fflush (stream);
close (fds[1]);
/* Wait for the child process to finish. */
waitpid (pid, NULL, 0);
}
return 0;
}
avg3.out is a file I compiled before. It simply calculate the average of the 3 params sent to it.
The output was 4, but when I tried to actually read from the stream, I added a declaration for char buffer[10] The code stopped working. That is, no output provided. I tried to rename it, to move the decleration to the start of the if statement. but nothing worked.
So, why does the program stop working when adding just an array declaration?
The parameter-array to go with calls to exec*() needs to be (char*)NULL-terminated.
This line
char* args[4]={"avg3.out","4","3","5"};
should be
char* args[] = {"avg3.out", "4", "3", "5", NULL};
As it isn't in your code, exec() might get lost searching for it.
By bad luck the stack might have been clean (0 filled) for the version of your code not declaring a and execv() found a NULL right after the pointer pointing to "5". Having a created on the stack then changed the content of the stack which made execv() getting lost searching for the NULL.
Additionally its worth mentioning that the OP's code misses error checking on most of the relevant system call.
Having done so together with a detailed examation of the errors' causes, probably by using calls to perror() might have led to solving this issue by providing relevant information.
In particular placing it after the call to execv() it soon would have been obvious what's wrong:
execv("avg3.out", args);
perror("execv() failed");

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