我正在尝试编写一个创建子进程的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 done sleeping ...”然后完成,然后父进程将打印出“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
答案 0 :(得分:3)
我发现了四个问题:
1)execlp()
失败:execlp()
(或任何exec
系列函数)如果成功则完全替换当前正在运行的过程映像 - 它不会返回,除非出了点问题。但是你看到了“Child:Done sleeping,return”的消息,所以它不可能成功。 (在您的示例中,我猜这可能是因为dmegs
应该是dmesg
。)
2)printf()
和cout
输出缓冲意味着无法保证您按照发生的顺序获得输出。如果你想通过打印输出来调试它,你最好打印到stderr
(例如fprintf(stderr, ...)
),这是(默认情况下)无缓冲。
3)正如其他人所指出的,wait((void *)pid)
是错误的。 wait(NULL)
或waitpid(pid, NULL, 0)
。
4)这个问题是否与平台有关,但...... execlp()
的终止空指针参数应明确写为(char *)0
,而不仅仅是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
$
答案 1 :(得分:0)
你为什么要这样做
wait((void*)pid)
等待指针指向状态
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
你几乎可以通过不可写的地址。测试等待重新编码,我打赌是抱怨很大的时间;
混合printf和couts是一种让自己迷惑的方法,他们的缓冲/冲洗方案可能会有所不同
答案 2 :(得分:0)
wait((void*)pid);
你不应该把事情变成无效*只是为了让编译器停止抱怨。 :)
看起来你可能想要 waitpid :http://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”,子进程基本上会立即退出。这将取消阻止父进程并允许它执行。
答案 3 :(得分:0)
您通常会打开结果以允许错误案例
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 );