最近在看操作系统方面的东西,然后看到一些同步的要求,比如锁,于是就想想它们是如何实现的?
比如应用层编码是调用EnterCriticalSection之类的,以前就想到这里,剩下的工作就由OS去管理了,反正它能实现;不过今天一想,它能有什么办法呢?如果是软件它貌似也没有什么比应用层强的方法;于是有点好奇,就查阅一些资料来研究一下究竟。
一般的锁实现都是通过处理器支持的原子操作来完成的,这些原子操作是不可分割的,不能被中断。在单处理器的CPU上,单条指令可以认为是原子操作,因为中断只能发生在指令之间。但对于多处理器结构就不一样了,这个时候需要一些硬件的支持来保证,这严重依赖于不同的硬件实现。
例如:x86平台上,CPU执行期间会对总线加锁的手段,也就是CPU芯片上有一个HLOCK Pin,可以通过发送指令来操作,将#HLOCK pin电位拉低,并持续到这条指令执行完毕,从而将总线锁定,因此同一个总线的其他CPU就不能通过总线来访存了,因此保证了这条指令在多处理器环境下的原子性。
最开始的时候这些功能是用于CPU测试使用,但最后被操作系统实现而封装成各种功能:关键代码段、信号量等。因此在应用层使用来说脱离了与不同硬件交互的过程。
这里看一个X86平台windows上的一个关键代码段实现。
原来代码如下:
1int _tmain(int argc, _TCHAR* argv[])
2{
3 CRITICAL_SECTION cs;
4 InitializeCriticalSection(&cs);
5
6 EnterCriticalSection(&cs);
7 int i =0;
8 i++;
9 LeaveCriticalSection(&cs);
10 return 0;
11} 反汇编到EnterCriticalSection,可以看到如下代码:
177BD69D0 push ebp
277BD69D1 mov ebp,esp
377BD69D3 sub esp,0Ch
477BD69D6 push esi
577BD69D7 push edi
677BD69D8 mov edi,dword ptr [ebp+8]
777BD69DB lea esi,[edi+4]
877BD69DE mov eax,esi
977BD69E0 lock btr dword ptr [eax],0
1077BD69E5 jae 77BE5F4B
1177BD69EB mov eax,dword ptr fs:[00000018h] 这里特别需要注意: lock btr dword ptr[eax], 0
lock是一个指令前缀,Intel的手册上对其的解释是:
Causes the processor's LOCK# signal to be asserted during execution of the accompanying instruction (turns the instruction into an atomic instruction). In a multiprocessor environment, the LOCK# signal insures that the processor has exclusive use of any shared memory while the signal is asserted.
也就是说lock会使紧跟在其后面的指令变成 atomic instruction。暂时的锁一下总线,指令执行完了,总线就解锁了.
从上面可以看到其验证了前面所说;到这里就算是理解了操作系统是如何实现锁机制的。