内存地址
指针 / 引用
指针、引用本质上就是内存地址,有了内存地址就可以操作对应的内存数据了。
不同的数据类型
字节序
- 大端序(Big Endian):字节顺序从低地址到高地址顺序存储的字节序
- 小端序(Little Endian):字节顺序从高地址到低地址顺序存储的字节序
对于人类来说,大端序比较好理解,字节序从低地址到高地址,对于计算机来说,小端序比较容易操作。
进制转换
二进制和十进制之间的转换
十进制转成二进制:整数除2取余;小数乘2取整
二进制转成十进制:按权求和方法
十进制和十六进制之间的转换
将十六进制转成十进制:按权展开求和方法,不过权的基数是16
十进制转成十六进制和十进制转成二进制的方法是一样的。
二进制和十六进制之间的转换
十六进制转成二进制:
- 每一位十六进制数字转换为四位二进制数字,
- 运算的顺序也是从低位向高位依次进行
二进制转成十六进制:
- 每四位二进制数字转换为一位十六进制数字,
- 运算的顺序是从低位向高位依次进行,
- 高位不足四位用零补齐
CPU的控制器、运算器、寄存器
-
寄存器用来暂存指令、数据等处理对象,可以将它看做是内存的一种,一个CPU的内部一般有20~100个寄存器。
-
控制器负责把内存中的指令、数据读入寄存器,并根据指令的执行结果来控制整个计算机。
-
运算器负责运算从内存中读入寄存器的数据。
CPU的执行流程
- 程序计数器 从 内存 中读取出的指令是放入 指令寄存器 中的
- 指令接下来由 指令解码器(一种寄存器)解码,接着由运算器计算结果
- 计算的结果最终可能返回到内存,也可能被写入某个寄存器中,在计算过程中也可能从内存中读取数据
程序计数器
程序计数器又叫指令指针寄存器,是寄存器的一种。
-
程序计数器负责从内存中获取指令,一开始指向第一条指令的内存地址,当这条指令被执行后,指针会自动指向下一条指令的内存地址(根据当前指令本身的字节大小计算偏移,如字节大小是3,则下一条地址就是从当前地址往后偏移3个字节)。
-
不同计算机程序计数器的实现不同,一般它就是一个寄存器。
CPU的指令集
不同架构的 CPU 的指令集不同,不同架构的 CPU 中相同名字的指令的含义也不一定相同。
指令
-
一条指令就是一串二进制码,它的前几位一般是 操作码, 如
mov
、add
-
操作码 后面跟着的一般是 操作数,操作数可以是寄存器,也可以是某个内存地址,或者是常量
-
不管什么样架构的CPU,CPU指令基本上都是按照 操作码 + 操作数 的实现方式
函数调用栈
函数调用栈中的每个元素是一个栈帧,栈帧包含了参数值,局部变量和返回地址。
栈内存
计算机状态交互和系统调用
用户态 & 内核态
- 内核态:可以完全访问所有的硬件,也可以执行机器能够运行的任何指令
- 用户态:只能执行一部分机器指令,对于那些会影响机器的控制或者可进行IO操作的指令,在用户态中的程序中是禁止的
用户态:是指应用程序在运行时 CPU 所处的状态,这个时候的 CPU 所处的状态的级别特别低,不能直接访问某些机器指令,或者不能直接访问 I/O (读写磁盘)。
内核态:是指操作系统在运行时 CPU 所处的状态,这个时候的 CPU 可以执行任何的一条的指令,包括特权指令,包括访问 I/O 指令等。
应用程序、操作系统、硬件之间的关系:
操作系统、CPU、内存之间的关系:
系统调用
系统调用是如何从用户态陷入内核态的?
- 在 32 位Linux操作系统中是通过 80 软中断实现的
- 在 64 位Linux操作系统中是通过 syscall 汇编指令实现的
64 位操作系统用户态发起系统调用的过程:
- 将参数保存到寄存器
- 根据系统调用名称得到系统调用号,并将其存储到CPU的
rax
寄存器中 - 执行
syscall
指令 - CPU会根据
rax
寄存器中的系统调用号到sys_call_table
中找到对应的系统调用函数并执行该函数的指令代码,将运算结果写到rax
寄存器中 - 从
rax
寄存器中获取系统调用的结果值返回给用户态应用程序