第一章
印象:
硬件PCI/ISA的架构
North Bridge相当于人的心脏,连接所有高速设备,CPU ->大脑
南桥芯片则负责低速设备连接
SMP
中间层 是解决很多问题的大方向
Any problem in computer science can be resolved by another layer of indirection
CPU密集型 IO密集型
这两种类型的Process,理论上优先级高的,也就是说最应该先得到CPU的是IO密集型
通俗的理解应该是IO密集型做完事情花的CPU时间最少,然后就会等待IO设备的反应,这样可以让设备性能最大化
Memory
分段分页 MMU
线程安全和线程模型
其中线程安全有两件事情要注意
Semaphore
Mutex
上面这两个可以做成全局的,并不一定是By Process的,例如POSIX pthread在
对Mutex做attr设定的时候就可以指定为 shared process
也就是说一个Process可以加锁,另外一个可以释放他。
另外这种Mutex必须处在共享内存中,否则没办法访问。有亲缘关系的Process可以通过mmap一个匿名映射做到
anyway有很多方式了。
Critical Section
这个是Inter Process的东西。
关于线程互斥的lock的问题
RW lock就是对普通lock记录两个状态来供read or write操作选择
属于线程本身的东西 TLS/Stack/Register
有时候编译器会为了做优化
内存和寄存器的数据会出现不sync的状态。
即使你用lock来做保护,也不一定能OK。然后volatile就出现了。
volatile最主要的作用就是thread内保证编译器不要做优化,防止这种不sync带来的问题。
一般是这样例如x变量,thread_1读到x变量放到了寄存器中,因为可能马上会再访问它,那么对x进行操作后就不会写回内存
这样即使你加了lock,这个时候lock也被释放掉了(操作完成),但是结果未能Sync,那么thread 2来访问x的时候,在内存
中拿到的值就变成dirty状态了。
另外一种过度优化就是CPU做的优化,有些上下语义无关的指令,CPU有可能会调整运行顺序。
书中有个经典样例
一段 Singleton pattern的double-check的代码
volatile T* pInst = NULL;
T* getInstance()
{
if (pInst == NULL)
{
lock();
if (pInst == NULL)
pInst = new T();
unlock();
}
return pInst;
}
这里有两点
第一,double-check 也就是双if能避免过多的无用的get lock,降低消耗
对临界区需要做保护的资源,可以提前去取状态,如果符合自己的预期,而且短时间不会有变化,那么就不用去拿锁了
不知道为啥我想到了unlikely,但仔细想一下,功能完全不同。
第二点也就是要说的CPU的过度优化
这里已经是声明volatile了,所以没有寄存器和内存不sync的问题
但是由于这里new需要先 malloc出空间,然后call T的constructor。
所以有可能会发生这种情况,malloc出空间后,把地址付给pInst,然后去做初始化;
这样就有可能另外一个线程取得的object是没有被完全初始化好的,是否会出问题depend on T的具体实现了。
许多CPU提供了barrier指令用来解决上面提到的问题。
线程模型
这个东西,我看了下,开始没看明白,这边书这个东西没讲清楚,后来去网上找了些资料。用户线程和内核线程的对应关系取决于调度单位。
也就是说内核把什么东西当做一个调度单位
拿Linux来说吧,Process是线程集和资源集
调度的时候,那些共享资源的Task(thread)之间的调度肯定比那些跨Process不共享资源的thread做context switch消耗的资源
多得多。
基于调度消耗之类的考量
模型分为下面几种
一对一,也就是说 user space create出来的线程就是和kernel的调度单位相同,称一一对应
一对多,应该是这样一种情况,kernel看到的是Process,userspace自己实现出来自己的thread,这个thread,kernel是不知道的
调度的时候kernel负责分批CPU给他能看到的Process,上层userspace自己来调度分配这个Process获得的CPU time给这个process中的
各个线程。
这样的分配就可以保证在一定的时间内只需要做一些register和stack的切换,不会有memory等等的switch。
坏处是上面的thread只要一个被suspend,那么这个Process里面的其他thread也就被suspend住了,一般上层调度程序
不会假定其他的thread能run,所以一般会是kernel把CPU time给其他process
多对多,就是一种混合的情况了,我想到了Android,但是Android是一对一模型,dalvik会保证Java thread对应下面一个
native thread,想说的是,这种虚拟机架构可以做成多对多的样子,一个native thread run一个JVM,JVM开出来很多Java Thread,
JVM负责调度这些Java Thread,Native负责调度JVM所在的Thread。
不知道我有没有讲错。