操作系统 (3)设备与文件系统

操作系统的设备管理功能。

设备

基本概念

  • 根据数据组织方式分为块设备与字符设备
  • I/O驱动软件层次分为:中断处理程序,设备驱动程序,I/O原语,用户软件

中断

  • 内部中断:计算机本身在工作过程中出现的需要紧急处理的事务,如出错中断、程序中断,多采用程序陷入的方式

  • 外部中断:主要来自I/O设备

  • I/O中断,时钟中断,系统请求中断,报警中断,程序错误中断,机器错误中断

  • 中断机构处理外设的I/O中断,陷入机构处理

  • 响应

    • 响应中断后,根据中断源找到对应中断处理程序入口地址

    • 中断矢量结构:一类地址指针的集合。中断发生后在固定位置找到指针,之后获得对应程序入口地址

    • 核心态:进行中断处理,返回,继续执行核心态程序(核心态运行中断处理程序,而核心态返回核心态,不发生进程的切换调度)

      用户态:中断处理之后,检查标志runrun是否设置(进程强迫调度标志是否置位,即是否有优先级更高的进程产生)

  • 驱动程序接口
    image-20201104221511040

  • 时钟

    • 系统时钟:硬件时钟,晶片震动产生的方波,控制处理器执行指令速率
    • 日历时钟:产生精确的时间计数
    • 实时时钟:每秒提供若干脉冲,每个脉冲产生一个时钟中断

设备管理数据结构

  • 设备控制表
    • 抽象的I/O操作将用户与复杂的I/O设备操作隔离
    • 记录每一个抽象设备描述、对应的实际设备地址和设备驱动程序,进程的设备调用通过控制表映射到物理设备
    • 每个表项对应一个设备,表中包含若干I/O系统调用指针
      image-20201104222043834
  • 设备开关表
    • 针对设备的物理特性,设置一套子程序,如打开、关闭、启动
    • 存放以上程序入口地址的数据结构——设备开关。各类设备开关构成设备开关表

Unix的设备管理

  • 磁盘调度

    • 先来先服务
    • 最短寻道法:磁头移到远处为另外的请求服务之前,先将接近当前磁头位置的所有请求服务完
    • 扫描法:磁头从磁盘的一端出发,遇到所需的磁道时服务,直到磁盘的另一端,从一端扫到另一端;或者到达另一端之后就立刻返回到磁盘的开头,返回过程中不进行服务(C-Scan)
  • 块设备管理

    • 缓冲控制块 buf

      • 缓冲区由存放数据的结构和控制块组成
      • 自由缓存队列控制块
    • 块设备表 iobuf,为管理块设备而设置

    • 块设备开关表

    • 缓冲队列

      • 自由buf队列

        • 不针对一个具体设备,供任何设备使用
        • FIFO管理
        • 双向链表结构
          image-20201104223053175
      • 设备缓冲区队列

        • 与具体设备相关联,连接所有使用过的或正在使用的缓冲区

        • 一个缓冲区被分配后,对应buf进入设备buf队列,并一直保留在该设备buf队列,除非其他设备也要使用
          image-20201104223317610

      • 空设备队列(NODEV)

        • 系统需要缓存,但不与特定的设备相关
        • 进程执行一个目标程序的开始阶段,用缓存存放传给该程序的参数;缓存存放文件系统的资源管理块
        • 系统初启时,所有空闲缓冲区的buf在自由buf队列和NODEV队列
      • 设备I/O请求队列

        • 每一个块设备都有一个队列

        • 单向链接

        • 需要I/O时,生成I/O请求块,并挂在对应设备的请求队列尾。同时位于该设备buf队列中。设备空闲时,从队首(或根据优先级)取出一个请求块,依据请求进行I/O操作
          image-20201104223852751

    • 管理算法

      • 请求分配
        • 从自由buf队列取出一个空闲buf,并从原设备buf队列取出,插入申请该缓冲区的设备buf队列
      • 读写完成
        • 立刻释放缓冲区,加入自由buf队列尾
        • 读:将字符块从设备读入缓冲区并传入指定内存区
        • 写:指定内存区信息传入缓冲区,写入指定物理设备。若一次写操作没有写满缓冲区,则延迟写——放到自由buf队列尾,并留在设备buf队列。若移到队首,不应当做一般的buf,而是提出I/O请求,将数据写到块设备。因此将它抽离自由buf队列,只留在设备buf队列。写操作结束后,清空延迟写的flag,挪到自由buf队尾并留在原设备buf队列中
    • 读盘块

      • 基本读入:bread()
        image-20201104224406936
      • 预读:检查第一块在不在缓冲区,如果不在则读,如果第二块也不在,则异步读。第一块读完后返回。第二块读完后产生I/O中断,进程直接取走第二块的数据。若第二块在缓冲区,则检查第二块是否在缓冲区,之后类似

