问题
I am trying to write a c++ program that creates a child process, runs a command and pipes the output back to the input of a command the parent is running.
I have the parent execute the wait(NULL) or wait((void*)pid) command but it does not wait.
here is the code:
#include <string.h>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
using namespace std;
int main(int argc, char * argv[])
{
char* commands[strlen(argv[1])];
char *command = NULL;
command = strtok(argv[1],"|");
int i = 0;
while(command != NULL)
{
commands[i] = command;
i++;
command = strtok(NULL,"|");
}
int numberOfCommands = i;
pid_t pid;
int pfd[2];
char* prgname = NULL;
if(pipe(pfd) == -1)
{
perror("error on pipe call");
return(1);
}
for(int j = 0;j<numberOfCommands;j++)
{
cout<<commands[j]<<endl;
}
pid = fork();
if(pid == 0){//child process
printf("Child: My PID = %d\n", getpid());
printf("Child: Running...\n");
close(pfd[0]); //close read end of pipe
dup2(pfd[1],1);//connect the pipes
close(pfd[1]);//close extra file descriptors
prgname = commands[0];//first command
cout<<"child starting command: "<<prgname<<endl;
execlp(prgname, prgname, 0);//Load the program
**printf("Child: Done sleeping, returning.\n");**
}
else
{
printf("Parent: My PID = %d\n", getpid());
**wait((void*)pid); //also tried wait(NULL); same effect
printf("Parent: Running...\n");**
close(pfd[1]); //close the write end of the pipe
dup2(pfd[0],0);//connect the pipes
close(pfd[0]); //close extra file descriptor
prgname = commands[1];//now run the second command
cout<<"parent starting command: "<<prgname<<endl;
execlp(prgname, prgname, 0);//Load the programm
}
cout<<"all done"<<endl;
return 0;
}
Take not of the bolded lines. I would expect the parent process to wait at the wait() command and the child would print out "Child done sleeping..." and then finish and then the parent would print out "Parent: running..."
What am I doing wrong!
Thanks!
Update: full output to program is:
dmegs
more
Child: My PID = 30070
Child: Running...
Parent: My PID = 30066
Parent: Running...
parent starting command: more
child starting command: dmegs
Child: Done sleeping, returning.
all done
回答1:
I see four problems:
1) execlp()
is failing: execlp()
(or any of the exec
family of functions) completely replaces the currently running process image if successful - it is not expected to return, unless something goes wrong. But you are seeing the "Child: Done sleeping, returning" message, so it cannot have succeeded. (In your example, I would guess that this is probably because dmegs
should have been dmesg
.)
2) printf()
and cout
output buffering means that there is no guarantee whatsoever that you are getting the output in the order in which it happens. If you want to debug this by printing output, you would be better off printing to stderr
(e.g. with fprintf(stderr, ...)
) which is (by default) unbuffered.
3) As noted by others, wait((void *)pid)
is wrong. wait(NULL)
or waitpid(pid, NULL, 0)
.
4) Whether this one is a problem or not is platform-dependent, but... the terminating null pointer argument to execlp()
should be explicitly written as (char *)0
rather than just 0
, to ensure that it is passed as a pointer rather than an integer. In general in C, 0
in a pointer context is by definition a null pointer, but when passing parameters to functions with variable numbers of arguments, the compiler does not have enough information to know that you are trying to use it in a pointer context, and so will pass it as an integer unless you explicitly cast it. This can get you into trouble on platforms where pointers and integers are not the same size.
So I reckon the wait()
is working, the child is not actually running the command you want, and the output from parent and child is getting mixed up due to buffering.
Here is a slightly modified version of your code, which doesn't use any C++, cuts out the command handling stuff, and just pipes the output of sleep 5
to cat
(which is rather pointless, as sleep
doesn't generate any output anyway, but the delay is useful to see what's going on):
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
int pfd[2];
if(pipe(pfd) == -1)
{
perror("error on pipe call");
return(1);
}
pid = fork();
if(pid == 0){//child process
fprintf(stderr, "Child: My PID = %d\n", getpid());
fprintf(stderr, "Child: Running...\n");
close(pfd[0]); //close read end of pipe
dup2(pfd[1],1);//connect the pipes
close(pfd[1]);//close extra file descriptors
fprintf(stderr, "child starting command: sleep 5\n");
execlp("sleep", "sleep", "5", (char *)0);//Load the program
fprintf(stderr, "child: execlp failed\n");
}
else
{
fprintf(stderr,"Parent: My PID = %d\n", getpid());
wait(NULL);
fprintf(stderr,"Parent: Running...\n");
close(pfd[1]); //close the write end of the pipe
dup2(pfd[0],0);//connect the pipes
close(pfd[0]); //close extra file descriptor
fprintf(stderr,"parent starting command: cat\n");
execlp("cat", "cat", (char *)0);//Load the programm
}
fprintf(stderr,"all done\n");
return 0;
}
Output:
$ gcc -Wall -o wait wait.c
$ ./wait
Child: My PID = 27846
Child: Running...
child starting command: sleep 5
Parent: My PID = 27845
(there is a 5 second delay here)
Parent: Running...
parent starting command: cat
$
回答2:
You would normally switch on the result to allow for the error case
pid = fork();
switch( pid ) {
case -1: // parent fail
case 0: // child success
default: // parent success
}
Waiting for a specific child you would want to use
waitpid( pid, NULL, 0 );
or waiting for any child
pid_t child = waitpid( -1, NULL, 0 );
回答3:
why are you doing
wait((void*)pid)
wait takes pointer to status
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
You are almost certanly passing non writable address. Test the wait retcode and I bet is is complaining big time;
Also mixing printf and couts is a way to confuse yourself, their buffering / flushing schemes can be different
回答4:
wait((void*)pid);
You shouldn't cast things to void* just to make the compiler stop complaining. :)
It looks like you probably want waitpid: http://linux.die.net/man/2/waitpid
Update:
You need to check whether the execlp call actually worked. Compare:
$ ./a.out "dmegs|more"
dmegs
more
Parent: My PID = 20806
Child: My PID = 20807
Child: Running...
Parent: Running...
parent starting command: more
child starting command: dmegs
Child: Done sleeping, returning.
all done
with:
$ ./a.out "dmesg|more"
dmesg
more
Parent: My PID = 20876
Child: My PID = 20877
Child: Running...
^C
In the first case, since execlp can't find "dmegs", the child process basically exits immediately. This unblocks the parent process and allows it to execute.
来源:https://stackoverflow.com/questions/3928612/wait-command-wont-wait-for-child-process-to-finish-c-cpp-c