内核中的同步机制(三)
来源:互联网 发布:绝地易语言辅助源码 编辑:程序博客网 时间:2024/05/01 15:12
★.可等待对象一览
1. Kevent 就是一个等待头 没啥说的 以上的例子也是举得这个KMUTANT 对应的是r3的mutex
typedef struct _KMUTANT { DISPATCHER_HEADER Header; LIST_ENTRY MutantListEntry; KTHREAD.MutantListHead struct _KTHREAD *OwnerThread; //一个互斥体属于某个线程 BOOLEAN Abandoned; //是否已经弃用 UCHAR ApcDisable; // 是否禁用某种apc 会影响到线程的响应} KMUTANT, *PKMUTANT, *PRKMUTANT, KMUTEX, *PKMUTEX, *PRKMUTEX;
只有所属线程才能释放这个对象,所以这个对象可以算是特别为线程访问资源同步设计的
2. 信号量
只是在DISPATCHER_header 后面加上一个Limit 数 限制了共享资源的最大值~ 差不对
3. 队列
KQUEUE 用来实现线程池 ,也就是实现的一组线程 限制了最大的活动线程数,超出最大线程数之后,线程只能进入等待,等到其他线程不活动了之后自己再活动
typedef struct _KQUEUE { DISPATCHER_HEADER Header; LIST_ENTRY EntryListHead; 队列中有待处理的项 ULONG CurrentCount; 活动线程数 ULONG MaximumCount; LIST_ENTRY ThreadListHead; 节点是KTHREAD.QueueListHead} KQUEUE, *PKQUEUE, *PRKQUEUE;
插入一个队列时,如果活动线程数小于最大数,并且有线程在等待队列 立即释放,否则就被插入到EntryListHead
4. 进程线程 挺简单不多说
5. 定时器
最后的tips
1.等待的线程解除时,优先级上升1(定时器除外),并且时限-1(不同系统环境未必一样)
2.在处理分发对象头时都要提升IRQL并且锁住分发数据库,这表明进入了一段线程调度器的代码 线程调度分布在 中断 异常 以及各种处理线程相关代码逻辑的位置
一般情况下都是在处理过之后退出,单象 KeSetEvent 有一个Wait变量,如果指明为true 证明可以保证有线程在等待 这时不退出调度器,而是
if (Wait != FALSE) { Thread = KeGetCurrentThread(); Thread->WaitNext = Wait; Thread->WaitIrql = OldIrql; } else { KiUnlockDispatcherDatabase(OldIrql);}
然后由等待的线程来处理,不知道这样做的优化到底有多少,但我知道我当年由于不好好看文档,用错这个参数,反正是没少蓝屏。。。。。。
★.门等待
绕过繁琐的线程调度,实现快速等待。实际上就是把门对象从线程等待对象的机制中抽离出来,自己实现一套专门的等待机制,
对象是一个KGATE 就是分发器头部对象,只不过开头被解释为一个锁
VOIDFASTCALLKeWaitForGate ( __inout PKGATE Gate, __in KWAIT_REASON WaitReason, __in KPROCESSOR_MODE WaitMode ){ PKTHREAD CurrentThread; KLOCK_QUEUE_HANDLE LockHandle; PKQUEUE Queue; PKWAIT_BLOCK WaitBlock; NTSTATUS WaitStatus; CurrentThread = KeGetCurrentThread(); do { KeAcquireInStackQueuedSpinLockRaiseToSynch(&CurrentThread->ApcQueueLock, &LockHandle); //先交付一下apc看看 if (CurrentThread->ApcState.KernelApcPending && (CurrentThread->SpecialApcDisable == 0) && (LockHandle.OldIrql < APC_LEVEL)) { KeReleaseInStackQueuedSpinLock(&LockHandle); continue; } //有队列才去锁? if ((Queue = CurrentThread->Queue) != NULL) { KiLockDispatcherDatabaseAtSynchLevel(); } KiAcquireThreadLock(CurrentThread);//Thread->ThreadLock KiAcquireKobjectLock(Gate); //对象分发头的锁 if (Gate->Header.SignalState != 0) { //有信号就立即满足 Gate->Header.SignalState = 0; //立即重置 KiReleaseKobjectLock(Gate); KiReleaseThreadLock(CurrentThread); if (Queue != NULL) { KiUnlockDispatcherDatabaseFromSynchLevel(); } KeReleaseInStackQueuedSpinLock(&LockHandle); break; } else { WaitBlock = &CurrentThread->WaitBlock[0];//构造等待块 WaitBlock->Object = Gate; WaitBlock->Thread = CurrentThread; CurrentThread->WaitMode = WaitMode; CurrentThread->WaitReason = WaitReason; CurrentThread->WaitIrql = LockHandle.OldIrql; CurrentThread->State = GateWait; //传说中的门等待状态~~现身~~ 不再放入等待对象中 CurrentThread->GateObject = Gate; InsertTailList(&Gate->Header.WaitListHead, &WaitBlock->WaitListEntry); //这边的链表还是要弄好 KiReleaseKobjectLock(Gate); KiSetContextSwapBusy(CurrentThread); KiReleaseThreadLock(CurrentThread); // // If the current thread is associated with a queue object, then // activate another thread if possible. // if (Queue != NULL) { //线程池,如果有 把执行权让给其他线程 if ((Queue = CurrentThread->Queue) != NULL) { KiActivateWaiterQueue(Queue); } KiUnlockDispatcherDatabaseFromSynchLevel(); } KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle); WaitStatus = (NTSTATUS)KiSwapThread(CurrentThread, KeGetCurrentPrcb()); //线程切换 if (WaitStatus == STATUS_SUCCESS) { return; } } } while (TRUE); return;} KeSignalGateBoostPriority这个函数获取到等待门对象的线程,直接放入延迟准备中去Entry = Gate->Header.WaitListHead.Flink; WaitBlock = CONTAINING_RECORD(Entry, KWAIT_BLOCK, WaitListEntry); WaitThread = WaitBlock->Thread; //取得等待的线程 if (KiTryToAcquireThreadLock(WaitThread)) { RemoveEntryList(Entry); WaitThread->WaitStatus = STATUS_SUCCESS; WaitThread->State = DeferredReady; //设置成延迟准备 直接调用 WaitThread->DeferredProcessor = KeGetCurrentPrcb()->Number; KiReleaseKobjectLock(Gate); KiReleaseThreadLock(WaitThread); Priority = CurrentThread->Priority; if (Priority < LOW_REALTIME_PRIORITY) { //调整优先级! Priority = Priority - CurrentThread->PriorityDecrement; if (Priority < CurrentThread->BasePriority) { Priority = CurrentThread->BasePriority; } if (CurrentThread->PriorityDecrement != 0) { CurrentThread->PriorityDecrement = 0; CurrentThread->Quantum = CLOCK_QUANTUM_DECREMENT; } } WaitThread->AdjustIncrement = Priority; WaitThread->AdjustReason = (UCHAR)AdjustBoost; if (WaitThread->Queue != NULL) { KiLockDispatcherDatabaseAtSynchLevel(); if ((Queue = WaitThread->Queue) != NULL) { Queue->CurrentCount += 1; } KiUnlockDispatcherDatabaseFromSynchLevel(); } KiDeferredReadyThread(WaitThread); KiExitDispatcher(OldIrql); return; } else {//继续尝试获取 KiReleaseKobjectLock(Gate); KeLowerIrql(OldIrql); continue; }
守护互斥体KGUARD_MUTEX是用互斥实现的 还有快速互斥 x86基于事件 x64基于门等待
★.两种执行体层面的同步机制——执行体互斥&push lock
两种同步机制基于上述的一些基本同步机制而实现,提供独占资源和共享资源的读取能力,这里只简单的看一下逻辑。
先看执行体资源的结构定义
typedef struct _ERESOURCE { LIST_ENTRY SystemResourcesList; 执行体资源由系统的一个链表ExpSytemResourceList统一管理 POWNER_ENTRY OwnerTable; 动态数组 保存共享属性和等待这个资源的线程的一些信息 SHORT ActiveCount; 当前获得这个资源的线程总数 USHORT Flag; 状态标志位 PKSEMAPHORE SharedWaiters; 信号量对象,利用信号量实现共享资源计数的统计 PKEVENT ExclusiveWaiters; 事件对象,用来实现独占 OWNER_ENTRY OwnerThreads[2]; 自己本身有两个owner_entry 提升效率 ULONG ContentionCount; 发生竞争的次数 USHORT NumberOfSharedWaiters; 共享等待数 USHORT NumberOfExclusiveWaiters; 独占等待数 union { PVOID Address; ULONG_PTR CreatorBackTraceIndex; }; KSPIN_LOCK SpinLock; 保证处理这个结构时的同步} ERESOURCE, *PERESOURCE;typedef struct _OWNER_ENTRY { ERESOURCE_THREAD OwnerThread; 所有者线程 union { LONG OwnerCount; 引用计数 ULONG TableSize; };} OWNER_ENTRY, *POWNER_ENTRY;
这个对象在非换页池分配,读访问时可以共享读,写的时候独占写,这是一种优化策略
以独占方式释放后,共享请求和独占请求都被发出,先满足共享在满足独占
共享被释放后,只接受独占请求
1. 请求独占访问时,
如果ActiveCount为0,证明没有人在访问这个资源 给予分配资源,否则就进入等待ExclusiveWaiters,
如果等待超时,试图提升正在独占或者所有正在共享这个资源的优先级,以便使这些线程能够尽快释放这个资源
2. 请求共享访问
如果ActiveCount为0,则填充一个OwnerThreads返回
如果当前线程已经独占该资源,增加第一个owner_entry的引用计数
如果该资源已经共享,就查看当前线程是否在OwnerThreads 如果不在且没有其他线程在等待独占,则加入;如果在数组中只增加相应的引用计数
以下是需要等待或者失败的情况(这取决于调用者悬着是否等待 wait参数):
该资源已经被其他线程独占
该资源已经共享,当前线程不再owner的数组中,并且现在有一个正在请求独占的线程
这两种情况,都要把自己添加到OwnerTread中,然后等待信号量的释放
3. 释放资源
如果是独占方式访问,减少引用计数,如果已经是0,清除owner thread,检查是否有共享请求如果有就释放信号量,如果没有共享请求就看是否有独占请求、有就设置设置事件
如果之前是共享方式访问 处理相应的owner thread 如果引用计数是0 则清除掉owner thread。如果ActiveCount为0,就检查是否有独占访问请求,设置事件满足等待者。
下面看一下push lock
typedef struct _EX_PUSH_LOCK {union { struct { ULONG_PTR Locked : 1; 表示已经被锁 ULONG_PTR Waiting : 1; 表示有先线程正在等待 ULONG_PTR Waking : 1; 一个线程试图唤醒另外一个线程 ULONG_PTR MultipleShared : 1; 共享线程数 ULONG_PTR Shared : sizeof (ULONG_PTR) * 8 - 4; w位是0, 后28是共享计数, 否则后28指向一个等待块 }; ULONG_PTR Value; PVOID Ptr;};} EX_PUSH_LOCK, *PEX_PUSH_LOCK;后28指向一个等待块typedef struct DECLSPEC_ALIGN(16) _EX_PUSH_LOCK_WAIT_BLOCK { union { KGATE WakeGate; KEVENT WakeEvent; }; PEX_PUSH_LOCK_WAIT_BLOCK Next; PEX_PUSH_LOCK_WAIT_BLOCK Last; PEX_PUSH_LOCK_WAIT_BLOCK Previous; LONG ShareCount; LONG Flags;} DECLSPEC_ALIGN(16) EX_PUSH_LOCK_WAIT_BLOCK;
Push lock在无竞争情况下(w始终为0),可以有一下情况
独占获取 前提:所有位全是0 获取之后成功: L位变为1
共享获取 前提:L 1 shared存在 或者所有位是0,获取之后成功:L为1 shared++
独占释放 L变回0
共享释放 shared--,如果没有块之后 L变为0
在有竞争情况下:
独占获取 前提:L=1 W=0 动作:构造一等待块 放入shared 然后置w位 1
前提:L=1 W=1 动作:构造一等待块 放入shared
然后等待门对象
共享获取
前提:L=1 shared=0 意味着这是一个独占的 动作:构造等待块 w=1
前提 L=1 W= 1 shared有值 意味独占中 并且有等待共享的线程 动作:构造一个等待块放入
独占释放
尝试找到一个独占式请求,释放门对象唤醒线程,如果没有,再唤醒共享的线程
共享释放
释放请求独占的线程
实际在win的实现中 还要处理K m 两位。
- 内核中的同步机制(三)
- 内核中的同步机制(一)
- 内核中的同步机制(二)
- 同步机制--android中的同步机制(三)
- Linux内核中的同步机制
- Linux内核中的同步机制
- Linux内核中的同步机制
- Linux内核中的同步机制
- linux中的内核同步机制
- Linux内核同步机制之(三):memory barrier
- Linux内核同步机制之(三):memory barrier
- Linux内核同步机制之三------Seqlock
- 解析Linux内核的同步与互斥机制(三)
- Linux内核的同步机制(转载)
- Linux内核的同步机制 (一)
- 内核同步机制-读写信号量(rw_semaphore)
- 内核同步机制-信号量(semaphore)
- 内核的同步机制(原子锁)
- row_number() over (partition by....order by...)用法 一
- 库存数据的保存
- Facebook 的 PHP 性能与扩展性
- Silverlight学习笔记三:WCF跨域调用,实现登陆页
- UITapGestureRecognizer与UIPanGestureRecognizer应用
- 内核中的同步机制(三)
- 腾讯搜搜退出PC搜索领域:百度搜狗迎来双龙竞争
- 关于Android的多种屏幕适配
- Struts2 标签库讲解
- 电商价格战暴露后台危机 低价品缺货成促销手段
- Java中的反射机制
- 互联网手机潮进入PK时代:周鸿祎激战小米雷军
- 小米360口水战背后:国产手机第三态诞生
- Android设置TextView的Selector来控制点击的颜色