目录
- I/O子系统概述
- 用户空间I/O软件
- 用户程序中的I/O函数
- 文件的基本概念
- 系统级I/O函数
- C标准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操作功能通过相应的陷阱指令(自陷制令,有时也叫做“软中断指令”),以“系统调用”的方式转换为由操作系统内核来实现。也就是说,任何I/O操作过程最终都是由操作系统内核控制完成的。
8.1 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硬件
操作系统在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操作符请求I/O操作。下图给出了用户程序用printf()来调出内核提供的write系统调用的过程。

可以看出,对于一个C语言用户程序,若在某过程(函数)中调用了printf(),则在执行到调用printf()语句时,便会转到C语言函数库中对应的I/O标准库printf()去执行,而printf()最终又会转到调用函数write();在执行到write()语句时,便会通过一系列步骤在内核空间中找到write对应的系统调用服务例程来执行,从而从用户态转到内核态执行。
每个系统调用的封装函数会被转换为一组与具体机器架构相关的指令序列,这个指令序列中至少有一条陷阱指令,在陷阱指令之前还可能有若干条传送指令用于将I/O操作的参数送入相应的寄存器。
I/O子系统工作的大致过程如下:首先,CPU在用户态执行用户进程,当CPU执行到系统调用封装函数对应的指令序列中的陷阱指令时,会从用户态陷入到内核态;转到内核态执行后,CPU根据陷阱指令执行时EAX寄存器中的系统调用号,选择执行一个相应的系统调用服务例程;在系统调用服务例程的执行过程中可能需要调用具体设备的驱动程序;在设备驱动程序执行过程中启动外设工作,外设准备好后发出中断请求,CPU响应中断后,就调出中断服务程序执行,在中断服务程序中控制主机与设备进行具体的数据交换。

如图所示,假定用户程序有一个语句调用了库函数printf(),在printf()函数中又通过一系列的函数调用,最终转到调用write()函数,在write()函数对应的指令序列中,一定有一条用于系统调用的陷阱指令。该陷阱指令执行后,进程就从用户态陷入到内核态执行。Linux中有一个系统调用的统一入口,即系统调用处理程序system_call()。CPU执行陷阱指令后,便转到system_call()的第一条指令执行。在system_call()中,将根据EAX寄存器中的系统调用号跳转到当前的系统调用服务例程sys_write()去执行。system_call()执行结束时,从内核态返回到用户态下的陷阱指令后面一条指令继续执行。
网友评论