我在C中探索IO缓冲区实现。我正在使用Ubuntu 12.04 / GCC。 对行缓冲行为的方式有疑问并触发底层系统调用read()。我对行缓冲区实现的理解是,在遇到流中的新行时,会对read()进行系统调用。 但是,如果你看第一个read(),它会读取200个字节。第一次写入在另一次read()完成之前打印114个字符,因为第二次写入在流中有一个新行。 但是,如果您查看后续的read(),它看起来不像是由换行符触发。 它总是读取200个字节,而不管流中是否有新行。另外,在第二次读取完成之前,流中已经有大约90个字符,当缓冲区大小为200时,如何读取额外的200个字符。 注意:如果缓冲在setvbuf中从Line缓冲更改为完全缓冲,则输出完全相同。有人能说清楚为什么这种行为会出现在线路缓冲应该如何触发底层系统调用?
以下是代码:
#define KBUFFER 200
#define LBUFFER 100
int main(int argc, char *argv[])
{
FILE *file;
char lbuf[LBUFFER], kbuf[KBUFFER];
if ((file = fopen("testing", "r")) == NULL){
printf("Failed to open testing\n");
exit(1);
}
// Set Line Buffer mode
setvbuf(file, kbuf, _IOLBF, KBUFFER);
while ( (fgets(lbuf, LBUFFER, file) != NULL ) ){
printf("[%s]", lbuf);
}
return 0;
}
测试中每行为110个字符;
strace的输出是:
open("testing", O_RDONLY) = 3
read(3, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 200) = 200
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7064a98000
write(1, "[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"...,114[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa][aaaaaaaaaaa
) = 114
write(1, "]", 1])
= 1
read(3, "bbbbbbbbbbbbbbbbbbbbb\ncccccccccc"..., 200) = 200
write(1, "[bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"..., 114[bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb][bbbbbbbbbbb
) = 114
write(1, "][cccccccccccccccccccccccccccccc"..., 115][ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc][ccccccccccc
) = 115
write(1, "]", 1]) = 1
read(3, "dddddddddddddddddddddddddddddddd"..., 200) = 200
write(1, "[ddddddddddddddddddddddddddddddd"..., 114[ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd][ddddddddddd
) = 114
write(1, "][eeeeeeeeeeeeeeeeeeeeeeeeeeeeee"..., 115][eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee][eeeeeeeeeee
) = 115
write(1, "]", 1])
= 1
read(3, "ffffffffffffffffffffffffffffffff"..., 200) = 200
write(1, "[fffffffffffffffffffffffffffffff"..., 114[fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff][fffffffffff
) = 114
write(1, "][gggggggggggggggggggggggggggggg"..., 115][ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg][ggggggggggg
) = 115
write(1, "]", 1]) = 1
read(3, "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"..., 200) = 88
write(1, "[hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"..., 114[hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh][hhhhhhhhhhh
) = 114
write(1, "]", 1]) = 1
read(3, "", 200) = 0
exit_group(0)
答案 0 :(得分:1)
对行缓冲行为的方式有疑问并触发底层系统调用read()
读取总是在C库中缓冲。
线路缓冲和其他缓冲模式仅适用于输出流。
答案 1 :(得分:0)
只允许输出行缓冲:
参数模式确定 如何缓冲流,如下所示:_IOFBF导致输入/输出 完全缓冲; _IOLBF使输出成为行缓冲; _IONBF 导致输入/输出无缓冲。
在实践中,某些库实现将输入流上的_IOLBF规范视为等效于_IOFBF,但标准中未指定此行为。
答案 2 :(得分:0)
fgets
正在使用自己的内部缓冲区来存储已读取但尚未返回的数据(因为它位于另一条线路上)。可以自由地将此缓冲区设置为作者希望的任何大小。唯一的限制是它不能将超过200个字节复制到你的缓冲区中。
我认为200字节的读取确实与您的缓冲区大小相匹配,这是一种有意识的优化,但同样,实现可以自由使用任何大小;它对你的代码没有任何影响。
您应不使用您以此方式获取的任何数据来“优化”您的程序,因为它不可移植,并且可能会在您下次更新C库时更改。
如果确切的读/写大小对您很重要,那么您应该完全绕过stdio并自己调用系统调用。
最后,您引用的行缓冲仅适用于输出流,而不是输入。