v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码

Python微信订餐小程序课程视频

https://blog.csdn.net/m0_56069948/article/details/122285951

Python实战量化交易理财系统

https://blog.csdn.net/m0_56069948/article/details/122285941
本篇关键词:指令格式、条件域、类型域、操作域、数据指令、访存指令、跳转指令、SVC(软件中断)

v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码_第1张图片

内核汇编相关篇为:

  • v74.01 鸿蒙内核源码分析(编码方式) | 机器指令是如何编码的
  • v75.03 鸿蒙内核源码分析(汇编基础) | CPU上班也要打卡
  • v76.04 鸿蒙内核源码分析(汇编传参) | 如何传递复杂的参数
  • v77.01 鸿蒙内核源码分析(可变参数) | 正在制作中 …
  • v78.01 鸿蒙内核源码分析(开机启动) | 正在制作中 …
  • v79.01 鸿蒙内核源码分析(进程切换) | 正在制作中 …
  • v80.03 鸿蒙内核源码分析(任务切换) | 看汇编如何切换任务
  • v81.05 鸿蒙内核源码分析(中断切换) | 系统因中断活力四射
  • v82.06 鸿蒙内核源码分析(异常接管) | 社会很单纯 复杂的是人
  • v83.01 鸿蒙内核源码分析(缺页中断) | 正在制作中 …

本篇说清楚 ARM指令是如何被编码的,机器指令由哪些部分构成,指令有哪些类型,每种类型的语法又是怎样的 ?

代码案例 | C -> 汇编 -> 机器指令

看一段C语言编译(clang)成的最后的机器指令(armv7)

int main(){
    int a = 0;
    if( a != 1) 
        a = 2*a + 1;
    return a;
}

 生成汇编代码如下:

 main:
