2011-09-07 136 views
2

我使用fork()创建了两个进程。子进程正在生成并向管道连续写入可变数量的数据(数组char)。父进程从管道读取并将接收到的数据打印到标准输出。通过管道读取和写入数据

的代码非常简单:

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(); 
    } 

} 

这里的问题是,输出始终是不可预测的:子进程将数据写入到管道时其他进程正在处理最后read,所以当它调用再次读取数据的其余部分是不存在了。

根据man 2 pipe,这不应该发生:

写入管道写端的数据由 内核缓冲,直到它被从管道的读端读取。

以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 

任何人都知道这里发生了什么?

+0

不是没有代码显示的写作,也许一些示例输出。 –

+0

究竟是什么问题? Minor nitpick:read()不读取以空字符结尾的字符串。你的%s格式需要一个nul结尾的字符串。也许你应该添加“readbuffer [n] = 0;”阅读后,给予足够的空间。 – wildplasser

+0

感谢您的意见。我刚刚编辑了一些更多信息的问题。 – alnacle

回答

3

你打电话read并捕获返回值,但你很大程度上忽略它;它告诉你readbuffer有多少个有效字节,但是你认为readbuffer就好像它包含一个零终止的字符串,它不一定是这样。实际上,如果您的数据写入过程通过管道发送0字节,则单个read可能会给您多个零终止的字符串;使用printf意味着你忽略了第二个和后续的。至少,您需要使用fwrite将特定的正确字节数写入stdout,尽管我怀疑您实际需要做的是先用newlines替换那些零。修改generate_data发送换行符而不是零可能是一个更好的主意。

+2

特别是,可能发生的情况是两个或多个字符串到达​​一个'read()'调用 - 但代码仅打印* first *字符串并忽略其余部分,因此看起来好像它们已经丢失。 – caf

+0

哦,是的,我敢打赌就是这样。写入过程通过管道发送'0'字节;他们正处于数据的中间! –

+0

@厄内斯特,你是对的。在读完之后我忘记了'readbuffer [n] = 0'。抱歉。但无论如何,问题仍然存在。 – alnacle

0

阅读不停止在nul字符处,您可能会在一个read()调用中读取两个“消息”。因此,读者必须检查在第一个0(但在读取的n个字节内)之后是否有更多数据,并保存它。下一次阅读电话会将其数据附加到这个剩余物。特殊情况是有剩余的时候,但缓冲区中还没有完整的消息。