作业、进程、线程与协程

从作业到协程,用户对计算过程的控制粒度越来越小。

作业

统一定义:作业(Job)是用户提交给系统的一个任务,一个作业通常包括多个进程,多个进程共同完成一个任务,就是作业。

作业作为一个比程序更为广泛的概念,它不仅包含了通常的程序和数据,而且还应配有一份作业说明书,系统根据该说明书来对程序的运行进行控制。在批处理系统中,是以作业为基本单位将资源从外存调入内存的

作业步(JobStep),通常在作业运行期间,每个作业都必须经过若干个相对独立,又相互关联的顺序加工步骤才能得到结果。我们把其中的每一个加工步骤称为一个作业步,各作业步之间存在着相互联系,往往是上一个作业步的输出作为下一个作业步的输入。例如,一个典型的作业可分成:“编译”作业步,“链接装配”作业步和“运行”作业步。

进程

进程的定义

在操作系统里面,为了使参与并发执行的每个(含数据的)程序都能够独立的运行,提出了进程这个概念。

程序段、数据段、PCB这三个部分就能组成一个进程实体(进程映像)。

从不同的角度,进程有不同的定义: 1、进程是程序的一次执行。 2、进程是一个程序及其数据在处理机上顺序执行时所发生的活动。 3、进程是具有独立功能的程序在一个数据集上运行的过程,它是系统进行资源分配和调度的一个基本单位。

统一定义:进程是进程实体的运行过程,是系统进行资源分配和调度的基本单位,是系统中最小的资源管理单元。

从进程到线程

  • 更好的描述和控制程序的并发执行。
  • 操作系统中,进程是资源分配的基本单位,进程数一多,系统的资源就很快就被分配完。进程的频繁切换,进程间通信所增加的时空成本。
  • 一个进程中可以拥有多个线程。同一个进程的线程共享进程内的资源。

创建进程

  • 分配PCB(PCB个数有限,分配失败则创建失败)
  • 分配资源(若分配失败,则进入阻塞状态)
  • 初始化PCB(初始化优先级)
  • 进入就绪队列

两分配、一初始、一进入

切换进程

  • 保存处理机上下文,包括程序计数器和其他寄存器。
  • 更新PCB信息,并移入相应队列。
  • 选择另一个进程,并更新其PCB。
  • 更新内存管理。
  • 恢复上下文。

一保存、三更新(PCB+PCB+内存管理)、一恢复

进程状态

创建态:进程正在被创建,完成后进入就绪态 就绪态:进程已经获得了除处理机以外的一切所需资源,等待处理机。 运行态:进程正在处理机上运行。 阻塞态(等待态):系统由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,该进程也无法进行运行。 结束态:进程先进入结束态,然后进一步释放资源。

Snipaste_2020-04-16_14-37-51.png

线程

线程定义

线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

每个线程完成不同的任务,但是共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。线程是系统调度和分配的基本单位,线程是最小的执行单元。

统一定义:线程是进程中一个单一顺序的控制流,是系统分配和调度的基本单位,是系统中最小的执行单元。

有了进程为啥还要线程

进程在执行的过程中如果阻塞,整个进程就会挂起,即使进程中有些工作不依赖于等待的资源,仍然不会执行。因此,操作系统引入了比进程粒度更小的线程,作为并发执行的基本单位,从而减少程序在并发执行时所付出的时空开销,提高并发性。
和进程相比,线程的优势如下:

  • 资源上来讲,线程是一种非常"节俭"的多任务操作方式。在linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。

  • 切换效率上来讲,运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需时间也远远小于进程间切换所需要的时间。

  • 通信机制上来讲,线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进城下的线程之间共享数据空间,所以一个线程的数据可以直接为其他线程所用,这不仅快捷,而且方便。

进程与线程的同通讯简单分析: 1、线程通信:由于多线程共享地址空间和数据空间,一个线程的数据可以直接提供给其他线程使用,而不必通过操作系统(也就是内核的调度)。 2、进程通信:共享存储器系统、管道通信系统、消息传递系统、客户机-服务器系统

切换线程

刷新寄存器

线程在切换的过程中需要保存当前线程Id、线程状态、堆栈、寄存器状态等信息。其中寄存器主要包括SP PC EAX等寄存器,其主要功能如下: 1. SP:堆栈指针,指向当前栈的栈顶地址。 2. PC:程序计数器,存储指令的地址。 3. EAX:累加寄存器,用于加法乘法的缺省寄存器。

协程

程序开发的一大矛盾是,你要用控制流去完成逻辑流。也就是说,你要用指令的执行来完成逻辑链条的前因后果。

在刚开始学程序的时候,往往都是从控制流等价于执行流的情况下学起,执行到哪,就意味着逻辑流走到了哪。这样的程序结构清晰,可读性好。

但是问题是中间有些过程是不能立即得到结果的,程序为了等结果就会阻塞。这种情况多见于一些I/O操作,这种情况下,控制流和逻辑流就脱节了。 为了提升效率,我们可以使用异步的api,通过回调/通知函数来响应操作结果,同时接着执行下一轮的逻辑。

异步回调/通知的问题在于,它把原本统一的逻辑流拆开成了几个阶段,这样控制流和逻辑流就不等价了。为了保证逻辑数据的传递,需要自己来维护状态,阅读起来也比较头疼。状态的维护历来就是bug层出的地方,很容易掉入无效状态而死掉。同时,这种机制调试起来还特别麻烦,因为状态所能提供的信息不够,往往还得手动跟踪调用链,这是相当费精力的。

一条线程 在多个任务之间来回切换,切换这个动作是浪费时间的。对于cpu,操作系统来说,协程是不存在的,他们只管执行线程。他们不管你执行哪个任务,只管执行线程的指令。

协程和线程、进程的区别

协程是一种编译器级别的任务调度机制,它可以让你用逻辑流的顺序去写控制流,而且还不会导致操作系统级的线程阻塞。 发起异步请求、注册回调/通知器、保存状态,挂起控制流、收到回调/通知、恢复状态、恢复控制流的所有过程都能过一个 yield 来默默完成。

一句话说明什么是协程:协程是一种用户态的轻量级线程。

协程机制下,协程调度切换时,会将自己的寄存器上下文和栈保存在其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

应用场景

线程应用场景:

需要长时间等待,或者通信频繁(需要加锁)。 多线程模型主要优势为线程间切换代价较小,因此适用于I/O密集型的工作场景,因为I/O密集型的工作场景经常会由于I/O阻塞导致频繁的切换线程。 同时,多线程模型也适用于单机多核分布式场景。

进程应用场景:

安全性,稳定性有要求。一个进程出问题,不会影响其他进程。但是线程共享资源,一旦出了问题,可能会相互影响。

总结

  • 作业是用户提交给系统的一个任务,一个作业通常包括几个进程,几个进程共同完成一个任务(作业)。
  • 进程是进程实体的运行过程,是系统进行资源分配和调度的基本单位,是系统中最小的资源管理单元。
  • 线程是进程中一个单一顺序的控制流,是系统分配和调度的基本单位,是系统中最小的执行单元。
  • 协程是一种用户态的轻量级线程。是一种编译器级别的任务调度机制,它可以让你用逻辑流的顺序去写控制流,而且还不会导致操作系统级的线程阻塞。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!