60c: sub	sp, sp, #8
610: mov	r0, #0
614: str	r0, [sp, #4]
618: str	r0, [sp]
61c: ldr	r0, [sp]
620: cmp	r0, #1
624: beq	640 0x34>
628: b 62c 0x20>
62c: ldr r1, [sp]
630: mov r0, #1
634: orr r0, r0, r1, lsl #1
638: str r0, [sp]
63c: b 640 0x34>
640: ldr r0, [sp]
644: add sp, sp, #8
648: bx lr

汇编代码对应的机器指令如下图所示:

v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码_第2张图片

便于后续分析,将以上代码整理成如下表格

汇编代码 机器指令(十六进制表示) 机器指令(二进制表示)
sub sp, sp, #8 e24dd008 1110 0010 0100 1101 1101 0000 0000 1000
mov r0, #0 e3a00000 1110 0011 1010 0000 0000 0000 0000 0000
str r0, [sp, #4] e58d0004 1110 0101 1000 1101 0000 0000 0000 0100
str r0, [sp] e58d0000 1110 0101 1000 1101 0000 0000 0000 0000
ldr r0, [sp] e59d0000 1110 0101 1001 1101 0000 0000 0000 0000
cmp r0, #1 e3500001 1110 0011 0101 0000 0000 0000 0000 0001
beq 640 0a000005 0000 1010 0000 0000 0000 0000 0000 0101
b 62c eaffffff 1110 1010 1111 1111 1111 1111 1111 1111
ldr r1, [sp] e59d1000 1110 0101 1001 1101 0001 0000 0000 0010
mov r0, #1 e3a00002 1110 0011 1010 0000 0000 0000 0000 0001
orr r0, r0, r1, lsl #1 e1800081 1110 0001 1000 0000 0000 0000 1000 0001
str r0, [sp] e58d0000 1110 0101 1000 1101 0000 0000 0000 0000
b 640 eaffffff 1110 1010 1111 1111 1111 1111 1111 1111
ldr r0, [sp] e59d1000 1110 0101 1001 1101 0001 0000 0000 0000
add sp, sp, #8 e28dd008 1110 0010 1000 1101 1101 0000 0000 1000
bx lr e12fff1e 1110 0001 0010 1111 1111 1111 0001 1110

CPSR寄存器

在理解本篇之前需了解下CPSR寄存器的高4[31,28] 表达的含义。关于寄存器的详细介绍可翻看 系列篇的 (寄存器篇)
v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码_第3张图片

N、Z、C、V均为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行!意义重大!

  • CPSR的第31位是 N,符号标志位。它记录相关指令执行后,其结果是否为负。
    如果为负 N = 1,如果是非负数 N = 0
  • CPSR的第30位是Z0标志位。它记录相关指令执行后,其结果是否为0
    如果结果为0。那么Z = 1。如果结果不为0,那么Z = 0
  • CPSR的第29位C,进位标志位(Carry)。一般情况下,进行无符号数的运算。
    加法运算:当运算结果产生了进位时(无符号数溢出),C=1,否则C=0
    减法运算(包括CMP):当运算时产生了借位时(无符号数溢出),C=0,否则C=1
  • CPSR的第28位是V,溢出标志位(Overflow)。在进行有符号数运算的时候,
    如果超过了机器所能标识的范围,称为溢出。

指令格式

ARM 指令流是一连串的字对齐的四字节指令流。每个 ARM 指令是一个单一的 32 位字(4字节),如图(3)
图(3)

解读
图为ARM指令的编码一级格式,所有的指令都必须符合一级格式,分成三部分:

  • 条件域: cond[31:28]表示,条件域会影响CPSR的条件码N、Z、C、V标志位。
  • 类型域: op1[27:25]op[4]arm将指令分成了六大类型 。
  • 操作域: 剩下的[24:5][4:0] 即图中的空白位/保留位,这是留给下级自由发挥的,不同的类型对这些保留位有不同的定义。可以理解为因类型变化而变化的二级格式。
  • 那有了二级格式会不会有三级格式 ? 答案是必须有, 二级格式只会对保留位定义部分位,会留一部分给具体的指令格式自由发挥。
  • 一定要理解这种层次结构才能理解ARM指令集的设计总思路,因为RISC(精简指令集) 的指令长度是固定的16/32/64位,以32位为例,所有的指令设计必须全用32位来表示,如果只有一层结构是难以满足众多的指令设计需求的,要灵活有包容就得给适当的空间发挥。

条件域

cond 为条件域,每一条可条件执行的条件指令都有4位的条件位域,2^4能表示16种条件

cond 助记符 含义(整型) 含义(浮点型) 条件标志
0000 EQ 相等 相等 Z == 1
0001 NE 不等 不等或无序 Z == 0
0010 CS 进位 大于等于或无序 C == 1
0011 CC 进位清除 小于 C == 0
0100 MI 减、负数 小于 N == 1
0101 PL 加、正数或 0 大于等于或无序 N == 0
0110 VS 溢出 无序 V == 1
0111 VC 未溢出 有序 V == 0
1000 HI 无符号大于 大于或无序 C == 1 and Z == 0
1001 LS 无符号小于或等于 小于或等于 C == 0 or Z == 1
1010 GE 有符号大于或等于 大于或等于 N == V
1011 LT 有符号小于 小于或无序 N != V
1100 GT 有符号大于 大于 Z == 0 and N ==V
1101 LE 有符号大于或等于 小于等于或无序 Z == 1 or N != V
1110 无条件 无条件 任何
  • 大部分的指令都是 1110 = e,无条件执行指令,只要看到 e开头的机器指令都属于这类

beq 640 // 机器码 0a000005 <=> 0000 1010 0000 0000 0000 0000 0000 0101
0000 EQ Equal(相等) Z == 1



### 类型域


**图(3)** 的 `op1` 域位于 `bits[27:25]`,占三位;`op` 域位于 `bit[4]`,占一位。它们的取值组合在一起,决定指令所属的分类(Instruction Class),其值对应的关系如下



op1 op 指令类型
00x - 数据处理以及杂项指令
010 - load/store word类型 或者 unsigned byte
011 0 同上
011 1 媒体接口指令
10x - 跳转指令和块数据操作指令,块数据操作指令指 STMDA 这类,连续内存操作。
11x - 协处理器指令和 svc 指令,包括高级的 SIMD 和浮点指令。


### 操作域


操作域是因类型变化而变化的二级格式 ,作用于保留位。包含


#### 00x | 数据处理类指令


![](https://gitee.com/weharmonyos/resources/raw/master/74/dp_register.png)


* 上图为涉及数据处理指令的对应编码,由 `op[占5位]`和`op2[占2位]`两项来确定指令的唯一性
* 一般情况下只需`op`指定唯一性,图中 `SUB`指令对应为 `0010x`,而代码案例中的第一句

sub sp, sp, #8 // 机器码 e24dd008 <=> 1110 0010 0100 1101 1101 0000 0000 1000


对应`[24:20]`位就是`0 0100`,从而`CPU`在译码阶段将其解析为`SUB`指令执行
* 需要用到`op2`的是 `MOV`系列指令,包括逻辑/算术左移右移,例如:

mov r0, #0 //e3a00000 <=> 1110 0011 1010 0000 0000 0000 0000 0000


中的`op = 1 1010` ,`op2 = 00` 对应 **MOV(register,ARM) on page A8-489**
`00x`中的`x`表示数据处理分两种情况
	+ `000` 无立即数参与(寄存器之间) ,图**A5.2.1** 表示了这种情况 `[27:25]= 000`
	+ `001` 有立即参与的运算,例如 `mov r0, #0` 中的 `[27:25]= 001`,此处未展示图,可前往 **ARM体系结构参考手册.pdf** 翻看


#### 010 | 加载存储指令


![](https://gitee.com/weharmonyos/resources/raw/master/74/load.png)


* `Load/store`是一组内存访问指令,用来在`ARM`寄存器和内存之间进行数据传送,`ARM`指令中有`3`种基本的数据传送指令


	+ 单寄存器 `Load/Store` 内存访问指令(`single register`):这些指令为ARM寄存器和存储器提供了更灵活的单数据项传送方式。数据可以使字节,16位半字或32位字
	+ 多寄存器 `Load/Store` 内存访问指令:可以实现大量数据的同时传送,主要用于进程的进入和退出、保存和恢复工作寄存器以及复制寄存器中的一片(一块)数据
	+ 寄存器交换指令(`single register swap`): 实现寄存器数据和内存数据进行交换,而且是在一条指令中完成,执行过程中不会受到中断干扰
* 出现在代码案例中的



str r0, [sp, #4] // 机器码 e58d0004 <=> 1110 0101 1000 1101 0000 0000 0000 0100
str r0, [sp] // 机器码 e58d0000 <=> 1110 0101 1000 1101 0000 0000 0000 0000
将r0中的字数据写入以SP为地址的存储器中
ldr r0, [sp] // 机器码 e59d0000 <=> 1110 0101 1001 1101 0000 0000 0000 0000
存储器地址地址为SP的数据读入r0 寄存器


`[27:25] = 010`说明都属于这类指令,完成对内存的读写,包括 `LDR`、`LDRB`、`LDRH`、`STR`、`STRB`、`STRH`六条指令。
`ldr` 为加载指令,但是加载到内存还是寄存器,这该怎么记 ? 因为主角是`CPU`,加载有进来的意思,将内容加载至寄存器中。`STR`有出去的意思,将内容保存到内存里。
`[sp]`相当于`C`语言的 `*sp` ,`sp` 指向程序运行栈当前位置
* [具体可看 >> ARM的六条访存指令集---LDR、LDRB、LDRH、STR、STRB、STRH](https://blog.csdn.net/biggbang)


#### 010 | 多媒体指令


![](https://gitee.com/weharmonyos/resources/raw/master/74/media.png)


多媒体指令使用较少,但是它涉及指令却很多


#### 10x | 跳转/分支/块数据处理 指令


![](https://gitee.com/weharmonyos/resources/raw/master/74/if.png)


* 出现在代码案例中的

beq 640 // 机器码 0a000005 <=> 0000 1010 0000 0000 0000 0000 0000 0101
b 62c // 机器码 eaffffff <=> 1110 1010 1111 1111 1111 1111 1111 1111


`[27:25] = 101`说明都属于这类指令
* 听得很多的`pop`,`push`也属于这类,成块的数据操作,例如`push`常用于将函数的所有参数一次性入栈。
* 内存 <> 寄存器 批量数据搬运指令 `STMDA (STMED)` `LDMDA/LDMF`。


#### 11x | 软中断/协处理器 指令


![](https://gitee.com/weharmonyos/resources/raw/master/74/svc.png)


* 其中最有名的就是`svc 0`,在系列篇中曾多次提及它,此处详细说下 `svc`, `svc`全称是 `Supervisor Call`, `Supervisor`是`CPU`的管理模式,`svc`导致处理器进入管理模式,很多人问的系统调用底层是怎么实现的? `svc`就是答案。
* 例如 `printf`是个标准库函数,在标准库的底层代码中会调用 `svc 0`,导致用户态的 `ARM` 程序通常将系统调用号传入 `R7` 寄存器(也被鸿蒙内核使用),然后用 `SVC` 指令调用 `0` 号中断来直接执行系统调用,
* 在以前的ARM架构版本中,`SVC`指令被称为`SWI`,软件中断。
* 描述`svc`功能的详细伪代码如下,请尝试读懂它

The TakeSVCException() pseudocode procedure describes how the processor takes the exception:
// TakeSVCException()
// ==================
TakeSVCException()
// Determine return information. SPSR is to be the current CPSR, after changing the IT[]
// bits to give them the correct values for the following instruction, and LR is to be
// the current PC minus 2 for Thumb or 4 for ARM, to change the PC offsets of 4 or 8
// respectively from the address of the current instruction into the required address of
// the next instruction, the SVC instruction having size 2bytes for Thumb or 4 bytes for ARM.
ITAdvance();
new_lr_value = if CPSR.T == ‘1’ then PC-2 else PC-4;
new_spsr_value = CPSR;
vect_offset = 8;
// Check whether to take exception to Hyp mode
// if in Hyp mode then stay in Hyp mode
take_to_hyp = (HaveVirtExt() && HaveSecurityExt() && SCR.NS == ‘1’ && CPSR.M == ‘11010’);
// if HCR.TGE is set to 1, take to Hyp mode through Hyp Trap vector
route_to_hyp = (HaveVirtExt() && HaveSecurityExt() && !IsSecure() && HCR.TGE == ‘1’
&& CPSR.M == ‘10000’); // User mode
// if HCR.TGE == ‘1’ and in a Non-secure PL1 mode, the effect is UNPREDICTABLE

preferred_exceptn_return = new_lr_value;
if take_to_hyp then
EnterHypMode(new_spsr_value, preferred_exceptn_return, vect_offset);
elsif route_to_hyp then
EnterHypMode(new_spsr_value, preferred_exceptn_return, 20);
else
// Enter Supervisor (‘10011’) mode, and ensure Secure state if initially in Monitor
// (‘10110’) mode. This affects the Banked versions of various registers accessed later
// in the code.
if CPSR.M == ‘10110’ then SCR.NS = ‘0’;
CPSR.M = ‘10011’;
// Write return information to registers, and make further CPSR changes: IRQs disabled,
// IT state reset, instruction set and endianness set to SCTLR-configured values.
SPSR[] = new_spsr_value;
R[14] = new_lr_value;
CPSR.I = ‘1’;
CPSR.IT = ‘00000000’;
CPSR.J = ‘0’; CPSR.T = SCTLR.TE; // TE=0: ARM, TE=1: Thumb
CPSR.E = SCTLR.EE; // EE=0: little-endian, EE=1: big-endian
// Branch to SVC vector.
BranchTo(ExcVectorBase() + vect_offset);

* 这部分内容在系列篇 **(寄存器篇)** ,**(系统调用篇)** ,**(标准库篇)** 中都有提及。


### 具体指令


细看几条代码案例出现的常用指令


#### sub sp, sp, #8



sub sp, sp, #8 // 机器码 e24dd008 < = > 1110 0010 0100 1101 1101 0000 0000 1000


是减法操作指令,减法编码格式为
![](https://gitee.com/weharmonyos/resources/raw/master/74/sub.jpg)


图中除了给出格式语法还有一段伪代码用于描述指令的使用条件


* `sp`为 `13`号寄存器, `lr`为 `14`号寄存器 ,`pc`为 `15`号寄存器。
* 如果是`PC`寄存器`(Rn = 15)`且`S`等于`0` 查看 `ADR`指令。。
* 如果是`SP`寄存器`(Rn = 13)` 看 `SUB`(申请栈空间)。
* 如果是`PC`寄存器`(Rd = 15)`且`S`等于`1` 。查看 `subs`  `pc`  `lr`相关指令
* 套用格式结合源码




| cond | op1 | 操作码 | S | Rn | Rd | imm12(立即数) |
| --- | --- | --- | --- | --- | --- | --- |
| 1110 | 001 | 0010 | 0 | 1101 | 1101 | 0000 0000 1000 |
| 无条件执行 | 表示数据处理 | SUB |  | sp | sp | 8 |


#### mov r0, #0


![](https://gitee.com/weharmonyos/resources/raw/master/74/mov.png)



mov r0, #0 //e3a00000 1110 0011 1010 0000 0000 0000 0000 0000


#### bx lr


![](https://gitee.com/weharmonyos/resources/raw/master/74/bx.png)



bx lr e12fff1e 1110 0001 0010 1111 1111 1111 0001 1110


* `Rm = 1110` 对应 `lr` 寄存器 ,其相当于高级语言的 `return`,函数执行完了需切回到调用它的函数位置继续执行,`lr`保存的就是那个位置,从哪里来就回到哪里去。


### 百文说内核 | 抓住主脉络


* 百文相当于摸出内核的肌肉和器官系统,让人开始丰满有立体感,因是直接从注释源码起步,在加注释过程中,每每有心得处就整理,慢慢形成了以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切。
* 与代码需不断`debug`一样,文章内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,`v**.xx` 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。
* 百文在 < 鸿蒙研究站 | 开源中国 | 博客园 | 51cto | csdn | 知乎 | 掘金 > 站点发布,**鸿蒙研究站 | weharmonyos** 中回复 **百文** 可方便阅读。
* ![](https://img-blog.csdnimg.cn/img_convert/7358818726626d4deff9d0d3d71d548b.png)


按功能模块:




| 基础知识 | 进程管理 | 任务管理 | 内存管理 |
| --- | --- | --- | --- |
| [双向链表](https://blog.csdn.net/biggbang)[内核概念](https://blog.csdn.net/biggbang)[源码结构](https://blog.csdn.net/biggbang)[地址空间](https://blog.csdn.net/biggbang)[计时单位](https://blog.csdn.net/biggbang)[宏的使用](https://blog.csdn.net/biggbang)[钩子框架](https://blog.csdn.net/biggbang)[位图管理](https://blog.csdn.net/biggbang)[POSIX](https://blog.csdn.net/biggbang)[main函数](https://blog.csdn.net/biggbang) | [调度故事](https://blog.csdn.net/biggbang)[进程控制块](https://blog.csdn.net/biggbang)[进程空间](https://blog.csdn.net/biggbang)[线性区](https://blog.csdn.net/biggbang)[红黑树](https://blog.csdn.net/biggbang)[进程管理](https://blog.csdn.net/biggbang)[Fork进程](https://blog.csdn.net/biggbang)[进程回收](https://blog.csdn.net/biggbang)[Shell编辑](https://blog.csdn.net/biggbang)[Shell解析](https://blog.csdn.net/biggbang) | [任务控制块](https://blog.csdn.net/biggbang)[并发并行](https://blog.csdn.net/biggbang)[就绪队列](https://blog.csdn.net/biggbang)[调度机制](https://blog.csdn.net/biggbang)[任务管理](https://blog.csdn.net/biggbang)[用栈方式](https://blog.csdn.net/biggbang)[软件定时器](https://blog.csdn.net/biggbang)[控制台](https://blog.csdn.net/biggbang)[远程登录](https://blog.csdn.net/biggbang)[协议栈](https://blog.csdn.net/biggbang) | [内存规则](https://blog.csdn.net/biggbang)[物理内存](https://blog.csdn.net/biggbang)[虚拟内存](https://blog.csdn.net/biggbang)[虚实映射](https://blog.csdn.net/biggbang)[页表管理](https://blog.csdn.net/biggbang)[静态分配](https://blog.csdn.net/biggbang)[TLFS算法](https://blog.csdn.net/biggbang)[内存池管理](https://blog.csdn.net/biggbang)[原子操作](https://blog.csdn.net/biggbang)[圆整对齐](https://blog.csdn.net/biggbang) |
| 通讯机制 | 文件系统 | 硬件架构 | 内核汇编 |
| [通讯总览](https://blog.csdn.net/biggbang)[自旋锁](https://blog.csdn.net/biggbang)[互斥锁](https://blog.csdn.net/biggbang)[快锁使用](https://blog.csdn.net/biggbang)[快锁实现](https://blog.csdn.net/biggbang)[读写锁](https://blog.csdn.net/biggbang)[信号量](https://blog.csdn.net/biggbang)[事件机制](https://blog.csdn.net/biggbang)[信号生产](https://blog.csdn.net/biggbang)[信号消费](https://blog.csdn.net/biggbang)[消息队列](https://blog.csdn.net/biggbang)[消息封装](https://blog.csdn.net/biggbang)[消息映射](https://blog.csdn.net/biggbang)[共享内存](https://blog.csdn.net/biggbang) | [文件概念](https://blog.csdn.net/biggbang)[文件故事](https://blog.csdn.net/biggbang)[索引节点](https://blog.csdn.net/biggbang)[VFS](https://blog.csdn.net/biggbang)[文件句柄](https://blog.csdn.net/biggbang)[根文件系统](https://blog.csdn.net/biggbang)[挂载机制](https://blog.csdn.net/biggbang)[管道文件](https://blog.csdn.net/biggbang)[文件映射](https://blog.csdn.net/biggbang)[写时拷贝](https://blog.csdn.net/biggbang) | [芯片模式](https://blog.csdn.net/biggbang)[ARM架构](https://blog.csdn.net/biggbang)[指令集](https://blog.csdn.net/biggbang)[协处理器](https://blog.csdn.net/biggbang)[工作模式](https://blog.csdn.net/biggbang)[寄存器](https://blog.csdn.net/biggbang)[多核管理](https://blog.csdn.net/biggbang)[中断概念](https://blog.csdn.net/biggbang)[中断管理](https://blog.csdn.net/biggbang) | [编码方式](https://blog.csdn.net/biggbang)[汇编基础](https://blog.csdn.net/biggbang)[汇编传参](https://blog.csdn.net/biggbang)[可变参数](https://blog.csdn.net/biggbang)[开机启动](https://blog.csdn.net/biggbang)[进程切换](https://blog.csdn.net/biggbang)[任务切换](https://blog.csdn.net/biggbang)[中断切换](https://blog.csdn.net/biggbang)[异常接管](https://blog.csdn.net/biggbang)[缺页中断](https://blog.csdn.net/biggbang) |
| 编译运行 | 调测工具 |  |  |
| [编译过程](https://blog.csdn.net/biggbang)[编译构建](https://blog.csdn.net/biggbang)[GN语法](https://blog.csdn.net/biggbang)[忍者无敌](https://blog.csdn.net/biggbang)[ELF格式](https://blog.csdn.net/biggbang)[ELF解析](https://blog.csdn.net/biggbang)[静态链接](https://blog.csdn.net/biggbang)[重定位](https://blog.csdn.net/biggbang)[动态链接](https://blog.csdn.net/biggbang)[进程映像](https://blog.csdn.net/biggbang)[应用启动](https://blog.csdn.net/biggbang)[系统调用](https://blog.csdn.net/biggbang)[VDSO](https://blog.csdn.net/biggbang) | [模块监控](https://blog.csdn.net/biggbang)[日志跟踪](https://blog.csdn.net/biggbang)[系统安全](https://blog.csdn.net/biggbang)[测试用例](https://blog.csdn.net/biggbang) |  |  |


### 百万注源码 | 处处扣细节


* 百万汉字注解内核目的是要看清楚其毛细血管,细胞结构,等于在拿放大镜看内核。内核并不神秘,带着问题去源码中找答案是很容易上瘾的,你会发现很多文章对一些问题的解读是错误的,或者说不深刻难以自圆其说,你会慢慢形成自己新的解读,而新的解读又会碰到新的问题,如此层层递进,滚滚向前,拿着放大镜根本不愿意放手。
* [< gitee](https://blog.csdn.net/biggbang) | [github](https://blog.csdn.net/biggbang) | [coding](https://blog.csdn.net/biggbang) | [gitcode >](https://blog.csdn.net/biggbang) 四大码仓推送 | 同步官方源码,**鸿蒙研究站 | weharmonyos** 中回复 **百万** 可方便阅读。


[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFyUqxW8-1650388244395)(https://gitee.com/weharmony/kernel_liteos_a_note/widgets/widget_card.svg?colors=393222,ebdfc1,fffae5,d8ca9f,393222,a28b40)]](https://blog.csdn.net/biggbang)


据说喜欢点赞分享的,后来都成了大神。



你可能感兴趣的:(计算机,计算机)