一.背景
在并发编程中进程和线程是不可忽略的两个概念,他们很好的完成了操作系统或者服务对于高并发的需求,然而随着时代的进步,协程的概念应运而生,本文旨在解释协程相对于进程和线程在高并发环境下的优势,所以会先介绍进程,线程,最后讲解协程的调度方式。
二.详细介绍
2.1 进程
2.1.1 概念
进程基本上是一个正在执行的程序,它是操作系统中最小的资源分配单位。
2.1.2 结构
当一个程序被加载到内存中并成为一个进程时,它可以分为四个部分——堆栈、堆、文本和数据。下图显示了主内存中进程的简化布局:
堆栈
进程堆栈包含临时数据,例如方法/函数参数、返回地址和局部变量。
堆
这是在进程运行时动态分配给进程的内存。
数据
包含全局变量和静态变量。
文本
包括由程序计数器的值和处理器寄存器的内容表示的当前活动。
2.1.3 进程上下文切换
进程的上下文切换是指 cpu 从一个进程切换到另一个进程。
进程上下文切换主要包含两个主要过程:进程地址空间切换和处理器状态切换
进程地址空间切换
切换原因:进程地址空间指的是进程所拥有的虚拟地址空间,而这个地址空间是假的,是 linux 内核通过数据结构来描述出来的,从而使得每一个进程都感觉到自己拥有整个内存的假象,cpu 访问的指令和数据最终会落实到实际的物理地址,对用进程而言通过缺页异常来分配和建立页表映射。进程地址空间内有进程运行的指令和数据,因此到调度器从其他进程重新切换到我的时候,为了保证当前进程访问的虚拟地址是自己的必须切换地址空间。
切换方式:将当前进程的 pgd 虚拟地址转换为物理地址存放在用户控件的页表基址寄存器,当访问用户空间地址的时候 mmu 会通过这个寄存器做遍历页表,获得物理地址。
原理是进程想要访问一个用户空间虚拟地址,cpu 的 mmu 所做的工作,就是从页表基址寄存器拿到页全局目录的物理基地址,然后和虚拟地址配合来查查找页表,最终找到物理地址进行访问(当然如果 tlb 命中就不需要遍历页表),每次用户虚拟地址访问的时候(内核空间共享不考虑),由于页表基地址寄存器内存放的是当前执行进程的页全局目录的物理地址,所以访问自己的一套页表,拿到的是属于自己的物理地址(实际上,进程是访问虚拟地址空间的指令数据的时候不断发生缺页异常,然后缺页异常处理程序为进程分配实际的物理页,然后将页帧号和页表属性填入自己的页表条目中),就不会访问其他进程的指令和数据,这也是为何多个进程可以访问相同的虚拟地址而不会出现差错的原因。
ps:地址空间切换过程中,还会清空 tlb(页表缓存:用于存放虚拟地址映射至物理地址的标签页表条目),防止当前进程虚拟地址转化过程中命中上一个进程的 tlb 表项,一般会将所有的 tlb 无效,但是这会导致很大的性能损失,因为新进程被切换进来的时候面对的是全新的空的 tlb,造成很大概率的 tlb miss,需要重新遍历多级页表
处理器状态切换
切换原因:需要将进程的内核栈和执行流进行切换。
切换方式:处理器状态切换就是将前一个进程的 sp,pc 等寄存器的值保存到一块内存上&#