2019-016-1- 进程、线程与协程

坐标上海松江高科技园,诚聘高级前端工程师
/ 高级Java 工程师,有兴趣的看JD :https://www.lagou.com/jobs/6361564.html
并发面试必备系列之进程、线程与协程
在 《Awesome Interviews》 归纳的常见面试题中,无论前后端,并发与异步的相关知识都是面试的中重中之重,《并发编程》系列即对于面试中常见的并发知识再进行回顾总结;你也可以前往 《Awesome Interviews》,在实际的面试题考校中了解自己的掌握程度。也可以前往《
在未配置
应用启动体现的就是静态指令加载进内存,进而进入
这样既耗费时间又浪费空间,所以我们才要研究多线程。一个进程创建的所有线程,都是共享一个内存空间的,所以线程做任务切换成本就很低了。现代的操作系统都基于更轻量的线程来调度,现在我们提到的“任务切换”都是指“线程切换”。
进程与线程
本部分节选自 《
Linux 与操作系统/ 进程管理》。
在未配置
进程(Process)
进程是操作系统对一个正在运行的程序的一种抽象,在一个系统上可以同时运行多个进程,而每个进程都好像在独占地使用硬件。所谓的并发运行,则是说一个进程的指令和另一个进程的指令是交错执行的。无论是在单核还是多核系统中,可以通过处理器在进程间切换,来实现单个
操作系统保持跟踪进程运行所需的所有状态信息。这种状态,也就是上下文,它包括许多信息,例如

在《

-
程序代码和数据,对于所有的进程来说,代码是从同一固定地址开始,直接按照可执行目标文件的内容初始化。
-
堆,代码和数据区后紧随着的是运行时堆。代码和数据区是在进程一开始运行时就被规定了大小,与此不同,当调用如
malloc 和free 这样的C 标准库函数时,堆可以在运行时动态地扩展和收缩。 -
共享库:大约在地址空间的中间部分是一块用来存放像
C 标准库和数学库这样共享库的代码和数据的区域。 -
栈,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。
-
内核虚拟存储器:内核总是驻留在内存中,是操作系统的一部分。地址空间顶部的区域是为内核保留的,不允许应用程序读写这个区域的内容或者直接调用内核代码定义的函数。
线程(Thread)
在现代系统中,一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。进程的个体间是完全独立的,而线程间是彼此依存的。多进程环境中,任何一个进程的终止,不会影响到其他进程。而多线程环境中,父线程终止,全部子线程被迫终止
而任何一个子线程终止一般不会影响其他线程,除非子线程执行了 exit()
系统调用。任何一个子线程执行 exit()
,全部线程同时灭亡。多线程程序中至少有一个主线程,而这个主线程其实就是有
线程共享的环境包括:进程代码段、进程的公有数据、进程打开的文件描述符、信号的处理器、进程的当前目录、进程用户
-
线程
ID :每个线程都有自己的线程ID ,这个ID 在本进程中是唯一的。进程用此来标识线程。 -
寄存器组的值:由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上时,必须将原有的线程的寄存器集合的状态保存,以便 将来该线程在被重新切换到时能得以恢复。
-
线程的堆栈:堆栈是保证线程独立运行所必须的。线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的函数堆栈,使得函数调用可以正常执行,不受其他线程的影响。
-
错误返回码:由于同一个进程中有很多个线程在同时运行,可能某个线程进行系统调用后设置了
errno 值,而在该 线程还没有处理这个错误,另外一个线程就在此时 被调度器投入运行,这样错误值就有可能被修改。所以,不同的线程应该拥有自己的错误返回码变量。 -
线程的信号屏蔽码:由于每个线程所感兴趣的信号不同,所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都共享同样的信号处理器。
-
线程的优先级:由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级。

线程模型
线程实现在用户空间下
当线程在用户空间下实现时,操作系统对线程的存在一无所知,操作系统只能看到进程,而不能看到线程。所有的线程都是在用户空间实现。在操作系统看来,每一个进程只有一个线程。过去的操作系统大部分是这种实现方式,这种方式的好处之一就是即使操作系统不支持线程,也可以通过库函数来支持线程。
在这在模型下,程序员需要自己实现线程的数据结构、创建销毁和调度维护。也就相当于需要实现一个自己的线程调度内核,而同时这些线程运行在操作系统的一个进程内,最后操作系统直接对进程进行调度。

