内核中的同步机制(二)

★.线程等待对象

一些对象时刻等待的 进程 线程 定时器 各种同步对象等等

可被等待的对象,开头都是一个分发器头,这是WRK中的定义,WDK中的定义要更复杂点

typedef struct _DISPATCHER_HEADER {
    union {
        struct {
            UCHAR Type; 对象类型
            union {
                UCHAR Absolute;
                UCHAR NpxIrql;
            };
            union {
                UCHAR Size;
                UCHAR Hand;
            };
            union {
                UCHAR Inserted;
                BOOLEAN DebugActive;
            };
        };
        volatile LONG Lock;
    };
    LONG SignalState; 受信状态
LIST_ENTRY WaitListHead; 这是链表头 连接了所有等待此对象的线程的KWAIT_BLOCK
} DISPATCHER_HEADER;



KiUnwaitThread用于唤醒一个线程,使其变成延迟就绪状态

KTHREAD.WaitBlockList记录线程等待的对象,指向第一个等待的对象

typedef struct _KWAIT_BLOCK {
    LIST_ENTRY WaitListEntry; //挂入WaitListHead,一个对象所有的等待块   struct _KTHREAD *Thread;
    PVOID Object; //指要等待的对象
    struct _KWAIT_BLOCK *NextWaitBlock; //挂入线程链表,这个链表组成的线程等待的所有对象,单循环链表,头是KTHREAD.WaitBlockList
    USHORT WaitKey;
    UCHAR WaitType; //WaitAny 任意对象满足后唤醒 WaitAll要等到所有都满足
    UCHAR SpareByte;
} KWAIT_BLOCK, *PKWAIT_BLOCK, *PRKWAIT_BLOCK;


1.线程进入等待
先看一下线程进入等待的状况,以KeWaitForSingleObject为例

//插入代码

等待一定时间的KeDelayxxx还有KeWaitForMultie 原理类似

NTSTATUS
KeWaitForSingleObject (
    __in PVOID Object,
    __in KWAIT_REASON WaitReason,
    __in KPROCESSOR_MODE WaitMode,
    __in BOOLEAN Alertable,
    __in_opt PLARGE_INTEGER Timeout
    )
--*/
{
    Hand = 0;
    Thread = KeGetCurrentThread();
    Objectx = (PKMUTANT)Object;
    OriginalTime = Timeout;
    Timer = &Thread->Timer;
    WaitBlock = &Thread->WaitBlock[0];
    WaitTimer = &Thread->WaitBlock[TIMER_WAIT_BLOCK]; //最后一个用于超时
    if (ReadForWriteAccess(&Thread->WaitNext) == FALSE) {
        goto WaitStart;
    }
    Thread->WaitNext = FALSE;
    /*InitializeWaitSingle();

#define InitializeWaitSingle() 宏展开*/                                            
    Thread->WaitBlockList = WaitBlock;     //初始化等待块                                
    WaitBlock->Object = Object;                                             
    WaitBlock->WaitKey = (CSHORT)(STATUS_SUCCESS);                          
    WaitBlock->WaitType = WaitAny;                                          
    Thread->WaitStatus = 0;                                                 
    if (ARGUMENT_PRESENT(Timeout)) {     //如果有到期时间 ,初始化一个定时器                                 
        KiSetDueTime(Timer, *Timeout, &Hand);                               
        DueTime.QuadPart = Timer->DueTime.QuadPart;                         
        WaitBlock->NextWaitBlock = WaitTimer;    //定时器等待块和要等待的等待块构成循环链表                             
        WaitTimer->NextWaitBlock = WaitBlock;                               
        Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;       
        Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;       
    } else {                                                                
        WaitBlock->NextWaitBlock = WaitBlock;                               
    }                                                                       
    Thread->Alertable = Alertable;                                          
    Thread->WaitMode = WaitMode;                                            
    Thread->WaitReason = (UCHAR)WaitReason;                                 
    Thread->WaitListEntry.Flink = NULL;                                     
    StackSwappable = KiIsKernelStackSwappable(WaitMode, Thread);            
    Thread->WaitTime = KiQueryLowTickCount()
    do {
   //这个循环是为了能够在等待过程中处理APC
        Thread->Preempted = FALSE;
        if (Thread->ApcState.KernelApcPending &&
            (Thread->SpecialApcDisable == 0) &&
            (Thread->WaitIrql < APC_LEVEL)) {
    //irql提升到DISPATCH_LEVEL,其他cpu投递了一个apc,dispatcher数据库仍在锁定的情况
    //这样会请求一个中断派发掉apc
            KiUnlockDispatcherDatabase(Thread->WaitIrql);
        } else {   
    //MutantObject要特别对待 Mutant对应的是R3的mutex概念 
            if (Objectx->Header.Type == MutantObject) {
                if ((Objectx->Header.SignalState > 0) ||
                    (Thread == Objectx->OwnerThread)) {
                    if (Objectx->Header.SignalState != MINLONG) {
                        KiWaitSatisfyMutant(Objectx, Thread);
                        WaitStatus = (NTSTATUS)(Thread->WaitStatus);
                        goto NoWait;
                    } else {
                        KiUnlockDispatcherDatabase(Thread->WaitIrql);
                        ExRaiseStatus(STATUS_MUTANT_LIMIT_EXCEEDED);
                    }
                }
            } else if (Objectx->Header.SignalState > 0) {//条件已经满足
                KiWaitSatisfyOther(Objectx);
                WaitStatus = (NTSTATUS)(0);
                goto NoWait;
            }
            TestForAlertPending(Alertable);   //检测是否可以被叫醒(用户态APC神马的) 更改等待状态 WaitStatus
    /* 以下是宏定义 如果被叫醒,则提前结束循环
    #define TestForAlertPending(Alertable) \
    if (Alertable) { \
        if (Thread->Alerted[WaitMode] != FALSE) { \
            Thread->Alerted[WaitMode] = FALSE; \
            WaitStatus = STATUS_ALERTED; \
            break; \
        } else if ((WaitMode != KernelMode) && \
                  (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) == FALSE) { \
            Thread->ApcState.UserApcPending = TRUE; \
            WaitStatus = STATUS_USER_APC; \
            break; \
        } else if (Thread->Alerted[KernelMode] != FALSE) { \
            Thread->Alerted[KernelMode] = FALSE; \
            WaitStatus = STATUS_ALERTED; \
            break; \
        } \
    } else if (Thread->ApcState.UserApcPending & WaitMode) { \
        WaitStatus = STATUS_USER_APC; \
        break; \
    }
    */
            if (ARGUMENT_PRESENT(Timeout)) {// 检查定时是否到期
                if (KiCheckDueTime(Timer) == FALSE) {
                    WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
                    goto NoWait;
                }
            }
            InsertTailList(&Objectx->Header.WaitListHead, &WaitBlock->WaitListEntry);// 等待块插入对象的派发头
   
            Queue = Thread->Queue;
            if (Queue != NULL) {
                KiActivateWaiterQueue(Queue);
            }
            Thread->State = Waiting;    //设置线程进入等待状态
            CurrentPrcb = KeGetCurrentPrcb();
            if (StackSwappable != FALSE) {
                InsertTailList(&CurrentPrcb->WaitListHead, &Thread->WaitListEntry);
            }
            ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
            KiSetContextSwapBusy(Thread);
            if (ARGUMENT_PRESENT(Timeout)) {
                KiInsertOrSignalTimer(Timer, Hand);
            } else {
                KiUnlockDispatcherDatabaseFromSynchLevel();
            }
            WaitStatus = (NTSTATUS)KiSwapThread(Thread, CurrentPrcb); //放弃执行,切换线程,当从这里返回之后意味着
                   //条件已经被满足,或者超时,或者有内核APC插入
            if (WaitStatus != STATUS_KERNEL_APC) {//只要不是内核apc插入,就可以返回了
                return WaitStatus;
            }
            if (ARGUMENT_PRESENT(Timeout)) {//否则这不是一次真的等到了,而是被内核apc搞醒的 重新计算时间 重新构造一个等待 循环
                Timeout = KiComputeWaitInterval(OriginalTime, //减少时间
                                                &DueTime,
                                                &NewTime);
            }
        }
WaitStart:
        Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
        InitializeWaitSingle();
        KiLockDispatcherDatabaseAtSynchLevel();
    } while (TRUE);
    KiUnlockDispatcherDatabase(Thread->WaitIrql);
    return WaitStatus;
NoWait:
    KiUnlockDispatcherDatabaseFromSynchLevel();
    KiAdjustQuantumThread(Thread);
    return WaitStatus;
}



