• I/O输入系统


    I/O输入系统

        计算机有两个主要任务:I/O操作与计算处理。在许多情况下,主要任务是I/O操作。而计算处理只是附带的。

        操作系统在计算机I/O方面的作用是管理和控制I/O操作和I/O设备。

    概述

        对与计算机相连设备的控制是操作系统设计者的主要任务之一。

        I/O设备技术呈现两个相矛盾的趋势。一方面,可以看到硬件与软件接口日益增长的标准化。这一趋势有助于将设备集成到现有计算机和操作系统。另一方面,也可以看到I/O设备日益增长的多样性。有的新设备与以前的设备区别很大,以至于很难集成到计算机和操作系统中,这种困难需要运用硬件和软件技术一起来解决。I/O设备的基本要素如端口、总线及设备控制器适用于许多不同的I/O设备。为了封装不同设备的细节与特点,操作系统内核设计成使用设备驱动程序模块的结构。设备驱动程序为I/O子系统提供了统一设备访问接口,就像系统调用为应用程序与操作系统之间提供了统一地标准接口一样。

    I/O硬件

        计算机使用很多种设备。绝大多数都属于存储设备(磁盘,磁带)、传输设备(网卡、Modem)和人机交互设备(屏幕,键盘,鼠标)。

        设备与计算系统的通信可以通过电缆甚至空气来传送信息。设备与计算机通信通过一个连接点(或端口),例如串行端口。如果一个或多个设备使用一组共同的线,那么这种连接则成为总线。总线(Bus)是一组线和一组严格定义的可以描述在线上传输信息的协议。

        总线在计算机系统结构中使用很广。下图显示了一个典型的PC总线结构。该图显示了一个PCI总线(最为常用的PC系统总线)用以连接处理器-内存子系统与快速设备,扩展总线(expansion bus)用于连接串行、并行端口和相对较慢的设备(如磁盘)。

        控制器(controller)是用于操作端口,总线或设备的一组电子电器。串行端口控制器是简单的设备控制器。它是计算机上的一块芯片或部分芯片,用以控制串行端口线上的信号。相比较而言,SCSI总线就不那么简单。由于SCSI协议的复杂性,SCSI总线控制器常常实现为与计算机相连的独立的线路板或主机适配器(host adapter)。该适配器通常有处理器、微码及一部分私有内存,以便能处理SCSI协议信息。有的设备有内置的控制器。控制器有一个或多个用于数据和控制信号的寄存器。处理器通过读写这些寄存器的位模式来与控制器通信。这种通信的一种方法是通过使用特殊I/O指令来向指定的I/O端口地址传输一个字节或字。I/O指令触发总线线路来选择合适设备并将信息传入或传出设备寄存器。另外,设备控制器也可支持内存映射I/O。这时,设备控制寄存器被映射到处理器的地址空间。处理器执行I/O请求是通过标准数据传输指令来完成对设备控制器的读写。

        有的系统使用两种技术。例如,PC使用I/O指令来控制一些设备,而使用内存映射来控制其他设备。下图显示了常用的PC I/O端口地址。

    图像控制器不但有I/O端口以完成基本控制操作,而且也有一个较大的内存映射区域以支持屏幕内容。进程通过将数据写入到内存映射区域来把输出发送到屏幕。图像控制器可以根据内存中的内容来生成屏幕图像。这种技术使用简单,而且向图像内存中写入数百万字节要比执行数百万条指令快很多。但是向内存映射I/O控制器写入的简单性也存在一个缺点。因为软件出错的常见类型之一就是通过一个错误指针向一个不该写的内存区域写数据,所以内存映射设备寄存器容易受到意外修改。当然,内存保护可以减低风险。

        I/O端口通常有4中寄存器,即状态寄存器,控制寄存器,数据输入寄存器与数据输出寄存器。

        数据输入寄存器被主机读出以获取数据。

        数据输出寄存器被主机写入以发送数据。

        状态寄存器包含一些主机可读取的位(bit)。这些位指示各种状态,例如,当前任务是否完成,数据输入寄存器中是否有数据可以读取,是否出现设备故障等。

        控制寄存器可以被主机用来向设备发送命令或改变设备状态。例如,一个串行端口的控制寄存器中的一个确定的位选择全功通信或单工通信,另一个位控制启动奇偶校验检查,第三个位设置字长位7或8位,其他位选择串行端口通信所支持的速度。

        数据寄存器通常为1-4B。有的控制器有FIFO芯片,可用来保留多个输入或输出数据,以在数据寄存器大小的基础上扩展控制器的容量。FIFO芯片可以保留少量突发数据,直到设备或主机可以接受数据。

    轮询

        主机与控制器之间交互的完成协议可能很复杂,但基本握手概念则比较简单。下面举例解释握手概念。假设有两个位来协调控制器与主机之间的生产者与消费者的关系。控制器通过状态寄存器的忙位(busy bit)来显示其状态(记住置位(set a bit)就是将1写到位中,而清位(clear a bit)就是将0写到位中)。控制器工作忙时就置忙位,而可以接受下一命令时就清忙位。主机通过命令寄存器中命令就绪位来表示其意愿。当主机有命令需要控制器执行时,就置命令就绪位。例如,当主机需要通过端口来写输出数据时,主机与控制器之间握手协调如下:

    1. 主机不断地读取忙位,直到该位被清除。
    2. 主机设备命令寄存器中的写位并向数据输出寄存器中写入一个字节。
    3. 主机设备命令就绪位。
    4. 当控制器注意到命令就绪位已被设置,则设置忙位。    
    5. 控制器读取命令寄存器,并看到写命令。它从数据输出寄存器中读取一个字节,并向设备执行I/O操作。
    6. 控制器清除命令就绪位,清除状态寄存器的故障位表示设备I/O成功,清除忙位表示完成。

        输出每个字节,都要执行以上循环。可以看出效率比较低。

        这时,如果让设备准备好时再通知处理器而不是由CPU轮询外设I/O是否已完成,那么效率就会更好。能使外设通知CPU的硬件机制称为中断(interrupt)。

    中断

        基本中断机制工作如下。CPU硬件有一条中断请求线(Interrupt-request line, IRL)。CPU在执行完每条指令后,都将检测IRL。当CPU检测到已经有控制器通过中断请求线发送了信号,CPU将保存当前状态并且跳转到内存固定位置的中断处理程序(interrupt-controller)。中断处理程序判断中断原因,进行必要的处理,重新恢复状态,最后执行中断返回(return from interrupt)指令以便使CPU返回中断以前的执行状态,即设备控制器通过中断请求线发送信号而引起(raise)中断,CPU捕获(catch)中断并分发(dispatch)到中断处理程序中,中断处理程序通过处理设备请求来清除(clear)中断。下图总结中断驱动I/O循环。

        这一基本中断机制可以使CPU响应异步事件,例如,设备控制器处于就绪状态,对于现代操作系统,需要更为成熟的中断处理特性,在进行关键处理时,能够延迟中断处理。更为有效将中断分发到合适的中断处理程序,而不是检查所有设备以决定哪个设备引起中断。需要多级中断,这样操作系统能区分高优先级或低优先级的中断,能根据紧迫性的程序来响应。

        对于现代计算机硬件来说,这三个特性是由CPU与中断控制器(interrupt-controller)硬件提供的。

        绝大多数CPU有两个中断请求线。一个是非屏蔽中断,主要用来处理如不可恢复内存错误等时间。另一个是可屏蔽中断,这可以由CPU在执行关键的不可中断的指令序列前加以屏蔽。可屏蔽中断可以被设备控制器用来请求服务。

        中断机制接受一个地址,以用来从一小集合内选择特定的中断处理程序。对绝大多数体系机构,这个地址是一个称为中断向量(interrupt vector)的表中偏移量。该向量包含了特殊中断处理程序的内存地址。向量中断机制的目的是用减少单个中断处理的需要,这些中断处理搜索所有可能的中断源以决定哪个中断需要服务。事实上,计算机设备常常要比向量内的地址多。解决这一问题的常用方法之一就是中断链接(interupt chaning)技术,即中断向量内的每个元素都指向中断处理程序列表的头。当有中断发生时,相应链表上的所有中断处理程序都将一一调用,直到发现可以处理请求的为止。这种结构是大型中断向量表的大开销与分发到单个中断处理程序的低效率之间的一个折中。

        下图显示了Intel Pentium中断向量的设计。事件0-31为非屏蔽中断,用来表示各种错误条件信号。事件32-255位为可屏蔽中断,用于设备产生的中断。

        中断机制也实现了中断优先级(interrupt priority)。该中断机制能使CPU延迟处理低优先级中断而不屏蔽所有中断,也可以让高优先级中断抢占低优先级中断处理。

          

        现代操作系统可以与中断机制进行多种方式的交互。在启动时,操作系统探查硬件总线以发现哪些设备是存在的,而且将相应中断处理程序安装到中断向量中。在I/O过程中,各种设备控制器如果准备好服务就会触发中断。这些中断表示输出已完成,或输入数据已准备好,或已检测到错误。中断机制也用来处理各种异常,如被0除,访问一个受保护的或不存在的内存地址,企图从用户态执行一个特权指令。触发中断的事件有一个共同特点:它们都是会导致CPU去执行一个紧迫的自我独立的程序的事件。

        对于能够保存少量处理器的状态并能调用内核中的特权程序的高效硬件和软件机制来说,操作系统还有其他用途。例如,许多操作系统使用中断机制进行虚拟内存分页。页错误引起中断异常,该中断会挂起当前进程并跳转到内核的页错误处理程序。该处理程序保存进程状态,将所中断地进程加到等待队列中,进行页面缓存管理,安排一个I/O操作来获取所需页面,安排另一个进程恢复执行,并从中断返回。

        另一个例子是系统调用的实现。通常一个程序使用库调用来执行系统调用。库程序检查应用程序所给的参数,建立一个数据结构将参数传递给内核,并执行一个称为软中断(software interrupt)或者陷阱指令(trap)的特殊指令。该指令有一个参数用来标识所需的内核服务。当系统调用执行陷阱指令时,中断硬件会保存用户代码的状态,切换到内核模式,分派到实现所请求服务的内核程序。陷阱所赋予的中断优先级要比设备中断优先级要低----位应用程序执行系统调用与在FIFO队列溢出并丢去数据之前的处理设备控制器相比,后者更为紧迫。

        多线程的内核体系结构非常适合实现多优先级中断,并确保中断处理的优先级要高于内核后台处理和用户程序的优先级。

        总而言之,中断在现代操作系统中用来处理异步事件和设置陷阱进入内核模式的管理程序。为了能使最紧迫的工作先做,现代计算机都使用中断优先级。设备控制器,硬件错误,系统调用都可以引起中断并触发内核程序。由于中断大量地用于时间敏感的处理,所以高性能系统需要高效中断处理。

    直接内存访问

        对于需要做大量传输的设备,例如磁盘驱动器,如果使用昂贵的通用处理器来观察状态位并按字节来向控制器寄存器送入数据---------一个称为程序控制I/O(Programmed I/O,PIO)的过程,那么就浪费了。许多计算机位为了避免用PIO增加CPU的负担,将一部分任务放给一个专用处理器,称之为直接内存访问(direct-memory access, DMA)控制器。在开始DMA传输时,主机向内存写入DMA命令块。该块包括传输的源地址指针,传输的目的地指针,传输的字节数。CPU在将该命令块的地址写入到DMA控制器中后,就继续其他工作。DMA控制器则继续下去直接操作内存总线,无须主CPU的帮助,就可以将地址放到总线以开始传输。一个简单的DMA控制器已经是PC的标准部件。一般来说,PC上采用总线控制I/O的主板都拥有它们自己的高速DMA硬件。

        DMA控制器与设备控制器之间的握手通过一对称为DMA-request和DMA-acknowledge的线来进行。当有数据需要传输时,设备控制器就通过DMA-request线发送信号。该信号会导致DMA控制器抓住内存总线,并在内存地址总线上放上所需地址,并通过DMA-acknowledge线发送信号。当设备控制器收到DMA-acknowlege信号时,就可以向内存传输数据,并清除DMA-request请求信号。

        当整个传输完成后,DMA控制器中断CPU。下图描述了这一过程。

        当DMA控制器抓住内存总线时,CPU会暂时不能访问主内存,但可以访问一级或二级高速缓存中的数据项。虽然这种周期挪用(cycle stealing)可能放慢CPU计算,但是将数据传输工作交给DMA控制器常常能改善系统总体性能。有的计算机体系结构的DMA使用物理内存地址,而有的使用直接虚拟内存访问(direct virtual-memory access DVMA),这里所使用的虚拟内存地址需要经过虚拟到物理地址装换。DVMA可以直接实现两个内存映射设备之间的传输,而无需CPU的干涉或使用主内存。

        对于保护模式内核,操作系统通常不允许进程直接向设备发送命令。该规定保护数据以免违反访问控制,并保护系统不因设备控制器的错误使用而崩溃。取而代之的是,操作系统导出一些函数,这些函数可以被具有足够特权的进程来访问低层硬件的低层操作。对于没有没有内核保护的内核,进程可以直接访问设备控制器。该直接访问可以得到高性能,这是因为它避免了内核通信,上下文切换及内核软件层。不过,这也破坏了系统的安全与稳定。通用操作系统的发展趋势是保护内存和设备,这样系统可以预防错误或恶意应用程序的破坏。

    I/O应用接口

        下图说明了内核中与I/O相关部分是如何按软件层来组织的。

    设备驱动程序层的作用是为内核I/O子系统隐藏设备控制器之间的差异,就如同I/O系统调用通用类型封装了设备行行为,为应用程序隐藏了硬件差异。将I/O子系统与硬件分离简化了操作系统开发人员的任务。

        绝大多数操作系统存在后门(escape或back door),这允许应用程序将任务命令透明地址传递到设备控制器。

    块与字符设备

        块设备接口规定了访问磁盘驱动器和其他基于块设备所需的各个方面。通常设备应能理解read()和write()命令,如果是随机访问设备,也应有seek()命令以描述下次传输哪个块。应用程序通常通过文件系统接口访问设备。大家看到read(),write(),see()描述了块存储设备的基本特点,这样应用程序就不必关注这些设备的低层差别。

        操作系统本身和特殊应用程序(如数据库管理系统)可能更加倾向于将块设备当做一个简单的线性数组来访问。这种访问方式有时称为原始I/O。如果应用程序执行自己的缓冲,那么使用文件系统会引起额外的不必要的缓冲。同样的,如果应用程序提供自己的文件块或者域的加锁,那么操作系统服务就会闲的多余,在最坏的情况下甚至带来冲突。为了避免这些冲突,将原始设备访问控制由操作系统直接转移到应用程序。这样的后果是管理设备的操作系统服务不再存在了。一种越来越常见的折中办法是允许禁止缓存和锁的文件操作模式。在UNIX中,这种方式称为直接I/O(direct I/O)。

        内存映射文件访问时建立在块设备驱动程序之上的。内存映射接口并不提供read和write操作,而是通过内存中的字节数组来访问磁盘存储。将文件映射到内存的系统调用返回一个字节数组的虚拟内存地址,该字节组数包含了文件的一个副本。实际的数据传输在需要时才执行,以满足内存映像的访问。因为传输采用了与按需分页虚拟内存访问相同的机制,所以内存映射I/O比较高效。内存映射也有益于程序员-----访问内存映射文件像读写内存一样简单。提供虚拟内存的操作系统常常为内存服务提供映射接口。例如,为执行程序,操作系统将可执行程序映射到内存中,并切换到可执行程序的入口地址。内存映射也常被内核用来访问磁盘交换空间。

        键盘是一种可以通过字节流接口访问的设备。这类接口的基本系统调用使得应用程序可以get()或put()一个字符。在此接口之上,可以构造库以提供具有缓冲和编辑功能的按行访问。

    网络设备

        由于网络I/O的性能和访问特点与磁盘I/O相比有很大差别,绝大多数操作系统所提供的网络I/O接口也不同于磁盘的read()-write()-seek()接口。许多操作系统所提供的接口是网络Socket接口,如UNIX和Windows NT。

        想一下墙上的电源插座:任何电器都可以插入。同样,Socket接口的系统调用可以让应用程序创建一个Socket,连接本地Socket和远程地址(将本地应用程序与由远程应用程序创建的Socket相连),监听要与本地Socket相连的远程应用程序,通过连接发送和接收数据。为了支持服务器的实现,Socket接口还提供了select()函数,以管理一组Sockt。

    时钟与定时器

        许多计算机都有硬件时钟和定时器以提供如下三个基本函数:

    • 获取当前时间
    • 获取已经逝去的时间
    • 设置订时器,以在T时触发操作X

    这些函数被操作系统和时间敏感的应用程序大量使用。不过,实现这些函数的系统调用并没有在操作系统之间实现标准化。

        测量逝去时间和触发操作的硬件称为可编程间隔定时器(programmable interval timer)。它可被设置为等待一定的时间,然后触发中断。它也可以设置成做一次或重复多次,以产生周期性中断。调度程序可以使用这种机制来产生中断,以抢占时间片用完的进程。

        对许多计算机,由硬件时钟产生的中断率约在每秒18-60次计时单元(tick)。

    阻塞与非阻塞I/O

        系统调用接口的另一个方面与阻塞与非阻塞I/O的选择有关。当应用程序发出一个阻塞系统调用时,应用程序的执行就被挂起。应用程序将会从操作系统的运行队列移到等待队列上去。在系统调用完成后,应用程序就移回到运行队列,并在适合的时候继续执行并能收到系统调用返回值。由I/O设备执行得物理动作常常是异步地:其执行时间可变或不可预计。然而,绝大多数操作系统为应用程序接口使用阻塞系统调用,这是因为阻塞应用代码比非阻塞应用代码更容易理解。

        有的用户级进程需要使用非阻塞I/O。用户接口是其中的一个例子,它用来接收键盘和鼠标输入,同时还要处理并在屏幕上显示数据。另一个例子是一个视频应用程序,它用来从磁盘文件上读取帧,同时解压缩并在显示器上显示输出。

    I/O内核子系统

        内核提供了许多与I/O有关的服务。许多服务如调度,缓冲,高速缓冲,假脱机,设备预留及错误处理是由内核I/O子系统提供的,并建立在硬件和设备驱动程序结构之上的。I/O子系统还负责保护自己免受错误进程和恶意用户的危害。

    I/O调度

        调度一组I/O请求就是确定一个合适的顺序来执行这些请求。应用程序所发布的系统调用的顺序并不一定总是最佳选择。调度能改善系统整体性能,能在进程之间公平地共享设备访问,能减少I/O完成所需要的平均等待时间。

        支持异步I/O的内核同时也要能跟踪许多I/O请求。为此,操作系统为设备状态表(device status table)配备等待队列。内核管理这个表,表中包含了每一个I/O设备的条目,每个表条目表明了设备类型、地址和状态(不工作,空闲或忙)。如果设备在忙于一个请求,那么请求的类型和其他参数将会被保存在该设备的表条目中。

        I/O子系统改善计算机效率的一种方法是进行I/O操作调度。另一种方法是使用主存货磁盘上的存储空间的技术,如缓冲,高速缓冲,假脱机。

    缓冲

        缓冲区是用来保存两个设备之间或在设备和应用程序之间所传输数据的内存区域。

    高速缓存

        高速缓存(cache)是可以保留数据副本的高速存储器。高速缓冲区副本的访问要比原始数据访问要更为高效。缓冲与高速缓存的差别是缓冲可能是数据项的唯一副本,而根据定义高速缓存只是提供了一个驻留在其他地方的数据在高速存储上的一个副本。

    假脱机与设备预留

        假脱机(Spooling)是用来保存设备输出的缓冲区,这些设备(如打印机)不能接收交叉的数据流。虽然打印机只能一次打印一个任务,但是可能多个程序希望并发打印而不将其输出混在一起。操作系统通过截取对打印机的输出来解决这一问题。应用程序的输出先是假脱机到一个独立的磁盘文件上。当应用程序完成打印时,假脱机系统将对相应的待送打印机的假脱机文件进行排队。假脱机系统一次复制一个已排队的假脱机文件到打印机上。有的操作系统采用系统守护进程来管理假脱机,而有的操作系统采用内核线程来处理假脱机。不管怎么样,操作系统都提供了一个控制接口以便用户和系统管理员来显示队列,删除那些尚未打印的而不再需要的任务,当打印机工作暂停打印等。

    错误处理

        采用内存保护的操作系统可以预防许多硬件和应用程序的错误,这样就不会应为小的机械失灵导致系统崩溃。设备和I/O传输的出错有多种方式,有的短暂,如网络过载;有的永久,如磁盘控制器缺陷。操作系统可以对短暂的出错进行弥补。

    I/O保护

        错误与保护息息相关。通过发出非法I/O指令,用户程序可以有意或无意地中断系统的正常操作。可使用各种机制以确保这种中断不会发生。

        为了防止用户执行非法I/O,定义所有I/O指令为特权指令。因此,用户不能直接发出I/O指令,它们必须通过操作系统来进行。要进行I/O,用户程序执行系统调用来请求操作系统代表用户执行I/O操作。如下图所示,操作系统在监控模式下,检查请求是否合法,如合法,则处理I/O请求,然后返回给用户。

    另外,所有内存映射和I/O端口内存位置都受到内存保护系统的保护,以阻止用户访问。注意,内核不能简单地拒绝所有用户访问。

    内核数据结构

        内核需要保存I/O组件使用的状态信息,可以通过若干内核数据结构如文件打开表等来完成。内核使用许多类似地结构来跟踪网络连接,字符设备通信和其他I/O活动等。

        UNXI提供对若干实体,如用文件、原设备和进程地址空间的文件系统访问。有的操作系统更为广泛地使用了面向对象方法。例如,Windows NT的I/O采用消息传递来实现。一个I/O请求首先转换成一条消息,然后再通过内核传递给I/O管理器,再到设备驱动程序。

    内核I/O子系统小结

        总而言之,I/O子系统为应用程序和内核其他部分提供了一个可扩展的服务集合,I/O子系统负责:

    • 文件和设备的命名空间的管理
    • 文件和设备的访问控制
    • 操作控制(例如,调制解调器不能使用seek())
    • 文件系统空间分配
    • 设备分配
    • I/O调度
    • 设备状态监控,错误处理以及失败恢复。
    • 设备驱动程序的配置和初始化

    把I/O操作转换成硬件操作

        以上描述设备驱动程序与设备控制器之间的握手,但还没有解释操作系统是如何将应用程序的请求与网络线路或特定磁盘扇区连接起来。这里以磁盘读文件为例来考虑这一问题。应用程序通过文件名来访问数据。对于一个磁盘,文件系统通过文件目录从文件名进行映射,从而得到文件的空间分配。

        文件名到磁盘控制器(硬件端口地址或内存映射控制器寄存器)的连接是如何建立的呢?首先来看一个相对简单的操作系统MS-DOS。MS-DOS文件名在冒号前的部分是一个字符串,用来表示特定硬件设备。设备名称空间有别于每个设备内文件系统的名称空间。这一区别有助于操作系统将额外功能与每个设备相连。

        如果设备名称空间集成到普通文件系统的名称空间,如UNIX,那么就自动提供了普通文件系统名称服务。如果文件系统提供对所有文件名称进行所有权和访问控制,那么设备就有所有权的访问控制。由于文件保存在设备上,这种接口提供对I/O系统的双层访问。名称能用来访问设备,也用来访问存储在设备上的文件。

        UNIX通过普通文件系统名称空间来给设备命名。与MS-DOS文件名称不一样(即有冒号分隔符),UNIX路劲名并不区别设备部分。事实上,路劲名中没有设备名称。UNIX中有一个装配表(mount table),用来将路劲名的前缀与特定设备名称相连。为了解析路劲名,UNIX检查装配表内的名称以找到最长的匹配前缀,装配表内相应条目就给来了设备名称。该设备名称在文件系统名称空间内也有一个名称。当UNIX在文件系统目录结构内查找该名称时,得到的不是inode号,而是设备号<主,次>。主设备号表示处理该设备I/O的设备驱动程序。次设备号传递给设备驱动程序以查找设备表。设备表内的相应条目会给出设备控制器的端口地址或内存映射地址。

        现代操作系统通过对请求与物理设备控制器之间的多级表查找,可以获得巨大的灵活性。应用程序与驱动程序之间的请求传递机制是通用的。因此,不必重新编译内核也能为计算机引入新设备和新驱动程序。事实上,有的操作系统能够按需加载设备驱动程序,在启东时,系统首先检测硬件总线与确定有哪些设备,接着操作系统就马上或等首次I/O请求时装入所需的驱动程序。

        下图描述了阻塞读请求的典型周期。

    1. 一个进程对已打开文件的文件描述符调用阻塞read()系统调用。
    2. 内核系统调用代码检查参数是否正确,对于输入,如果数据已在高速缓存中,那么就将该数据返回给进程并完成I/O请求。
    3. 否则,就需要执行物理I/O请求。这时,该进程会从运行队列移到设备的等待队列,并调度I/O请求。最后I/O子系统对设备驱动程序发出请求。根据操作系统的不同,该请求可能通过子程序调用或内核消息传递。
    4. 设备驱动程序分配内核缓冲空间以接收数据,并调度I/O。最后,设备驱动程序通过写入设备控制器寄存器来对设备控制器发送命令。
    5. 设备控制器控制设备硬件以执行数据传输。
    6. 驱动程序可以轮询检测状态和数据,或通过设置DMA将数据传入到内核内存。假定DMA控制器管理传输,当传输完成后会产生中断。
    7. 合适的中断处理程序通过中断向量表收到中断,保存必要的数据,并向内核设备驱动程序发送信号通知,然后从中断返回。
    8. 设备驱动程序接收信号,确定I/O请求是否完成,确定请求状态,并向内核I/O子系统发送信号,通知请求已完成。
    9. 内核将数据或返回代码传递请求进程的地址空间,将进程从等待队列移到就绪队列。
    10. 将进程移到就绪队列会使该进程不再阻塞。当调度器给该进程分配CPU时,该进程就继续在系统调用完成后继续执行。

    性能

        I/O是影响系统性能的重要因素之一。执行设备驱动程序代码以及随着进程阻塞变化而公平且高效地调度进程,这些都增加了CPU的负荷。由此而产生的上下文切换也增加了CPU及其硬件高速缓存的负担。I/O暴露出内核中断机制的任何效率缺陷。

    小结

        相关I/O硬件的基本要素是总线设备控制器设备本身。设备与内存间的数据传输工作由CPU按程序控制I/O完成,或转交给DMA控制器。控制设备的内存模块称为设备驱动程序。提供给应用程序的系统调用接口用于处理若干基本类型的硬件,包括块设备、字符设备、内存映射文件、网络Socket、可编程间隔定时器。系统调用通常会使调用进程阻塞,但是非阻塞和异步调用可以为内核自己使用,也可为不能等待I/O操作完成的应用程序所使用。

        内核I/O子系统提供了若干服务。其中有I/O调度、缓冲、高速缓存、假脱机、设备预留以及出错处理。另一个服务是名称转换,即在硬件设备和应用程序所用的符号文件名之间建立连接。这包括多级映射,用来将字符文件名称映射到特定设备驱动程序和设备地址,然后到I/O端口和总线控制器的物理地址。这一映射可以发生在文件系统名称空间内,如UNIX,也可以出现在独立的设备名称空间内,如MS-DOS。

        流是使得设备驱动程序可以重用和更容易使用的一种实现方法。通过流,驱动程序可以堆叠,数据可以按单向和双向来传输和处理。

        由于物理设备和应用程序之间的多层软件,I/O系统调用所用的CPU周期较多。这些层带来了多种开销,如穿过内核保护边界的上下文切换,用于I/O设备的信号和中断处理,用于内核缓冲区和应用程序之间的数据复制所需的CPU和内存系统的负荷。

  • 相关阅读:
    Java实现 LeetCode 792 自定义字符串排序(暴力)
    Java实现 LeetCode 792 自定义字符串排序(暴力)
    asp.net session对象的持久化
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 791 自定义字符串排序(桶排序)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    Java实现 LeetCode 790 多米诺和托米诺平铺(递推)
    小白也能看懂的约瑟夫环问题
  • 原文地址:https://www.cnblogs.com/kexinxin/p/9939093.html
Copyright © 2020-2023  润新知