看到标准IO库,总会不自然地往回翻文件IO的部分
文件IO里的read,write都是内核态系统调用,依赖于操作系统来对文件进行读写,虽然速度快,但都是不带缓冲的,而且之前有写IO的效率,CPU用时还与其中一个参数每次读取的字节数相关
标准库IO会先在内存开辟一个缓冲区,给程序中文件来使用,比如执行读操作,先从磁盘文件将数据读入内存缓冲区,满了之后再从内存缓冲区一次读入;写操作先将数据写入内存缓冲区,满了之后再写入文件;因此缓冲区的大小会决定读写磁盘IO的次数,也会影响着执行速度,效率
标准IO库其实就在系统IO上封装了一层缓冲,目的是为了减少使用read,write调用的次数,对每个IO流自行进行缓冲处理
(1)全缓冲是在塞满了标准IO缓冲区后才进行实际的IO操作,磁盘上的文件通常是这样,在一个流上执行第一次IO操作时,标准IO函数通常调用malloc获取需要的缓冲区
(2)行缓冲是在输入和输出遇到换行符或者是缓冲区满了才进行实际IO操作
(3)无缓冲是不进行缓存,直接输出,也就是可能就直接通过系统调用write,read等进行IO操作
下面是网上对内核钻研的更深的人写的一段话,看了十分有感触:
1:不带缓存,不是直接对磁盘文件进行读取操作,像read()和write()函数,它们都属于系统调用,只不过在用户层没有缓存,所以叫做无缓存IO,但对于内核来说,还是进行了缓存,只是用户层看不到罢了
2:带不带缓存是相对来说的,如果你要写入数据到文件上时(就是写入磁盘上),内核先将数据写入到内核中所设的缓冲储存器,假如这个缓冲储存器的长度是100个字节,你调用系统函:
ssize_t write (int fd,const void * buf,size_t count);
写操作时,设每次写入长度count=10个字节,那么你几要调用10次这个函数才能把这个缓冲区写满,此时数据还是在缓冲区,并没有写入到磁盘,缓冲区满时才进行实际上的IO操作,把数据写入到磁盘上,所以上面说的“不带缓存”不是没有缓存而是没有直接写进磁盘就是这个意思
那么,既然不带缓存的操作实际在内核是有缓存器的,那带缓存的IO操作又是怎么回事呢?
带缓存IO也叫标准IO,符合ANSI C 的标准IO处理,不依赖系统内核,所以移植性强,我们使用标准IO操作很多时候是为了减少对read()和write()的系统调用次数,带缓存IO其实就是在用户层再建立一个缓存区,这个缓存区的分配和优化长度等细节都是标准IO库代你处理好了,不用去操心,还是用上面那个例子说明这个操作过程:
上面说要写数据到文件上,内核缓存(注意这个不是用户层缓存区)区长度是100字节,我们调用不带缓存的IO函数write()就要调用10次,这样系统效率低,现在我们在用户层建立另一个缓存区(用户层缓存区或者叫流缓存),假设流缓存的长度是50字节,我们用标准C库函数的fwrite()将数据写入到这个流缓存区里面,流缓存区满50字节后在进入内核缓存区,此时再调用系统函数write()将数据写入到文件(实质是磁盘)上,看到这里,你应该明白一点,标准IO操作fwrite()最后还是要调用无缓存IO操作write,这里进行了两次调用fwrite()写100字节也就是进行两次系统调用write()
仔细分析这两条:
无缓存IO操作数据流向路径:数据——内核缓存区——磁盘
标准IO操作数据流向路径:数据——流缓存区——内核缓存区——磁盘
上面这例子,假如内核态缓冲区100字节,每次系统调用write写了10字节,那么系统调用就要进行10次,才能将内核缓冲区写满进而进行实际的IO操作;而通过标准IO库调用fwrite在系统调用write外面封装了用户层的缓冲区,也就是write要等到塞满了这个用户层的缓冲区才会调用,比如用户层缓冲区50字节,那么满了50字节之后,才会进入内核态(fwrite调用write)调用write一次,接着还会调用write一次,也就是这种情况下一共就只有两次write系统调用,我的理解!
可是上面这种比较,第一种情况还是每次写10字节的状态,假如是50字节呢?与后面一种情况不是一种意思么?