http://blog.csdn.net/xypzwl/article/details/51416883

一、存储设备的存储原理

机械硬盘:

机械硬盘使用磁性物质作为存储介质,用N、S极性来代表0或1;

以无磁性的盘片作为基片(一般材质为铝合金或者玻璃),磁性物质在盘片上以同心圆的方式排列,这些同心圆的圆周被称为磁道;

磁道又被细分成一个个扇区,作为读写的最小单位(即就算需要读写的数据小于一个扇区,硬盘在实际读写时也会读取一个扇区的数据)。一般一个扇区的大小为512字节(1.非固定,现在也有4K大小的扇区;2.也叫做物理扇区,由硬盘的物理结构决定,无法更改);每个扇区都有一个从1开始的唯一的编号。

数据的读写是有磁头来进行的,硬盘使用时,盘片在高速旋转而磁头保持不动,由磁盘控制器控制磁头的半径依次遍历所有的扇区,从而达到读写数据的功能;

例:

假设硬盘有十个磁道,每次磁道上有十个扇区;现在需要读取第五磁道的第五扇区上的数据:

Step1.外部设备告知磁盘控制器数据的位置,第五磁道第五扇区;磁头由磁盘控制器控制半径大小从第1磁道开始依次经由第2、3、4磁道,最终到达第五磁道;

Step2.磁头保持半径不动,等待第五扇区旋转到磁头下方;

Step3.第五扇区已经到达磁头下方了,磁头开始读取第五扇区内的数据;并将数据发送给外部设备;

固态硬盘(SSD):

不了解,以后补充

U盘(flash存储设备):

不了解,以后补充

IO写入的代码表示:

  1. #define SECTOR_SIZE     //物理扇区大小,常见为512B,也有4KB的
  2. #define TOTAL_SIZE      //硬盘的总大小
  3. int Disk_write(int offset, void *data, int size)
  4. {
  5. static int pos = 0;
  6. int count = 0;
  7. int ret = -1;
  8. int startOffset, remain;
  9. char buf[SECTOR_SIZE] = {0};
  10. while(1)
  11. {
  12. startOffset = offset / SECTOR_SIZE;
  13. remain = offset % SECTOR_SIZE;
  14. if (startOffset == pos)
  15. {
  16. memcpy(buf + remain, data + count, MIN(SECTOR_SIZE - remain, size - count));
  17. count += MIN(SECTOR_SIZE - remain, size - count);
  18. offset += SECTOR_SIZE - remain;
  19. ret = write(buf, SECTOR_SIZE);  //是SECTOR_SIZE没错,每次写入一定是整数个扇区
  20. if (ret < 0 || count >= size)
  21. {
  22. return count;
  23. }
  24. }
  25. pos += SECTOR_SIZE;
  26. pos %= TOTAL_SIZE;
  27. sleep(10);  //表示寻道/写惩罚消耗的时间
  28. }
  29. }

二、IO流程:

这里针对的是基于操作系统之上的应用程序的I/O流程。大致的I/O流程如下图:

如上图显示,往硬盘写数据需要经过四个过程:

Step1: 应用程序发起IO调用(如fwrite/fread),要写入的数据被复制到应用层缓冲区中,在应用层缓冲区中数据会被重新组合,使得传递给下一步的数据是对齐的。(这一步的操作时非必需的,当调用库函数fwrite/fread时,会有这一层的缓冲,但如果是直接调用的系统函数write/read,将直接跳到下一步)

Step2:系统由用户态切换到系统态,数据被写入到系统缓冲区,这一步完成后write函数就认为写操作已经完成了,返回写数据结果(选择同步IO除外,选择同步IO的话会等到step4完成后才会返回)。

Step3:系统缓冲区内的数据被复制到操作系统page里面进行进一步的组合

Step4:数据被写入到硬盘的cache中,等待合适的时机将数据写到硬盘的指定位置;

Step5:硬盘的磁头到达指定的位置,将数据写入到硬盘中;只有当这一步完成时,数据才是真正的被保存下去了,如果在这一步完成之前出现异常,数据将会丢失。

在整个写数据流程中,之所以需要这么多的缓冲和数据复制,其原因是1.硬盘的读写速度相较于cpu的计算速度来说太慢了,需要缓冲区来缓冲数据,保证数据不会丢失;2.加快硬盘写入速度,通过这些缓冲,可以对应用层的读写操作进行优化合并,以减少不必要的读写操作,从而加快速度;

