v25.05 鸿蒙内核源码分析(并发并行) | 听过无数遍的两个概念 原创
子曰:“好勇疾贫,乱也。人而不仁,疾之已甚,乱也。” 《论语》:泰伯篇
百篇博客系列篇.本篇为:
v25.xx 鸿蒙内核源码分析(并发并行篇) | 听过无数遍的两个概念
任务管理相关篇为:
- v03.06 鸿蒙内核源码分析(时钟任务) | 触发调度谁的贡献最大
- v04.03 鸿蒙内核源码分析(任务调度) | 任务是内核调度的单元
- v05.05 鸿蒙内核源码分析(任务管理) | 任务池是如何管理的
- v06.03 鸿蒙内核源码分析(调度队列) | 内核有多少个调度队列
- v07.08 鸿蒙内核源码分析(调度机制) | 任务是如何被调度执行的
- v21.07 鸿蒙内核源码分析(线程概念) | 是谁在不断的折腾CPU
- v25.05 鸿蒙内核源码分析(并发并行) | 听过无数遍的两个概念
- v32.03 鸿蒙内核源码分析(CPU) | 整个内核就是一个死循环
- v37.06 鸿蒙内核源码分析(系统调用) | 开发者永远的口头禅
- v41.03 鸿蒙内核源码分析(任务切换) | 看汇编如何切换任务
本篇说清楚并发并行
读本篇之前建议先读系列进程/线程篇,会对并行并发更深的理解.
理解并发概念
-
并发(Concurrent):多个线程在单个核心运行,同一时间只能一个线程运行,内核不停切换线程,看起来像同时运行,实际上是线程被高速的切换.
-
通俗好理解的比喻就是高速单行道,单行道指的是CPU的核数,跑的车就是线程(任务),进程就是管理车的公司,一个公司可以有很多台车.并发和并行跟CPU的核数有关.车道上同时只能跑一辆车,但因为指挥系统很牛,够快,在毫秒级内就能换车跑,人根本感知不到切换.所以外部的感知会是同时在进行,实现了微观上的串行,宏观上的并行.
-
线程切换的本质是CPU要换场地上班,去哪里上班由哪里提供场地,那个场地就是任务栈,每个任务栈中保存了上班的各种材料,来了就行立马干活.那些材料就是任务上下文.简单的说就是上次活干到那里了,回来继续接着干.上下文由任务栈自己保存,CPU不管的,它来了只负责任务交过来的材料,材料显示去哪里搬砖它就去哪里搬砖.
记住一个单词就能记住并行并发的区别, 发单,发单(并发单行).
理解并行概念
并行(Parallel)每个线程分配给独立的CPU核心,线程真正的同时运行.
通俗好理解的比喻就是高速多行道,实现了微观和宏观上同时进行. 并行当然是快,人多了干活就不那么累,但干活人多了必然会带来人多的管理问题,会把问题变复杂,请想想会出现哪些问题?
理解协程概念
这里说下协程,例如go语言是有协程支持的,其实协程跟内核层没有关系,是应用层的概念.是在线程之上更高层的封装,用通俗的比喻来说就是在车内另外搞了几条车道玩.其对内核来说没有新东西,内核只负责车的调度,至于车内你想怎么弄那是应用程序自己的事.本质的区别是CPU根本没有换地方上班(没有被调度),而并发/并行都是换地方上班了.
内核如何描述CPU
这是内核对CPU的描述,主要是两个排序链表,一个是任务的排序,一个是定时器的排序.什么意思?
在系列篇中多次提过,任务是内核的调度单元,注意可不是进程,虽然调度也需要进程参与,也需要切换进程,切换用户空间.但调度的核心是切换任务,每个任务的代码指令才是CPU的粮食,它吃的是一条条的指令.每个任务都必须指定取粮地址(即入口函数).
另外还有一个东西能提供入口函数,就是定时任务.很重要也很常用,没它某宝每晚9点的准时秒杀实现不了.在内核每个CPU都有自己独立的任务和定时器链表.
每次Tick的到来,处理函数会去扫描这两个链表,看有没有定时器超时的任务需要执行,有则立即执行定时任务,定时任务是所有任务中优先级最高的,0号优先级,在系列篇中有专门讲定时器任务,可自行翻看.
LOSCFG_KERNEL_SMP
多CPU核的操作系统有3种处理模式(SMP+AMP+BMP) 鸿蒙实现的是 SMP 的方式
-
非对称多处理(Asymmetric multiprocessing,AMP)每个CPU内核运行一个独立的操作系统或同一操作系统的独立实例(instantiation)。
-
对称多处理(Symmetric multiprocessing,SMP)一个操作系统的实例可以同时管理所有CPU内核,且应用并不绑定某一个内核。
-
混合多处理(Bound multiprocessing,BMP)一个操作系统的实例可以同时管理所有CPU内核,但每个应用被锁定于某个指定的核心。
宏LOSCFG_KERNEL_SMP表示对多CPU核的支持,鸿蒙默认是打开LOSCFG_KERNEL_SMP的。
多CPU核支持
鸿蒙内核对CPU的操作见于 los_mp.c ,因文件不大,这里把代码都贴出来了.
代码一一都加上了注解,这里再一一说明下:
1.OsMpInit
多CPU核的初始化, 多核情况下每个CPU都有各自的编号, 内核有分成主次CPU, 0号默认为主CPU, OsMain()由主CPU执行,被汇编代码调用.
初始化只开了个定时任务,只干一件事就是回收不用的任务.回收的条件是任务是否收到了被干掉的信号. 例如shell命令 kill 9 14 ,意思是干掉14号线程的信号,这个信号会被线程保存起来. 可以选择自杀也可以等着被杀. 这里要注意,鸿蒙有两种情况下任务不能被干掉, 一种是系统任务不能被干掉的, 第二种是正在运行状态的任务.
2.次级CPU的初始化
同样由汇编代码调用,通过以下函数执行,完成每个CPU核的初始化
可以看出次级CPU有哪些初始化步骤:
-
初始化MMU,OsArchMmuInitPerCPU
-
设置当前任务 OsCurrTaskSet
-
初始化硬件中断 HalIrqInitPercpu
-
初始化定时器队列 OsSwtmrInit
-
创建空任务 OsIdleTaskCreate, 外面没有任务的时CPU就待在这个空任务里自己转圈圈.
-
开始自己的工作流程 OsStart,正式开始工作,跑任务
多CPU核还有哪些问题?
-
CPU之间抢资源的情况要怎么处理?
-
CPU之间通讯(也叫核间通讯)怎么解决?
-
如果确保两个CPU不会同时执行同一个任务?
-
汇编代码如何实现对各CPU的调动
请前往系列篇或直接前往内核注解代码查看.这里不再做说明.
百万汉字注解.精读内核源码
百篇博客分析.深挖内核地基
给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,.xx
代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。
基础工具>> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
加载运行>> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
进程管理>> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
编译构建>> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
进程通讯>> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
内存管理>> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
前因后果>> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
任务管理>> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
文件系统>> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
硬件架构>> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。
这位兄弟的文章标题,总有一种让人……的感觉。
是不是心潮澎湃了呀