百篇博客系列篇.本篇为:

精读内核源码就绕不过汇编语言,鸿蒙内核有6个汇编文件,读不懂它们就真的很难理解以下问题.

1.系统调用是如何实现的?

2.CPU是如何切换任务和进程上下文的?

3.硬件中断是如何处理的?

4.main函数到底是怎么来的?

5.开机最开始发生了什么?

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 = 1;
for(int a = 0;a < 100; a++){
sum = sum + fp(a);
}
return sum;
}
//编译器: armv7-a clang (trunk)
square(int, int):
sub sp, sp, #8 @sp减去8,意思为给square分配栈空间,只用2个栈空间完成计算
str r0, [sp, #4] @第一个参数入栈
str r1, [sp] @第二个参数入栈
ldr r1, [sp, #4] @取出第一个参数给r1
ldr r2, [sp] @取出第二个参数给r2
mul r0, r1, r2 @执行a*b给R0,返回值的工作一直是交给R0的
add sp, sp, #8 @函数执行完了,要释放申请的栈空间
bx lr @子程序返回,等同于mov pc,lr,即跳到调用处
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=1
str r0, [sp] @再把1也保存在SP的位置
ldr r0, [sp] @把SP的值给R0
ldr r1, [sp, #4] @把SP+4的值给R1
add r1, r0, r1 @执行r1=a+b
mov 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:
push {r11, lr} @r11(fp)/lr入栈,保存调用者的位置
mov r11, sp @r11用于保存sp值,函数栈开始位置
sub sp, sp, #16 @sp减去8,意思为给main分配栈空间,只用2个栈空间完成计算
mov r0, #0 @初始化r0
str r0, [r11, #-4] @作用是保存SUM的初始值
str r0, [sp, #8] @sum将始终占用SP+8的位置
str r0, [sp, #4] @a将始终占用SP+4的位置
b .LBB1_1 @跳到循环开始位置
.LBB1_1: @循环开始位置入口
ldr r0, [sp, #4] @取出a的值给r0
cmp r0, #99 @跟99比较
bgt .LBB1_4 @大于99,跳出循环 mov pc .LBB1_4
b .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,保存到r1
ldr 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的值,赋给r0
add r0, r0, #1 @r0增加1
str 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

这个简单的汇编并不是鸿蒙的汇编,只是先打个底,由浅入深, 但看懂了它基本理解鸿蒙汇编代码没有问题, 后续将详细分析鸿蒙内核各个汇编文件的作用.

开始分析上面的汇编代码.

第一: 上面的代码和鸿蒙内核用栈方式一样,都采用了递减满栈的方式, 什么是递减满栈? 递减指的是栈底地址高于栈顶地址,满栈指的是SP指针永远在栈顶.一定要理解递减满栈,否则读不懂内核汇编代码.举例说明:

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

首句汇编的含义就是申请栈空间, sp = sp - 8 ,一个栈内单元(栈空间)占4个字节,申请2个栈空间搞定函数的计算,仔细看下代码除了在函数的末尾 sp = sp + 8 又恢复在之前的位置的中间过程,SP的值是没有任务变化,它的指向是不动的, 这跟很多人对栈的认知是不一样的,它只是被用于计算,例如

ldr r1, [sp, #4] 的意思是取出SP+4这个虚拟地址的值给r1寄存器,SP的值并没有改变的,为什么要+呢,因为SP是指向栈顶的,地址是最小的. 满栈就是用栈过程中对地址的操作不能超过SP,所以你很少在计算过程中看到 把sp-4地址中的值给某个寄存器, 除非是特别的指令,否则不可能有这样的指令.

第二: sub sp, sp, #8add sp, sp, #8 是成对出现的,这就跟申请内存,释放内存的道理一样,这是内核对任务的运行栈管理方式,一样用多少申请多少,用完释放.空间大小就是栈帧,这是栈帧的本质含义.

第三: push {r11, lr}pop {r11, lr} 也是成对出现的,主要是用于函数调用,例如 A -> B, B要保存A的栈帧范围和指令位置, lr保存是是A函数执行到哪个指令的位置, r11干了fp的工作,其实就是指向 A的栈顶位置,如此B执行完后return回A的时候,先mov pc,lr 内核就知道改执行A的哪条指令了,同时又知道了A的栈顶位置.

第四: 频繁出现的R0寄存器的作用用于传参和返回值, A调用B之前,假如有两个参数,就把参数给r0 ,r1记录,充当了A的变量, 到了B中后,先让 r0,r1入栈,目的是保存参数值, 因为 B中要用r0,r1 ,他们变成B的变量用了. 返回值都是默认统一给r0保存. B中将返回值给r0,回到A中取出R0值对A来说这就是B的返回值.

这是以上为汇编代码的分析,追问两个问题

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

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

鸿蒙内核源码分析.总目录

v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o

百万汉字注解.百篇博客分析

百万汉字注解 >> 精读鸿蒙源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee| github| csdn| coding >

百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< 51cto| csdn| harmony| osc >

关注不迷路.代码即人生

QQ群:790015635 | 入群密码: 666

原创不易,欢迎转载,但请注明出处.

鸿蒙内核源码分析(用栈方式篇) | 程序运行场地谁提供的 | 百篇博客分析OpenHarmony源码 | v20.04的更多相关文章

  1. 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视GCC编译全过程 | 百篇博客分析OpenHarmony源码| v57.01

    百篇博客系列篇.本篇为: v57.xx 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视编译全过程 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...

  2. 鸿蒙源码分析系列(总目录) | 百万汉字注解 百篇博客分析 | 深入挖透OpenHarmony源码 | v8.23

    百篇博客系列篇.本篇为: v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51.c.h .o 百篇博客.往期回顾 在给OpenHarmony内核源码加注过程中,整理出以下 ...

  3. 鸿蒙内核源码分析(信号消费篇) | 谁让CPU连续四次换栈运行 | 百篇博客分析OpenHarmony源码 | v49.04

    百篇博客系列篇.本篇为: v49.xx 鸿蒙内核源码分析(信号消费篇) | 谁让CPU连续四次换栈运行 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁 ...

  4. 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 百篇博客分析OpenHarmony源码 | v28.03

    百篇博客系列篇.本篇为: v28.xx 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当 ...

  5. v76.01 鸿蒙内核源码分析(共享内存) | 进程间最快通讯方式 | 百篇博客分析OpenHarmony源码

    百篇博客分析|本篇为:(共享内存篇) | 进程间最快通讯方式 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) | 同样 ...

  6. 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分配方式  | 百篇博客分析OpenHarmony源码 | v11.02

    百篇博客系列篇.本篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分配方式 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些 ...

  7. 鸿蒙内核源码分析(Shell编辑篇) | 两个任务,三个阶段 | 百篇博客分析OpenHarmony源码 | v71.01

    子曰:"我非生而知之者,好古,敏以求之者也." <论语>:述而篇 百篇博客系列篇.本篇为: v71.xx 鸿蒙内核源码分析(Shell编辑篇) | 两个任务,三个阶段 ...

  8. 鸿蒙内核源码分析(信号生产篇) | 信号安装和发送过程是怎样的? | 百篇博客分析OpenHarmony源码 | v48.03

    百篇博客系列篇.本篇为: v48.xx 鸿蒙内核源码分析(信号生产篇) | 年过半百,依然活力十足 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管 ...

  9. 鸿蒙内核源码分析(进程回收篇) | 老父亲如何向老祖宗临终托孤 ? | 百篇博客分析OpenHarmony源码 | v47.01

    百篇博客系列篇.本篇为: v47.xx 鸿蒙内核源码分析(进程回收篇) | 临终前如何向老祖宗托孤 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管 ...

随机推荐

  1. flutter捕获应用退出弹出对话框

    使用WillPopScope组件,它会检测到子组件的Navigation的pop事件,并拦截下来.我们需要在它的onWillPop属性中返回一个新的组件(一般是一个Dialog)处理是否真的pop该页 ...

  2. 【js】--获取开始时间 和 截止时间中间的所有时间

    1.工具函数  将[中国标准时间] 转换成 [年月日 时分秒] /* * timeStamp: 标准时间 例: 'Tue Sep 22 2020 00:00:00 GMT+0800 (中国标准时间)' ...

  3. lwm2m 协议

    1.DTLS介绍 1.1 DTLS的作用 互联网先驱们最开始在设计互联网协议时主要考虑的是可用性,安全性是没有考虑在其中的,所以传输层的TCP.UDP协议本身都不具备安全性.SSL/TLS协议是基于T ...

  4. C# Fakes

    我们为了测试程序的运行逻辑,需要写单元测试来验证程序的逻辑.有的时候我们的逻辑需要依赖于外界的事物(需要一个文件,eg:数据库),我们不可能在运行单元测试的计算机都创建一个数据库,所以这个时候我们就需 ...

  5. 【ArcGIS】 设置管段的流向

    在排水管网或者燃气管网中对管段进行几何网络分析,常常用到设置管段流向,一般有三种方法: 1,有流向字段的,直接进行唯一值渲染, 2,没有流向字段的需要建立几何网络, 2.1 在几何网络存在的情况下,设 ...

  6. 回忆java输入输出流,走出误区

    input read 将一个XXX读入(input)---从输入流中读取数据的下一个字节(code操作的).output write 将一个类型的数据写入此流(code操作的)---然后把XXX输出( ...

  7. JavaWeb之JavaMail

    时间:2016-12-19 11:58 --邮件协议1.收发邮件    发邮件是从客户端把邮件发送到服务器,收邮件是把邮件服务器的邮件下载到客户端    2.邮件协议概述    与HTTP协议相同, ...

  8. Alibaba cloud 3 安装docker

    最近因为公司买阿里服务器装的 Alibaba cloud Linux 系统,在部署环境的时候也是遇到各种坑,网上教程大多都是其他系统的,今天就来分享一下自己安装Docker的步骤,同时也是给自己记录一 ...

  9. RabbitMq内存分页

  10. SpringBoot笔记(2)

    一.容器功能 1.1 组件添加 1. @Configuration Full模式:获取对象时,首先在容器内搜索是否存在,如存在直接拿出 默认为Full模式,单例 配置类组件之间有依赖关系,方法会被调用 ...