使用缓冲的目的当然就是为了提高性能了。在UNIX中,I/O 有两层缓冲:
1. 内核缓冲
2. stdio 库缓冲
内核缓冲:
当使用系统调用write
的时候,并不是直接写入磁盘,这样性能太挫。而是先写到内核缓冲区,等缓冲区满了,或者调用sync
的时候,才写入磁盘。经过实验,我们发现当缓冲区大小为4096个字节时,性能几乎达到最优,于是乎就用这个值了。
stdio 库缓冲:
当使用printf
及其同类的时候,并不相当于调用了write
,而是在用户空间中开辟了一块 stdio 缓冲区,先写到这里。为什么又要加一层缓冲呢?试想,如果用户反复地调用write
,那么就会产生大量的系统调用。系统调用虽然很快,但是多了的话开销还是很大的咧!为了减少系统调用带来的开销,于是有了 stdio 库缓冲。
混合使用:
试编译如下代码:
printf("If I have more time, \n");
write (STDOUT_FILENO, "I would have written you a shorter letter.\n", 43);
如果执行./a.out
,输出如下:
If I have more time,
I would have written you a shorter letter.
如果执行./a.out > outfile
,outfile 如下:
I would have written you a shorter letter.
If I have more time,
其实第二种情况反而比较好理解,因为write
直接写到内核缓冲,而printf
先写到stdio缓冲,然后再写到内核缓冲,所以输出就慢了。
而第一种情况为什么又是按顺序的呢?其实是因为'\n'
。如果把'\n'
去掉,输出就跟outfile一样了。原因是,如果输出到终端,且含有'\n'
,就会直接flush掉,不会缓冲。
练习:
了解了UNIX的缓冲机制,来一道小题联系联系:实现tail -n
功能(使用 lseek, read, write 等实现)。
实现如下所示,
#include "tlpi_hdr.h"
#include <fcntl.h>
#define MAX_BUF_SIZE 4096
int main(int argc, char const *argv[])
{
int fd;
int lineNum = 10;
off_t offset;
int read_size;
int numRead;
char buf[MAX_BUF_SIZE];
int ch;
/*Get option: [-n num]*/
while ((ch = getopt(argc, argv, "n:")) != -1) {
switch (ch) {
case 'n':
lineNum = atoi(optarg);
break;
case '?':
default:
printf("Usage: ./tail [-n num] [file]\n");
exit(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;
/* Open file. */
fd = open(argv[0], O_RDONLY);
if (fd == -1)
errExit("open");
/* Seek to end and read backward. */
offset = lseek (fd, 0, SEEK_END);
while (offset > 0) {
/* Seek backward. */
read_size = min (offset, MAX_BUF_SIZE);
offset = lseek (fd, -read_size, SEEK_CUR);
if (offset == -1)
errExit("lseek");
/* pread will not change the offset. */
numRead = pread (fd, buf, read_size, offset);
if (numRead == -1)
errExit("pread");
/* Count how many lines in buf. */
if (buf[numRead - 1] == '\n') // Some files are not end of '\n'.
numRead--;
for (; numRead > 0; numRead--) {
if (buf[numRead - 1] == '\n') lineNum--;
if (lineNum <= 0) break;
}
if (numRead > 0) {
offset = lseek (fd, numRead, SEEK_CUR);
if (offset == -1)
errExit ("lseek");
break;
}
}
/* print all the last n lines.*/
while ((numRead = read(fd, buf, MAX_BUF_SIZE)) > 0) {
if (write(STDOUT_FILENO, buf, numRead) == -1)
errExit("write");
}
close(fd);
exit (0);
}