【问题标题】:Coding pipe "|" in C++: Why is a blank line entering getline after piping?编码管道“|”在 C++ 中:为什么在管道后进入 getline 的空行?
【发布时间】:2019-03-18 03:59:57
【问题描述】:

我正在编写一个模拟 shell,我目前正在使用 dup2 对管道进行编码。这是我的代码:

bool Pipe::execute() {
    int fds[2]; //will hold file descriptors
    pipe(fds);
    int status;
    int errorno;
    pid_t child;
    child = fork();

    if (-1 == child) {
        perror("fork failed");
    }
    if (child == 0) {
        dup2(fds[1], STDOUT_FILENO);
        close(fds[0]);
        close(fds[1]);
        this->component->lchild->execute();
        _exit(1);
    }
    else if (child > 0) {
        dup2(fds[0], STDIN_FILENO);
        close(fds[0]);
        close(fds[1]);
        this->component->rchild->execute();
        waitpid(child, &status, 0);
        if ( WIFEXITED(status) ) {
            //printf("child exited with = %d\n",WEXITSTATUS(status));
            if ( WEXITSTATUS(status) == 0) {
                cout << "pipe parent finishing" << endl;
                return true;
            }
        }
        return false;
    }
}

this-&gt;component-&gt;lchild-&gt;execute();this-&gt;component-&gt;rchild-&gt;execute(); 在相应的命令上运行 execvp。我已经通过在父进程中打印出一个声明来确认这些返回。但是,在我的Pipe::execute() 中,子进程似乎没有完成,因为父进程中的 cout 语句从未打印,并且在提示 ($) 初始化后出现分段错误(见图)。这是每次执行后初始化提示的主要函数:

int main()
{
    Prompt new_prompt;
    while(1) {
        new_prompt.initialize();
    }
    return 0;
}

这里是initialize() 函数:

void Prompt::initialize()
{
    cout << "$ ";
    std::getline(std::cin, input);

    parse(input);
    run();
    input.clear();
    tokens.clear();
    fflush(stdout);
    fflush(stdin);
    return;
}

似乎ls | sort 运行良好,但是当提示初始化时,getline 将一个空行读入输入。我尝试过使用 cin.clear()、cin.ignore 以及上面的 fflush 和 clear() 行。这个空白字符串被“解析”,然后run() 函数被调用,它试图取消引用一个空指针。关于为什么/在哪里将这个空白行输入到 getline 的任何想法?我该如何解决这个问题?谢谢!

更新:管道中的父进程现在正在完成。我还注意到我的 I/O 重定向类(&gt;&lt;)也出现了段错误。我想我没有正确刷新流或关闭文件描述符......

这是我用于 lchild 和 rchild 的 execute() 函数:

bool Command::execute() {
    int status;
    int errorno;
    pid_t child;
    vector<char *> argv;
    for (unsigned i=0; i < this->command.size(); ++i) {
        char * cstr = const_cast<char*>(this->command.at(i).c_str());
        argv.push_back(cstr);
    }
    argv.push_back(NULL);
    child = fork();
    if (-1 == child) {
        perror("fork failed");
    }
    if (child == 0) {
        errorno = execvp(*argv.data(), argv.data());
        _exit(1);

    } else if (child > 0) {
        waitpid(child, &status, 0);
        if ( WIFEXITED(status) ) {
            //printf("child exited with = %d\n",WEXITSTATUS(status));
            if ( WEXITSTATUS(status) == 0) {
                //cout << "command parent finishing" << endl;
                return true;
            }
        }
        return false;
    }
}

【问题讨论】:

  • 它应该像shell终端提示符 - 它等待用户输入
  • 不管实际上是什么导致了空白行,我建议在运行时使用程序 not segfault - 尽快执行检查并给出错误,而不是 segfaulting .
  • 更新后是一个不同的问题,需要其他部分的代码。我怀疑那里有类似的问题,但没有看到代码就无法判断
  • @MichaelVeksler 我添加了执行功能。这个函数也应该适用于其他命令类型,所以我害怕关闭其中的一个流(例如,如果我运行 echo hello && pwd,我不想关闭任何东西 - 只有当我使用重定向)

标签: c++ pipe fork getline dup2


【解决方案1】:

这是错误:

else if (child > 0) {
    dup2(fds[0], STDIN_FILENO);
    close(fds[0]);
    close(fds[1]);
    this->component->rchild->execute();

您正在关闭父级的标准输入,而不仅仅是正确的子级。之后父进程的标准输入与右子进程的标准输入相同。

之后

std::getline(std::cin, input);

尝试读取左孩子的输出,而不是原来的标准输入。到那时,左边的孩子已经完成了,管道的那一端已经关闭。这会导致读取 stdin 失败,并使 input 保持其原始状态不变。

编辑:你的设计有一个小缺陷和一个大缺陷。小缺陷是您不需要Pipe::execute 中的分叉。主要缺陷是孩子应该是重定向流并关闭描述符的人。

只需通过 fork() 传递输入和输出参数,然后让孩子 dup2 这些。不要忘记让它也关闭不相关的管端。如果你不这样做,左孩子将完成,但它的输出管道将继续存在于其他进程中。只要该描述符的其他副本存在,正确的孩子在读取其管道末端时永远不会获得 EOF - 并且永远是星期块。

【讨论】:

  • 啊啊啊我明白了。那么有没有办法“重新打开”标准输入?还是我需要在某个地方做一个额外的关闭?
  • @SarahAllec 你应该在右孩子本身而不是父母中调用close
  • 嗯,实际上似乎没有正确的孩子,因为只有一个电话给fork()。一位家长,一位(左)孩子。至少,这就是class Pipe 级别的内容。
  • @BenVoigt 查看更新后的问题。正如我怀疑的那样,execute 方法调用了 fork,那是正确的孩子。在问题得到澄清后,我只是删除了关于这个假设的注释
  • 现在有 3 个子进程,这仍然不是正确的 fork() 调用数量,也不是正确子进程中的管道管理。
猜你喜欢
  • 2015-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-04
相关资源
最近更新 更多