进程与线程的关系

进程和线程的概念

进程
进程是对运行时程序的封装,是系统进行资源调度和分配的基本单位,实现了操作系统的并发;一个进程由三部分组成:程序、数据及进程控制块(PCB)。进程控制块是记录进程有关信息的一块主存,是进程存在的程序唯一标识。
优点:内存隔离,单个进程的崩溃不会导致这个系统的崩溃。而且进程方便测试以及编程简单。
缺点:创建销毁比较麻烦,进程间数据的共享麻烦,并且消耗的资源比较多。

线程
线程是进程的子任务,是 CPU 调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发。(线程自身是不能拥有系统资源的,但是它可以拥有自己的堆、栈、局部变量以及程序计算器。)
优点:可以提高系统的并行性,数据共享比较方便,切换比较快。
缺点:没有内存隔离,一个线程的崩溃会导致整个进程的崩溃。编程复杂以及调试困难。

进程和线程的关系

数量上
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(通常说的主线程)
例如,Redis 中一个 Redis 进程包含很多线程,网络 IO 的、数据处理的等等。

资源共享上
资源分配给进程,同一进程的所有线程共享该进程的所有资源

同步上
线程在执行过程中,需要协作同步,不同进程的线程间要利用消息通信的方法实现同步

CPU 上
处理机分配给线程,即真正在处理机上运行的是线程
线程是指进程内的一个执行单元,也是进程内的可调度实体

进程和线程的区别

Ⅰ 拥有资源:进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程资源。一个进程包含多个线程。
Ⅱ 调度:线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。
Ⅲ 系统开销:由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。
Ⅳ 通信方面:线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。

进程中线程共有哪些资源,线程有哪些资源

线程私有资源(线程上下文):栈帧、程序计数器、栈指针、寄存器。

栈帧:函数返回、函数参数、局部变量、该函数使用的寄存器。

线程共用的存储:代码区、数据区(全局变量)、堆区、栈区、动态链接库、打开的文件。

代码区:保存的是程序编译后的可执行机器指令,它是从可执行文件中加载到内存的。线程之间共享代码区,这就意味着任何一个函数都可以放到线程中去执行不存在某个函数只能被特定线程执行的情况。

数据区:存放的全局变量,即定义在函数之外的变量。在程序运行期间(run time),数据区中的全局变量有且仅有一个实例,所有的线程都可以访问到该全局变量。

堆区:存放的是程序中通过动态申请内存得到的变量的存放位置。在堆区的变量,只要知道变量的地址,也就是指针,任何一个线程都可以访问指针指向的数据,数据进程的资源。

栈区:从抽象的概念上来说,栈区是线程私有的,但从实际的实现上来看,栈区属于线程私有这一规则并没有被严格遵守。通常情况下,栈区是线程私有的,存在不通常情况的原因是,与进程地址空间之间的严格隔离不同,线程的栈区之间没有严格的隔离机制,也就是说,线程之间是存在相互修改数据的可能性的。

动态链接库:可执行文件是由编译器生成的,这句话只对了一半。编译器在将可执行程序翻译成机器指令后,需要链接器链接目标文件后,才能生成可执行程序。

线程间共享了哪些资源 | popo の blog

协程与线程的区别

  • 数量上:一个线程可以有多个协程,一个进程也可以有多个协程。
  • 所属范畴:协程不被操作系统内核管理,而完全是由程序控制。线程是被分割的 CPU 资源,协程是组织好的代码流程,线程是协程的资源。但协程不会直接使用线程,协程直接利用的是执行器关联任意线程或线程池。
  • 同步异步:线程和进程都是同步机制,而协程是异步机制。
  • 抢占/非抢占:线程是抢占式,而协程是非抢占式的。需要用户释放使用权切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
  • 协程能保留上一次调用时的状态。

进程/线程的切换流程

进程切换分两步:
1、切换页表以使用新的地址空间,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。
2、切换内核栈和硬件上下文

线程切换只需:
切换内核栈和硬件上下文

每个进程有自己的虚拟地址空间,线程共享所在进程的虚拟地址空间。
因此,同进程内的线程切换不需要切换页表。

进程的状态 & 状态的切换

  • 创建状态(created) :进程正在被创建,尚未到就绪状态。
  • 就绪状态(ready) :进程已处于准备运行状态,即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源(处理器分配的时间片)即可运行。
  • 运行状态(running) :进程正在处理器上上运行(单核 CPU 下任意时刻只有一个进程处于运行状态)。
  • 阻塞状态(waiting) :又称为等待状态,进程正在等待某一事件而暂停运行如等待某资源为可用或等待 IO 操作完成。即使处理器空闲,该进程也不能运行。
  • 结束状态(terminated) :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行。

