为了从更深层次了解计算机的IO ,需要对计算的文件系统进行深入学习,下边是本人的学习笔记,请大家一起参考,要是有说的不对的地方,请大家指正!
1、Linux 中的PageCache
我们通过VMvare虚拟机,来做个实验来证明linux 中的pagecache
编写java 代码
import org.junit.Test;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
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();
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);
}
}
//测试文件NIO
public static void testRandomAccessFileWrite() throws Exception {
RandomAccessFile raf = new RandomAccessFile(path, "rw");
raf.write("hello niotest\n".getBytes());
raf.write("hello seanzhou\n".getBytes());
System.out.println("write------------");
System.in.read();
raf.seek(4);
raf.write("ooxx".getBytes());
System.out.println("seek---------");
System.in.read();
FileChannel rafchannel = raf.getChannel();
//mmap 堆外 和文件映射的 byte not objtect
MappedByteBuffer map = rafchannel.map(FileChannel.MapMode.READ_WRITE, 0, 4096);
map.put("@@@".getBytes()); //不是系统调用 但是数据会到达 内核的pagecache
//曾经我们是需要out.write() 这样的系统调用,才能让程序的data 进入内核的pagecache
//曾经必须有用户态内核态切换
//mmap的内存映射,依然是内核的pagecache体系所约束的!!!
//换言之,丢数据
//你可以去github上找一些 其他C程序员写的jni扩展库,使用linux内核的Direct IO
//直接IO是忽略linux的pagecache
//是把pagecache 交给了程序自己开辟一个字节数组当作pagecache,动用代码逻辑来维护一致性/dirty。。。一系列复杂问题
System.out.println("map--put--------");
System.in.read();
// map.force(); // flush
raf.seek(0);
ByteBuffer buffer = ByteBuffer.allocate(8192);
// ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
int read = rafchannel.read(buffer); //buffer.put()
System.out.println(buffer);
buffer.flip();
System.out.println(buffer);
for (int i = 0; i < buffer.limit(); i++) {
Thread.sleep(200);
System.out.print(((char)buffer.get(i)));
}
}
准备shell 脚本
[root@node20 testfileio]# cat /opt/file/myshell.sh
#!/bin/bash
rm -rf *out*
/usr/local/soft/jdk1.8.0_191/bin/javac OSFileIO.java
strace -ff -o out /usr/local/soft/jdk1.8.0_191/bin/java OSFileIO $1
前提是安装好java 环境变量,运行shell 脚本
[root@node20 file]# sh myshell.sh 0
然后打开一个新的窗口看目录/root/testfileio/out.txt 中out.txt 文件大小的变化
[root@node20 testfileio]# ll
total 448
-rw-r--r-- 1 root root 339390 Sep 24 03:41 out.txt
[root@node20 testfileio]# ll
total 960
-rw-r--r-- 1 root root 671910 Sep 24 03:41 out.txt
[root@node20 testfileio]# ll
total 960
-rw-r--r-- 1 root root 876440 Sep 24 03:41 out.txt
[root@node20 testfileio]# ll
total 1984
-rw-r--r-- 1 root root 1051820 Sep 24 03:41 out.txt
[root@node20 testfileio]# ll
total 1984
-rw-r--r-- 1 root root 1237410 Sep 24 03:41 out.txt
[root@node20 testfileio]# ll
total 1984
-rw-r--r-- 1 root root 1480060 Sep 24 03:41 out.txt
[root@node20 testfileio]# ll
total 1984
-rw-r--r-- 1 root root 1734110 Sep 24 03:41 out.txt
[root@node20 testfileio]# ll
total 4032
-rw-r--r-- 1 root root 2169910 Sep 24 03:41 out.txt
可以看到文件一直在不停的增长,这个时候,我们在VMvare 上操作关机,
ps:红色框框框起来的地方说明是强制拔电源关机,没给电脑准备的时间,如果点上面的'关闭客户机'属于在本机计算机按了关机按钮,如果是“关闭客户机”,会经历刷将内存中的字节数组刷写到磁盘的过程,而“关机”不会经历将内存中的自己数组刷写到磁盘的过程
我们再次开机,查看/root/testfileio/out.txt 该文件的大小
[root@node20 /]# cd /root/testfileio/
[root@node20 testfileio]# ll
total 0
[root@node20 testfileio]#
发现该文件已经没有了,说明我们生产的文件,都是到了内核中,虚拟机关机,内核中 的数据没有刷写到硬盘就已经消失了。
2、调整Linux的pageCache
在Linux系统中 通过如下命令可以查看操作系统对pagecache的设置
[root@node20 ~]# sysctl -a |grep dirty
sysctl: reading key "net.ipv6.conf.all.stable_secret"
sysctl: reading key "net.ipv6.conf.default.stable_secret"
sysctl: reading key "net.ipv6.conf.ens33.stable_secret"
sysctl: reading key "net.ipv6.conf.lo.stable_secret"
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 30
vm.dirty_writeback_centisecs = 500
[root@node20 ~]#
参数说明:
vm.dirty_background_ratio当pagecahe中的数据占了内存可用空间的百分比,就会将脏页中的数据写入到磁盘;举个例子,假设我们的内存可用容量是1个G,也就是说pagecacha中的数据到达1024MB*10%=102.4MB的时候,才会将pagecache中的数据刷到硬盘
vm.dirty_background_bytes这个参数与上边的类似,只不过单位不在是百分比,而是byte,这两个只能有一个生效,不生效的那一项需要设置为0
vm.dirty_ratio这个值说的是当pagecache中的数据到达系统可用内存空间的百分比后,就无法在pagecache中写入新的数据了
vm.dirty_bytes = 0这个也和上边的类似,单位为byte
vm.dirty_expire_centisecs表示数据在cache中允许被缓存多长时间。以百分之一秒为单位
vm.dirty_writeback_centisecs表示间隔多长时间,系统去检查一次cache中的数据量是否超过门限值,以百分之一秒为单位
2.1、调整相关参数,使用插件来查看pagecache的变化
我们先将vm.dirty_background_ratio和 vm.dirty_ratio故意调整的大一点,这里我们都设置成90,设置这两个参数需要修改/etc/sysctl.conf这个文件,修改内容为
[root@node20 /]# vim /etc/sysctl.conf
[root@node20 /]# sysctl -p
vm.dirty_ratio = 90
vm.dirty_background_ratio = 90
vm.dirty_writeback_centisecs = 5000
vm.dirty_expire_centisecs = 30000
sysctl -p这个命令是修改生效,然后我们继续上边的代码,不过传参改成1,使用 BufferedOutputStream来包装一下,使写文件的速度快一点。
pcstat 可以查看某一个进程或者某个文件的pagecache 的变化,我打开两个标签页,一个标签页用来执行我们的脚本,一个标签页用来实时查看我们的pagecache的使用情况
第一个标签页
[root@node20 file]# sh myshell.sh 1
第二个标签页
[root@node20 testfileio]# ll -h && pcstat out.txt
total 249M
-rw-r--r-- 1 root root 214M Sep 25 02:17 out.txt
+---------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|---------+----------------+------------+-----------+---------|
| out.txt | 228550140 | 55799 | 55799 | 100.000 |
+---------+----------------+------------+-----------+---------+
[root@node20 testfileio]#
[root@node20 testfileio]# ll -h && pcstat out.txt
total 505M
-rw-r--r-- 1 root root 369M Sep 25 02:17 out.txt
+---------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|---------+----------------+------------+-----------+---------|
| out.txt | 391842360 | 95665 | 95665 | 100.000 |
+---------+----------------+------------+-----------+---------+
[root@node20 testfileio]# ll -h && pcstat out.txt
total 505M
-rw-r--r-- 1 root root 427M Sep 25 02:17 out.txt
+---------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|---------+----------------+------------+-----------+---------|
| out.txt | 450400860 | 109962 | 109962 | 100.000 |
+---------+----------------+------------+-----------+---------+
[root@node20 testfileio]# ll -h && pcstat out.txt
total 505M
-rw-r--r-- 1 root root 472M Sep 25 02:17 out.txt
+---------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|---------+----------------+------------+-----------+---------|
| out.txt | 498467970 | 121697 | 121697 | 100.000 |
+---------+----------------+------------+-----------+---------+
[root@node20 testfileio]# ll -h && pcstat out.txt
total 1017M
-rw-r--r-- 1 root root 513M Sep 25 02:17 out.txt
+---------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|---------+----------------+------------+-----------+---------|
| out.txt | 541621080 | 132232 | 132232 | 100.000 |
+---------+----------------+------------+-----------+---------+
[root@node20 testfileio]# ll -h && pcstat out.txt
total 1017M
-rw-r--r-- 1 root root 555M Sep 25 02:17 out.txt
+---------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|---------+----------------+------------+-----------+---------|
| out.txt | 585069030 | 142840 | 142840 | 100.000 |
+---------+----------------+------------+-----------+---------+
[root@node20 testfileio]# ll -h && pcstat out.txt
total 1017M
-rw-r--r-- 1 root root 596M Sep 25 02:17 out.txt
+---------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|---------+----------------+------------+-----------+---------|
| out.txt | 628901910 | 153541 | 153541 | 100.000 |
+---------+----------------+------------+-----------+---------+
[root@node20 testfileio]# ll -h && pcstat out.txt
total 1017M
-rw-r--r-- 1 root root 638M Sep 25 02:17 out.txt
+---------+----------------+------------+-----------+---------+
| Name | Size (bytes) | Pages | Cached | Percent |
|---------+----------------+------------+-----------+---------|
| out.txt | 674159850 | 164590 | 164590 | 100.000 |
+---------+----------------+------------+-----------+---------+
我们可以看到文件大小在快速的增长,pcstat 文件也写入了cache中,我本机的虚拟机为2G内存,根据上边的参数设置,也就是说当我们的文件大小在2G*90%=1.8G大小的时候,才会刷写进磁盘,我们在文件大小还没有到达1.8G 的时候,执行刚才的关机操作,再次打开虚拟机发现out.txt文件已经不存在了。
3、总结
linux系统将文件写入磁盘的时候,先将文件写入pagecache,当写的文件触发pagecache的极限的时候,操作系统在将pagecache中的文件写入磁盘。