如何正确利用管道,分支和execv启动并与辅助程序交互?

时间:2013-03-28 00:08:41

标签: c++ pipe fork exec

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

上下文:有一个程序'A'从STDIN获取用户输入并输出对STDOUT的响应。 “A”的输入和输出不适合我的目标用户。不幸的是,不能直接编辑或扩充“A”。

方法:我创建程序'B',它要求子进程启动'A'并使用管道拦截和转换输入/输出(即在通过管道发送到A的STDIN之前接受用户输入和调整,反之接收通过管道STDOUT'A'并为用户调整。)

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

  1. 与管道2相关联的流末尾没有EOF。这是一个指示性症状,即管道1按预期运行,并且不受类似问题的影响,可能是因为程序“A”使用cin处理输入并查找'\ n'而不是EOF。我尝试调整我的程序也逐行读取但在执行'A'的第一行之后(除了子进程打印的自我声明行之外)它停止接收数据。
  2. 当孩子在尝试从中读取管道的同时写入管道时,存在某种争用。
  3. fgetc()可能不会在“管道2”流缓冲区中“消耗”数据,因此子进程会阻塞,直到“收到”此数据为止。这可能是问题,因为shell / OS(STDIN)可以完美地接收'A'(管道1数据)的输入,并且在那一端更好地/不同地处理缓冲,而STDOUT接收'A'的输出和将它交付给管道2,因此所有缓冲责任都落在管道机制上。
  4. 以下是程序B(“test.cpp”)的代码和我用于测试而不是A(“test2.cpp”)的虚拟模拟程序。

    TEST.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;   
    }
    

    我提前为冗长的问题和单片代码道歉。

1 个答案:

答案 0 :(得分:0)

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

dup2是2级io,用于处理原始的低级文件描述符; fgetc是3级io,它在缓冲的c文件上运行; iostream是C ++上比FILE更高的东西。

我建议:

  • 坚持一个范例,坦率地说'c'可能最容易,因为lots examples available
  • 使用像Perl或Python这样的脚本语言:他们非常擅长这种事情。

或者你可以看到others who have asked this same question的一些答案。