编译系统
hello.c 程序的编译过程分为4个阶段。
- 预处理阶段。
预处理器(cpp)根据以字符 # 开头的命令修改源文件。
比如遇到#include
- 编译阶段
编译器(ccl) 将文本文件 hello.i 翻译成文本文件 hello.s 的汇编程序。
- 汇编阶段
汇编器 as 将 hello.s 翻译成机器语言指令,将结果保存在目标文件中。
-
链接阶段
-
将程序中要到的函数合并到程序文件中。
操作系统有两个基本功能
- 防止硬件被失控的应用程序滥用。
- 向应用程序提供简单一致的机制来控制复杂而又各不相同的硬件设备。
操作系统通过几个基本的概念来实现这两个功能。
- 文件 是对 I/O 设备的抽象表示。
- 虚拟内存 是对主存和磁盘I/O设备的抽象表示。
-
进程 则是对 处理器、主存和I/O设备的抽象表示。
-
进程 是操作系统对一个正在运行的程序的抽象。
- CPU 每个核都有寄存器和L1、L2高速缓存。L1分为数据和指令,L2是统一的。
- L3统一高速缓存为所有核共享。
超线程
- 是一项允许一个CPU执行多个控制流的技术,即单个物理内核同时执行多个线程。
- 它涉及CPU某些硬件有多个备份,比如程序计数器和寄存器文件。而其他的硬件部分只有一份,比如:执行浮点算术运算的单元。
- 常规的处理器大约需要 2 万个时钟周期做不同的的线程切换。
- 超线程的处理器可以在单个时钟周期的基础上决定要执行哪一个线程。
- 这使得CPU能够更好的利用它的处理资源。
指令集并行
- 一条指令的执行需要很多个时钟周期,但是CPU采用的聪明的流水线方式执行指令,可以达到1个时钟周期执行一条指令。
- 如果CPU可以达到比一周期一条指令更快的速度,称为超标量。
SRAM 和 DRAM
- 锁存器(Latch)是一种对脉冲电平敏感的存储单元电路,它们可以在特定输入脉冲电平作用下改变状态。
- SRAM 不需要充电刷新,用于CPU中的高速缓存cache。
- DRAM 数据保存在电容中,需要不停的充电刷新,刷新频率一般为4 ~ 64 毫秒。用于内存条。
X86_64 寄存器
- 16个整数寄存器。
- ymm0 ~ ymm15 一共16个256位浮点型寄存器
- xmm0 ~ xmm15 上面寄存器的低128位。
虚拟内存
- 操作系统为每一个进程提供一个独立的页表,将虚拟地址映射为物理地址。
- 多个虚拟页面可以同时映射到同一个共享物理页面上。
-
内核虚拟内存的某些区域被映射到所有进程共享的物理页面。
- Linux同时也将一组连续的大小等于DRAM总量的虚拟页面 映射到相应的一组连续的物理页面。这样可以方便内核访问任意的物理内存位置。有一些设备也映射到特定物理内存位置,这样可以直接通过操作内存来操作特定设备。
-
每个进程的地址空间中内核部分除了含有相同的内核代码和内核数据结构。还有一部分数据是每个进程都不同的。这部分保存了当前进程的页表,进程上下文,以及记录虚拟地址空间当前组织的各种数据结构。
-
虚拟地址空间中有大量间隙,这些间隙不占内存,磁盘或者内核本身中任何额外资源。
- Linux为每个进程维护一个任务结构task_struct ,其中的mmp为一个vm_are_struct 的链表。链表中的每个元素记录了虚拟地址空间中各个段(例如:栈,共享库,数据段,代码段)的起始地址,权限标记,以及是否共享等信息。
当MMU在试图翻译某个虚拟地址时触发了一个缺页,这个异常会导致控制转移到内核的缺页处理程序,随后执行如下步骤:
-
判断虚拟地址A是不是在某个段的区间内,如果没有直接触发segmentation fault 终止程序。
-
如果存在,判断访问是否合法,例如是不是对只读内存区域执行了写操作,如果是会触发一个保护异常。
-
fork 函数,写时复制
-
当fork函数调用时,内核为新进程创建各种数据结构,分配pid,以及mm_struct,区域结构和页表的原始样本,同时将两个进程的所有区块都标记为只读。两个进程共享所有数据,当一个进程试图写私有只读数据时,写时复制机制才会创建新页面。
-
execve 用来运行新程序
-
mmap 用来映射文件到内存。
- free(void *ptr) 传入的指针是之前通过malloc ,calloc 或 realloc 分配的。应该是分配时记录了分配的长度。
- malloc 函数分配的内存地址在64位系统上以16字节对齐,32系统8字节对齐。
malloc 不初始化内存。calloc会初始化内存为0。realloc可以改变之前分配块的大小。
Unix I/O
- Linux shell创建的每个进程开始时都有三个打开的文件描述符,标准输入0,标准输出1,标准错误2。
- 中断就是一个硬件或软件请求,要求CPU暂停当前的工作,去处理更重要的事情
I/O 多路复用 ,使用select函数,要求内核挂起线程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。
-
线程由内核自动调度,每个线程都有自己的线程上线文,包括唯一的整数线程ID,栈,栈指针,程序计数器,通用目的寄存器和条件码。
-
pthread_once_t 。pthread_once 作用和dispatch_once_t 一样。