文件系统

系统调用与标准子例程

系统调用

  • 文件的创建、打开、关闭和取消

    • 文件创建creat

      • mode以二进制的位值为该文件设置的存储控制权限
    • 返回打开文件标识数

      • 只能用于写
      1
      2
      3
      fd = creat(pathname, mode);
      int fd, mode;
      char *pathname;
    • 文件打开open

      • flags为本次打开后对文件做的操作
      1
      2
      3
      fd = open(pathname, flags);
      int fd, flags;
      char *pathname;
    • 文件关闭close

    • 文件链接linkunlink

      • 为名字name1的文件再起一个新的文件名

        1
        link("name1", "name2")
      • 取消文件的一个文件名

        1
        2
        unlink(pathname)
        char *pathname
  • 文件读写writeread

    1
    2
    3
    4
    5
    n = read(fd, buf, nbytes);
    n = write(fd, buf, nbytes);
    int fd, n; //n:实际读出或写入的字节数
    unsigned nbytes; //要读写的字节数
    char *buf //要写入或读出文件的目标地址或源地址
  • 更改读写位置lseek

    1
    2
    3
    lseek(fd, offset, whence);
    int fd, whence; //whence:为0则参考点为文件起始位置,为1时为当前读写位置,为2时为文件尾部
    long offset; //相对于whence位置的字节偏移值
  • 创建任何类型文件mknod:目录、有名管道、特别文件只能用mknod创建

  • 其他调用

    • 改文件权限chmod
    • 设置文件屏蔽字unmask
    • 创建管道pipe

标准子例程

  • 比函数更为底层,但不是系统调用

  • 通过一个FILE类型结构建立与打开文件的联系,组织方式成为流

  • 流文件打开与关闭:fopenfclose

    1
    2
    3
    4
    fp = 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操作,减少了从用户态到内核带、从内核态到用户态的开销
      image-20201104231639037
  • 行的输入fgets与输出fputs

  • 单字符I/O操作fgetcfputc

  • 获取流文件读写位置ftell与调整流文件读写位置fseek

Unix文件系统

  • 索引节点:只将文件名从文件控制块中抽出,其余控制信息构成文件索引节点dinode(磁盘I节点)

    • 结构体中的属性有字符数组,记录文件盘块号
    • 所有I节点放在磁盘的I节点区
  • 索引结构:多级索引
    image-20201104232236548

  • 打开文件结构

    • 内存索引节点:打开一个文件时要读入磁盘I节点,在内存保存节点的副本
    • 打开文件控制块:存放打开文件的动态信息,为文件的每次打开分配一个表项
    • 进程打开文件表:是PCB扩充控制块的一个元素u_ofile,为指针数组。
      • 进程打开文件时,按下标序号使用该数组的某一个空闲项,填入打开文件控制块的地址,打开文件描述字即为该空闲项的下标
      • 进程打开一个文件时,分配一个空闲内存inode(内存索引节点),将磁盘I节点主要内容复制过去。之后分配一个打开文件控制块,是其中的指针f_uinode指向内存inode,最后在U_ofile找一个空闲项,记下打开文件控制块的地址,以数组的索引值作为打开文件描述字返回给用户
        image-20201104232900670

管道与管道通信

  • 只有与生成管道文件的进程同属一簇的进程才能利用pipe
  • 有名管道有目录项,能在文件系统长久存在,有访问权限的用户都可打开它
  • 创建管道时
    • 分配一个磁盘I节点和内存I节点
    • 分配用于读打开和写打开的打开文件控制块,指向同一个内存I节点
    • 分配两个文件描述字,一个指向读打开,一个指向写打开
    • fd[0]为读端文件描述字
    • fd[1]为写端文件描述字

image-20201104233458471