前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文快速了解进程、线程与协程

一文快速了解进程、线程与协程

作者头像
全菜工程师小辉
发布2021-06-25 21:01:50
13.9K0
发布2021-06-25 21:01:50
举报
文章被收录于专栏:后端开发你必须学会的干货

进程与线程

进程是操作系统进行资源分配的基本单位,每个进程都有自己的独立内存空间。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

线程又叫做轻量级进程,是进程的一个实体,是处理器任务调度和执行的基本单位位。它是比进程更小的能独立运行的基本单位。线程只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

对于操作系统来说,一个任务就是一个进程(Process)。比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。

有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,进程内的这些“子任务”称为线程(Thread)。

由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。

协程

协程,又称微线程,是一种用户态的轻量级线程,协程的调度完全由用户控制(也就是在用户态执行)。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到线程的堆区,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

如果对内核态用户态不了解的话,可以先看博客《一文理解JVM线程属于用户态还是内核态

协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和线程切换相比,线程数量越多,协程的性能优势就越明显。不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。此外,一个线程的内存在MB级别,而协程只需要KB级别。

进程和线程的区别

每个线程都是一个轻量级进程(Light Weight Process),都有自己的唯一PID和一个TGID(Thread group ID)。TGID是启动整个进程的thread的PID。

例如,当一个进程被创建的时候,它其实是一个PID和TGID数值相同线程。当线程A启动线程B时,线程B会有自己的唯一PID,但它的TGID会从A继承而来。这样通过PID线程可以独立得到调度,而相同的TGID可以知道哪些线程属于同一个进程,这样可以共享资源(RAM,虚拟内存、文件等)。

线程进程的区别体现在6个方面:

  • 根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。
  • 资源开销:每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一进程的线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。
  • 包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的。
  • 内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的。
  • 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
  • 执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。两者均可并发执行。

协程与线程的区别

  1. 一个线程可以有多个协程。
  2. 大多数业务场景下,线程进程可以看做是同步机制,而协程则是异步。
  3. 线程是抢占式,而协程是非抢占式的,所以需要用户代码释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
  4. 协程并不是取代线程,而且抽象于线程之上。线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行。

进程间的通信方式(IPC)

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)

Linux IPC的主要方式

1.管道(pipe)

管道,通常指无名管道,是UNIX系统IPC最古老的形式。

管道是一种半双工(即数据只能在一个方向上流动)的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。

进程的亲缘关系指的是父子进程或者兄弟进程关系。

当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。

特点:

  1. 面向字节流,
  2. 生命周期随内核。
  3. 自带同步互斥机制。
  4. 半双工,单向通信,两个管道实现双向通信。

2.命名管道(namedpipe)

FIFO,也称为命名管道,它是一种文件类型,也是半双工的通信方式。多个进程都可以通过一个约定好的名字找到同一个管道。FIFO允许无亲缘关系进程间的通信。FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清除数据,并且“先进先出”。

3.消息队列(messagequeue)

消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

特点:

  1. 消息队列可以认为是一个全局的一个链表,链表节点钟存放着数据报的类型和内容,有消息队列的标识符进行标记。
  2. 消息队列允许一个或多个进程写入或者读取消息。
  3. 消息队列的生命周期随内核。
  4. 消息队列可实现双向通信。

但是当发送到消息队列的信息量大或操作频繁的场合,需要拷贝的时间也就越多,此时可以采用共享内存通信。

4.共享内存(shared memory)

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。

共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制(如信号量)配合使用来实现进程间的同步和通信。

特点:

  1. 不用从用户态到内核态的频繁切换和拷贝数据,直接从内存中读取就可以。
  2. 共享内存是临界资源,所以需要操作时必须要保证原子性。使用信号量或者互斥锁都可以。
  3. 生命周期随内核。

5.信号量(semophore)

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

信号量主要实现进程之间的同步和互斥,而不是存储通信内容。

信号量定义了两种操作,p操作和v操作,p操作为申请资源,会将数值减去M,表示这部分被他使用了,其他进程暂时不能用。v操作是归还资源操作,告知归还了资源可以用这部分。

6.信号(signal)

信号是软件中断产生,用于进程间异步传递信息。信号可以用来直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。

一般在shell中操作,进程获取信号进行处理,一共有64种信号,在shell中输入 kill -l 可查阅

7.套接字(socket)

套接字(有的时候被译为插座)也是一种进程间通信机制,与其他通信机制不同的是,套接字允许两个进程进行通讯,这两个进程可能运行在同一个机器上,也可能运行在不同机器上。

相对于共享内存可以多对多的读取与写入,套接字只能一对一。此外由于序列化等操作占用大量资源,相对于共享内存,套接字更适合传输少量数据。

进程通信方式总结

  1. 管道:速度慢,容量有限,只有父子进程能通讯
  2. 命名管道:任何进程间都能通讯,但速度慢
  3. 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
  4. 共享内存:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全问题。
  5. 信号量:不能传递复杂消息,只能用来同步。
  6. 信号:用于通知接收进程某个事件已经发生。
  7. 套接字:可用于不同机器之间的进程间通信。

线程间的通信方式

锁(Lock)

锁机制包括互斥锁、条件变量、读写锁。

  1. 互斥锁提供了以排他方式防止数据结构被并发修改的方法。
  2. 读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
  3. 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