进程同步 vs 进程通信

  • 进程同步:控制多个进程按一定顺序执行;
  • 进程通信:进程间传输信息。

进程通信是一种手段,而进程同步是一种目的。也可以说,为了能够达到进程同步的目的,需要让进程进行通信,传输一些进程同步所需要的信息。

进程间的通信方式 IPC

大概有 7 种常见的进程间的通信方式。

下面这部分总结参考了: 《进程间通信 IPC (InterProcess Communication)》  这篇文章,推荐阅读,总结的非常不错。

  1. 管道/匿名管道(Pipes) :半双工的通信方式,数据只能单向流动,而且只能在具有父子进程或者兄弟进程中的进程间使用。
  2. 有名管道(Names Pipes) : 相较于管道,去除了只能在父子进程中使用的限制。严格遵循先进先出。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
  3. 信号(Signal) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
  4. 消息队列(Message Queuing) :消息队列是消息的链表, 具有特定的格式, 存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显示地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询, 消息不一定要以先进先出的次序读取, 也可以按消息的类型读取. 比 FIFO 更有优势。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺。
  5. 信号量(Semaphores) :信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
  6. 共享内存(Shared memory) :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。
  7. 套接字(Sockets) : 此方法主要用于在客户端和服务器之间(即不同设备进程)通过网络进行通信。套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。

进程间的同步方式

  1. 临界区
    对临界资源进行访问的那段代码称为临界区。

    优点:某一时刻只有一个线程访问数据。
    缺点:只适用于同一进程下的多个线程。

  2. 信号量
    信号量(Semaphore)是一个整型变量,可以对其执行 down 和 up 操作,也就是常见的 P 和 V 操作。down 和 up 操作需要被设计成原语,不可分割,通常的做法是在执行这些操作的时候屏蔽中断。

    优点:适用于跨进程同步,如对 Socket(套接字)程序中线程的同步。例如同一时间内访问同一页面的用户数进行控制,只有不大于设定的最大用户数的线程才可以访问,其他需要挂起。
    缺点:(1)必须有公共内存、不能用于分布式系统(2)操作分散,维护困难(3)PV 操作不易控制管理,容易出错。

  3. 互斥量
    信号量为 1 时,互斥量。如果信号量的取值只能为 0 或者 1,那么就成为了互斥量(Mutex),0 表示临界区已经加锁,1 表示临界区解锁。

    优点:适用于跨进程同步。
    缺点:计数只能记一次。若只用于一个进程中多个线程的同步,那么临界区更好,能介绍资源占用。并且,互斥量一旦创建,就可以通过名字打开,难免其他进程做些什么。

线程间的同步方式

线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。操作系统一般有下面三种线程同步的方式:

  1. 信号量(Semphares) :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
  2. 互斥量(Mutex):采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
  3. 事件(Event) :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较

临界区的概念、解决冲突方法

每个进程中访问临界资源的那段程序称为临界区,一次仅允许一个进程使用的资源称为临界资源。

解决冲突的办法:

  • 如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入,如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待;
  • 进入临界区的进程要在有限时间内退出。
  • 如果进程不能进入自己的临界区,则应让出 CPU,避免进程出现“忙等”现象。

进程的调度算法

  • 先到先服务(FCFS)调度算法:就绪队列先来后到执行。
  • 优先级调度:根据某个优先级指标生成调度顺序,具有相同优先级的进程以 FCFS 方式执行。
  • 短作业优先(SJF)的调度算法:根据估算时间选择最短的进程执行。
  • 时间片轮转调度算法 : 每个进程分配相同的时间片,时间过切换下一个进程。
  • 多级反馈队列调度算法 :短进程优先的调度算法,仅照顾了短进程而忽略了长进程 。多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成。,因而它是目前被公认的一种较好的进程调度算法,UNIX 操作系统采取的便是这种调度算法。队列时间片时间:1,2,4…

批处理系统:先来先服务,短作业优先。
交互式系统:(快速响应)时间片轮转,优先级调度,多级反馈队列。
实时系统:硬实时系统,软实时系统。

选择多线程 or 多进程?

  1. 频繁创建、销毁时,使用线程,因为创建和销毁进程的代价较大。
  2. 频繁切换时,使用线程,
  3. 稳定安全时,使用进程;速度优先时,使用线程。

图片说明

死锁

死锁的四个必要条件

  • 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。
  • 占有并等待:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
  • 非抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
  • 循环等待:有一组等待进程  {P0, P1,..., Pn}, P0  等待的资源被  P1  占有,P1  等待的资源被  P2  占有,…,Pn-1  等待的资源被  Pn  占有,Pn  等待的资源被  P0  占有。

注意,只有四个条件同时成立时,死锁才会出现。

哲学家进餐的问题

五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。

解决:奇数拿左侧,偶数拿右侧,不会死锁。