原文发表于百度空间,2009-04-04
==========================================================================

分析了Windows句柄表的分配算法之后,综合WRK1.2中的代码以及Window XP下的实践,继续分析句柄的分配算法~~
为了便于描述,先定义几个概念:FreeHandle即尚未被使用的Handle,FreeHandleList是由FreeHandle依靠HandleTableEntry->NextFreeTableEntry组成的一个链表
在分析句柄表的分配算法时,已经注意到系统对每个一级句柄表进行填充,把所有可用句柄串联起来形成一个链表,称之为FreeHandleList,该链表也可以看成是一个FreeHandle队列.而HANDLE_TABLE结构中的FirstFree则始终指向该队列头部.
看下面这个图来回顾一下:

首先分析句柄的创建,对应的函数是ExCreateHandle
NTKERNELAPI
HANDLE //返回值,即创建好的句柄

ExCreateHandle (
__inout PHANDLE_TABLE HandleTable, //句柄表
__in PHANDLE_TABLE_ENTRY HandleTableEntry //HANDLE_TABLE_ENTRY,里面包含了对象信息
);

而ExCreateHandle的主要功能是调用ExpAllocateHandleTableEntry实现的,然后把传进来的对象及属性放入申请到的HANDLE_TABLE_ENTRY里.这很容易理解,创建句柄的过程其实就是把传进来的对象放入目标句柄表中,然后计算出它在句柄表中的索引作为句柄返回就可以了.要放置对象,就必须要有一个有效的HANDLE_TABLE_ENTRY,而申请工作就是由ExpAllocateHandleTableEntry完成的.

PHANDLE_TABLE_ENTRY
ExpAllocateHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
OUT PEXHANDLE pHandle
)
/*++
Routine Description:
This routine does a fast allocate of a free handle. It's lock free if
possible.
Only the rare case of handle table expansion is covered by the handle
table lock.
Arguments:
HandleTable - Supplies the handle table being allocated from.
pHandle - Handle returned
Return Value:
PHANDLE_TABLE_ENTRY - The allocated handle table entry pointer or NULL
on failure.
--*/
{
PKTHREAD CurrentThread;
ULONG OldValue, NewValue, NewValue1;
PHANDLE_TABLE_ENTRY Entry;
EXHANDLE Handle;
BOOLEAN RetVal;
ULONG Idx;
CurrentThread = KeGetCurrentThread ();
while () {//这是一个循环过程
OldValue = HandleTable->FirstFree; //取当前FirstFree的值,这是FreeHandleList的队列头
while (OldValue == ) { //判断是否为0.若为0则表示FreeHandleList已经用完了,需要进行一些处理
//
// Lock the handle table for exclusive access as we will be
// allocating a new table level.
//
ExpLockHandleTableExclusive (HandleTable, CurrentThread); //锁定句柄表
//
// If we have multiple threads trying to expand the table at
// the same time then by just acquiring the table lock we
// force those threads to complete their allocations and
// populate the free list. We must check the free list here
// so we don't expand the list twice without needing to.
//
OldValue = HandleTable->FirstFree; //再取FirstFree的值,这里考虑到多个线程访问同一个句柄表的情况,可能我们这次得到的值不为0.因为在这期间可能已经有线程触发了扩充句柄表的操作
if (OldValue != ) { //这时如果FirstFree不为0,说明句柄表已经扩充,有FreeHandle了,那就解锁退出循环
ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
break;
}
//如果仍然没有可用句柄,继续下面的操作
//
// See if we have any handles on the alternate free list
// These handles need some locking to move them over.
//
OldValue = ExpMoveFreeHandles (HandleTable);//对句柄表进行整理,至于如何整理稍后分析.
if (OldValue != ) {//若不为0,表明整理之后又有了可用句柄,解锁退出循环,进行真正处理
ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
break;
}
//
// This must be the first thread attempting expansion or all the
// free handles allocated by another thread got used up in the gap.
//
//确实没有可用句柄(句柄表已满)时的处理
RetVal = ExpAllocateHandleTableEntrySlow (HandleTable, TRUE);//扩充句柄表,细节在分析句柄表分配算法时已分析
ExpUnlockHandleTableExclusive (HandleTable, CurrentThread);
OldValue = HandleTable->FirstFree;//再取此时的FirstFree的值,如果上面的扩充是成功的,此时应该有可用句柄
//
// If ExpAllocateHandleTableEntrySlow had a failed allocation
// then we want to fail the call. We check for free entries
// before we exit just in case they got allocated or freed by
// somebody else in the gap.
//
if (!RetVal) { //若此if成立则说明ExpAllocateHandleTableEntrySlow在扩充句柄表时操作失败,那就直接返回失败
if (OldValue == ) {
pHandle->GenericHandleOverlay = NULL;
return NULL;
}
}
}
//OldValue的值来自HandleTable->FirstFree
Handle.Value = (OldValue & FREE_HANDLE_MASK); //给新句柄赋值
Entry = ExpLookupHandleTableEntry (HandleTable, Handle); //查找新句柄对应的HANDLE_TABLE_ENTRY,用来放置对象,至此我们有了一个可用的HANDLE_TABLE_ENTRY
Idx = ((OldValue & FREE_HANDLE_MASK)>>) % HANDLE_TABLE_LOCKS;
ExpLockHandleTableShared (HandleTable, CurrentThread, Idx); //锁定句柄表
if (OldValue != *(volatile ULONG *) &HandleTable->FirstFree) { //再比较一下,若两者不相等,则可能又有线程在此期间进行了句柄申请或释放操作,为避免出错,下一次循环再试
ExpUnlockHandleTableShared (HandleTable, CurrentThread, Idx);
continue;
}
KeMemoryBarrier ();
NewValue = *(volatile ULONG *) &Entry->NextFreeTableEntry; //取新申请到的HANDLE_TABLE_ENTRY中的NextFreeTableEntry.也就是当前申请到的Handle所指向的下一个FreeHandle
NewValue1 = InterlockedCompareExchange ((PLONG)&HandleTable->FirstFree,
NewValue,
OldValue);//修改HandleTable->FirstFree的值为下一个FreeHandle,由前面的操作可知,相当于从FirstFree所指向的FreeHandleList队列头部取走了一个FreeHandle,并且把FirstFree指向新的头部
ExpUnlockHandleTableShared (HandleTable, CurrentThread, Idx); //解锁
if (NewValue1 == OldValue) {//NewValue存放的是原来的FirstFree
EXASSERT ((NewValue & FREE_HANDLE_MASK) < HandleTable->NextHandleNeedingPool);
break;
} else {
//
// We should have eliminated the A-B-A problem so if only the sequence number has
// changed we are broken.
//
EXASSERT ((NewValue1 & FREE_HANDLE_MASK) != (OldValue & FREE_HANDLE_MASK));
}
}
InterlockedIncrement (&HandleTable->HandleCount); //当前句柄表的计数增加一
*pHandle = Handle; //把当前新句柄返回 return Entry;
}

