操作系统(下)

文件系统

1.文件的基本结构

Linux给每一个文件分配两个数据结构,文件本身存放在数据块区中。

  • 索引节点(index node):⽤来记录⽂件的元信息,比如inode 编号、文件大小、访问权限、创建时间、修改时间、数据在磁盘的位置等。每一个索引节点对应一个文件。存放在硬盘中,需要占一定的空间。
  • 目录项:⽤来记录文件的名字、索引节点指针以及与其他目录项的层级关联关系,多个目录项关联起来,就会形成目录结构,但它与索引节点不同的是,目录项是由内核维护的⼀个数据结构,不存放于磁盘,而是缓存在内存。所以每一个文件都会挂载到一个目录项中。与目录不同,目录是一个特殊的文件,里面可以包含文件或者子目录。
  • 数据块:磁盘读写的最小单位是扇区,但是扇区太小(512B),为了增加效率。把多个扇区组成一个逻辑块(数据块)。

2.什么是虚拟文件系统(VFS)?

 操作系统中有很多的文件系统,需要定义一套接口来限制。文件系统的分类:

  • 磁盘的文件系统:它是直接把数据存储在磁盘中,比如 Ext 2/3/4、XFS 等都是这类文件系统。
  • 内存的文件系统:这类文件系统的数据不是存储在硬盘的,而是占用内存空间,我们经常用到的 /proc 和 /sys 文件系统都属于这⼀类,读写这类文件,实际上是读写内核中相关的数据。
  • 网络络的文件系统:用来访问其他计算机主机数据的文件系统,比如 NFS、SMB 等等。 

 3.文件的存储

  • 连续空间存放方式:需要事先知道文件的"起始块位置和长度",用来计算文件的大小, 给文件安排一个大小合适的位置。 缺点:磁盘碎片,文件长度不易扩展
  • 非连续空间存放方式:
    • 链表方式:
      • 隐式链表:实现的方式是文件头要包含「第⼀块」和「最后⼀块」的位置, 并且每个数据块里面留出⼀个指针空间,⽤来存放下⼀个数据块的位置。缺点:只能顺序访问,链表指针消耗了一定的空间

      • 显式链表:将所有的链表的指针放在一个"连接表"中,整个磁盘只有一张。 通过连接表进行访问。 缺点:整个连接表加载到内存中,太大。
    • 索引方式: 每一个文件定义一个"索引数据块",放在磁盘中。文件头有一个指针指向这个索引数据块。这个索引数据块中存放数据的指针列表。跟显式链表相似, 但是是每一个文件都有一个列表,不会像显式链表一样放在一个列表上。 缺点:无法解决单个文件太大,索引数据块太大的问题。

    • 链式索引方式: 把索引方式中的"索引数据块"进行分割,以隐式链表的形式进行连接。

    • 多级索引块: 搞一个索引数据块的数据库,数据库里是指针,指向多个索引数据块。

 

 4.空闲空间管理

  •  空闲表法:维持一张表,表中记录空闲空间"初始位置和长度"。记录连续的空闲空间

 

 

  • 空闲链表法:维持一个链表,每一个空闲空闲指向下一个空闲空间。需要的时候取下来, 不需要的时候放进去。
    • 缺点:以上俩种方式都需要单独的空间存放空闲表/链表。 如果是大型文件系统,占用的空间就大,也不好查询。

 

 

  • 位图法:以二进制01100001111表示所有的空间,1表示已分配,0表示空闲。Linux中使用。 一个位图只能表示一个块组,不能表示整个系统,所以系统中需要好多块组。

5.文件系统的结构

