Windows事件等待学习笔记(二)—— 线程等待与唤醒

要点回顾

之前学习了如何自己实现临界区以及什么是自旋锁,这两种同步方案在线程无法进入临界区时都会让当前线程进入等待状态
一种是通过Sleep函数实现的,一种是通过让当前的CPU“空转”实现的,但这两种等待方式都有局限性:

  1. 通过Sleep函数进行等待,等待时间该如何确定呢?
  2. 通过“空转”的方式进行等待,只有等待时间很短的情况下才有意义,否则对CPU资源是种浪费,并且自旋锁只能在多核的环境下才有意义

思考:有没有更加合理的等待方式,只有在条件成熟的时候才将当前线程唤醒?

等待与唤醒机制

描述:在Windows中,一个线程可以通过等待一个或者多个可等待对象,从而进入等待状态,另一个线程可以在某些时刻唤醒等待这些对象的其他线程
在这里插入图片描述

可等待对象

在Windbg中查看以下结构体:

dt _KPROCESS

在这里插入图片描述

dt _KTHREAD

在这里插入图片描述

dt _KTIMER

在这里插入图片描述

dt _KSEMAPHORE

在这里插入图片描述

dt _KEVENT

在这里插入图片描述

dt _KMUTANT

在这里插入图片描述

dt _FILE_OBJECT

在这里插入图片描述


总结

  1. 可等待对象正常情况下都是以 _DISPATCHER_HEADER 结构体开头的,但是有一些特殊的结构体并不是以 _DISPATCHER_HEADER 开头的(如 _FILE_OBJECT),但是windows又希望把它们也变成所谓的可等待对象,因此在它们内部嵌入一个 _DISPATCHER_HEADER 这样的结构体
  2. 只要是包含 _DISPATCHER_HEADER 结构体的对象,都可以看作是可等待对象,都可以使用 WaitForSingleObjectWaitForMultipleObjects 这两个函数进入等待状态

可等待对象的差异

WaitForSingleObject(3环)

NtWaitForSingleObject(内核)

  1. 通过3环用户提供的句柄,找到等待对象的内核地址
  2. 如果_DISPATCHER_HEADER 开头,直接使用。
  3. 如果不是_DISPATCHER_HEADER 开头的对象,则找到在其中嵌入_DISPATCHER_HEADER 对象

KeWaitForSingleObject(内核) //核心功能

线程与等待对象

描述:一个线程可以等待一个对象,也可以等待多个对象

一个线程等待一个对象

在这里插入图片描述

实验
第一步:编译并运行以下代码
#include <stdio.h>
#include <windows.h>

HANDLE hEvent[2];

DWORD WINAPI ThreadProc(LPVOID lpParamter)
{
	::WaitForSingleObject(hEvent[0], -1);

	printf("ThreadProc函数执行\n");
	return 0;
}


int main(int argc, char* argv[])
{
	hEvent[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL);		//创建一个可等待对象 _KEVENT

	::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL);
	
	getchar();
	return 0;
}
第二步:在WinDbg中找到该进程

!process 0 0

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第三步:查看线程信息

查看 _KTHREAD
在这里插入图片描述
查看 _KWAIT_BLOCK

kd> dt _KWAIT_BLOCK 0x85f49278 
nt_400000!_KWAIT_BLOCK
   +0x000 WaitListEntry    : _LIST_ENTRY [ 0x86177180 - 0x86177180 ]
   +0x008 Thread           : 0x85f49208 _KTHREAD		//所属线程
   +0x00c Object           : 0x86177178 Void			//被等待对象的地址
   +0x010 NextWaitBlock    : 0x85f49278 _KWAIT_BLOCK	//单向循环链表,只有一个时指向自己
   +0x014 WaitKey          : 0							//等待块的索引(下标)
   +0x016 WaitType         : 1							//只等待一个等待块时置1
   														//等待所有等待对象符合条件时,置0

一个线程等待多个对象

在这里插入图片描述

实验
第一步:编译并运行以下代码
#include <stdio.h>
#include <windows.h>

HANDLE hEvent[2];

DWORD WINAPI ThreadProc(LPVOID lpParamter)
{
	::WaitForMultipleObjects(2, hEvent, FALSE, -1);

	printf("ThreadProc函数执行\n");
	return 0;
}


int main(int argc, char* argv[])
{
	hEvent[0] = ::CreateEvent(NULL, TRUE, FALSE, NULL);		//创建可等待对象
	hEvent[1] = ::CreateEvent(NULL, TRUE, FALSE, NULL);		//创建可等待对象

	::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL);
	
	getchar();
	return 0;
}
第二步:在WinDbg中找到该进程

!process 0 0

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第三步:查看线程信息

dt _KTHREAD 862b3340

在这里插入图片描述
共有两个等待块
在这里插入图片描述
注意:此时WaitType字段的值仍是1,这是因为只要有一个对象满足条件,当前线程就可以被唤醒(详见第一步代码部分)

第四步:查看等待块具体细节

代码中创建的对象为 _KEVENT,所以 _KWAIT_BLOCK +0x00C Object 指向 _KEVENT +0x000 _DISPATCHER_HEADER

kd> dt _DISPATCHER_HEADER 0x86015db8 
nt_400000!_DISPATCHER_HEADER
   +0x000 Type             : 0 ''
   +0x001 Absolute         : 0x21 '!'
   +0x002 Size             : 0x4 ''
   +0x003 Inserted         : 0x86 ''
   +0x004 SignalState      : 0n0
   +0x008 WaitListHead     : _LIST_ENTRY [ 0x862b33c8 - 0x862b33c8 ]

WaitListHead:双向链表,圈着所有等待块的WaitListEntry

等待网

在这里插入图片描述

总结

  1. 等待中的线程,一定在等待链表中(KiWaitListHead),同时也一定在这张网上(KTHREAD +5C的位置不为空)
  2. 线程通过调用WaitForSingleObject/WaitForMultipleObjects函数将自己挂到这张网上
  3. 线程什么时候会再次执行取决于其他线程何时调用相关函数,等待对象不同调用的函数也不同
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
cda备考学习学习笔记——基础知识篇(二)主要涉及了计算机科学与技术领域的基本概念和知识。 首先,它介绍了计算机网络的基础知识。网络是将多台计算机通过通信链路连接起来,使它们能够相互通信和共享资源的系统。笔记中详细介绍了网络的组成、拓扑结构和通信协议等重要内容。 其次,笔记还解释了计算机系统的基本组成。计算机系统由硬件和软件两部分组成,其中硬件包括中央处理器、存储器、输入输出设备等,而软件则分为系统软件和应用软件。笔记详细介绍了各种硬件和软件的功能和作用。 此外,笔记还对数据库管理系统进行了介绍。数据库管理系统是一种用于管理和组织数据的软件系统,它能够实现数据的存储、检索和更新等操作。笔记中详细介绍了数据库的概念、结构和操作等内容。 最后,笔记还包括了算法和数据结构的基础知识。算法是解决问题的一系列步骤和规则,而数据结构则是组织和存储数据的方式。笔记中介绍了常用的算法和数据结构,如排序算法、树和图等。 总之,通过学习CDA备考学习笔记中的基础知识篇(二),我们能够更好地理解计算机网络、计算机系统、数据库管理系统以及算法和数据结构等相关概念和知识。这些基础知识对于我们深入研究计算机科学与技术领域是非常重要的,也为我们日后的学习和工作奠定了坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值