现在对句柄的分配应该有了一个大致的了解.每次分配句柄时总是从FreeHandleList队列头取走第一个FreeHandle,依次类推

再来看句柄销毁的过程:
句柄的销毁由ExDestroyHandle来完成.

NTKERNELAPI
BOOLEAN
ExDestroyHandle (
__inout PHANDLE_TABLE HandleTable,
__in HANDLE Handle,
__inout_opt PHANDLE_TABLE_ENTRY HandleTableEntry
);

该函数的内部关键操作如下:

HandleTableEntry = ExpLookupHandleTableEntry( HandleTable, LocalHandle ); //根据传入句柄找到对应的HANDLE_TABLE_ENTRY
Object = InterlockedExchangePointer (&HandleTableEntry->Object, NULL);//把Object清零
ExpFreeHandleTableEntry( HandleTable,
LocalHandle,
HandleTableEntry ); //在指定的句柄表中释放指定的Handle对应的HANDLE_TABLE_ENTRY
这里的释放并不是释放空间的意思,而是使该HANDLE_TABLE_ENTRY重新变得可用,下面重点分析ExpFreeHandleTableEntry. VOID
ExpFreeHandleTableEntry (
IN PHANDLE_TABLE HandleTable,
IN EXHANDLE Handle,
IN PHANDLE_TABLE_ENTRY HandleTableEntry
)
/*++
Routine Description:
This worker routine returns the specified handle table entry to the free
list for the handle table.
Note: The caller must have already locked the handle table
Arguments:
HandleTable - Supplies the parent handle table being modified
Handle - Supplies the handle of the entry being freed
HandleTableEntry - Supplies the table entry being freed
Return Value:
None.
--*/
{
PHANDLE_TABLE_ENTRY_INFO EntryInfo;
ULONG OldFree, NewFree, *Free;
PKTHREAD CurrentThread;
ULONG Idx;
ULONG SeqInc;
PAGED_CODE();
//一些断言和检测
EXASSERT (HandleTableEntry->Object == NULL);
EXASSERT (HandleTableEntry == ExpLookupHandleTableEntry (HandleTable, Handle));
//
// Clear the AuditMask flags if these are present into the table
//
EntryInfo = ExGetHandleInfo(HandleTable, Handle.GenericHandleOverlay, TRUE);
if (EntryInfo) {
EntryInfo->AuditMask = ;
}
//
// A free is simply a push onto the free table entry stack, or in the
// debug case we'll sometimes just float the entry to catch apps who
// reuse a recycled handle value.
//
InterlockedDecrement (&HandleTable->HandleCount); //释放时,句柄表中句柄计数减一
CurrentThread = KeGetCurrentThread ();
NewFree = (ULONG) Handle.Value & ~(HANDLE_VALUE_INC - ); //计算要销毁的句柄的值(掩去了低两位)
#if DBG
if (ExReuseHandles) {
#endif //DBG
if (!HandleTable->StrictFIFO) { //判断StrictFIFO标记,该标记直接影响到句柄的分配算法
//
// We are pushing potentially old entries onto the free list.
// Prevent the A-B-A problem by shifting to an alternate list
// read this element has the list head out of the loop.
//
//这里是StrictFIFO=0的处理方法,普通进程的句柄表的StrictFIFO是等于0的
Idx = (NewFree>>) % HANDLE_TABLE_LOCKS;
if (ExTryAcquireReleasePushLockExclusive (&HandleTable->HandleTableLock[Idx])) { //锁定
SeqInc = GetNextSeq();
Free = &HandleTable->FirstFree; //Free指向了FirstFree
} else {
SeqInc = ;
Free = &HandleTable->LastFree; //若锁定失败,则Free指向LastFree.实际上,上面的操作通常是成功的
}
} else {//这里是StrictFIFO=1的处理方法,PspCidTable设置了此标志
SeqInc = ; //增量为0
Free = &HandleTable->LastFree; //Free指向LastFree
}
while () {//这是一个循环尝试操作
OldFree = ReadForWriteAccess (Free); //读取Free所指向的值,即当前FreeHandleList的队列头
HandleTableEntry->NextFreeTableEntry = OldFree; //把目标HANDLE_TABLE_ENTRY的NextFreeTableEntry指向原来的队列头
if ((ULONG)InterlockedCompareExchange ((PLONG)Free,
NewFree + SeqInc,
OldFree) == OldFree) {//Free指向队列头,设置新的队列头为刚释放的句柄.这个操作相当于在FreeHandleList队列头部插入了一个新元素
EXASSERT ((OldFree & FREE_HANDLE_MASK) < HandleTable->NextHandleNeedingPool);
break;
}
}
#if DBG
} else {
HandleTableEntry->NextFreeTableEntry = ;
}
#endif //DBG
return;
}

