C programming - handling stdout and stdin using pi

2019-07-21 06:18发布

问题:

I am writting a C program formed by a parent and his child (using fork). They comunicate through a pipe. Parent writes into the pipe through the standard output and child reads from the pipe through the standard input. Once they are connected, parent writes "hello world" into the pipe, and son calls exec. My code looks like this:

int main(int argc, char *argv[])
{
 int p, a;
 char buf[1024];
 FILE *file;
 size_t nread;
 int fd[2];
 char argument[PATH_MAX];

 if(pipe(fd)<0){
 return 1;
 }

 p = fork();
 switch(p){
   case -1: perror("Error en el fork()"); return 1;
   case 0:
     close(fd[1]);
     close (0);
     dup(fd[0]);
     close(fd[0]);
     sprintf(argument,"/usr/bin/%s",argv[1]);
     execvp(argument,argv);
     perror("Error en el execv");
     exit(1);
   default: break;
 }
 close(fd[0]);
 close(1);
 a = dup(fd[1]);
 close(fd[1]);
 write(1,"Hello World\n",12);
 close(a);
 wait(NULL);
 return 0;
}

exec function executed by the son calls functions rev or wc. If called without arguments, rev and wc should be applied to the standard input ("hello world" in my case). But this is not working and I don't know why. Any help would be really appreciated.

回答1:

this is not working and I don't know why

Because you are using dup(). To redirect the standard input of the child process to the pipe, the correct system call to use is dup2()

case 0:
    close( fd[1] );
    dup2( fd[0], 0 ); // this "dup"s the read-end of the pipe onto STDIN
    close( fd[0] );

Note that you don't need the dup() call at all in the parent code branch. Just write to the write-end of the pipe:

write( fd[1], "Hello World\n", 12 );

However, if you want to use execvp in the parent branch also, to launch another program with its standard output redirected, then you would have to use dup2() here also:

dup2( fd[1], 1 ); // this "dup"s the write-end of the pipe onto STDOUT
close( fd[1] );

Read the manpage for dup2 for details.

Also, another problem with your code is the use of execvp with argv as the argument list. This will cause programs like rev and wc to receive the entire command line of the parent program and thus find an argument to process rather than read from standard input. You probably want

execvp( argv[1], &argv[1] );


回答2:

You are using execvp() wrongly in the following code:

 sprintf(argument,"/usr/bin/%s",argv[1]);
 execvp(argument,argv);

If you run your program by ./a.out wc, then in this case the arguments to /usr/bin/wc would be ./a.out wc, i.e. the original argument list to ./a.out, then /usr/bin/wc will try to open a file named wc in current working directory instead of reading from standard input.

So change the above code to

 sprintf(argument,"/usr/bin/%s",argv[1]);
 execvp(argument,NULL);

should fix the problem, this time the argument list to /usr/bin/wc will be a empty list.