找回密码
 立即注册
Qt开源社区 门户 查看内容

Linux文件IO缓存实验

2019-8-16 06:18| 发布者: admin| 查看: 729| 评论: 0

摘要: 上文最后留了两个问题:1. write只是将数据写入内核缓存就返回了,并没有写入磁盘,如何证明?2. 数据既然只是写入内核缓存,那数据什么时候写入磁盘?有哪些影响因素?可否改变?本文就讨论下这两个问题。1. 脏页概 ...
上文最后留了两个问题:

1. write只是将数据写入内核缓存就返回了,并没有写入磁盘,如何证明?
2. 数据既然只是写入内核缓存,那数据什么时候写入磁盘?有哪些影响因素?可否改变?本文就讨论下这两个问题。

1. 脏页概念

    因为硬盘的读写速度远赶不上内存的速度,系统就把读写比较频繁的数据事先放到内存中,以提高读写速度,这就叫高速缓存,linux是以页作为高速缓存的单位,当进程修改了高速缓存里的数据时,该页就被内核标记为脏页,内核将会在合适的时间把脏页的数据写到磁盘中去,以保持高速缓存中的数据和磁盘中的数据是一致的。

    根据脏页的定义,write函数将数据写入内核缓存,然后返回。此时,数据还未写入磁盘,即内存数据与磁盘数据不一致,这时就会产生脏页。

2. 查看脏页

    write函数返回时,数据在内存中会标记为脏页,如果能看到脏页的大小,并且与write写的数据大小一致,第一个问题则得到证明。

    如何查看脏页大小呢?其实很简单:cat /proc/meminfo就可以做到。

1xzkj@xzkj:~$ cat /proc/meminfo
2MemTotal:        1514724 kB
3MemFree:          404436 kB
4MemAvailable:    1118264 kB
5Buffers:          273244 kB
6Cached:           502964 kB
7SwapCached:          160 kB
8Active:           578192 kB
9Inactive:         307812 kB
10Active(anon):      89016 kB
11Inactive(anon):    24160 kB
12Active(file):     489176 kB
13Inactive(file):   283652 kB
14Unevictable:           0 kB
15Mlocked:               0 kB
16SwapTotal:       1570812 kB
17SwapFree:        1570580 kB
18Dirty:                 0 kB
19Writeback:             0 kB
20AnonPages:        109632 kB
21Mapped:            98840 kB
22Shmem:              3384 kB
23


    输出的第18行,即为系统当前脏页大小。

3. 脏页实验 1//test.c
2#include <stdio.h>
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <fcntl.h>
6#include <string.h>
7#include <stdlib.h>
8
9
10#define LEN    50*1024*1024
11
12int main()
13{
14    int fd;
15    char *buf = malloc(LEN);
16    if (buf == NULL)
17        return -1;
18
19    memset(buf,0x5a,LEN);
20
21    fd = open("123.txt",O_RDWR|O_CREAT,666);
22    if (fd < 0)
23        return -1;
24
25    write(fd,buf,LEN);
26    //延时以便查看脏页
27    sleep(15);
28
29    free(buf);
30
31    close(fd);
32
33    return 0;
34}
    测试代码中,我们向文件写入50MB数据,write后故意延迟15s,然后立刻通过上述命令查看脏页。测试结果:1xzkj@xzkj:~$ cat /proc/meminfo | grep Dirty
2Dirty:             51200 kB

    可以看到脏页大小刚好等于50MB。那如何清空脏页呢?即如何快速把数据写入磁盘。可以使用 sync 命令。
1xzkj@xzkj:~$ sync
2xzkj@xzkj:~$ sync
3xzkj@xzkj:~$ cat /proc/meminfo | grep Dirty
4Dirty:                 0 kB

   

4. fsync实验
    fsync函数可同步数据到磁盘,并等待写磁盘结束后才返回。我们做个实验验证下。
1//test.c
2#include <stdio.h>
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <fcntl.h>
6#include <string.h>
7#include <stdlib.h>
8
9
10#define LEN    50*1024*1024
11
12int main()
13{
14    int fd;
15    char *buf = malloc(LEN);
16    if (buf == NULL)
17        return -1;
18
19    memset(buf,0x5a,LEN);
20
21    fd = open("123.bin",O_RDWR|O_CREAT,666);
22    if (fd < 0)
23        return -1;
24
25    write(fd,buf,LEN);
26
27    //write返回后,立刻调用fsync
28    fsync(fd);
29
30    //延时以便查看脏页
31    sleep(15);
32
33    free(buf);
34
35    close(fd);
36
37    return 0;
38}


    测试代码里,write返回后,立刻调用fsync,然后延时,查看脏页大小

