鸿蒙内核源码分析(汇编基础篇) | CPU在哪里打卡上班

2024-04-30 00:20

本文主要是介绍鸿蒙内核源码分析(汇编基础篇) | CPU在哪里打卡上班,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本篇通过拆解一段很简单的汇编代码来快速认识汇编,为读懂鸿蒙汇编打基础.系列篇后续将逐个剖析鸿蒙的汇编文件.

汇编很简单

  • 第一: 要认定汇编语言一定是简单的,没有高深的东西,无非就是数据的搬来搬去,运行时数据主要待在两个地方:内存和寄存器。寄存器是CPU内部存储器,离运算器最近,所以最快.

  • 第二: 运行空间(栈空间)就是CPU打卡上班的地方,内核设计者规定谁请CPU上班由谁提供场地,用户程序提供的场地叫用户栈,敏感工作CPU要带回公司做,公司提供的场地叫内核栈,敏感工作叫系统调用,系统调用的本质理解是CPU要切换工作模式即切换办公场地。

  • 第三:CPU的工作顺序是流水线的,它只认指令,而且只去一个地方(指向代码段的PC寄存器)拿指令运算消化。指令集是告诉外界我CPU能干什么活并提供对话指令,汇编语言是人和CPU能愉快沟通不拧巴的共识语言。一一对应了CPU指令,又能确保记性不好的人类能模块化的设计idea, 先看一段C编译成汇编代码再来说模块化。

square(c -> 汇编)

//编译器: armv7-a clang (trunk)
//++++++++++++ square(c -> 汇编)++++++++++++++++++++++++
int square(int a,int b){return a*b;
}
square(int, int):sub     sp, sp, #8     @sp减去8,意思为给square分配栈空间,只用2个栈空间完成计算str     r0, [sp, #4]   @第一个参数入栈str     r1, [sp]       @第二个参数入栈ldr     r1, [sp, #4]   @取出第一个参数给r1ldr     r2, [sp]       @取出第二个参数给r2mul     r0, r1, r2     @执行a*b给R0,返回值的工作一直是交给R0的add     sp, sp, #8     @函数执行完了,要释放申请的栈空间bx      lr             @子程序返回,等同于mov pc,lr,即跳到调用处

fp(c -> 汇编)

//++++++++++++ fp(c -> 汇编)++++++++++++++++++++++++
int fp(int b)
{int a = 1;return square(a+b,a+b);
}
fp(int):push    {r11, lr}      @r11(fp)/lr入栈,保存调用者main的位置mov     r11, sp        @r11用于保存sp值,函数栈开始位置 sub     sp, sp, #8     @sp减去8,意思为给fp分配栈空间,只用2个栈空间完成计算str     r0, [sp, #4]   @先保存参数值,放在SP+4,此时r0中存放的是参数mov     r0, #1         @r0=1str     r0, [sp]       @再把1也保存在SP的位置ldr     r0, [sp]       @把SP的值给R0ldr     r1, [sp, #4]   @把SP+4的值给R1add     r1, r0, r1     @执行r1=a+bmov     r0, r1         @r0=r1,用r0,r1传参bl      square(int, int)@先mov lr, pc 再mov pc square(int, int)   mov     sp, r11        @函数执行完了,要释放申请的栈空间 pop     {r11, lr}      @弹出r11和lr,lr是专用标签,弹出就自动复制给lr寄存器bx      lr             @子程序返回,等同于mov pc,lr,即跳到调用处

main(c -> 汇编)

