【计算机基础】面试常问之进程、线程和协程

2023-11-04

文章目录



【背景】为什么会有进程、线程和协程?

由于CPU与其他资源之间速度的不协调,为了提高资源利用率,提出了多任务系统

  • 得益于CPU的计算速度,我们可以“同时”运行多个任务,即出现了并发,实质上是多个任务之间轮流使用CPU资源,由于速度超快,给用户的感觉就是连续的。
  • 内部可以是多CPU并行,也可以是单CPU时间分片,能快速的切换逻辑流,看起来像是同时跑的就行。

假如有两个任务A和B,需要读取大量的数据输入(I/O操作),而其实CPU只能处在等待状态,等任务A读取完数据再能继续进行,这样就白白浪费了CPU资源。

  • 于是人们就想,能否在任务A读取数据的过程中,让任务B去执行,当任务A读取完数据之后,暂停任务B,让任务A继续执行?

但是多个程序一起跑的时候,出现了一些问题,比如:

  • A计算到一半,刚把多次方程解到最后一步,B突然插进来,那么A的中间状态怎么办,A用来储存的内存被B覆盖了怎么办

所以跑在一个CPU里面的并发都需要处理上下文切换的问题。

1、进程的出现:

进程是操作系统对一个正在运行的程序的一个抽象,搭配虚拟内存、进程表之类的东西,用来管理独立的程序运行、切换。

  • 每个进程对应一定的内存地址空间,并且只能使用它自己的内存空间,各个进程之间互不干扰。
  • 进程同时也保存了程序每个时刻的运行状态,为进程切换提供了可能。
    • 当进程暂停时,它会保存当前进程的状态(进程标识、进程使用的资源等),在下一次切换回来时根据之前保存的状态进行恢复,接着继续执行。

出现了进程之后,性能得到了大大的提升。虽然进程的出现解决了并发问题,但是人们不满足,逐渐对实时性有了要求

  • 因为程序的使用涉及大量的计算机资源配置,如果给用户程序进行操作,非常容易搞崩整个系统,资源分配也很难做到相对的公平。

    • 所以核心的操作需要陷入内核(kernel),切换到操作系统来安排。
  • 但是切换进程得反复进入内核,置换掉一大堆状态。

当进程数过高时,大部分系统资源就被进程切换给消耗掉了。

2、线程的出现:

将一个进程拆分成多个称为线程的执行单元,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。

  • 这样一个进程包含多个线程,但是这些线程共享进程占有的内存地址空间和资源
  • 因此,如果A处阻塞了,但还有其他地方的逻辑流可以计算,而这些逻辑流是共享一个地址空间的,这样就不用特别麻烦地切换页表、刷新TLB,只要把寄存器刷新一遍就行,能比切换进程开销少点

进程让操作系统的并发性成为了可能,而线程让进程的内部并发成为了可能。

  • 进程是操作系统进行资源分配的基本单位(进程之间互不干扰)。
  • 线程是操作系统进行调度的基本单位(线程间互相切换)。

如果连时钟阻塞、线程切换这些功能都不用,自己在进程里面写一个逻辑流调度的东西,那么我们既可以利用到并发优势,又可以避免反复系统调用以及进程切换造成的开销,分分钟给你上几千个逻辑流不费力,这就是用户态线程

实现一个用户态线程有两个必须要处理的问题:

  • 碰到阻塞式I\O会导致整个进程被挂起
  • 由于缺乏时钟阻塞,进程需要自己拥有调度线程的能力

3、协程的出现:

如果一种实现使得每个线程需要自己通过调用某个方法,主动交出控制权,那么我们就称这种用户态线程是协作式的,即是协程。

  • 可以把协程看作是用户空间下的线程,但是它并不会参与 CPU 时间调度,并没有均衡分配到时间。

协程的发明主要是为了解决 Concurrency(并发)问题,而线程的发明主要解决的 Parallelism(并行)问题。

  • 它的出现本质是为了能让多组过程能不独自占用完所有资源,交叉执行。


1)进程(Process)

1.1 什么是进程?