三、I/O优化:

I/O的最大瓶颈在于往存储设备里真正写数据的时候(上面I/O流程中的step4),这也是为什么应用数据进行I/O要经过多重缓冲的原因;

回顾下存储设备IO的流程:

1.      将磁头移到到指定的磁道(寻道);

2.      等待指定的扇区转到磁头下方(等待);

3.      真实IO操作;

(注:这是机械硬盘的流程,SSD和U盘没有寻道操作)

实际IO的速度有存储设备本身物理特性决定的,无法优化;但我们可以优化寻道和等待的时间(最理想的情况下是硬盘不需要寻道,也不需要等待),我们可以通过以下操作降低寻道和等待消耗的时间:

1.      按顺序读取数据;这样就不需要寻道和等待了。

2.      数据尽可能一次性读写,减少文件碎片;文件碎片就是一个文件在存储设备上存放时不是连续存放的,假设一个文件2K大小,可能它的前512字节存放在扇区1,第二个512字节存放在扇区10,最后1K存放在扇区5。这样虽然在逻辑上读取文件时没有seek操作,但实际上IO时还是需要等待的。

大部分的文件系统是先到先得的原则,因此一次性写入可以极大的降低文件碎片的情况。

3.      字节对齐;硬盘IO的最小单位为扇区,即使需要IO的数据小于一个扇区,实际IO时也是对一个扇区进行操作的。eg:假设一个扇区为512字节,现在需要分两次一共写入1K的数据,这1K数据存放在两个连续的扇区,扇区1和扇区2中。

(1)如果每次写512字节,整个IO消耗的时间就是两次真实IO的时间;

(2)而如果不对齐写,第一次写511个字节,第二次写513个字节;由于硬盘一次必须写入512字节,因此第一次IO时扇区1中写入了512字节,最后一个字节是无效数据,第二次IO时磁头已经偏移到扇区2了,但控制器发现第512个字节是在扇区1里面的,所以控制器会等到扇区1再次出现在磁头下面后才开始第二次IO,并且这次IO实际上只有一个字节是有效的;写完后还需要对扇区2再进行一次IO操作。因此整个IO消耗的时间等于三次真实IO的时间加上等待盘片转完一遍的时间(U盘的话还存在一个写惩罚,即每次IO时都要先将原来的数据读取出来,然后擦除数据,最后再将新数据写入进去,这也需要时间)

IO过程中,数据在这些缓冲区内进行复制也要消耗时间和性能的,字节对齐后,这些复制操作可视情况去掉(通过open时传入的参数进行修改),这里又可以节省点时间

4.      减少不必要的IO同步操作;一般IO时,操作系统、磁盘控制器等都会对传下来的数据进行优化,比如将多次write合并成一次,调整IO顺序,数据对齐等操作;而IO同步时不经过这些优化操作,一旦传下来的数据诸如字节未对齐,多次IO等会极大影响IO的性能。

5.      减少不必要的文件开关操作;大部分的文件系统中,文件的属性数据和文件的真实数据是分开存放的,open一个文件时,操作系统要先找到文件的属性数据将这些数据载入到内存,然后根据属性数据再偏移到文件真实数据存放的扇区对数据进行操作;close时一般操作系统会强制刷新数据到存储设备,然后再偏移到文件的属性数据存放的扇区更新属性信息,同样也会强制刷新数据;

6.      减少调用IO的频率,能一次写完的数据不要分成多次;这一点其实跟IO关系不大,主要是linux的关系;linux系统分为用户态和系统态(或叫内核态);一般的操作都是在用户态下进行的,而一些系统调用需要先切换到系统态才能执行,切换时需要将当前环境压入到寄存器,操作完成后再从寄存器中读取环境信息来进行恢复;减少IO的调用次数可以减少系统状态切换的开销。

