i/o架构图

write read i/o - 图1

读写流程

  1. {
  2. char *buf = malloc(MAX_BUF_SIZE);
  3. strncpy(buf, src, , MAX_BUF_SIZE);
  4. fwrite(buf, MAX_BUF_SIZE, 1, fp);
  5. fclose(fp);
  6. }

malloc的buf对于图层中的application buffer
fwrite —> CLib buffer
fclose调用会把这些数据刷新到磁盘介质上
fflush函数只是把数据从CLib buffer 拷贝到page cache 中,并没有刷新到磁盘上

fsync函数page cache刷新到磁盘上

  • sync.是强制将所有页面缓冲区都更新到磁盘上。
  • fsync.是强制将某个fd涉及到的页面缓存更新到磁盘上(包括文件属性等信息).
  • fdatasync.是强制将某个fd涉及到的数据页面缓存更新到磁盘上

write函数,是直接通过系统调用把数据从应用层拷贝到内核层,app buffer —> page cache
write**是一个异步调用,**当数据到达page cache后,内核并不会立即把数据往下传递,数据什么时候写入硬盘,有内核IO调度决定(和内核调优参数相关)
write的异步过程改成同步过程,就是在open文件的时候带上O_SYNC标记

read调用是先检查page cache里面是否有数据,如果有,就取出来返回用户,如果没有,就同步传递下去并等待有数据,再返回用户,所以read是一个同步过程

write会触发用户态/内核态切换,如何避免? mmap可以把page cache 地址空间映射到用户空间,应用程序像操作应用层内存一样,写文件
如何绕过page cache? 通过open文件带上O_DIRECT参数,这是write该文件。就是直接写到设备上
driver(filesystem module)通过DMA写入disk_cache之后(使用fsync就可以强制刷新)到disk上面了。
如何直接写扇区?RAW设备写,绕开了文件系统,直接写扇区,想fdsik,dd,cpio之类的工具

安全性

进程挂调:application cache,CLib cache中的数据会丢失,数据到了page cache。进程挂掉,即使数据还没有到硬盘。数据也不会丢失
内核挂掉:只要数据没有到达disk cache,数据都会丢失
掉电:全部丢失

线程安全一致性

fwrite:同一进程clib buff 是共享的所以线程不安全
write:是原子操作,能保证两个进程“AAA”,“BBB”写操作,不会出现“ABAABB”这样的数据交错。

阻塞非阻塞

阻塞原因:
1、正在被调度执行。数据在用户空间和内核空间的拷贝是卡这完成的
2、就绪状态。i/o资源不可用(read 没数据,write buff满了)

常规文件:write,read 不阻塞
终端和网络文件:write,read 都是阻塞的

如果在open一个设备时指定了O_NONBLOCK标志,read/write就不会阻塞。
注意此时的返回值和errno

cache中脏数据相关参数

如果缓存相关参数设置得不好,对系统的运行反而会有不良的影响。如果cache过大,当需要把cache中的巨量数据刷入硬盘时,会让系统显得卡顿,甚至无法调度线程。
Linux提供的接口,以支持修改cache容量。

sysctl -a | grep dirty可以看到一组相关的内核参数

  1. $ sysctl -a | grep dirty
  2. vm.dirty_background_bytes = 0
  3. vm.dirty_background_ratio = 5
  4. vm.dirty_bytes = 0
  5. vm.dirty_expire_centisecs = 600
  6. vm.dirty_ratio = 10
  7. vm.dirty_writeback_centisecs = 100
  • vm.dirty_background_bytes:脏数据量达到此门限后,系统会开始把脏数据写入外存。此时脏数据可能仍然继续写入内存。
  • vm.dirty_ratio:脏数据量达到此门限后,系统会阻塞式地将脏数据写入外存,新的数据将被阻塞,无法写入cache。
  • vm.dirty_background_bytesvm.dirty_bytes:与vm.dirty_background_bytes/vm.dirty_ratio类似,差异仅仅是门限的计数单位,不是内存总量的百分比,而是bytes。注意_ratio与_bytes,有且仅有一个组能生效,如果一组被设置了非0值,另一组就应该被置0。
  • vm.dirty_writeback_centisecs:表示间隔多长时间,系统去检查一次cache中的数据量是否超过门限值,以百分之一秒为单位。
  • vm.dirty_expire_centisecs:表示数据在cache中允许被缓存多长时间。以百分之一秒为单位。

    drop cache相关参数与操作

    使用top命令,可以查看当前系统中cache的大小。

    # top
    top - 16:50:19 up 4 days,  7:00, 22 users,  load average: 25.06, 24.12, 23.14
    Tasks: 493 total,   7 running, 486 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 37.1 us, 18.9 sy,  0.0 ni, 42.3 id,  0.0 wa,  0.0 hi,  1.7 si,  0.0 st
    KiB Mem : 65622752 total, 30293032 free, 29879144 used,  5450576 buff/cache
    KiB Swap:        0 total,        0 free,        0 used. 31415264 avail Mem
    

    使用echo 3 > /proc/sys/vm/drop_caches可以手动清理cache。但这并不是一个好主意。cache有它存在的必要。假设一台网页服务器,在drop_cache之后,所有用户访问的数据都将从硬盘中读出,降低网页加载速度。
    如果发现cache确实过大,我们应该尽量使用系统提供的参数来调优,不采用主动drop_cache的方式。

  • vm.vfs_cache_pressure:表示系统清理cache的“努力程度”。默认值为100。越大则表示内核会越频繁地回收cache。

    参数建议

    在一台内存为64G,硬盘读写量长期保持在500KB/s的服务器上,在未调优前,经常发生因系统回收cache导致的系统挂起。因此,我们考虑修改参数,提高flush dirty和cache回收的频率,避免脏数据与cache累积过多,一次性回收导致的系统挂起无响应。
    以下参数作参考:

    vm.dirty_background_bytes = 0
    vm.dirty_background_ratio = 5
    vm.dirty_bytes = 0
    vm.dirty_expire_centisecs = 600
    vm.dirty_ratio = 10
    vm.dirty_writeback_centisecs = 100
    vm.vfs_cache_pressure = 500