Query on pipe creation between two descriptors of parent-child process - c

In my below working program, I would like to understand,
How/who established(created) a single pipe communication between {child's pair[1]} descriptor and {parent's pair[0]} descriptor?
Because in my below program, i just fork()'d a process and immediately who has established pipe connection between {child's pair[1]} descriptor and {parent's pair[0]} descriptor? Do you think it is obvious to accept this point?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int pair[2];
char buf[30] ="";
pipe(pair);
if (!fork()) {
printf(" CHILD: writing to the pipe\n");
write(pair[1], "test", 5);
printf(" CHILD: exiting\n");
exit(0);
} else {
printf("PARENT: reading from pipe\n");
read(pair[0], buf, 5);
wait(NULL);
printf(" PARENT: exiting\n");
}
return 0;
}
Please help me!!

Your questions are not particularly clear; the second "question" says something looks obvious, but appears to contain no question itself. For instance, you don't actually say what is going wrong in the samples you have presented (if anything).
I think what you what to know might be as follows:
pipe() creates two file descriptors which are linked within the operating system, a reader and a writer.
When you fork() all open FDs are available in both the parent and the child. So if you have called pipe() prior to the fork(), both the reader and the writer will be available to the child.
As normally the parent will be the reader and the child the writer, or the parent will be the writer and the child the reader (i.e. neither will both write and read), it is normal for the parent to close one fd and the child to close the other.

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.

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…)

Can a single pipe be used for 2 way communication between parent and a child?

Suppose I use pipefdn[2] and pipe() on it , can bidirectional communication be implemented using a single pipe or do you need 2 pipes ?
Though this operation results as success in some cases, but it is not a recommended way , especially in the production code. As pipe() by default dont provide any sync mechanism and moreover the read() can go for an infinite hang, if no data or read() is called before write() from other process.
Recommended way is to always use 2 pipe. pipe1[2], pipe2[2] for two way communication.
For more info please refer the following video description.
https://www.youtube.com/watch?v=8Q9CPWuRC6o&list=PLfqABt5AS4FkW5mOn2Tn9ZZLLDwA3kZUY&index=11
No sorry. Linux pipe() is unidirectional. See the man page, and also pipe(7) & fifo(7). Consider also AF_UNIX sockets, see unix(7).
Correct me if I am wrong: But I think you can. The problem is that you probably don't want to do that. First, of all create a simple program:
#include <stdio.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int pd[2];
int num = 2;
int main(){
pid_t t = fork();
/* create a child process */
if(t<0){
printf("error in fork");
exit(1);
}
/* create a pipe */
if(pipe(pd)==-1){
printf("error in pipe");
exit(3);
}
else if(t==0){
//close(pd[1]); // child close writing end
int r = read(pd[0], &num, sizeof(num));
if(r<0){
printf("error while reading");
exit(2);
}
printf("i am the child and i read %d\n",num);
// close(pd[0]);
exit(0);
}
/* parent process */
//close(pd[0]); /* parents closes its reading end
if(write(pd[1],&num,sizeof(num)<0)){
printf("error in reading");
exit(4);
}
//close(pd[1]);
/*parent wait for your child to terminate;*/
int status;
wait(&status);
printf("my child ended with status: %d\n",status);
return 0;
}
Try to play with close(). Skip it by putting it in a comment or include it. You will find out that in order this program to run the only really needed system-call close is the one before the child reads. I found here in stack overflow an answer saying that " Because the write-end is open the system waits because a potential write could occur .. " . Personally, I tried to run it without it and I discovered that it would not terminate. The other close(), although are a good practice , don't influence the execution. ( I am not sure why that happens maybe someone more experienced can help us).
Now let's examine what you asked:
I can see some problems here:
If two processes write in the same channel you may have race conditions:
They write to the same file descriptor at the same time:
What if one process reads its own writings instead of those of the process
it tries to communicate with? How you will know, where in the file you should read?
What if the one process, writes "above" the writings of the other?
Yes it can, I've done that before. I had a parent and child send each other different messages using the same 2 pipes and receive them correctly. Just make sure you're always reading from the first file descriptor and writing to the second.

