ReactOS分析CriticalSection

来源:互联网 发布:java中final 编辑:程序博客网 时间:2024/06/05 03:48

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

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

NTSTATUSNTAPIRtlInitializeCriticalSectionAndSpinCount(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是系统提供的专用于保护访问互斥;而这个数据同样也是经过这个函数初始化,所以需要有一个分支过程。(个人觉得这里在定义的时候直接进行初始化,然后定义一个特殊的函数进行初始化,毕竟分支影响处理器的流水线,个人理解,还望高手指点迷津)

NTSTATUSNTAPIRtlEnterCriticalSection(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可用。
NTSTATUSNTAPIRtlpWaitForCriticalSection(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">NTSTATUSNTAPIRtlLeaveCriticalSection(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

0 0
原创粉丝点击