进程和线程的区别,什么是协程?
进程和线程的区别
线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。另外,也正是因为共享资源,所以线程中执行时一般都要进行同步和互斥。总的来说,进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
- 进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位(根本区别)
- 线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程,线程也被称为轻量级进程(包含关系)
- 同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的。(内存分配)
- 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。(影响关系)
- 每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行。(执行过程)
协程
协程(Coroutines)是一种比线程更加轻量级的存在,正如一个进程可以拥有多个线程一样,一个线程可以拥有多个协程。
协程不是被操作系统内核所管理的,而是完全由程序所控制,也就是在用户态执行。这样带来的好处是性能大幅度的提升,因为不会像线程切换那样消耗资源。
协程不是进程也不是线程,而是一个特殊的函数,这个函数可以在某个地方挂起,并且可以重新在挂起处外继续运行。所以说,协程与进程、线程相比并不是一个维度的概念。
一个进程可以包含多个线程,一个线程也可以包含多个协程。简单来说,一个线程内可以由多个这样的特殊函数在运行,但是有一点必须明确的是,一个线程的多个协程的运行是串行的。如果是多核CPU,多个进程或一个进程内的多个线程是可以并行运行的,但是一个线程内协程却绝对是串行的,无论CPU有多少个核。毕竟协程虽然是一个特殊的函数,但仍然是一个函数。一个线程内可以运行多个函数,但这些函数都是串行运行的。当一个协程运行时,其它协程必须挂起。
协程的作用:
协程只有在非常有限制的情况下,才有一些用处,在单进程单线程任务中的交互之暇,才有它的用武之地。
协程算是用户态的线程,优势主要是少了内核态用户态的切换和能自己来做调度。 然后协程一般只在有IO操作的时候才能用到,对于一些会阻塞的IO操作,可以自己选择协程切换,等IO就绪了再切回来,可以更充分利用CPU。
进程和线程的切换者都是操作系统,而协程的切换者是用户,换时机是用户自己的程序所决定的。
进程间通信方式IPC
-
共享内存
共享内存是最快的进程间通讯的方式
相对于其他几种方式,共享内存直接在进程的虚拟地址空间进行操作,不再通过执行进入内核的系统调用来传递彼此的数据
-
信号
信号量和以前的IPC通信方式不同,信号量的本质是计数器,用于多进程对共享数据对象的访问。
在进程访问临界资源之前,需要测试信号量,如果为正数,则信号量-1并且进程可以进入临界区,若为非正数,则进程挂起放入等待队列,直至有进程退出临界区,释放资源并+1信号量,此时唤醒等待队列的进程。
信号量本身就是临界资源,所以必须是原子操作。
-
管道
单向,一端输入,另一端输出,先进先出FIFO。管道也是文件。管道大小4096字节。
特点:管道满时,写阻塞;空时,读阻塞。
分类:普通管道(仅父子进程间通信)位于内存;命名管道位于文件系统,没有亲缘关系管道只要知道管道名也可以通讯。
-
匿名管道与命名管道的区别:
匿名管道由pipe函数创建并打开。
命名管道由mkfifo函数创建,打开用open
FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
-
消息队列
消息队列是先进先出FIFO原则
-
信号量
是一个计数器,用来控制多个进程对资源的访问,它通常作为一种锁机制。
用户态和核心态
概念
用户态:
当一个进程在执行用户自己的代码时处于用户运行态(用户态),此时特权级最低,为3级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态。Ring3状态不能访问Ring0的地址空间,包括代码和数据;
内核态:
当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态),此时特权级最高,为0级。执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。
区别
- 处于用户态执行时,进程所能访问的内存空间和对象受到限制,其所处于占有的处理机是可被抢占的
- 而处于核心态执行中的进程,则能访问所有的内存空间和对象,且所占有的处理机是不允许被抢占的。
操作系统分配的进程空间是怎样的?线程能共享哪些?
程序计数器,线程私有
虚拟机栈,线程私有
本地方法栈,线程私有
堆,线程公有
方法区,线程公有
操作系统内存管理方式,分页分段以及段页式的优缺点
内存管理方式
内存管理方式主要分为:页式管理、段式管理和段页式管理。
分页存储管理:
页表是系统为进程建立的数据结构,通过页表实现从页(逻辑地址)到页框(物理地址)的映射。
分段存储管理:
进程的地址空间被划分成若干个段,系统为每个段分配一个连续的物理内存区域,各个不同的段可以离散的放在物理内存不同的区域。
段页式存储管理:
将用户进程的逻辑空间先划分为若干个段,每个段再划分为若干个页。进程以页为单位在物理内存中离散存放,每个段中被离散存放的页具有逻辑相关性。
分页分段以及段页式的优缺点
页式:
优点:没有外碎片,每个内碎片不超过页的大小。
缺点:程序全部装入内存,要求有相应的硬件支持,如地址变换机构缺页中断的产生和选择淘汰页面等都要求有相应的硬件支持。增加了机器成本和系统开销。
段式:
优点:可以分别编写和编译,可以针对不同类型的段采取不同的保护,可以按段为单位来进行共享,包括通过动态链接进行代码共享。
缺点:会产生碎片。
段页式:
优点:段页式管理是段式管理和页式管理相结合而成,具有两者的优点。
缺点:由于管理软件的增加,复杂性和开销也增加。另外需要的硬件以及占用的内存也有所增加,使得执行速度下降。
页面置换算法有哪些,FIFO为什么不好?如何改进?LRU思想,手写LRU
-
最佳置换算法(OPT)(理想置换算法):从主存中移出永远不再需要的页面;如无这样的页面存在,则选择最长时间不需要访问的页面。于所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。 即被淘汰页面是以后永不使用或最长时间内不再访问的页面。(往后看)
访问页面 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 物理块1 7 7 7 2 2 2 2 2 7 物理块2 0 0 0 0 4 0 0 0 物理块3 1 1 3 3 3 1 1 缺页否 √ √ √ √ √ √ √ √ -
先进先出置换算法(FIFO):是最简单的页面置换算法。这种算法的基本思想是:当需要淘汰一个页面时,总是选择驻留主存时间最长的页面进行淘汰,即先进入主存的页面先淘汰。其理由是:最早调入主存的页面不再被使用的可能性最大。 即优先淘汰最早进入内存的页面。(往前看)
访问页面 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 物理块1 7 7 7 2 2 2 4 4 4 0 0 0 7 7 7 物理块2 0 0 0 3 3 3 2 2 2 1 1 1 0 0 物理块3 1 1 1 0 0 0 3 3 3 2 2 2 1 缺页否 √ √ √ √ √ √ √ √ √ √ √ √ √ √ √ -
最近最久未使用(LRU)算法:这种算法的基本思想是:利用局部性原理,根据一个作业在执行过程中过去的页面访问历史来推测未来的行为。它认为过去一段时间里不曾被访问过的页面,在最近的将来可能也不会再被访问。所以,这种算法的实质是:当需要淘汰一个页面时,总是选择在最近一段时间内最久不用的页面予以淘汰。 即淘汰最近最长时间未访问过的页面。(往前看)
再对上面的实例釆用LRU算法进行页面置换,如图所示。进程第一次对页面2访问时,将最近最久未被访问的页面7置换出去。然后访问页面3时,将最近最久未使用的页面1换出。
访问页面 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 物理块1 7 7 7 2 2 4 4 4 0 1 1 1 物理块2 0 0 0 0 0 0 3 3 3 0 0 物理块3 1 1 3 3 2 2 2 2 2 7 缺页否 √ √ √ √ √ √ √ √ √ √ √ √ 实际上,LRU算法根据各页以前的情况,是“向前看”的,而最佳置换算法则根据各页以后的使用情况,是“向后看”的。
-
时钟(CLOCK)置换算法
LRU算法的性能接近于OPT,但是实现起来比较困难,且开销大;FIFO算法实现简单,但性能差。所以操作系统的设计者尝试了很多算法,试图用比较小的开销接近LRU的性能,这类算法都是CLOCK算法的变体。 简单的CLOCK算法是给每一帧关联一个附加位,称为使用位。当某一页首次装入主存时,该帧的使用位设置为1;当该页随后再被访问到时,它的使用位也被置为1。对于页替换算法,用于替换的候选帧集合看做一个循环缓冲区,并且有一个指针与之相关联。当某一页被替换时,该指针被设置成指向缓冲区中的下一帧。当需要替换一页时,操作系统扫描缓冲区,以查找使用位被置为0的一帧。每当遇到一个使用位为1的帧时,操作系统就将该位重新置为0;如果在这个过程开始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换;如果所有帧的使用位均为1,则指针在缓冲区中完整地循环一周,把所有使用位都置为0,并且停留在最初的位置上,替换该帧中的页。由于该算法循环地检查各页面的情况,故称为CLOCK算法,又称为最近未用(Not Recently Used, NRU)算法。
FIFO为什么不好?如何改进?
FIFO原理:按照“先进先出(First In,First Out)”的原理淘汰数据
FIFO的实现:
- 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动;
- 淘汰FIFO队列头部的数据;
特点:
>> 命中率 命中率很低,因为命中率太低,实际应用中基本上不会采用。
>> 复杂度 简单
>> 代价 实现代价很小
不好的原因:命中率很低,因为命中率太低,实际应用中基本上不会采用。
改进: --> Second Chance
FIFO改进版,如果被淘汰的数据之前被访问过,则给其第二次机会(Second Chance)
实现:
每个数据会增加一个访问标志位,用于标识此数据放入缓存队列后是否被再次访问过。
如上图,A是FIFO队列中最旧的数据,且其放入队列后没有被再次访问,则A被立刻淘汰;否则如果放入队列后被访问过,则将A移到FIFO队列头,并且将访问标志位清除。
如果所有的数据都被访问过,则经过一次循环后就会按照FIFO的原则淘汰数据。
特点:
>> 命中率 命中率比FIFO高。
>> 复杂度 与FIFO相比,需要记录数据的访问标志位,且需要将数据移动
>> 代价 实现代价比FIFO高
死锁条件,解决方式。
什么是死锁?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
死锁条件
死锁的四个必要条件:
- 互斥条件:对所分配到的资源不允许其他线程进行访问,若其他线程访问该资源,只能等待,直至占有该资源的线程使用完成后释放该资源
- 请求和保持条件:线程程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他线程占有,此时请求阻塞,但又对自己获得的资源保持不放
- 不可剥夺条件:线程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
- 环路等待条件:是指线程发生死锁后,若干线程之间形成一种头尾相接的循环等待资源关系
如何防止死锁
以上四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之 一不满足,就不会发生死锁。
所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。