Fork parent child communication

I need some way for the parent process to communicate with each child separately.
I have some children that need to communicate with the parent separately from the other children.
Is there any way for a parent to have a private communication channel with each child?
Also can a child for example, send to the parent a struct variable?
I'm new to these kind of things so any help is appreciated. Thank you
(I'll just assume we're talking linux here)
As you probably found out, fork() itself will just duplicate the calling process, it does not handle IPC.
From fork manual:
fork() creates a new process by duplicating the calling process.
The new process, referred to as the child, is an exact duplicate of
the calling process, referred to as the parent.
The most common way to handle IPC once you forked() is to use pipes, especially if you want "a private comunication chanel with each child". Here's a typical and easy example of use, similar to the one you can find in the pipe manual (return values are not checked):
#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;
pipe(pipefd); // create the pipe
cpid = fork(); // duplicate the current process
if (cpid == 0) // if I am the child then
{
close(pipefd[1]); // close the write-end of the pipe, I'm not going to use it
while (read(pipefd[0], &buf, 1) > 0) // read while EOF
write(1, &buf, 1);
write(1, "\n", 1);
close(pipefd[0]); // close the read-end of the pipe
exit(EXIT_SUCCESS);
}
else // if I am the parent then
{
close(pipefd[0]); // close the read-end of the pipe, I'm not going to use it
write(pipefd[1], argv[1], strlen(argv[1])); // send the content of argv[1] to the reader
close(pipefd[1]); // close the write-end of the pipe, thus sending EOF to the reader
wait(NULL); // wait for the child process to exit before I do the same
exit(EXIT_SUCCESS);
}
return 0;
}
The code is pretty self-explanatory:
Parent forks()
Child reads() from the pipe until EOF
Parent writes() to the pipe then closes() it
Datas have been shared, hooray!
From there you can do anything you want; just remember to check your return values and to read dup, pipe, fork, wait... manuals, they will come in handy.
There are also a bunch of other ways to share datas between processes, they migh interest you although they do not meet your "private" requirement:
shared memory "SHM", the name says it all...
sockets, they obviously work as good if used locally
FIFO files which are basically pipes with a name
or even a simple file... (I've even used SIGUSR1/2 signals to send binary datas between processes once... But I wouldn't recommend that haha.)
And probably some more that I'm not thinking about right now.
Good luck.

How do I declare a pipe in a header file? (In C)