文件系统由一个一个"块组"构成

    •  超级块,包含的是文件系统的重要信息,比如 inode 总个数、块总个数、每个块组的 inode 个数、每 个块组的块个数等等。
    • 块组描述符,包含文件系统中各个块组的状态,比如块组中空闲块和 inode 的数目等,每个块组都包 含了文件系统中「所有块组的组描述符信息」。
    • 数据位图和 inode 位图, 用于表示对应的数据块或 inode 是空闲的,还是被使用中。
    • inode 列表,包含了块组中所有的 inode,inode ⽤于保存⽂件系统中与各个文件和目录相关的所有元 数据。
    • 数据块,包含文件的有用数据。

 6.目录的存储

  • 普通文件的块里面保存的是文件数据,而目录文件的块里面保存的是目录里面⼀项⼀项的文件信息。
  • 目录的保存格式是把目录中的文件存放在"哈希表"中,哈希表的值对应一个文件。

 7.软链接和硬链接

  • 硬链接:多个目录项的索引指向一个文件,不可跨文件系统。 比如:f1指向f2,其实f1和f2都指向同一个文件,删除f1或者f2中的一个, 都可以正常访问。相当于备份。
  • 软链接:相当于快捷方式,可以跨文件系统。比如f1指向f2,f2删除了,f1就无法访问了。                                       

 8.文件I/O

  • 缓存与非缓存I/O:根据是否利用"标准库缓冲(应用缓存)"
  • 直接与非直接I/O:根据是否利⽤"操作系统的缓存"
  • 同步I/O: 主要两个步骤:「内核数据准备好」和「数据从内核态拷贝到用户态
    • 阻塞I/O:两个步骤都要等待.(BIO)

    • 非阻塞I/O:第一个步骤无序等待,但是会一直询问内核是否准备好。(BIO的多线程版)

    • 多路复用I/O:在非阻塞的前提下优化,不会一直询问。但是多一个select期。(NIO)

  • 异步I/O: 两个步骤都无需等待,询问完交给内核自动完成。(AIO)

 9.零拷贝

前提知识:

  • IO读写的模型

  • 网络通信:在Linux中,万事万物皆文件。所谓的网络通信其实就是将数据写入文件,通过网络发送,接收方通过网络读文件。本质还是文件的读写
  • 在操作系统中,网络io的读写通过socket完成。网络传过来的数据先放到网卡中,然后通过socket对象进行io操作,将数据读取到进程中,操作完将数据写入网卡中。

零拷贝:

  • 从上图可以看到,数据会从内核缓存区进行拷贝到用户缓存区中,这只是对于一次读的操作。如果在网络编程中,进行文件的传输,就需要进行下面4次拷贝,4次切换。无疑会消耗CPU的性能。

  • 所以提出零拷贝(主要是想减少内核缓冲区和用户缓存区的拷贝)进行优化。两种方式
    • mmap:将内核缓冲区里的数据「映射」到用户空间,这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。但是还需要进行内核缓存区到socket缓存区的拷贝,就进行3次拷贝,4次切换

  • sendFile:
    • 在 Linux 内核版本 2.1 中,提供了一个专门发送文件的系统调用函数 sendfile(),它只是把用户缓存区去掉了,也没有真正实现零拷贝。
    • Linux 内核2.4进行升级,成为真正的零拷贝。直接将内核缓存区文件,放到网卡上。2次拷贝,2次切换。

    • 在nio中,只需要调用filechannel.transferTo()就可以实现零拷贝。Linux系统中可以一次完成,windows系统每次只能传输8M文件,需要自己多次调用。

10.提高文件系统性能的方式

  • 高速缓存:高速缓存指的是一系列的块,它们在逻辑上属于磁盘,但实际上基于性能的考虑被保存在内存中。比如上图中的PageCache区。
  • 块提前读:在需要用到块之前试图提前将其写入高速缓存从而提高命中率。许多文件都是顺序读取,比如文件系统在某个文件中生成块 k,文件系统执行相关操作并且在完成之后,会检查高速缓存,以便确定块 k + 1 是否已经在高速缓存。如果不在,文件系统会为 k + 1 安排一个预读取,因为文件希望在用到该块的时候能够直接从高速缓存中读取。
  • 减少磁盘臂运动:把有可能顺序访问的块放在一起,当然最好是在同一个柱面上,从而减少磁盘臂的移动次数。

  • 磁盘碎片整理

 11.RAID 的不同级别

    •  RAID 称为 磁盘冗余阵列,简称 磁盘阵列。利用虚拟化技术把多个硬盘结合在一起,成为一个或多个磁盘阵列组,目的是提升性能或数据冗余。

