1.进程、线程基础知识
1.1 进程
进程是什么
运行中的程序
中断是什么
CPU可以从进程1切换到进程2,记录进程1当前状态,要恢复执行进程1就需要向CPU发送中断
并行和并发的区别
并行:同一时刻,进行多个任务。
并发:同一时间段,进行多个任务,切换频率很高让人无法感受到中断。
1.1.1 进程的状态
一个进程的活动期间至少具备三种基本状态,
运行状态(Running),该时刻进程占用CPU
就绪状态(Ready),可运行,由于其他进程处于运行状态而暂时停止运行
阻塞状态(Blocked),该进程正在等待某一事件完成(比如IO操作)而暂时停止运行,这时即使给它CPU控制权也无法运行。
进程的另外两个基本状态:
创建状态(new),进程正在被创建时的状态
结束状态(Exit),进程正在从系统中消失时的状态
完整的进程状态变迁:
- null→创建状态:new创建状态,
- 创建状态→就绪状态:创建完成并初始化后,进入就绪队列,等待被CPU调度,
- 就绪状态→运行状态:被CPU调度,进入运行状态,
- 运行状态→就绪状态:当前时间片完则回到就绪状态,让给下一个进程运行
- 运行状态→阻塞状态:若需要等待事件,例如IO,则进入阻塞状态,
- 阻塞状态→就绪状态:事件完成则进入就绪状态,
- 运行状态→结束状态:若进程完成或出错,则进入结束状态
挂起状态 为什么产生、是什么意思、有什么作用
如果有大量处于阻塞状态的进程,这些进程会占用物理内存空间,极大的浪费了物理内存。
所以,在虚拟内存管理的操作系统中,通常把阻塞状态的进程的物理内存空间换出到硬盘,再次运行的时候,再从硬盘换入到物理内存。
挂起状态:描述进程没有占用实际的物理内存空间的情况
挂起状态分为两种:
阻塞挂起状态:进程在外存并等待某个事件的出现
就绪挂起状态:进程在外存,但只要进入内存,就立刻运行
还有两种情况也会导致进程挂起:
通过sleep让进程间歇性挂起,原理是设置一个定时器,到期后唤醒进程。
用户希望挂起一个程序的执行,例如Linux中用Ctrl+z
挂起进程
1.1.2 进程的控制结构
OS使用进程控制块(process control block,PCB)数据结构来描述进程。
PCB是进程存在的唯一标识
PCB包含什么?
进程描述信息
进程控制和管理信息
资源分配清单
CPU相关信息
每个PCB是如何组织的
通常通过链表的方式进行组织,把具有相同状态的进程链在一起,组成各种队列。
1.1.3 进程的控制
进程的创建、终止、阻塞、唤醒的过程
创建进程
终止进程
阻塞进程
唤醒进程
1.1.4 进程的上下文切换
一个进程切换到另一个进程运行,称为进程的上下文切换。
CPU上下文切换
CPU在运行任何任务前,必须依赖CPU寄存器和程序计数器。CPU寄存器和程序计数器就叫做CPU上下文
CPU上下文切换:先把当前任务的CPU上下文保存起来,然后加载新任务的上下文,再跳转到程序计数器所指的新位置,运行新任务。系统内核会存储保持下来的上下文信息,当此任务再次被分配给CPU运行时,CPU会重新加载这些上下文,这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。
CPU上下文按任务的种类,可以分成:进程上下文切换、线程上下文切换和中断上下文切换。
进程上下文切换 切换什么?
进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的资源。
发生进程上下文切换的场景有哪些?
1.2 线程
1.3 调度
调度算法
先到先服务:最先进入最先执行,直到完成或被阻塞
短作业优先:运行时间最短最先执行,直到完成或被阻塞
时间片轮转:每个进程享有一个时间片,片完即调度
优先级:高优先级先执行,直到完成或阻塞
多级反馈队列:兼顾优先级和短作业
1.4 进程与线程
什么是进程和线程?
进程(Process) 是指计算机中正在运行的一个程序实例。
线程(Thread) 也被称为轻量级进程,更加轻量。
进程和线程的区别是什么?
线程是进程划分成的更小的运行单位,一个进程在其执行的过程中可以产生多个线程。
线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。
线程执行开销小,但不利于资源的管理和保护;而进程正相反。
有了进程为什么还需要线程?
进程切换是一个开销很大的操作,线程切换的成本较低。
线程更轻量,一个进程可以创建多个线程。
多个线程可以并发处理不同的任务,更有效地利用了多处理器和多核计算机。而进程只能在一个时间干一件事,如果在执行过程中遇到阻塞问题比如 IO 阻塞就会挂起直到结果返回。
同一进程内的线程共享内存和文件,因此它们之间相互通信无须调用内核。
2.进程间有哪些通信方式
2.1 管道
管道:本机两个亲缘进程通信,存在形式为内存文件
有名管道:本机任意两个进程通信,通信数据遵循先进先出,存在形式为磁盘文件
2.2 消息队列
消息队列:实现消息随机查询,通信数据遵循先进先出,存在于内核,只能显式删除
2.3 共享内存
共享内存:多个进程访问同一个内存区域,依靠互斥锁,信号量等同步操作实现
2.4 信号量
信号量:计数器,实现进程同步
2.5 信号
2.6 Socket
套接字:不同主机之间的进程进行双向通信的端点,用于客户端和服务端之间通过网络进行通信
2.7 总结
3.多线程冲突了怎么办
3.1 为什么要用多线程
总体上来说。
从计算机底层来说: 线程可以比作是轻量级的进程,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。
从当代互联网发展趋势来说: 现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。
计算机底层:
单核时代:在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。 假设只运行了一个 Java 进程的情况,当我们请求 IO 的时候,如果 Java 进程中只有一个线程,此线程被 IO 阻塞则整个进程被阻塞。CPU 和 IO 设备只有一个在运行,那么可以简单地说系统整体效率只有 50%。当使用多线程的时候,一个线程被 IO 阻塞,其他线程还可以继续使用 CPU。从而提高了 Java 进程利用系统资源的整体效率。
**多核时代: 多核时代多线程主要是为了提高进程利用多核 CPU 的能力。**举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,不论系统有几个 CPU 核心,都只会有一个 CPU 核心被利用到。而创建多个线程,这些线程可以被映射到底层多个 CPU 上执行,在任务中的多个线程没有资源竞争的情况下,任务执行的效率会有显著性的提高,约等于(单核时执行时间/CPU 核心数)。
3.2 竞争与协作
3.3 互斥与同步的实现和使用
3.4 经典同步问题
4.怎么避免死锁
4.1 死锁的概念
什么是:多个进程/线程同时被阻塞,一个/全部在等待某个资源被释放,由于进程/线程被无限期阻塞导致程序无法被正常终止。
4.2 模拟死锁问题的产生
产生死锁的4个必要条件,缺一不可:
1资源互斥,一次只能被一个进程占有
2占有并等待,进程占有A资源并等待B资源释放
3非抢占,资源不可被抢占,只有任务完成才会释放
4循环等待,进程A等进程B释放,进程B等进程C释放,进程C等进程A释放
4.3 解决死锁的思路与方法
思路
可以从预防、避免、检测、解除四个方面考虑
详细的四种思路:
- 预防,思想是破坏4个必要条件,主要针对第二和第四个条件进行预防,因为第一个条件——互斥,有的资源默认就是互斥无法被同时访问,例如打印机;第三个条件——非抢占,虽然可以用剥夺式调度算法,但他只适合主存资源和处理器资源,否则会导致资源利用率下降。破坏第二个条件——占有并等待,采用静态分配策略,即要么不占有资源,要么占有所有所需资源直接执行,但会导致资源利用率低下,因为有可能占有很少用到但还是需要的资源从而导致其他进程等待破坏第四个条件——循环等待,采用层次分配策略,即从低到高逐层申请资源,从高到低逐层释放资源整体来说,预防会导致低效的进程运行和资源利用率。
- 避免,与预防相反,运行4个必要条件存在,但将系统分为安全状态和不安全状态。前者意味着在有限时间内所有进程能得到所有需要的资源并执行完毕,后者则反之。处于安全状态就分配资源,反之则拒绝分配。采用银行家算法可以保证系统处于安全状态,这个算法简单来说就是假设给一个进程分配了资源,并通过安全性算法判断分配后系统是否安全,如果安全则真的分配,反之则作废,进程继续等待。
- 检测+解除,思想是分配时不管会不会死锁,等真的出现死锁再进行解除。利用进程-资源分配图来检测是否发生了死锁,无环路无死锁,有环路且每个资源类只有一个资源则必然死锁,有环路但每个资源类有多个资源则不一定死锁。
方法:
- 1无需考虑损失时,可以直接重启操作系统解除死锁。
- 2撤销所有死锁相关进程,这样损失同样很大,前功尽弃。
- 3逐个撤销与死锁相关的进程,回收资源,直到解除死锁。
- 4从与死锁相关的进程中抢占资源,直到死锁解除