本篇关键词:进程上下文、、、
下载 >> 离线文档.鸿蒙内核源码分析(百篇博客分析.挖透鸿蒙内核).pdf
内核汇编相关篇为:
- v74.01 鸿蒙内核源码分析(编码方式) | 机器指令是如何编码的
- v75.03 鸿蒙内核源码分析(汇编基础) | CPU上班也要打卡
- v76.04 鸿蒙内核源码分析(汇编传参) | 如何传递复杂的参数
- v77.01 鸿蒙内核源码分析(链接脚本) | 正在制作中 ...
- v78.01 鸿蒙内核源码分析(开机启动) | 从开机到main经历了什么
- v79.01 鸿蒙内核源码分析(进程切换) | 正在制作中 ...
- v80.03 鸿蒙内核源码分析(任务切换) | 看汇编如何切换任务
- v81.05 鸿蒙内核源码分析(中断切换) | 系统因中断活力四射
- v82.06 鸿蒙内核源码分析(异常接管) | 社会很单纯 复杂的是人
- v83.01 鸿蒙内核源码分析(缺页中断) | 正在制作中 ...
站长正在努力制作中 ... , 请客官稍等时日 , 可前往其他篇幅观看
再看下那些地方会调用 LOS_ArchMmuContextSwitch
,下图一目了然。
哪些地方会切换mmu
上下文
第一:通过调度算法,被选中的进程的空间改变了,自然映射页表就跟着变了,需要切换mmu上下文,还是直接看代码。代码不是很多,就都贴出来了,都加了注释,不记得调度算法的可去系列篇中看 鸿蒙内核源码分析(调度机制篇),里面有详细的阐述。
//调度算法-进程切换 STATIC VOID OsSchedSwitchProcess(LosProcessCB *runProcess, LosProcessCB *newProcess) { if (runProcess == newProcess) { return; } #if (LOSCFG_KERNEL_SMP == YES) runProcess->processStatus = OS_PROCESS_RUNTASK_COUNT_DEC(runProcess->processStatus); newProcess->processStatus = OS_PROCESS_RUNTASK_COUNT_ADD(newProcess->processStatus); LOS_ASSERT(!(OS_PROCESS_GET_RUNTASK_COUNT(newProcess->processStatus) > LOSCFG_KERNEL_CORE_NUM)); if (OS_PROCESS_GET_RUNTASK_COUNT(runProcess->processStatus) == 0) {//获取当前进程的任务数量 #endif runProcess->processStatus &= ~OS_PROCESS_STATUS_RUNNING; if ((runProcess->threadNumber > 1) && !(runProcess->processStatus & OS_PROCESS_STATUS_READY)) { runProcess->processStatus |= OS_PROCESS_STATUS_PEND; } #if (LOSCFG_KERNEL_SMP == YES) } #endif LOS_ASSERT(!(newProcess->processStatus & OS_PROCESS_STATUS_PEND));//断言进程不是阻塞状态 newProcess->processStatus |= OS_PROCESS_STATUS_RUNNING;//设置进程状态为运行状态 if (OsProcessIsUserMode(newProcess)) {//用户模式下切换进程mmu上下文 LOS_ArchMmuContextSwitch(&newProcess->vmSpace->archMmu);//新进程->虚拟空间中的->Mmu部分入参 } #ifdef LOSCFG_KERNEL_CPUP OsProcessCycleEndStart(newProcess->processID, OS_PROCESS_GET_RUNTASK_COUNT(runProcess->processStatus) + 1); #endif /* LOSCFG_KERNEL_CPUP */ OsCurrProcessSet(newProcess);//将进程置为 g_runProcess if ((newProcess->timeSlice == 0) && (newProcess->policy == LOS_SCHED_RR)) {//为用完时间片或初始进程分配时间片 newProcess->timeSlice = OS_PROCESS_SCHED_RR_INTERVAL;//重新分配时间片,默认 20ms } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33这里再啰嗦一句,系列篇中已经说了两个上下文切换了,一个是这里的因进程切换引起的mmu上下文切换,还一个是因task切换引起的CPU的上下文切换,还能想起来吗?
第二:是加载
ELF
文件的时候会切换mmu
,一个崭新的进程诞生了,具体翻看(进程映像篇)。其余是虚拟空间回收和刷新空间的时候,这个就自己看代码去吧。mmu是如何快速的通过虚拟地址找到物理地址的呢?答案是:TLB
,注意上面还有个TTB
,一个是寄存器, 一个是cache
,别搞混了。
asid寄存器
asid(Adress Space ID) 进程标识符,属于CP15协处理器的C13号寄存器,ASID可用来唯一标识进程,并为进程提供地址空间保护。当TLB试图解析虚拟页号时,它确保当前运行进程的ASID与虚拟页相关的ASID相匹配。如果不匹配,那么就作为TLB失效。除了提供地址空间保护外,ASID允许TLB同时包含多个进程的条目。如果TLB不支持独立的ASID,每次选择一个页表时(例如,上下文切换时),TLB就必须被冲刷(flushed)或删除,以确保下一个进程不会使用错误的地址转换。
TLB页表中有一个bit来指明当前的entry是global(nG=0,所有process都可以访问)还是non-global(nG=1,only本process允许访问)。如果是global类型,则TLB中不会tag ASID;如果是non-global类型,则TLB会tag上ASID,且MMU在TLB中查询时需要判断这个ASID和当前进程的ASID是否一致,只有一致才证明这条entry当前process有权限访问。
看到了吗?如果每次mmu上下文切换时,把TLB全部刷新已保证TLB中全是新进程的映射表,固然是可以,但效率太低了!!!进程的切换其实是秒级亚秒级的,地址的虚实转换是何等的频繁啊,怎么会这么现实呢,真实的情况是TLB中有很多很多其他进程占用的物理内存的记录还在,当然他们对物理内存的使用权也还在。所以当应用程序 new了10M内存以为是属于自己的时候,其实在内核层面根本就不属于你,还是别人在用,只有你用了1M的那一瞬间真正1M物理内存才属于你,而且当你的进程被其他进程切换后,很大可能你用的那1M也已经不在物理内存中了,已经被置换到硬盘上了。明白了吗?只关注应用开发的同学当然可以说这关我鸟事,给我的感觉有就行了,但想熟悉内核的同学就必须要明白,这是每分每秒都在发生的事情。
最后一个函数留给大家,asid是如何分配的?
/* allocate and free asid */
status_t OsAllocAsid(UINT32 *asid)
{
UINT32 flags;
LOS_SpinLockSave(&g_cpuAsidLock, &flags);
UINT32 firstZeroBit = LOS_BitmapFfz(g_asidPool, 1UL << MMU_ARM_ASID_BITS);
if (firstZeroBit >= 0 && firstZeroBit < (1UL << MMU_ARM_ASID_BITS)) {
LOS_BitmapSetNBits(g_asidPool, firstZeroBit, 1);
*asid = firstZeroBit;
LOS_SpinUnlockRestore(&g_cpuAsidLock, flags);
return LOS_OK;
}
LOS_SpinUnlockRestore(&g_cpuAsidLock, flags);
return firstZeroBit;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
百文说内核 | 抓住主脉络
- 百文相当于摸出内核的肌肉和器官系统,让人开始丰满有立体感,因是直接从注释源码起步,在加注释过程中,每每有心得处就整理,慢慢形成了以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切。
- 与代码需不断
debug
一样,文章内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx
代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。 - 百文在 < 鸿蒙研究站 | 开源中国 | 博客园 | 51cto | csdn | 知乎 | 掘金 > 站点发布,鸿蒙研究站 | weharmonyos 中回复 百文 可方便阅读。
按功能模块:
- 基础知识 >> 双向链表 | 内核概念 | 源码结构 | 地址空间 | 计时单位 | 优雅的宏 | 钩子框架 | 位图管理 | POSIX | main函数 |
- 进程管理 >> 调度故事 | 进程控制块 | 进程空间 | 线性区 | 红黑树 | 进程管理 | Fork进程 | 进程回收 | Shell编辑 | Shell解析 |
- 任务管理 >> 任务控制块 | 并发并行 | 就绪队列 | 调度机制 | 任务管理 | 用栈方式 | 软件定时器 | 控制台 | 远程登录 | 协议栈 |
- 内存管理 >> 内存规则 | 物理内存 | 虚拟内存 | 虚实映射 | 页表管理 | 静态分配 | TLFS算法 | 内存池管理 | 原子操作 | 圆整对齐 |
- 通讯机制 >> 通讯总览 | 自旋锁 | 互斥锁 | 快锁使用 | 快锁实现 | 读写锁 | 信号量 | 事件机制 | 信号生产 | 信号消费 | 消息队列 | 消息封装 | 消息映射 | 共享内存 |
- 文件系统 >> 文件概念 | 文件故事 | 索引节点 | VFS | 文件句柄 | 根文件系统 | 挂载机制 | 管道文件 | 文件映射 | 写时拷贝 |
- 硬件架构 >> 芯片模式 | ARM架构 | 指令集 | 协处理器 | 工作模式 | 寄存器 | 多核管理 | 中断概念 | 中断管理 |
- 内核汇编 >> 编码方式 | 汇编基础 | 汇编传参 | 链接脚本 | 开机启动 | 进程切换 | 任务切换 | 中断切换 | 异常接管 | 缺页中断 |
- 编译运行 >> 编译过程 | 编译构建 | GN语法 | 忍者无敌 | ELF格式 | ELF解析 | 静态链接 | 重定位 | 动态链接 | 进程映像 | 应用启动 | 系统调用 | VDSO |
- 调测工具 >> 模块监控 | 日志跟踪 | 系统安全 | 测试用例 |
- 前因后果 >> 总目录 | 源码注释 | 静态站点 | 参考手册 |
百万注源码 | 处处扣细节
百万汉字注解内核目的是要看清楚其毛细血管,细胞结构,等于在拿放大镜看内核。内核并不神秘,带着问题去源码中找答案是很容易上瘾的,你会发现很多文章对一些问题的解读是错误的,或者说不深刻难以自圆其说,你会慢慢形成自己新的解读,而新的解读又会碰到新的问题,如此层层递进,滚滚向前,拿着放大镜根本不愿意放手。
< gitee | github | coding | gitcode > 四大码仓推送 | 同步官方源码,鸿蒙研究站 | weharmonyos 中回复 百万 可方便阅读。
关注不迷路 | 代码即人生
据说喜欢点赞分享的,后来都成了大神。😃