I've been trying to use the pipe() system call to create a shell that supports piping (with an arbitrary number of commands).
Unfortunately, I haven't had much luck using pipe(). After spending a few days looking at various online resources, I decided to put together an oversimplified program that has the same effect as executing ls | sort
to see if I could even get a pipe to work for two sibling, child processes. Here's the code:
#include <sys/wait.h>
#include <unistd.h>
void run(char *cmd) {
char *args[2];
args[0] = cmd;
args[1] = NULL;
execvp(cmd, args);
}
int main(void) {
int filedes[2];
pipe(filedes);
pid_t pid_a, pid_b;
if (!(pid_a = fork())) {
dup2(filedes[1], 1);
run("ls");
}
if (!(pid_b = fork())) {
dup2(filedes[0], 0);
run("sort");
}
waitpid(pid_a, NULL, 0);
waitpid(pid_b, NULL, 0);
return 0;
}
开发者_开发问答The pipe is created in the parent and I know that after the execvp() call, each child process inherits the file descriptors that pipe() creates in the parent. For the ls
process, I'm using dup2() to redirect its standard out (1) to the write-end of the pipe and for the sort
process, standard in (0) is being redirected to the read-end of the pipe.
Finally, I wait for both processes to finish before exiting.
My intuition tells me this should work, but it doesn't!
Any suggestions?
You have to close the pipes you're not using.
at least sort
will read from its stdin until stdin is closed.
In this case, it's stdin is never closed, as you still have 2 open filedescriptors for it.
- filedes[0] in the ls child (this likely gets closed when ls finishes)
- filedes[0] in the parent program (this never gets closed as you waitpid() for sort to end, but it never will because the parent keeps its stdin open)
Change your program to
if (!(pid_a = fork())) {
dup2(filedes[1], 1);
closepipes(filedes);
run("ls");
}
if (!(pid_b = fork())) {
dup2(filedes[0], 0);
closepipes(filedes);
run("sort");
}
closepipe(filedes);
waitpid(pid_a, NULL, 0);
waitpid(pid_b, NULL, 0);
where closepipes is something like
void closepipes(int *fds)
{
close(fds[0]);
close(fds[1]);
}
Before calling waitpid
in the parent process you have to close all file descriptors from the pipe that you don't need. These are:
filedes[0]
inpid_a
filedes[1]
inpid_b
- both
filedes[0]
andfiledes[1]
in the parent process
You should also check that pipe()
and fork()
didn't return -1
, which means an error happened.
You need to close (at least) the writing end of the pipe in the parent process. Otherwise, the reading end of the pipe will never read EOF status, and sort
will never finish.
This code working properly...
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
void run(char *cmd) {
char *args[2];
args[0] = cmd;
args[1] = NULL;
execvp(cmd, args);
}
void closepipe(int *fds)
{
close(fds[0]);
close(fds[1]);
}
int main(int argc,char *argv[]) {
int filedes[2];
pipe(filedes);
char lss[]="ls";
char sorts[]="sort";
pid_t pid_a, pid_b;
chdir(argv[1]);
if (!(pid_a = fork())) {
dup2(filedes[1], 1);
closepipe(filedes);
run(lss);
}
if (!(pid_b = fork())) {
dup2(filedes[0], 0);
closepipe(filedes);
run(sorts);
}
closepipe(filedes);
waitpid(pid_a, NULL, 0);
waitpid(pid_b, NULL, 0);
return 0;
}
精彩评论