微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 Linux 上实现双向管道

如何解决在 Linux 上实现双向管道

我们想在我们的程序中执行一个交互式命令行实用程序(Samba smbclient)。 smbclient 就像一个典型的交互式 ftp 客户端,你向它提供命令(在它的提示之后),它访问网络共享并返回结果。对于我们的程序,我们要输入一个命令,检索结果,输入下一个命令,等等..

这是我们的示例程序。 /root/testfifo 是已通过 mkfifo 在 bash 中创建的 fifo。

int main()
{
    const char *cmd = "smbclient //machine/share \
    -U domain/user%pwd  > /root/testfifo";

    FILE *out = popen(cmd,"w");
    if (!out)
    {
        return 0;
    }

    // Write the command to smbclient via 1st pipe
    fputs("ls\n",out);
    fflush(out);

    // Read the result from smbclient via 2nd pipe
    FILE *in = fopen("/root/testfifo","r");
     
    if (!in)
    {
        return 0;
    }
    
    char buf[1000];
    // Pending here..
    fgets(buf,1000,in);
    
    fclose(in);
    pclose(out);
    
    return 0;
}

问题是,程序在 fgets 行等待来自 in 的输入。但是,如果我删除第二个管道 in,首先运行我们的程序,然后打开一个单独的终端从同一个管道(例如,tail -f /root/testfifo)读取,它不会阻塞并从 smbclient 打印正确的结果.

这是为什么?

解决方法

问题似乎是因为 smbclient 正在执行某种缓冲,因此只有在您执行 pclose(outpipe);

时才会刷新结果

此带有 cat 的版本有效:

#include <cstdio>

int main()
{
#define cmd "{ sleep 10; cat; } > /tmp/testfifo"

    FILE* outpipe = popen(cmd,"w");
    if (!outpipe)
    {
        return 0;
    }

    // Write the command to smbclient via 1st pipe
    fputs("Message sent\n",outpipe);
    fflush(outpipe);

    // Read the result from smbclient via 2nd pipe
    FILE* inpipe = fopen("/tmp/testfifo","r");
     
    if (!inpipe)
    {
        return 0;
    }
    
    char buf[1000];
    // Pending here..
    fgets(buf,1000,inpipe);
    printf("This is the message received : [%s]\n",buf);
    
    pclose(inpipe);
    pclose(outpipe);

    return 0;
}

,

以编程方式操作专为人类设计的用户界面不一定那么容易。通常,基于文本的 UI 更容易,但您似乎偶然发现了其中一个问题:如果 C 或 C++ 程序的标准流完全缓冲,如果程序确定它们未连接到交互式设备(通常表示终端)。通过 fopen()popen() 打开的文件也是如此。

你的程序依赖于它自己的流是行缓冲还是非缓冲。您可以通过 setvbuf() or setlinebuf():

安排
    setlinebuf(outpipe);
    setlinebuf(inpipe);

但这可能还不够。如果 smbclient 使用 stdio 函数来读取命令和发出输出,那么它的一侧也会有缓冲,这不在您的控制范围内。

您真正需要的是一个用于运行 smbclient 的伪终端,这样它就好像在交互式运行一样。您可以推出自己的产品,但这类事情正是libexpect 的全部内容。我建议围绕使用此库来启动和与 smbclient 通信重新编写您的程序。作为奖励,您无需管理 fifo。

,

事实证明,这与 smbclient(或任何其他具有类似缓冲的应用程序)内部的 I/O 缓冲有关。

要解决这个问题,

const char *cmd =
"stdbuf -oL smbclient //machine/share \
    -U domain/user%pwd  > /root/testfifo";

更多读者参考: Turn-off-buffering-in-pipe

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。