IO流程及优化的更多相关文章

  1. elasticsearch的数据写入流程及优化

    Elasticsearch 写入流程及优化 一. 集群分片设置:ES一旦创建好索引后,就无法调整分片的设置,而在ES中,一个分片实际上对应一个lucene 索引,而lucene索引的读写会占用很多的系 ...

  2. Linux 0.11源码阅读笔记-文件IO流程

    文件IO流程 用户进程read.write在高速缓冲块上读写数据,高速缓冲块和块设备交换数据. 什么时机将磁盘块数据读到缓冲块? 什么时机将缓冲块数据刷到磁盘块? 函数调用关系 read/write( ...

  3. 【转】IO流程

    原文地址:http://blog.chinaunix.net/uid-26922071-id-3954900.html IO之流程与buffer概览 为了说明这个流程,还是用图来描述一下比较直观.   ...

  4. MySQL优化篇(一),我可以和面试官多聊几句吗?——SQL优化流程与优化数据库对象

    我可以和面试官多聊几句吗?只是想偷点技能过来.MySQL优化篇(基于MySQL8.0测试验证),上部分:优化SQL语句.数据库对象,MyISAM表锁和InnoDB锁问题. MyISAM表锁和InnoD ...

  5. slave IO流程之一:mysql登陆过程(mysql_real_connect)

    最近看了slave IO的源码,发现slave IO的写relay log貌似是单线程单连接的,这让我有点小失望. slave IO的主函数是handle_slave_io,处理流程如下: 图1 ha ...

  6. [搜片神器]DHT后台管理程序数据库流程设计优化学习交流

    谢谢园子朋友的支持,已经找到个VPS进行测试,国外的服务器: sosobt.com 大家可以给提点意见... 服务器在抓取和处理同时进行,所以访问速度慢是有些的,特别是搜索速度通过SQL的like来查 ...

  7. kvm和qemu交互处理io流程

    1.IO虚拟化的分类 (1)全虚拟化:宿主机截获客户机对I/O设备的访问请求,然后通过软件模拟真实的硬件.这种方式对客户机而言非常透明,无需考虑底层硬件的情况,不需要修改操作系统. QEMU模拟I/O ...

  8. 数据库的IO and 数据库优化问题

    一.IO介绍 IO有四种类型:连续读,随机读,随机写和连续写,连续读写的IO size通常比较大(128KB-1MB),主要衡量吞吐量,而随机读写的IO size比较小(小于8KB),主要衡量IOPS ...

  9. 磁盘IO概念及优化入门知识

    在数据库优化和存储规划过程中,总会提到IO的一些重要概念,在这里就详细记录一下,对这个概念的熟悉程度也决定了对数据库与存储优化的理解程度,以下这些概念并非权威文档,权威程度肯定就不能说了. 读/写IO ...

随机推荐

  1. C语言基础:常见循环语句 分类: iOS学习 c语言基础 2015-06-10 21:46 13人阅读 评论(0) 收藏

    for语句 for( 初始化表达式; 循环判断条件  ;增量表达式); while(条件表达式){ 循环体; } 先判断条件表达式,如果为真就执行循环体,执行完再去判断条件表达式 do{ 循环体; } ...

  2. Vue CLI 3 配置兼容IE10

    最近做了一个基于Vue的项目,需要兼容IE浏览器,目前实现了打包后可以在IE10以上运行,但是还不支持在运行时兼容IE10及以上. 安装依赖 yarn add --dev @babel/polyfil ...

  3. 好玩的Python库tqdm (转载)

    原文地址: https://blog.csdn.net/zejianli/article/details/77915751 可以显示循环的进度条的库,再也不用担心不知道程序跑到哪里还要跑多久了 tqd ...

  4. 如何使用 python3 将RGB 图片转换为 灰度图

    首先,介绍第一种方法, 使用  PIL  库,   PIL库是一种python语言常用的一个图形处理库. 关于   PIL  库的安装本文就不介绍了. from PIL import Image I ...

  5. I.MX6 Android Linux UART send receive with multi-thread and multi-mode demo

    /******************************************************************************************* * I.MX6 ...

  6. Android shell command execute Demo

    package com.android.utils; import java.io.File; import java.io.IOException; import java.io.InputStre ...

  7. PHP webservice初探

    背景:在最近的开发中,为了解决公司内部系统与外部系统的对接,开始接触到了webservice接口,外部公司提供接口供我们调用,已达到数据同步的目的,因此有必要普及一下web service的知识了! ...

  8. JPQL详解

    JPA在说jpql之前必须要说一下什么是JPA,否则在后续学习的时候,你会弄混的.JPA是一种规范,什么是规范呢,规范就是一个钥匙可以开这把锁.一般对于规范来说我们都是用接口,如果有人要我们则实现我们 ...

  9. java8 array、list操作 汇【4】)- Java8 Lambda表达式 函数式编程

    int tmp1 = 1; //包围类的成员变量 static int tmp2 = 2; //包围类的静态成员变量 //https://blog.csdn.net/chengwangbaiko/ar ...

  10. sleep和 wait