文章目录
为了提升IO的效率,计算机的整体各处都是存在页缓存的概念的。
作为应用进程来说,一般都会存在以4k为单位的缓存页,也就是buffer,可以想象java中的bufferreader等。。
接着,作为系统内核来说,它内部也存在页缓存(pagecache)。
最后,对于硬盘的驱动本身,也存在缓冲区。
因此可以想象,读取一份文件,会从磁盘驱动本身开始,每次读到缓冲区大小的内容才会向上级返回,这样大大提高IO的效率。
而除了多级缓存之外,我们也不难想到,IO是个很慢的操作,如果一直让CPU去频繁的跟进IO的操作,那系统效率就太低了!
因此在早期的时候,就已经出现了一个协处理DMA,它来从CPU的手中分担IO的操作,由它专门来进行对磁盘内容读取到内核的操作,而只需要最终的时候发起中断信号让CPU将内核缓冲区来自磁盘已经填满的内容一次刷洗给用户空间的程序。
上面说的,是针对读取IO操作,pagecache的一个作用凸现。
而实际上,一个进程本身也本质就是一个二进制的落在磁盘上的文件,因此也会涉及到读入用户程序的一个过程,此时也依旧是操作系统先将内容读取到pagecache进行缓存。那这样有什么好处呢?当多个进程同时试图访问一处文件的时候,不需要消耗多份的文件内存,去共享这个缓存就好了。
在linux中包装成fd文件描述符,实际它们指向的是同一份pagecache,只不过各自文件描述符的各自偏移量不同,这样才能保证进程之间对文件的读取互不影响。
实验
写一个mysh小脚本,待会儿使用的时候参数传给0
rm -rf *out*
strace -ff -o out javac OSFileIO.java && java OSFileIO $1
pcstat out.txt
查看文件缓存的大小,100%都在内存中,说明关机就会丢失数据
OSFileIO.java
public class OSFileIO {
static byte[] data = "123456789\n".getBytes();
static String path = "/root/testfileio/out.txt";
public static void main(String[] args) throws Exception {
switch ( args[0]) {
case "0" :
testBasicFileIO();
break;
case "1":
testBufferedFileIO();
break;
case "2" :
testRandomAccessFileWrite();
case "3":
// whatByteBuffer();
default:
}
}
//最基本的file写
public static void testBasicFileIO() throws Exception {
File file = new File(path);
FileOutputStream out = new FileOutputStream(file);
while(true){
// Thread.sleep(10);
out.write(data);
}
}
//测试buffer文件IO
// jvm 8kB syscall write(8KBbyte[])
public static void testBufferedFileIO() throws Exception {
File file = new File(path);
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
while(true){
Thread.sleep(10);
out.write(data);
}
}
用上面的mysh启动,传参为0,也就是执行了testBasicFileIO()这个方法
测试突然断电。断电前我们看到,out.txt文件在不断增大:
断电后重启,文件大小都是0:
上面这个测试说明了缓冲区的存在。在一段时间内,并没有进行磁盘的刷写。