实验结果:
1xzkj@xzkj:~$ cat /proc/meminfo | grep Dirty
2Dirty:                 4 kB


根据实验,fsync确实可将数据同步到磁盘。

5. sync实验
1//test.c
2#include <stdio.h>
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <fcntl.h>
6#include <string.h>
7#include <stdlib.h>
8
9
10#define LEN    50*1024*1024
11
12int main()
13{
14    int fd;
15    char *buf = malloc(LEN);
16    if (buf == NULL)
17        return -1;
18
19    memset(buf,0x5a,LEN);
20
21    fd = open("123.bin",O_RDWR|O_CREAT);
22    if (fd < 0)
23        return -1;
24
25    write(fd, buf ,LEN);
26
27   //fwrite返回后,立刻调用fsync
28    sync(fd);
29
30    //延时以便查看脏页
31    sleep(15);
32
33    free(buf);
34
35    close(fd);
36
37    return 0;
38}
    sync函数也可将数据刷新到磁盘,我们也来做个实验验证下。

结果:

1xzkj@xzkj:~$ cat /proc/meminfo | grep Dirty
2Dirty:                 0 kB


6. 脏页回写参数

    write将数据写入内核缓存后,就返回了。那内核缓存里的数据什么时候写入磁盘呢?这由脏页回写参数决定。那如何查看这些参数呢?内核参数的查看与配置可使用sysctl命令:
1xzkj@xzkj:~$ sysctl -a 2>/dev/null| grep dirty
2vm.dirty_background_bytes = 0
3vm.dirty_background_ratio = 5
4vm.dirty_bytes = 0
5vm.dirty_expire_centisecs = 3000
6vm.dirty_ratio = 10
7vm.dirty_writeback_centisecs = 500
8vm.dirtytime_expire_seconds = 43200


其中:

    vm.dirty_background_ratio:表示当文件系统缓存脏页数量达到系统内存百分之多少时(如5%)就会触发pdflush/flush/kdmflush等后台回写进程运行,将一定缓存的脏页异步地刷入外存,dirty_background_bytes与此类似,只是通过字节数来设置;

    vm.dirty_ratio:绝对脏数据限制,当文件系统缓存脏页数量达到系统内存百分之多少时(如10%),新的IO请求将被阻塞,直到脏页数据写入磁盘;

    vm.dirty_expire_centisecs:脏数据存活时间,单位为百分之一秒,上述是30秒;

    dirty_writeback_centisecs:多长时间进行一次脏数据写操作。

7. 脏数据参数实验
1//test.c
2#include <stdio.h>
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <fcntl.h>
6#include <string.h>
7#include <stdlib.h>
8
9
10#define LEN    50*1024*1024
11
12int main()
13{
14    int fd;
15    char *buf = malloc(LEN);
16    if (buf == NULL)
17        return -1;
18
19    memset(buf,0x5a,LEN);
20
21    fd = open("123.bin",O_RDWR|O_CREAT);
22    if (fd < 0)
23        return -1;
24
25    write(fd, buf ,LEN);
26
27    //延时以便查看脏页
28    sleep(15);
29
30    free(buf);
31
32    close(fd);
33
34    return 0;
35}
    还是刚开始的脏页实验代码,write后等待15s,上述实验中能够看到脏页数据大小为50MB。这里,我们更改脏页回写参数,将脏页存活时间改为1秒,即write返回1秒后,脏页数据将被刷入磁盘。

1xzkj@xzkj:~$ sudo sysctl -w vm.dirty_expire_centisecs=100
2vm.dirty_expire_centisecs = 100

1xzkj@xzkj:~$ cat /proc/meminfo | grep Dirty
2Dirty:                 0 kB
    可以看到,此时没有调用fsync或sync函数,脏页数据大小仍旧为0。即我们设置 的脏页回写参数起作用了。
    至此,文件开始部分提出的两个问题,都已解决^_^。

欢迎关注公众号交流~




----------------------------------------------------------------------------------------------------------------------
我们尊重原创,也注重分享,文章来源于微信公众号:Linux技术积累,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
----------------------------------------------------------------------------------------------------------------------

鲜花

握手

雷人

路过

鸡蛋

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了