Download presentation
Presentation is loading. Please wait.
1
第八章 I/O操作的实现 用户空间I/O软件 I/O硬件与软件的接口 内核空间I/O软件
2
I/O和文件操作 主要教学目标 主要教学内容
通过揭示高级语言程序中的I/O及文件操作请求的底层实现机制,使学生深刻理解OS在输入/输出系统中的重要作用;深刻理解计算机中硬件和软件如何协调工作以完成计算机功能。 主要教学内容 I/O子系统的组成和层次结构 用户空间I/O软件 I/O硬件与软件的接口 内核空间I/O软件
3
I/O操作的实现 分以下三个部分介绍 第一讲:用户空间I/O软件 I/O子系统概述 文件的基本概念 用户空间的I/O函数
设备驱动程序 中断服务程序
4
复习:一个典型程序的转换处理过程 经典的“ hello.c ”源程序 #include <stdio.h> int main()
hello.c的ASCII文本表示 #include <stdio.h> int main() { printf("hello, world\n"); } # i n c l u d e <sp> < s t d i o . h > \n \n i n t <sp> m a i n ( ) \n { \n <sp> <sp> <sp> <sp> p r i n t f ( " h e l l o , <sp> w o r l d \ n " ) ; \n }
5
复习:Hello程序的数据流动过程 Unix>./hello hello, world unix> Red:shell命令行处理
Blue:可执行文件加载 Cyan:hello程序执行过程 “hello” “hello,world/n” “hello” “hello,world/n” Hello可执行文件 问题:hello程序何时被装?谁来装入?被谁启动?每次是否被装到相同的地方?Hello程序是否能直接访问硬件资源?
6
操作系统在程序执行过程中的作用 例如,利用printf()函数最终调出内核服务程序访问硬件。
Shell进程生成子进程,子进程调用execve系统调用启动加载器,以装入Hello程序,最后跳转到第一条指令执行 在Hello程序执行过程中,Hello本身不会直接访问键盘、显示器、磁盘和主存储器等硬件资源,而是依靠OS提供的服务来间接访问。 操作系统是在应用程序和硬件之间插入的一个中间软件层。 操作系统的两个主要的作用: 硬件资源管理,以达到以下两个目的: 统筹安排和调度硬件资源,以防止硬件资源被用户程序滥用 对于广泛使用的复杂低级设备,为用户程序提供一个简单一致的使用接口 为用户(最终用户、用户程序)使用系统提供一个操作接口 例如,利用printf()函数最终调出内核服务程序访问硬件。
7
I/O子系统概述 所有高级语言的运行时(runtime)都提供了执行I/O功能的机制
例如,C语言中提供了包含像printf()和scanf()等这样的标准I/O库函数,C++语言中提供了如 <<(输入)和 >>(输出)这样的重载操作符。 从高级语言程序中通过I/O函数或I/O操作符提出I/O请求,到设备响应并完成I/O请求,涉及到多层次I/O软件和I/O硬件的协作。 I/O子系统也采用层次结构 从用户I/O软件切换到内核I/O软件的唯一办法是“异常”机制:系统调用(自陷)
8
I/O子系统概述 OS 最终用户:键盘、鼠标通过操作界面传递给OS 用户程序:通过函数(高级语言)转换为系统调用传递给OS
(1) 用户层I/O软件(I/O函数调用系统调用) (2) 与设备无关的操作系统I/O软件 (3) 设备驱动程序 (4) I/O中断处理程序 大部分I/O软件都属于操作系统内核态程序,最初的I/O请求在用 户程序中提出。 OS OS在I/O系统中极其重要!
9
I/O子系统实例 bioskey()返回键盘状态的基本函数
函数原型在bios.h中,完成直接键盘操作(stdio.h——standard input&output 标准输入输出头文件) 原型表示:int bioskey(int cmd) 其中cmd有3个参数:0,1,2 cmd= 0,bioskey()返回下一个在键盘键入的值(它将等待到按下一个键)。它返回一个16位的二进制数,包括两个不同的值。当按下一个普通键时,它的低8位数存放该字符的ASCII码;对于特殊键(如方向键、F1~F12等等),低8位为0,高8位字节存放该键的扫描码。 cmd = 1:当cmd是1,bioskey()查询是否按下一个键,若按下一个键则返回非零值,否则返回0。 cmd = 2:当cmd是2,bioskey()返回Shift、Ctrl、Alt、ScrollLock、NumLock、CapsLock、Insert键的状态。各键状态存放在返回值的低8位字节中。
10
用户I/O软件 用户软件可用以下两种方式提出I/O请求: (1)使用高级语言提供的标准I/O库函数。例如,在C语言程序中可以直接使用像fopen、fread、fwrite和fclose等文件操作函数,或printf、putc、scanf和getc等控制台I/O函数。 程序移植性很好! 但是,使用标准I/O库函数有以下几个方面的不足: (a) 标准I/O库函数不能保证文件的安全性(无加/解锁机制) (b) 所有I/O都是同步的,程序必须等待I/O操作完成后才能继续执行 (c) 有时不适合甚至无法使用标准I/O库函数实现I/O功能,如,不提供读取文件元数据的函数(元数据包括文件大小和文件创建时间等) (d) 用它进行网络编程会造成易于出现缓冲区溢出等风险 (2)使用OS提供的API函数或系统调用。如,在Windows中直接使用像CreateFile、ReadFile、WriteFile、CloseHandle等文件操作API函数,或ReadConsole、WriteConsole等控制台I/O的API函数。对于Unix或Linux用户程序,则直接使用像open、read、write、close等系统调用封装函数。
11
用户I/O软件 用户进程请求读磁盘文件操作
用户进程使用标准C库函数fread,或Windows API函数 ReadFile,或Unix/Linux的系统调用函数read等要求读一个磁盘文件块。 用户程序中涉及I/O操作的函数最终会被转换为一组与具体机器架构相关的指令序列,这里我们将其称为I/O请求指令序列。 例如,若用户程序在IA-32架构上执行,则I/O函数被转换为IA-32的指令序列。 每个指令系统中一定有一类陷阱指令(有些机器也称为软中断指令或系统调用指令),主要功能是为操作系统提供灵活的系统调用机制。 在I/O请求指令序列中,具体I/O请求被转换为一条陷阱指令,在陷阱指令前面则是相应的系统调用参数的设置指令。
12
系统I/O软件 OS在I/O子系统中的重要性由I/O系统以下三个特性决定: (1)共享性。I/O系统被多个程序共享,须由OS对I/O资源统一调度管理,以保证用户程序只能访问自己有权访问的那部分I/O设备,并使系统的吞吐率达到最佳。 (2)复杂性。I/O设备控制细节复杂,需OS提供专门的驱动程序进行控制,这样可对用户程序屏蔽设备控制的细节。 (3)异步性。不同设备之间速度相差较大,因而,I/O设备与主机之间的信息交换使用异步的中断I/O方式,中断导致从用户态向内核态转移,因此必须由OS提供中断服务程序来处理。 那么,如何从用户程序对应的用户进程进入到操作系统内核执行呢? 系统调用!
13
系统调用和API OS提供一组系统调用为用户进程的I/O请求进行具体的I/O操作。
API 函数最终通过调用系统调用实现 I/O。一个API 可能调用多个系统调用,不同 API 可能会调用同一个系统调用。但是,并不是所有 API 都需要调用系统调用。 从编程者来看,API 和 系统调用之间没有什么差别。 从内核设计者来看,API 和 系统调用差别很大。API 在用户态执行,系统调用封装函数也在用户态执行,但具体服务例程在内核态执行。
14
系统调用及其参数传递 在用户态,当进程调用一个系统调用时,CPU切换到内核态,并开始执行一个被称为系统调用处理程序的内核函数
例如,IA-32中,可以通过两种方式调用Linux的系统调用 执行软中断指令int 80 执行指令sysenter(老的x86不支持该指令) 内核实现了许多系统调用,因此,用一个系统调用号(存放在EAX中)来标识不同的系统调用 除了调用号以外,系统调用还需要其他参数,不同系统调用所需参数的个数和含义不同,输入参数通过通用寄存器传递,若参数个数超出寄存器个数,则将需传递参数块所在内存区首址放在寄存器中传递(除调用号以外,最多6个参数) 传递参数的寄存器顺序:EAX(系统调用号)、EBX、ECX、EDX、ESI、EDI和EBP 返回参数为整数值。正数或0表示成功,负数表示出错码
15
用户程序、C函数和内核 用户程序总是通过某种I/O函数或I/O操作符请求I/O操作。
例如,读一个磁盘文件记录时,可调用C标准I/O库函数fread(),也可直接调用系统调用封装函数read()来提出I/O请求。不管是C库函数、API函数还是系统调用封装函数,最终都通过操作系统内核提供的系统调用来实现I/O。 printf()函数的调用过程如下:
16
Linux系统中printf()函数的执行过程
用户空间、运行在用户态 内核空间、运行在内核态 main() { … printf(); } printf() { … xxxx(); } write() { … int $0x80 } system_call() { … xxxx(); } sys_write() { … } I/O标准库函数 系统调用封装函数 系统调用处理程序 系统调用服务例程 用户程序 某函数调用了printf(),执行到调用printf()语句时,便会转到C语言I/O标准库函数printf()去执行; printf()通过一系列函数调用,最终会调用函数write(); 调用write()时,便会通过一系列步骤在内核空间中找到write对应的系统调用服务例程sys_write来执行。 在system_call中如何知道要转到sys_write执行呢? 根据系统调用号!
17
应用层的Read函数在Linux内核中的单向20次以上的调用!!
18
用户空间中的I/O函数 用户程序可通过调用特定的I/O函数的方式提出I/O请求。
在UNIX/Linux系统中,可以是C标准I/O库函数或系统调用的封装函数,前者如文件I/O函数fopen()、fread()、fwrite()和fclose()或控制台I/O函数printf()、putc()、scanf()和getc()等;后者如open()、read()、write()和close()等。 标准I/O库函数比系统调用封装函数抽象层次高,后者属于系统级I/O函数。与系统提供的API函数一样,前者是基于后者实现的。
19
用户空间中的I/O函数
20
文件的基本概念 Stream!字节流 printf在哪显示信息?
所有I/O操作通过读写文件实现,所有外设,包括网络、终端设备,都被看成文件。 所有物理设备抽象成逻辑上统一的“文件”使得用户程序访问物理设备与访问真正的磁盘文件完全一致。例如,fprintf/fwrite(主要是磁盘文件) 和 printf (stdout) 都通过统一的write函数陷入内核,差别则由内核处理! UNIX系统中,文件就是一个字节序列。 通常,将键盘和显示器构成的设备称为终端(terminal),对应标准输入、和标准(错误)输出文件;像磁盘、光盘等外存上的文件则是普通文件 。 根据文件的可读性,文件被分成ASCII文件和二进制文件两类。 ASCII文件也称文本文件,可由多个正文行组成,每行以换行符‘\n’ 结束,每个字符占一个字节。标准输入和标准(错误)输出文件是ASCII文件。 普通文件可能是文本文件或二进制文件。 printf在哪显示信息? stdout文件! 即终端显示器TTY Stream!字节流
21
文件的创建和打开 读写文件前,用户程序须告知将对文件进行何种操作:读、写、添加还是可读可写,通过打开或创建一个文件来实现。
已存在的文件:可直接打开 不存在的文件:则先创建 创建文件:int creat(char *name, mode_t perms); 创建新文件时,应指定文件名和访问权限,系统返回一个非负整数,它被称为文件描述符fd (file descriptor)。 文件描述符用于标识被创建的文件,在以后对文件的读写等操作时用文件描述符代表文件。 2. 打开文件:int open(char *name, int flags, mode_t perms); 标准输入(fd=0)、标准输出(fd=1)和标准错误(fd=2)三种文件自动打开,其他文件须用creat或open函数显式创建或打开后才能读写 参数perms用于指定文件的访问权限,通常在open函数中该参数总是0,除非以创建方式打开,此时,参数flags中应带有O_CREAT标志。 参数flags:O_RDONLY, O_WRONLY|O_APPEND, O_RDWR等 例:fd=open(“test.txt”,O_RDONLY, 0);
22
文件的读/写 3. 读文件:size_t read(int fd, void *buf, size_t n);
将fd中当前位置k开始的n个字节读到buf中,读后当前位置为k+n。若文件长度为m,当k+n>m时,则读取字节数为m-k<n,读后当前位置为文件尾。返回实际字节数,当m=k(EOF)时,返回值为0。 4. 写文件:ssize_t write(int fd, const void *buf, size_t n); 将buf中n字节写到fd中,从当前位置k处开始写。返回实际写入字节数m,写后当前位置为k+m。对于普通文件,实际字节数等于n。 对于read和write系统调用,可以一次读/写任意个字节。显然,按一个物理块大小读/写较好,可减少系统调用次数。 有些情况下,真正读/写字节数比设定所需字节数少,这并不是一种错误。在读/写磁盘文件时,除非遇到EOF,否则不会出现这种情况。但当读/写的是终端设备或网络套接字文件、UNIX管道、Web服务器等都可能出现这种情况。
23
文件的定位和关闭 5. 设置读写位置:long lseek(int fd, long offset, int origin);
例:lseek(fd,5L,0);表示定位到文件开始后的第5字节 lseek(fd, 0L, 2);表示定位到文件末尾 返回的是位置值,若发生错误,则返回-1 6. 元数据统计:int stat(const *name, struct stat *buf); int fstat(int fd, struct stat *buf); 文件的所有属性信息,包括:文件描述符、文件名、文件大小、创建时间、当前读写位置等,由内核维护,称为文件的元数据(metadata)。 用户程序可通过stat()或fstat()函数查看文件元数据。 stat第一个参数是文件名,而fstat指出的是文件描述符,除第一个参数类型不同外,其他全部一样。 7. 关闭文件:close(int fd);
24
典型的stdio.h的部分内容 C标准I/O库函数基于系统调用实现
#define NULL #define EOF (-1) #define BUFSIZ #define OPEN_MAX /* 最多打开文件数 */ typedef struct _iobuf { int cnt; /*未读写字节数 */ char *ptr; /*下一可读写位置 */ char *base; /* 起始位置 */ int flag; /* 存取模式 */ int fd; /*文件描述符 */ } FILE; extern FILE _iob[OPEN_MAX]; #define stdin (&_iob[0]) #define stdout (&_iob[1]) #define stderr (&_iob[2]) enum _flags { _READ= 01, /* file open for reading */ _WRITE= 02, /* file open for writing */ _UNBUF= 04, /* file is unbuffered */ _EOF= 010, /* EOF has occurred on this file */ _ERR= /* error occurred on this file */ }; C标准I/O库函数基于系统调用实现 C标准I/O库函数将打开文件抽象为一个类型为FILE的“流”,它在stdio.h中定义。 用数组实现I/O(文件)缓冲 FILE _iob[OPEN_MAX] = { { 0, ( char * ) 0, ( char * ) 0, _READ, 0 }, { 0, ( char * ) 0, ( char * ) 0, _WRITE, 1 }, { 0, ( char * ) 0, ( char * ) 0, _WRITE | _UNBUF, 2 }, }; stdout和stderr都用于输出,但是, stderr为非缓存 stdout为带缓存
25
带缓冲I/O的实现 从文件fp中读数据时,FILE中定义的缓冲区为输入流缓冲(在内存)
首先要从文件fp中读入1024(缓冲大小BUFSIZ=1024)个字节数据到缓存,然后,再按需从缓存中读取1个(如getc)或n个(如fread)字节并返回 未读部分 已读部分 输入流缓冲 (fp)->base (fp)->ptr (fp)->cnt 用FILE结构描述 输入 相对于fp首的位移 未读部分 已读部分 已读入并出缓冲 文件中未缓存 文件当前指针 输入流缓冲 fp文件对应的字节流 fp文件在哪里? 磁盘上或键盘输入
26
带缓冲I/O的实现 向文件fp中写数据时,FILE中定义的缓冲区为输出流缓冲
先按需不断地向缓存写1个(如putc)或n个(如fwrite)字节,遇到换行符\n或缓存被写满1024(缓冲大小BUFSIZ=1024)个字节,则将缓存内容一次写入文件fp中 未写部分 已写部分 输出流缓冲 (fp)->base (fp)->ptr (fp)->cnt 输出 未写部分 已写部分 已写入fp并出缓冲 文件当前指针 输出流缓冲 fp文件对应的字节流
27
stdout和stderr的差别 输出结果为:world! hello 输出结果为:world!hello 输出结果为:hello
#include<stdio.h> int main() { fprintf(stdout, “hello "); fprintf(stderr, “world!\n"); return 0; } 猜一下在Linux中以下程序输出什么? #include<stdio.h> int main() { fprintf(stdout, “hello "); fprintf(stderr, “world!"); return 0; } 输出结果为:world! hello #include<stdio.h> int main() { fprintf(stdout, “hello \n"); fprintf(stderr, “world!"); return 0; } 输出结果为:world!hello stdout和stderr都用于标准输出,但是, stderr为 _WRITE | _UNBUF stdout为 _WRITE 输出结果为:hello world! 有缓冲:遇到换行符\n或缓冲满(BUFSIZE=1024)才写文件!
28
举例:文件复制功能的实现 哪种方式更好? 方式一更好!Why? 因其系统调用次数少! 对于方式二,若文件长度为n,则需执行2n次系统调用;
/* 方式一: getc/putc版本 */ void filecopy(FILE *infp, FILE *outfp) { int c; while ((c=getc(infp)) != EOF) putc(c, outfp); } /* 方式二: read/write版本 */ void filecopy(int *infp, int *outfp) char c; while (read(infp,&c,1) != 0) write(outfp,&c,1); 哪种方式更好? 方式一更好!Why? 因其系统调用次数少! 对于方式二,若文件长度为n,则需执行2n次系统调用; 对于方式一,若文件长度为n,则执行系统调用的次数约为n/512。 为何要尽量减少系统调用次数? 系统调用的开销有多大? 相当大! 还有其他的实现方式吗? 使用fread()和fwrite() 使用fgetc()和fputc() 使用WindowsAPI函数CopyFile() SKIP 实现一个功能有多种方式,但开销和性能不同,需要权衡!
29
I/O操作的实现 分以下三个部分介绍 第一讲:用户空间I/O软件 I/O子系统概述 文件的基本概念 用户空间的I/O函数
设备驱动程序 中断服务程序
30
I/O硬件的组成 I/O硬件建立了外设与主机之间的“通路”: 主机----I/O总线(桥)----设备控制器----电缆----外设
如何把用户I/O请求转换为对设备的控制命令并完成设备I/O任务,需要I/O软件与I/O硬件之间的协调工作 如:printf("hello, world\n");
31
主机----I/O总线(桥)----设备控制器(带连接器)----电缆----外设
32
连接外部设备的连接器 主机----I/O总线(桥)----设备控制器(带连接器)----电缆----外设
33
外部设备的通用模型 设备控制器 (连接器) 电缆 通过电缆与设备控制器(I/O接口)进行数据、状态和控制信息的传送
控制逻辑根据控制信息控制设备的操作,并检测设备状态 缓冲器用于保存交换的数据信息 变换器用于在电信号形式(内部数据)和其他形式的设备数据之间进行转换 控制 状态 数据 缓冲器 变换器 控制逻辑 所有设备都可抽象成该通用模型! 设备所用电缆中有三种信号线: 控制信号、状态信号、数据信号 设备数据 环境
34
设备控制器的结构 设备控制器的一般结构:不同I/O模块在复杂性和控制外设的数量上相差很大 通过发送命令字到I/O控制寄存器来向设备发送命令
设备控制器又称 I/O控制器简称 I/O模块 或 I/O接口 通过发送命令字到I/O控制寄存器来向设备发送命令 通过从状态寄存器读取状态字来获取外设或I/O控制器的状态信息 通过向I/O控制器发送或读取数据来和外设进行数据交换 将I/O控制器中CPU能够访问的各类寄存器称为I/O端口 对外设的访问通过向I/O端口发命令、读状态、读/写数据来进行
35
显卡的外部连接特征 VGA连接器 连接到显示器 连接到 I/O总线 (主机侧) 将I/O控制器中CPU能够访问的各类寄存器称为I/O端口
36
I/O端口的寻址方式 (1)统一编址方式(内存映射方式) 一个I/O控制器可能会占有多个端口地址 I/O端口必须编号后,CPU才能访问它
教室和办公室可以连号(统一编址),也可单独编号(独立编址) (1)统一编址方式(内存映射方式) 与主存空间统一编址,主存单元和I/O端口在同一个地址空间中。 (将I/O端口映射到某个主存区域,故也称“存储器映射方式”) 例如,RISC机器、Motorola公司的处理器等采用该方案 VRAM(显示存储器)通常也和主存统一编址 (2)独立编址方式(特殊I/O指令方式) 单独编号,不和主存单元一起编,使成为一个独立的I/O地址空间 (因为需专门I/O指令,故也称为“特殊I/O指令方式”) 例如,Intel公司和Zilog公司的处理器就是独立编址方式
37
驱动程序与I/O指令 控制外设进行输入/输出的底层I/O软件是驱动程序
将控制命令送到控制寄存器来启动外设工作; 读取状态寄存器了解外设和设备控制器的状态; 访问数据缓冲寄存器进行数据的输入和输出。 对I/O端口的访问操作由I/O指令完成,它们是一种特权指令 IA-32中的I/O指令:in、ins、out和outs in和ins用于将I/O端口的内容取到CPU内的通用寄存器中; out和outs用于将通用寄存器内容输出到I/O端口。 如 IN AL, DX:DX中存放I/O端口地址,将I/O端口中的内容取到AL中
38
三种基本I/O方式 程序直接控制方式(最简单的I/O方式) 无条件传送:对简单外设定时(同步)进行数据传送
条件传送:CPU主动查询,也称程序查询或轮询(Polling)方式 I/O Interrupt (中断I/O方式): 几乎所有系统都支持中断I/O方式 若一个I/O设备需要CPU干预,它就通过中断请求通知CPU CPU中止当前程序的执行,调出OS(中断处理程序)来执行 处理结束后,再返回到被中止的程序继续执行 Direct Memory Access (DMA方式): 磁盘等高速外设所用的方式 磁盘等高速外设成批地直接和主存进行数据交换 需要专门的DMA控制器控制总线,完成数据传送 数据传送过程无需CPU参与
39
程序查询I/O方式 外设 CPU sys_write系统调用服务例程 探询 此时,CPU处于停止状态吗? “踏步”现象
启动 探询 完成 “踏步”现象 工作 此时,CPU处于停止状态吗? 不是!只是不断执行 “ IN-TEST-JE” 3条指令,称为“忙等待”! “探询”期间,可一直不断查询(独占查询),也可定时查询(需保证数据不丢失!)。 特点: 简单、易控制、外围接口控制逻辑少; CPU与外设串行工作,效率低、速度慢,适合于慢速设备 查询开销极大 (CPU完全在等待“外设完成”) 工作方式:完全串行或部分串行,CPU用100%的时间为I/O服务!
40
中断I/O方式 基本思想: 完成 完成 工作 工作 工作 外设 启动 启动 CPU 启动 请求 响应 请求 响应 返回
当外设准备好(ready)时,便向CPU发中断请求,CPU响应后,中止现行程序的执行,转入“中断服务程序”进行输入/出操作,以实现主机和外设接口之间的数据传送,并启动外设工作。 “中断服务程序”执行完后,返回原被中止的程序断点处继续执行。此时,外设和CPU并行工作。 中断服务程序 sys_write系统调用服务例程 完成 完成 工作 工作 工作 外设 启动 启动 P Q Q Q CPU 启动 请求 响应 请求 响应 返回 P 被阻塞,调其他进程Q执行 上述哪段时间CPU和外设并行工作? 程序切换(响应中断)由硬件完成,即执行“中断隐指令”,时间为
41
中断控制器的基本结构 INTR …… …… 中断类型号形成线路 判 优 线 路 屏蔽寄存器 中断请求寄存器
中断类型号送到什么线上? 何时采样中断请求信号? 中断查询信号发出后的固定时间内 数据线上!为什么? CPU CPU采样到INT信号有效,则进入“中断响应周期”! 中断类型:32+i 中断请求信号INT 中断控制器 中断类型号形成线路 INTR 判 优 线 路 中断查询信号何时发出? …… 每条指令最后一个控制信号启动查询! CPU发出中断查询请求信号 屏蔽寄存器 中断请求寄存器 …… 来自CPU,通过OUT指令设置 IRQ0、… 、IRQi 、 … 来自不同外设,如IRQ0为键盘中断
42
中断I/O方式 中断过程 中断检测(硬件实现) 中断响应(硬件实现) 中断处理(软件实现) 中断响应
中断响应是指主机发现外部中断请求,中止现行程序的执行,到调出中断服务程序这一过程。 中断响应的条件 ① CPU处于开中断状态 ② 在一条指令执行完 至少要有一个未被屏蔽的中断请求 中断处理 中断响应 问题:中断响应的时点与异常处理的时点是否相同?为什么? 通常在指令执行结束时查询有无中断请求,有则立即响应; 而异常发生在指令执行过程中,一旦发现则马上处理。
43
中断处理过程 单重中断不允许在中断处理时被新的中断打断,因而直到中断返回前才会开中断。单重中断系统无需设置中断屏蔽字。
中断响应的结果就是调出相应的中断服务程序 中断处理是指执行相应中断服务程序的过程 不同的中断源其对应的中断服务程序不同。 典型的多重中断处理(中断服务程序)分为三个阶段: 先行段(准备阶段) 保护现场及旧屏蔽字 查明原因(软件识别中断时) 设置新屏蔽字 开中断 本体段(具体的中断处理阶段) 结束段(恢复阶段) 关中断 恢复现场及旧屏蔽字 清“中断请求” 中断返回 处在“关中断”状态, 不允许被打断 处在“开中断”状态,可被新的处理优先级更高的中断打断 处在“禁止中断”状态,不允许被打断 单重中断不允许在中断处理时被新的中断打断,因而直到中断返回前才会开中断。单重中断系统无需设置中断屏蔽字。
44
多重中断的概念 多重中断和中断处理优先权的动态分配 多重中断的概念:
在一个中断处理(即执行中断服务程序)过程中,若又有新的中断请求发生,而新中断优先级高于正在执行的中断,则应立即中止正在执行的中断服务程序,转去处理新的中断。这种情况为多重中断,也称中断嵌套。 中断优先级的概念: 中断响应优先级----由查询程序或硬联排队线路决定的优先权,反映多个中断同时请求时选择哪个响应。 中断处理优先级----由各自的中断屏蔽字来动态设定,反映本中断与其它中断间的关系。 回想一下,中断屏蔽字在何处用到的?
45
多重中断嵌套 中断处理优先级的顺序是: 3# > 2# > 1# 1# 对 2# 开放(不屏蔽) 2# 对 3# 开放(不屏蔽)
46
轮询方式和中断方式的比较 举例:假定某机控制一台设备输出一批数据。数据由主机输出到接口的数据缓冲器OBR,需要1μs。再由OBR输出到设备,需要1ms。设一条指令的执行时间为1μs(包括隐指令)。试计算采用程序传送方式和中断传送方式的数据传输速度和对主机的占用率。 问题:CPU如何把数据送到OBR,I/O接口如何把OBR中的数据送到设备? CPU执行I/O指令来将数据送OBR;而I/O接口则是自动把数据送到设备。 对主机占用率: 在进行I/O操作过程中,处理器有多少时间花费在输入/出操作上。 数据传送速度(吞吐量、I/O带宽): 单位时间内传送的数据量。 假定每个数据的传送都要重新启动!即是字符型设备
47
DMA方式的基本要点 DMA方式的基本思想 在高速外设和主存间直接传送数据 由专门硬件(即:DMA控制器)控制总线进行传输
高速设备(如:磁盘、光盘等) 成批数据交换,且数据间间隔时间短,一旦启动,数据连续读写 采用“请求-响应”方式 每当高速设备准备好数据就进行一次“DMA请求”,DMA控制器接受到DMA请求后,申请总线使用权 DMA控制器的总线使用优先级比CPU高,为什么? 与中断控制方式结合使用 在DMA控制器控制总线进行数据传送时,CPU执行其他程序 DMA传送结束时,要通过“DMA结束中断”告知CPU
48
读一个磁盘扇区 - 第一步 CPU对DMA控制器初始化: 将传送方向(读)、传送数据个数、 磁盘逻辑块号、主存起始地址等参数送到DMA控制器
CPU chip Register file ALU Main memory Bus interface I/O bus DMA 控制器 传送数据个数被送到计数器中 USB controller Graphics adapter Disk controller keyboard mouse Monitor Disk
49
读一个磁盘扇区–第二步 磁盘控制器读相应的扇区,并由DMA控制器控制总线把数据从磁盘控制器送主存,此时,CPU执行其他进程 DMA 控制器
CPU chip Register file ALU Main memory Bus interface I/O bus DMA 控制器 每传送一个数据,则计数器减1 USB controller Graphics adapter Disk controller Mouse Keyboard Monitor Disk
50
读一个磁盘扇区–第三步 当DMA传送结束(计数为0),DMA控制器向CPU发出“DMA结束中断请求”,要求CPU进行相应的后处理。 DMA
CPU chip 当DMA传送结束(计数为0),DMA控制器向CPU发出“DMA结束中断请求”,要求CPU进行相应的后处理。 Register file ALU Main memory Bus interface I/O bus DMA 控制器 USB controller Graphics adapter Disk controller Mouse Keyboard Monitor Disk
51
I/O操作的实现 分以下三个部分介绍 第一讲:用户空间I/O软件 I/O子系统概述 文件的基本概念 用户空间的I/O函数
设备驱动程序 中断服务程序
52
内核空间I/O软件 外设 CPU 启动 完成 工作 请求 响应 返回 所有用户程序提出的I/O请求,最终都通过系统调用实现
设备无关软件层 设备驱动程序层 中断服务程序层 设备驱动程序层、中断服务程序层与I/O硬件密切相关 系统调用服务例程,被陷阱指令调出执行,一旦发送“启动”命令,则所代表的进程被送等待队列(即被阻塞) 外设 CPU 启动 完成 工作 请求 响应 返回 P 被阻塞 Q P
53
与设备无关层 设备驱动层 read 在Linux内核中单向调用20次以上 文件系统层 通用块设备层 I/O调度层
Int 0x80触发系统调用 sys_read 文件系统层 与设备无关层 通用块设备层 I/O调度层 设备驱动层
54
设备无关I/O软件层 设备驱动程序统一接口
所有设备都抽象成文件,设备名和文件名在形式上没有差别,设备和文件具有统一的接口,不同设备名和文件名被映射到对应设备驱动程序。 缓冲处理 每个设备的I/O都需使用内核缓冲区,因而缓冲区的申请和管理等处理是所有设备公共的,可包含在与设备无关的I/O软件部分 错误报告 I/O操作在内核态执行时所发生的错误信息,都通过与设备无关的I/O软件返回给用户进程,也即:错误处理框架与设备无关。 直接返回编程等错误,无需设备驱动程序处理,如,请求了不可能的I/O操作;写信息到一个输入设备或从一个输出设备读信息;指定了一个无效缓冲区地址或者参数;指定了不存在的设备等。 有些错误由设备驱动程序检测出来并处理,若驱动程序无法处理,则将错误信息返回给设备无关I/O软件,再由设备无关I/O软件返回给用户进程,如写一个已被破坏的磁盘扇区;打印机缺纸;读一个已关闭的设备等。
55
设备无关I/O软件层 打开与关闭文件 逻辑块大小处理
对设备或文件进行打开或关闭等I/O函数所对应的系统调用,并不涉及具体的I/O操作,只要直接对主存中的一些数据结构进行修改即可,这部分工作也由设备无关软件来处理。 逻辑块大小处理 为了为所有的块设备和所有的字符设备分别提供一个统一的抽象视图,以隐藏不同块设备或不同字符设备之间的差异,与设备无关的I/O软件为所有块设备或所有字符设备设置统一的逻辑块大小。 对于块设备,不管磁盘扇区和光盘扇区有多大,所有逻辑数据块的大小相同,这样,高层I/O软件就只需处理简化的抽象设备,从而在高层软件中简化了数据定位等处理 。
56
设备驱动程序 每个外设具体的I/O操作需通过执行设备驱动程序来完成
外设种类繁多、其控制接口不一,导致不同外设的设备驱动程序千差万别,因而设备驱动程序与设备相关 每个外设或每类外设都有一个设备控制器,其中包含各种I/O端口。 CPU通过执行设备驱动程序中的I/O指令访问个各种I/O端口 设备所采用的I/O控制方式不同,驱动程序的实现方式也不同 程序直接控制:驱动程序完成用户程序的I/O请求后才结束。这种情况下,用户进程在I/O过程中不会被阻塞,内核空间的I/O软件一直代表用户进程在内核态进行I/O处理 。(干等!) 中断控制:驱动程序启动第一次I/O操作后,将调出其他进程执行,而当前用户进程被阻塞。在CPU执行其他进程的同时,外设进行I/O操作,此时,CPU和外设并行工作。外设完成I/O时,向CPU发中断请求,然后CPU调出相应中断服务程序执行。在中断服务程序中再次启动I/O操作。 DMA控制:驱动程序对DMA控制器初始化后,便发送“启动DMA传送”命令,外设开始进行I/O操作并在外设和主存间传送数据。同时CPU执行处理器调度程序,转其他进程执行,当前用户进程被阻塞。DMA控制器完成所有I/O任务后,向CPU发送一个“DMA完成”中断请求信号。
57
中断服务程序 中断控制和DMA控制两种方式下都需进行中断处理
中断控制方式:中断服务程序主要进行从数缓器取数或写数据到数缓器,然后启动外设工作 DMA控制方式:中断服务程序进行数据校验等后处理工作 在内核I/O软件中用到的I/O指令、“开中断”和“关中断”等指令都是特权指令,只能在操作系统内核程序中使用
58
本章小结 用户程序通常通过调用编程语言提供的库函数或操作系统提供的API函数来实现I/O操作
与设备无关的操作系统软件 设备驱动程序 中断服务程序 具体I/O操作通过设备驱动程序和中断服务程序控制I/O硬件来实现 设备驱动程序的实现主要取决于具体的I/O控制方式: 程序查询方式、中断方式、DMA方式
Similar presentations