操作系统的设备管理功能。
设备
基本概念
- 根据数据组织方式分为块设备与字符设备
- I/O驱动软件层次分为:中断处理程序,设备驱动程序,I/O原语,用户软件
中断
内部中断:计算机本身在工作过程中出现的需要紧急处理的事务,如出错中断、程序中断,多采用程序陷入的方式
外部中断:主要来自I/O设备
I/O中断,时钟中断,系统请求中断,报警中断,程序错误中断,机器错误中断
中断机构处理外设的I/O中断,陷入机构处理
响应
响应中断后,根据中断源找到对应中断处理程序入口地址
中断矢量结构:一类地址指针的集合。中断发生后在固定位置找到指针,之后获得对应程序入口地址
核心态:进行中断处理,返回,继续执行核心态程序(核心态运行中断处理程序,而核心态返回核心态,不发生进程的切换调度)
用户态:中断处理之后,检查标志runrun是否设置(进程强迫调度标志是否置位,即是否有优先级更高的进程产生)
驱动程序接口
时钟
- 系统时钟:硬件时钟,晶片震动产生的方波,控制处理器执行指令速率
- 日历时钟:产生精确的时间计数
- 实时时钟:每秒提供若干脉冲,每个脉冲产生一个时钟中断
设备管理数据结构
- 设备控制表
- 抽象的I/O操作将用户与复杂的I/O设备操作隔离
- 记录每一个抽象设备描述、对应的实际设备地址和设备驱动程序,进程的设备调用通过控制表映射到物理设备
- 每个表项对应一个设备,表中包含若干I/O系统调用指针
- 设备开关表
- 针对设备的物理特性,设置一套子程序,如打开、关闭、启动
- 存放以上程序入口地址的数据结构——设备开关。各类设备开关构成设备开关表
Unix的设备管理
磁盘调度
- 先来先服务
- 最短寻道法:磁头移到远处为另外的请求服务之前,先将接近当前磁头位置的所有请求服务完
- 扫描法:磁头从磁盘的一端出发,遇到所需的磁道时服务,直到磁盘的另一端,从一端扫到另一端;或者到达另一端之后就立刻返回到磁盘的开头,返回过程中不进行服务(C-Scan)
块设备管理
缓冲控制块 buf
- 缓冲区由存放数据的结构和控制块组成
- 自由缓存队列控制块
块设备表 iobuf,为管理块设备而设置
块设备开关表
缓冲队列
自由buf队列
- 不针对一个具体设备,供任何设备使用
- FIFO管理
- 双向链表结构
设备缓冲区队列
与具体设备相关联,连接所有使用过的或正在使用的缓冲区
一个缓冲区被分配后,对应buf进入设备buf队列,并一直保留在该设备buf队列,除非其他设备也要使用
空设备队列(NODEV)
- 系统需要缓存,但不与特定的设备相关
- 进程执行一个目标程序的开始阶段,用缓存存放传给该程序的参数;缓存存放文件系统的资源管理块
- 系统初启时,所有空闲缓冲区的buf在自由buf队列和NODEV队列
设备I/O请求队列
每一个块设备都有一个队列
单向链接
需要I/O时,生成I/O请求块,并挂在对应设备的请求队列尾。同时位于该设备buf队列中。设备空闲时,从队首(或根据优先级)取出一个请求块,依据请求进行I/O操作
管理算法
- 请求分配
- 从自由buf队列取出一个空闲buf,并从原设备buf队列取出,插入申请该缓冲区的设备buf队列
- 读写完成
- 立刻释放缓冲区,加入自由buf队列尾
- 读:将字符块从设备读入缓冲区并传入指定内存区
- 写:指定内存区信息传入缓冲区,写入指定物理设备。若一次写操作没有写满缓冲区,则延迟写——放到自由buf队列尾,并留在设备buf队列。若移到队首,不应当做一般的buf,而是提出I/O请求,将数据写到块设备。因此将它抽离自由buf队列,只留在设备buf队列。写操作结束后,清空延迟写的flag,挪到自由buf队尾并留在原设备buf队列中
- 请求分配
读盘块
- 基本读入:
bread()
- 预读:检查第一块在不在缓冲区,如果不在则读,如果第二块也不在,则异步读。第一块读完后返回。第二块读完后产生I/O中断,进程直接取走第二块的数据。若第二块在缓冲区,则检查第二块是否在缓冲区,之后类似
- 基本读入:
文件系统
系统调用与标准子例程
系统调用
文件的创建、打开、关闭和取消
文件创建
creat
:mode
以二进制的位值为该文件设置的存储控制权限
返回打开文件标识数
- 只能用于写
1
2
3fd = creat(pathname, mode);
int fd, mode;
char *pathname;文件打开
open
:flags
为本次打开后对文件做的操作
1
2
3fd = open(pathname, flags);
int fd, flags;
char *pathname;文件关闭
close
文件链接
link
与unlink
为名字
name1
的文件再起一个新的文件名1
link("name1", "name2")
取消文件的一个文件名
1
2unlink(pathname)
char *pathname
文件读写
write
与read
1
2
3
4
5n = read(fd, buf, nbytes);
n = write(fd, buf, nbytes);
int fd, n; //n:实际读出或写入的字节数
unsigned nbytes; //要读写的字节数
char *buf //要写入或读出文件的目标地址或源地址更改读写位置
lseek
1
2
3lseek(fd, offset, whence);
int fd, whence; //whence:为0则参考点为文件起始位置,为1时为当前读写位置,为2时为文件尾部
long offset; //相对于whence位置的字节偏移值
创建任何类型文件
mknod
:目录、有名管道、特别文件只能用mknod
创建其他调用
- 改文件权限
chmod
- 设置文件屏蔽字
unmask
- 创建管道
pipe
- 改文件权限
标准子例程
比函数更为底层,但不是系统调用
通过一个
FILE
类型结构建立与打开文件的联系,组织方式成为流流文件打开与关闭:
fopen
与fclose
1
2
3
4fp = fopen(pathname, type)
fclose(fp)
FILE *fp;
char *pathname, *type;1
2
3
4
5
6
7
8
9
10
* 流文件的读写
```c
n = fread(buf, size, nitems, fp);
n = fwrite(buf, size, nitems, fp);
int n, size, nitems;
char *buf;
FILE *fp;
//将nitems个大小为size的数据对象读入buf所指ide存储区/从buf所指的存储区写道fp指定的输出流文件,n给出成功读取或写入的数据对象个数- 需要多次调用才需要一次实际的I/O操作,减少了从用户态到内核带、从内核态到用户态的开销
- 需要多次调用才需要一次实际的I/O操作,减少了从用户态到内核带、从内核态到用户态的开销
行的输入
fgets
与输出fputs
单字符I/O操作
fgetc
与fputc
获取流文件读写位置
ftell
与调整流文件读写位置fseek
Unix文件系统
索引节点:只将文件名从文件控制块中抽出,其余控制信息构成文件索引节点
dinode
(磁盘I节点)- 结构体中的属性有字符数组,记录文件盘块号
- 所有I节点放在磁盘的I节点区
索引结构:多级索引
打开文件结构
- 内存索引节点:打开一个文件时要读入磁盘I节点,在内存保存节点的副本
- 打开文件控制块:存放打开文件的动态信息,为文件的每次打开分配一个表项
- 进程打开文件表:是PCB扩充控制块的一个元素
u_ofile
,为指针数组。- 进程打开文件时,按下标序号使用该数组的某一个空闲项,填入打开文件控制块的地址,打开文件描述字即为该空闲项的下标
- 进程打开一个文件时,分配一个空闲内存inode(内存索引节点),将磁盘I节点主要内容复制过去。之后分配一个打开文件控制块,是其中的指针
f_uinode
指向内存inode
,最后在U_ofile
找一个空闲项,记下打开文件控制块的地址,以数组的索引值作为打开文件描述字返回给用户
管道与管道通信
- 只有与生成管道文件的进程同属一簇的进程才能利用pipe
- 有名管道有目录项,能在文件系统长久存在,有访问权限的用户都可打开它
- 创建管道时
- 分配一个磁盘I节点和内存I节点
- 分配用于读打开和写打开的打开文件控制块,指向同一个内存I节点
- 分配两个文件描述字,一个指向读打开,一个指向写打开
- fd[0]为读端文件描述字
- fd[1]为写端文件描述字