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

发布于 2024-09-27 08:15:05 字数 2661 浏览 0 评论 0原文

我正在尝试编写一个 C++ 程序,该程序创建一个子进程,运行一个命令并将输出传输回父进程正在运行的命令的输入。

我让父进程执行 wait(NULL) 或 wait((void*)pid) 命令,但它不等待。

这是代码:

#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;
}

不要采用粗体线。我希望父进程在 wait() 命令处等待,子进程会打印出“Child did sleep...”,然后完成,然后父进程会打印出“Parent: running...”

我在做什么错误的!

谢谢!

更新:程序的完整输出为:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

感性不性感 2024-10-04 08:15:05

我看到四个问题:

1) execlp() 失败:execlp() (或任何 exec 系列函数)完全取代了当前正在运行的进程映像如果成功 - 预计不会返回,除非出现问题。但您看到的是“孩子:睡觉完毕,正在返回”消息,因此它不可能成功。 (在您的示例中,我猜测这可能是因为 dmegs 应该是 dmesg。)

2) printf()cout 输出缓冲意味着无法保证您按照发生的顺序获得输出。如果您想通过打印输出来调试它,最好打印到 stderr (例如使用 fprintf(stderr, ...)),这是(默认情况下)无缓冲。

3)正如其他人指出的, wait((void *)pid) 是错误的。 wait(NULL)waitpid(pid, NULL, 0)

4) 这是否是一个问题取决于平台,但是...execlp() 的终止空指针参数应该显式写为 (char *)0(char *)0 code> 而不仅仅是 0,以确保它作为指针而不是整数传递。一般来说,在 C 中,指针上下文中的 0 根据定义是空指针,但是当将参数传递给参数数量可变的函数时,编译器没有足够的信息来知道您正在尝试在指针上下文中使用它,因此除非您显式转换它,否则会将其作为整数传递。在指针和整数大小不同的平台上,这可能会给您带来麻烦。


所以我认为 wait() 正在工作,子进程实际上并没有运行您想要的命令,并且父进程和子进程的输出由于缓冲而变得混合。


这是代码的稍微修改版本,它不使用任何 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;
}

输出:

$ gcc -Wall -o wait wait.c
$ ./wait
Child: My PID = 27846
Child: Running...
child starting command: sleep 5
Parent: My PID = 27845

(这里有 5 秒的延迟)

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

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
$
GRAY°灰色天空 2024-10-04 08:15:05

您通常会打开结果以允许错误情况

pid = fork();
switch( pid ) {
 case -1: // parent fail
 case 0: // child success
 default: // parent success
}

等待您想要使用的特定子项

waitpid( pid, NULL, 0 );

或等待任何子项

pid_t child = waitpid( -1, NULL, 0 );

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 );
无法回应 2024-10-04 08:15:05

你为什么要

wait((void*)pid)

等待需要指向状态的指针

   #include <sys/types.h>
   #include <sys/wait.h>

   pid_t wait(int *status);

你几乎肯定会传递不可写的地址。测试一下等待重新编码,我敢打赌一定会抱怨得很厉害;

另外,混合 printf 和 couts 也是一种让自己感到困惑的方法,它们的缓冲/刷新方案可能不同

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

梦在夏天 2024-10-04 08:15:05
wait((void*)pid);

你不应该仅仅为了让编译器停止抱怨而将东西强制转换为 void* 。 :)

看来您可能需要waitpidhttp://linux. die.net/man/2/waitpid

更新:

您需要检查 execlp 调用是否确实有效。比较:

$ ./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

与:

$ ./a.out "dmesg|more"
dmesg
more
Parent: My PID = 20876
Child: My PID = 20877
Child: Running...
^C

第一种情况,由于execlp找不到“dmegs”,所以子进程基本上立即退出。这会解除父进程的阻塞并允许其执行。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文