跃迁引擎

空気を読んだ雨降らないでよ

iOS Research & Development


进程与线程

我们常说的进程和线程听起来名字很像,但他们之间有着什么样的联系呢?

———– 2020年11月23日更新 ———–

本文更新内容摘录自《进程和线程,我只问这19个问题》

进程

什么是进程?

进程是一个具有一定独立功能的程序在一个数据集合上依次动态执行的过程。进程是一个正在执行程序的实例,包括程序计数器、寄存器和程序变量的当前值。

简单来说进程就是一个程序的执行流程,内部保存程序运行所需的资源

在操作系统中可以有多个进程在运行,可对于CPU来说,同一时刻,一个CPU只能运行一个进程,但在某一时间段内,CPU将这一时间段拆分成更短的时间片,CPU不停的在各个进程间游走,这就给人一种并行的错觉,像CPU可以同时运行多个进程一样,这就是伪并行

进程和程序有什么联系

一个进程是某种类型的一个活动,它有程序、输入、输出以及状态。单个处理器可以被若干进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务。

  • 程序是产生进程的基础
  • 程序的每次运行产生不同的进程
  • 进程是程序功能的体现
  • 通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序

进程和程序有什么区别

  • 进程是动态的,程序是静态的:程序是有序代码的集合,进程是程序的执行。
  • 进程是暂时的,程序是永久的:进程是一个状态变化的过程,程序可长久保存。
  • 进程和程序的组成不同:进程的组成包括程序、数据和进程控制块(进程状态信息),而程序是一组计算机能识别和执行的指令,它以某些程序设计语言编写,运行于某种目标结构体系上。

进程有什么特点

  • 动态性:可动态的创建和结束进程
  • 并发性:可以被独立的调度并占用处理机并发运行
  • 独立性:不同进程的工作不相互影响
  • 制约性:因访问共享资源或进程间同步而产生制约

进程如何创建

  • 系统初始化:当启动操作系统时,通常会创建很多进程,有些是同用户交互并替他们完成工作的前台进程,其它的都是后台进程
  • 正在运行的程序执行了创建进程的系统调用:在一个进程中又创建了一个新的进程,这种情况很常见。
  • 用户请求创建一个新进程:用电脑时双击某个应用图标,就会有至少一个进程被创建
  • 一个批处理作业的初始化:这种情形不常见,仅在大型机的批处理系统中应用,用户在这种系统中提交批处理作业,在操作系统认为有资源可运行另一个作业时,它创建一个新的进程,并运行其输入队列中的下一个作业

进程创建之后,父子进程都有各自不同的地址空间,子进程的初始化空间是父进程的一个副本。

进程终止的原因

  • 正常退出(自愿):进程完成了工作正常终止
  • 出错退出(自愿):进程发现了错误而退出
  • 严重错误(非自愿):进程发生了严重的错误而不得不退出
  • 被其它进程杀死(非自愿):其它进程执行kill系统调用通知操作系统杀死某个进程

进程的生命周期

  • 创建

  • 运行

  • 等待

    在以下情况下进程会等待(阻塞):

    • 请求并等待系统服务,无法马上完成
    • 启动某种操作,无法马上完成
    • 需要的数据没有到达。

    注意:进程只能自己阻塞自己,因为只有进程自身才能知道何时需要等待某种事件的发生。

  • 唤醒

    进程只能被别的进程或操作系统唤醒,唤醒进程的原因有:

    • 被阻塞进程需要的资源可被满足
    • 被阻塞进程等待的事件到达
    • 将该进程的PCB插入到就绪队列
  • 结束

    在以下四种情况下进程会结束:

    • 自愿型正常退出
    • 自愿型错误退出
    • 强制型致命错误退出
    • 强制型被其它进程杀死退出

进程的状态

  • 运行状态:进程正在处理机上运行时就处在运行状态,该时刻进程始终占用着CPU
  • 就绪状态:进程已经获得了除处理机之外的一切所需资源,一旦得到处理机就可以运行(就绪态中的进程其实可以运行,但因为其它进程正在占用着CPU而暂时停止运行)
  • 等待状态(阻塞状态):进程正在等待某一事件而暂停运行,等待某个资源或者等待输入输出完成(除非某种外部事件发生,否则阻塞态的进程不能运行)