有关Java的锁机制,可以点击查看《详解Java多线程锁之synchronized》和《详解Java多线程锁之Lock和ReadWriteLock

有关条件变量,可以点击查看《Java多线程的可见性与有序性》中有关volatile的讲解。

信号量(Semaphore)

可以查看这篇博客《快速了解基于AQS实现的Java并发工具类》中有关Semaphore的讲解,感受下信号量如何在java线程通信中的使用。

信号(Signal)

可以查看这篇博客:《彻底搞懂Java的等待-通知(wait-notify)机制

协程间的通信方式

与线程不同,协程使用程序自定义的调度器进行调度,因此更容易控制协程之间的执行顺序,要想充分利用协程的调度模型,有一个完备的通信机制是很重要的。它主要应该有以下的功能:

  1. 能从一个协程发送消息到另一个协程,通知另一个协程特定的事件已经发生。
  2. 能够让协程在事件未发生之前挂起,等待事件发生后被调度并处理,从而有效让出CPU时间。
  3. 能够在消息中附带相应的数据。

能完成这样任务的模型很多,原理也不尽相同,但思路其实和线程的通信方式大体相同,这里以后再单独讲解。

补充

Nginx的进程通信有哪些

Nginx的进程通信分为三种类别:linux系统与Nginx通信(信号),master进程与worker进程通信(套接字),worker进程间通信(共享内存)。

  1. linux系统与Nginx通信 答:linux系统与Nginx是通过信号进行通信的,通过信号控制Nginx重启、关闭以及加载配置文件等。比如:./nginx –s quit 向master进程发送信号。
  2. master进程与worker进程通信 答:master在fork worker进程前,先调用socketpair,创建一个socket对用来master与worker进程间的通信。这个socket就是一个元素个数为2的数组channel,channel[0]用于master进程写入,channel[1]用于worker进程读入。
  3. worker进程间通信 答:共享内存是最快的通信方式,是worker进程之间的通信方式。使用共享内存的好处是当多个进程使用同一块共享内存时,在任何一个进程修改了共享内存中的内容后,其他进程通过访问这段共享内存都能够得到修改后的内容。

进程七态

各种状态的意义:

  • 创建态:进程正在被创建。
  • 就绪态:可运行,但因为其他进程正在运行而暂时停止。
  • 运行态:正在占用CPU。
  • 结束态:进程正在从系统中消失。
  • 阻塞状态:该进程等待某个事件(比如IO读取)而停止运行,此时即使有CPU时间片也无法继续运行。

状态变化事件:

  • NULL => 创建态:一个进程被创建时的第一个状态。
  • 创建态 => 就绪态:当进程创建完成,进入就绪态。
  • 就绪态 => 运行态:CPU从就绪队列选择进程执行,进入运行态。
  • 运行态 => 结束态:当进程已经运行完成或出错时,进入结束态。
  • 运行态 => 就绪态:分配给进程的时间片使用完,进入就绪态。
  • 运行态 => 阻塞状态:进程执行等待事件,进入阻塞态。
  • 阻塞状态 => 就绪态:进程事件完成,CPU收到中断信号,进入就绪态。

进程状态变化中,还有一种状态叫挂起态,挂起态代表该进程没有占用内存空间,这跟阻塞状态是不一样。

挂起和阻塞的区别:

  • 挂起是一个行为,而阻塞是进程的一种状态。
  • 进程存放的位置不同:挂起是将进程移到外存中,而处于阻塞状态的进程还是在内存中。
  • 原因不同:导致进程被挂起的原因一般是内存不足或者是系统、用户的请求,协调、修改进程,研究进程的状态等,进程阻塞是进程正在等待某一事件发生,可能是等待资源或者响应等(eg.等待I/O完成等)而暂时停止运行。
  • 挂起对应的行为是激活,将外存中的进程调入内存中。而处于阻塞状态的进程需要其他进程或系统唤醒。
  • 挂起是被动的行为,进程被迫从内存中移至外存中。而进入阻塞可以看成是一个主动的行为(eg.进程I/O时,进程在等待I/O设备完成时,进程主动进入阻塞状态,I/O完成,进程被激活)

挂起态可以分为下面两种:

  • 阻塞挂起状态:进程在外存(磁盘)并等待某个事件的出现。
  • 就绪挂起状态:进程在外存(磁盘)激活后进入就绪态。

参考文档:

  1. 《深入理解计算机系统》
  2. 《深入理解Nginx》
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全菜工程师小辉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 进程与线程
  • 协程
  • 进程和线程的区别
  • 协程与线程的区别
  • 进程间的通信方式(IPC)
  • Linux IPC的主要方式
    • 1.管道(pipe)
      • 2.命名管道(namedpipe)
        • 3.消息队列(messagequeue)
          • 4.共享内存(shared memory)
            • 5.信号量(semophore)
              • 6.信号(signal)
                • 7.套接字(socket)
                  • 进程通信方式总结
                  • 线程间的通信方式
                    • 锁(Lock)
                      • 信号量(Semaphore)
                        • 信号(Signal)
                        • 协程间的通信方式
                        • 补充
                          • Nginx的进程通信有哪些
                            • 进程七态
                            相关产品与服务
                            消息队列 CMQ 版
                            消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档