• Operating Systems 3 Easy Pieces 阅读笔记:Chapter 36 I/O设备


    Chapter 36 I/O设备

    第三部分是持久化Persistence

    在介绍持久化之前,先了解输入输出设备input/output (I/O) device,以及操作系统如何和这些输入输出设备被打交道。

     

    核心问题:IO设备是如何集成进系统的?他们通用的特性有哪些?如何使对它的操作变得高效?

     

    1 系统架构 System Architecture

     

    一个典型的系统架构如上图所示。CPU和主存通过Memory Bus直接连接;

    一些高性能设备如显卡,通过General I/O BUS (比如PCI)进行连接。

    接下来是外围总线(peripheral bus),连接低速设备(SCSI, SATA, or USB

     

    2 一个标准的设备(A Canonical Device)

    一个标准设备由两部分组成:

     

    1. 向外面系统暴露的“硬件接口”(interface),来让操作系统进行操作和控制

    2 .内部结构,具体实现暴露接口的功能,以及内部运行。简单的设备可以再一个小芯片上硬编码完成功能,复杂的设备自身会有一个CPU,一个小内存,还有一些专用芯片(device-specific chip)来完成上述工作。像RAID的控制器还会有上千行代码的固件(Firmware->software within a hardware device)。

     

     

    3. 一个标准协议 (The Canonical Protocol)

    再上述化简的设备中,包含了一个设备需要的最基础的三个寄存器:

    - Status寄存器,表示当前设备的状态

    - Data寄存器,用于传data给设备,或者从设备中取出data

    - Command寄存器,用于告诉设备去执行具体的任务

    那么一个简单的协议如下所示:

     

    上述协议四步走,

    1. 轮询设备的状态是否为BUSY;

    2. 拷贝数据到DATA寄存器

    3. 通知设备处理

    4. 轮询设备状态(看看是不是完成了,或者出现了错误)

    When the main CPU is involved with the data movement (as in this example protocol),

    we refer to it as programmed I/O (PIO).

    PIO: CPU亲自参与数据从内存到设备的拷贝工作,如第二步。

     

    上述方法的好处在于简单并且能健壮工作。

    但是,上述方法并不便捷而且低效:轮询会消费大量的CPU时,阻止CPU去处理别的进程的任务。

     

    4. 通过中断来降低CPU的浪费(Lowering CPU Overhead With Interrupts)

    通过中断和上下文切换,可以做到多进程任务的重叠执行:

     

    上图表示Disk在写1的时候,CPU切出去执行2,Disk在Finish之后会出发终端让CPU回来执行1.

     

    但是,进行上下文的切换本身也是有成本的。

    如果设备本身足够快,切换上下文的成本是得不偿失的。POLLING轮询是更好的选项。

     

    如果设备的速度是未知的,可以采用混合(hybrid)的策略:先轮询一会,如果设备还是没有完成任务,就进入中断。

     

    在网络应用的考虑中,也不建议使用中断。大量的包同时连接主机,如果使用中断,可能会导致livelock:切出了太多太多执行体,导致系统除了context switch啥也干不了。(* 所以handle 一个 request 就开一个线程(即使线程本身没有开销)也是不合理的,可以使用M:N线程来获得对执行本身的control和多路复用之间的平衡 *)。

     

    还有一种基于中断的优化是聚合(coalescing)。在这种情况下,每次设备想发起中断时先等待一段时间,看看会不会有其他requests也要完成了,如果有就打包成一个中断发送给系统。当然等待时间本来就会带来延迟,因此这种方法也需要工程上的权衡。

      

    5. 更进一步:用DMA来加速数据拷贝(More Efficient Data Movement With DMA)

     

    在我们的标准设备中,还有一个值得注意的问题。就是PIO(Programmed I/O)传输大块大块的数据,CPU手把手把数据从内存往设备拷贝的时候,CPU的负担依旧时很重的,而且用在了一个这么简单的任务上,应该把这部分负担解决掉,PIO的时间用来处理别的进程。

     

    那么如何降低PIO带来的浪费?

    在设备Device和主存Main Memory之间引入DMA(Direct Memeory Access)Engine。

    有DMA之后,操作系统就会告诉DMA数据在内存中的位置,长度,以及输送的目标,然后context switch去执行别的任务,知道数据拷贝完成,如下所示:

     

     

     

    6. 与设备交互的方法(Methods Of Device Interaction)

    1. explicit I/O instructions

    IBM发明的,显式的命令,让OS来发送数据给某个具体设备的register,然后再进行上述的protocol。

    在x86指令集上,就有in和out的指令来操作设备。比如向设备发送数据,执行体就要给两个参数,一个是目前保存了数据的寄存器,另一个是具体的port指向一个具体的设备。

    这样的命令往往是高权限的,会带来一些安全问题。

    2. memory-mapped I/O

    把硬件的寄存器映射到内存地址中,这样就能像访存写存一样操作设备。

    To access a particular register, the OS issues a load (to read) or store (to write) the address; the hardware then routes the load/store to the device instead of main memory.

     

    7. 接入OS:设备驱动 (Fitting Into The OS:The Device Driver)

    越通用越好:As Gerneral As Possible

    一个文件系统,应该能在SCSI, IDE, USB的设备上都能使用

    进行抽象↓↓↓

     

    底层的软件了解设备工作的细节,并向上层提供接口,又名设备驱动Device Driver。

    一个上层的文件系统(以及Applications)每次就发起块级的写入读取请求,然后被分流到具体的设备驱动进行。

    这种做法也有坏处:SCSI设备具有丰富的错误码,但是ATA和IDE的error handling非常简单,所以上层的软件只能收到的是EIO(Generic IO Error)。

     

    设备驱动占了一个操作系统内核代码的70%。

    然后设备驱动往往不是最专业的内核编程者写的,sloppy, 更多BUG,导致内核crashes。

     

     

    8. 简单案例: IDE Disk Driver

      

    一个IDE具有四种寄存器:

    1. control
    2. command block
    3. status
    4. error

    这四种寄存器通过访问具体的("I/O Address")比如0x3F6来读写,读写采用in和out命令。

    它的基本执行流程如下:

     

      XV6系统将上述流程Protocol实现为四个函数:

    1. 等待设备

     

    2. ide_rw

    当有别的读写请求pending时,入队一个读/写请求

    当没有,就通过ide_start_request直接写入磁盘

     

    3. ide_start_request

     

    4. ide_intr() 当一个中断发生时执行。它从device中读取数据,写到需要这个数据的process中,并lanuch下一个ide_start_request()。

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    如何将多个DataTable的不同列,合并成一个新的DataTable?
    ./与../区别
    C#获取前一天日期
    获取 本周、本月、本季度、本年 半年的开始时间或结束时间
    C# 多元运算符写法
    sql 求总行数和分页语句结合
    Web请求筛选被配置为拒绝该请求 内容长度超过配置的值
    How to understand primitive types are immutable
    Value type vs. Reference Type
    Error creating bean with name ‘xxxMapper‘ defined in file xxx.class
  • 原文地址:https://www.cnblogs.com/aweffr/p/9254002.html
Copyright © 2020-2023  润新知