RAID 有不同的级别

  • RAID 0 - 无容错的条带化磁盘阵列
  • RAID 1 - 镜像和双工
  • RAID 2 - 内存式纠错码
  • RAID 3 - 比特交错奇偶校验
  • RAID 4 - 块交错奇偶校验
  • RAID 5 - 块交错分布式奇偶校验
  • RAID 6 - P + Q冗余

IO设备管理

 1.设备控制器

  • 我们的电脑设备可以接⾮常多的输⼊输出设备。为了屏蔽设备之间的差异,每个设备都有⼀个叫设备控制器的组件,比如硬盘有硬盘 控制器、显示器有视频控制器等。
  • 设备控制器里有芯片,它可执行自己的逻辑,也有自己的寄存器(3种),用来与 CPU 进行通信。

      • 数据寄存器:CPU 向 I/O 设备写⼊需要传输的数据,比如要打印的内容是「Hello」,CPU 就要先发 送⼀个 H 字符给到对应的 I/O 设备。
      • 命令寄存器:CPU 发送⼀个命令,告诉 I/O 设备,要进⾏输⼊/输出操作,于是就会交给 I/O 设备去工作,任务完成后,会把状态寄存器里面的状态标记为完成。
      • 状态寄存器:目的是告诉 CPU ,现在已经在工作或工作已经完成,如果已经在工作状态,CPU 再发 送数据或者命令过来,都是没有用的,直到前面的工作已经完成,状态寄存标记成已完成,CPU 才能 发送下⼀个字符和命令。
  • 那 CPU 是如何与设备的控制寄存器和数据缓冲区进行通信的?存在两个方法:
    • 端口I/O:每个控制寄存器被分配⼀个 I/O 端⼝,可以通过特殊的汇编指令操作这些寄存器,比如 in/out 类似的指令。
    • 内存映射 I/O:将所有控制寄存器映射到内存空间中,这样就可以像读写内存⼀样读写数据缓冲区。
  • 设备控制器主要分为两种
    • 块设备:把数据存储在固定大小的块中,每个块有自己的地址,硬盘、USB 是常见的块设备。
    • 字符设备:以字符为单位发送或接收⼀个字符流,字符设备是不可寻址的,也没有任何寻道操作,鼠标是常见的字符设备。

 2. 什么是设备驱动程序

  •  在计算机中,设备驱动程序是一种计算机程序,它能够控制或者操作连接到计算机的特定设备。驱动程序提供了与硬件进行交互的软件接口,使操作系统和其他计算机程序能够访问特定设备,不用需要了解其硬件的具体构造。
  • 我们知道,设备控制器完成读写任务之后,会向操作系统发起一个中断。操作系统就会执行中断处理程序,这个程序就是该程序。

 3.中断处理过程(简化版)

 

  1.  在 I/O 时,设备控制器如果已经准备好数据,则会通过中断控制器向 CPU 发送中断请求。
  2. 保护被中断进程的 CPU 上下文。
  3. 转入相应的设备中断处理函数。
  4. 进行中断处理。
  5. 恢复被中断进程的上下文。

 4. I/O控制方法(软件访问硬件的几种方式)

