write调用-linux系统编程

来源:互联网 发布:js button 不可用 编辑:程序博客网 时间:2024/06/09 23:24

0x01 缘由    

  当write()调用返回时,内核已经把数据从提供的缓冲区拷贝到内核缓冲区中,但不保证数据已经写到目的地。实际上,write调用执行非常快,因此不可能保证数据已经写到目的地。处理器和硬盘之间的性能差异使得这种情况非常明显。

如下图所示:


  write调用和内核缓冲区数据写入硬盘完全是两个独立的操作。write没有等待内核缓冲区数据写入硬盘完成后才返回,因为硬盘写IO要比处理器执行用户和内核缓冲区拷贝所花费的时间多得多。

0x02 write调用过程

  当用户空间发起write()系统调用时,Linux内核会做几项检查,然后把数据拷贝到缓冲区。然后,在后台,内核收集所有这样的“脏”缓冲区(即存储的数据比磁盘上的数据新),进行排序优化,然后把这些缓冲区写到磁盘上(这个过程称为回写writeback)。通过这种方式,write()可以频繁调用并立即返回。这种方式还支持内核把写操作推迟到系统空闲时期,批处理很多写操作。

0x03 延迟写的三个问题

1.延迟写没有改变数据读写一致性语义
  举个例子:假设要对一份刚写到缓冲区但还没写到磁盘的数据执行读操作,请求响应时会直接读取缓冲区的数据,而不是读取磁盘上的“陈旧”数据。这种方式进一步提高了效率,因为对于这个读请求,是从内存缓冲区而不是从硬盘中读的。如期望的那样,读写请求相互交织,而结果也和预期一致——前提是在数据写到磁盘之前系统没有崩溃!虽然应用可能认为写操作已经成功,在系统崩溃情况下,数据却没有写入到磁盘。
2.延迟写无法强制“顺序写”
  虽然应用可能会考虑对写请求进行排序,按特定顺序写入磁盘;而内核主要是出于性能考虑,按照合适的方式对写请求重新排序。只有当系统崩溃时,延迟写才会有问题,因为最终所有的缓冲区都会写回,而且文件的最终状态和预期的一直。实际上绝大多数应用并不关心写顺序。数据库是少数几个关心顺序的,它们希望写操作有序,确保数据库不会出于不一致状态。
3.延迟写对某些I/O错误的提示信息不准确
  在回写时产生的任何I/O错误,比如物理磁盘驱动出错,都不能报告给发起写请求的进程。实际上,内核内“脏”缓冲区和进程无关。多个进程可能会“弄脏”(即更新)同一片缓冲区中的数据,进程可能在数据仅写到缓冲区尚未写到磁盘的时候就退出了。进程操作失败,如果“事后”与之通信呢?

0x04 如何解决延迟写带来的问题

  对于这些潜在的问题,内核试图最小化延迟写带来的风险。为了保证数据按时写入,内核设置了“最大缓存时效”(maximum buffer age),并在超出给定时效前将所有脏缓存的数据写入磁盘。用户可以用/proc/sys/vm/dirty_expire_centisecs 来配置这个值,该值单位是厘秒(0.01秒)。

0x05 设置Linux系统内存回收的阀值

  linux内核的策略是最大程度的利用内存cache 文件系统的数据,提高IO速度,虽然在机制上是有进程需要更大的内存时,会自动释放Page Cache, 但不排除释放不及时或者释放的内存由于存在碎片不满足进程的内存需求。
可以通过命令 echo 3 > /proc/sys/vm/drop_caches来手动执行以释放Page Cache, 但是有时仍然发现释放的内存不够,这是因为Linux 提供了这样一个参数min_free_kbytes,用来确定系统开始回收内存的阀值,值越高, free memory也越高,如:echo 10240 > /proc/sys/vm/min_free_kbytes, 就会确保Free Memory有100M。
原创粉丝点击