> 文档中心 > 进程、线程和协程(基础篇)

进程、线程和协程(基础篇)

目录

进程

进程的类型

进程具有的特征

进程的用户空间

Linux进程的状态

进程后面跟的修饰符

进程的状态转化

僵尸进程

线程

协程

进程和线程的关系和区别

进程和线程的选择

线程和协程关系和区别

串行、并发和并行

同步和异步、阻塞和非阻塞

同步和异步

阻塞和非阻塞

同步/异步与阻塞/非阻塞

怎么理解呢

总结


我们在开发高并发和高可用的程序的时候,我们都不可能只启动一个进程或者一个线程,我们都会使用多进程,多线程或者协程等这些知识点。那么什么是进程,什么是线程,什么是协程?以其他们是什么样的关系?使用他们需要知道那些知识呢?我们在这里和大家一起学习研究一下,废话不多说直接开始我们的学习之路吧~~~~~~


进程

进程就是一个就有独立功能的程序,在执行过程中独立的内存体,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体,是一个抽象概念。它有自己独立的地址空间,有自己的堆,上级挂靠单位是操作系统,系统会给它分配系统资源。

进程一般由程序、数据集合和进程控制块三部分组成

程序用于描述进程要完成的功能,是控制进程执行的指令集;

数据集合是程序在执行时所需要的数据和工作区;

程序控制块(Program Control Block,简称PCB),包含进程的描述信息和控制信息,是进程存在的唯一标志。

进程的类型

交互进程:这种进程经常与用户交互。典型的有shell进程、文本编辑进程及图形应用程序。
批处理进程:这种进程不必与用户交互,因此经常在后台运行。典型的有编译程序、数据库搜索引擎及科学计算。
守护进程:Linux系统启动时启动的进程,并在后台运行

进程具有的特征

  • 动态性:是程序的一次执行
  • 并发性:任何进程都可以同其他进程一起并发执行
  • 独立性:进程是系统进行资源分配和调度的一个独立单位
  • 结构性:进程由程序、数据和进程控制块三部分组成
  • 异步性:进程间的相互制约,使进程执行具有间隙

进程的用户空间

BSS区:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

数据区:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

代码区:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆区(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈区(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区

Linux进程的状态

为了更好的了解进程运行的情况,我们就需要知道进程有哪些状态,它们是怎么样的关系,如何转化的?

1. 运行 R (正在运行或在运行队列中等待)

2. 中断 S (休眠中, 受阻, 在等待某个条件的形成或接受到信号)

3. 不可中断 D (收到信号不唤醒和不可运行, 进程必须等待直到有中断发生)

4. 僵死 Z (进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放)

5. 停止 T (进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行)

6. 死掉的进程 X  (只是一个返回状态,不会在任务列表里看到这个状态,它非常短暂)

进程后面跟的修饰符

<    高优先级

N    低优先级
L    有些页被锁进内存
s    包含子进程
+    位于后台的进程组
l    多线程,克隆线程  multi-threaded (using CLONE_THREAD, like NPTL pthreads do)

进程的状态转化

从上图我们可以看出,为什么有了可中断睡眠 S,还要不可中断睡眠 D呢?其实很简单,比如A进程要存数据到磁盘中,它的状态是可中断睡眠 S,那问题来了,如果OS在这个时候把A进程杀掉了,那么存盘失败的时候,这问题就没有办法解决了,所以我们才有不可中断睡眠 D。

僵尸进程

从进程状态我们知道,有一个“僵死 Z” 状态,进入该状态的进程就是僵尸进程。这僵尸进程怎么产生的呢?就是当子进程退出并且父进程没有读取到子进程退出的返回代码时而产生。该进程状态会一直保持在进程表task_struct(PCB)中,直到父进程读取退出状态代码或者父进程退出。

危害:

1.造成内存资源的浪费

2.内存泄漏

3.一直维护

线程

有时被称为轻量级进程(Lightweight Process,LWP),是操作系统调度(CPU调度)执行的最小单位,线程是进程的一个实体。

协程

协程是一种用户态的轻量级线程,协程的调度完全由用户(程序执行)控制

协程的原理:

当出现IO阻塞的时候,由协程的调度器进行调度,通过将数据流立刻yield掉(主动让出),并且记录当前栈上的数据,阻塞完后立刻再通过线程恢复栈,并把阻塞的结果放到这个线程上去跑,这样看上去好像跟写同步代码没有任何差别,这整个流程可以称为coroutine,而跑在由coroutine负责调度的线程称为Fiber。

进程和线程的关系和区别

1.线程是最小的执行单元,进程是最小的资源管理单元

2.进程和线程都可以并发执行

3.进程是拥有资源的一个独立单位(进程之间相互独立),线程不拥有系统资源,但可以访问隶属于进程的资源。进程所维护的是程序所包含的资源(静态资源),线程所维护的运行相关的资源(动态资源)

4.一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线

5.多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些

进程和线程的选择

计算密集型:主要是一些复杂的逻辑判断和复杂的运算的程序框架设计,我比较推荐使用多进程,那样并发或并⾏效率更⾼

IO密集型:即对IO操作比较多的程序,对cpu利用率不高的。我比较推荐使用多线程,那样并发或并⾏效率更⾼

但是我们也要根据自己的框架设计和业务行为,综合的使用多进程和多线程。

线程和协程关系和区别

1.  一个线程可以多个协程,一个进程也可以单独拥有多个协程

2.  线程进程都是同步机制,而协程则是异步

3.  协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

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

5.  协程并不是取代线程, 而且抽象于线程之上, 线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行

6.  线程是协程的资源。协程通过执行器来间接使用线程这个资源

7.  极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显

8.  不需要多线程的锁机制:因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多

9.  线程的默认Stack大小是1M,而协程更轻量

串行、并发和并行

  • 并发: 指的是任务数多于CPU核数,通过操作系统的各种任务调度算法进行不停切换,实现用多任务一起执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起在执行而已)
  • 并行: 指的是任务数小于等于CPU核数,即任务真的在一起执行的
  • 串行: 多个任务,执行时执行完一个再执行另一个

清晰图形如下:

并发的重点在于有处理多个任务的能力,不一定要同时;而并行的重点在于就是有同时处理多个任务的能力。并行是并发的子集

同步和异步、阻塞和非阻塞

在网络编程和IO操作的时候,我们经常提到这几个概念:同步、异步、阻塞和非阻塞。

同步和异步

  • 同步(同步协调): 是指在访问某一资源时,获得了资源的返回结果之后才会执行其他操作,(先做某件事,再做某件事)
  • 异步: 与同步相对,是指再访问某一资源时,无论是否取得返回结果,都进行下一步操作;当有了资源返回结果时,系统自会通知。

阻塞和非阻塞

阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作方法的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入方法会立即返回一个状态值

同步/异步与阻塞/非阻塞

1)同步阻塞调用:得不到结果不返回,线程进入阻塞态等待。