进程为何会出现挂起

进程挂起就是为了合理且充分的利用系统资源,把一个进程从内存转到外存。

进程在挂起状态时,意味着进程没有占用内存空间,处在挂起状态的进程映射在磁盘上。进程挂起通常有两种状态:

  • 阻塞挂起状态:进程在外存并等待某事件的出现
  • 就绪挂起状态:进程在外存,但只要进入内存即可运行

进程挂起的几种可能:

  • 阻塞到阻塞挂起:没有进程处于就绪状态或就绪进程要求更多内存资源时,会进行这种转换,以提交新进程或运行就绪进程
  • 就绪到就绪挂起:当有高优先级阻塞进程或低优先级就绪进程时,系统会选择挂起低优先级就绪进程
  • 运行到就绪挂起:对于抢占式分时系统,当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时,系统可能会把运行进程转到就绪挂起状态
  • 阻塞挂起到就绪挂起:当有阻塞挂起进程有相关事件出现时,系统会把阻塞挂起进程转换为就绪挂起进程

有进程挂起那就有进程解挂:指一个进程从外存转到内存,相关状态有

  • 就绪挂起到就绪:没有就绪进程或就绪挂起进程优先级高于就绪进程时,就会进行这种转换
  • 阻塞挂起到阻塞:当一个进程释放足够内存时,系统会把一个高优先级阻塞挂起进程转换为阻塞进程

进程间通讯有几种方式

进程间通信(IPC,Inter-Process Communication),由于各个进程不共享相同的地址空间,任何一个进程的全局变量在另一个进程中都不可见,所以如果想要在进程之间传递数据就需要通过内核,在内核中开辟出一块区域,该区域对多个进程都可见,即可用于进程间通信。

如何开辟这种公共区域来进行进程间通信

  • 匿名管道:匿名管道就是pipe,pipe只能在父子进程间通信,而且数据只能单向流动(半双工通信)

  • 高级管道:通过popen将另一个程序当作一个新的进程在当前进程中启动,它算作当前进程的子进程,高级管道只能用在有亲缘关系的进程间通信,这种亲缘关系通常指父子进程

  • 命名管道:命名管道其实就是一种特殊类型的文件,所谓的命名其实就是文件名,文件对各个进程都可见,通过命名管道创建好特殊文件后,就可以实现进程间通信。匿名管道有个缺点就是通信的进程一定要有亲缘关系,而命名管道就不需要这种限制

  • 消息队列:本质上MessageQueue是存放在内核中的消息链表,每个消息队列链表会由消息队列标识符表示,这个消息队列存于内核中,只有主动的删除该消息队列或者内核重启时,消息队列才会被删除(消息队列读消息不一定要使用先进先出(FIFO)的顺序,每个消息可以赋予类型,可以按消息的类型读取,不是指定类型的数据还存在队列中)

  • 共享内存:可开辟中一块内存,用于各个进程间共享,使得各个进程可以直接读写同一块内存空间,该方式基本上是最快的进程间通信方式,因为没有系统调用干预,也没有数据的拷贝操作,但由于共享同一块地址空间,数据竞争的问题就会出现,需要自己引入同步机制解决数据竞争问题

  • 信号:信号也是进程间通信的一种方式,信号可以在任何时候发送给某一个进程,如果进程当前并未处于执行状态,内核将信号保存,直到进程恢复到执行态再发送给进程,进程可以对信号设置预处理方式,如果对信号设置了阻塞处理,则信号的传递会被延迟直到阻塞被取消,如果进程结束,那信号就被丢弃。我们常用的CTRL+C和kill等就是信号的一种,也达到了进程间通信的目的,进程也可以对信号设置signal捕获函数自定义处理逻辑。这种方式有很大的缺点:只有通知的作用,通知了一下消息的类型,但不能传输要交换的任何数据

  • 信号量:信号量就是一个特殊的变量,程序对其访问都是原子操作,每个信号量开始都有个初始值。信号量有两个操作,P和V:P:如果信号量变量值大于0,则变量值减1,如果值为0,则阻塞进程;V:如果有进程阻塞在该信号量上,则唤醒阻塞的进程,如果没有进程阻塞,则变量值加1。

    • 信号量和信号有什么关系:没有任何关系,完全是不同的东西。
    • 信号量与互斥量有什么区别:互斥量用于互斥,信号量用于同步,互斥指的是某一资源同一时间只允许一个访问者访问,但无法限制访问顺序,访问是无序的,而同步在互斥的基础上可以控制访问者对资源的顺序
  • 套接字:就是网络传输,网络通信都可以多机通信呢,更不用说进程间通信

  • 文件:显而易见,多个进程可以操作同一个文件,所以也可以通过文件来进行进程间通信

