⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄

📁 兼容内核漫谈 适合想将Windows上的程序移植到其它平台上的朋友研究查看
💻
📖 第 1 页 / 共 5 页
字号:
    /* 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 + -