跳至主要內容

操作系统 - 系统以下

LincZero大约 4 分钟

操作系统 - 系统以下

物理内存 vs 程序员眼中的内存

内存的物理机制

  • 内部部分
    • 内存集成电路
  • 引脚 (接口) 部分
    • 有多个电路引脚 (以那种单片机上黑色的22引脚的小内存为例)
      • (2) VCC 电源,GND 电源
      • (10) A0-A9 地址信号引脚。= 10位 = 1024个地址 = 索引1024Byte的空间
      • (8) D0-D7 数据信号引脚。= 8位 = 一字节 = 每个地址可以存储8个字节
      • (2) RD WR 控制信号引脚。即读还是写
    • 信号引脚中,通过5V高电平,0V低电平,来表示10
    • 顺序
      • 写则依次:(1) 指定地址、(2) 输入数据、(3) 将WR设为1
      • 读则依次:(1) 制定地址、(2) 将RD设为1、(3) 从数据引脚中获取

字节序

详见CSAPP或内存管理相关笔记

高级语言、汇编语言、机器语言

略,详见CSAPP

objdump -d -S hello.o // -S可以看对应的C语言语句

不过还是Godbolt香 (戈伯特)

CPU执行过程

物理

集成电路,根据指令处理各种处理的电子电路,每个指令对对应相应的电子电路工作

  • 控制器:负责传入寄存器,得到结果后控制计算机
  • 寄存器 (20~100个):暂存指令、数据等(PC(计数器)有可能也在这个位置,例如IP就是指令指针寄存器)
  • 运算器:运算

流程

  1. 读取:CPU从内存中读取指令
  2. 解码:将10串拆成操作码和操作数,知道要去执行什么操作
  3. 执行
  4. (循环,根据计数器(PC)循环)

CPU指令集

  • arm架构CPU
  • X86架构CPU

查看CPU架构

  • windows:systeminfo,找 "系统类型" 一栏
  • linux:lscpu,找 "Architecture" 一栏

常用指令

详见SCAPP

  • mov A,B
  • add A,B
  • push A
  • pop A
  • call A
  • ret 无

常用操作数

  • 寄存器
    • rbp (register base pointer):栈基址寄存器(栈帧指针),指向当前帧的栈底地址
    • rsp (register stack pointer):栈顶寄存器(栈指针),指向栈顶元素
    • (函数调用时会使用上面两个)
    • rip:存储下一条指令的内存地址
  • 内存
  • 常数

程序 - 机器码 的对应

函数调用

int mod(int a, int b) {
    return a % b;
}

int add(int a, int b) {
    int res = mod(a, b);
    return res + b;
}

void main() {
    int a = 3;
    int b = 2;
    int c = add(a, b);
}

函数调用栈

  • 函数调用链:main -> add -> mod -> add -> main
  • 每个栈帧会包含:
    • 参数值
    • 局部变量
    • 返回地址 (出栈时使用,并返回对应的位置)

汇编:

000000000000000 <mod>
	push rbp			# 栈基址
	mov rbp, rsp
	……
	pop rbp				# 将rbp和rsp退出上一个栈中。退回地址:rbp退回到他指向的地址
	ret					# 回调。回调地址:rip存着
	
000000000000015 <add>
	push rbp			# 栈基址
	mov rbp, rsp
	……
	call 32 <add+0x1d>	# 调用mod函数
	mov DWORD PTR [rbp-0x4], eax
	……
	leave				# = mov rbp, rsp + pop rbp
	ret
	
00000000000003f <main>
	push rbp			# 栈基址
    mov rbp, rsp
	……
	call 64 <main+0x25>	# 调用add函数
	mov DWORD PTR [rbp-0xc], eax
	leave
	ret

main函数不是第一个被调用的函数,被名为start函数调用。在执行main函数之前,就已经存在栈帧了

其他指令

if 指令

void main() {
    int a = 3;
    int a = 2;
    int c;
    if (a == b) {
        c = a + b;
    } else {
        c = a - b;
    }
    return c;
}

汇编

000000000000000 <main>
	push rbp
	mov rbp, rsp
	# int a = 3;
    mov DWORD PTR [rbp-0x8], 0x3
    # int b = 2;
    mov DWORD PTR [rbp-0xc], 0x2
    # int c;
    # if (a == b) {
    mov eax, DWORD PTR [rbp-0x8]
    cmp eax, DWORD PTR [rbp-0xc]	# cmp =compare,比较结果放在条件码寄存器(标志位寄存器)
    jne 27 <main+0x27>				# jnp = jump if not equal,会去查看条件寄存器的零标志位
    mov eax, DWORD PTR [rbp-0xc]
    	# c = a+b;
    mov edx, DWORD PTR [rbp-0x8]
    add eax, edx
    mov DWORD PTR [rbp-0x4], eax
    jmp 34 <main+0x34>
    # } else {
    #  	  c = a - b
0x0027
	mov eax, DWORD PTR [rbp-0xc]
	mov edx, DWORD PTR [rbp-0x8]
	sub edx, eax
	mov eax, edx
	mov DWORD PTR [rbp-0x4], eax
	# }
	# return c;
0x0034
	mov eax, DWORD PTR [rbp-0x4]
	# }
	pop rbp
	ret

for 指令

int main() {
    int a = 0;
    for (int i = 0; i < 3; i++) {
        a += i;
    }
    return a;
}

汇编

000000000000000 <main>
	push rbp
	mov rbp, rsp
	# int a = 0;
	mov DWORD PTR [rbp-0x4], 0x0
	# for (int i = 0; i < 3; i++) {
	mov DWORD PTR [rbp-0x8], 0x0
	jmp 1e <main+0x1e>
	#     a += i;
0x0014
	mov eax, DWORD PTR [rbp-0x8]
	add DWORD PTR [rbp-0x4], eax
	# int main() {
	#     int a = 0;
	#     for (int i = 0; i < 3; i++) {
	add DWORD PTR [rbp-0x8], 0x1
0x001e
    cmp DWORD PTR [rbp-0x8], 0x2
    jle 14 <main+0x14>
	#         a += i;
	#     }
	#     return a;
	mov eax, DWORD PTR [rbp-0x8], 0x4
	# }
	pop rbp
	ret

goto

C语言有个goto指令,可以代替条件控制。用那个来代替 if、for 等流程控制(或者说在流程控制出来前就是这样做的),应该就很好理解了