线程

什么是线程

线程是进程当中的一条执行流程,一个进程内可以有多个子执行流程,即线程。

  • 从资源组合的角度:进程把一组相关的资源组合起来,构成一个资源平台环境,包括地址空间(代码段、数据段),打开的文件等各种资源

  • 从运行的角度:代码在这个资源平台上的执行流程,然而线程貌似也是这样,但是进程比线程多了资源内容列表样式:那就有一个公式:进程 = 线程 + 共享资源

为什么使用线程

因为需要并发编程,如果使用多进程方式进行并发编程,进程间的通信也很复杂,并且维护进程的系统开销较大(创建进程时分配资源建立PCB,撤销进程时回收资源撤销PCB,进程切换时保存当前进程的状态信息)。

为了使并发编程的开销尽量小,所以引入多线程编程,它不仅可以并发执行也可以共享相同的地址空间。并行实体拥有共享同一地址空间和所有可用数据的能力,这是多进程模型所不具备的能力。

使用线程的优点:

  • 可以多个线程存在于同一个进程中
  • 各个线程之间可以并发的执行
  • 各个线程之间可以共享地址空间和文件等资源
  • 线程比进程更轻量级,创建线程撤销线程比创建撤销进程要快的多(在许多系统中,创建一个线程速度是创建一个进程速度的10-100倍)
  • 如果多个线程是CPU密集型的,并不能很好的获得更好的性能,但如果多个线程是IO密集型的,线程存在着大量的计算和大量的IO处理,有多个线程允许这些活动彼此重叠进行,从而会加快整体程序的执行速度

使用线程的缺点:

  • 一旦一个线程崩溃,会导致其所属进程的所有线程崩溃
  • 由于各个线程共享相同的地址空间,那么读写数据可能会导致竞争关系,因此对同一块数据的读写需要采取某些同步机制来避免线程不安全问题

什么时候用进程,什么时候用线程

进程和线程的区别:

  • 进程是资源分配单位,线程是CPU调度单位
  • 进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈
  • 线程同样具有就绪阻塞和执行三种基本状态,同样具有状态之间的转换关系
  • 线程能减少并发执行的时间和空间开销
    • 线程的创建时间比进程短
    • 线程的终止时间比进程短
    • 同一进程内的线程切换时间比进程短
    • 由于同一进程的各线程间共享内存和文件资源,可直接进行不通过内核的通信

我们可以得出结论:可以在强调性能时候使用线程,如果追求更好的容错性可以考虑使用多进程,在多CPU系统中,多线程是有益的,在这样的系统中,通常情况下可以做到真正的并行。

最近的文章

2018 年终总结

2018 年终总结,记录在今年的最后一天工作日。本来只想发个沸点,但是写着写着发现字数太多了… ⇎_⇎ 总感觉是,碌碌无为。 关于工作: 年中的时候,送走了合作一年多的老搭档,一个月后迎来了一位新搭档,嗯,三个月后又送走了她。 年初开发沸点的时候,遇到性能瓶颈,开发周期也非常紧张,做了个冒险的决 …

, 开始阅读
更早的文章

难追难回味

你瞧这些白云聚了又聚,散了又散,人生离合,亦复如斯。 人们都说,这是金庸对人生的感悟,将人世的分分合合看的很透彻。 大概他们都忘了,这句话的后半段写的是什么: 她话虽如此说, 却也忍不住流下泪来。 道理我们都明白,聚散苦匆匆,可是真正经历下来,又有谁能独善其身呢。 知与谁同“啊,难受” 一整 …

, 开始阅读
comments powered by Disqus