操作系统知识
线程通信(IPC)
-
管道(通过共享存储空间实现,一方写入,一方读取)(在同一操作系统通信)
- 无名管道 使用管道的两个线程必须存在某种联系(例如父子线程)
- 记名管道 不同进程间的线程通信
-
套接字(跨应用,跨网络通信)
使用套接字通信双方均创建一个套接字,一方为服务端,一方为客户端,服务器先创建套接字并监听等待连接,形成点到点的通信
套接字不发送数据也不接收数据,仅仅产生客户套接字
-
信号量(一个简单整数,空服之当前和其它进程的执行
管道和套接字必须要创建连接,要消耗系统资源。
信号量不仅是通信机制也是同步机制
-
共享内存(需要共享大量数据的时候使用)
两个进程共享同一片内存,共享内存机制的两个进程必须在同一台物理机器上,且访问方式随机的,管道中只能一方读一方写,管道中的数据只能读一次,共享内存中的可以反复读
-
消息队列(排列好的消息,新消息在队尾,旧消息在队头)
相比于管道没有固定的读写进程,任何进程都可读写,支持同时使用多个进程
线程,进程切换
-
切换页目录以使用新的地址空间;(进程)
-
切换内核栈和硬件上下文。(线程、进程)
进程切换=上下文切换
每个进程都有自己的虚拟地址空间,进程内的所有线程共享进程的虚拟地址空间
进程切换涉及虚拟地址空间的切换而线程不会。
因为虚拟地址的切换耗时,页表查询是一个很慢的过程,通常通过缓存(TLB)来加速查找,每个进程都有自己的页面,页表切换后TLB就失效了
进程上下文切换
[进程1+进程1上下文保存+加载进程2上下文+进程2]
-
保存进程A的状态(寄存器和操作系统数据);
-
更新PCB中的信息,对进程A的“运行态”做出相应更改;
-
将进程A的PCB放入相关状态的队列;
-
将进程B的PCB信息改为“运行态”,并执行进程B;
-
B执行完后,从队列中取出进程A的PCB,恢复进程A被切换时的上下文,继续执行A。
-
线程分为用户级线程和内核级线程。同一进程中的用户级线程切换的时候,只需要保存用户寄存器的内容,程序计数器,栈
发生场景:
- 运行的时间片耗尽,从运行态转为就绪态
- 系统资源不足,进程挂起
- sleep函数主动挂起
- 高优先级进程执行
- 硬件中断,进程挂起
线程上下文切换
发生场景
- 硬件中断,软件中断,线程挂起
- 时间片用完
- 用户状态切换
减少切换的方法:
- 无锁并发编程(例如用不同的线程处理不同段的数据,HASH分段)
- CAS算法
- 使用最少线程(避免创建不需要的线程)
- 协程(在单线程里维持多个任务间的切换)
CAS算法(CompareAndSwap)
基于原子性,此操作不会被打断
3个操作数(内存值V,旧的预期值A,新的预期值B)
do{
//在主存中读取内存值V A=V
//对读进来的值修改 A=V
}while(!CAS(V,A,B))
两者进行比较时,如果相等,则证明共享数据没有被修改,替换成新值,然后继续往下运行;如果不相等,说明共享数据已经被修改,放弃已经所做的操作,然后重新执行刚才的操作。
JavaGC(标记-清除)
标记阶段:引用计数算法,可达性分析算法,对象的Finalization机制(判断对象是否存活)
清除阶段:标记清除算法 复制算法 标记压缩算法 分代收集算法
-
引用计数算法(实现简单,但无法处理循环引用问题,会导致内存泄漏)
每个对象都保存一个整形的引用计数器,记录被引用的情况,为0时回收
-
可达性分析算法
以根对象为GCRoots,从上至下搜索连接的对象是否可达,存活的对象会被直接、间接连接,若有对象不可达,则回收(解决了循环引用的问题
-
对象的finalization机制
如所有GCRoots无法访问某个对象,则这个对象不再使用,但有可能复活
虚拟机中对象由三种状态:
-
可达的 从GCRoots可达
-
可复活的,对象引用被释放,可能在finalize()中复活
-
不可达的 finalize()调用不会复活
只有在对象不可达时才可以被回收
-
回收阶段
-
标记清除算法
对堆内存线性遍历,某个对象为不可达对象则回收
缺点:
- 效率不高,GC时需要停止用户程序
- 产生了内存碎片
- 并不是真的删除,只是把需要清楚的对象地址保存在空闲地址列表
-
复制算法
将活着的内存空间分为两块,每次只使用一块,回收时将正在使用的存活对象复制到未被使用的内存块,清楚正在使用的内存块中的对象,交换两个内存的角色,完成垃圾回收
- 实现简单,不会出现内存碎片
- 需要两倍的空间
- 需要维护对象的引用关系
-
标记压缩算法
-
第一阶段同标记清除算法一致,从根节点标记所有的被应用对象
-
第二阶段将所有存活对象缩到内存一段
-
在清理边界外的所有空间
特点:
- 消除了复制算法内存减半的代价
- 效率低于复制算法
- 移动过程中需要暂停用户程序
-
-
分代收集算法
年轻代:对象生命周期短,回收频繁,采用复制算法(效率高)
老年代:对象生命周期长,存活率高由标记清除算法 标记压缩算法混合实现
-
增量收集算法(基础是标记清除算法和复制算法)
让垃圾收集线程和应用线程交替执行,垃圾收集下线程指挥收集一小片区域的内存空间,再切换应用程序线程
三色标记
算法(可达性分析)
可以在程序执行的过程中收集,不需要暂停整个程序
原理:
- 创建黑白灰三个集合
- 将所有对象放入白色集合
- 从根节点遍历对象(不进行递归),把遍历到的对象从白色集合放入灰色集合
- 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
- 重复4直到灰色中没有对象
- 通过write-barrier检测对象变化,重复以上操作
- 回收所有白色对象
STW
STW指JVM进行GC时,java程序的所有线程被挂起(除垃圾收集器)。Java中的一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能和JVM交互。GolangGC没有该过程
CMS垃圾收集器
以获取最短回收停顿时间为目标(Concurrect Mark Sweep)
基于标记清除算法,步骤未:
- 初始标记
需要STW,标记GCRoots能直接关联到的对象 - 并发标记
从GCRoots开始找到它能引用的所有其他对象,需要STW - 重新标记
修正并发打标机期间因用户程序继续动作而导致标记产生的那一部分对象的标记记录 - 并发清除
优点:
- 并发收集,低停顿( 用户线程)
缺点: - CMS收集器对CPU资源非常敏感
- CMS收集器无法处理浮动垃圾
堆=新生代+老年代+永久代
永久代:存放class和元数据的信息,gc不会对永久代进行清理,导致永久代随着加载的class增多而胀满,最终抛出OOM(out of memory)异常
线程
线程同步的方式
- 临界区
临界区的对象一次只能被一个进程使用 - 事件
允许一个线程处理完一个任务后唤醒另外一个线程 - 互斥量
与临界区相似,但只允许在进程间使用,临界区允许在各线程间使用 - 信号量(本质:计数器)
使用计数器限制 使用共享资源的线程数目
线程分类
- 内核级线程,依赖于内核,称为轻量级线程,由内核创建并管理
- 用户级线程,不依赖于操作系统核心,由线程库创建管理,操作系统无法感知用户级线程的存在
死锁
产生条件:
- 互斥 一个资源一次只能被一个进程使用
- 请求与保持 一个进程因请求资源而阻塞时,对已获得资源保持不放
- 不剥夺条件 进程获得的资源,在未完全使用完之前,不能强行剥夺
- 循环等待条件 若干进程之间形成一种头尾相接的环形等待资源关系
进程调度方式
- 先来先服务
- 短作业优先
- 最短剩余时间优先
- 时间片轮转
- 优先级调度
分页 分段
分页(访问页表得到物理块+offset)
把内存空间划分为大小相等且固定的块,作为主存的基本单位。因为程序数据存储在不同的页面中,而页面又离散的分布在内存中,因此需要一个页表来记录映射关系,以实现从页号到物理块号的映射。
分段(段号_段内地址)
为了提高内存利用率,而分段是为了满足程序员在编写代码的时候的一些逻辑需求(比如数据共享,数据保护,动态链接等)
区别:
- 分页对程序员是透明的,但是分段需要程序员显式划分每个段。
- 分页的地址空间是一维地址空间,分段是二维的。
- 页的大小不可变,段的大小可以动态改变。
- 分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。
页面替换算法
程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。
- 最佳算法(最长事件不被访问的换出)
- 先进先出
- LRU(最久未使用的页换出)
- 时钟算法
IO多路复用
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程
场景
- 处理多个服务或协议
- 同时处理多个套接字
- 既要监听套接口,也要处理已经连接的套接口
- 既要处理TCP,也要处理UDP
硬链接和软链接
硬链接A是B的硬链接(A和B都是文件名)就是在目录下创建一个条目,记录着文件名与 inode 编号,这个 inode 就是源文件的 inode。删除任意一个条目,文件还是存在,只要引用数量不为 0。但是硬链接有限制,它不能跨越文件系统,也不能对目录进行链接。rm A删除的只是A这个文件名,而A对应的数据块(文件)只有在inode节点链接数减少为0的时候才会被系统回收。不能对目录做硬链接
符号链接A是B的硬链接(A指向B件名)文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为 Windows 的快捷方式。当源文件被删除了,链接文件就打不开了。因为记录的是路径,所以可以为目录建立符号链接。可以跨文件系统
中断处理过程
- 保护现场 将执行程序的相关数据保存在寄存器中入栈
- 开中断 执行中断时能响应更高级的中断请求
- 中断处理
- 关中断 保证恢复现场不被新中断打扰
- 恢复现场,从堆栈中取出程序数据,恢复终端前的执行状态