??
字號:
/* Check for Success and release if such */
if(NT_SUCCESS(Status)) {
LONG Prev;
/* Save the Old State */
DPRINT("Releasing Mutant\n");
Prev = KeReleaseMutant(Mutant, MUTANT_INCREMENT, FALSE, FALSE);
ObDereferenceObject(Mutant);
/* Return it */
if(PreviousCount) {
_SEH_TRY . . . . . . _SEH_END;
}
}
/* Return Status */
return Status;
}[/code]
顯然,這里實質性的操作是KeReleaseMutant(),我們順著往下看。
[code][NtReleaseMutant() > KeReleaseMutant()]
LONG
STDCALL
KeReleaseMutant(IN PKMUTANT Mutant, IN KPRIORITY Increment,
IN BOOLEAN Abandon, IN BOOLEAN Wait)
{
KIRQL OldIrql;
LONG PreviousState;
PKTHREAD CurrentThread = KeGetCurrentThread();
/* Lock the Dispatcher Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save the Previous State */
PreviousState = Mutant->Header.SignalState;
/* Check if it is to be abandonned */
if (Abandon == FALSE) {
/* Make sure that the Owner Thread is the current Thread */
if (Mutant->OwnerThread != CurrentThread) {
DPRINT1("Trying to touch a Mutant that the caller doesn't own!\n");
ExRaiseStatus(STATUS_MUTANT_NOT_OWNED);
}
/* If the thread owns it, then increase the signal state */
Mutant->Header.SignalState++;
} else {
/* It's going to be abandonned */
DPRINT("Abandonning the Mutant\n");
Mutant->Header.SignalState = 1;
Mutant->Abandoned = TRUE;
}
/* Check if the signal state is only single */
if (Mutant->Header.SignalState == 1) {
if (PreviousState <= 0) {
DPRINT("Removing Mutant\n");
RemoveEntryList(&Mutant->MutantListEntry);
}
/* Remove the Owning Thread and wake it */
Mutant->OwnerThread = NULL;
/* Check if the Wait List isn't empty */
DPRINT("Checking whether to wake the Mutant\n");
if (!IsListEmpty(&Mutant->Header.WaitListHead)) {
/* Wake the Mutant */
DPRINT("Waking the Mutant\n");
KiWaitTest(&Mutant->Header, Increment);
}
}
/* If the Wait is true, then return with a Wait and don't unlock the Dispatcher Database */
if (Wait == FALSE) {
/* Release the Lock */
KeReleaseDispatcherDatabaseLock(OldIrql);
} else {
/* Set a wait */
CurrentThread->WaitNext = TRUE;
CurrentThread->WaitIrql = OldIrql;
}
/* Return the previous state */
return PreviousState;
}[/code]
參數Abandon表示在退出臨界區以后是否要廢棄這個互斥門。我們從NtReleaseMutant()的代碼中可以看出,實際傳下來的參數值是FALSE。那么什么情況下這個參數會是TRUE呢?據“Native API”書中說,這發生于互斥門的業主、就是已經通過這個互斥門進入了臨界區的線程突然要結束其生命的時候。
既然臨界區中的線程要退出,這個互斥門就變成無主的了,所以把Mutant->OwnerThread設置成NULL。其余的代碼就留給讀者自己去理解了。
4. 事件(Event)
信號量機制的另一個變種是“事件”,這是通過事件對象實現的。Windows為事件對象的創建和打開提供了NtCreateEvent()和NtOpenEvent()兩個系統調用。由于所有此類函數的相似性,這兩個系統調用的代碼就不用看了,只要知道內核中代表著事件對象的數據結構是KEVENT就可以了:
[code]typedef struct _KEVENT {
DISPATCHER_HEADER Header;
} KEVENT, *PKEVENT, *RESTRICTED_POINTER PRKEVENT;[/code]
這就是說,除DISPATCHER_HEADER以外,KEVENT就不需要有別的什么字段了。
Windows定義和提供了兩種不同類型的事件,每個事件對象也因此而分成兩種類型,這就是:
[code]typedef enum _EVENT_TYPE {
NotificationEvent,
SynchronizationEvent
} EVENT_TYPE;[/code]
事件對象的類型是在創建的時候(通過參數)設定的,記錄在對象頭部的Type字段中,設定以后就不能改變。為不同的應用和目的需要使用不同類型的事件對象。
類型為NotificationEvent的事件代表著“通知”。通知是廣播式的,其作用就是通知公眾某個事件業已發生(Header中的SignalState為1),一個觀看者看了這個布告并不影響別的觀看者繼續觀看。所以,在通知型事件對象上的P操作并不消耗資源,也就是不改變其數值。在這一點上它就像是一個全局(跨進程)的變量。但是,如果事件尚未發生(SignalState為0),則所有的觀看者、即對此對象執行P操作的線程全都被阻塞而進入睡眠,直到該事件發生,在這一點上又不太像“通知”,而反倒是起著同步的作用了(設想你去看高考發榜,但是還沒貼出來,你就被“套住”等在那兒了)。讀者也許會想到,既然P操作不改變SignalState的值,那豈不是一旦SignalState變成1就永遠是1、從而事件對象只能一次性使用了?這確實是個問題,所以Windows又專門提供了一個系統調用NtResetEvent(),用來“重啟(Reset)”一個事件對象、即將其SignalState清0。
類型為SynchronizationEvent的事件對象則用于同步,這就相當于初值為0、最大值為1的信號量。對于同步型的事件對象,一次P操作相當于消耗一個籌碼(通行證),而V操作則相當于提供一個籌碼。
回顧一下前面KiIsObjectSignaled()的代碼,這是P操作中用來判斷是否可以(拿到籌碼)進入臨界區的函數。這個函數對于除互斥門以外的所有對象都返回(!Object->SignalState <= 0)。這就是說,不管是同步型還是通知型的事件對象,執行P操作的線程能拿到籌碼或看到通知的條件都是SignalState為1,否則就要睡眠等待。
再回顧一下KiSatisfyObjectWait()的代碼,這是P操作中拿到籌碼以后的操作:
[code]KiSatisfyObjectWait(PDISPATCHER_HEADER Object, PKTHREAD Thread)
{
/* Special case for Mutants */
if (Object->Type == MutantObject) {
. . . . . .
} else if ((Object->Type & TIMER_OR_EVENT_TYPE) == EventSynchronizationObject) {
/* These guys (Syncronization Timers and Events) just get un-signaled */
Object->SignalState = 0;
} else if (Object->Type == SemaphoreObject) {
/* These ones can have multiple signalings, so we only decrease it */
Object->SignalState--;
}
}[/code]
這里Object->Type的最低3位記錄著對象的類型,如果是EventSynchronizationObject就說明是同步型的事件對象,此時把Object->SignalState置0,表示把籌碼消耗掉了。由于事件對象的SignalState只有兩個值0或非0,因而在SignalState為1的條件下將其設置成0跟使之遞減是等價的。可是,如果是通知型的事件對象,那就沒有任何操作,所以并沒有把通知“消耗”掉。所以,這又是變相的P操作。
跟信號量和互斥門一樣,對事件對象的P操作就是系統調用NtWaitForSingleObject()或NtWaitForMultipleObjects(),或者(如果從內核中調用)也可以是KeWaitForSingleObject(),而KiIsObjectSignaled()和KiSatisfyObjectWait()都是在P操作內部調用的函數。
事件對象的V操作是系統調用NtSetEvent(),意思是把事件對象的SignalState設置成1。就像NtReleaseMutant()的主體是KeReleaseMutant()一樣,NtSetEvent()的主體是KeSetEvent()。我們跳過NtSetEvent()這一層,直接看KeSetEvent()的代碼。
[code][NtSetEvent() > KeSetEvent()]
LONG STDCALL
KeSetEvent(PKEVENT Event, KPRIORITY Increment, BOOLEAN Wait)
{
. . . . . .
/* Lock the Dispathcer Database */
OldIrql = KeAcquireDispatcherDatabaseLock();
/* Save the Previous State */
PreviousState = Event->Header.SignalState;
/* Check if we have stuff in the Wait Queue */
if (IsListEmpty(&Event->Header.WaitListHead)) {
/* Set the Event to Signaled */
DPRINT("Empty Wait Queue, Signal the Event\n");
Event->Header.SignalState = 1;
} else {
/* Get the Wait Block */
WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink,
KWAIT_BLOCK, WaitListEntry);
/* Check the type of event */
if (Event->Header.Type == NotificationEvent || WaitBlock->WaitType == WaitAll) {
if (PreviousState == 0) {
/* We must do a full wait satisfaction */
DPRINT("Notification Event or WaitAll, Wait on the Event and Signal\n");
Event->Header.SignalState = 1;
KiWaitTest(&Event->Header, Increment);
}
} else {
/* We can satisfy wait simply by waking the thread, since our signal state is 0 now */
DPRINT("WaitAny or Sync Event, just unwait the thread\n");
KiAbortWaitThread(WaitBlock->Thread, WaitBlock->WaitKey, Increment);
}
}
/* Check what wait state was requested */
if (Wait == FALSE) {
/* Wait not requested, release Dispatcher Database and return */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -