马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
临界区&自旋锁
这两个章节在”多核同步“篇已经学习过了,必要相识的可以自行检察对应章节。
线程期待与叫醒
我们在之前的课程内里相识了怎样自己实现临界区以及什么是Windows自旋锁,这两种同步方案在线程无法进入临界区时都会让当火线程进入期待状态。
一种是通过Sleep函数实现的,一种是通过让当前的CPU”空转“实现的,但这两种期待方式都有范围性:
- 通过Sleep函数举行期待,并没有办法确定具体期待的时间,有大概出现期待过长或过短的环境;
- 通过让CPU”空转“举行期待,只有在期待变乱很短的环境下才故意义,否则空转时间过长,对CPU资源来说就是一种浪费,而且自旋锁的方式只在多核的环境下存在。
我们发现如上所说的两种方案,都是由于期待条件的不成熟,所产生的范围。
期待与叫醒机制
在Windows中,一个线程可以通过期待一个大概多个可期待对象,从而进入期待状态,另一个线程可以在某些时候叫醒期待这些对象的其他线程,这就是Windows的期待与叫醒机制。
可期待对象
所谓可期待对象就是结构体,如下是一些结构体,我们可以在Windbg中检察这些结构体。
在Windbg中检察结构体,我们会发现这些结构体的第一个成员都是_DISPATCHER_HEADER,也就表现第一个成员为_DISPATCHER_HEADER的结构体就是可期待对象。
除了这个以外,在Windows中另有一些特别的结构体,也称之为可期待对象,如_FILE_OBJECT,我们可以瞥见该结构体的第一个成员就不是_DISPATCHER_HEADER,但是在它的0x5C偏移位成员有一个_KEVENT结构体,这个结构体是一个可期待对象,因此_FILE_OBJECT也是一个可期待对象。
因此,综上所述我们可以知道只要结构体中成员有_DISPATCHER_HEADER,或包罗了_DISPATCHER_HEADER结构体的,我们都可以称之为可期待对象。
差别
固然以上所述的两种范例结构体都称之为可期待对象,但两者之间也是有差别的。差别在期待函数调用过程中表现出来。
当我们使用WaitForSingleObject函数时,进入内核函数NtWaitForSingleObject,这个内核函数会通过3环用户提供的句柄找到期待对象的内核所在;然后判定期待对象的第一个成员是否是_DISPATCHER_HEADER,如果是的话则直接使用;如果不是的话,则去期待对象中找到嵌入的_DISPATCHER_HEADER对象。末了再将找到的对象所在作为参数调用KeWaitForSingleObject函数,该函数焦点功能会在后续章节中学习。
期待块
一个线程可以期待一个或多个对象,线程与期待对象创建接洽重要通过期待块,我们可以做个实行来看一下。
一个线程期待一个对象
起首我们可以在XP中编译如许一段代码,来看一下一个线程期待一个对象的环境:
#include <windows.h>
#include <stdio.h>
HANDLE hEvent[2];
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
::WaitForSingleObject(hEvent[0], -1);
printf("ThreadProc函数实行...\n");
return 0;
}
int main(int argc, char* argv[])
{
hEvent[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL);
::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL);
getchar();
return 0;
}
运行步调之后,在Windbg停止一下,然后通过如下图中的指令找到当进步程中正在期待的线程所在:
接着我们以线程结构体的方式代入线程所在检察,找到0x5C偏移位成员,即期待块:
期待块是一个_KWAIT_BLOCK结构体,它将线程与被期待对象接洽到了一起,我们接着来看一下它的成员寄义:
nt!_KWAIT_BLOCK
+0x000 WaitListEntry : _LIST_ENTRY // 稍后相识
+0x008 Thread : Ptr32 _KTHREAD // 当火线程所在
+0x00c Object : Ptr32 Void // 期待对象的所在(当前实行中为_KEVENT)
+0x010 NextWaitBlock : Ptr32 _KWAIT_BLOCK // 下一个期待块所在,这是一个单向循环链表,存储的是与当火线程关联的多个期待块结构体所在,如果只有一个期待块则该所在指向当前期待快所在
+0x014 WaitKey : Uint2B // 期待块的索引,当前为第一个期待块,因此该值为0
+0x016 WaitType : Uint2B // 期待范例,若当前只要有一个期待对象符合条件就可以使得线程被叫醒,那么该值就是1;如果你期待多个对象必须全部符合条件才可以使得线程被叫醒,那么该值0
综上所述,我们可以使用如下图来表现一个线程期待一个对象的环境:
一个线程期待多个对象
一个线程期待多个对象的环境,我们必要将代码举行修改,如下代码所示,我们添加两个可等
|