wait command wont wait for child process to finish c cpp c++

喜欢而已 提交于 2021-02-07 21:22:50

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!