2017-08-15 51 views
0

下面是程序:读取和写入到一个管道ç问题

int fd[2]; 
    char buf1 [31]; 
    int i; 
    char buf2; 

    pipe(fd); 

    if (fork() == 0) { // child 
    close(fd[1]); // close writing pipe 
    for(i = 0; i< 20; i++) { 
     read(fd[0], buf1, 30); 
     printf("%s\n", buf1); 

    } 
    close(fd[0]); 
    } 

    else { // parent 
    close(fd[0]); // close reading end 
    buf2 = 'a'; 
    for (i = 0; i < 20; i++) { 
     write(fd[1], &buf2, sizeof(buf2)); 
    } 
    close(fd[1]); 
    } 
    wait(NULL); 

由于读取块,如果没有什么在那里,我们将首先写入管道。

我的问题是关于函数。 for循环运行20次,每次我向管道写入a。在20次迭代后,写入端的管道将包含aaaaaaaaaaaaaaaaaaaa

在孩子中,我读取所有20 a'sbuf1,并打印出结果。

现在是空的,因为我们写了,读了我们写的东西?

因为还有一个for循环在这里,我们再次阅读,但是这一次,我想管是空的,所以那里有东西可读。所以我觉得以后的阅读20次迭代的最终输出只是 aaaaaaaaaaaaaaaaaaaa

但实际上它是这样的:

aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 
aaaaaaaaaaaaaaaaaaaa 

为什么?我们阅读后管道是不是空的?

+1

在将数据读入缓冲区后,您不清除缓冲区,因此当您再次打印缓冲区时,您第一次读取的数据仍然存在。如果您检查函数调用的返回值,这将更清晰。 –

+0

另请注意,在读取数据后,您无法追加空终止符;因此依靠一个人是不正确的。你幸运了。或者可能不是,因为如果你已经使用了'read()'的返回值(正确)来确定终止符应该去哪里,那么你就不会感到困惑。 –

+0

@JohnBollinger这取决于;这不是一个MCVE,所以我们不知道'buf1'声明在哪里以及如何发生。如果它是静态的而不是在'main'中发生,或者如果它有一个未包含在这里的初始化,那么其余的字符都是0,它是安全的。如果所有这些都在'main'中,那么是的,这可能是一个问题。 –

回答

2

你是对的,如果孩子在父母写入任何东西之前调用read,呼叫将被阻止;但是,在父关闭文件描述符后,这不再是这种情况,read将返回EOF的0。

read调用不会清除缓冲区。操作系统成功读取零个字符,并因此更改缓冲区的前零个字符。也就是说,它根本不会改变buf1。因此,一旦所有数据都被读入缓冲区,printf调用将始终执行相同的操作。

请注意,您不能保证获得此输出。这是完全可能的,孩子将在父母的一些但不是全部write调用后运行,在这种情况下,它不会一次读取所有20 a s并打印较短的字符串。这就是为什么我上面说过,printf电话总是会做同样的事情一旦所有的数据被读取。要处理并非所有数据都已写入的情况,应检查返回值read。如果小于20,则没有将完整的字符串读入缓冲区(并且应该再次调用read,并且目标指针高级,以免覆盖数据);如果它是零,那么你已经达到了文件结束(写入结束被关闭);如果是负值则出现错误,您应该检查errno来处理它。

+0

那么你是说在第一次读取调用时,读取所有20个a的读取,之后,由于管道是空的,因此它不再读取,因此它不会更改缓冲区。所以20a的印20次 –

+0

是的。虽然这只是运气,你可以得到第一个'read'调用,读取3'a's并打印出来,第二个'read'调用读取其余的17'a's并打印17'a's(因为它覆盖了前三个),而其余的阅读电话都是17'a's。 –

+0

好的,谢谢。但我还有一个问题。我明白,当你写信给管道时,首先我们写了一个'a',然后我们写了另一个'a',现在我们有'aa',当我们读到时,希望我们读回'aa'。我体验过,而不是'a',我决定先写'hello',然后再写'hello'。所以我假设在管道上有'hellohello',但实际上,当我阅读时(只有一次读取呼叫),只打印“hello”,而不打印“hellohello”。为什么会这样呢? –