[英]Using std::cin to read from pipe after dup2()
我只是想编写一个简单的回送功能,该功能在设置管道并使用dup2()复制到stdin和stdout后在子进程中运行。 但是环回挂起它试图从管道读取的点。 管道的写端(在父进程中)是使用fputs()的C函数。 我知道父进程有效,因为如果子回送功能被另一个在C中使用read()的函数替代了,则它可以正常工作。
一旦完成这项工作,我就可以用exec()替换回送功能,并且我希望它可以与用C ++编写的程序一起使用。
有很多与此类似的问题,但是调用setvbuf()之类的解决方案对我不起作用(您可以看到我确实在父回送函数中对其进行了调用)。 其他提问者直接在管道文件描述符上使用read()(在我执行该操作时可以工作-但我想在C ++中使用std :: cin进行测试)。
因此主要功能如下:
int pipeIn[2]; // To be read by child process
int pipeOut[2]; // To be written by child process
#define PARENT_TO_CHILD_READ_END pipeIn[0]
#define PARENT_TO_CHILD_WRITE_END pipeIn[1]
#define CHILD_TO_PARENT_READ_END pipeOut[0]
#define CHILD_TO_PARENT_WRITE_END pipeOut[1]
int main(int argc, char** argv) {
pipe(pipeIn);
pipe(pipeOut);
pid_t hijo = fork();
if (hijo == 0) {
// CHILD
dup2(PARENT_TO_CHILD_READ_END, STDIN_FILENO);
dup2(CHILD_TO_PARENT_WRITE_END, STDOUT_FILENO);
close(PARENT_TO_CHILD_READ_END);
close(CHILD_TO_PARENT_WRITE_END);
close(PARENT_TO_CHILD_WRITE_END);
close(CHILD_TO_PARENT_READ_END);
Child_plusplus_Loopback();
} else if (hijo == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else {
// PARENT
close(PARENT_TO_CHILD_READ_END);
close(CHILD_TO_PARENT_WRITE_END);
Parent_FILE_Loopback(
PARENT_TO_CHILD_WRITE_END,
CHILD_TO_PARENT_READ_END);
close(PARENT_TO_CHILD_WRITE_END);
close(CHILD_TO_PARENT_READ_END);
wait(NULL);
}
return 0;
}
环回功能如下所示:-
void
Parent_FILE_Loopback(const int outPipe, const int inPipe) {
FILE * toChild = fdopen(outPipe, "w");
FILE * fromChild = fdopen(inPipe, "r");
setvbuf(toChild, NULL, _IONBF, 0);
fputs("Hello", toChild);
const size_t bufferSize(256);
char buffer[ bufferSize ];
fgets(buffer, bufferSize, fromChild);
printf("PARENT : %s\n\n", buffer);
}
void
Child_plusplus_Loopback(void) {
string buffer;
cin >> buffer; // this hangs
string message("CHILD : ");
message += buffer;
cout << message;
}
strace -f的输出如下所示:-
clone(Process 6989 attached (waiting for parent)
Process 6989 resumed (parent 6988 ready)
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb74e5768) = 6989
[pid 6989] dup2(3, 0 <unfinished ...>
[pid 6988] close(3) = 0
[pid 6989] <... dup2 resumed> ) = 0
[pid 6988] close(6 <unfinished ...>
[pid 6989] dup2(6, 1 <unfinished ...>
[pid 6988] <... close resumed> ) = 0
[pid 6989] <... dup2 resumed> ) = 1
[pid 6988] fcntl64(4, F_GETFL <unfinished ...>
[pid 6989] close(3 <unfinished ...>
[pid 6988] <... fcntl64 resumed> ) = 0x1 (flags O_WRONLY)
[pid 6989] <... close resumed> ) = 0
[pid 6989] close(6 <unfinished ...>
[pid 6988] brk(0 <unfinished ...>
[pid 6989] <... close resumed> ) = 0
[pid 6988] <... brk resumed> ) = 0x848f000
[pid 6989] close(4 <unfinished ...>
[pid 6988] brk(0x84b0000 <unfinished ...>
[pid 6989] <... close resumed> ) = 0
[pid 6988] <... brk resumed> ) = 0x84b0000
[pid 6989] close(5) = 0
[pid 6988] fstat64(4, <unfinished ...>
[pid 6989] fstat64(0, <unfinished ...>
[pid 6988] <... fstat64 resumed> {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
[pid 6989] <... fstat64 resumed> {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
[pid 6988] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <unfinished ...>
[pid 6989] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <unfinished ...>
[pid 6988] <... mmap2 resumed> ) = 0xb77d3000
[pid 6989] <... mmap2 resumed> ) = 0xb77d3000
[pid 6988] _llseek(4, 0, <unfinished ...>
[pid 6989] read(0, <unfinished ...>
[pid 6988] <... _llseek resumed> 0xbfefde40, SEEK_CUR) = -1 ESPIPE (Illegal seek)
[pid 6988] fcntl64(5, F_GETFL) = 0 (flags O_RDONLY)
[pid 6988] fstat64(5, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
[pid 6988] mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77d2000
[pid 6988] _llseek(5, 0, 0xbfefde40, SEEK_CUR) = -1 ESPIPE (Illegal seek)
[pid 6988] munmap(0xb77d3000, 4096) = 0
[pid 6988] write(4, "Hello", 5) = 5
[pid 6989] <... read resumed> "Hello", 4096) = 5
[pid 6988] read(5, <unfinished ...>
[pid 6989] brk(0) = 0x848f000
[pid 6989] brk(0x84b0000) = 0x84b0000
[pid 6989] read(0, 0xb77d3000, 4096) = ? ERESTARTSYS (To be restarted)
[pid 6988] <... read resumed> 0xb77d2000, 4096) = ? ERESTARTSYS (To be restarted)
[pid 6989] --- SIGWINCH (Window changed) @ 0 (0) ---
[pid 6988] --- SIGWINCH (Window changed) @ 0 (0) ---
[pid 6989] read(0, <unfinished ...>
[pid 6988] read(5,
从std::cin
(或FILE*
)进行的任何读取通常都会被缓冲,这意味着对read()
的调用将请求大量字节。 系统安排从控制台进行的读取返回,即使已读取较少的字节也是如此,但这并不适用于从管道进行的读取。 仅当缓冲区已填充或管道的写侧已关闭时,才会调用调用填充缓冲区的read
。
您可以通过调用std::cin.setbuf
稍微控制缓冲,但这可能很棘手。 通常,它应该在std::cin
的第一个输入之前完成。 而且,如果您使用>>
输入字符串, std::cin
将继续调用read
直到看到空格(或文件结尾)为止。
编辑:
现在还不清楚您要做什么。 如果您需要基于消息的协议,那么您不仅需要管道和iostream,还需要更多的东西。 这两个都是面向流的,而不是面向消息的。
我过去处理此消息的通常方式是编写消息的长度(例如,四字节整数),然后是消息。 如果管道上只有一个读者和一个作家,这是相当简单的。 如果有更多的作者,则必须确保每次写入的长度加上消息都是原子的,并且如果有多个读者,则不能真正做到这一点-每个读者都需要一个单独的管道。 对于文本格式,可以使用std::ostringstream
和std::istringstream
; 编写时,将std::ostringstream
的数据转换为字符串,然后:
void
writeOneMessage( int fd, std::string const& message )
{
std::size_t size = message.size();
char sizeBuffer[4] =
{
(size >> 24) & 0xFF,
(size >> 16) & 0xFF,
(size >> 8) & 0xFF,
(size ) & 0xFF
}
write( fd, sizeBuffer, 4 );
write( fd, message.data(), message.size() );
}
读取要复杂得多,因为您必须检查许多其他错误情况:
std::string
readOneMessage( int fd )
{
char sizeBuffer[4];
if ( read( fd, sizeBuffer, 4 ) != 4 ) {
// Really too simple: if you read 0, it's end of file
// if you read anythong other than 0 or 4, it's a serious
// error.
}
size_t size = ((sizeBuffer[0] & 0xFF) << 24)
| ((sizeBuffer[1] & 0xFF) << 16)
| ((sizeBuffer[2] & 0xFF) << 8)
| ((sizeBuffer[3] & 0xFF) );
std::string message( size );
if ( read( fd, &message[0], size ) != size ) {
// Can only be a format error...
}
return message;
}
同样,一旦您阅读了该消息,就可以使用它来构造一个std::istringstream
,并根据需要对其进行解析。
这实际上是您可以通过管道可靠地实现基于消息的协议的唯一方法; 另一种方法是为每条消息写一个以'\\0'
结尾的字符串,并逐字节读取直到找到'\\0'
为止。 (实际上,使用FILE*
,将所有流设置为行缓冲,通常会在大多数时间工作,前提是消息足够小,但并不能保证也不可靠。)
从std::cin
读取通常会被行缓冲。 这意味着>>
运算符只有在读取换行符或到达流的末尾后才返回。 即使std::cin
发生不以你的情况进行缓冲,流必须不断尝试读取,直到它看到字符串的结束,这发生在空白或流的末尾( 不一定在目前结束-可用字节)。 无论如何,您都不必担心底层read()
调用的详细信息。 这是您的C ++库实现的责任。
您的服务器将“ Hello”的五个字符写入管道,但是fputs()
不会自动在它们后跟换行符(不同于puts()
)或其他任何字符。 如果要发送换行符-以便与读取端的行缓冲良好地互操作-那么必须显式发送:
fputs("Hello\n", toChild);
要么
fputs("Hello", toChild);
fputc('\n', toChild);
即使读取端没有缓冲,您也需要至少发送一个空格或制表符,以便读取器可以识别字符串的结尾。 只要您仍然需要这样做,最好使用换行符。
无论如何,如果输出流已缓冲,那么您可能需要使用fflush(toChild)
进行后续处理,但是由于您明确地使其fflush(toChild)
缓冲(不一定是明智的选择),因此上述内容足以使客户的阅读回报。
请注意,类似的考虑也适用于子级发回父级的消息: fgets()
读取直到换行符或EOF,并且看起来子级不以换行符终止其答复消息。 或冲洗。
正确设置文件描述符后,您需要一个已定义的通信协议。
使用std :: cout / std :: cin的管道:
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/wait.h>
int parent_pipe[2];
int child_pipe[2];
void parent_loop() {
std::cout << "Hello Child" << ' ' << "Disconnect" << std::endl;
std::string receive("Parent Receives: ");
while(true) {
std::string buffer;
if( ! (std::cin >> buffer) || buffer == "Disconnect")
break;
receive += buffer;
}
std::cout << "Disconnect";
std::cerr << receive << std::endl;
}
void child_loop() {
std::string receive(" Child Receives: ");
while( true) {
std::string buffer;
if( ! (std::cin >> buffer) || buffer == "Disconnect")
break;
receive += buffer;
}
std::cout << "Hello Parent" << std::endl;
std::cout << "Disconnect" << std::endl;
std::cerr << receive << std::endl;
}
int main(int argc, char** argv) {
pipe(parent_pipe);
pipe(child_pipe);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return EXIT_FAILURE;
}
else if (pid) {
// Parent
if((dup2(child_pipe[0], STDIN_FILENO) == -1)
|| (dup2(parent_pipe[1], STDOUT_FILENO) == -1))
{
std::cerr << "Setup Failure\n";
return EXIT_FAILURE;
}
close(child_pipe[1]);
close(parent_pipe[0]);
parent_loop();
close(child_pipe[0]);
close(parent_pipe[1]);
}
else {
// Child
if((dup2(parent_pipe[0], STDIN_FILENO) == -1)
|| (dup2(child_pipe[1], STDOUT_FILENO) == -1))
{
std::cerr << "Setup Failure\n";
return EXIT_FAILURE;
}
close(parent_pipe[1]);
close(child_pipe[0]);
child_loop();
close(parent_pipe[0]);
close(child_pipe[1]);
}
return 0;
}
请注意使用std::endl
将分隔符放入流中并刷新流。 如果要使用未格式化的IO,则可以使用读写功能(子级可能会读取父级写入的块,直到EOF为止)。 注意:记录和错误消息转到std :: cerr(STDERR_FILENO)
输出:
Child Receives: HelloChild
Parent Receives: HelloParent
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.