您的位置:首页 > Web前端 > React

ReactOS分析CriticalSection

2014-09-01 09:02 1361 查看
首先要区分的是,之前提过的critical region和critical section的区别,前者是主要是通过IRQL阻止线程被打断,而后者则是通过包装内核对象来防止线程被打断。

为了减少不必要的代码量,直接跳过一些函数的封装。下面是critical section的初始化实现。

NTSTATUS
NTAPI
RtlInitializeCriticalSectionAndSpinCount(PRTL_CRITICAL_SECTION CriticalSection,
ULONG SpinCount)
{
PRTL_CRITICAL_SECTION_DEBUG CritcalSectionDebugData;
CriticalSection->LockCount = -1;
CriticalSection->RecursionCount = 0;
CriticalSection->OwningThread = 0;
CriticalSection->SpinCount = (NtCurrentPeb()->NumberOfProcessors > 1) ? SpinCount : 0;
CriticalSection->LockSemaphore = 0;
CritcalSectionDebugData = RtlpAllocateDebugInfo();
if (!CritcalSectionDebugData) {
return STATUS_NO_MEMORY;
}
CritcalSectionDebugData->Type = RTL_CRITSECT_TYPE;
CritcalSectionDebugData->ContentionCount = 0;
CritcalSectionDebugData->EntryCount = 0;
CritcalSectionDebugData->CriticalSection = CriticalSection;
CriticalSection->DebugInfo = CritcalSectionDebugData;

if ((CriticalSection != &RtlCriticalSectionLock) && (RtlpCritSectInitialized)) {
RtlEnterCriticalSection(&RtlCriticalSectionLock);
InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
RtlLeaveCriticalSection(&RtlCriticalSectionLock);

} else {
InsertTailList(&RtlCriticalSectionList, &CritcalSectionDebugData->ProcessLocksList);
}

return STATUS_SUCCESS;
}
首先,critical section中的LockCount小于0的时候,表明criticalsection可以访问,反之,当LockCount大于0时,则不能访问。LockCount和下面的RecursionCount有关,LockCount初始化为-1,表明一次只能有一个线程访问。RecursionCount计数当前线程递归访问criticalsection的数目,一般等于LockCount+1,初始化为0.下面设置线程的句柄,后面是互斥访问criticalsection的信号量计数。最后一个是额外的调试数据,调试数据的处理分为两种情况,一种是在全局初始化时,另一种则是初始化普通的CRITICAL_SECTION结构体。

在初始化调试数据之后,就将critical secction插入到队列的尾部。这里之所以需要有一个判断,是因为RtlCriticalSectionLock是系统提供的专用于保护访问互斥;而这个数据同样也是经过这个函数初始化,所以需要有一个分支过程。(个人觉得这里在定义的时候直接进行初始化,然后定义一个特殊的函数进行初始化,毕竟分支影响处理器的流水线,个人理解,还望高手指点迷津)

NTSTATUS
NTAPI
RtlEnterCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
{
HANDLE Thread = (HANDLE)NtCurrentTeb()->ClientId.UniqueThread;
if (InterlockedIncrement(&CriticalSection->LockCount) != 0) {
if (Thread == CriticalSection->OwningThread) {
CriticalSection->RecursionCount++;
return STATUS_SUCCESS;
}
RtlpWaitForCriticalSection(CriticalSection);
}
CriticalSection->OwningThread = Thread;
CriticalSection->RecursionCount = 1;
return STATUS_SUCCESS;
}
这个函数实现进入到critical section,首先会利用原子操作自加LockCount。如果自加之后的数目不等于0,则表明已经有线程进入到critical section。如果已经有线程进入到critical section,则需要判断是否是当前线程递归访问critical section。如果是递归访问则自加RecursionCount,并返回函数执行成功。否则需要等待Critical Section可用。
NTSTATUS
NTAPI
RtlpWaitForCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
{
NTSTATUS Status;
EXCEPTION_RECORD ExceptionRecord;
BOOLEAN LastChance = FALSE;
LARGE_INTEGER Timeout;
Timeout.QuadPart = 150000L * (ULONGLONG)10000;
Timeout.QuadPart = -Timeout.QuadPart;
if (!CriticalSection->LockSemaphore) {
RtlpCreateCriticalSectionSem(CriticalSection);
}
if (CriticalSection->DebugInfo)
CriticalSection->DebugInfo->EntryCount++;
for (;;) {
if (CriticalSection->DebugInfo)
CriticalSection->DebugInfo->ContentionCount++;
Status = NtWaitForSingleObject(CriticalSection->LockSemaphore,
FALSE,
&Timeout);
if (Status == STATUS_TIMEOUT) {
LastChance = TRUE;
} else {
return STATUS_SUCCESS;
}
}
}
等待函数的实现非常简单,如果当前critical section为空,则创建一个新的信号量句柄。并在此事件上等待2.5分钟,不过等待只有两次机会,如果超过两次机会则会抛出异常。之前分配的调试信息结构体在此时需要手机调试相关的信息,便于在程序崩溃之后进行相应的分析。到这一步也可以看出,虽然CRITICAL_SECTION不是核心对象,然而其实现却依赖于核心对象。
<pre code_snippet_id="461785" snippet_file_name="blog_20140901_5_3253223" name="code" class="cpp">NTSTATUS
NTAPI
RtlLeaveCriticalSection(PRTL_CRITICAL_SECTION CriticalSection)
{
if (--CriticalSection->RecursionCount) {
InterlockedDecrement(&CriticalSection->LockCount);
} else {
CriticalSection->OwningThread = 0;
if (-1 != InterlockedDecrement(&CriticalSection->LockCount)) {
RtlpUnWaitCriticalSection(CriticalSection);
}
}
return STATUS_SUCCESS;
}


退出critical section段的操作不需要加锁,因为可以执行这一语句的肯定是进入到critical section的线程。首先会递减递归访问计数,然后原子递减当前的LockCount(这里主要是对应于前面的原子操作自加)。如果RecursionCount自减之后等于0,则清除当前的线程句柄,然后递减LockCount。在这里有一种可能是Critical Section的线程句柄等于0。在前面进入到等待的时候,如果某个线程在OwingThread等于0的时候,请求进入到critical section,由于LockCount大于0,所以判断是否等于当前线程,肯定线程句柄是不等于0的,所以进入等待状态。唤醒等待的线程很简单,设置事件就可以了。

完整的源代码网址:http://doxygen.reactos.org/d0/d06/critical_8c_source.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  windows 内核