操作系统对上层提供的三类接口(对硬件命令封装,隐藏实现细节)
1:系统调用(应用程序)
2:gui(用户直接对操作系统)
3:命令(黑窗口)
@os具有扩充性
*可以对os进行再编程,封装硬件命令,提升os功能
os的并行与并发
*并发:一个时间段处理事件的能力
*并行:同一时间点处理事件的能力(发生的时间数量(较少,如收费站))
,物理极限明显(cpu数量=并行数量)
os的异步性
os发展历程
*手工操作阶段
*批处理阶段(监督程序(操作系统)
¥单道批处理阶段(内存中只有一道程序)
¥多道批处理系统
1:例如有两道程序,第一道程序执行输出队列,第二道程序就可以由cpu执行了
*分时操作系统
*实时操作系统
*微机操作系统(pc,个人计算机)
*网络操作系统
*分布式操作系统
************操作系统的运行机制*************
在系统调用的过程中同样发发生了CPU上下文切换:
CPU寄存器里面原来用户态的指令位置,需要先保存起来,
接着运行内核态代码CPU寄存器需要更新为内核态指令的位置,
执行内核态代码系统调用结束后,CPU寄存器需要恢复原来保存的用户态,
然后切换为用户空间,所以一次系统调用的过程,会发生两次的CPU上下文切换
******************故障中断*************************
中断处理的过程中需要开关中断2次
开始:
程序正常执行
↓响应中断
关中断(开始中断程序后,CPU不能再响应更高级的中断,所以先关闭)
↓保存断点;保存中断程序地址;保存现场(这里是保存中断请求的必要信息)
开中断(把中断打开,现在允许执行更高级别的中断)
↓执行中断服务程序(这里是可以响应高级别中断的)
关中断(和第一次中断一样,在恢复现场的情况下,不能响应另外的中断)
↓恢复现场
开中断
↓中断返回
************************************************************************************
**********操作系统的结构**************
*************微内核的基本概念*******
************什么是进程*********
1:进程的结构
2:进程与线程的区别
3:线程是os运算调度的最小单位
4:进程拥有内存的所有权,线程可以向进程申请内存资源
5:进程是os资源分配的最小单位
6:线程是小进程,也能中断切换
线程不占有资源的好处:
1:线程若拥有资源,中断过程保留现场会过于繁重
2:轻量级,向进程访问资源
CPU 上下文切换的类型
根据任务的不同,可以分为以下三种类型 -
进程上下文切换 - 线程上下文切换 - 中断上下文切换
系统调用
从用户态到内核态的转变,需要通过系统调用来完成。比如,当我们查看文件内容时,就需要多次系统调用来完成:首先调用 open() 打开文件,然后调用 read() 读取文件内容,并调用 write() 将内容写到标准输出,最后再调用 close() 关闭文件。
在这个过程中就发生了 CPU 上下文切换,整个过程是这样的:
1、保存 CPU 寄存器里原来用户态的指令位
2、为了执行内核态代码,CPU 寄存器需要更新为内核态指令的新位置。
3、跳转到内核态运行内核任务。
4、当系统调用结束后,CPU 寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程。
所以,一次系统调用的过程,其实是发生了两次 CPU 上下文切换。(用户态-内核态-用户态)
不过,需要注意的是,系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程。这跟我们通常所说的进程上下文切换是不一样的:进程上下文切换,是指从一个进程切换到另一个进程运行;而系统调用过程中一直是同一个进程在运行。
所以,系统调用过程通常称为特权模式切换,而不是上下文切换。系统调用属于同进程内的 CPU 上下文切换。但实际上,系统调用过程中,CPU 的上下文切换还是无法避免的。
发生进程上下文切换的场景
为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。这样,当某个进程的时间片耗尽了,就会被系统挂起,切换到其它正在等待 CPU 的进程运行。
进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行。
当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度。
当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行
发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序。
一次系统调用的过程,其实是发生了两次 CPU 上下文切换 。.
(用户态-内核态-用户态). 不过,需要注意的是, 系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,
也不会切换进程 。. 这跟我们通常所说的进程上下文切换是不一样的:
进程上下文切换,是指从一个进程切换到另一个进程运行;
而系统调用过程中一直是同一个进程在运行。. 所以,
系统调用过程通常称为特权模式切换,而不是上下文切换。
系统调用属于同进程内的 CPU 上下文切换 。
//系统调用是cpu切换状态,通过内核的int 080x指令,切换到内核态,用户态
//系统调用也叫模式切换,只是寄存器进行了切换
接下来是
(1)进程之间的上下文切换:A进程切换到B进程
(2)进程和中断之间的上下文切换:进程A被中断打断
(3)中断之间的上下文切换:低级别中断被高级别中断打断
//线程切换的本质是pcb或tcb(唯一标识)
线程的实现方式
1:用户级方式(用户级线程)
对于内核而言,其只知道进程,不知道线程,因为线程都是由进程进行管理的
根据时间轮转机制,若想调用cpu,
每个进程都分到相同的时间调用cpu,
导致线程多的进程执行效率低
用户级线程仅存在于用户空间中,此类线程的创建、撤销、线程之间的同步与通信功能,都无须利用系统调用来实现。用户进程利用线程库来控制用户线程。由于线程在进程内切换的规则远比进程调度和切换的规则简单,不需要用户态/核心态切换,所以切换速度快。由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少为了在操作系统中加入线程支持,采用了在用户空间增加运行库来实现线程,这些运行库被称为“线程包”,用户线程是不能被操作系统所感知的。用户线程多见于一些历史悠久的操作系统,例如Unix操作系统
2.3 用户级线程的优点//
用户线程的优点主要有
可以在不支持线程的操作系统中实现。
创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多, 因为保存线程状态的过程和调用程序都只是本地过程
允许每个进程定制自己的调度算法,线程管理比较灵活。这就是必须自己写管理程序,与内核线程的区别
线程能够利用的表空间和堆栈空间比内核级线程多
不需要陷阱,不需要上下文切换,也不需要对内存高速缓存进行刷新,使得线程调用非常快捷
线程的调度不需要内核直接参与,控制简单。
2.4 用户线程的缺点
用户线程的缺点主要有
线程发生I/O或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程, 因此同一进程中只能同时有一个线程在运行
页面失效也会产生类似的问题。
一个单独的进程内部,没有时钟中断,所以不可能用轮转调度的方式调度线程
资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用
2:内核级方式(内核级线程)
tcb在内核空间,程序段和数据段在用户空间
3.1 线程的内核级线程实现
在内核级线程中,内核线程建立和销毁都是由操作系统负责、通过系统调用完成的。在内核的支持下运行,无论是用户进程的线程,或者是系统进程的线程,他们的创建、撤销、切换都是依靠内核实现的。
线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口. 内核为进程及其内部的每个线程维护上下文信息,调度也是在内核基于线程架构的基础上完成。图2-2(b)说明了内核级线程的实现方式。
内核线程驻留在内核空间,它们是内核对象。有了内核线程,每个用户线程被映射或绑定到一个内核线程。用户线程在其生命期内都会绑定到该内核线程。一旦用户线程终止,两个线程都将离开系统。这被称作”一对一”线程映射,
线程的创建、撤销和切换等,都需要内核直接实现,即内核了解每一个作为可调度实体的线程
这些线程可以在全系统内进行资源的竞争
内核空间内为每一个内核支持线程设置了一个线程控制块(TCB),内核根据该控制块,感知线程的存在,并进行控制
3.2 内核线程的特点
当某个线程希望创建一个新线程或撤销一个已有线程时,它进行一个系统调用
3.3 内核线程的优点
内核线程的优点:
多处理器系统中,内核能够并行执行同一进程内的多个线程
如果进程中的一个线程被阻塞,能够切换同一进程内的其他线程继续执行(用户级线程的一个缺点)
所有能够阻塞线程的调用都以系统调用的形式实现,代价可观
当一个线程阻塞时,内核根据选择可以运行另一个进程的线程,而用户空间实现的线程中,运行时系统始终运行自己进程中的线程
信号是发给进程而不是线程的,当一个信号到达时,应该由哪一个线程处理它?线程可以“注册”它们感兴趣的信号
5 、用户级线程和内核级线程的区别
内核支持线程是OS内核可感知的,而用户级线程是OS内核不可感知的。
用户级线程的创建、撤消和调度不需要OS内核的支持,是在语言(如Java)这一级处理的;而内核支持线程的创建、撤消和调度都需OS内核提供支持,而且与进程的创建、撤消和调度大体是相同的。
用户级线程执行系统调用指令时将导致其所属进程被中断,而内核支持线程执行系统调用指令时,只导致该线程被中断。
在只有用户级线程的系统内,CPU调度还是以进程为单位,处于运行状态的进程中的多个线程,由用户程序控制线程的轮换运行;在有内核支持线程的系统内,CPU调度则以线程为单位,由OS的线程调度程序负责线程的调度。
用户级线程的程序实体是运行在用户态下的程序,而内核支持线程的程序实体则是可以运行在任何状态下的程序。
在用户级线程,pcb,数据段,程序段都在用户空间,即是不用切换到内核态,就可以完成线程的切换
在内核级线程,pcb在内核空间,即是要切换到内核态,才能完成线程的切换,要经过三次切换,用户->内核->内核->用户
//进程的三种状态
1:就绪
2:执行
3:阻塞
就绪到执行的过程叫进程调度(低级调度),就绪是还未获得cpu执行权
当前进程有io请求,当前线程被丢入阻塞队列,就绪队列的其它进程开始执行(中级调度)
当前执行线程时间片用完,被丢到就绪队列
阻塞状态是属于放弃cpu的一种暂停执行的状态
4:创建和终止
终止有外界原因,故障原因,正常原因,
终止要做两件事情
1:pcb标记为终止,因为终止由操作系统操作,可能会失败,所以要先标记,防止回队列
2:释放资源
//操作系统通过原语进行线程控制
进程控制:
1:挂起与激活
挂起是从阻塞或就绪状态进入挂起,存入磁盘(外存)(非原来磁盘的位置)
创建时如果内存资源满了,就会挂起进程,存入外存
//cpu的调度
1:作业调度?磁盘上的文件进入”作业队列“,加载入内存,创建进程
2:内存调度?内存上的进程suspend,合适条件active(调度区域在内存兑换区)
3:进程调度?进程控制中的就绪队列,执行状态,阻塞状态,调度从就绪到执行
//进程调度原则
1:剥夺,抢占式调度(时间片原则,短进程原则)
2:非抢占式调度(等待当前线程完成阻塞,适用于批处理,不适用实时系统)
***********
一条进程运行在操作系统中,运行在用户态权限少
运行在内核态权限多,两种状态切换不涉及进程的
上下文切换,切换进程只能发生在内核
***********
运行的进程时间片用完后,丢到就绪队列
新创建的进程不是,其丢到作业队列
(进程调度的等待时间)进程在队列等待执行的时间
高响应比优先调度算法
主要用于作业调度,是对FCFS调度算法和SJF调度算法的一种综合平衡,同时考虑了每个作业的等待时间和估计的运行时间。在每次进行作业调度时,先计算后备队列中每个作业的响应比,从中选出响应比最高的作业投入运行。
响应比的变化规律可描述为:
响应比Rp = (等待时间+要求服务时间)/要求服务时间
根据公式可知:
①作业的等待时间相同时,要求服务时间约旦,响应比越高,有利于短作业。
②要求服务时间相同时,作业的响应比由其等待时间决定,等待时间越长,其响应比越高,因而它实现的是先来先服务。
③对于长作业,作业的响应比可以随等待时间的增加而提高,等待时间足够长时,其响应比便可升到很高,从而可以获得处理机,不会饿死。
先来先服务(FCFS)调度算法
FCFS是一种最简单的调度算法,它既可以用于作业的调度,又可以用于进程调度。在作业调度中,算法每次从后备作业队列中选择最先进入该队列的一个或几个作业,将他们调入内存,分配必要的资源,创建进程并放入就绪队列。
在进程调度中,FCFS调度算法每次从就绪队列中选择最先进入该队列的进程,将处理机分噢诶给它,使之投入运行,直到完成或尹某种原因而阻塞时才释放处理机。
FCFS属于不可剥夺(抢占)算法。从表面上看,它对所有作业都是公平的,但是如果有一个长作业先到达系统,就会使后面许多短作业等待很长时间,因此这种方法肯定不能作为分时系统和实时系统的调度方法,但是它常被结合在其他调度策略使用。比如在使用优先级作为调度策略的系统中,往往对多个具有相同优先级的进程按FCFS原则处理。
· 特点分析:算法简单,但是效率低下;对长作业较为有利,对短作业不利;利于CPU繁忙型作业,不利于I/O繁忙型作业。
短作业优先(SJF)调度算法
短作业(进程)优先调度算法是指对短作业(进程)优先调度算法。短作业优先调度算法从后备队列中选择一个或若干估计运行时间最短的作业,将它们调入内存运行;短进程优先(SPF)调度算法是从就绪队列中选择一个估计运行时间最短的进程,将处理机分配给它,使之立即指向,直到完成或发生某时间而阻塞时,才释放处理机。
但是这种算法有着不容忽视的缺点:
①该算法对长作业不利,SJF中长作业的周转时间会增加。更糟的是,若一旦有长作业进入系统的后备队列,由于调度程序总是优先调度那些短作业(即使是后来的短作业也会被优先安排给处理机),导致长作业长期不被调度,饿死在后备队列中。
②完全没有考虑作业的紧迫程度,因而不能保证紧迫的作业会被及时处理。
③由于作业的长短只是根据用户所提供的预估的执行时间而定的,而用户又可能会有意无意地缩短其作业的估计运行时间,使得算法不一定能真正做到短作业优先调度。
但这算法的优点也显而易见:平均等待时间、平均周转时间最少。
短作业优先(SJF)调度算法
短作业(进程)优先调度算法是指对短作业(进程)优先调度算法。短作业优先调度算法从后备队列中选择一个或若干估计运行时间最短的作业,将它们调入内存运行;短进程优先(SPF)调度算法是从就绪队列中选择一个估计运行时间最短的进程,将处理机分配给它,使之立即指向,直到完成或发生某时间而阻塞时,才释放处理机。
但是这种算法有着不容忽视的缺点:
①该算法对长作业不利,SJF中长作业的周转时间会增加。更糟的是,若一旦有长作业进入系统的后备队列,由于调度程序总是优先调度那些短作业(即使是后来的短作业也会被优先安排给处理机),导致长作业长期不被调度,饿死在后备队列中。
②完全没有考虑作业的紧迫程度,因而不能保证紧迫的作业会被及时处理。
③由于作业的长短只是根据用户所提供的预估的执行时间而定的,而用户又可能会有意无意地缩短其作业的估计运行时间,使得算法不一定能真正做到短作业优先调度。
但这算法的优点也显而易见:平均等待时间、平均周转时间最少。
优先级调度算法
又称优先权调度算法,它既可以用于作业调度,又可用于进程调度。该算法的优先级用于描述作业运行的紧迫程度。
在作业调度中,优先级调度算法每次从后备作业队列中选择优先级最该的一个或几个作业,将他们调入内存,分配必要的资源,创建进程并放入就绪队列。在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,并分配处理机,运行。
根据新的更高的优先级进程能否抢占正在执行的进程,可将该调度算法分为如下两种:
①非剥夺(抢占)式优先级调度算法:当一个进程正在处理机上运行时,即使有某个更在重要或者紧迫的进程进入就绪队列,仍然让正在运行的进程继续运行,直到由于自身的原因而主动让出处理机时(任务完成或等待),才把处理机分配给更重要或紧迫的进程。
②剥夺式优先级调度算法:当一个进程正在处理机上运行,若有某个更为重要或紧迫的进程进入就绪队列,则立即暂停正在运行的进程,将处理机分配给更重要或紧迫的进程。
而根据进程创建后其优先级是否可以改变,可以将进程优先级分为一下两种:
①静态优先级:优先级是在创建进程时确定的,并且进程的整个运行期间保持不变。确定静态优先级的主要依据有进程类型、进程对资源的要求、用户要求。
②动态优先级:在进程运行过程中,根据进程情况的变化动态调整优先级。动态调整优先级的主要依据有进程占有CPU的时间的长短、就绪进程等待CPU时间的长短。
一般来说,进程优先级可以参考一下原则:
①系统进程>用户进程。
②交互型进程>非交互型进程(前台进程>后台进程)
③I/O型进程>计算型进程。
时间片轮转调度算法
时间片轮转调度算法主要适用于分时系统。在这种算法中,系统将所有就绪进程按到达时间的先后次序排成一个队列,进程调度程序总是选择就绪队列中的第一个进程执行,即先来先服务的原则,但是仅能运行一个时间片。在使用完一个时间片后,即使进程并未完成其运行,它也必须释放出(被抢占)处理机给下一个就绪的进程,而被抢占的进程返回到就绪队列的末尾重新排队,等候再次运行。
在时间片轮转的调度算法中,时间片的大小对系统性能有很大影响。如果时间片足够大,以至于所以进程都能在一个事件内执行完毕,则时间片轮转调度算法就退化成FCFS算法。如果时间片很小,则处理机将在进程间过于频繁地切换,使得处理机开销增大,而真正用于运行用户进程的时间将减少。因此,时间片的选择要适当,可以根据系统响应时间、就绪队列中的进程数目和系统的处理能力等决定。
多级反馈队列调度算法//
多级反馈队列调度算法是时间片轮转算法和优先级调度算法的综合与发展。通过动态调整进程优先级和时间片的大小,多级反馈队列调度算法可以兼顾多方面的系统目标。例如,为了提高系统吞吐量和缩短平均周转时间而照顾短进程;为了获得较好的I/O设备利用率和缩短响应时间而照顾I/O型进程;同时,也不必实现估计进程的执行时间。
多级反馈队列调度算法的实现思想如下:
(1)设置多个就绪队列,并为各个队列赋予不同的优先级,第一级队列的优先级最高,第二级队列次之,其余队列的优先级逐次降低。
(2)赋予各个队列中进程执行时间片的大小各不相同。在优先级越高的队列中,每个进程的运行时间片越小。例如,第二级队列的时间片要比第一级队列的时间片长一倍…第i+1级队列的时间片要比第i级队列的时间片长一倍。
(3)当一个新的进程进入内存后,首先将它放入第一级队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时候,如果它能在时间片内完成,便可准备撤离系统;若它在一个时间片结束时尚未完成,调度程序便将该进程转入第二级末尾,再同样按FCFS原则等待调度执行;若它在第二级队列中运行一个时间片后仍未完成,再以同样的方法进入第三级队列…如此下去,当一个长进程从第一级队列一次降到第n级队列后,在第n级队列中便采用时间片轮转方式进行。
(4)仅当第一级队列为空时,调度程序才调度第二级队列中的进程进行;仅当第1到(i-1)级队列均为空,才会调度第i级队列中的进程运行。若处理机正在执行第i级队列中的某个进程,此时又有新的进程进入优先级较高的队列[第1到(i-1)级的任意一级],则此时行进程将抢占正在运行的处理机,即由调度程序把正在运行的进程放回第i级队列末尾,把处理机分配给新到的更高优先级进程。
这种调度方法优势如下:
(1)终端型作业用户:短作业优先。
(2)短批处理作业用户:周转时间较短。
(3)长批处理作业用户:经过前面几个队列得到部分执行,不会饿死。
//进程通信
共享存储
*共享数据结构
*共享存储区
消息传递
*直接通信
*间接通信
共享存储区与间接通信(广播信箱)的异同
*后者是通过os提供的原语进行操作的
*前者是通过直接操作内存
管道通信
*管道(内存中的文件,缓冲区)
*半双工通信
//进程同步
前提
两种相互制约形式
*间接相互制约关系:互斥排他性
*直接相互制约关系:进程间的合作,管道通信
进程同步(合作)互斥(一个进程访问)访问临界资源的机制
1:信号量机制(pv操作)(也叫信号灯,对等待的进程进行通知)
*p操作:wait原语,进程等待
*v操作:signal原语,唤醒等待进程
会发生忙等(整形信号量),优化:进入阻塞(利用记录型信号灯)
信号量有整形信号量,记录型信号量
记录型信号量为一个带value显示的队列
可以理解为数据结构
当占用资源的进程释放完资源后,会唤醒等待队列队首的进程
信号灯数量加一,队首的进程继续执行signal原语,直到进程全部唤醒
信号灯数量变为一,可以被队首进程占用
实际工作signalall,直接唤醒全部
死锁避免:安全性算法
1:根据进程要求的最大资源数量和管程的资源可用数量分配资源
达成是否安全的判断
死锁的检测:根据资源分配图,死锁定理,当且仅当当前资源分配图是不可完全简化的
简化过程和拓扑排序类似,即是找出不是孤点(孤点没有入边和出边),又不阻塞
最后结果是所有节点都变成孤点
//拓扑排序
将节点的入边和出边定义出来
将没有入边的节点依次拿出,进行排序
进程的执行
1:静态链接
在执行程序与静态库函数一起加载,调用库的函数,将两者结合成一个可执行文件的集合
2:动态链接
根据对外部库的依赖,在程序运行时加载库的函数
静态链接的优缺点
静态链接的缺点很明显,一是浪费空间,因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本;另一方面就是更新比较困难,因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。
//动态链接的优缺点
动态链接的优点显而易见,就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;另一个优点是,更新也比较方便,更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。但是动态链接也是有缺点的,因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。
据估算,动态链接和静态链接相比,性能损失大约在5%以下。经过实践证明,这点性能损失用来换区程序在空间上的节省和程序构建和升级时的灵活性是值得的。
在装入过程中将所有动态链接库载入内存。 应用程序在运行时,将所有可能要运行到的模块都全部装入内存。 显然这比运行时动态链接低效很多,因为往往会有些目标模块根本就不运行。 比较典型的例子是作为错误处理用的目标模块,如果程序在整个运行过程中都不出现错误,则显然就不会用到该模块。
//动态装入与动态链接区别,(装入整个库)
在装入过程中将所有动态链接库载入内存。 应用程序在运行时,将所有可能要运行到的模块都全部装入内存。 显然这比运行时动态链接低效很多,因为往往会有些目标模块根本就不运行。 比较典型的例子是作为错误处理用的目标模块,如果程序在整个运行过程中都不出现错误,则显然就不会用到该模块。
//原理
图片.jpg
//马士兵
程序的装入
1:绝对装入(指定装入的内存地址,适合单道程序,保证地址没被占用)
2:可重定位装入
根据程序中的数据指定内存中的最小地址,指定为0,初始地址,往后计算大小
(适用于多道程序)
3:动态运行时装入:
先将部分数据装入到内存,再一边运行一边装入。内存分配较乱,内存管理不便
//链接程序:将所有的代码和库封装在一起,形成一个可装入模块
//编译:是由用户完成的,不属于os的操作
静态链接的特点:在完成装入程序之前,链接程序就已经完成了链接
(适合比较小的程序)
如果装入的程序比较大,就可以边链接边装入,一边由装入程序装入已链接的模块
一边由链接程序链接编译好的库和二进制文件,减轻链接程序的压力,实现两个程序
并行,这就装入时动态链接,例如,下载游戏
逻辑地址和物理地址的区别
逻辑地址是中央处理系统从程序角度生成的对象的地址,而物理地址是内存管理单元计算的对象的实际地址。
内存保护:内存保护的主要目的是防止某个进程去访问不是操作系统配置给它的寻址空间。这个机制可以防止某个进程,因为某些程序错误或问题,而有意或无意地影响到其他进程或是操作系统本身的运行状态和数据。
内存扩充
1:覆盖技术的思想:将程序分为多个段(多个模块)。
常用的段常驻内存,不常用的段在需要时调入内存。
内存中分为一个“固定区”和若干个“覆盖区”。
需要常驻内存的段放在“固定区”中,调入后就不再调出(除非运行结束)
不常用的段放在“覆盖区”,需要用到时调入内存,用不到时调出内存
必须由程序员声明覆盖结构,操作系统完成自动覆盖。缺点:对用户不透明,增加了用户编程负担。
覆盖技术只用于早期的操作系统中,现在已成为历史。
2:
交换(对换)技术的设计思想:内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中
某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)
中级调度(内存调度),就是要决定将哪个处于挂起状态的进程重新调入内存。
暂时换出外存等待的进程状态为挂起状态(挂起态,suspend)
挂起态又可以进一步细分为就绪挂起、阻塞挂起两种状态。
具有对换功能的操作系统中,通常把磁盘空间分为 文件区 和 对换区 两部分。
文件区 主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;
对换区 空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式(学过文件管理章节后即可理解)。总之, 对换区的I/O速度比文件区的更快 。
2. 什么时候应该交换?
交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;
如果缺页率明显下降,就可以暂停换出。
3. 应该换出哪些进程?
可优先换出阻塞进程;可换出优先级低的进程;为了防止优先级低的进程在被调入内存后很快又被换出,有的系统还会考虑进程在内存的驻留时间…(注意:PCB 会常驻内存,不会被换出外存)
连续分配管理方式
1:单一连续分配
单一连续分区分配方式:是一种最简单的 存储管理 方式 ,在早期的单道 批处理系统 的 小型机 中常使用这种管理方案。 采用这种管理方案时,内存被分成两个区域,一个是系统区域,仅供操作系统使用,可以驻留在内存的低地址部分,也可以驻留在高地址部分 (通常设置在内存的低端);另一个是用户区,它是除系统区以外的全部内存区域,这部分区域是提供给用户使用的区域,任何时刻 主存储器 中最多只有一个作业。 所以,单一连续区 存储管理 只适用于单用户的情况。
单一连续分配方式适合单道程序、采用静态重定位的主存分配,可采用覆盖技术,不需要额外的硬件支持,但无法实现多道程序共享主存。
不一定需要内存保护
2:固定分区分配
固定分区分配是最简单的一种多道程序存储管理方式,它将用户内存空间划分为若干个固定大小的区域,每个分区只装入一道作业。 当有空闲分区时,便可以再从外存的后备作业队列中,选择适当大小的作业装入该分区,如此循环。
3:动态分区分配
又称可变分区分配,它是在进程装入内存时,根据进程的实际需要,动态地为之分配内存,并使分区的大小正好适合进程的需要。
(非连续内存分配)四种动态分区分配算法
、四种分配算法原理
1.首次适应算法(First Fit)
将空闲分区链以地址递增的顺序连接;在进行内存分配时,从链首开始顺序查找,直到找到一块分区的大小可以满足需求时,按照该作业的大小,从该分区中分配出内存,将剩下的空闲分区仍然链在空闲分区链中。(按低地址查找)
2.循环首次适应算法(Next Fit)
分配内存时不是从链首进行查找可以分配 内存的空闲分区,而是从上一次分配内存的空闲分区的下一个分区开始查找,直到找到可以为该进程分配内存的空闲分区;(邻近适应算法,找到旁边的,从旁边开始找)
3.最佳适应算法(Best Fit)
将空闲分区链中的空闲分区按照空闲分区由小到大的顺序排序,从而形成空闲分区链。每次从链首进行查找合适的空闲分区为作业分配内存,这样每次找到的空闲分区是和作业大小最接近的,所谓“最佳”。(按容量从小到大)
4.最坏适应算法(Worst Fit)
与最佳适应算法刚好相反,将空闲分区链的分区按照从大到小的顺序排序形成空闲分区链,每次查找时只要看第一个空闲分区是否满足即可(按容量从大到小)会有很多的内部碎片
优点是容易查找
https://blog.youkuaiyun.com/weixin_43886592/article/details/107581653 网址
//内存回收
内存回收指的是对用户空间中的堆段和文件映射段进行回收(用户使用 malloc、mmap 等分配出去的空间)。 用户可以手动地使用 free () 等进行内存释放。
每次回收一个分区,就要更新分区表,回收的当前分区的号码前后如果都为空闲资源,那么
就合并前面,现在,后面号码的内存资源,整合成一个大的内存分区
//内存管理的意义:提高内存利用率,cpu访问速度
//基本分页存储管理方式
分页存储管理的基本概念(本质是上面的固定分区)
将内存空间分为一个个大小相等的分区(比如:每个分区4KB),每个分区就是一个“页框”,或称“页帧”、“内存块”、“物理块”。每个页框有一个编号,即“页框号”(或者“内存块号”、“页帧号”、“物理块号”)页框号从0开始。
将用户进程的地址空间也分为与页框大小相等的一个个区域,称为“页”或“页面”。每个页面也有一个编号,即“页号”,页号也是从0开始。
(注:进程的最后一个页面可能没有一个页框那么大。因此,页框不能太大,否则可能产生过大的内部碎片)
操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。也就是说,进程的页面与内存的页框有一一对应的关系。各个页面不必连续存放,也不必按先后顺序来,可以放到不相邻的各个页框中
//页表,存在于pcb中用于逻辑地址和物理地址的关系映射
如同一个一维数组,块号相当于数组的元素,页号相当于数组内元素的索引
所以说页式管理中数组空间是一维的;
cpu通过页表寄存器来读取页表信息,操作内存
//如何计算它们的地址,页号呢?
根据基本地址变换机构(硬件实现)
公式:
物理地址=(页号->块号)+偏移量
页号p=逻辑地址A/页面长度大小(4096)(固定值)
偏移量W=逻辑地址A%页面长度L(4096)
P=A>>12;W=A&4095
逻辑地址再内存中做位运算,得到页号,交给cpu,再由页表寄存器访问页表,得到块号,cpu再去访问,这一个读取消耗了两次访问,而访问物理地址的过程是必不可少的,可以从减少页表的访问入手,因此引出快表,块表是将页表放置在高速缓存区,其读速度快,自此cpu得到页号以后,直接访问快表,快表再拿块号到内存做基本地址变换(偏移量),得到物理地址空间
//问:为什么不放在页表寄存器中::其空间小
//相对于内存,高速缓存的物理空间也很小,运算速度很快,所以对于多次访问的页号,就放入快表中去(局部性原理),cpu先访问快表,快表没有就访问慢表
快表数组不是一维的,因为不是递增增加的
如果在快表中找到了,就叫 命中 。
一个页表支持4个G的内存空间,每一个块4k内存空间
一个页表本身最多占用4M内存空间
二级页表(增加搜索效率)(索引思想)
1:将逻辑地址拆分成三部分
2:从pcb中查出2级页表的位置
3:根据一级页号查出二级页表的位置
4:根据二级页号查内存块号,加偏移量计算物理地址
//基本分段存储管理方式
段与段内数据地址之间是没有联系的
都是从零开始分配逻辑地址
段外,段与段之间分配的地址是连续的
//段表(二维)
除了段号,还有段长和基址(编译器)
1:分段
2:段表
3:地址变换机构
4:段的共享与保护(其它程序需要访问某一存在的段址,在自身的pcb中存入一个对应的段表即可)
拓展
外部碎片:
可变分区存储管理可能产生外部碎片
在可变分区存储管理中,主存中的分区不是事先划分好的,而是在主存空间充足时,根据该作页需要的空间大小分配一个分区给他,实现了作业大小等于分区大小,解决了内部碎片的问题.
那么作业大小等于分区大小为什么还会产生碎片呢?
例子:
我们可以看到,上图的四个分区中有三个正在被使用,一个未被使用,那么当我们要装入大小为12KB的作业时,系统会为我们在15KB的空闲区中划分出一个12个KB大小的分区,这样这个15KB大小的分区就变成了一个12KB大小的分区和一个3KB大小的分区,12KB大小的分区被作业占用了,3KB大小的分区因为太小所以能容纳的作业太少,所以被占用的可能性也很小,很多个这种小的内存空间长时间不被占用就是一种对内存空间的浪费,我们称之为外部碎片
//段页式管理方式
1:先分段,再分页
2:一个进程-》一个段表
3:一个段表项-》一个页表
4:一个页表-》多个物理块
cpu访问流程:
1段表地址+段号找到段表项(逻辑地址给cpu->段表寄存器,其存放了起始地址)
2根据页表长度检查页号越界情况
3页表地址+页号找到页表项
页表地址就是段页表中记录的页表存放所在的块号
4内存块号+页内地址得到物理地址
//概述一下,动态分区是进程间的分配,分段内存管理是进程内的分配。两者的分配方式十分相似。动态分区是进程间的,一个进程的所有代码作为一块存储,中间没有其他进程的代码捣乱,因此是连续分配;分段式内存管理将
动态分区是按需分配,开始时内存能充分利用,但当不停有进程被换入、换出时会形成很多进程间的内存碎块,称为外部碎片。
分段是将进程划分成若干个段,但段的长度不必相等,一个进程可以有程序段、数据段和堆栈段,这些段在内存中的地址可以不连续,分段有点类似于动态分区。分段消除了主存的内部碎片,但会产生外部碎片。一个进程的代码按逻辑分为多个段,多个段被在内存中不同的位置,所以是非连续分配。
//连续内存分配管理方式和非连续内存分配管理方式
连续:对进程分配
1:单一连续,占整个内存
2:固定连续,内部碎片
3:动态连续,进程间的外部碎片
非连续:进程内部分配
1:页表
2:段表
3:段页表
以上三个都是一个进程对于一个表1,2,3
基本内存分页管理是一次性把进程装入到内存中
请求分页管理是动态装入,先装入核心的,有需要再向磁盘(操作系统)中请求资源
虚拟内存
概念:具有请求调入和置换功能,从逻辑上对内存容量加以扩充的一种存储器系统
虚拟内存包括除cpu外的存储资源
//局部性原理
1:时间局部性
对同一数据的多次操作,例如循环操作同一数据
2:空间局部性
对同一地址多次访问,例如访问同一数组的各个元素多次
//催生缓存
虚拟内存的特征
1:多次性
2:对换性(原本进程常驻内存,对换性要求:不用则移出内存,即挂起)
3:虚拟性(逻辑上扩大内存的容量)