0. 开始【《链接、装载与库》学习笔记】
系统软件
系统软件可以初步分成两块:平台性的操作系统内核、驱动程序、运行库等;用于程序开发的编译器、汇编器、链接器等。
每个层次需要相互通信,就需要定义一个通信的协议,一般称为接口(interface)。从层次结构上看,开发工具与应用程序属于同一层次,因为他它们都使用操作系统API。Linux下Glibc库提供POSIX的API,Windows运行库提供Windows API。
运行库使用操作系统的系统调用接口,在实现中往往以软件中断的方式提供。
操作系统
程序每运行一段时间都让出的程序协作模式叫分时系统。
现在操作系统一般采用多任务系统,所有的应用程序都以进程的方式运行在比操作系统更低的级别。CPU使用抢占式。
文件系统保存了文件的储存结构,负责维护这些数据结构、保证磁盘中的扇区能够有效组织和利用。
内存
我们为了高效利用内存空间,把程序给出的地址看作是一种虚拟地址,然后通过某些映射的方法,把虚拟地址转换成实际的物理地址。
地址空间
地址空间可以想象成一个很大的数组,数组大小由地址空间的地址长度决定。比如32位地址空间大小为 。
地址空间分为虚拟地址空间和物理地址空间。
分段
分段的基本思路是把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间。空间的映射通过操作系统来设置。
分页
分页的基本方法是把地址空间人为等分成固定大小的页,每一页的大小由硬件决定,或者硬件支持多种大小的页,由操作系统决定页的大小。我们把物理内存中的页叫物理页(PP),把磁盘中的页叫磁盘页(DP)。
当进程需要用到不在内存中的页是,硬件会捕获信息,也就是页错误,然后操作系统接管进程,把页从磁盘中读出来装入内存,并建立映射关系。
虚拟内存与物理内存之间的映射一般使用部件MMU(Memory Management Unit)来处理。
线程
线程,有时被称为轻量级进程(Lightweight Process, LWP),是程序执行流的最小单元。由线程id,当前指令指针(PC),寄存器集合和堆栈组成。
线程的私有储存空间包括:
- 栈
- 线程局部存储(Thread Local Storage, TLS)
- 寄存器
线程通常有至少三种状态分别是运行、就绪和等待。处于运行中线程拥有的可以执行的一端时间被成为时间片。
频繁等待的线程被成为IO密集型线程,很少等待的被成为CPU密集型线程。
与windows相比,linux内核中并不存在真正意义上的线程概念。Linux把所有的进程/线程都称为Task。可以用以下命令调用:
| 系统调用 | 作用 |
|---|---|
| fork | 复制当前进程 |
| exec | 使用新的可执行映像覆盖当前可执行映像 |
| clone | 创建子进程并从指定位置执行 |
| fork产生新任务很快,因为它与原任务一起共享一个写时复制(copy on write, COW)的内存空间。 |
线程安全
单指令的操作被称为原子的,windows有一套原子操作API,被称为Interlocked API.
同步与锁
线程在访问数据之前先试图获取(Acquire)锁,在访问结束之后释放锁。
- 二元信号量(Binary Semaphore)有两种状态:占用与非占用。
- 互斥量(Mutex)要求哪个线程获取互斥量,哪个线程就负责释放。
- 临界区(Critical Section)作用仅限于本进程。
- 读写锁(Read-Write Lock)有两种获取方式:共享的和独占的。
- 条件变量可以让许多线程一起等待某事件发生。
一个可重入的用函数可以在多线程环境下放心使用。
CPU的barrier指令可以防止CPU换序导致的内存安全问题。
线程模型
- 一对一模型:一个内核控制一个线程
- 多对一模型:多个用户线程映射到一个内核线程
- 多对多模型:将多个用户线程映射到少数但不止一个内核线程。