2

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

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

方法:我创建了程序'B',它派生一个子进程来启动'A'并使用管道来拦截和翻译输入/输出(即在通过管道发送到A的STDIN之前获取用户输入并进行调整,并反过来接收'的STDOUT A' 通过管道并为用户调整。)

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

  1. 与管道 2 关联的流末尾没有 EOF。这方面的一个指示性症状是管道 1 按预期运行,并且不受类似问题的影响,可能是因为程序“A”用于cin处理输入并查找“\ n'不是EOF。我尝试调整我的程序以逐行读取,但是在执行“A”的第一行之后(除了子进程打印的自我声明行)它停止接收数据。
  2. 当子级在尝试读取管道的同时,子级正在写入管道时,存在某种争用。
  3. fgetc()可能不会“消耗”管道 2 流缓冲区中的数据,因此子进程会阻塞,直到“接收到”该数据。这可能是问题所在,因为外壳/操作系统(STDIN)完美地接收了“A”(管道 1 数据)的输入,并且在那里缓冲处理得更好/不同,而 STDOUT 接收“A”的输出和将其传递给管道 2,因此所有缓冲责任都落在管道机制上。

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

测试.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;
}

测试2.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;   
}

对于冗长的问题和单一的代码,我提前道歉。

4

1 回答 1

0

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

dup2 是 2 级 io,对原始的低级文件描述符进行操作;fgetc 是对缓冲的 c 文件进行操作的 3 级 io;iostream 是高于 FILE 的 C++ 东西。

我会建议:

  • 坚持一种范式,坦率地说,使用“c”可能是最简单的,因为有很多可用的例子
  • 使用像 Perl 或 Python 这样的脚本语言:他们非常擅长这种事情。

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

于 2013-05-21T03:52:36.557 回答