一、线程概念
什么是线程
进程:一个运行起来的执行流,一个加载到内存中的程序 (教材)。进程 = 内核数据结构 + 自己的代码和数据。 线程:进程内部的一个执行流,轻量化。 观点:进程是系统分配资源的基本单位 (内核角度,给进程下的定义), 线程是 CPU 调度的基本单位。
第四次谈地址空间
物理内存结构——page
- 页表的每一个虚拟到物理的映射条目叫做页表项。
- 访问内存的基本单位是字节,但是页表并不是以字节为单位映射的。因为地址空间一共有 4GB 空间 = 4 * 1024 * 1024 * 1024 字节,假设一个页表项占 8 个字节(32 位下虚拟地址 4 字节,物理地址 4 字节),按字节映射的话每一个字节的虚拟地址都需要一个 8 字节的页表项来存储映射关系,那么页表一共就会占 8 * 4 * 1024 * 1024 * 1024 字节 = 32GB 空间,远超 4GB 的物理内存空间大小,很明显不合理。
- 我们在讲文件系统的时候讲过,磁盘存储是以 4kb 为单位,实际上物理内存也从逻辑层面上被划分成了一个个 4kb 大小的小内存,内存与磁盘之间的 IO 就叫做文件系统 IO,且 IO 的基本单位大小是 4kb。
- 物理内存中的每一个 4kb 空间被叫做页框或者页帧。
- 当每出现一个概念时,OS 都需要对其管理,页框也是同理,在内核中有一个 struct page 结构体用来描述页框,结构体当作有一个位图标志位字段用于描述页框的状态,还会有一个整型变量表示当前内存页的引用计数等等,struct page 结构体比较小,因为 4GB 内存会存在 4GB / 4KB = 1048576 个 struct page 结构体,所以单个 struct page 不能占据太多空间。
- 将页框进行描述后还需要对其进行管理,OS 是用一个结构体数组:struct page pages[1048576] 对其进行管理的,那么未来 OS 对物理内存的管理就转化成为了对 pages 数组的增删查改。
- 有了上面的知识,我们就认识到了一个颠覆认知的知识:所有物理地址,都可以通过数组下标转化而来:数组下标 0 对应物理内存第一个 4kb 内存块,数组下标 1 对应物理内存第二个 4kb 内存块…这样一来物理地址这个概念就被弱化了,所以在内核中我们经常会看到虚拟内存地址,但很少看到物理内存地址,因为物理地址是拿着数组起始地址和 page 元素下标进行计算、转化得来的。
- 进程申请物理内存本质就是在 pages 数组中申请一个 page 结构体,然后 OS 拿到数组的下标,并结合 pages 数组起始地址就能找到该物理内存块的地址了,转化关系是:页框起始地址 = pages 数组下标 * 4KB。
虚拟/物理内存转化
32 位系统下 PC 指针(程序计数器)的具体实现是 EIP 寄存器,32 位系统下页表不是一个整体,而是采用二级页表结构,下面是利用页表进行虚拟到物理内存转化的示意图(一个页表对应一个 4KB 页框空间):
当我们拿着虚拟地址要找物理地址时,首先要查页表,找到要访问的物理地址在哪一个页框,然后加上页内偏移量就能找到具体物理地址了,下面是详细过程(整个查询过程由硬件 MMU 完成,比用软件效率更高): 首先用虚拟地址的高 10 位查页目录表,2^10 正好对应页目录表中 1024 个页表起始地址。利用页目录表索引到具体哪个页表后,然后用虚拟地址的次高 10 位查该页表,查找页表后就得到了待找页框的起始物理地址。最后用虚拟地址的低 12 位正好就能索引整个页框 4kb 空间的所有字节,这里我们称为页内偏移,由此就能访问页内的任意一个字节了。
这样映射的话一个进程的页表所占空间大小:页目录表(4kb)+ 1024 个页表(1024 * 4kb)约等于 4MB,并且一个进程并不会把整个物理内存映射完,实际大小只会小于 4MB,远小于按字节映射的 32GB 大小。
下面是一些细节补充:
- CR3 保存当前进程页表的基地址,该地址是物理地址。
- 虚拟地址 32 个比特位被划分为 10/10/12 三部分编译器不参与。
- 虚拟地址高 20 位相同的地址,映射后一定存放在同一个页框中。
- 除了能把虚拟地址转化为物理地址,也能把物理地址转化为虚拟地址。
- 但进程首次加载磁盘块时,OS 会做以下事情:先内存管理申请内存(在 pages 数组中申请 page 结构体)-> 拿到数组下标 -> 计算出页框的起始物理地址 -> 把页框的起始物理地址填充到页表中 -> ELF 拿 MMU 进行虚实转换。
- 除了访问单字节变量外,我们还可能访问 int 数组 结构体等等变量,这些变量大小不止一个字节,但是所有变量都只有一个地址——开辟空间的最小字节的地址,所以页表在进行转化的时候,虽然只能拿到一个字节的地址,实际是参考变量具体类型,利用起始地址 + 偏移量的方式进行转换。
- 有了上面的认识,我们就能认识到 OS 申请和管理物理内存都是以 4KB 为单位的,所以写时拷贝和缺页中断并不是只拷贝访问的一个变量、缺页中断也不是只换入一个变量,它们俩都是以 4KB 为单位进行操作的。
- 既然 OS 申请和管理物理内存都是以 4KB 为单位的,为什么我们可以用 new、malloc 申请 1、4、n 字节空间呢?这是因为 c/c++ 在语言层面有自己的内存管理机制,语言已经为你提前向系统申请好了一些空间,并用链表组织管理起来,你需要多少就给你多少,类似于 STL 的空间配置器。