当 CPU 给设备发送了⼀个指令,让设备控制器去读设备的数据,它读完的时候,要怎么通知 CPU 呢?通知CPU就相当于程序驱动程序执行完毕,通知CPU,就是软件访问硬件。

  • 轮询等待:CPU ⼀直查寄存器的状态,直到状态标记为完成。缺点:太消耗CPU性能
  • 中断驱动:设置一个中断控制器,当设备完成任务后触发中断到中断控制器,中断控制器就通知 CPU,⼀个中断产生了,CPU 需要停下当前手里的事情来处理中断。
    • 缺点:就算使用了软中断和硬中断,中断的方式对于频繁读写数据的磁盘,并不友好,这样 CPU 容易经常被打断,会占用 CPU ⼤量的时间。
  • DMA:内存直接访问,CPU 当要读取磁盘数据的时候,只需给 DMA 控制器发送指令,然后返回去做其他事情;DMA将磁盘数据拷贝到内存后,DMA 控制机器通过中断的方式,告诉 CPU 数据已经准备好了,可以从内存读数据 了。仅仅在传送开始和结束时需要 CPU 干预。

    • 好处:所传送的数据从设备直接送入内存,或者从内存直接输出到设备上,很少与CPU进行交互,进一步提高了 CPU 与 I/O 设备的并行操作程度。
    • DMA 方式的线路简单、价格低廉,适合高速设备与主存之间的成批数据传送,小型、微型机中的快速设备均采用这种方式,但其功能较差,不能满足复杂的 I/O 要求。
  • 通道控制方法:通道,独立于 CPU 的专门负责输入输出控制的处理机,它控制设备与内存直接进行数据交换。有自己的通道指令,这些指令由 CPU 启动,并在操作结束时向 CPU 发出中断信号。

 5.键盘敲入字母时,期间发生了什么?

  • 那当用户输入了键盘字符,键盘控制器就会产生扫描码数据,并将其缓冲在键盘控制器的寄存器中,紧接着键盘控制器通过总线给 CPU 发送中断请求。
  • CPU 收到中断请求后,操作系统会保存被中断进程的 CPU 上下⽂,然后调用键盘的中断处理程序。
  • 键盘中断处理函数的功能就是从键盘控制器的寄存器的缓冲区读取扫描码,再根据扫描码找到⽤户在键盘输入的字符,如果输入的字符是显示字符,那就会把扫描码翻译成对应显示字符的 ASCII 码,比如用户在键盘输入的是字母 A,是显示字符,于是就会 把扫描码翻译成 A 字符的 ASCII 码。
  • 得到了显示字符的 ASCII 码后,就会把 ASCII 码放到「读缓冲区队列」,接下来就是要把显示字符显示屏幕了。显示设备的驱动程序会定时从「读缓冲区队列」读取数据放到「写缓冲区队列」,最后把「写缓冲区队列」的数据⼀个⼀个写入到显示设备的控制器的寄存器中的数据缓冲区,最后将这些数据显示在屏幕里。
  • 显示出结果后,恢复被中断进程的上下文。

 

调度算法

1.进程调度算法(6)

  • 先来先服务调度算法:每次从就绪队列选择最先进入队列的进程,然后⼀直运行,直到进程退出或被阻塞,才会继续从队列中选择第⼀个进程接着运行。
    • 缺点:不利于短作业。当⼀个长作业先运行了,那么后面的短作业等待的时间就会很长。

  • 最短作业优先调度算法:优先选择运行时间最短的进程来运行。
    • 缺点:不利于长作业。⼀个长作业在就绪队列等待运行,而这个就绪队列有非常多的短作业,那么就会使得长作业不断的往后推,周转时间变长,使长作业长期不会被运行。

  • 高响应比优先调度算法:权衡了长作业和短作业,每次进行进程调度时,先计算「响应比优先级」,然后把「响应比优先级」最高的进程投入运行。

 

 

  • 时间片轮转调度算法(RR调度算法):每个进程被分配⼀个时间段,称为时间片,即允许该进程在该时间段中运行。
    • 缺点:时间片太短,就会导致过多的上下文切换;时间片太长,又可能引起对短作业进程的响应时间变长。

 

  • 最高优先级调度算法:从就绪队列中选择最高优先级的进程进行运行。
    • 缺点:如果高优先权的太多,可能会导致低优先级的进程永远不会运行。
  • 多级反馈队列调度算法:「时间片轮转算法」和「最高优先级算法」的 综合和发展。
    • 设置了多个队列,赋予每个队列不同的优先级,每个队列优先级从高到低,同时优先级越高时间片越短;
    • 新的进程会被放入到第⼀级队列的末尾,按先来先服务的原则排队等待被调度,如果在第⼀级队列规 定的时间片没运行完成,则将其转入到第⼆级队列的末尾,以此类推,直至完成;
    • 当较高优先级的队列为空,才调度较低优先级的队列中的进程运行。如果进程运行时,有新进程进入 较高优先级的队列,则停止当前运行的进程并将其移入到原队列末尾,接着让较高优先级的进程运行;

 2.内存页面置换算法(5)

