简体   繁体   English

wait命令不会等待子进程完成c cpp c ++

[英]wait command wont wait for child process to finish c cpp c++

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. 我正在尝试编写一个c ++程序,该程序创建一个子进程,运行一个命令,并将输出通过管道传递回父级正在运行的命令的输入。

I have the parent execute the wait(NULL) or wait((void*)pid) command but it does not wait. 我让父母执行wait(NULL)或wait((void *)pid)命令,但它不等待。

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..." 我希望父进程等待wait()命令,然后子进程将打印出“子进程正在休眠...”,然后完成操作,然后父进程将打印出“父进程:正在运行...”。

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

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. 1) execlp()失败:如果成功, execlp() (或任何exec系列功能)将完全替换当前正在运行的进程映像-除非出现问题,否则它不会返回。 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 .) (在您的示例中,我想这可能是因为dmegs应该是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. 2) printf()cout输出缓冲意味着无法保证您按发生的顺序获得输出。 If you want to debug this by printing output, you would be better off printing to stderr (eg with fprintf(stderr, ...) ) which is (by default) unbuffered. 如果要通过打印输出进行调试,最好打印到(默认情况下)没有缓冲的stderr (例如,使用fprintf(stderr, ...) )。

3) As noted by others, wait((void *)pid) is wrong. 3)如其他人所述, wait((void *)pid)是错误的。 wait(NULL) or waitpid(pid, NULL, 0) . wait(NULL)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. 4)这个问题是否与平台相关,但是... execlp()的终止空指针参数应显式写为(char *)0而不是0 ,以确保将其传递为指针而不是整数。 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. 通常,在C语言中,指针上下文中的0根据定义是一个空指针,但是当将参数传递给具有可变数量参数的函数时,编译器没有足够的信息来知道您正在尝试在指针上下文中使用它,因此,除非您明确将其强制转换,否则它将作为整数传递。 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. 因此我认为wait()正在工作,子进程实际上并未运行您想要的命令,并且由于缓冲,父子进程的输出变得混乱。


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): 这是代码的略微修改版本,它不使用任何C ++,切掉命令处理内容,而只是将sleep 5的输出通过管道传递给cat (这毫无意义,因为sleep不会生成任何输出,但延迟对于了解发生的情况很有用):

#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) (这里有5秒的延迟)

Parent: Running...
parent starting command: cat
$

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 );

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 另外,混合使用printf和cout是一种使自己困惑的方法,它们的缓冲/刷新方案可能不同

wait((void*)pid);

You shouldn't cast things to void* just to make the compiler stop complaining. 您不应该为了使编译器停止抱怨而将内容强制转换为void *。 :) :)

It looks like you probably want waitpid : http://linux.die.net/man/2/waitpid 看来您可能想要waitpidhttp : //linux.die.net/man/2/waitpid

Update: 更新:

You need to check whether the execlp call actually worked. 您需要检查execlp调用是否真正起作用。 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. 在第一种情况下,由于execlp找不到“ dmegs”,因此子进程基本上会立即退出。 This unblocks the parent process and allows it to execute. 这将解除对父进程的阻止,并允许其执行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM