我只是尝试编写一个简单的loopback函数,该函数在管道设置完成后在子进程中运行,并使用dup2()复制到stdin和stdout。但环回在它试图从管道读取的点处挂起。管道的写端 - 在父进程中 - 是一个使用fputs()的C函数。我知道父进程是有效的,因为如果子回送函数被替换为在C中使用read()的另一个函数,它就可以工作。
一旦我完成了这项工作,我将能够用exec()替换loopback函数,并且我希望能够使用用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,
答案 0 :(得分:0)
std::cin
(或FILE*
)的任何读取通常都会被缓冲,
这意味着对read()
的调用会请求大量的。{1}}
字节。系统安排从控制台读取甚至返回
如果读取的字节数较少,但这并不适用于从a读取
管;调用填充缓冲区的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*
,将所有流设置为
行缓冲,通常会在大多数情况下工作,提供消息
足够小,但它不是真的有保证,也不可靠。)
答案 1 :(得分:0)
从std::cin
读取通常会行缓冲。这意味着>>
运算符在读取换行符或到达流末尾之前不会返回。即使std::cin
在您的情况下没有缓冲,流也必须继续尝试读取,直到它看到字符串的结尾,这发生在空格或流的末尾(不是必然在当前可用字节的末尾)。无论如何,您无需担心基础read()
调用的详细信息。这是您的C ++库实现的责任。
您的服务器写入&#34; Hello&#34;的五个字符。到管道,但fputs()
不会自动跟他们使用换行符(不像puts()
)或其他任何内容。如果你想要发送换行符 - 在读取端与线缓冲很好地互操作 - 那么你必须明确地发送它:
fputs("Hello\n", toChild);
或
fputs("Hello", toChild);
fputc('\n', toChild);
即使读取方是无缓冲的,您也需要至少发送一个空格或制表符,以便读者能够识别字符串的结尾。只要你还需要这样做,你最好使用换行符。
任何方式,如果输出流被缓冲,那么你可能想用fflush(toChild)
来跟进它,但是因为你明确地使它无缓冲(不一定是明智的选择),上面应该是足以使客户的阅读回归。
请注意,类似的考虑因素适用于孩子发送回父母的邮件:fgets()
读取换行符或EOF,并且看起来孩子不会终止其回复带换行符的消息。或冲洗。
答案 2 :(得分:0)
正确设置文件描述符后,您需要一个已定义的通信协议。
使用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