当 CPU 访问的页面不在物理内存时,便会产⽣⼀个缺页中断异常,请求操作系统将所缺页调入到物理内存。就是通过一定的算法把物理内存中不用的页换出到磁盘中,从磁盘中换入需要的页,放在空闲的位置。

  • 最佳页面置换算法(未来):置换在「未来」最长时间不访问的页面。理想状态,没法实现。
  • 先进先出算法:选择在内存驻留时间很长的页面进行置换。效率低   
  • 最近最久未使用算法LRU(历史):选择最长时间没有被访问的页面进行置换。也就是说,该算法假设已经很久没有使⽤的页面很有可能在未来较长的⼀段时间内仍然不会被使用。可实现,开销大,需要维护一个链表。
  • 时钟页面置换算法:上面2个的结合,遇到访问位为1置换为0,遇到0进行置换。跟 LRU 近似,又是对 FIFO 的⼀种改进。

  • 最不常用算法:选择「访问次数」最少的那个页面,并将其淘汰
    • 缺点:没考虑时间的问题,比如有些页面在过去时间里访问的频率很高, 但是现在已经没有访问了,而当前频繁访问的页面由于没有这些页面访问的次数高, 在发⽣缺页中断时,就会可能会误伤当前刚开始频繁访问,但访问次数还不高的页面。

 3.磁盘调度算法

在向磁盘读写数据的时候,寻道的时间是磁盘访问最耗时的部分,如果请求顺序优化的得当,必然可以节省⼀些不必要的寻道时间, 从而提高磁盘的访问性能, 减少磁盘臂运动。

  • 先来先服务算法:效率低

 

  • 最短寻道时间优先:根据"磁头",找离磁头最近的磁道。可能产生"饥饿现象",主要原因是磁头在一个小区域内来回移动。以98,183,37,122,14,124,65,67为例。
    • 饥饿现象 :如果后续来的请求都是小于 183 磁道的,那么 183 磁道可能永远不会被响应,183就会"饥饿",最后可能导致该位置不可用。

 

  • 扫描算法:磁头在⼀个方向上移动,访问所有未完成的请求,直到磁头到达该方向上的最后的磁道,才调换方向。解决饥饿现象,但是中间部分响应频率太高。

  • 循环扫描算法:只有磁头朝某个特定⽅向移动时,才处理磁道访问请求,而返回时直接快速移动至最靠边缘的磁道,也就是复位磁头,这个过程是很快的,并且返回中途不处理任何请求

    • 以上俩种扫描算法有个缺点, 磁头在最初端/最末端才进行调换方向。
  • LOOK 与 C-LOOK算法: 对上面两种算法进行优化,优化的思路就是磁头在移动到「最远的请求」位置,然后立即反向移动。
    • Look是对Scan的优化,C-Look是对C-Scan的优化。

 

posted @ 2021-11-25 22:28  小猴子_X  阅读(278)  评论(0编辑  收藏  举报