?? 漫談兼容內(nèi)核之十六:windows的進程間通信.txt
字號:
/* 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]
顯然,這里實質(zhì)性的操作是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]
參數(shù)Abandon表示在退出臨界區(qū)以后是否要廢棄這個互斥門。我們從NtReleaseMutant()的代碼中可以看出,實際傳下來的參數(shù)值是FALSE。那么什么情況下這個參數(shù)會是TRUE呢?據(jù)“Native API”書中說,這發(fā)生于互斥門的業(yè)主、就是已經(jīng)通過這個互斥門進入了臨界區(qū)的線程突然要結(jié)束其生命的時候。
既然臨界區(qū)中的線程要退出,這個互斥門就變成無主的了,所以把Mutant->OwnerThread設(shè)置成NULL。其余的代碼就留給讀者自己去理解了。
4. 事件(Event)
信號量機制的另一個變種是“事件”,這是通過事件對象實現(xiàn)的。Windows為事件對象的創(chuàng)建和打開提供了NtCreateEvent()和NtOpenEvent()兩個系統(tǒng)調(diào)用。由于所有此類函數(shù)的相似性,這兩個系統(tǒng)調(diào)用的代碼就不用看了,只要知道內(nèi)核中代表著事件對象的數(shù)據(jù)結(jié)構(gòu)是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]
事件對象的類型是在創(chuàng)建的時候(通過參數(shù))設(shè)定的,記錄在對象頭部的Type字段中,設(shè)定以后就不能改變。為不同的應(yīng)用和目的需要使用不同類型的事件對象。
類型為NotificationEvent的事件代表著“通知”。通知是廣播式的,其作用就是通知公眾某個事件業(yè)已發(fā)生(Header中的SignalState為1),一個觀看者看了這個布告并不影響別的觀看者繼續(xù)觀看。所以,在通知型事件對象上的P操作并不消耗資源,也就是不改變其數(shù)值。在這一點上它就像是一個全局(跨進程)的變量。但是,如果事件尚未發(fā)生(SignalState為0),則所有的觀看者、即對此對象執(zhí)行P操作的線程全都被阻塞而進入睡眠,直到該事件發(fā)生,在這一點上又不太像“通知”,而反倒是起著同步的作用了(設(shè)想你去看高考發(fā)榜,但是還沒貼出來,你就被“套住”等在那兒了)。讀者也許會想到,既然P操作不改變SignalState的值,那豈不是一旦SignalState變成1就永遠是1、從而事件對象只能一次性使用了?這確實是個問題,所以Windows又專門提供了一個系統(tǒng)調(diào)用NtResetEvent(),用來“重啟(Reset)”一個事件對象、即將其SignalState清0。
類型為SynchronizationEvent的事件對象則用于同步,這就相當于初值為0、最大值為1的信號量。對于同步型的事件對象,一次P操作相當于消耗一個籌碼(通行證),而V操作則相當于提供一個籌碼。
回顧一下前面KiIsObjectSignaled()的代碼,這是P操作中用來判斷是否可以(拿到籌碼)進入臨界區(qū)的函數(shù)。這個函數(shù)對于除互斥門以外的所有對象都返回(!Object->SignalState <= 0)。這就是說,不管是同步型還是通知型的事件對象,執(zhí)行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的條件下將其設(shè)置成0跟使之遞減是等價的。可是,如果是通知型的事件對象,那就沒有任何操作,所以并沒有把通知“消耗”掉。所以,這又是變相的P操作。
跟信號量和互斥門一樣,對事件對象的P操作就是系統(tǒng)調(diào)用NtWaitForSingleObject()或NtWaitForMultipleObjects(),或者(如果從內(nèi)核中調(diào)用)也可以是KeWaitForSingleObject(),而KiIsObjectSignaled()和KiSatisfyObjectWait()都是在P操作內(nèi)部調(diào)用的函數(shù)。
事件對象的V操作是系統(tǒng)調(diào)用NtSetEvent(),意思是把事件對象的SignalState設(shè)置成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 */
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -