操作系统学习
基础概念
操作系统是什么
是一种软件,提供给用户和其他上层软件方便的接口和环境!!!
系统资源的管理者
- 提供处理机(CPU)、存储器、文件、设备管理
打开QQ -- 相关数据放入内存 -- 进程被CPU处理 -- 视频通话(摄像头设备)
向上层提供方便易用的服务
- GUI(图形 用户 接口) 比如window下拖拽到回收站删除文件
- 联机/脱机命令接口 比如cmd, 联机就是cd ls等,脱机就是.bat 也叫批处理
- 程序接口 比如printf底层就是调用了操作系统的显示接口
最接近硬件的一层软件
- 硬件只能听懂010101,操作系统把这些硬件功能封装成简单易用的服务
- 没有任何软件支持就叫裸机,裸机安装操作系统就更方便使用了!
操作系统特征
并发 共享(二者相辅相成) 虚拟 (有并发才有意义虚拟) 异步(没有并发就不会异步)
并发
- 指两个事件在同一个tick内发生,但实际上是交替(交替很快)发生的 如果实际上也是同时发生,那叫并行!!!!
- 单核CPU只能并发,多核CPU可以并行!!
共享
- 资源共享:指的是多个程序并发时同时使用某资源
- 分为互斥共享(不可同时用摄像头)、同时共享(同时访问硬盘文件 实际上一般也是交替)
虚拟(空分复用 时分复用)
- 把物理实体变为若干个逻辑上的对应物,比如一个程序需要放入内存并分配CPU才可执行,实际上电脑单核,但是用户感受 可以开多个应用,这就叫虚拟!!
异步
- 系统资源是有限的,这个资源被占用了,另一个进程就会被阻塞!!
- 只有并发才会可能导致异步!!!
操作系统的发展
- 程序员用纸带机输入输出(有孔为1 无孔为0)
- 单道批处理系统
- 多道批处理系统
- 分时操作系统
- 实时操作系统
程序
内核程序(Kernel) 应用程序(app)
-
理论上只需内核就可以使用一个操作系统
-
CPU可以分辨出哪些是特权指令 哪些是非特权指令
-
CPU两状态(通过一个状态寄存器) 内核态(可执行特权指令)和用户态(不可以)
也就是说,内核态到用户态,需要写入这个寄存器
-
刚开机CPU是内核态 若用户态时收到特权指令会中断并拒绝执行该程序后续命令,然后进入内核态执行中断程序,执行完后恢复用户态
-
并不是库函数都涉及系统调用,文件操作肯定涉及系统调用 取绝对值不涉及
中断
没有中断,就不会从用户态切回内核态,就不可能实现并发!!!
- 内中断 :与当前执行的指令有关(非法指令),中断信号来自CPU内部
例如:用户态特权指令、除数为0、应用程序请求内核服务(陷入/trap/访管指令) - 外中断 :与当前执行的指令无关,中断信号来自CPU外部
例如:时钟中断、I/O中断
操作系统体系结构
内核咋设计?
- 原语 哪怕遇到中断也会执行完再中断!!!
- 微内核只包括时钟管理、中断处理、原语。 大内核还包括进程、存储器、设备管理。 微内核比如windows 大内核比如linux unix
- 外核负责分配未经抽象的系统资源(一整块连续的磁盘空间、物理内存)
分层结构 模块化
- 模块化可以并行开发,只需要提前定义好函数名、参数、返回值
- 模块化一般可以动态扩充,因为彼此间独立
- 模块化调试困难
- 分层结构调试简单 但是更消耗资源,而且有互相依赖的不好分层
引导
开机后如何让操作系统运行起来
- 在磁盘上安装一个操作系统,如下图
- C盘一般是活动分区(也就是安装了操作系统的分区)分为引导记录PBR(负责找到启动管理器) 根目录 其他
- ROM存储BIOS(basci input output system) 主要包含ROM引导程序,作用是指示CPU把MBR读入RAM,MBR的磁盘引导程序又把PBR读入RAM,执行启动管理器
总结:CPU从特定地址取指令执行ROM引导(先硬件自检) --> 引导MBR写入RAM(作用是扫描分区表) --> 从活动分区读PBR到RAM --> 找到启动管理器
位于 C:/Windows/BOOT
虚拟机
将一台物理机器虚拟化为多台虚拟机器,实现一个电脑上跑好几个操作系统
进程 线程 协程
进程
进程之间是树形关系,并不是并列关系,也就是说有几个根进程,产生诸多子进程
进程概念
ctrl+shift+ESC 打开任务管理器 每一行都是一个进程
-
程序是一个静态可执行文件,比如一个qq程序打开三次,算三个进程(三个pid),进程是动态的
-
创建一个进程就会产生一个PCB(进程控制块 一个struct) 包含pid、uid、cpu使用时间使用率 磁盘写入读取情况、占用内存、使用的io设备、寄存器的值等信息
比如三个qq进程,他们的PCB 数据段各不相同,但是程序段相同
进程状态
进程的状态:运行 就绪 阻塞(三基本态) 创建 终止
-
正在创建(创建态) -> 创建完等待运行(就绪) -> 被CPU选中并执行(运行) -> 需要用的设备被占用(阻塞) -> 设备空闲(就绪) -> 运行态 -> 运行结束或遇到错误(终止)
-
组织方式(如何实现不通进程切换)可能是通过PCB指针切换,有执行指针、就绪队列指针、阻塞队列指针(可能有多个阻塞队列),也可能是通过多个索引表,指针指向索引表实现
进程控制
用原语(一气呵成 不会被中断)实现进程控制
-
为什么要原语,比如一个进程从阻塞变为就绪,至少两步:PCB的state=1 PCB从阻塞队列放入就绪队列。 如果不是一气呵成 那么会出现阻塞队列里的PCB state=1,关键数据信息不统一!!!
-
原语是如何实现的呢? 关中断--指令1--指令2--开中断,一旦关中断就表示不会被中断打断!
-
进程什么情况下创建呢? 创建原语、特定事件(登录后起另一个进程 系统服务起一个进程 进程请求创建子进程 外存转到内存需要新建进程)
-
进程什么情况下终止呢? 撤销原语 特定事件(执行完终止、异常终止、外部干扰)
-
还有阻塞原语、唤醒原语(必须成对使用,由什么阻塞由什么唤醒) 切换原语
进程通信(IPC)
不同进程的内存地址空间互相独立!!!为了安全考虑!!!
- 共享存储(保证不同进程的访问互斥!!否则会出现写入冲突等)
- 申请共享存储区,不同进程都可以访问这块内存!
例如linux下
int shm_open(...); //发起通信的进程A 申请共享存储区
void *mmap(...); //所有参与通信的进程,把共享存储区映射到自己的内存空间!!
- 申请共享数据结构
相当于一种特殊的全局变量,比如共享区只能放长度10的一个数组,限制多且速度慢!!
- 消息传递
进程之间的数据交换以格式化消息为单位,通过发送和接收原语实现!!!
- 格式化的消息:消息头 消息体
- 消息传递分为直接通信(通过pid) 间接通信(通过信箱)
直接通信也是通过操作系统内核区消息队列传递的,一方发送到内核,一方从内核接收!!
间接通信是通过发送原语发送到信箱,然后另一个进程从信箱读
- 管道通信
-
只可能是单向的,实际上就是在内存中开辟一个大小固定的缓冲区,是一个特殊的共享文件
-
和共享的区别是,共享没有限制,管道只能单向,一个写一个读,而且先入先出(只要管道不空就可以读 )
-
管道只能是半双工,想全双工只能再开一个管道,由操作系统实现互斥访问
-
管道满了之后,写进程被阻塞,直至读走了一个,管道空了之后,读进程被阻塞,直至写了一个
-
linux多写多读,但是要求读进程轮流,轮流呢,也就是多写一读。。。。。
线程
概念
线程是程序执行流(CPU调度)的最小单位!!! 进程是CPU分配系统资源的最小单位!!!
-
优势:切换进程的运行环境开销很大,但是一个进程不同线程开销不大
-
线程也有TID TCP 有就绪阻塞运行三态 几乎不拥有系统资源(在进程那) 统一进程的线程间通信甚至无需系统干预
实现方式
- 用户级线程 --早期,用户自己写线程库实现。。。。(包括创建销毁调度)
- 优点是无需切换CPU到内核态 开销小 效率高 缺点是并发度低(多核浪费),容易被阻塞!!!!
- 内核级线程
- 线程切换需要切换到核心态 优点是并发能力强 不怕某线程阻塞 缺点是开销大 成本高
- 线程库和内核级线程结合: 一个用户级线程对应一个内核级线程 还有 多对一(实际上就是用户级。。。) 多对多(用户级线程数大于内核级线程!!)
调度
按照某种规则决定先处理哪个任务!!
基本概念
-
高级调度(作业调度):作业指一个具体的任务,好几个作业,看下先写哪个作业,也可以理解为先启动哪个程序,从外存调度到内存,从无到创建!!
-
低级调度(进程调度/处理机调度):从就绪队列里选一个进程,把处理机分配给他
- 进程调度频率很高,一般几十毫秒一次,是最基本的调度
- 中级调度(内存调度):内存不足时把某些进程数据调出到外存,进入挂起状态(挂起队列),调度时回到内存,取消挂起变成就绪!!频率比高级调度高
调度方式
- 低级调度分为主动放弃(运行结束 异常 主动请求阻塞比如请求I/O)和被动放弃(时间片用完 被插队)
- 中断处理过程不能切换,内核临界区(如访问就绪队列 但是访问打印机可切换)、原语不可以切换
-
调度方式分为非抢占(即便有紧急任务也先执行完再给你,开销小但是无法及时响应紧急任务)和抢占式(适合分时操作系统 实时操作系统)
-
调度与切换的关系,调度指的是从就绪队列里挑一个进程运行,这个进程如果是刚刚暂停的进程就不用切换,如果是另一个进程就需要切换!!!!!
进程切换包括保存原来进程的各种数据、恢复新进程的各种数据
- 进程切换不要太循环,否则频繁保存恢复会浪费大量系统资源!!!
调度程序
-
调度程序就是执行调度过程的程序,需要制定调度的算法和时间(一个时间片)
-
什么时候会触发?
创建新进程、退出进程、进程阻塞、I/O中断,对于非抢占,只有阻塞和退出会触发,对于抢占 时钟中断(每个时钟周期检查一次就绪队列,如果有VIP就拿来执行)会触发 -
调度程序永远的备胎。。闲逛进程,也就是RTOS的空闲任务,优先级最低
调度算法
评价指标:CPU利用率 系统吞吐量(单位时间完成多少作业) 周转时间(作业提交到完成) 等待时间(对于客户和进程来说等待时间不同!!!) 响应时间(提出请求到首次被响应)
- 先来先服务
该算法用于作业调度,考虑哪个作业先到达后被队列,用于进程调度,考虑哪个进程先到达就绪队列
-
优点就是公平,实现不复杂,缺点是有利于长作业,如果是排在长作业后面的一个短作业,他就很愤怒
-
非抢占式 不会导致饥饿
- 短作业(进程)优先
该算法考虑追求最少的平均周转时间、平均等待时间,选择当前已到达且运行时间最短的作业!!比如说我只用一秒就处理完了,我前面这个比要五秒,我就先干我的让他等一秒!!!
-
会饥饿 默认非抢占式(也有抢占式,叫最短剩余时间优先!!) 被抢占后不用从头开始 剩多少干多少
-
优点是整体数据更好看,缺点是运行时间其实是用户提供的,如果被骗了,会导致诚实的长作业饿死
- 高响应比优先
该算法优先考虑响应比
- 不会饥饿 一般是非抢占式!!!!
以上的三种算法,根本没考虑用户的响应时间和任务紧急程度,只用于早期的批处理系统!!!!
- 时间片轮转(时间片太大就退化为先来先服务了 时间片太小就消耗太多时间、资源用于切换了)
公平、轮流的为各个进程服务,一个时间片没弄完就滚去队尾等下一次服务吧!!
-
只用于进程调度(因为只有作业从外存放到内存才涉及时间片概念)
-
抢占式,不会饥饿
- 优先级调度
选择优先级高的进程,优先级可以是静态的,也可以是动态调整的
(系统进程>用户进程 前台进程>后台进程 IO繁忙进程>CPU繁忙进程)
-
作业调度、进程调度 甚至I/O调度都会用到
-
有抢占也有非抢占 会饥饿
- 多级反馈队列调度
基于多个队列,每个进程刚来的时候放在最高优先级的队列,一个时间片后放到次高优先级队尾,直至执行完或跑到了最低优先级队列!!!
- 优先级越高的队列,时间片越小!!! 只有当前优先级队列空了,才会继续执行下一级优先级队列!!
一种情况: 当前还没执行完, 更高优先级队列又来一个, 那就把他放在当前队列队尾 无需降优先级!
- 相对公平 每个进程都可以较快被响应 避免用户谎报运行时间,
- 抢占式算法!可能导致饥饿 一般情况下就说他是无敌平衡的6666
不同类型的进行导入不同优先级的队列,比如系统进程 交互式进程(打字 游戏) 批处理进程(特效渲染)
调度方式不仅可以高优先级空了再低优先级,也可以时间片划分,比如依次分配0.5 0.4 0.1的时间
不同队列也可以采用不同调度策略
进程同步与互斥
异步指的是各个并发执行的进程以各自独立、不可预测的速度推进
比如进程通信的管道通信中,写数据和读数据异步,但是要保证先写再读,就需要同步!!
资源共享分为互斥共享和同时共享 原则:空闲让进 忙则等待 有限等待 让权等待(临界区空闲直接进,不空闲就等,设置最长等待时间,到期了还没进就让出处理机)
软件实现互斥
- 单标志法: 定义一个int,指定哪个进程可以进入临界区,也就是说某个进程进入临界区的权限只能通过另一个进程给予
- 问题:违反空闲让进,前一个进程让出了临界区,下一个进程迟迟不访问的情况(⑦turn=0只能表示P1用完临界区了,P0如果不想运行,会卡在②) 运行时序P0-P1-转给P0 但是P0不用了-P1想用不能用
- 双标志先检查法:定义一个bool数组,用于标记想进入临界区的意愿,flag[i]=true表示想且能立即进入临界区
- 问题:违反了忙则等待,二者并发运行,①之后没来得及执行② 就切换并执行了⑤ 会导致二者抢占,
二者的区别在于,单标志法是检测临界区是否被使用,不被使用就进入(哪怕我现在不需要进入临界区),双标志法是先看对方是否想用,直至对方不想用了,再表示自己想用,使用完后再表示自己不用了
也就是说不想用时单标志法会卡在临界区执行语句,双标志法会卡在给flag[]赋值的语句
- 双标志后检查法
- 问题: 违背了空闲让进和有限等待 并发时会卡在2 6,产生饥饿!!!
- Peterson(皮特森算法)
- 并发时,看谁后谦让!!!后谦让的算数,谁后说的客气话,谁失去执行权!!!
未遵循让权等待!!!其实就是结合单标志和双标志
硬件实现互斥
- 中断屏蔽方法(硬件)
- 也就是通过关中断--临界区--开中断 实现!!优点是简单高效 缺点是不适用多处理机,不适用用户进程(用户进程操作内核态特权指令开关中断不安全!!)
- TS/TSL指令(硬件)
- 只是逻辑展示,实际上是通过硬件寄存器实现的,稳定可靠,可用于多处理机环境,但仍不满足让权等待
- swap指令(Exange XCHG)(硬件)
- 最开始的lock已经有值了,但我们不知道他是啥,进入循环,lock=true,old=刚刚的lock,如果此时的old=true,说明已经上过锁了,那么两个true继续交换直至lock变成false(在另一个占用临界区的进程里变成false) 这时再交换一下old就会变成false,执行临界区代码
互斥锁
最简单的解决临界区的工具就是互斥锁,acquire()获得锁 release()释放锁 锁里有个布尔变量
-
以上就是自旋锁,比如TSL SWAP 单标志法
-
主要缺点是忙等待,不适用单核处理机,因为忙等的时候不可能获得锁
信号量机制
一对原语,wait(S) signal(S) S表示信号量,简称为P V操作P(S) V(S)
- 分为整形信号量和记录型信号量
-
对整形信号量的操作,只有三种 初始化 P V
类似双标志先检查。但他是原语,检查上锁一气呵成,避免异步问题。问题:不满足让权等待
-
记录型信号量 wait原语和整形的不同 解决了让权等待!
这里为什么是≤,因为只有其他资源wait完了才会阻塞,才需要wakeup!!!如果大于零就不需要wakeup了!! 也就是说 阻塞和唤醒 不是必须的操作!!!
- P原语用于分配资源,导致S.value--,直至变成负的说明资源已分配光了,因此自我阻塞。V原语用于释放资源,如果释放后S.value依然≤0,说明还有进程在等待该资源,因此唤醒阻塞队列的其中一个
信号量实现互斥 同步 前驱
互斥:
同步,初始化为0,先P1再P2 还是先P2再P1 都可以保证代码1 代码2 代码4 的顺序
- 总结:在前操作(12)后执行V,在后操作(4)之前执行P
前驱
生产者-消费者问题
类似于读写,满了不能写 空了不能读 “不能”的时候必须等待,另一侧唤醒我!
读写缓冲区,缓冲区是互斥资源,否则会导致第二个资源写的覆盖我写的
死锁
实现互斥的P操作一定要在实现同步的P操作之后!!!不然会死锁!!!
- V操作顺序无所谓的
- 生产产品和使用产品可以放到临界区,但是会导致临界区执行时间变长,不推荐
多生产者,多消费者问题
也就是多读多写
- 场景 盘子就一个空 父亲放苹果给女儿吃 母亲放橘子给儿子吃
前操作之后V 后操作之前P
对盘子来说前操作是取走水果 后操作是放入水果(初始盘子为1)
对水果来说前操作是放入水果 后操作是取走水果(初始水果0)
- 最终发现不设置mutex也可以....根本原因是盘子容量为1!!!
吸烟者问题
也就是多读一写
- 同样不用设置互斥
个人理解 P相当于加锁、等待xxx V相当于解锁、释放xxx
读者写者问题
要求:可以多个读者同时读 但某时刻只能有一个写,而且有人在写,不可以任何访问(读写)
- 用于防止读的时候发生改变、写的时候发生覆盖!
- 问题在于,P(rw)和count++中间如果发生了进程切换,会出现加锁两次!第一个加的被阻塞
- 解决方法是加mutex
潜在问题:读进程优先,如果一直有人在读,写进程就会饿死
解决:添加一个写优先的信号量
哲学家问题
与其他问题不同的是,需要两个资源才可以运行
- 如何预防死锁?
- 最多允许四个哲学家同时进餐,可以保证至少有一个哲学家可以拿起俩筷子!
- 奇数先拿左,偶数先拿右
出现的问题:0号拿左拿右 1号无法拿左阻塞,同时他持有mutex,接着切2 2因为没有mutex也被阻塞,尽管左右筷子都空但是还是不让你拿
出现的问题:0号拿左拿右 4号拿左,右边无法拿 如果此时上了3 3就是大怨种,4占着茅坑不拉屎!!!(可以规定只有两边都可以拿,再拿)
虽然是问题 但已经是最好的办法了!!
说白了就是让拿筷子的操作互斥的进行,就算拿筷子拿到一半被阻塞,也没人继续尝试拿筷子
管程
- 每次只允许一个变量访问函数,管程相当于一个class
说白了就是编译器帮你解决怎么互斥,先哪个P再哪个V,我只需要保证
-.- 没听懂,,,,后面再补吧
死锁
上面哲学家问题说过了!!其实就是各个进程都在等待其他资源 不想自己先放
- 死锁指的是各进程都无法推进,饥饿指的是某个进程不能推进,死循环一般是bug
- 饥饿可能处于阻塞(得不到外设)可能处于就绪(得不到CPU) 死锁一定是阻塞
死锁的必要条件 互斥 不剥夺 请求和保持 循环等待
- 只有争抢互斥的资源才会导致死锁,可以同时让多个进程使用的资源不会死锁比如内存
- 资源只能主动释放,不能被抢走
- 请求另一个资源,又不释放当前已有资源
- 循环等待就比如哲学家问题(必要不充分 比如这个资源数量大于1)
处理策略
- 预防,避免四个必要条件
- 避免,银行家算法
- 检测和接触,由操作系统解决
预防死锁
也就是破坏死锁的四个必要条件的某一个/某几个
- 互斥条件 SPOOLing技术,把互斥的资源改为共享.....NB,对于两个进程来说都在同时访问,只是访问后还需要等罢了,使用比较窄。。
- 不剥夺条件 请求新资源被拒绝(不可被剥夺) 立即释放当前拥有的所有资源//或者改优先级缺点是实现复杂、反复申请释放增加系统开销,而且释放容易导致前面的工作失效
- 请求和保持条件 运行前一次性请求完他需要的所有资源!!缺点是资源利用率低 导致饥饿
- 循环等待条件 规定每个进程必须按编号递增请求,同编号的资源一次申请完 只有已占用小资源才可以申请大资源,所以任何一个时刻总有一个进程拥有最大号的资源,它后面的申请畅通无阻。缺点是不方便新增资源(需要重新编号) 编号顺序不好确定
避免死锁
已知安全状态一定不会死锁,不安全状态可能死锁,所以每次分配资源前判断是否不安全即可
也就是说,只有资源数全部满足才可以开始执行,开始执行才可以执行完,执行完才可以归还
如果分配完了找不到安全序列咋办呢!???阻塞等待呗
检测和解除
用一种数据结构保存资源的请求和分配情况,根据这些信息判断是否进入死锁
此时P2的资源被P1占用了,比如P1运行完,会归还所有资源也就是相连的边清空
如果最后可以全部消除,说明是一个安全序列,不会死锁
如果不能消除所有的边 一定是正在死锁
- 解除方式:资源剥夺(挂到外存上 记得拿回来) 撤销进程法(简单粗暴 直接给你终止了) 进程回退法(不太容易实现 需要记录历史信息)
如何决定对谁动手?
- 优先级低的,刚开始运行的,还要很久才能结束的,已经用了很多资源的,批处理的进程
内存
基础知识
内存是为了缓和CPU和硬盘(外存)的速度矛盾,因为CPU处理得很快,硬盘很慢!!
- 一个4G的设备,他就一共有4 * 2^30 = 2^32个存储单元,需要32位二进制数表示内存地址
-
“按字节编址”就是一个存储单元一字节,八位;“按字编址”需要看计算机字长几位就是几位
-
绝对地址问题,比如程序的指令是向地址20的地方写入一个数,实际上程序最后编译成“装入模块”(也就是exe) 起始地址并不是0,就会导致这个数写入位置错误,他应该写入起始地址+20!!
-
策略1:绝对装入,预先知道起始地址为100,直接让他放在120的地址(只适用没操作系统裸机)
-
策略2:可重定位装入(静态重定位)装入时+100(必须一次分配全部内存空间 运行后不可移动)
-
策略3:动态运行时装入(动态重定位)运行时+100(可动态申请内存 可不连续分配 优点多多 只是需要一个重定位寄存器支持)
-
从写程序到程序运行
-
三种链接方式,和上面三种策略一样。。。
- 静态链接,把所有模块和所需库函数链接,之后不再拆开
- 装入时动态链接,如果有的模块没用上就不装入,其他用到的就边装入边连接
- 运行时动态链接,程序运行时发现需要哪个模块,才链接他
内存管理
实现内存的分配回收、逻辑上扩充物理地址、地址重定位(装入+100)、内存保护(互不干扰)
- 内存保护的方法:上下限寄存器 / 重定位寄存器(基址)+界地址寄存器(限长)
覆盖与交换
内存空间的扩充:覆盖 交换 虚拟存储(这个是重点)
-
覆盖用于解决物理内存 < 程序大小,思想是把程序分段,常用的常驻内存,不常用的需要时调入内存中分为一个固定区(常驻 调入后结束再调出)、若干个覆盖区(需要调入 不需要调出)
-
交换:内存紧张,把某些进程调到外存(挂起!) 中级调度就是用于决定把哪个挂起的放入内存 一般把磁盘分为文件区和对换区,文件区离散分配(磁盘利用率高),对换区占小部分,存放被挂起的进程 连续分配,也就是说对换区I/O速度更快点
一般优先换出阻塞进程、优先级低的进程 PCB数据结构会常驻内存,不会被换出!!!
连续分配
单一连续分配
分为系统区 用户区,同一时刻只能有一个程序在用户区!!
- 优点是简单 无外部碎片 可以采用覆盖技术 不一定用内存保护,
- 缺点是只能单用户单任务 有内部碎片(分配给某进程的存储空间没用上!!) 存储空间利用率低
固定分区分配
分为两种,分区大小相等/不等
-
大小不等有灵活性,大小相等适合控制多个相同对象
-
通过一个数据结构记录划分情况、使用情况等
-
优点是简单 无外部碎片,缺点是用户程序太大只能用覆盖,会产生内部碎片,内存利用率低
动态分区分配
进程装入内存时,再根据进程大小动态划分分区
-
用空闲分区表/链 记录分区情况
-
有多个空闲分区可以满足新来进程的要求,放在哪呢??动态分区算法!!
-
首次适应:每次都从低地址开始,找到第一个能满足大小要求的空闲分区
-
最佳适应算法:优先使用更小的空闲分区!也就是按容量递增的次数对分区表排序 每次分配从第一个开始找到最小的能装入的分区(缺点会逐渐产生很多的外部碎片!!!)
-
最坏适应算法:优先使用更大的空闲分区!!!(缺点是后来的大进程无处安放)
-
临近适应算法:每次从上次查找结束的位置开始查找(好处是更新后也不用重新排列),解决了首次适应算法导致的低地址存在很多碎片问题
-
回收后空闲分区需要合并,分配后可能导致分区减少,也可能导致增加
-
没有内部碎片(分配了但没用上),但是有外部碎片(某些空闲分区太小导致无法利用) 外部碎片可以通过紧凑/拼凑基数利用
分页存储管理
内存空间分为很多大小相等的分区(一个分区叫一个页框!!) 进程的逻辑地址也相等分区,叫页面,页面号、页框号从0开始!!
-
页面不必连续存放到页框内!!!
-
操作系统用页表(存在PCB中)控制
- 一般只用存储块号(页框号)i号页面号的地址就是X + 3 * i(存页框号占3字节 起始地址X)
基本地址变换结构
进程没有上CPU时,页表起始地址F和页表长度M放在PCB,上处理机后,放在页表寄存器(PTR)
- CPU是怎么按照逻辑地址找到下一条指令地址的呢?
- 根据逻辑地址算出页面号和偏移量
- 判断页号是否越界(页号从0,长度至少是1,所以页号≥页表长度都越界)
- 查询页表找到页号对应的页表项!
- 块号(页框号、页表项)也是存在内存中的,只要是内存就被分为页面了,比如存一个块号要三字节,一个页框/页面是4kb,那最后会导致一个页面能存1365个块号,多出1B碎片!!!!
最后规定一个页表项占4k,放在连续的内存块, 方便查找。。。
快表地址变换结构
快表又称为联想寄存器,是一种访问速度比内存快很多的缓存!!!注意不是内存是缓存!!
-
进程切换快表要被清除!!所以第一次查找肯定不会命中,要先查找内存,并放入快表,下次用
-
为什么快表命中率高?统计学。。。如果一个指令(数据)被执行,不久后很可能再次被执行 某个存储单元附近的存储单元也可能会在不久后被访问
两级页表
单级页表的问题1:存块号需要连续存储,而光要存块号(页框号 页表项)就要很多。。。不合理
单级页表的问题2:进程在某个时间一般只用访问几个特定的页面,没必要整个页表常驻内存!!
- 刚刚说过,块号也是放在内存中的,假设一个页面4k,存储一个块号需要4B,那一个页面可以存1024个块号,一共有多少块号呢?取决于一共有多少页面,一共有多少页面呢?比如系统4G,一个页面4k,那也就是2^20个页面/块号,
-
220个块号一共需要存在210个页面中(也就是1024个 编号0--1023)!!!!!!!
-
所谓二级页表,就是把上述0--1023个页面,也记录在某个页表中,称为页目录表/顶级页表
-
各级页表大小不要超过一个页面,比如上例,一个页面4k,页表大小1024=1k没超过
1G = 1024 M = 2^10 M
1M = 1024 K = 2^10 K
1K = 1024 B = 2^10 B
**1B = 8字节, 1G = 2^30 B **
基本分段存储管理方式
程序自身分段!!段名是程序员规定的!!进程上CPU也分配段表寄存器(包括始址和长度)
-
每个段可以不同长,所以相比于页,需要一个额外的“段长”
-
每个段表项包括段长、基址,比如规定6B用于存放一个段表项,就可以省略段号不存储
-
分页完全是系统行为,用户不可见;分段对用户可见,用户需要显式给出段名
段页式管理
虚拟存储
上述的几种 分页分段 固定、动态分区,都无法解决:大进程无法运行、多进程并发度下降
- 虚拟技术其实就是把程序中很快用到的部分装入,暂时用不到的放在外存,执行时发现要用到再从外存调入,建立在离散分配(分页 分段 段页)的基础上!!!!!!
请求分页管理方式
与基本分页存储管理相比,操作系统还需要知道每个页面在内存还是外存,外存的什么位置
- 需要引入缺页中断(内中断)机构,也就是没在内存的时候产生中断,从外存调入,如果没有空闲页了就需要淘汰一个!!!
页面置换算法
上面说的会淘汰,淘汰哪个呢!!这个决定的,追求最少的缺页率。
- 最佳置换:每次都淘汰之后不会再用/最长时间不用的页面
实际上根本无法实现啊,洒必,操作系统怎么会知道后面用哪个页面。。
- 先进先出置换:每次都淘汰最先进入的页面(性能差)
会belady异常:给进程分配内存块数增加,缺页次数反而增加...因为先入的块可能频繁调用
- 最近最久未使用置换LRU:淘汰自上次被访问后历时最久没有再次被访问的
需要特定的硬件支持,开销大
- 简单时钟置换:淘汰访问位为0的页面(每个页面预留一个访问位)
最多需要两轮扫描,比如第一轮全是1,扫描时会把1置0,0换出
- 改进型时钟置换:当页面被淘汰后,如果修改位为0就不用重新写入外存,外存肯定有
最多需要四轮扫描,第一轮找没修改、没访问,第二轮找修改、没访问(1置0) 第三次找没修改没访问 第四次找修改、没访问
页面分配策略
- 驻留集:给进程分配的物理块的集合
-
采用虚拟技术后,驻留集大小一般小于进程总大小,但太小又会导致缺页,所以要适当选择
-
如何选择呢? 固定分配 可变分配(驻留集大小可不可变) 局部置换 全局置换(可不可随意置换)
-
固定分配局部置换 容易导致分配不合理
-
可变分配全局置换 容易导致进程驻留集减少,缺页率增加
-
可变分配局部置换 一般认为这个比较叼
-
全局置换意味着进程的驻留集一定会改变,所以全局置换一定不是固定分配
-
预调页策略:程序员指定,用于进程首次调入
-
抖动就是频繁的调入调出
内存映射文件
简单来说就是操作系统项程序员提供的接口。。。
-
传统的方式是open seek read write...
-
内存映射文件是open mmap close不用read write(自动把修改过的保存到磁盘)了。。!!!
-
内存映射可以让多个进程映射到同一个文件,实现共享文档。。!!!
文件
文件管理
-
同一目录下不允许有重名文件,标识符不是文件名,只是系统区分各个文件的一串乱码
-
可以分为流式文件(文本,一些二进制/字符流组成)和记录式文件(xls 由一些相同的记录组成)
-
目录其实也是一种有结构的特殊文件
-
外存也是分块的!!!
文件的逻辑结构
文件分为无结构(.txt)、有结构(数据库表)文件,有结构根据每条记录占用存储空间是否相等分为定长记录、可变长记录
- 逻辑结构把有结构分为顺序(顺序存储或链式存储、可以按关键字存也可以不按、可以定长也可以不定)、索引、索引顺序
是否可以随机存取??
我非要用可变长记录,就不能随机存取了吗?不是的,用索引表(定长记录的顺序文件)记录
- 用索引表跟页表一样,好占地方,咋办呢,,索引顺序文件,类似于二级页表的思想。。SB 实际上占用了更大的物理空间,但是可以拓展到更大的虚拟空间,,也就是通过增加io次数,,2B
文件目录
- 目录也是一种特殊的文件,每个文件都对应一个文件控制块(FCB)
- 索引节点是一种对FCB的改进
-
最开始是单级目录结构,缺点是不允许重名!!
-
两级目录结构:主文件目录MFD和用户文件目录UFD,可以重名了 还可以添加访问控制!!
-
多级目录结构,又叫树形目录结构,是最常用的(不便实现文件共享)
从根目录出发的路径叫绝对路径!
- 无环图目录结构
也就是不同的目录可以指向同一个文件!!
- 删除时比较麻烦,可以设置一个共享计数器,这个删了另一个还能看
文件的物理结构
探讨文件的数据要怎么存在外存里!!!
- 连续分配,就是一个文件的所有块都连续存放在物理地址中
-
需要知道起始块号和长度,支持顺序访问(找2要先01)和直接访问
-
访问磁盘需要移动磁头,两个块离太远访问就太慢,所以连续分配很快
-
缺点是,如果分配着发现不够了,就要整体迁移,很浪费时间的,而且也不能充分利用磁盘空间(全是零散的块无法利用)
- 连接分配,分为显式连接(指针存放在一张表中)、隐式连接
-
除了最后一块 其他块都会保存一个指向下一块的指针,只能顺序访问
-
显式链接只需在FCB中记录起始块号,隐式链接还要记录结尾块号
-
显式链接需要的表FAT开机时读入内存并常驻,而且可以随机\顺序访问
-
显式链接比隐式链接快,因为FAT常驻内存不用访问磁盘!!!
- 索引分配,为每个文件建立一个索引表,记录文件每一块对应的物理块!!
-
支持随机访问,易于拓展,但是占用空间!!!!!!
-
索引表也需要存储,如果一个索引表太大,就要采取措施:连接方案(多个索引块链接,但如果要访问最后的一块,就要一级一级读索引,读到索引才可以找,很慢)、多层索引(类似于多级页表,第一层索引指向第二层索引)、混合索引(顶级索引表包含直接地址、一级、二级等)
一次完整的文件操作
- 链式存储和连接分配没有直接关系!!!!!
文件存储空间管理
探讨对空闲部分如何分配
-
空闲表法(记录空闲起始块和总块数),适合连续分配,可采用首次适应、最佳适应、最坏适应等
-
空闲链表法
-
位示图法
- 也就是用一串二进制数表示,每一位表示每一块是否空闲,
- 成组链接法
- 目录区中开辟一块内存叫超级块!
文件的基本操作
- 创建,create系统调用,需要提供外存空间、路径、文件名
- 找到空闲空间、找到路径信息目录
- 删除,delete系统调用,需要提供路径、文件名
- 找到路径,找到外存位置,回收磁盘块、从目录表中删除该目录项
- 打开,open系统调用,需要提供路径、文件名、权限控制信息(读写)
- 找到路径、目录复制到“打开文件表(被多个进程打开,会有一个计数项)”
- 如果俩进程同时打开,一个进程想删除就会提示:该文件正在被使用
- 读文件, read系统调用,需要提供哪个文件(打开文件表的索引号)、读入多少数据(几KB)、读入的数据放在内存的哪里
- 写文件,write系统调用,需要提供哪个文件、多少内存、数据在内存哪
文件共享
硬链接(索引节点)、软链接(符号链)
- 索引节点上面写了
- 软链接:类似于windows的快捷方式。。。是一种特殊格式的文件link类型,打开它时,系统判断他是这个类型就直接跳转到他指向的文件
文件保护
- 口令保护,为文件设置一个口令(通常在FCB或索引节点里)
- 优点是开销小,缺点是口令存在系统内部不安全啊·!!
- 加密保护,访问文件时需要根据某密码,对数据解密
- Eg, 异或加密,优点是保密性强,缺点是花费时间
- 访问控制,在每个FCB中增加一个访问控制表
- 对每个用户来说太多记录了,精简一下按照组,比如windows的用户组、管理员组、其他成员等
文件系统层次结构
- 举例:我现在要删除D:/1.xls的前一百条记录
- 打开文件,open系统调用,(用户接口)
- 操作系统找到目录项(文件目录系统)
- 看看让不让打开,让不让访问(存取控制模块)
- 把用户提供的要删除的记录号,转变为逻辑地址(逻辑文件系统与文件信息缓冲区)
- 逻辑地址转换为物理地址(物理文件系统)
- 删除物理地址上的数据,(设备管理模块) 回收空闲磁盘(辅助分配模块)
文件系统的全局结构
-
初始磁盘,先物理格式化,就是把磁盘划分为扇区,并检测有无坏扇区,有的话就要用备用扇区顶替他
-
逻辑格式化,进行分卷(C D E盘)
- 用户打开文件表存在PCB中
虚拟文件系统
普通文件系统,比如有磁盘,U盘,移动硬盘,打开文件的用户接口都不一样,虚拟文件系统就用于统一这些接口!!
-
实际上 是VFS强制要求文件系统实现它的规定格式,不然不让你参与我这个操作系统,,,,666
-
文件系统挂载,,说白了就是吧u盘插电脑上这种,挂载后在vfs中注册挂载表,新来的文件系统需要向vfs提供函数地址(open write..)列表,挂载点指的是挂在哪个父目录下!!
设备管理
I/0设备
UNIX系统把外部设备抽象为一种文件,所以也就是read write做IO
I/0控制器
-
分为机械部件(LED屏 鼠标键盘)和电子部件(插入主板的一块印刷电路板)
-
CPU通过控制电子部件,间接控制机械部件!!这个电子部件就叫IO控制器
-
电子部件(IO控制器)作用: 识别CPU命令、向CPU报告设备状态、数据交换、地址识别
I/O控制方式
- 程序直接控制
- 比如说CPU下发命令让你读,IO控制器中把设备状态寄存器设为1,设备比CPU慢,导致CPU一直轮询检查直至空闲,再读数据寄存器的值
- 优点是实现简单,缺点是CPU轮循一直占用资源
- 中断驱动方式
- 其实就是IO完成后通过发送中断信号,避免CPU一直轮询,CPU收到中断信号后再切过来处理
- 优点是优化了轮循不断等待这个问题,缺点是频繁中断消耗资源
- DMA(直接存储器存取)方式
- 上面两个,实际上数据都是一个字一个字地IO设备 <-> CPU <-> 内存这样流向,为了简化,也就是不经过CPU,
- 咋办呢?数据用块做传送单位,只有每块开始/结束才需要用中断找CPU传送,,说白了还是要CPU,萨比
- 优点就是传输效率更高了,缺点就是只能读写连续块,不能离散的
- 通道控制方式
- 通道是一种硬件,弱化版的CPU,可以识别并执行一系列通道指令
- 优点就是资源利用率更高,缺点就是需要硬件、实现复杂
IO软件层次结构
- 各种IO控制器,内部实现肯定也不同,所以就需要驱动程序!!!!!
从上到下依次是:printf、系统调用、设备驱动(独立进程)、中断处理
IO接口和驱动程序接口
- get/put read/write socket套接字
- 阻塞IO指的是,发出系统调用请求后进程要阻塞等待!例如scanf
- 非阻塞IO指的是发出系统调用后可以迅速返回,不用等待,比如write
假脱机(SPOOLing)技术
假脱机技术就是用软件实现脱机技术!!
- 所谓脱机技术,看第一章最开始《批处理阶段》
- 实现方式:输入井/输出井 输入进程/输出进程,比如共享打印机,,,
设备的分配与回收
- 安全分配/不安全分配,分配一个设备后就阻塞,直至IO完成才唤醒进程就叫安全分配,不阻塞就叫不安全
- 静态分配和动态分配,运行前一次性分配所有资源叫静态!!
缓冲区管理
可以用硬件寄存器(联想寄存器 上面有) 或者内存做缓冲区
- 作用是解决CPU和IO的速度矛盾(CPU一下充满就去干别的了,IO慢慢读) 减少CPU中断次数 提高cpu和IO的并行性
- 缓冲区非空,不可写只可读,缓冲区为空,可写且必须写满之后才可读
- 单缓冲、双缓冲...其实就是几个缓冲区的意思!!循环缓冲区指
- 缓冲池,就是放了很多缓冲区的池子,
磁盘的结构
磁盘原来真的是磁性物质组成,,用这些物质记录二进制数据,,,NB
磁盘调度算法
磁盘一次读写: 启动磁头臂、移动磁头到磁道、磁头转到数据处、读写数据(主要是转磁头到指定位置),能优化的部分只有移动磁头到磁道这部分!
- 先来先服务的话,优点是简单,缺点是磁道分散的情况下性能极差
- 最短寻找时间优先的话,相当于贪心算法,缺点是可能会饥饿!!
- 扫描算法:只有移动到最内才可以向外,只有移动到最外才可以向内·
- LOOK算法,边移动边观察,移动到最后发现这边没有请求了立即掉头
- 循环扫描算法,响应频率更平均,,,但是没什么软用我感觉,,
- C-LOOK算法,优化循环扫描,因为循环扫描又叫C-SCAN,,,杀软
减少磁盘延迟方法(旋转的方法)
- 现在读取2号,下一个要读取3,但是转到3的时候2还没处理完就错过了
- 读取连续编号扇区时,采用柱面、盘面、扇区更高效(相比盘柱扇)
解决方式:交替编号,就是让2 3不相邻;错位命名(读连续地址的扇区时)
磁盘的管理
- 扇区由头 数据区 尾组成,像校验码、链接等一般放在头尾不影响数据
- 初始化程序放在ROM中(出厂时写入后面无法修改) 现在是在ROM中方很小的初始装入程序,这个程序找到C盘的自举程序(初始化程序),正因为他在C所以C才是系统盘!!!
固态硬盘SSD
-
固态硬盘是基于闪存技术,机械硬盘是基于磁性物质(闪存芯片就是上图黑黑的那个小块)
-
块 = 磁道 页 = 扇区
-
SSD优点是无噪音,能耗低,读写快,耐摔!!随机访问性能高,缺点是一个块被擦除次数过多可能会损坏,而且造价贵
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具