目录
-
分段分页
-
线程相关
分段分页
进程地址直接映射内存物理地址存在的问题(MMU)
- 进程之间的地址空间不隔离
- 内存的使用效率低
- 程序运行地址不稳定, 第二次加载与第一次加载地址不一致
分段:程序整体运行的虚拟地址映射到内存
- 解决了 1 3 问题
分页: 分成大小固定的页, 一般为4k
- 解决了2 问题
线程相关
线程的访问权限
线程私有: 局部对象, 函数参数, TLS
线程之间共享(进程所有):全局变量, 堆上的数据, 函数里的静态变量, 程序代码, 打开的文件(内核对象)
线程优先级
- 用户指定优先级
- 根据进入等待状态的频繁程度提升或者降低优先级
- 长时间得不到执行被提升优先级
可抢占和不可抢占
抢占:线程在用尽时间片之后会被强制剥夺继续执行的权利,进入就绪状态
非抢占: 线程主动放弃时间片, 线程试图等待某个事件(IO)
线程安全 同步 锁
-
锁
-
二原信号量
-
信号量, 可以标记多个资源
线程访问资源首先获取信号量 将信号量减1, 如果信号量的值小于0, 进入等待状态,否则继续执行, 访问完资源后, 线程释放信号量--->将信号量值加1,如果信号量小于1, 唤醒一个等待中的线程
-
mutex
-
临界区
-
读写锁
-
条件变量:线程可以等待和唤醒一个条件变量
mutex和二元信号量区别:
mutex:哪个线程加锁,哪个线程解锁;
二元信号量:系统中的任何一个线程都可以加解锁
临界区和互斥量与信号量区别:
信号量和MUtex是内核变量,一个进程创建了互斥量和信号量,另一个进程也可以使用
临界区作用仅仅限制在本进程
可重入和线程安全
- 不使用任何静态或者全局非const变量
- 不返回任何静态或者全局非const变量的指针
- 仅依赖于调用方提供的参数
- 不依赖于任何单个资源的锁()mutex等)
- 不调用任何不可重入的函数
volilate和barrier
volatile T* pInst = 0;
T* GetInstance()
{
if (pInst == NULL)
{
lock();
if(pInst == NULL)
{/* 不安全版本
pInst = new T;
*/
T *tmp = new T;
barrier()
pInst = tmp;
}
unlock();
}
return pInst;
}
- 这段代码的问题在于cup的乱序执行
- C++new有两个操作
- 分配内存
- 调用构造函数初始化
- 所以pInst = new T 有三个操作
- 分配内存
- 在内存位置上调用构造函数
- 将内存地址赋值给pInst
- 因为2 3 位置可以颠倒,所以多线程并发不安全
- volatile 可以做到两件事
- 阻止编译器为了提高速度将一个变量缓存到寄存器而不写回
- 阻止编译器调整操作volatile变量的指令顺序
- barrier指令会组织CPU将该指令之前的指令交换到barrier之后
综上:volatile防止编译器调整顺序,barrier防止运行时优化也就是CPU动态调度换序