这样做有一些优点,首先就是确实在操作系统中实现了真实的多线程,其次就是线程的调度只是在用户态,减少了操作系统从内核态到用户态的切换开销。这种模式最致命的缺点也是由于操作系统不知道线程的存在,因此当一个进程中的某一个线程进行系统调用时,比如缺页中断而导致线程阻塞,此时操作系统会阻塞整个进程,即使这个进程中其它线程还在工作。还有一个问题是假如进程中一个线程长时间不释放
线程实现在操作系统内核中
内核线程就是直接由操作系统内核(Kernel)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情,支持多线程的内核就叫做多线程内核(Multi-Threads Kernel
程序员直接使用操作系统中已经实现的线程,而线程的创建、销毁、调度和维护,都是靠操作系统(准确的说是内核)来实现,程序员只需要使用系统调用,而不需要自己设计线程的调度算法和线程对
使用用户线程加轻量级进程混合实现
在这种混合实现下,即存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换、析构等操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,大大降低了整个进程被完全阻塞的风险。在这种混合模式中,用户线程与轻量级进程的数量比是不定的,即为

Linux 中的线程
在
为了完全兼容1 对 1
模型。

进程的实现是调用pid_t fork(void);
,线程的实现是调用int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...)
。与标准 fork()
相比,线程带来的开销非常小,内核无需单独复制进程的内存空间或文件描写叙述符等等。这就节省了大量的
内核线程
内核线程是由内核自己创建的线程,也叫做守护线程(Deamonps -Al
列出的所有进程中,名字以do_fork()
创建,每个线程都有独立的current->mm
是空的,与内核使用同一张页表;而用户线程则可以看到完整的
在
Coroutine | 协程
协程是用户模式下的轻量级线程,最准确的名字应该叫用户空间线程(User Space Thread
协程的优势如下:
- 节省内存,每个线程需要分配一段栈内存,以及内核里的一些资源
- 节省分配线程的开销(创建和销毁线程要各做一次
syscall ) - 节省大量线程切换带来的开销
- 与
NIO 配合实现非阻塞的编程,提高系统的吞吐
比如
Go 的协程模型
-
G: 表示Goroutine ,每个Goroutine 对应一个G 结构体,G 存储Goroutine 的运行堆栈、状态以及任务函数,可重用。G 并非执行体,每个G 需要绑定到P 才能被调度执行。 -
P: Processor,表示逻辑处理器,对
G 来说,P 相当于CPU 核,G 只有绑定到P( 在P 的local runq 中) 才能被调度。对M 来说,P 提供了相关的执行环境(Context) ,如内存分配状态(mcache) ,任务队列(G)等,P 的数量决定了系统内最大可并行的G 的数量(物理CPU 核数>= P 的数量) ,P 的数量由用户设置的GOMAXPROCS 决定,但是不论GOMAXPROCS 设置为多大,P 的数量最大为256 。 -
M: Machine,
OS 线程抽象,代表着真正执行计算的资源,在绑定有效的P 后,进入schedule 循环;M 的数量是不定的,由Go Runtime 调整,为了防止创建过多OS 线程导致系统调度不过来,目前默认最大限制为10000 个。
在

在
- 一级是操作系统的调度系统,该调度系统调度逻辑处理器占用
cpu 时间片运行; - 一级是
Go 的运行时调度系统,该调度系统调度某个Goroutine 在逻辑处理上运行。
使用

Java 协程的讨论
目前,
从线程的切换开销的角度来看,我们常说的切换开销往往是针对于活跃线程;而普通的
实际上我们引入协程的场景,更多的是面对所谓百万级别连接的处理,典型的就是
Java 线程与操作系统线程
对于

在现在的操作系统中,因为线程依旧被视为轻量级进程,所以操作系统中线程的状态实际上和进程状态是一致的模型。从实际意义上来讲,操作系统中的线程除去
ready
:表示线程已经被创建,正在等待系统调度分配CPU 使用权。running
:表示线程获得了CPU 使用权,正在进行运算。waiting
:表示线程等待(或者说挂起) ,让出CPU 资源给其他线程使用。
对于
延伸阅读

您还可以前往 NGTE Books 主页浏览包含知识体系、编程语言、软件工程、模式与架构、