//++++++++++++ main(c -> 汇编)++++++++++++++++++++++++
int main()
{int sum = 0;for(int a = 0;a < 100; a++){sum = sum + fp(a);}return sum;
}
main:push    {r11, lr}      @r11(fp)/lr入栈,保存调用者的位置mov     r11, sp        @r11用于保存sp值,函数栈开始位置sub     sp, sp, #16    @sp减去16,意思为给main分配栈空间,只用4个栈空间完成计算mov     r0, #0         @初始化r0str     r0, [r11, #-4] @执行sum = 0str     r0, [sp, #8]   @sum将始终占用SP+8的位置str     r0, [sp, #4]   @a将始终占用SP+4的位置b       .LBB1_1        @跳到循环开始位置
.LBB1_1:                       @循环开始位置入口ldr     r0, [sp, #4]   @取出a的值给r0cmp     r0, #99        @跟99比较bgt     .LBB1_4        @大于99,跳出循环 mov pc .LBB1_4b       .LBB1_2        @继续循环,直接 mov pc .LBB1_2
.LBB1_2:                       @符合循环条件入口ldr     r0, [sp, #8]   @取出sum的值给r0,sp+8用于写SUM的值str     r0, [sp]       @先保存SUM的值,SP的位置用于读SUM值ldr     r0, [sp, #4]   @r0用于传参,取出A的值给r0作为fp的参数bl      fp(int)        @先mov lr, pc再mov pc fp(int)mov     r1, r0         @fp的返回值为r0,保存到r1ldr     r0, [sp]       @取出SUM的值add     r0, r0, r1     @计算新sum的值,由R0保存str     r0, [sp, #8]   @将新sum保存到SP+8的位置b       .LBB1_3        @无条件跳转,直接 mov pc .LBB1_3
.LBB1_3:                       @完成a++操作入口ldr     r0, [sp, #4]   @SP+4中记录是a的值,赋给r0add     r0, r0, #1     @r0增加1str     r0, [sp, #4]   @把新的a值放回SP+4里去b       .LBB1_1        @跳转到比较 a < 100 处
.LBB1_4:                       @循环结束入口ldr     r0, [sp, #8]   @最后SUM的结果给R0,返回值的工作一直是交给R0的mov     sp, r11        @函数执行完了,要释放申请的栈空间pop     {r11, lr}      @弹出r11和lr,lr是专用标签,弹出就自动复制给lr寄存器bx      lr             @子程序返回,跳转到lr处等同于 MOV PC, LR

代码有点长,都加了注释,如果能直接看懂那么恭喜你,鸿蒙内核的6个汇编文件基于也就懂了。这是以下C文件全貌

文件全貌

#include <stdio.h>
#include <math.h>int square(int a,int b){return a*b;
}int fp(int b)
{int a = 1;return square(a+b,a+b);
}int main()
{int sum = 0;for(int a = 0;a < 100; a++){sum = sum + fp(a);}return sum;
}

代码很简单谁都能看懂,代码很典型,具有代表性,有循环,有判断,有运算,有多级函数调用。编译后的汇编代码基本和C语言的结构差不太多,区别是对循环的实现用了四个模块,四个模块也好理解:
一个是开始块(LBB1_1), 一个符合条件的处理块(LBB1_2),一个条件发生变化块(LBB1_3),最后收尾块(LBB1_4).

按块逐一剖析.

先看最短的那个

int square(int a,int b){return a*b;
}
//编译成
square(int, int):sub     sp, sp, #8     @sp减去8,意思为给square分配栈空间,只用2个栈空间完成计算str     r0, [sp, #4]   @第一个参数入栈str     r1, [sp]       @第二个参数入栈ldr     r1, [sp, #4]   @取出第一个参数给r1ldr     r2, [sp]       @取出第二个参数给r2mul     r0, r1, r2     @执行a*b给R0,返回值的工作一直是交给R0的add     sp, sp, #8     @函数执行完了,要释放申请的栈空间bx      lr             @子程序返回,等同于mov pc,lr,即跳到调用处

首先上来一句 sub sp, sp, #8 等同于 sp = sp - 8 ,CPU运行需要场地,这个场地就是栈 ,SP是指向栈的指针,表示此时用栈的刻度. 代码和鸿蒙内核用栈方式一样,都采用了递减满栈的方式(FD).

什么是递减满栈? 递减指的是栈底地址高于栈顶地址,栈的生长方向是递减的, 满栈指的是SP指针永远指向栈顶. 每个函数都有自己独立的栈底和栈顶,之间的空间统称栈帧.可以理解为分配了一块

区域给函数运行,sub sp, sp, #8 代表申请2个栈空间,一个栈空间按四个字节算.

用完要不要释放?当然要,add sp, sp, #8 就是释放栈空间. 是一对的,减了又加回去,空间就归还了.

ldr r1, [sp, #4] 的意思是取出SP+4这个虚拟地址的值给r1寄存器,而SP的指向并没有改变的,还是在栈顶, 为什么要+呢, +就是往回数, 定位到分配的栈空间上.

一定要理解递减满栈,这是关键! 否则读不懂内核汇编代码.

入参方式

一般都是通过寄存器(r0…r10)传参,fp调用square之前会先将参数给(r0…r10)

        add     r1, r0, r1     @执行r1=a+bmov     r0, r1         @r0=r1,用r0,r1传参bl      square(int, int)@先mov lr, pc 再mov pc square(int, int) 

到了square中后,先让 r0,r1入栈,目的是保存参数值, 因为 square中要用r0,r1 ,

        str     r0, [sp, #4]   @先入栈保存第一个参数str     r1, [sp]       @再入栈保存第二个参数ldr     r1, [sp, #4]   @再取出第一个参数给r1,(a*b)中a值ldr     r2, [sp]       @再取出第二个参数给r2,用于计算 (a*b)中b值

是不是感觉这段汇编很傻,直接不保存计算不就完了吗,这个是流程问题,编译器统一先保存参数,至于你想怎么用它不管,也管不了.

另外返回值都是默认统一给r0保存. square中将(a*b)的结果给了r0,回到fp中取出R0对fp来说这就是square的返回值,这是规定.

函数调用
main 和 fp 中都需要调用其他函数,所以都出现了

        push    {r11, lr}//....pop     {r11, lr}

这哥俩也是成对出现的,这是函数调用的必备装备,作用是保存和恢复调用者的现场,例如 main -> fp, fp要保存main的栈帧范围和指令位置, lr保存的是main函数执行到哪个指令的位置, r11的作用是指向main的栈顶位置,如此fp执行完后return回main的时候,先mov pc,lr, PC寄存器的值一变, 表示执行的代码就变了,又回到了main的指令和栈帧继续未完成的事业.

内存和寄存器数据怎么搬?

数据主要待在两个地方:内存和寄存器. 寄存器<->寄存器 , 内存<->寄存器 , 内存<->内存 搬运指令都不一样.

        str     r1, [sp]       @ 寄存器->内存ldr     r1, [sp, #4]   @ 内存->寄存器

这又是一对,用于 内存<->寄存器之间,熟知的 mov r0, r1 用于 寄存器<->寄存器

追问三个问题
第一:如果是可变参数怎么办? 100个参数怎么整, 通过寄存器总共就12个,不够传参啊

第二:返回值可以有多个吗?

第三:数据搬运可以不经过CPU吗?

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线。大家可以进行参考学习:https://qr21.cn/FV7h05

①全方位,更合理的学习路径
路线图包括ArkTS基础语法、鸿蒙应用APP开发、鸿蒙能力集APP开发、次开发多端部署开发、物联网开发等九大模块,六大实战项目贯穿始终,由浅入深,层层递进,深入理解鸿蒙开发原理!

②多层次,更多的鸿蒙原生应用
路线图将包含完全基于鸿蒙内核开发的应用,比如一次开发多端部署、自由流转、元服务、端云一体化等,多方位的学习内容让学生能够高效掌握鸿蒙开发,少走弯路,真正理解并应用鸿蒙的核心技术和理念。

③实战化,更贴合企业需求的技术点
学习路线图中的每一个技术点都能够紧贴企业需求,经过多次真实实践,每一个知识点、每一个项目,都是码牛课堂鸿蒙研发团队精心打磨和深度解析的成果,注重对学生的细致教学,每一步都确保学生能够真正理解和掌握。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:https://qr21.cn/FV7h05

如何快速入门:

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr21.cn/FV7h05

大厂鸿蒙面试题::https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

这篇关于鸿蒙内核源码分析(汇编基础篇) | CPU在哪里打卡上班的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/947538

相关文章

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

Spring的基础事务注解@Transactional作用解读

《Spring的基础事务注解@Transactional作用解读》文章介绍了Spring框架中的事务管理,核心注解@Transactional用于声明事务,支持传播机制、隔离级别等配置,结合@Tran... 目录一、事务管理基础1.1 Spring事务的核心注解1.2 注解属性详解1.3 实现原理二、事务事

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

Java中最全最基础的IO流概述和简介案例分析

《Java中最全最基础的IO流概述和简介案例分析》JavaIO流用于程序与外部设备的数据交互,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),处理... 目录IO流简介IO是什么应用场景IO流的分类流的超类类型字节文件流应用简介核心API文件输出流应用文

java 恺撒加密/解密实现原理(附带源码)

《java恺撒加密/解密实现原理(附带源码)》本文介绍Java实现恺撒加密与解密,通过固定位移量对字母进行循环替换,保留大小写及非字母字符,由于其实现简单、易于理解,恺撒加密常被用作学习加密算法的入... 目录Java 恺撒加密/解密实现1. 项目背景与介绍2. 相关知识2.1 恺撒加密算法原理2.2 Ja