piping stockfish 在 fedora 中行为不端
piping stockfish misbehaves in fedora
在我的项目中的某个地方,我使用 fork 和 pipe 来执行另一个进程并通过管道传输它的 I/O 以与其通信(我正在用 C++ 编写它)。我在Ubuntu 14.04编译的时候没有问题,可以正常运行,但是我在WMWare虚拟机的fedora编译的时候开始出现奇怪的事情。如果我 运行 终端中的二进制文件,则没有错误,但不会在管道中写入任何内容(但获取字符流将起作用)。我试图在 fedora 中调试我的代码,我在我的代码中放置了一个断点,但是当进程试图从管道读取时给出了一个损坏的管道信号(在终端中执行时没有信号)。
那么,你们以前遇到过这样的问题吗? debian 和 red hat linux 之间的管道有什么区别吗?还是因为我在虚拟机上 运行ning fedora?
代码:
int mFD_p2c [2];
int mFD_c2p [2];
int mEnginePID;
if (pipe(mFD_p2c) != 0 || pipe(mFD_c2p) != 0)
{
cout << "Failed to pipe";
exit(1);
}
mEnginePID = fork();
if (mEnginePID < 0)
{
cout << "Fork failed";
exit(-1);
}
else if (mEnginePID == 0)
{
if (dup2(mFD_p2c[0], 0) != 0 ||
close(mFD_p2c[0]) != 0 ||
close(mFD_p2c[1]) != 0)
{
cout << "Child: failed to set up standard input";
exit(1);
}
if (dup2(mFD_c2p[1], 1) != 1 ||
close(mFD_c2p[1]) != 0 ||
close(mFD_c2p[0]) != 0)
{
cout << "Child: failed to set up standard output";
exit(1);
}
string engine = "stockfish";
execlp(engine.c_str(), (char *) 0);
cout << "Failed to execute " << engine;
exit(1);
}
else
{
close(mFD_p2c[0]);
close(mFD_c2p[1]);
string str = "uci";
int nbytes = str.length();
if (write(mFD_p2c[1], str.c_str(), nbytes) != nbytes)
{
cout << "Parent: short write to child";
exit(1);
}
cout << "The following string has been written to engine:\n"
<< string(1, '\t') << str;
char readBuffer[2];
string output = "";
while (1)
{
int bytes_read = read(mFD_c2p[0], readBuffer, sizeof(char));
if (readBuffer[0] == '\n')
break;
readBuffer[bytes_read] = '[=10=]';
output += readBuffer;
}
cout << "Got: " << output;
}
debian 和 red hat 之间的管道没有区别,但以下问题列表可能会对您有所帮助:
-Ubuntu 和 Fedora 使用相同的体系结构吗(64 位与 32 位)?
-您是否使用相同版本的 gcc(或任何其他编译器)?
(建议:将 cerr 用于您的错误输出,也许您的调试输出也是 -> 您复制了标准输出和输入,因此如果出现故障您可能看不到它)
无论如何,下面是如何将它变成一个独立的、可编译的示例:
鳕鱼
#cat stockfish
tr a-z A-Z #just so we do something
echo #need to end with a "\n" or else the parent won't break out of the while loop
运行 命令:
make pipes && PATH=.:$PATH pipes
pipes.cc
//pipes.cc
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
using namespace std;
int mFD_p2c [2];
int mFD_c2p [2];
int mEnginePID;
if (pipe(mFD_p2c) != 0 || pipe(mFD_c2p) != 0)
{
cout << "Failed to pipe";
exit(1);
}
mEnginePID = fork();
if (mEnginePID < 0)
{
cout << "Fork failed";
exit(-1);
}
else if (mEnginePID == 0)
{
if (dup2(mFD_p2c[0], 0) != 0 ||
close(mFD_p2c[0]) != 0 ||
close(mFD_p2c[1]) != 0)
{
cout << "Child: failed to set up standard input";
exit(1);
}
if (dup2(mFD_c2p[1], 1) != 1 ||
close(mFD_c2p[1]) != 0 ||
close(mFD_c2p[0]) != 0)
{
cout << "Child: failed to set up standard output";
exit(1);
}
string engine = "stockfish";
char *const args[]={};
int ret;
execvp(engine.c_str(), args);
//I need the endl here or else it doesn't show for me when the execvp fails; I wasn't able to compile the original exec command so I used a different one from the exec* family
cout << "Failed to execute " << engine << endl;
exit(1);
}
else
{
close(mFD_p2c[0]);
close(mFD_c2p[1]);
string str = "uci";
int nbytes = str.length();
if (write(mFD_p2c[1], str.c_str(), nbytes) != nbytes)
{
cout << "Parent: short write to child";
exit(1);
}
//My particular child process tries to read to the end, so give it the EOF
close(mFD_p2c[1]);
cout << "The following string has been written to engine:\n"
<< string(1, '\t') << str;
char readBuffer[2];
string output = "";
while (1)
{
int bytes_read = read(mFD_c2p[0], readBuffer, sizeof(char));
if (readBuffer[0] == '\n')
break;
readBuffer[bytes_read] = '[=12=]';
output += readBuffer;
}
cout << "Got: " << output;
}
return 0;
}
输出:
The following string has been written to engine:
uciGot: UCI
我看到您正在使用 Stockfish。我也确实经历过 Stockfish 的这种行为。问题在于它如何处理输出。定义于 misc.h
:
#define sync_cout std::cout << IO_LOCK
再次查看代码,我们会发现 IO_LOCK
是一个枚举,用于 cout 的重载友元运算符:
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m;
if (sc == IO_LOCK)
m.lock();
if (sc == IO_UNLOCK)
m.unlock();
return os;
}
我在这里看到的是,在使用 cout 期间,互斥量被锁定。我不知道这究竟如何影响 cout 在管道而不是 stdout 中的输出,但我肯定这是问题的原因。您可以通过删除锁定功能来检查它。
Edit:我忘了提到管道行为在基于 linux 的系统中没有什么不同,如前所述,但处理互斥锁的发行版之间可能存在细微差别与管道一起使用。
在我的项目中的某个地方,我使用 fork 和 pipe 来执行另一个进程并通过管道传输它的 I/O 以与其通信(我正在用 C++ 编写它)。我在Ubuntu 14.04编译的时候没有问题,可以正常运行,但是我在WMWare虚拟机的fedora编译的时候开始出现奇怪的事情。如果我 运行 终端中的二进制文件,则没有错误,但不会在管道中写入任何内容(但获取字符流将起作用)。我试图在 fedora 中调试我的代码,我在我的代码中放置了一个断点,但是当进程试图从管道读取时给出了一个损坏的管道信号(在终端中执行时没有信号)。 那么,你们以前遇到过这样的问题吗? debian 和 red hat linux 之间的管道有什么区别吗?还是因为我在虚拟机上 运行ning fedora?
代码:
int mFD_p2c [2];
int mFD_c2p [2];
int mEnginePID;
if (pipe(mFD_p2c) != 0 || pipe(mFD_c2p) != 0)
{
cout << "Failed to pipe";
exit(1);
}
mEnginePID = fork();
if (mEnginePID < 0)
{
cout << "Fork failed";
exit(-1);
}
else if (mEnginePID == 0)
{
if (dup2(mFD_p2c[0], 0) != 0 ||
close(mFD_p2c[0]) != 0 ||
close(mFD_p2c[1]) != 0)
{
cout << "Child: failed to set up standard input";
exit(1);
}
if (dup2(mFD_c2p[1], 1) != 1 ||
close(mFD_c2p[1]) != 0 ||
close(mFD_c2p[0]) != 0)
{
cout << "Child: failed to set up standard output";
exit(1);
}
string engine = "stockfish";
execlp(engine.c_str(), (char *) 0);
cout << "Failed to execute " << engine;
exit(1);
}
else
{
close(mFD_p2c[0]);
close(mFD_c2p[1]);
string str = "uci";
int nbytes = str.length();
if (write(mFD_p2c[1], str.c_str(), nbytes) != nbytes)
{
cout << "Parent: short write to child";
exit(1);
}
cout << "The following string has been written to engine:\n"
<< string(1, '\t') << str;
char readBuffer[2];
string output = "";
while (1)
{
int bytes_read = read(mFD_c2p[0], readBuffer, sizeof(char));
if (readBuffer[0] == '\n')
break;
readBuffer[bytes_read] = '[=10=]';
output += readBuffer;
}
cout << "Got: " << output;
}
debian 和 red hat 之间的管道没有区别,但以下问题列表可能会对您有所帮助:
-Ubuntu 和 Fedora 使用相同的体系结构吗(64 位与 32 位)?
-您是否使用相同版本的 gcc(或任何其他编译器)?
(建议:将 cerr 用于您的错误输出,也许您的调试输出也是 -> 您复制了标准输出和输入,因此如果出现故障您可能看不到它)
无论如何,下面是如何将它变成一个独立的、可编译的示例:
鳕鱼
#cat stockfish
tr a-z A-Z #just so we do something
echo #need to end with a "\n" or else the parent won't break out of the while loop
运行 命令:
make pipes && PATH=.:$PATH pipes
pipes.cc
//pipes.cc
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
using namespace std;
int mFD_p2c [2];
int mFD_c2p [2];
int mEnginePID;
if (pipe(mFD_p2c) != 0 || pipe(mFD_c2p) != 0)
{
cout << "Failed to pipe";
exit(1);
}
mEnginePID = fork();
if (mEnginePID < 0)
{
cout << "Fork failed";
exit(-1);
}
else if (mEnginePID == 0)
{
if (dup2(mFD_p2c[0], 0) != 0 ||
close(mFD_p2c[0]) != 0 ||
close(mFD_p2c[1]) != 0)
{
cout << "Child: failed to set up standard input";
exit(1);
}
if (dup2(mFD_c2p[1], 1) != 1 ||
close(mFD_c2p[1]) != 0 ||
close(mFD_c2p[0]) != 0)
{
cout << "Child: failed to set up standard output";
exit(1);
}
string engine = "stockfish";
char *const args[]={};
int ret;
execvp(engine.c_str(), args);
//I need the endl here or else it doesn't show for me when the execvp fails; I wasn't able to compile the original exec command so I used a different one from the exec* family
cout << "Failed to execute " << engine << endl;
exit(1);
}
else
{
close(mFD_p2c[0]);
close(mFD_c2p[1]);
string str = "uci";
int nbytes = str.length();
if (write(mFD_p2c[1], str.c_str(), nbytes) != nbytes)
{
cout << "Parent: short write to child";
exit(1);
}
//My particular child process tries to read to the end, so give it the EOF
close(mFD_p2c[1]);
cout << "The following string has been written to engine:\n"
<< string(1, '\t') << str;
char readBuffer[2];
string output = "";
while (1)
{
int bytes_read = read(mFD_c2p[0], readBuffer, sizeof(char));
if (readBuffer[0] == '\n')
break;
readBuffer[bytes_read] = '[=12=]';
output += readBuffer;
}
cout << "Got: " << output;
}
return 0;
}
输出:
The following string has been written to engine:
uciGot: UCI
我看到您正在使用 Stockfish。我也确实经历过 Stockfish 的这种行为。问题在于它如何处理输出。定义于 misc.h
:
#define sync_cout std::cout << IO_LOCK
再次查看代码,我们会发现 IO_LOCK
是一个枚举,用于 cout 的重载友元运算符:
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m;
if (sc == IO_LOCK)
m.lock();
if (sc == IO_UNLOCK)
m.unlock();
return os;
}
我在这里看到的是,在使用 cout 期间,互斥量被锁定。我不知道这究竟如何影响 cout 在管道而不是 stdout 中的输出,但我肯定这是问题的原因。您可以通过删除锁定功能来检查它。
Edit:我忘了提到管道行为在基于 linux 的系统中没有什么不同,如前所述,但处理互斥锁的发行版之间可能存在细微差别与管道一起使用。