操作系统
内核:
管理进程线程进行调度
管理内存
管理硬件设备
提供系统调用(文件、网络等)
CPU两种运行状态:用户态、系统态(内核态)。
用户态的进程可以使用用户程序的数据,而系统态的进程可以使用计算机几乎所有资源
系统调用:
用户态的进程在涉及文件管理、内存管理、设备管理、进程控制等系统态级别操作时,必须通过系统调用 ,由操作系统代为完成。
虚拟地址空间:CPU通过MMU将虚拟地址映射为物理地址。
通过虚拟地址访问内存有以下优势:
- 程序可以使用连续虚拟地址来访问物理内存中非连续的空间。
- 程序可以使用大于物理内存的虚拟内存。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
- 不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
内存分段:段是逻辑信息的单位
逻辑内存中存储段选择因子和段偏移量,操作系统通过段选择因子索引到段表,将虚拟内存映射至物理内存。
问题:会产生内存碎片,而且内存交换效率低。
内存交换:将需要挪动的内存段读取到硬盘,然后再从硬盘中读取该内存段到新的区域。
内存分页:
将逻辑内存和物理内存都分为固定大小的页/帧(解决内存碎片),二者通过页表映射。把最近没有使用过的内存页写到硬盘上(释放
单页表问题:页表占用内存
多级页表:空间结构的优化
一级页表覆盖所有内存空间,但不必创建没有使用到的二级页表,需要时创建即可。
而且根据局部性原理,二级页表可以存储在磁盘中。
局部性原理:
- 时间局部性 :如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。
- 空间局部性 :一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,这是因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。
时间局部性是通过将近来使用的指令和数据保存到高速缓存存储器中,并使用高速缓存的层次结构实现。空间局部性通常是使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。虚拟内存技术实际上就是建立了 “内存一外存”的两级存储器的结构,利用局部性原理实现髙速缓存。
快表TLB:将经常使用到的页表存储在高速缓存,在进行地址映射时,先在缓存中寻找,然后再去内存中寻找。
分页分段区别:
- 共同点 :
- 分页机制和分段机制都是为了提高内存利用率,减少内存碎片。
- 页和段都是离散存储的,所以两者都是离散分配内存的方式。但是,每个页和段中的内存是连续的。
- 区别 :
- 页的大小是固定的,由操作系统决定;而段的大小不固定,取决于我们当前运行的程序。
- 分页仅仅是为了满足操作系统内存管理的需求,而段是逻辑信息的单位,在程序中可以体现为代码段数据段,能够更好满足用户的需要。
虚拟内存:
虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。为了更好的管理内存,操作系统将内存抽象成地址空间。每个进程拥有私有连续地址空间,这个地址空间被分割为固定大小的页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。用页面置换的时间来换取比物理内存更大的空间。
请求分页/分段存储管理:当内存已满,将页面/段腾出至外存。若发现要访问的页面/段不在内存中,产生缺页中断 ,由CPU告知操作系统,将页面/段调入内存。
请求分页存储管理是基于内存分页机制上的,请求分页存储管理可以将内存的部分页腾到外存中,而内存分页机制本身不提供这种操作,仅仅将内存进行分页处理。因此,内存分页机制本身是不能实现虚拟内存的。
页面置换:在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。
- LRU(最近最久未使用)
维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最久未访问的。
- FIFO、第二次机会、时钟:基于FIFO,但增加标识位R,若一个页面既老且R=0,则置换出去。若R=1,则将r改为0,并置入队头。时钟使页面链表变成循环链表,这样就不用挪动页面到队头,而只需要指针。
同步异步: 指的是消息通知机制。A线程交给B线程的任务,同步是A需要自己轮询查看B是否完成了这项任务,而异步是B在完成这项任务后会通知A。
阻塞/非阻塞:指的是线程是否被挂起。以下载东西为例:同步就是指下载完没有任何提示,得自己去看是否下载完。而阻塞是指在等下载东西的时候,自己是干等着还是去做别的事情
下载东西 |
同步 |
异步 |
阻塞 |
眼睛盯着下载条啥也不干 |
啥也不干,但是下载完会通知 |
非阻塞 |
去做别的事情,但是偶尔去看一眼下载完没有 |
去做别的事情,下载完会通知 |
进程调度:
内核运行调度的条件:
一个进程被终结or一个进程从running切换到waiting
非抢占的内核:
进程在执行系统调用时,在内核中,不会出现抢占。也有抢占的内核。
调度算法:
- 先到先服务(FCFS)调度算法 : 从就绪队列中选择一个最先进入该队列的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。
会产生队头阻塞,平均周转时间和平均等待时间大、平均等待时间波动大、非抢占式。
- 短作业优先(SJF)的调度算法 : 从就绪队列中选出一个估计运行时间最短的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。也有可以抢占正在占用当前CPU时间片的算法(SRT)。
平均等待时间最小。但会使长任务长时间得不到处理。需要预估运行时间。
- 最高响应比优先:SJF的改进。R=(w+s)/s优先。
- 时间片轮转调度算法 : 时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法,又称 RR(Round robin)调度。每个进程被分配一个时间片。上下文切换过于频繁。
- 多级反馈队列 :分为多级队列,每个队列按优先级决定先后,若当前进程在当前队列中没有执行完,则被交给下一级队列的队尾。进程的特征会改变其队列位置(反馈)
- 优先级调度 : 为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。
进程的状态:
- 创建状态(new) :进程正在被创建,尚未到就绪状态。
- 就绪状态(ready) :进程已处于准备运行状态,即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源(处理器分配的时间片)即可运行。
- 运行状态(running) :进程正在处理器上上运行(单核 CPU 下任意时刻只有一个进程处于运行状态)。
- 阻塞状态等待状态(waiting) :进程正在等待某一事件而暂停运行如等待某资源为可用或等待 IO 操作完成。即使处理器空闲,该进程也不能运行。
- 结束状态(terminated) :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行。
进程间通信:
- 匿名管道(Pipes) :用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。在内存中。
- 有名管道(Names Pipes) : 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循FIFO,以磁盘文件的方式存在,可以实现本机任意两个进程通信。管道只能承载无格式字节流
- 信号(Signal) :软中断,用于通知进程某个事件已经发生,用于后续处理(或者不处理)。不能传数据。
- 消息队列(Message Queuing) :消息队列是消息的链表,具有特定的格式(而不是字节流),消息队列存放在内核中,只有在内核重启或者显式地删除,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,不一定被动接受(如FIFO),也可以按消息的类型有选择的读取.。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 信号量(Semaphores) :信号量是一个计数器,信号量的意图是多进程间同步。P:信号量-1,若<0,则让调用P的进程等待。V:信号量+1,若<=0, 说明存在等待进程,选择一/几个唤醒(通常使用FIFO)。二进制信号量,类似互斥量、锁机制。
- 共享内存(Shared memory) :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。
- Sockets: TCP
进程死锁条件与线程死锁一样。
进程死锁的解决:鸵鸟策略(不管)、预防、避免、检测解除
死锁预防:破坏死锁条件。死锁预防条件太多开销太大
避免:不破坏死锁条件。进行资源预分配,若会产生不安全状态,则拒绝分配。银行家算法。开销略大
检测解除:根据资源请求与分配关系建立有向图,若图中的环无法消除,则存在死锁
解除:杀死死锁进程、回滚(撤销资源分配)、抢占(强行分配)
Select, poll, epoll: