繁体   English   中英

如何正确使用管道,派生和execv启动辅助程序并与之交互?

[英]How do i correctly utilize pipe, fork and execv to launch and interact with a secondary program?

编程语言:C和C ++混合代码

上下文:有一个程序“ A”,它接收来自STDIN的用户输入并向STDOUT输出响应。 “ A”的输入和输出不适合我的预期用户。 不幸的是,直接编辑或增加“ A”不是一个选择。

方法:我创建了程序“ B”,该程序分叉了一个子进程来启动“ A”,并使用管道来截取和转换输入/输出(即,在通过管道发送到A的STDIN之前接受用户输入和调整,然后反过来接收“ A'通过管道并根据用户进行调整。)

问题:尝试接收“ A”的输出时,程序似乎在通信中“挂起”。 我的研究使我相信该问题可能是3个问题中的1个:*为了清楚起见,让Pipe 1是从父(B)发送到'A'的STDIN的数据,而Pipe 2是用来从'父母'。

  1. 与管道2关联的流的末尾没有EOF。这的一个明显症状是管道1可以按预期运行,并且不受类似问题的影响,可能是因为程序“ A”使用cin来处理输入并查找“ \\ n不是EOF。 我尝试将程序调整为也逐行读取,但是在执行“ A”的第一行(除了由子进程打印的自声明行)之后,它停止接收数据。
  2. 当孩子在父母试图从管道读取数据的同时向管道写入数据时,会发生某种争用。
  3. fgetc()可能不会“消耗”管道2流缓冲区中的数据,因此子进程将阻塞,直到“接收”该数据为止。 这可能是问题所在,因为Shell / OS(STDIN)完美地接收了“ A”(管道1数据)的输入,并且在那一端更好/不同地处理了缓冲,而STDOUT接收了“ A”的输出,并且将其传送到管道2,结果所有缓冲责任都落在管道机制上。

下面是程序B(“ test.cpp”)和我用来测试而不是A(“ test2.cpp”)的虚拟模仿程序的代码。

测试文件

#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

using namespace std;

int main()
{
pid_t pid;
int wpipe[2];//write to child pipe
int rpipe[2];//read from child pipe

//initiate pipes here
if(pipe(wpipe))
{
    cout << "write pipe creation failed"<<endl;
    return 1;
}
if(pipe(rpipe))
{
    cout << "read pipe creation failed"<<endl;
    return 1;
}

//fork child here
pid_t procID = vfork();//does not copy address space. better than fork? 

if(procID==0)
{//child
    //close unused pipe ends
    close(wpipe[1]);//read from wpipe
    close(rpipe[0]);//write to rpipe
    //handle stdin & stdout pipes
    dup2(wpipe[0],STDIN_FILENO);//reroute stdin to wpipe[0]
    dup2(rpipe[1],STDOUT_FILENO);//reroute stdout to rpipe[1]
    //close pipes since they are copied??
    close(wpipe[0]);
    close(rpipe[1]);
    //prepare array for execv
    char** args;
    args = new char*[2];
    args[0] = new char[8];
    strcpy(args[0],"test2");
    args[1] = (char*)0;

    cout << "I'm child. Relinquishing procID & memory image to test 2."<<endl;

    int test = execv(args[0],args); 
    if(test==-1)
        cout << "Error: execv failed."<<endl;
    delete[] args[0];
    delete[] args;
    exit(0);
}else if(procID>0)
{//parent
    int state,c;

    //close unused pipe ends
    close(wpipe[0]);//write to wpipe
    close(rpipe[1]);//read from rpipe

    //communicate with child
    FILE *wstream;
    wstream = fdopen(wpipe[1],"w");
    FILE *rstream;
    rstream = fdopen(rpipe[0],"r");

    //read from child
    c = fgetc(rstream);
    while(c!=EOF)
    {
        putchar(c);
        c = fgetc(rstream);
    }

    fprintf(wstream,"test1\n");

            c = fgetc(rstream);
    while(c!=EOF)
    {
        putchar(c);
        c = fgetc(rstream);
    }

    fprintf(wstream,"test2\n");

            c = fgetc(rstream);
    while(c!=EOF)
    {
        putchar(c);
        c = fgetc(rstream);
    }

    fclose(wstream);
    fclose(rstream);

    waitpid(procID,&state,0);
    cout << "I'm parent."<<endl;
}
else
{//failure to fork
    cout << "Fork failed" << endl;
    return 1;
}

return 0;
}

test2.cpp

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string tmp = "";
    cout << "started" << endl;
    while(tmp=="")
        cin >> tmp;
    cout << tmp << endl;
    tmp="";
    while(tmp=="")
        cin >> tmp;
    cin >> tmp;
    cout << tmp << endl;

    cout << "exiting" << endl;

return 0;   
}

对于冗长的问题和单一代码,我深表歉意。

您似乎正在使用三种不同类型的io。

dup2是2级io,它对原始的低级文件描述符进行操作; fgetc是级别3 io,它对缓冲的c FILE进行操作; iostream是FILE之上的C ++。

我会建议:

  • 坚持一个范例,坦率地说,使用'c'可能是最简单的,因为有很多 可用示例
  • 使用像Perl或Python这样的脚本语言:它们在这种事情上非常出色。

或者,您可能会看到对提出相同问题的其他人的一些答案。

暂无
暂无

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

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