第一章 温故而知新
计算机软件体系结构
- 开发工具与应用程序使用操作系统应用程序编程接口;应用程序接口的提供者是运行库;运行库使用操作系统提供的系统调用接口;系统调用接口在实现中往往以软件中断的方式提供。
cpu
- 分时系统:每个程序运行一段时间后都主动让出CPU,使得一段时间内每个程序都有机会运行一小段时间。
内存
程序直接使用物理地址的缺点:
- 地址空间不隔离:所有程序能直接访问物理地址,如果一不小心修改了其他程序的数据,就会使其他程序也崩溃。
- 内存使用效率低:大量数据换入换出,导致效率十分低下。
- 程序运行的地址不确定:程序每次运行装入时,都需要从内存中分配足够大的空闲区域,这个空闲区域的位置是不固定的。
空间隔离:每个进程都有自己的独立的虚拟空间,而且每个进程只能访问自己的地址空间。
- 分段:把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间。
- 分页:把地址空间人为地等分为固定大小的页
- 共享内存:把虚拟空间的有些页映射到同一个物理页
线程
- 一般把频繁等待的线程称之为IO密集型线程;把很少等待的线程称之为CPU密集型线程。
- IO密集型线程总是比CPU密集型线程容易得到优先级的提升。
- 饿死现象:线程优先级比较低,总是有较高的优先级的线程要执行,因此这个优先级线程始终无法执行。
- 改变线程优先级的方法:
- 用户指定优先级
- 根据进入等待状态的频繁程度提升或降低优先级
- 长时间得不到执行而被提升优先级
Linux多线程
- 写时复制:两个任务可以同时只有读取内存,但任意一个任务试图对内存进行修改时,内存就会复制一份提供给修改方单独使用,以免影响到其他人物的使用。
- fork产生新任务的速度非常快,因为它并不复制原任务的地址空间,而是和原任务一起共享一个写时复制的内存空间。
线程安全
- 自增(++)操作在多线程环境下会出现错误是因为这个操作被编译为汇编代码之后不止一条指令,在执行的时候可能执行了一半就被调度系统打断。
- 自增(++)操作实现:
- 读取i到某个寄存器x;
- x++;
- 将x的内容存储回i;
- 同步:在一个县城访问数据未结束时,其他线程不得对同一个数据进行访问。
- 函数重入:函数没有执行完成,由于外部因素或者内部调用,又一次进入该函数执行。
- 函数被重入有两种情况:
- 多个线程同时执行该函数
- 函数自己调用自己
- 可重入函数的要求:
- 不使用任何(局部)静态或全局的非const变量;
- 不返回任何(局部)静态或全局的非const变量的指针;
- 仅依赖于调用方提供的参数;
- 不依赖任何单个资源的锁;
- 不调用任何不可重入的函数;
- volatile关键字:
- 编译器有时会过度优化导致程序错误,volatile关键字试图阻止过度优化。
- volatile作用:
- 阻止编译器为了提高速度将一个变量缓存到寄存器内而不写回。
- 阻止编译器调整操作volatile变量的指令顺序。
- volatile即使能够阻止编译器调整顺序,也无法阻止cpu动态调度换序。