进程(process)是操作系统对一个正在运行的程序的一种抽象,即进程是正在运行的程序的实例

  • 进程的概念主要有两点:
    • 进程是一个实体,每个进程都有自己的地址空间,一般包括文本区域、数据区域、和堆栈。
    • 进程是一个执行中的程序,程序只是一堆代码,只有当操作系统分配资源(CPU时间片、内存等)给程序时,才有了进程。

*程序与进程的区别?

  • 程序是一堆代码和数据。
  • 进程是执行中程序的一个具体的实例。
  • 系统中的每个程序都运行在某个进程的上下文中。
    • 上下文是由程序正常运行所需的状态组成的,包括存放在内存中的程序的代码和数据等。

特点:

  • 并发运行:一个进程的指令和另一个进程的指令交错执行。
  • 上下文切换:无论是在单核还是多核系统中,一个CPU看上去都像是在并发地执行多个进程,这是通过处理器在进程间切换来实现的。
    • 操作系统实现这种交错执行的机制称为上下文切换,即保存当前进程的上下文,恢复新进程的上下文,然后将控制权传递给新进程
      • 上下文(context)操作系统保持和跟踪进程运行所需要的所有状态信息,这种状态就是上下文,包括如PC和寄存器文件的当前值,以及内存的内容。

实现进程这个抽象概念需要低级硬件和操作系统软件之间的紧密合作。

从一个进程到另一个进程的转换是由操作系统内核(kernel)管理的。

  • 内核:操作系统代码常驻主存的部分,不是一个独立的进程,是系统管理全部进程所用的代码和数据结构的集合
  • 当应用程序需要操作系统的某些操作时,比如读写文件,它就会执行一条特殊的系统调用(system call)指令,将控制器传递给内核。然后内核执行被请求的操作并返回应用程序

1.2 进程通信及使用场景

进程间通信(IPC,InterProcess Communication)的主要方式包括:管道、FIFO、消息队列、信号量、共享内存、socket。

  • 无名管道(pipe):

    将一个程序的输出交给另一个程序进行处理。
    • 局限性:半双工通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用
    • 通常,一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父进程和子进程之间使用了。

  • 命名管道(FIFO):

    FIFO其实是一种文件类型,创建FIFO类似于创建文件。
    • 与无名管道不同的是,可以在不相关的程序之间交换数据。
    • 两种用途:
      • shell命令使用FIFO将数据从一条管道传送到另一条管道时,无须创建中间的临时文件。
      • 客户进程-服务器进程应用程序中,FIFO用作汇聚点,在客户进程和服务器进程二者之间传递数据。

  • 消息队列(message queue):

    消息队列是消息的链接表,存储在内核中,有消息队列标识符标识。
    • msgget用于创建一个新的消息队列或打开一个现有队列。msgsnd用于将新消息添加到队列尾端。msgrcv用于从队列中去消息。
    • 不一定以先进先出的顺序取消息,可以按照消息的类型字段取消息。

  • 信号量(semophore):

    信号量是一个计数器,可以用来控制多个进程对共享资源的访问。
    • 它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。
    • 因此,主要作为进程间以及同一进程内不同线程之间的同步手段

  • 共享内存( shared memory ):

    共享存储允许两个或多个进程共享一个给定的存储区。
    • 因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的IPC。
    • 它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
      • 信号量用于同步共享存储访问。

  • 套接字( socket ) :

    套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

*几种IPC方式优缺点比较

  • 如果用户传递的信息较少,或者只是为了触发某些行为。信号是一种简洁有效的通信方式。但若是进程间要求传递的信息量较大或者存在数据交换的要求,就需要考虑别的通信方式了。
  • 无名管道与有名管道的区别在于单向通信以及有关联的进程
  • 消息队列允许任意进程通过共享队列来进行进程间通信。并由系统调用函数来实现消息发送和接收之间的同步。从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题,使用相对方便。
    • 但是消息队列中信息的复制需要耗费CPU时间,不适宜信息量大或频繁操作的场合。
  • 消息队列与管道方式的区别在于,消息队列可以实现多对多,并需要在内存中实现,而管道可以在内存或磁盘上实现。
  • 共享内存无须复制,信息量大是其最大的优势。但是需要考虑同步问题。