2)同步非阻塞调用:得不到结果不返回,线程不阻塞一直在CPU运行。

3)异步阻塞调用:去到别的线程,让别的线程阻塞起来等待结果,自己不阻塞。

4)异步非阻塞调用:去到别的线程,别的线程一直在运行,直到得出结果。

怎么理解呢

同步异步与阻塞非阻塞的主要区别是针对对象不同。

同步异步是针对调用者来说的,调用者发起一个请求后,一直干等被调用者的反馈就是同步,不必等去做别的事就是异步。

阻塞非阻塞是针对被调用者来说的,被调用者收到一个请求后,做完请求任务后才给出反馈就是阻塞,收到请求直接给出反馈再去做任务就是非阻塞。

比如说周末的时候我去买奶茶的行为:

同步:我交了钱之后,然后我就一直在那里等啥也不干,然后拿到奶茶之后,我才开始修改bug,我这种行为就是同步

异步:我交了钱之后,我就开始修改bug,然后奶茶好了我就去拿,我这种行为就是异步

阻塞:我交了钱之后,奶茶妹也不说(不反馈),就去做奶茶了,直到奶茶做好给我。奶茶妹的这种行为就是阻塞 

非阻塞:我交了钱之后,奶茶妹就给了我一张小票(反馈),奶茶妹的这种行为就是非阻塞。

同步阻塞:我交了钱之后,奶茶妹也不说(不反馈),我啥也不干一直等待奶茶,我这种行为就是同步阻塞

同步非阻塞:我交了钱之后,奶茶妹就给了我一张小票之后(反馈),我就开始修改bug,这种行为就是同步非阻塞

异步阻塞:我交了钱之后(不管奶茶妹有没有反馈),而我啥也不干一直等待奶茶,我这种行为就是异步阻塞

异步非阻塞:我交了钱之后(不管奶茶妹有没有反馈),然后我就修改bug去了,这种行为就是异步非阻塞

总结

这篇文章主要学习一下进程、线程和协程,并发和并行等这些基础概念和它们之间的关系,尤其各自的一些特征。而一些更深入的知识(安全问题,它们怎么通信,尤其相关的调度算法等等)我们会在下面章节介绍。希望该文章对你有所帮助,感谢阅读!觉得能帮助到您,可以点个赞,关注一下哈~谢谢~