一、操作系统基础
1. 启动过程
- BIOS在主板的一个rom空间里面,cpu启动会优先调用bios
- bios是不可改的,一般出厂就写好的
- bios从硬盘读取bootloader来交换控制权
- bootloader为硬盘的第一扇区(引导扇区),512字节
1
BIOS->bootloader->OS
2. 中断、异常和系统调用
2.1. 内核
- 内核是完全被信任的
- 内核防止上层程序破坏操作系统
- 内核操作硬件
- 只有内核可以执行特权指令
- 内核给上层提供接口
2.2. 中断
- 来自于硬件设备的中断
- 中断是异步的,不确定产生时间
- 对应用程序透明
- 每个内部外部事件都会设置一个中断标记给cpu识别,每个中断事件都有一个id
- 中断出现后,需要保留当前软件状态,处理中断服务,恢复软件状态继续执行
2.3. 异常
- 来自于应用程序的非法指令或者坏的处理状态
- 异常是同步的,程序运行时的异常
- 需要对应用程序进行处理,杀死或者重新执行
- 异常也是有异常编号,需要保留现场,对异常处理(杀死或者重新执行),恢复现场
- 这里的异常是操作系统中执行系统调用时产生的异常
2.4. 系统调用
- 来源于应用程序向操作系统发起的服务请求
- 系统调用是异步或同步,返回结果可以异步返回
- 等待服务处理完后持续执行下一个处理
- 系统调用其实就是操作系统给应用程序提供的一套api接口
api接口列举
- win32 api: windows专用
- posix api: 用于
POSIX-based system
- 包括unix、linux、mac os x所有版本
- java api: java虚拟机使用(jvm)
用户态和内核态
- 用户态特权级别很低
- 内核态可以完全控制整个计算机系统
- 系统调用就是将用户态应用程序转成内核态
- 函数调用是进程内的栈空间处理,系统调用需要从用户态的堆栈到内核态的堆栈
- 用户态转用户态之间的开销,为了安全
跨越系统边界开销
- 建立中断/异常/系统调用号与对应服务的映射关系(初始化)
- 建立内核堆栈
- 验证系统调用参数,防止异常调用
- 用户态到内核态之间的映射关系
- 内核态独立地址空间
3. 连续内存分配
3.1. 内存分层体系
- 内存分层为金字塔型
- CPU中寄存器内有L1缓存最快
- CPU外层有一个L2缓存稍快
- 主存就是内存条中的内存
- 硬盘上的交换分区就是虚拟内存,最慢
为什么要有逻辑地址空间
- 进程间不用关心内存映射,管理自己的空间
- 进程间地址空间独立,保护进程内内存空间
- 虚拟化,可以使用更多的内存,不用担心是来自硬盘还是内存条
- 可以进程间访问共享内存
3.2. 地址空间 & 地址生成
- 所有逻辑地址空间都会映射到物理地址空间的地址上
- 逻辑地址和物理地址之间的映射表是操作系统管理的
- 操作系统会建立维护一个映射和界限表,防止进程内存访问异常
指令运行过程
- cpu加载指令到ALU(计算逻辑单元)
- 根据指令需要的内存逻辑地址,找mmu模块找地址
- mmu模块根据mmu表查找逻辑地址对应的物理地址空间
- 查找物理地址空间加载数据给ALU处理
内存安全检查
- cpu执行时如果发现访问的逻辑地址超出界限,会抛出一个内存访问异常
- 操作系统需要根据这个异常进行处理
3.3. 内存碎片和内存动态分配
- 内存碎片分为外碎片和内碎片
内存分配算法
- 第一匹配分配: 找到内存区域中第一个符合要求的内存块分配
- 优点: 简单、快速、可以产生更多更大的内存空间
- 缺点: 容易出现很多的内存碎片块
- 最优分配: 找到内存区域中最合适分配的内存块
- 优点: 避免拆分大的空闲块,最小化外碎片的尺寸,比较适合小的内存分配
- 缺点: 会出现跟多小的外碎片块无法使用,需要对空闲块排序,需要考虑合并
- 最差分配: 找到内存中最大的内存块进行分配
- 优点: 可以拆分大的空闲块,避免产生更小的内存碎片
- 缺点: 需要考虑合并和排序,可能导致更大的内存申请无法分配
3.4. 压缩式和交换式碎片整理
压缩式碎片整理
- 重置程序合并孔洞
- 需要所有程序是动态可重置的
- 需要在程序没有调用cpu时处理
- 需要频繁拷贝,会导致开销很大
交换式碎片整理
- 程序需要更多地内存,没有那么多内存
- 抢占程序,将被抢占程序的占用内存放到硬盘上
4. 非连续内存分配
4.1. 分段
连续内存分配缺点
- 分配的物理内存连续
- 内存利用率底
- 有外碎片和内碎片的问题
非连续内存分配的优点
- 程序的物理地址空间不连续
- 内存利用管理更好
- 允许共享代码和数据
- 动态加载和动态链接
非连续内存分配的缺点
- 需要考虑虚拟地址和物理地址的转换
- 硬件实现(快): 分段、分页
- 软件实现(开销大)
分段
- 分段地址分为
段号+偏移
- 段表里面记录
段号=>物理地址+段长
- 段是有长度限制的,cpu会判断长度限制,超出抛内存异常
- 段表是操作系统设置和维护的
4.2. 分页
- 将物理内存划分为固定大小的帧
- 将逻辑地址空间分为固定大小的页
- 转换逻辑地址到物理地址(页到帧)
- 页表
- MMU/TLB
- 物理地址分为
帧号+偏移
- 虚拟地址分为
页号+偏移
- 页大小和帧大小一致
- 页表存的是
页号=>帧号
,页表还有一个基地址 - 页表也是操作系统设置维护的
页寻址机制
- 页映射到帧
- 页是连续的虚拟内存
- 帧是非连续的物理内存
- 不是所有的页都有对应的帧
5. 系统状态 睡眠/休眠/关机
- 睡眠: 内存保持上电,电脑不运行,仅内存耗电。开机可快速恢复
- 休眠: 内存写入磁盘,电脑不运行,可以断电。开机恢复较慢,需要从硬盘写回内存
- 关机: 内存丢失,电脑不运行。开机一切重新开始
二、变量内存分配方式
内存分配有三种:静态存储区、堆区、栈区。
- 静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。
- 栈区:在执行函数时,函数(包括main函数)内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。(任何变量都处于站区,例如int a[] = {1, 2},变量a处于栈区。数组的内容也存在于栈区。)
- 堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,并立即将指针置位NULL,防止产生野指针。
三、链接库
1. 静态库
- 编译时将会编译进可执行文件,运行时不依赖
- 占用内存高,运行时会全部加载
2. 动态库
- 编译时链接,用时依赖
- 多个进程同时需要链接,只会加载一份
四、虚拟内存
虚拟内存本身出现是因为内存不够用,需要使用磁盘空间代替内存进行使用,所以出现了虚拟内存。同时使用了虚拟内存在进程间可以实现隔离,算是附带好处,不过被使用的很普遍。
1. 技术介绍
虚拟内存别称虚拟存储器(Virtual Memory)。电脑中所运行的程序均需经由内存执行,若执行的程序占用内存很大或很多,则会导致内存消耗殆尽。为解决该问题,Windows中运用了虚拟内存 [2] 技术,即匀出一部分硬盘空间来充当内存使用。当内存耗尽时,电脑就会自动调用硬盘来充当内存,以缓解内存的紧张。若计算机运行程序或操作所需的随机存储器(RAM)不足时,则 Windows 会用虚拟存储器进行补偿。它将计算机的RAM和硬盘上的临时空间组合。当RAM运行速率缓慢时,它便将数据从RAM移动到称为“分页文件”的空间中。将数据移入分页文件可释放RAM,以便完成工作。 一般而言,计算机的RAM容量越大,程序运行得越快。若计算机的速率由于RAM可用空间匮乏而减缓,则可尝试通过增加虚拟内存来进行补偿。但是,计算机从RAM读取数据的速率要比从硬盘读取数据的速率快,因而扩增RAM容量(可加内存条)是最佳选择。
虚拟内存是Windows 为作为内存使用的一部分硬盘空间。虚拟内存在硬盘上其实就是为一个硕大无比的文件,文件名是PageFile.Sys,通常状态下是看不到的。必须关闭资源管理器对系统文件的保护功能才能看到这个文件。虚拟内存有时候也被称为是“页面文件”就是从这个文件的文件名中来的。
内存在计算机中的作用很大,电脑中所有运行的程序都需要经过内存来执行,如果执行的程序很大或很多,就会导致内存消耗殆尽。为了解决这个问题,WINDOWS运用了虚拟内存技术,即拿出一部分硬盘空间来充当内存使用,这部分空间即称为虚拟内存,虚拟内存在硬盘上的存在形式就是 PAGEFILE.SYS这个页面文件。
2. 调度介绍
2.1. 页式调度
1、页式虚存地址映射页式虚拟存储系统中,虚地址空间被分成等长大小的页,称为逻辑页;主存空间也被分成同样大小的页,称为物理页。相应地,虚地址分为两个字段:高字段为逻辑页号,低字段为页内地址(偏移量);实存地址也分两个字段:高字段为物理页号,低字段为页内地址。通过页表可以把虚地址(逻辑地址)转换成物理地址。
在大多数系统中,每个进程对应一个页表。页表中对应每一个虚存页面有一个表项,表项的内容包含该虚存页面所在的主存页面的地址(物理页号),以及指示该逻辑页是否已调入主存的有效位。地址变换时,用逻辑页号作为页表内的偏移地址索引页表(将虚页号看作页表数组下标)并找到相应物理页号,用物理页号作为实存地址的高字段,再与虚地址的页内偏移量拼接,就构成完整的物理地址。现代的中央处理机通常有专门的硬件支持地址变换。
2、转换后援缓冲器由于页表通常在主存中,因而即使逻辑页已经在主存中,也至少要访问两次物理存储器才能实现一次访存,这将使虚拟存储器的存取时间加倍。为了避免对主存访问次数的增多,可以对页表本身实行二级缓存,把页表中的最活跃的部分存放在高速存储器中,组成快表。这个专用于页表缓存的高速存储部件通常称为转换后援缓冲器(TLB)。保存在主存中的完整页表则称为慢表。
3、内页表是虚地址到主存物理地址的变换表,通常称为内页表。与内页表对应的还有外页表,用于虚地址与辅存地址之间的变换。当主存缺页时,调页操作首先要定位辅存,而外页表的结构与辅存的寻址机制密切相关。例如对磁盘而言,辅存地址包括磁盘机号、磁头号、磁道号和扇区号等。
2.2. 段式调度
段是按照程序的自然分界划分的长度可以动态改变的区域。通常,程序员把子程序、操作数和常数等不同类型的数据划分到不同的段中,并且每个程序可以有多个相同类型的段。在段式虚拟存储系统中,虚地址由段号和段内地址(偏移量)组成。虚地址到实主存地址的变换通过段表实现。每个程序设置一个段表,段表的每一个表项对应一个段。每个表项至少包含下面三个字段:
- 有效位:指明该段是否已经调入实存。
- 段起址:指明在该段已经调入实存的情况下,该段在实存中的首地址。
- 段长:记录该段的实际长度。设置段长字段的目的是为了保证访问某段的地址空间时,段内地址不会超出该段长度导致地址越界而破坏其他段。段表本身也是一个段,可以存在辅存中,但一般驻留在主存中。
段式虚拟存储器有许多优点:
- 段的逻辑独立性使其易于编译、管理、修改和保护,也便于多道程序共享。
- 段长可以根据需要动态改变,允许自由调度,以便有效利用主存空间。
段式虚拟存储器也有一些缺点:
- 因为段的长度不固定,主存空间分配比较麻烦。
- 容易在段间留下许多外碎片,造成存储空间利用率降低。
- 由于段长不一定是2的整数次幂,因而不能简单地像分页方式那样用虚地址和实地址的最低若干二进制位作为段内偏移量,并与段号进行直接拼接,必须用加法操作通过段起址与段内偏移量的求和运算求得物理地址。因此,段式存储管理比页式存储管理方式需要更多的硬件支持。
2.3. 段页式调度
段页式虚拟存储器是段式虚拟存储器和页式虚拟存储器的结合。实存被等分成页。每个程序则先按逻辑结构分段,每段再按照实存的页大小分页,程序按页进行调入和调出操作,但可按段进行编程、保护和共享。它把程序按逻辑单位分段以后,再把每段分成固定大小的页。程序对主存的调入调出是按页面进行的,但它又可以按段实现共享和保护,兼备页式和段式的优点。缺点是在映象过程中需要多次查表。在段页式虚拟存储系统中,每道程序是通过一个段表和一组页表来进行定位的。段表中的每个表目对应一个段,每个表目有一个指向该段的页表起始地址及该段的控制保护信息。由页表指明该段各页在主存中的位置以及是否已装入、已修改等状态信息。如果有多个用户在机器上运行,多道程序的每一道需要一个基号,由它指明该道程序的段表起始地址。虚拟地址格式如下:
1 | 基号 段号 页号 页内地址 |
3. 变换算法
虚拟存储器地址变换基本上有3种形虚拟存储器工作过程式:全联想变换、直接变换和组联想变换。任何逻辑空间页面能够变换到物理空间任何页面位置的方式称为全联想变换。每个逻辑空间页面只能变换到物理空间一个特定页面的方式称为直接变换。组联想变换是指各组之间是直接变换,而组内各页间则是全联想变换。替换规则用来确定替换主存中哪一部分,以便腾空部分主存,存放来自辅存要调入的那部分内容。常见的替换算法有4种。
- 随机算法:用软件或硬件随机数产生器确定替换的页面。
- 先进先出:先调入主存的页面先替换。
- 近期最少使用算法(LRU,Least Recently Used):替换最长时间不用的页面。
- 最优算法:替换最长时间以后才使用的页面。这是理想化的算法,只能作为衡量其他各种算法优劣的标准。
虚拟存储器的效率是系统性能评价的重要内容,它与主存容量、页面大小、命中率,程序局部性和替换算法等因素有关。
五、io模型
名词解释
AIO
: 异步ioBIO
: 阻塞ioNIO
: 非阻塞io
1. 同步io
- 指读写io的操作是在同一个线程
- linux不存在异步io,epoll和select都属于同步io
1.1. 阻塞io accept
- 正常讲的accept属于阻塞io,读写io需要等待
- 就算每个链接新开线程,同样是同步io,应用层异步而已
1.2. 非阻塞io select
- select属于典型的非阻塞io,可以在没有数据时立刻返回
- 但是由于读写在同一个线程,属于同步非阻塞io
1.3. 多路复用 epoll
- select处理是针对单个fd进行,如果想处理多个fd,需要自己写循环,耗时
- epoll的调用是注册fd到操作系统,操作系统发现某个fd存在数据,直接返回fd,继续进行操作,更加节省性能
- 但是由于读写在同一个线程,属于同步非阻塞io
2. 异步io
- windows上是异步io实现,系统读取完数据调用回调函数
六、cpu介绍
1. 不同型号cpu
1.1. x86
1) 实模式/保护模式/长模式
- 实模式: 16bit,可以调用bios内置函数
- 保护模式: 32bit,使用GDT来进行分段管理内存
- 长模式: 64bit,使用mmu进行分页管理内存
2. cpu缓存
- L1和L2每个cpu一个
- L1缓存分为L1i和L1d,分别存储指令和数据
- L2缓存不区分指令和数据
- L3缓存所有核心公用一个,不区分指令和数据
- TLB为MMU服务,缓存MMU转化的页表,可以看 一个故事看懂CPU的TLB
3. cpu中sockets、cores、threads的含义
- sockets代表物理上几个插槽
- cores代表cpu中有几个核
- threads代表单个cpu核心有几个线程
七、原子操作
1. 自增操作
- 单核下INC不可分割可以实现原子操作
- 多核下INC不安全,使用xaddl,会锁内存总线达到原子操作
- 或者使用CAS,先比较再赋值,如果内存值更改,重新读取进行操作