1.3 进程控制相关

  • 进程ID:

    • 每个进程都有一个唯一的正数(非零)进程ID(PID)。

  • 创建和终止进程:

    • 进程的三种状态(从程序员角度):
      • 运行:进程要么在CPU上执行,要么在等待被执行且最终会被内核调度。
      • 停止:进程的执行被挂起(suspended),且不会被调度。
      • 终止:进程被永远地停止了。
    • 父进程通过调用fork函数创建一个新的运行的子进程。
      • 新进程几乎但不完全与父进程相同,最大的区别在于它们有不同的PID。

1.4 进程调度

  • 无论是在批处理系统还是分时系统中,用户进程数一般都多于处理机数,这将导致它们互相争夺处理机
  • 另外,系统进程也同样需要使用处理机。

这就要求进程调度程序按一定的策略,动态地把处理机分配给处于就绪队列中的某一个进程,以使之执行

1.4.1 进程的四个基本属性

  • 多态性:从诞生,运行,直至消灭。
  • 多个不同的进程可以包括相同的程序。
  • 三种基本状态:它们之间可进行转换。
  • 并发性:并发执行的进程轮流占用处理器。

1.4.2 进程的三种基本状态

  • 等待态:等待资源或者某个事件的完成。
  • 就绪态:等待系统分配处理器以便运行。
  • 运行态:占有处理器,正在运行。
  • 运行态→等待态:往往是由于等待外部设备,等待主存等资源分配或等待人工干预而引起的。
  • 等待态→就绪态:等待的条件已满足,只需分配到处理器后就能运行。
  • 运行态→就绪态:不是由于自身原因,而是外界原因使运行状态的进程让出处理器,这时候就变成就绪态。
    • 例如时间片用完,或有更高优先级的进程来抢占处理器等。
  • 就绪态→运行态:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态。

1.4.3 进程调度的方式

  • 非剥夺方式:

    分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生某事件而阻塞时,才把处理机分配给另一个进程。
  • 剥夺方式:

    当一个进程正在运行时,系统可以基于某种原则,剥夺已分配给它的处理机,将之分配给其它进程。
    • 剥夺原则有:优先权原则、短进程优先原则、时间片原则。
    • 衡量进程调度性能的指标有:周转时间、响应时间、CPU-I/O执行期。

1.4.4 进程调度算法

  • 先进先出算法(FIFO):

    总是把处理机分配给最先进入就绪队列的进程,一个进程一旦分得处理机,便一直执行下去,直到该进程完成或阻塞时,才释放处理机。
    • FIFO算法服务质量不佳,容易引起作业用户不满,常作为一种辅助 调度算法 。
  • 最短CPU运行期优先调度算法(SCBF):

    Shortest CPU Burst First,从就绪队列中选出下一个“CPU执行期最短”的进程,为之分配处理机。
    • 该算法虽可获得较好的调度性能,但难以准确地知道下一个CPU执行期,而只能根据每一个进程的执行历史来预测。
  • 时间片轮转法:

    简单轮转法:系统将所有就绪进程按FIFO规则排队,按一定的时间间隔把处理机分配给队列中的进程。
    • 在分时系统中,都采用时间片轮转法。
  • 多级反馈队列:

    将系统中所有进程分成若干类,每类为一级,在系统中设置多个就绪队列,并赋予各队列以不同的优先权。


2)线程(Thread)

2.1 什么是线程?

线程(thread)是允许应用程序并发执行多个任务的一种机制。

  • 一个进程可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并且共享同样的代码和全局数据
    • 同一程序中的所有线程均会独立执行相同程序,且共享同一份全局内存区域,其中包括初始化数据段(initialized data)、未初始化数据段(uninitialized data),以及堆内存段(heap segment)。
  • 同一进程中的多个线程可以并发执行。在多处理器环境下,多个线程可以同时并行。如果一线程因等待I/O操作而遭阻塞,那么其他线程依然可以继续运行。


3)协程(Coroutine)

3.1 什么是协程?

协程,是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(即在用户态执行)

  • Coroutine的实现,通常是对某个语言做相应的提议,然后通过后成编译器标准,然后编译器厂商来实现该机制。

