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.
Related
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.
I've been trying to implement shell-like functionality with pipes in an application and I'm following this example. I will reproduce the code here for future reference in case the original is removed:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/**
* Executes the command "cat scores | grep Villanova | cut -b 1-10".
* This quick-and-dirty version does no error checking.
*
* #author Jim Glenn
* #version 0.1 10/4/2004
*/
int main(int argc, char **argv)
{
int status;
int i;
// arguments for commands; your parser would be responsible for
// setting up arrays like these
char *cat_args[] = {"cat", "scores", NULL};
char *grep_args[] = {"grep", "Villanova", NULL};
char *cut_args[] = {"cut", "-b", "1-10", NULL};
// make 2 pipes (cat to grep and grep to cut); each has 2 fds
int pipes[4];
pipe(pipes); // sets up 1st pipe
pipe(pipes + 2); // sets up 2nd pipe
// we now have 4 fds:
// pipes[0] = read end of cat->grep pipe (read by grep)
// pipes[1] = write end of cat->grep pipe (written by cat)
// pipes[2] = read end of grep->cut pipe (read by cut)
// pipes[3] = write end of grep->cut pipe (written by grep)
// Note that the code in each if is basically identical, so you
// could set up a loop to handle it. The differences are in the
// indicies into pipes used for the dup2 system call
// and that the 1st and last only deal with the end of one pipe.
// fork the first child (to execute cat)
if (fork() == 0)
{
// replace cat's stdout with write part of 1st pipe
dup2(pipes[1], 1);
// close all pipes (very important!); end we're using was safely copied
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*cat_args, cat_args);
}
else
{
// fork second child (to execute grep)
if (fork() == 0)
{
// replace grep's stdin with read end of 1st pipe
dup2(pipes[0], 0);
// replace grep's stdout with write end of 2nd pipe
dup2(pipes[3], 1);
// close all ends of pipes
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*grep_args, grep_args);
}
else
{
// fork third child (to execute cut)
if (fork() == 0)
{
// replace cut's stdin with input read of 2nd pipe
dup2(pipes[2], 0);
// close all ends of pipes
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
execvp(*cut_args, cut_args);
}
}
}
// only the parent gets here and waits for 3 children to finish
close(pipes[0]);
close(pipes[1]);
close(pipes[2]);
close(pipes[3]);
for (i = 0; i < 3; i++)
wait(&status);
}
I have trouble understanding why the pipes are being closed just before calling execvp and reading or writing any data. I believe it has something to do with passing EOF flags to processes so that they can stop reading writing however I don't see how that helps before any actual data is pushed to the pipe. I'd appreciate a clear explanation. Thanks.
I have trouble understanding why the pipes are being closed just before calling execvp and reading or writing any data.
The pipes are not being closed. Rather, some file descriptors associated with the pipe ends are being closed. Each child process is duping pipe-end file descriptors onto one or both of its standard streams, then closing all pipe-end file descriptors that it is not actually going to use, which is all of the ones stored in the pipes array. Each pipe itself remains open and usable as long as each end is open in at least one process, and each child process holds at least one end of one pipe open. Those are closed when the child processes terminate (or at least under the control of the child processes, post execvp()).
One reason to perform such closures is for tidiness and resource management. There is a limit on how many file descriptors a process may have open at once, so it is wise to avoiding leaving unneeded file descriptors open.
But also, functionally, a process reading from one of the pipes will not detect end of file until all open file descriptors associated with the write end of the pipe, in any process, are closed. That's what EOF on a pipe means, and it makes sense because as long as the write end is open anywhere, it is possible that more data will be written to it.
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…)
Just started learning about pipes (IPC in general). After I went through some man pages, websites and few SO questions like this, This and few others. I got to know the basic and I see that this communication is done only once, i.e., parent writes to child and child reads it or parent and child reads and writes to each other just once and then the pipe closes.
What I want is keep this communication between the processes without the pipe closing, i.e.,
say, my program has 2 child processes where 1st child process is running something in a while loop and the 2nd is running a timer continuously. At certain intervals, my 2nd process sends some 'signal' to 1st child and my 1st stops and prints something at that instant and restarts again for next timer stop. (<-This I have done using threads)
This is the program that I tried just as a sample. But I'm not able to keep the communication continuous.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes, count = 5;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
/* Send "string" through the output side of pipe */
while(count--)
{
pipe(fd);
close(fd[0]);
write(fd[1], string, (strlen(string)+1));
close(fd[1]);
}
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
while(count--)
{
pipe(fd);
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s\n", readbuffer);
close(fd[0]);
close(fd[1]);
}
}
int status;
waitpid(getppid(), &status, 0);
printf("Done!\n");
return(0);
}
From those example, I inferred that the pipe get's closed after each send/read.
I tried opening new pipe every time, still I could't get it.
Can anyone please help me what am I missing or what should I do?
Right now both the parent and child creates their own pair of pipes, that the other process have no knowledge about.
The pipe should be created in the parent process before the fork.
Also, you close the reading/writing ends of the pipe in the loops, when you should close them after the loop, when all the communication has been done.
And a small unrelated issue...
In the reader you should really loop while read doesn't return 0 (then the write-end of the pipe is closed) or -1 (if there's an error).
It would be great if you use the shared memory approach. In this approach the parent will allocate a memory area which will be shared among all the processes. Use locks to secure your resource i.e. shared memory. You can also visit this answer which details what is the concept behind. Also remember that in shared memory approach the communication can be many-to-many. But in case of pipes it is one-to-one.
Cheers,
K.
Infoginx.com
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.