最新文章

  1. 数据结构杂谈(二)简单有趣的地精排序Gnome sort
  2. Xamarin 开发常见问题
  3. Building Local Unit Tests
  4. 以不同用户身份运行程序,/savecred只需要输入一次密码(GetTokenByName取得EXPLORER.EXE的令牌,然后调用CreateProcessAsUser,而且使用LoadUserProfile解决另存文件的问题)good
  5. 今天工作中遇到的根据用户id取得产品大类和相关小类的问题
  6. Java项目 打war包方法
  7. iOS之UIWebView无法获取web标题
  8. hadoop2.x源码编译
  9. django 模板继承与重写
  10. 大数据批量导入,解决办法,实践从定时从 sqlserver 批量同步数据到 mySql
  11. Bootstrap3.0学习第二轮(栅格系统原理)
  12. Http_code码
  13. HTML相关知识点总结
  14. xargs用法
  15. Guideline 2.5.1 - Performance - Software Requirements
  16. 房产地图google map的初步应用点滴.2)(转)
  17. 访问localhost文件下的testmysql.php文件报Not Found
  18. 2、Android-UI(关于Nine-Patch图片)
  19. maven 手动安装jar包
  20. Informatica 常用组件Source Qualifier之三 联接查询

热门文章

  1. Centos常用命名
  2. Ubuntu 16.04下在Shell终端下使用nautilus快速打开窗口文件夹
  3. uibutton去掉点击后背景有阴影的方法
  4. python异常捕获异常堆栈输出
  5. SolidEdge 如何绘制辅助视图
  6. leetcode 刷题之路 68 Gas Station
  7. ubuntu如何修改root密码
  8. 【转载】.NET Remoting学习笔记(二)激活方式
  9. [转载]C函数的实现(strcpy,atoi,atof,itoa,reverse)
  10. 使用string实现一个用于储存那些太大而无法使用 long long 的数