haribote&&linux0.11并发笔记整理抽取
Posted 资质平庸的程序员
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了haribote&&linux0.11并发笔记整理抽取相关的知识,希望对你有一定的参考价值。
1 初识并发
“在一段时间内,多个进程(线程)都被CPU执行”是并发初期更被广为流传的定义。
跟众多其他计算机技术一样,并发也得到了扩展。此文先从CPU进程特例层面认识并发。
p1 -=. =. =. =. =. -.
p2 -. =. =. =. =. =-
| <--concurrency--> |
p1,p2: 进程1,进程2;
= : 进程持续被CPU执行时间;
. : CPU切换进程时间;
- : 进程启动或退出时间。
由于CPU速度快——切换进程时间很小(如0.1ms),若进程持续运行时间也被设置得比较小(如20ms),则p1和p2分别每20.1ms就会被持续执行20ms。
当进程数较少(如小于10)时,各进程间隔执行时间(小于201ms)和持续运行时间(小于200ms)在与人交互过程中不会被人感知出来,使得并发进程就像在同时运行一样。
是谁提出利用CPU速度支持程序并发的呢?幻想此文是他的组长吧——此文对他的欣赏可以从分配给他的年奖数看出些端倪。
2 进程并发执行的时间效率——并发执行 VS 串行执行
假设CPU没有运行其他进程。
2.1 并发 < 串行
比较当
[1] p1,p2不包含任何操作CPU外设的指令
[2] p1,p2不竞争或等待任何资源
如
/* The C-level routines of both p1&&p2 processes */
#incldue <stdint.h>
int main(void)
uint64_t cnt = 0;
while(++cnt)
;
return 0;
时p1和p2并发和串行执行的时间效率。
p1 -=. =. =. =. =. -.
p2 -. =. =. =. =. =-
| <--concurrency--> |
p1,p2并行执行时间
-=.-.=.=.=.=.=.=.=.=.-.=-
将CPU切换进程的时间放在末尾得
-=-========-=-...........
p1,p2串行执行时间
-=====--=====-
可以看出,p1p2并发执行比串行执行多消耗的时间来自进程切换。按之前为并发所假设的时间,进程切换时间只占进程持续运行时间的0.1/20=0.5%。
即若p1p2串行执行需1天时间,并发执行p1p2约多花7分钟。7分钟即0.5%是否会成为关键时间段,由具体应用对时间的苛刻程度决定。
在并发执行时间效率稍不如串行执行时,并发的优势在于,并发期间各进程都能被执行——即并发可较好地维持一碗水端平的现象。当然,并发期间会比串行占用更多的内存资源。
若对串并发的时间或计算机资源等差异不敏感时,就可在实现程序时略去对相应因素的考虑。
2.2 并发 > 串行
进程除了包含单纯的运算功能外,往往还包含与I/O交互的功能,或会竞争共享资源。在这些场景下,可通过并发提升CPU利用率从而从总体提升进程执行效率。
以进程与I/O交互为例看下并发为何能提升进程总体执行效率吧。
深谙CPU和I/O速度差距编程的娃不会将正等待I/O数据期间的进程加入并发行列中,而让CPU去执行其他可继续运行的进程,待I/O数据到来后再将相应进程加入到并发行中。
这种进程并发调度策略虽然不能提升(反而会些许降低)单个进程的执行效率,但能让CPU利用进程等待I/O的时间段去执行其他进程,从而为全局提升并发执行效率创造了可能性。
p3 -=. =. ^. d =. =.=. -.
p4 -. =. =.=.=. ^. d =. -
| <--concurrency--> |
= : 进程持续被CPU执行时间;
. : CPU切换进程时间;
^ : 等进程所需资源时间(与CPU速度相比较长);
d : 进程等待资源来临时刻;
- : 进程启动或退出时间。
p3,p4并发执行时间为
=.-.=.=.^.=.=.=.=.^.=.=.=.-.-
d d
p3,p4串行时间为
==^^^^^^^^^^^^d===--====^^^^^^d=-
在进程等待I/O数据时间段,CPU能执行很多程序指令呢。以键盘输入为例,假设此文每分钟能键入1000个字符,即各字符平均间隔着60ms。60ms对于如今的计算机大概是多长一段时间呢——在i5-7300HQ 2.5GHz windows上,可以执行完一个计数约6万次的Python程序。
此文将windows和Python标黑加粗是为了突出在60ms期间,CPU远远不仅是做了6万次计数。
2.3 并发时间效率小结
[1] 与串行执行时间效率相比,单个进程在并发中的执行时间效率会下降。若各进程都是类似于p1p2这样全程可持续执行的进程,并发执行的总体时间效率也会下降。在时间效率不敏感时,就可以隐式地牺牲一些时间去换取更酷的“同时运行”效果。
[2] 若各进程因等待资源而间隔运行场景(闲暇时间)较多,在进程总切换时间小于进程总闲暇时间时,并发总体执行时间效率就能得到提升。或者是,在编写程序时,在能满足单个程序性能前提下应尽可能地降低CPU占用率,以提升无延迟感的并发量。
[3] 在用并发追求时间效率时,进程切换(进程数量)和进程闲暇被权衡到位时,才会有效果。这种权衡力与三流程序员的拷贝力完全不等。不过,很多场景都不用去追求精准的时间效率——不卡就行。
3 h&&l并发调度
haribote&&linux0.11正是对CPU层面并发有一定见解才能写出适用于操作系统角色的并发调度程序。
3.1 haribote并发调度策略
|——————————————————————|
| |----------| |
| level 9 |0 1 2...99| |
| |----------| |
| PCB |
|——————————————————————|
.
.
.
|——————————————————————|
| |----------| |
| level 1 |0 1 2...99| |
| |----------| |
| PCB |
|——————————————————————|
|——————————————————————|
| |----------| |
| level 0 |0 1 2...99| |
| |----------| |
| PCB |
|——————————————————————|
haribote分10个进程层级管理并发调度,
每个进程层级中最多同时包含100个PCB。
[1]
进程层级level数值越小其调度优先级越高;
处于同层级PCB所对应进程将被依次调度运行(定时器衡量运行时间)。
[2]
在进程层级调度标志置位或当前进程级已无可运行进程时,
调度程序会调度
包含可运行进程且优先级最高进程级中的进程运行。
level 9中置有闲置进程。
[3]
进程阻塞等待资源时,该进程将进入睡眠状态(PCB标识),
其PCB也将从进程级中移除,
然后根据[1]或[2]策略调度下一进程运行。
待该进程资源来临时,再恢复其可运行的状态,
并重新将其PCB加入到某进程级中。
若该进程需立即运行的话,
就可将其加入到进程级0中并置任务层调度标志。
3.2 linux0.11并发调度策略
|------------|
|0 1 2 3...63|
|------------|
PCBs
[1]
从PCBs末尾往前遍历,唤醒——置进程状态为可运行状态
有信号且处于就绪状态的进程,而后遍历出
处于运行状态
运行时间片最大
的PCB,并切换该PCB对应的进程运行。
对于可运行状态进程的运行时间片相同时,
PCBs下标大者先运行。
[2]
PCBs[0]固定管理初始进程,初始进程运行时间片不会减少,
当有其他可运行进程时,初始进程不会被调度运行。
linux0.11用户态初始化完毕后,
初始进程运行时将唤醒有信号且处于就绪状态的进程,
然后按照[1]调度下一进程运行。
[3]
用户进程运行时间片完毕(在定时器中自减),
按照[1]调度下一进程运行;
进程阻塞等待资源并进入未就绪状态时则按照[1]
调度下一个进程运行;待所等资源来临时,该进程被唤醒;
每次系统调用完毕后,
若当前进程运行时间片完毕或已成为未就绪状态
则按照[1]调度下一进程运行。
linux0.11中进程调度管理涉及的进程状态。
|调
[1]|度
V
可运行状态
↗ ↖
↙ ↘
就绪状态 <--> 未就绪状态
僵死状态,停止状态。
4 认识其他并发
CPU更版的一些新功能会让进程切换时间增加——CPU与内存交换信息变得更多。
|------------------|
| ... |
| |--------------| |
| |instrs && data| |
| |--------------| |
| pr2 |
| ... |
| |--------------| |
| |instrs && data| |<-----|
| |--------------| | |
| pr1 | |
| ... | |
| |--------------| | |
| | LDT items | | |
| |--------------| | |
| LDT2 | |
|-----------| | ... | |
| TLB | | PCBs ... PG_TABs| |CS:EIP =
| TR | | ... | |TSS1.cs:TSS1.eip;
|GDTR=n LDTR| | |--------------| | ③|LDT1&&CS:EIP -->
| CS EIP | | | LDT items | |<-----|pr1当前指令地址;
| others | | |--------------| | |
|-----------| | LDT1 | |
CPU | ... | |
| |--------------| | |
| |.ldt .cs .eip | | |
| | others | | |
| |--------------| | |
| TSS2 | |
| ... | |
| |--------------| | |
| |.ldt .cs .eip | |<--| |
| | others | | | |
| |--------------| | | |LDTR =
| TSS1 | | |TSS1.ldt << 64 +
| ... | | |GDT[TSS1.ldt >> 3];
|n|--------------| | ①| ②|LDTR --> LDT1;
| | GDT items | |---|---
| |--------------| |TR=q << 64 + GDTR[q >>3 ]
| GDT |--> TSS1;
| ... |TLB=PAGE_TAB[x];
-------------------|
内存 */
大公司峰期并发需求量的增大不断促进并发技术的发展。
4.1 线程
线程是进程中的一段机器指令集,在程序中普遍以函数形式存在。一个进程中可包含多个线程,各线程共享进程内存地址空间。
与进程比较,线程可轻量化并发的上下文切换和内存资源占用。
|---------------|
| ... |
| |-----------| |
| | ... | |
| | |-------| | |
| | | stack | | |
| | |-------| | |
| | |instrs | | |
|------------| | | |-------| | |
| TR | | | thread_1 | |
| GDTR LDTR | | | ... | |
| CS EIP | | | |-------| | |
| others | | | | stack | | |
|------------| | | |-------| | |
CPU | | |instrs | | |
| | |-------| | |
| | thread_x | |
| | ... | |
| |-----------| |
| process_m |
| |
| ... |
| GDT...LDT |
| PCBs |
| .... |
|---------------|
memory
可用进程并发的硬件和软件策略管理线程并发,
只是线程并发稍加轻量化:
上下文切换不涉及TLB切换;线程共享进程内存地址空间,
线程执行栈空间就是进程地址空间的一部分。
4.2 协程
协程是线程或进程中的一段机器指令即,在程序中普遍以函数形式存在——进程或线程中可包含多个协程。协程与线程相比,协程可进一步轻量化并发的上下文切换和内存资源占用。
协程上下文切换可仅在用户程序层面,可不在内核中分配数据结构体资源。进程和线程在机器指令层面发生上下文切换,协程在编程语言语句层面发生上下文切换,即在访问共享内存时,前者需要互斥访问机制(如锁),互斥可从不同程度地降低并发性。
|-----------------|
| stack_space |
|-----------------|
| CTXs |
| ... |
| coroutine_fn1 |
| . |<-----|
| . |② |
| async I/O1 |---| |
| . | | |
| | | |
| ... | | |
| coroutine_fn2 | | |
| . | | |
| . |<--| ①|
| async I/O2 |------|
| . |
| |
| other instrs |
|-----------------|
thread_x
如CPU执行到线程async I/O 2处无数据返回时,
便跳转coroutine_fn1处执行,待执行到
async I/O 1处且无数据返回时再跳转回
async I/O 2处......
这样可提升CPU利用率以从整体提升协程执行效率。
协程中上下文跳转信息保存在用户内存空间中,
比起进程和线程的上下文信息要轻量许多,一
般只包含寄存器信息。
除了类似基于栈的协程切换机制外,还有基于堆
实现的协程切换机制,如python中生成器(yield)。
4.3 I/O并发
进程、线程以及协程的精髓在于——在与慢速设备交互时,用尽可能小的切换代价去提升CPU利用率,以从总体上提升CPU所执行程序的效率。
这个并发思想也能用在I/O上,对于一个I/O设备,串行使用方式为
|---| 1 req --> |--------------|
| C | 1 resp <-- | I/O |
| P | .... | device |
| U | n req --> | e.g. network |
|---| n resp <-- |--------------|
即1个I/O请求,1个I/O响应依时进行。
较CPU请求速度比,I/O响应比较慢。所以可趁I/O响应期间继续发异步I/O请求(如向不同网站的请求),让各I/O各凭己速依次响应,以尽量塞满I/O工作量以提升I/O利用率——从总体上提升I/O请求效率。
|---| 1 req --> |--------|
| | 2 req --> | |
| C | 3 req --> | I/O |
| | ... --> | |
| P | 3 resp <-- | |
| | ... --> | |
| | 1 resp <-- | device |
| U | ... --> | |
|---| x resp <-- |--------|
在I/O响应期间继续发I/O请求,
让各I/O各凭己速响应,从而提升I/O利用率。
实际上,在用多进程、多线程以及协程与I/O交互时,已隐式应用了I/O并发。在对响应较缓慢I/O(如互联网)有大量请求时,可使用“协程+I/O并发”提升效率,尤其内存资源不够时——进程或线程并发较I/O并发会耗更多内存资源。
CSDN 社区图书馆,开张营业! 深读计划,写书评领图书福利~以上是关于haribote&&linux0.11并发笔记整理抽取的主要内容,如果未能解决你的问题,请参考以下文章
haribote&&linux0.11引导程序阅读笔记整理抽取