后端八股精讲——操作系统——锁,内存管理
锁
死锁&产生死锁的必要条件
死锁:多个进程进程竞争资源而相互等待,无法继续推进
必要条件:
-
互斥:资源只能被一个进程占用
-
保持并等待:进程持有资源并等待其他资源
-
不剥夺:已分配资源不能被剥夺
-
循环等待:存在进程资源的循环等待链
破坏死锁
-
通过算法分配资源确保系统处于安全状态
-
进程一次性申请所有需要的资源
-
允许剥夺资源
-
对资源申请进行线性排序,按顺序申请,按相反顺序释放
内存管理
操作系统设计了虚拟内存,每个进程都有自己的虚拟内存
虚拟内存&解决什么问题
虚拟内存:每个进程的虚拟地址空间映射到物理内存,通过页表等机制管理,可使用磁盘作为后备存储
解决问题:
-
内存隔离,保护进程间不干扰(不同进程虚拟内存之间互相独立)
-
扩展可用内存,支持大于物理内存的程序(真正使用时按需分配)
-
简化内存管理(连续逻辑地址,物理上可以离散)
页表
分页将整个虚拟内存和物理内存空间切分为一段段固定尺寸的大小,以页为单位进行管理。
一个连续并且尺寸固定的内存空间称为页。linux操作系统默认一页大小为4KB
虚拟地址和物理地址通过页表进行映射。
分页机制下,虚拟地址分为页号和页内偏移量。通过页号在页表当中查询对应的物理页框号,将页框号组合偏移量得到物理内存地址。
缺页异常
进程访问的虚拟地址在页表中查询不到有效的物理映射时出现 缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后返回用户空间,恢复进程运行。
内存碎片
内存分页由于内存空间都是预先划分好的,也就不会像内存分段一样段与段之间产生间隙非常小的内存(分段产生内存碎片的原因)。
页与页之间紧密排列,不会存在外部碎片。
同时,由于内存分页机制分配内存最小单位是一页,即使程序不足一页大小,因此页内会出现内存浪费——内部内存碎片。
段表
虚拟地址可以通过段表与物理地址进行映射。分段机制会将程序虚拟地址空间按照业务意义划分为多个段(代码段、数据段、堆、栈),每个段在段表中对应一个项,这个项中找到段的基地址,加上偏移量就能够找到物理内存中的地址。
虚拟地址如何转换为物理地址
通过内存管理单元Memory Management Unit完成。
转换过程当中通常使用页表进行映射。页表将虚拟空间划分为固定大小的页对应物理内存中页框。
程序访问一个虚拟地址时,MMU将虚拟地址分解为页号和页偏移量,之后查找页表,根据页号找到对应页表项,其中包含物理页地址和页框号。MMU将物理页地址和页内偏移量组合得到对应物理地址。
堆栈区别
-
分配方式:堆是动态分配内存,程序员手动申请和释放内存,通常用于存储动态数据结构和对象。栈是静态分配内存,编译器自动分配和释放内存,用于存储函数局部变量和函数调用信息
-
内存管理:堆需要程序员手动管理和释放,管理不当可能导致内存泄漏/移除。栈由编译器自动管理,遵循后进先出原则,变量生命周期由作用域决定。
-
大小&速度:堆通常较大,动态分配内存和释放内存需要时间开销。
fork会复制什么东西
-
fork阶段会复制父进程的页表
-
fork之后,若发生写时复制,复制物理内存
Copy on Write写时复制
主进程执行fork的时候,操作系统将主进程的页表复制一份给子进程。
页表记录虚拟地址和物理地址之间的映射关系,不复制物理内存。
子进程就共享了父进程的物理内存数据,能够节省物理内存资源,页表对应的页表属性项记录物理内存权限为只读。
当父进程和子进程像这个内存发起写操作时,CPU触发写保护中断,操作系统在写保护中断处理函数中进行物理内存复制,并重新设置内存映射关系,将父子进程的内存读写权限设定为可读写。
也就是在发生写操作时操作系统才会复制物理内存,防止fork创建子进程时复制时间过长导致父进程长时间阻塞。
操作系统内存不足发生什么?
应用程序通过malloc申请内存时,实际上申请的是虚拟内存,并不会分配物理内存。
当应用程序读写到这块虚拟内存,CPU会去访问这个虚拟内存,这时候发现按虚拟内存没有映射到物理内存,CPU产生缺页中断,进程从用户态切换到内核态,交由内核缺页中断处理函数 处理。
缺页中断处理函数会查看是否存在空闲物理内存,存在则直接分配物理内存并建立虚拟内存和物理内存间映射关系。
没有空闲物理内存,内核开始回收内存工作,两种回收方式:
-
后台内存回收(kswapd):物理内存紧张时唤醒kswapd内核线程回收内存,过程异步,不需要阻塞进程
-
直接内存回收:后台异步回收跟不上进程内存申请速度,开始直接回收,同步执行,会阻塞进程执行
直接内存回收之后还是无法满足物理内存申请,触发OOM机制。
OOM Killer机制会选择一个占用物理内存较高的进程,然后将其杀死释放内存资源,若依然不足会继续杀死占用物理内存较高进程直到释放足够内存。
哪些内存可以被回收
主要有两类内存可以被回收:
-
文件页:内核缓存的磁盘数据和内核缓存的文件数据。大部分文件页都可以直接释放缓存,需要时再次从磁盘读取。脏页需要先写入磁盘之后释放内存。
干净页直接释放内存,脏页先写入磁盘再释放内存
-
匿名页(堆内存中对象、用户栈等):没有实际载体,可能还要被再次访问,不能直接释放内存,需要通过Linux的Swap机制,将不常访问的内存写到磁盘中,释放这些内存;需要再次访问时从磁盘读取。
内存对齐
数据存储在内存中时其首地址按照一定规则(例如数据类型大小倍数)防止
原因:
-
硬件访问效率:许多CPU每次读取固定宽度数据,对齐可以避免多次访问出错
-
可移植性:一些平台强制对其否则引发异常
-
缓存性能:减少缓存行分裂
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)