不同的程序员对理解协程的切入点不一样,侧重点不一样:

  • 熟悉JAVA、.net的程序员,是基于多线程来理解的,协程=线程。
    • 他们比较熟悉多线程,因此最直观的的理解是,协程就是一个不由OS内核抢占调度,而由程序管理在用户态自管理的协作式“线程”
    • 不使用线程,就减少了OS的线程数,也就:
      • 省去了cpu线程切换的开销
      • 降低了内存消耗
      • 提高了cpu缓存命中率
      • 整体上提高了性能
      • 不提高硬件的前提下,提升了系统的负载能力
    • 他们是从对比线程,从系统整体的的角度来理解协程的。

  • 熟悉js、lua的程序员,是基于回调理解的,协程=回调调度器。
    • 对于更熟悉单线程回调模式的程序员而言,不会考虑太多线程的问题,而回调却是常需要面对的问题。
    • 他们需要一种方式来避免杂乱且不易读的回调,协程对他们而言就是一个调度器,回调的事全交给协程来解决。
    • 他们可以写yield写看上去更像同步的代码,把回调的事推给协程的调度器。
    • 他们是从开发的角度以语法糖来理解协程的。

3.2 协程的优势

协程通常是纯软件实现的多任务,与CPU和操作系统通常没有关系,所以没有理论上限。

  • 极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
  • 不需要多线程的锁机制:因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
  • 跨平台、跨体系架构


4)面试常问之进程和线程的区别?

传统UNIX通过创建多个进程来实现并行任务

  • 网络服务器的设计为例,服务器进程(父进程)在接受客户端的连接后,会调用fork()来创建一个单独的子进程,以处理与客户端的通信
  • 采用这种设计,服务器就能同时为多个客户端提供服务。虽然这种方法在很多情境下都屡试不爽,但对于某些应用来说也确实存在如下一些限制。

1、信息共享和效率

  • 对于进程:

    • 进程间的信息难以共享。由于除去只读代码段外,父子进程并未共享内存,因此必须采用一些进程间通信(inter-process communication,简称IPC)方式,在进程间进行信息交换。
    • 调用 fork()来创建进程的代价相对较高。即便利用写时复制(copy-on-write)技术,仍然需要复制诸如内存页表(page table)和文件描述符表(file descriptor table)之类的多种进程属性,这意味着fork()调用在时间上的开销依然不菲。

  • 但线程解决了上述两个问题:

    • 线程之间能够方便、快速地共享信息。只需将数据复制到共享(全局或堆)变量中即可。
      • 不过,要避免出现多个线程试图同时修改同一份信息的情况,这需要使用同步技术
    • 创建线程比创建进程通常要快10倍甚至更多。
      • 线程的创建之所以较快,是因为调用 fork()创建子进程时所需复制的诸多属性,在线程间本来就是共享的。特别是,既无需采用写时复制来复制内存页,也无需复制页表。

2、地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间。

3、资源:进程是资源分配的基本单位,同一个进程内的线程共享进程的资源。

4、线程是处理器调度的基本单位,但进程不是。

5、每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。



5)协程和线程的区别?

1、线程进程都是同步机制,而协程则是异步

2、线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力



【部分内容参考自】

  • 《深入理解计算机系统》
  • 《操作系统导论》
  • 进程和线程的由来与区别:https://blog.csdn.net/whl_program/article/details/70217354
  • 线程和进程的区别是什么?:https://www.zhihu.com/question/25532384
  • 进程间的八种通信方式:https://www.cnblogs.com/lmh001/p/9754122.html
  • 进程间通信及使用场景:https://www.jianshu.com/p/4989c35c9475
  • 进程调度详细总结:https://blog.csdn.net/kavensu/article/details/8075642
  • 协程的好处有哪些?:https://www.zhihu.com/question/20511233
  • 进程、线程和协程之间的区别和联系:https://blog.csdn.net/daaikuaichuan/article/details/82951084
  • 协程与线程的区别:https://blog.csdn.net/fadbgfnbxb/article/details/88787361
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【计算机基础】面试常问之进程、线程和协程 的相关文章

  • 微信小程序滚动Tab选项卡:左右可滑动切换

    最终效果如上 问题 1 tab标题总共8个 所以一屏无法全部显示 2 tab内容区左右滑动切换时 tab标题随即做标记 active 3 当active的标题不在当前屏显示时 要使其能显示到当前屏中 一 wxml结构 tab标题因一排八个

随机推荐