在 Linux 上实现双向管道
Implementing bidirectional pipe on Linux
我们要在程序中执行一个交互式命令行实用程序 (Samba smbclient)。 smbclient 就像一个典型的交互式 ftp 客户端,您向它输入命令(在其提示后)并访问网络共享并 returns 您得到结果。对于我们的程序,我们要输入一个命令,检索结果,输入下一个命令,等等..
这是我们的示例程序。 /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 的伪终端,这样它就好像是 运行ning 交互一样。您可以自己推出,但是 正是 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
我们要在程序中执行一个交互式命令行实用程序 (Samba smbclient)。 smbclient 就像一个典型的交互式 ftp 客户端,您向它输入命令(在其提示后)并访问网络共享并 returns 您得到结果。对于我们的程序,我们要输入一个命令,检索结果,输入下一个命令,等等..
这是我们的示例程序。 /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 的伪终端,这样它就好像是 运行ning 交互一样。您可以自己推出,但是 正是 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