# 操作系统手册
**Repository Path**: weafafafa/operating-system-manual
## Basic Information
- **Project Name**: 操作系统手册
- **Description**: 操作系统记录的笔记,待完善。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2024-09-22
- **Last Updated**: 2024-10-06
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 操作系统篇
本人做的操作系统笔记
可配套于小林coding 操作系统章节综合学习,面试小伙伴们可以快速复习
[图解系统介绍 | 小林coding (xiaolincoding.com)](https://xiaolincoding.com/os/)
# 进程与线程
#### **1.进程的控制结构**
描述进程:进程控制块(process control block,PCB)
PCB包含:
1.进程**描述**信息:进程标识符、用户标识符
2.进程**控制和管理**信息:进程当前状态、进程优先级
3.**资源**分配清单:有关内存地址空间或虚拟地址空间的信息,打开文件的列表和和使用I/O设备信息
4.CPU相关信息:进程切换,CPU状态信息保存在PCB里,以便一下次重新执行从断点恢复
一般通过**链表**形式组织,相同状态的PCB在一起,组成队列:比如,就绪队列,阻塞队列。
#### **2.进程的状态**
Java中有六大状态:初始(New)、运行(RUNNABLE、包含就绪和运行中)、阻塞、等待、超时等待、终止
和操作系统底层是一样的,1.创建进程、2.终止进程、3.阻塞进程、4.唤醒进程
阻塞、唤醒本质上都是操作PCB控制块,修改其状态为阻塞、唤醒,并将其插入到对应的队列中
#### 3.**进程上下文切换**
进程上下文切换、线程上下文切换、中断上下文切换
进程的上下文不仅包含了虚拟内存、栈、全局变量等用户空间,还包含了内核堆栈、寄存器等内核资源
#### **4.进程与线程的比较**
根本区别:线程是调度的基本单位,进程是资源拥有的基本单位
两个线程属于同一个进程,因为虚拟内存共享,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程私有数据、寄存器等不共享数据
#### **5**.线程的实现方式
1.用户线程:在用户空间实现的线程,不由内核管理,由用户态线程库来管理
2.内核线程:在内核实现的线程,由内核管理
3.轻量级线程:在内核中支持用户线程
#### 6.用户线程和内核线程的优缺点
**1.用户线程**(多用户线程对一内核线程,多对一模型):用户线程库维护
优点:TCB由用户线程库维护,可用于不支持线程技术操作系统;上下文切换快,无需内核态切换
缺点:线程阻塞导致其他线程阻塞,每个线程都无法打断其他线程(只有操作系统才有权限);更少的时间片
**2.内核线程**(一用户线程对一内核线程,一对一模型):操作系统维护
优点:一个线程被阻塞,不会影响其他内核线程的运行;更多的时间片
缺点:线程的创建、终止、切换都是内核态,系统开销大;需要内核维护PCB和TCB
**所以说轻量级线程采用(M:N模型)是一个折衷!**(M=1、N=1)退化成内核线程,(M!=1、N=1)退化成用户线程。
#### 7.进程、线程调度是什么?调度原则有哪些?
**调度**:操作系统选择某一个进程、线程执行时机的功能称为调度程序。
**5个调度原则**:CPU利用率、系统吞吐率、周转时间、等待时间、响应时间
CPU利用率:CPU始终是匆忙的
系统吞吐率:单位时间CPU完成进程数,短作业会提高吞吐率
周转时间:进程运行+阻塞时间+等待时间总和,越小越好
等待时间:在就绪队列中等待的时间,不是阻塞状态时间,越短越好
响应时间:用户请求到系统第一次响应时间,交互系统,越快越好
#### 8.调度算法(单核)
1.先去先来服务:适用于CPU繁忙作业,不适用于I/O繁忙作业系统
2.最短作业优先:长作业的周转时间很长
3.高响应比优先:等待时间/要求服务时间,等待时间越大,服务时间越小,优先级越高。理想算法,预期服务时间不能得到,不能实现。
4.时间片轮转算法:靠固定时间片来控制每个进程执行时间
5.最高优先级调度算法:可能导致低优先进程永远不会运行
6.多级反馈队列:综合了时间片轮转和最高优先级调度算法。核心是长作业优先级没执行完会满满降低,而每次执行分配的时间片更多。短作业优先级较高,在第一梯队,但是每次只分配较短时间片。
#### **9.进程间有哪些通信方式?**
6种:管道、消息队列、共享内存、信号量、信号、Socket
**管道**:匿名管道(只在亲缘关系进程通信),命名管道,通信数据是无格式的,单向
**消息队列**:与管道不同,通信数据是用户自定义数据类型,消息体,且不会随进程关闭而关闭
**共享内存**:开销小,多个进程虚拟内存对应同一块开辟的共享物理内存,从而可以直接访问修改
**信号量**:用来同步或者互斥,P、V操作。
**信号**:异步通信机制,用来进行异常通知,比如kill,进程三种相应方式:1.执行默认操作 2.捕捉信号处理 3.忽略信号
**Socket**:可以用于不同主机的进程通信,有UDP\TCP、以及本地进程间通信(绑定本地文件)三种方式
#### **10.经典同步互斥问题**
**1.生产者、消费者问题**:
①生产者生成数据,放在缓存中;②消费者从缓冲区取出数据处理;③只有一个生产者或消费者可以访问
解决方案:互斥+同步
**2.哲学家就餐问题(死锁)**:
①哲学家要左右两边叉子才能吃饭
解决方案:偶数编号的哲学家拿左边叉子,奇数编号的哲学家拿右边叉子(资源有序分配法,破坏头尾相连循环等待);或者一个哲学家在左边两边一次性去尝试拿(破坏请求与保持条件);一定时间没拿到就放弃自己的(破坏不可剥夺条件)
**3.读写者问题**:
①读-读允许 ②读-写互斥 ③写写互斥
解决方案:读者优先、写者优先、读写公平 ;互斥+同步or公平队列
#### **11.死锁产生条件、如何避免死锁?**
**死锁产生条件**:1.互斥 2.请求与保持 3.不可剥夺 4.头尾相连循环等待
**破坏死锁**:
针对请求与保持:一次申请所有的资源
针对不可剥夺:一定时间还是没加锁成功,释放锁
针对头尾相连循环等待:资源有序分配法,两个线程都先获得某一资源才能尝试下一个资源的获取
如果真产生了死锁,通过java 的**jstack**和调试工具进行**死锁检测**。
#### **12.什么是悲观锁、乐观锁?**
悲观锁:先尝试加锁,再执行(比如lock)
乐观锁:先尝试,如果发现没有冲突,达到预期,就提交(比如CAS操作、比如腾讯在线文档)
#### **13.线程崩溃了,进程也会崩溃吗?**
在JAVA中是不会的,C++会,因为JAVA有JVM
针对StackOverFlowError和NullPointException,JVM对这些崩溃信号做了自己的信号处理函数,因为这些错误太常见了,为了维护程序的健壮性,并且自定义一些处理逻辑,比如记录crash信息。
# 调度算法
### *进程调度*
#### 1.常见的进程调度算法?
①.先来先服务(First Come First Severd, FCFS)
每次从就绪队列选择最先进入队列进程,直到该进程执行完退出或者阻塞。
②.最短作业优先(Shortest Job First, SJF)
优先选择运行时间最短的进程运行
③.高响应比优先(Highest Response Ratio Next,HRRN)
理想算法。优先权=(等待时间/要求服务时间)+1
④.时间片轮转调度算法(Round Robin,RR)
每个进程分配一个时间段,称为时间片,允许进程在该时间段运行。
⑤.最高优先级(Highest Priority First,HPF)
需要确定优先级指标如何计算。
⑥.多级反馈队列(Multilevel Feedback Queue)
【时间片轮转算法】和【最高优先级算法】的综合发展
多级:多个队列,每个队列优先级从高到底,分配的时间片由低到高
反馈:新进程进入第一级队列末尾,按照先来先服务。如果高一级执行完时间片没有完成,则放入低一级队列。
长作业等待时间变长了,但是分配时间片大小也更大了,能够兼顾长短作业和响应时间。
### *内存页面置换*
#### **2.常见内存页面置换算法?**
当出现缺页异常,需要调度新页面而内存已满时,选择被置换的物理页。
需要尽可能减少页面换入换出的I/O操作。
①.最佳页面置换(OPT)
预测页面下一次访问前所需等待时间,过于理想,可以作为基线。
②.先进先出置换(FIFO)
驻留时间最长的页置换掉。
③.最近最久未使用置换(LRU)
选择历史最长时间没有被访问的页面进行置换
④.时钟页面置换(Lock)
与LRU近似,把所有页面保存在一个类似钟面的环形链表中,一个表针指着最老页面。
当发生缺页中断,顺时针寻找:
1.如果访问位为0,淘汰该页,置换
2.如果访问位为1,清除该访问位,并把表针前移一个位置,重复该过程直到找到访问位为0的并淘汰
⑤.最不常用算法
当发生缺页中断时,淘汰历史访问次数最少的页面。
### *磁盘调度*
#### **3.常见磁盘调度算法?**
①.先来先服务(FCFS)
先来请求,先服务。
②.最短寻道时间优先(Shortest Seek First,SSF)
优先选择离当前磁头位置最近的磁道。
出现**饥饿**现象:磁头在一小块区域反复来回移动
③.扫描算法(SCAN,又称电梯算法)
磁头在一个方向移动,访问所有未完成的请求,直到到达该方向最后的磁道,才调转方向。
④.循环扫描算法(CSCAN)
与扫描算法不同,磁道只响应一个方向上的请求,返回途中不处理任何请求。
⑤.LOOK和C-LOOK
不一定需要移动到某个方向的最后一个磁道,而是设定一个最远移动距离后返回。
LOOK:针对SCAN
C-LOOK:针对CSCAN
# 网络系统
### *文件传输*
#### 1.为什么要有DMA技术?
DMA可以代替CPU完成【将数据从磁盘控制器缓冲区搬运到内核空间】的工作,从而在这个过程中解放CPU,提高效率。
#### 2.零拷贝?如何使用零拷贝?
**零拷贝本质**:没有通过CPU来搬运数据,所有数据都是DMA来传输的。
1.mmap+write (4次上下文切换,3次数据拷贝)
mmap:内核和用户共享一个缓冲区
3次拷贝指的是**:读DMA拷贝** 磁盘控制器缓存->内核缓存,**CPU拷贝** 内核缓存区->内核socket缓冲区,**写DMA拷贝** 内核socket缓冲区->磁盘控制器缓存。
2.sendfile (2次上下文切换,3次数据拷贝)
减少了内核与用户态切换,2次上下切换,但是仍然是3次数据拷贝。
3.基于SG-DMA的sendfile技术
读DMA拷贝 磁盘控制器缓存->内核缓存,写DMA磁盘 内核缓存->磁盘控制器缓存
#### 3.PageCache有什么作用?
零拷贝技术基于PageCache,PageCache具有两大作用:
1.缓存功能,缓存数据供给读程序,此外,缓存尽可能多的I/O请求并合并后写,减少磁盘寻址操作
2.预读功能,更多读入一些预期数据
大文件不适合PageCache,小文件适合PageCache,因为大文件会把PageCache缓冲区占满。
所以,小文件采用**PageCache**+**零拷贝技术**,大文件采用**直接IO-异步IO**
### *Socket网络通信传输*
#### **4.TCP Socket的过程?**
服务端:Socket()、bind()、listen()、accept()、read()
客户端:Socket()、connet()、write()
Socket有两个,一个监听Socket,一个是已连接Socket(用来传输数据)
#### **5.服务器如何服务更多的用户(I/O多路复用)?**
**采用I/O多路复用:select/poll/epoll**
select/poll:本质没有区别,select是数组,poll是链表,连续结构存储在 **Socket文件描述符集合**中,有读可用内核态遍历集合标记对应Socket,然后把集合传给用户态,用户态仍遍历一遍,所以是2次遍历。
epoll:基于红黑树+链表
利用红黑树跟踪所有待检测的文字描述符,通过链表记录就绪事件。事件驱动传链表给用户态就可以了,效率更高。此外,epoll有**水平触发**(Socket事件没处理完不断触发提醒)和**边缘触发**(Socket事件没处理完只触发一次,所以尽可能一次读完),边缘触发更高效。
### 一致性哈希
#### 6.什么是一致性哈希,为什么需要它?
一致性哈希算法就很好地解决了分布式系统在**扩容或者缩容**时,发生过多的**数据迁移**的问题。
哈希算法是对节点数量取模,而一致性哈希是对2^32取模运算,取模对象固定值。
一致性哈希通过哈希环,尽可能让节点均匀分布在环上,然后对应的key对2^32取模后顺时针寻找对应所属的存储节点。
#### **7.如果节点分布不均匀会怎样,如何解决?**
**影响**:某节点突然宕机,可能导致其他节点迁移压力过大,导致崩溃,需要尽可能分布均匀。
**解决**:
核心:节点越多,相邻节点存储的key范围小。
如果节点不足,可以采用**虚拟节点扩充**,比如A 有 A-1,A-2,A-3.B 有 B-1,B-2,B-3.C有C-1,C-2,C-3。虚拟节点更多均匀分布密度更大,当B宕机仍然有6个,然后虚拟节点映射自己对应的节点即可。·
### 高性能网络模式:Reactor和Proactor
#### 8.Reactor是什么?实现方案有哪些?
**解释**:Reactor是一种用于处理服务请求并分发到对于处理器的模型。
该模式中场景包含三个对象:Reactor、Accept、Handler
Reactor:监听和分发事件
Accept:获取连接
Handler:处理业务
**实现方案**
:
1.单Reactor 单线程:无法利用到多核CPU
2.单Reactor 多线程:只有一个Reactor承担所有事件的监听(建立连接)和响应(处理请求),高并发事件场景下差
3.多Reactor 多线程:父Reactor只负责建立连接,不负责处理请求,交给了子Reactor处理
#### **9.Proactor是什么?和Reactor有什么区别?**
**解释**:Proactor是一种异步/IO实现的网络模型。感知的是已经完成的I/O通知。
**区别**:Reactor来了事件操作系统通知应用程序(通知后,I/O需要read),Proactor操作系统完成事件通知应用程序(操作系统已经帮忙完成内核到用户态拷贝了,只通知)。
# 内存管理
#### **1.虚拟内存,物理内存?**
操作系统提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。
程序中所使用的内存地址叫 **虚拟内存地址**
硬件里的空间地址叫 **物理内存地址**
##### 衍生:**为什么需要虚拟内存?**
1.程序运行有局部性原理,对于程序不经常使用到的内存,可以swap置换到磁盘。
2.每个进程虚拟内存独立,解决了多进程之间地址冲突问题。
#### 2.操作系统如何管理虚拟内存和物理内存地址的关系?
①.内存分段
不同的段有不同属性,用分段形式把这些段分离出来。
通过**段号+段内偏移量**映射。
缺点:产生外部内存碎片(无内部内存碎片)、内存交换率低(物理内存和磁盘的swap)
②.内存分页
整个虚拟和物理内存空间切成一段段固定尺寸的大小,称为页。linux下每一个页的大小为4kB。
通过**页号+页内偏移量**映射。
优点:不会产生外部内存碎片(有内部内存碎片)、内存交换率高(物理内存和磁盘的swap)
③.段页式
通过**短号+页号+页内偏移量**映射。
内存分段+分页。使用不多,linux是段页式,但实际上段的基线都是0,所以本质上是内存分段式。
#### **3.如何解决分页下页表过大情况?**
多级页表。通过局部性原理,没有使用到的内存,导致后面级别的页表可能不会创建。
**新问题:**多级页表导致查询页表时间开销增大。**解决:**TLB,缓存最近常被访问的页表项。
#### **4.linux虚拟内存的划分?**
虚拟空间可以划分为**用户态+内核态**空间
用户态包括:代码段、全局变量(初始化的静态、全局变量)、BSS(未初始化的静态、全局变量)、堆(自底向上)、栈(自顶向下)、映射区
#### **5.malloc是如何分配内存的?**
malloc分配的是虚拟内存。
分配方式:
1.系统调用brk(),堆区分配内存。
2.系统调用mmap(),文件映射区分配内存。
malloc(1)会分配更大的空间作为内存池。
其中内存块的头信息记录了内存大小,所以free只需要输入一个参数。
#### **6.free释放内存,会归还给操作系统吗?**
malloc通过brk()方式申请的内存,free释放后会缓存在malloc内存池,待下次使用
malloc通过mmap()方式申请的内存,free释放后会彻底的释放。
一般,brk()用于小于128KB的空间开辟,mmap()用于大于128KB的空间开辟。
#### **7.物理内存满了,操作系统如何回收?**
如果空闲**物理内存**不够
**回收方式**:
1.后台内存回收:唤醒kswapd线程回收,不会阻塞进程执行
2.直接内存回收:后台内存回收跟不上内存申请速度,开始直接回收,阻塞进程执行
**可回收的内存类型**:
1.文件页回收:干净页直接释放,脏页换入磁盘,后释放
2.匿名页回收:开启swap机制后,swap机制会将不长访问的匿名页换出磁盘,下次需要换回来。
**回收方法:**
文件页回收和匿名页回收 都是基于LRU算法
如果后台回收和直接回收仍然不行,触发OOM机制:
通过内存占用情况和oom_score_adj的值进行打分,分越高越先杀死。