计算机系统概论 复习笔记
做(mo)了一周的PA之后,终于要开始面对计系概抽代数分自动机了,祝我好运(
2.1 整数
书本 p22
信息储存
- 字节:8位
- 虚拟内存,地址,虚拟地址空间
十六进制表示法
- 以0x或0X开头
字数据大小
- 字长:指针数据的标称大小。字长为 位的机器,虚拟地址的范围是 ,程序最多访问 个字节。
寻址和字节顺序
- 小端法(最低有效字节在最前面),大端法(最高……)(PPT p7有图)
整数表示
整数数据类型
- 大小:PPT p9
- 取值范围:书本 p42-43
整数编码方式
- 无符号数:原码(表示:数字后带U)
- 带符号数:补码
- 非负数:补码 = 原码
- 负数:将原码的符号位保持不变,其余各位取反,然后加1,得到补码
- 补码的补码 = 原码
- 取值范围:无符号数 ,带符号数
- 强制类型转换:位值不变,改变了解释这些位的方式
- C语言中,无符号数与带符号数混合使用,带符号数默认转换为无符号数
整数运算
无符号加法
- PPT p17,书本 p60
补码加法
- 公式:
- PPT p18,书本 p63
补码的非
- 书本 p66(网络旁注)
无符号乘法 & 补码乘法
- 书本 p67
无符号数除以
- 逻辑右移,PPT p21,书本p 72
带符号数除以
- 需要加上偏置 ,算术右移,PPT p24,书本 p73
2.2 浮点数
书本 p75
IEEE 浮点表示
- 表示形式:
- 符号:s
- 尾数:M 是一个二进制小数,范围是 ,或者 。
- 阶码:E
- 储存形式和数据类型大小:PPT p7
- 规格化浮点数:阶码域不全为0也不全为1,M≥1,PPT p9,书本 p79
- 非规格化浮点数:阶码域全为0,不包含开头的1,PPT p11
- 特殊值:阶码全为1,表示无穷
舍入
- 向偶数舍入/向最接近的值舍入:PPT p23,书本 p83
- 如果不在两个值正中间:取最接近的值
- 如果在正中间:保证最低位是偶数(0)
- 向零舍入,向下舍入,向上舍入
C语言中的浮点数
- PPT p29,书本 p86
3 80X86汇编语言与C语言
书本 p113
程序编码
PPT 3.1
机器级代码
- 程序计数器(PC,%rip):给出将要执行的下一条指令在内存中的地址
- 整数寄存器文件:包含16个命名的位置,分别储存64位的值,这些寄存器可以储存地址或整数数据
- 条件码寄存器:保存着最近执行的算术或逻辑指令的状态信息
- 内存(Main memory,也称为存储器):以字节编码的连续存储空间,存放程序代码(object code)、数据(program data)、运行栈(stack)以及操作系统数据(OS data)
- 寄存器和内存的比较:PPT p6
数据格式
- 字,双字,四字。1字 = 2字节
- 整型数据
- C语言数据类型在x86-64中的大小:书本 p119
- 数据传送指令:b(字节),w(字),l(双字),q(四字)
访问信息
- 整数寄存器信息:书本 p120
操作数指示符
- 操作数:书本 p121,PPT p17)
- 立即数:用来表示常数值,书写方式是 $ 后面跟一个用标准C表示法表示的整数
- 寄存器:表示某个寄存器的内容
- 内存引用
- 操作数格式:书本 p121
数据传送指令
- MOV类:把数据从源位置复制到目的位置
- 源操作数:立即数,存储在寄存器中或者内存中
- 目的操作数:指定一个位置,要么是一个寄存器,要么是一个内存地址
- 不能两个操作数都为内存数据!PPT p18
- 扩展数据传送指令:书本 p123
- 当参数少于7个时,参数从左到右放入寄存器:rdi, rsi, rdx, rcx, r8, r9。当参数为7个及以上时,前6个传送方式不变,但后面的依次从“右向左”放入栈中
压入和弹出栈数据
- pushq %rbp:
- subq $8, %rsp
- movq %rbp, (%rsp)
- popq %rax:
- movq (%rsp), %rax
- addq $8, %rsp
- 书本p128
算术和逻辑操作
- 加载有效地址 leaq 将有效地址写入目的操作数
- lea [%ebx+8], %eax 就是将 %ebx+8 这个值直接赋给 %eax,而不是把 %ebx+8 处的内存地址里的数据赋给 %eax。
- mov [%ebx+8], %eax 则是把内存地址为 %ebx+8 处的数据赋给 %eax。
- 整数计算指令:PPT p31,书本 p130
控制
PPT 3.2
- 控制流:PPT p4
- 条件码:PPT p5
- 临时数据 ( %rax, … )
- 栈顶地址 ( %rsp )
- 当前指令地址(下一条) ( %rip, … )
- 条件码 ( CF, ZF, SF, OF )
条件码
- CF:进位标志 —— 可用于检测无符号整数运算的溢出
- ZF:零标志 —— ZF set if t == 0
- SF:符号标志 —— SF set if t < 0
- OF:溢出标志 —— 补码运算溢出(即带符号整数运算)
- 书本 p135,PPT p6
- 比较指令 CMP,测试指令 TEST:PPT p7
访问条件码
- SET 指令:书本 p136,PPT p11
跳转指令
- 书本 p139,PPT p12
条件转移指令
- 书本 p146,PPT p16
循环
- 书本 p149,PPT p23
switch语句
- 跳转表
- 书本 p159,PPT p32
过程
PPT 3.1,书本 p164
运行时栈
- 栈帧:当前正在执行的过程的帧总是在栈顶,当过程P调用过程Q时,会把返回地址压入栈中
转移控制
- call Q:调用过程Q,把地址A压入栈中,并将PC设置为Q的起始地址,压入的地址A被称为返回地址,是紧跟在call指令后面的那条指令的地址。对应的指令ret会从栈中弹出地址A,并把PC设置为A
数据传送
- 不超过6个整型(整数和指针)参数:寄存器,有特殊顺序 - 书本 p168
- 超过部分:用栈传递,参数7位于栈顶,所有数据大小都向8的倍数对齐
栈上的局部储存
- 局部数据必须存放在内存中的情况:
- 寄存器不足够存放所有的本地数据
- 对一个局部变量使用地址运算符 ‘&’
- 某些局部变量是数组或结构
- 例子:书本 p171
寄存器中的局部储存空间
- 被调用者保存寄存器:%rbx,%rbp,%r12 ~ %r15
- 调用者保存寄存器:其他所有的寄存器,除了%rsp
数组分配和访问
PPT 5,书本 p176
一维数组
- T A[N] :A 为指向数组开头的指针,数组元素 i 会被存放在地址为 的地方。
指针运算
- 计算出来的值会根据该指针引用的数据类型的大小进行伸缩
- 某一个对象 Expr,地址指针是 &Expr;某一个地址 AExpr,它的值是 *AExpr
二维数组
- 在内存中按照“行优先”的顺序排列
异质的数据结构
书本 p183
结构
- 数据存储位置对齐,PPT p27
- 结构起始地址的对齐要求等同于该结构各个元素中对齐要求最高的那个
- 结构中元素的对齐要求必须满足该元素自身的对齐要求
- 结构的长度必须是该结构各个元素中对齐要求最高的那个元素长度的整数倍
联合
- union中可以定义多个成员,union的大小由最大的成员的大小决定
- union成员共享同一块大小的内存,一次只能使用其中的一个成员
链接
书本 p464,PPT 6.1
编译器驱动程序
- C预处理器(cpp):将C的源程序main.c翻译成一个ASCII码的中间文件main.i
- C编译器(ccl):将main.i翻译成一个ASCII汇编语言文件main.s
- 汇编器(as):将main.s翻译成一个可重定位目标文件main.o
- 链接器(ld):将main.o和sum.o以及一些必要的系统目标文件组合起来,创造一个可执行目标文件prog
- shell调用操作系统中一个叫做加载器的函数,将可执行文件prog中的代码和数据复制到内存,然后将控制转移到这个程序的开头
静态链接
- 符号解析:目标文件定义和引用符号,每个符号对应于一个函数、一个全局变量或一个静态变量(static),目的是将每个符号引用和符号定义关联起来
- 重定位:链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得它们指向这个内存位置
- 书本 p466,PPT p8
目标文件
- 可重定位目标文件
- 可执行目标文件
- 共享目标文件
- PPT p10
ELF文件
- ELF:可执行可链接格式
- 书本 p467,PPT p12
符号和符号表
- 全局符号
- 外部符号
- 局部符号
- 书本 p468,PPT p14
符号解析
- 如何解析
- 强符号:函数和已初始化的全局变量
- 弱符号:未初始化的全局变量
- 规则:
- 不允许有多个同名的强符号
- 强弱同名:选择强符号
- 多个弱符号同名:任意选择
- 与静态库链接
- 静态库文件(.a 文件)
- 将多个相关的重定位对象文件集成为一个单一的带索引的文件(称为归档文件,archive file)
- 增强链接器的功能使之能够在归档文件中解析外部符号
- 如果归档文件中的某个成员解析了外部符号,就将其链接入执行文件
- 共享库文件
内存
内存布局
PPT 6.2 p4
缓冲区溢出
PPT p8,书本 p194
7 虚存
书本 p560
物理和虚拟寻址
- 物理地址(Physical Address,PA)
- 物理寻址
- 虚拟地址(VA)
- 虚拟寻址,地址翻译,内存管理单元(MMU)
- 地址空间
- 线性地址空间
- 物理地址空间
虚拟内存
- 虚拟内存:一个由存放在磁盘上的N个连续的字节大小的单元组成的数组。
- 虚拟页(VP),物理页/页帧(PP)
- 虚拟页面的集合
- 未分配的
- 缓存的
- 未缓存的
- 页表
- 页表条目(PTE):有效位 + 地址字段
- 页命中(Page Hit)
- 页缺失(Page Fault):DRAM 缓存不命中
- 处理页缺失:选择一个牺牲页,取代,指令重新执行
- 内存保护:在PTE上添加许可位(SUP, READ, WRITE)
地址翻译
- 地址翻译符号:书本 p568
- 页表基址寄存器(PTBR)指向当前页表
- n 位虚拟地址:p 位虚拟页面偏移(VPO)+ (n-p) 位虚拟页号(VPN),VPN k 选择 PTE k,将页表条目中的物理页号(PPN)和虚拟地址中的 VPO 串联起来,得到相应的物理地址
- TLB(翻译后备缓冲器,快表)
- TLB 标记(TLBT)
- TLB 索引(TLBI)
- 翻译方法:PPT p30
内存映射
- 虚拟内存区域可以映射到这两种中的一种:
- 普通文件,文件区被分成页大小的片
- 匿名文件
- 交换文件,交换空间
8 内存分配
书本 p587
- 动态内存分配器:维护一个进程的虚拟内存区域,称为“堆”,将堆视为一组大小不同的块的集合,每个块是一个连续的虚拟内存片(已分配/空闲)。
- 显式分配器:malloc,free
- 隐式分配器/垃圾收集器
malloc 和 free 函数
- malloc 函数:返回一个指针,指向大小为至少 size 字节的块,会为可能包含在这个块内的任何数据对象类型做对齐。32位模式中,返回地址是8的倍数;64位模式中,地址是16的倍数。
- 遇到问题:返回 NULL,设置 errno。
- 想要已初始化的动态内存:使用 calloc。
- 想改变一个以前已分配块的大小:使用 realloc。
- free 函数:释放已分配的堆块,参数必须指向一个从 malloc、calloc 或者 realloc 获得的已分配块的起始位置。
- PPT p7,书本 p589
分配器的要求和目标
- 约束条件:
- 处理任意请求序列
- 立即响应请求
- 只使用堆
- 对齐块
- 不修改已分配的块
- 目标:
- 最大化吞吐率
- 吞吐率:每个单位时间里完成的请求数
- 最大化内存利用率
- 峰值利用率:书本 p591
- 最大化吞吐率
碎片
- 内部碎片:一个已分配块比有效载荷大
- 外部碎片:空闲内存合计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大可以来处理这个请求
隐式空闲链表
- 书本 p592
放置已分配的块
- 首次适配
- 下一次适配
- 最佳适配
分割 & 合并空闲块
带边界标记的合并
- 脚部
9 异常控制流(ECF)
书本 p501,PPT 9.1
异常
- 异常:控制流中的突变,用来响应处理器状态中的某些变化
异常处理
- 异常号,异常表
异常的类别
书本 p504
- 异步异常
- 中断(Interrupt):由外部事件引起,往往是外部触发
- 同步一场
- 陷阱(Trap):有意的异常,是执行一条指令的结果
- 故障(Fault):由错误情况引起,程序“无意”引起的,且可能可以恢复正常执行
- 终止(Abort):不可恢复的致命错误造成的结果,通常是一些硬件错误
进程
- 进程:一个执行中程序的实例
逻辑控制流
- 轮流使用处理器
并发流
- 一个逻辑流的执行在时间上与另一个流重叠,称为并发流
- 并发,多任务/时间分片
- 并行流
上下文切换
- 调度,调度器
进程控制
获取进程 ID
- getpid:返回 PID
pid_t getpid(void)
- getppid:返回父进程 PID
pid_t getppid(void)
创建和终止进程
- exit:以status退出状态来终止进程
void exit(int status)
- atexit:注册退出时要运行的函数 PPT p35
- fork:父进程通过调用 fork 函数来创建一个新的运行的子进程
pid_t fork(void)
- 调用一次,返回两次:在父进程中,返回子进程的 PID;在子进程中,返回0
- 进程的先后顺序不定,进程图中所有顶点的拓扑排序表示程序中语句的一个可行的全局排列
- 相同但是独立的地址空间
- 共享文件
回收子进程
- 僵死进程
- waitpid:等待子进程终止或者停止
pid_t waitpid(pid_t pid, int *statusup, int options)
- 书本 p516
- wait:挂起当前进程直到它的一个子进程终止,返回值是终止的子进程的 pid;如果没有子进程,则直接返回-1
加载并运行程序
- execve:在当前进程的上下文中加载并运行一个新程序
int execve(const char *filename, const char *argv[], const char *envp[])
Shell
PPT 9.2
信号
书本 p526,PPT p10
- 信号列表:书本 p527
发送信号
- 进程组:由正整数进程组 ID 来标示
- 用 /bin/kill 程序发送信号:一个负的 PID 会导致信号被发送到进程组 PID 中的每个进程
- 从键盘发送信号:作业,Ctrl+C 终止,Ctrl+Z 挂起
- 用 kill 函数发送信号
- 用 alarm 函数发送信号
接收信号
- 默认行为
- 进程终止
- 进程终止并转储内存
- 进程挂起直到被 SIGCONT 信号重启
- 进程忽略该信号
- signal 函数
sighandler_t signal(int signum, sighandler_t handler)
,书本 p531- 当一个进程捕获了一个类型为 k 的信号时,会调用为信号 k 设置的处理程序,一个整数参数被设置为 k。这个参数允许同一个处理函数捕获不同类型的信号。
等待和阻塞信号
- 如果发送了信号,但尚未接收到,则信号处于 pending 状态
- 任何特定类型的pending信号最多只能有一个
- 如果一个进程有一个类型为 k 的pending信号,那么随后发送给该进程的类型为 k 的信号将被丢弃
- 进程可以阻塞某些信号的接收
- 一个被目标进程阻塞的信号可以被递送,但是在信号被解除阻塞之前不会被接收
编写信号处理程序
- 异步信号安全的函数:书本 p534
非本地跳转
- 非本地跳转:用户级异常控制流形式,将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用-返回序列
- setjmp函数:书本 p547
int setjmp(jmp_buf env)
- 在 env 缓冲区中保存当前调用环境,并返回0;longjmp 调用之后 setjmp 再次返回,并带有非零的返回值 retval
- 必须在 longjmp 之前调用,标示 longjmp 的返回地点
- 调用一次,返回一次或多次实现
- longjmp函数
void longjmp(jmp_buf env, int retval)
- 从 env 缓冲区中恢复调用环境,触发一个
- 调用一次,从不返回
- 只能长跳到已经被调用但尚未完成的功能环境
10 IO 处理
书本 p622
Unix I/O
- Linux 文件:一个 m 个字节的序列,所有的 I.O 设备都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写
- 普通文件:包含任意数据,区分文本文件和二进制文件
- 目录:包含一组链接的文件
- 套接字:用来与另一个进程进行跨网络通信的文件
打开和关闭文件
- open:书本 p624
int open(char *filename, int flags, mode_t mode)
- flags:只读/只写/可读可写/……
- mode:访问权限位,书本 p625
- close
int close(int fd)
读和写文件
- read
ssize_t read(int fd, void *buf, size_t n)
- 从描述符为 fd 的当前文件位置复制最多 n 个字节到内存位置 buf
- 返回值 -1 表示一个错误,0 表示 EOF,否则表示的是实际传送的字节数量
- write
ssize_t write(int fd, const void *buf, size_t n)
- 从内存位置 buf复制最多 n 个字节到描述符为 fd 的当前文件位置
- 不足值出现的原因
- 读时遇到 EOF
- 从终端读文本行
- 读和写网络套接字
元数据
- 元数据:关于文件的数据,书本 p632
- stat
int stat(const char *filename, struct stat *buf)
- 以一个文件名作为输入,填写一个 stat 数据结构中的各个成员
- fstat
int fstat(int fd, struct stat *buf)
- 以文件描述符作为输入
读取目录内容
- opendir
DIR *opendir(const char *name)
- 以路径名为参数,返回指向目录流的指针
- readdir
struct dirent *readdir(DIR *dirp)
- 返回指向流 dirp 中下一个目录项的指针
- 出错时:返回 NULL,设置errno
- closedir
int closedir(DIR *dirp)
- 关闭流并释放其所有的资源
共享文件
- 内核用三个相关的数据结构来表示打开的文件:
- 描述符表:指向文件表中的一个表项
- 文件表:打开文件的集合,表项组成包括当前的文件位置,引用计数,以及一个指向 v-node 表中对应表项的指针
- v-node 表:每个表项包含 stat 结构中的大多数信息
- 书本 p634,PPT p26
I/O 重定向
- dup2
int dup2(int oldfd, int newfd)
- 复制描述符表表项 oldfd 到描述符表表项 newfd,覆盖描述符表表项 new-fd 以前的内容
标准 I/O
- PPT p38,书本 p638
11 线程与线程同步基础
书本 p691
- 线程:运行在进程上下文中的逻辑流
- 进程上下文:包括唯一的整数线程 ID (TID)、栈、栈指针、程序计数器、通用目的寄存器和条件码
线程执行模型
- 主线程,对等线程
- 一个线程可以杀死它的任何对等线程,或者等待它的任意对等线程终止
Posix 线程
创建线程
- pthread_create
int pthread_create(pthread_t *tid, pthread_attr_t *attr, func *f, void *arg)
- 创建一个新的线程,并带着一个输入变量 arg,在新的线程的上下文中运行线程例程 f,能用 attr 参数来改变创建线程的默认属性
- 返回时,参数 tid 包含新创建线程的 ID
终止线程
- 终止方式:
- 顶层的线程例程返回时,线程会隐式地终止
- 调用 pthread_exit 函数:显式地终止
- 主线程调用:等待其他所有线程终止,然后再终止主线程和整个进程
void pthread_exit(void *thread_return)
- 返回值为 thread_return
- 某个对等线程调用 Linux 的 exit 函数,该函数终止进程以及所有与该进程相关的线程
- 另一个对等线程通过以当前进程 ID 作为参数调用 pthread_cancel 函数来终止当前线程
int pthread_cancel(pthread_t tid)
回收已终止现成的资源
- pthread_join
int pthread_join(pthread_t tid, void **thread_return)
- 函数会阻塞直到线程 tid 终止,将线程例程返回的通用 (void*) 指针赋为 thread_return 指向的位置,然后回收已终止线程占用的所有内存资源
- 只能等待一个指定的线程终止
共享
线程内存模型
- 线程上下文:线程 ID、栈、栈指针、程序计数器、条件码、通用目的寄存器值
- 线程之间共享进程上下文的剩余部分,包括整个用户虚拟地址空间
- 寄存器是从不共享的,虚拟内存总是共享的
将变量映射到内存
- 全局变量:虚拟内存的读/写区域只包含每个全局变量的一个实例,任何线程都可以引用
- 本地自动变量:每个线程的栈都包含它自己的本地自动变量的实例
- 本地静态变量:虚拟内存的读/写区域只包含每个本地静态变量的一个实例,任何线程都可以引用
- 一个变量 x 被共享,当且仅当多个线程引用 x 的(至少)一个实例。
互斥
书本 p698
- 线程 i 的循环代码分解:
- :在循环头部的指令块
- :加载共享变量 cnt 到累加寄存器 的指令
- :更新(增加) 的指令
- :将 的更新值存回到共享变量 cnt 的指令
- :循环尾部的指令块
- 头和尾只操作本地栈变量,而中间操作共享计数器变量的内容
进度图
- 临界区,不安全区,安全轨迹线,不安全轨迹线
信号量
- 信号量 s:具有非负整数值的全局变量,只能由两种特殊的操作来处理,书本 p702,PPT p31
- P(s):
while (s == 0) wait (); s--;
- V(s):
s++;
- 信号量不变性
- P(s):
- 实现互斥:将每个共享变量与一个信号量 s(初始为 1)联系起来,然后用 P(s) 和 V(s) 操作将相应的临界区包围起来
- 二元信号量,互斥锁,加锁,计数信号量
12 80X86-64 汇编编程
建议看 PPT
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~