通过管道读写数据
我使用 fork() 创建了两个进程。子进程正在生成可变数量的数据(数组字符)并将其连续写入管道。父进程从管道读取数据并将接收到的数据打印到stdout。
代码非常简单:
switch (fork()) {
case -1:
exit (1);
break;
case 0:
close(fd[0]);
generate_data(fd[1]);
break;
default:
close(fd[1]);
while(1) {
n = read(fd[0], readbuffer, sizeof(readbuffer));
readbuffer[n] = 0;
if (n > 0)
printf ("read: %s\n", readbuffer);
else
exit(1);
}
break;
}
generate_data(int)
迭代列表,将每个元素(字符串)写入作为参数给出的文件描述符(在本例中为管道的写入端):
void generate_data(int fd)
{
node_t node* = list;
while (node != NULL) {
write(fd, node->data, strlen(node->data)+1);
node = node->next();
}
}
其中 这里的问题是输出总是不可预测的:当另一个进程正在处理最后一个读取时,子进程将数据写入管道,因此当它再次调用读取时,其余数据不再存在。
根据 man 2 pipeline
的说法,这种情况不应该发生:
写入管道写入端的数据由 内核直到从管道的读取端读取它。
获取包含 10 个元素的列表,一些输出示例:
示例 1:
read: element_4
read: element_8
read: element_9
示例 2:
read: element_7
read: element_8
read: element_9
read: element_10
示例 3:
read: element_2
read: element_8
有人知道这里发生了什么吗?
I have created two processes using fork(). The child process is producing and writing continuously a variable amount of data (array char) to the pipe. The parent process reads from the pipe and prints the received data to stdout.
The code is very simple:
switch (fork()) {
case -1:
exit (1);
break;
case 0:
close(fd[0]);
generate_data(fd[1]);
break;
default:
close(fd[1]);
while(1) {
n = read(fd[0], readbuffer, sizeof(readbuffer));
readbuffer[n] = 0;
if (n > 0)
printf ("read: %s\n", readbuffer);
else
exit(1);
}
break;
}
Where generate_data(int)
iterates over a list, writing each element (string) to the file descriptor given as argument (the write end of the pipe in this case):
void generate_data(int fd)
{
node_t node* = list;
while (node != NULL) {
write(fd, node->data, strlen(node->data)+1);
node = node->next();
}
}
The problem here is that the output is always unpredictable: the child process writes data to the pipe when the other process is processing the last read
, so when it calls to read again the rest of the data is not there anymore.
According to man 2 pipe
, this shouldn't be happening:
Data written to the write end of the pipe is buffered by the
kernel until it is read from the read end of the pipe.
Taking a list of 10 elements, some output examples:
Example 1:
read: element_4
read: element_8
read: element_9
Example 2:
read: element_7
read: element_8
read: element_9
read: element_10
Example 3:
read: element_2
read: element_8
Anyone has any idea what's happening here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您调用 read 并捕获返回值,但随后您基本上会忽略它;它告诉您 readbuffer 有多少有效字节,但您将 readbuffer 视为包含以零结尾的字符串,但事实并非如此。事实上,如果您的数据写入过程通过管道发送
0
字节,则单个读取
可能会给您多个以零结尾的字符串;使用printf
意味着您将忽略第二个和后续的。至少,您需要使用 fwrite 将特定的、正确的字节数写入标准输出,尽管我怀疑您实际上需要做的是首先用换行符替换这些零。修改generate_data
以发送换行符而不是零可能是一个更好的主意。You call
read
and capture the return value, but then you largely ignore it; it's telling you how many valid bytes are inreadbuffer
, but you're treatingreadbuffer
as if it contains a zero-terminated string, which it does not necessarily. In fact, a singleread
may be giving you multiple zero-terminated strings, if your data-writing process is sending0
bytes through the pipe; usingprintf
means you're ignoring the second and subsequent ones. At the very least, you'll need to usefwrite
to write the specific, correct number of bytes to stdout, although I suspect what you'll actually need to do is replace those zeroes with newlines first. It might be a better idea to modifygenerate_data
to send newlines instead of zeroes.读取不会在 nul 字符处停止,您可能会在一次 read() 调用中读取两条“消息”。因此,您的读取器必须检查第一个 0 之后(但在读取的 n 个字节内)是否有更多数据,并保存它。下一次读取调用应将其数据附加到此剩余部分。一种特殊情况是缓冲区中有剩余但尚未完整的消息。
Read does not stop at the nul character, you might read two "messages" in one read() call. So your reader has to check if there is more data after the first 0 (but within the n bytes read), and save it. A next read call should append its data to this leftover. A special case is when there is a leftover, but not yet a complete message in the buffer.