I have an assignment in which I need to declare a pipe in a header file. I really have no idea how to do this. It might be a really stupid question and I might be missing something obvious. If you could point me in the right direction I would greatly appreciate it.
Thanks for your time.
EDIT:
Sorry about the question being so vague. Maybe I need to reinforce my understanding of pipes.
I'm trying to create a pipe between two child processes. One child will write random characters into the pipe while the other child will read characters out of the pipe.
I guess I don't really understand what happens when I write something like:
int fd[2];
pipe = pipe(fd);
Am I right in saying that the writing and reading file descriptors for the pipe are put into fd[0] and fd[1] respectively? If in one of the child processes I close fd[1], that child could be thought of as my writer, correct?
EDIT 2:
Okay, it looks as if I pretty much have everything figured out and done, except I am getting an error pertaining to the file descriptors.
My code looks like this: (This is only the code relating to the pipe)
proj2.h
extern int fd[2];
proj2.c
int fd[2];
pipe(fd);
writer.c
close(fd[0]);
result = write(fd[1], &writeBuffer, sizeof(writeBuffer));
if(result < 0){
perror("Write");
}
reader.c
close(fd[1]);
result = read(fd[0], &readBuffer, sizeof(readBuffer))
if(result < 0){
perror("Read");
}
After executing the code, I get an error for every iteration of read() and write() with the error "Bad file descriptor". I've tried searching online to solve this myself, but I do not think I know enough about this material in order to do so. Any direction would be greatly appreciated once again. Everybody that has contributed has done a wonderful job so far, thank you very much. Also, if it looks like I'm just having you do my homework for me, I'm putting forth an honest effort and this isn't the entirety of the assignment.
EDIT 3:
Is the write() system call writing to standard output? What if I only want the contents to be printed after the reader reads them out of the pipe? How do I write them into the pipe without it writing them to standard output?
EDIT 4:
I've figured everything out now. Thanks for all of the help everybody. The only thing I'm still curious about is if I could somehow get the status of the parent process. I've collected the statuses from the child process using the wait() system call and was wondering how to retrieve the status of the parent process.
Here's an example of a program that creates a pipe and then forks the process and calls a sender function in the parent and a receiver in the child. The pipe creation and file descriptors are in one source code file with an associated header file, as are the sender and receiver. The main file requests the pipe be created then does the fork() and calls the sender and receiver functions.
pipe.h - this contains the extern declaration for the pipe file descriptors as well as the declaration of the function that creates the pipe:-
#ifndef PIPE_H
#define PIPE_H
extern int pipe_fd[2];
void create_pipe(void);
#endif
pipe.c - contains the actual definition of the pipe_fd array:-
#include "pipe.h"
#include <unistd.c>
int pipe_fd[2];
void create_pipe(void)
{
pipe(pipe_fd);
}
sender.h - declares the prototype for the sender() function
#ifndef SENDER_H
#define SENDER_H
void sender(void);
#endif
sender.c:-
#include "sender.h"
#include "pipe.h"
#include <unistd.h>
#include <stdio.h>
void sender(void)
{
char buf[]="Hello world";
printf("Sender: PID = %d\n", getpid());
close(pipe_fd[0]);
write(pipe_fd[1], buf, sizeof(buf));
}
receiver.h:-
#ifndef RECEIVER_H
#define RECEIVER_H
void receiver(void);
#endif
receiver.c - mirror image of sender
#include <stdio.h>
#include <unistd.h>
#include "receiver.h"
#include "pipe.h"
void receiver(void)
{
int bytes;
char buf[101];
printf("Receiver: PID = %d\n", getpid());
close(pipe_fd[1]);
bytes = read(pipe_fd[0], buf, 100);
buf[bytes]='\0';
printf("Receiver got: %s\n", buf);
}
main.c - ties it all together
#include "pipe.h"
#include "sender.h"
#include "receiver.h"
#include <sys/types.h>
#include <unistd.h>
void launch_sender_receiver(void)
{
pid_t forkpid;
forkpid = fork();
if (forkpid == 0)
receiver(); /* child */
else
sender();
}
int main(int argc, char* argv[])
{
create_pipe();
launch_sender_receiver();
return 0;
}
Hopefully you can follow all this from the code but if not here's a little extra explanation.
The create_pipe() function in pipe.c creates a pipe and puts the two file descriptors into file_fd. The pipe.h file provides an extern declaration for the file descriptors so that they can be accessed by the sender and receiver files (better programming practice would be to provide "getter" functions for these file descriptors in pipe.h so that sender() and receiver() are not accessing global variables).
Sender and Receiver use the pipe_fd array to either write or read from the pipe after they close the file descriptor that they don't need. The main() function ties it all together by calling the pipe creation function and then doing the fork and calling sender or receiver depending on whether it is the parent or child respectively.
Running this as a complete program should get you the following output (although of course the PIDs you get will be different):-
Receiver: PID = 3285
Sender: PID = 3284
Receiver got: Hello world
Does all that make sense?
Your question is almost impossibly vague, but I'm going to guess
extern int myPipe[2];
?
If you intend to fork() to create the two processes then your "int fd[2]; pipe(fd);" will work as you described.
i.e. use fd[0] in one process and fd[1] in the other.
However, if you're not going to fork then you're probably going to have to create a pipe in the filesystem and communicate through this.
Use mkfifo to create your pipe and then open it for reading in one process and open it for writing in the other.
Why do you need to do it in a header file?
You just call pipe() from your program.
Then call fork().
As a result you have two processes accessing the same pipe.

Resources