KeWaitForMultie需要在每次被唤醒后需要重新构造等待块,判断条件是否满足,如果不满足,那就继续等待下去

2.测试已满足等待条件
KeSetEvent 等等使对象受信的函数会调用测试线程是否在等待这个对象,并且尝试释放

    Event->Header.SignalState = 1;
    if ((OldState == 0) &&
        (IsListEmpty(&Event->Header.WaitListHead) == FALSE)) {
        if (Event->Header.Type == EventNotificationObject) {
            KiWaitTestWithoutSideEffects(Event, Increment); //事件通知 一次唤醒所有线程
        } else {
            KiWaitTestSynchronizationObject(Event, Increment); //事件同步 一次唤醒一个线程并且重置事件
        }
}


看一下这个KiWaitTestWithoutSideEffects

VOID
KiWaitTestWithoutSideEffects (
    IN PVOID Object,
    IN KPRIORITY Increment
    )
{
    PKEVENT Event = Object;
    PLIST_ENTRY ListHead;
    PRKTHREAD Thread;
    PRKWAIT_BLOCK WaitBlock;
    PLIST_ENTRY WaitEntry;

    ListHead = &Event->Header.WaitListHead;
    ASSERT(IsListEmpty(&Event->Header.WaitListHead) == FALSE);
    WaitEntry = ListHead->Flink;
    do {
   //遍历等待这个对象的等待块,如果是waitany 可以直接唤醒,否则以APC形式唤醒,交由waitformutie处理
        WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
        Thread = WaitBlock->Thread;
        if (WaitBlock->WaitType == WaitAny) {
            KiUnwaitThread(Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
        } else {
            KiUnwaitThread(Thread, STATUS_KERNEL_APC, Increment);
        }
        WaitEntry = ListHead->Flink;
    } while (WaitEntry != ListHead);
    return;
}


其他测试函数也大同小异

3.线程取消等待
线程取消等待的函数是KiUnwaitThread,它先调用了了KiUnlinkThread

KiUnlinkThread这个函数做了三件事,移除Thread->WaitBlockList,移除Timer,递增线程队列的活动系数(Thread->Queue)

之后KiUnwaitThread调用了KiReadyThread使线程进入了延迟等待状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值