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

  • 要点回顾
  • 等待与唤醒机制
    • 可等待对象
    • 可等待对象的差异
  • 线程与等待对象
    • 一个线程等待一个对象
      • 实验
        • 第一步:编译并运行以下代码
        • 第二步:在WinDbg中找到该进程
        • 第三步:查看线程信息
    • 一个线程等待多个对象
      • 实验
        • 第一步:编译并运行以下代码
        • 第二步:在WinDbg中找到该进程
        • 第三步:查看线程信息
        • 第四步:查看等待块具体细节
    • 等待网
    • 总结

要点回顾

之前学习了如何自己实现临界区以及什么是自旋锁,这两种同步方案在线程无法进入临界区时都会让当前线程进入等待状态
一种是通过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. 线程什么时候会再次执行取决于其他线程何时调用相关函数,等待对象不同调用的函数也不同

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

  1. Windows编程课程学习笔记

    一. Windows程序内部运行机制--Windows编程课程学习笔记 二. MFC框架程序分析--Windows编程课程学习笔记 三. 简单绘图--Windows编程课程学习笔记 四. 文本编程-- ...

  2. Windows事件等待学习笔记(四)—— 事件信号量互斥体

    Windows事件等待学习笔记(四)-- 事件&信号量&互斥体 要点回顾 事件 实验:验证SignalState 第一步:编译并运行以下代码 第二步:观察结果 第三步:修改代码并执行 ...

  3. Windows事件等待学习笔记(一)—— 临界区自旋锁

    Windows事件等待学习笔记(一)-- 临界区&自旋锁 基础知识 演示代码 案例一 案例二 LOCK 单行代码原子操作 多行代码原子操作 临界区 演示代码 手动实现 自旋锁 分析 KeAcq ...

  4. Windows事件等待学习笔记(三)—— WaitForSingleObject函数分析

    Windows事件等待学习笔记(三)-- WaitForSingleObject函数分析 要点回顾 WaitForSingleObject NtWaitForSingleObject KeWaitFo ...

  5. Windows消息机制学习笔记(二)—— 窗口与线程

    Windows消息机制学习笔记(二)-- 窗口与线程 要点回顾 消息从哪里来? 实验一:Spy++捕获消息 实验二:消息捕获 消息到哪里去? 窗口在哪? 实验:分析CreateWindowExW 窗口 ...

  6. oracle job enq tx,【学习笔记】Oracle等待事件 enq:TX–allocate ITL entry产生原因和解决办法...

    天萃荷净 运维DBA反映Oracle数据库出现enq:TX–allocate ITL entry等待事件,结合案例分析该等待事件产生原因和解决办法 今天在分析一份awr中发现了较为明显的enq: TX ...

  7. Windows x64内核学习笔记(二)—— IA-32e模式

    Windows x64内核学习笔记(二)-- IA-32e模式 IA-32e模式 模式检测 强制平坦段 任务切换 中断门描述符 FS / GS 模式切换 32位程序进内核 64位程序进内核 实验:模式 ...

  8. Windows内存管理学习笔记(二)—— 物理内存的管理

    Windows内存管理学习笔记(二)-- 物理内存的管理 物理内存 实验一:理解MmNumberOfPhysicalPages MmPfnDatabase _MMPFN 物理页状态 六个链表 实验二: ...

  9. java队列等待唤醒_Java深入学习29:线程等待和唤醒的两个方案

    Java深入学习29:线程等待和唤醒的两个方案 模拟场景 一个门店,有一个店员,有消费者来消费商品(每次消费1件商品),有仓库人员来添加(生产)商品(每次生产1件商品),并假设库存上限是2. 基础代码 ...

最新文章

  1. Android 获取 屏幕状态栏高度和标题栏高度 避免出现0的情况
  2. Algs4-1.5.1使用quick-find算法处理序列
  3. 关于自动布局更新约束方法的总结
  4. android studio创建文件,如何在Android Studio中创建File Templates
  5. JDK 11 将引入低延迟 GC,大幅度缩短 GC 暂停时长
  6. 前后端分离重复提交_java+react前后端分离项目处理重复提交问题
  7. 广东省的盆友们,这波退税及时雨你赶上了吗?
  8. 好程序员web前端分享逻辑运算
  9. WordPress缓存插件WP-Super-Cache安装使用
  10. mysql inner 连接多表_MySQL数据库之多表查询inner join内连接
  11. 记录数据库面试题及答案21~41
  12. 使用多个路由器有线桥接实现无线漫游
  13. SAP汇率转换函数[BAPI_EXCHANGERATE_GETDETAIL]
  14. api文档 luci_openwrt中luci学习笔记
  15. 制造并批量生产现实版“储物戒指”
  16. Win10切换到了Users用户怎么切换回来
  17. Maven引包问题.lastUpdated
  18. 根据表格背景色统计表格数量
  19. OpenCV-Python投影透视变换函数getPerspectiveTransform及warpPerspective详解
  20. 4G、5G多卡聚合技术在打造智慧城市中的解决方案

热门文章

  1. Py之Numpy:Numpy库中常用函数的简介、应用之详细攻略
  2. LSTM:《Understanding LSTM Networks》的翻译并解读
  3. DL之BN-Inception:BN-Inception算法的简介(论文介绍)、架构详解、案例应用等配图集合之详细攻略
  4. CV:基于Keras利用CNN主流架构之mini_XCEPTION训练性别分类模型hdf5并保存到指定文件夹下
  5. Py:递归求解汉诺塔,简单的几行编程可以搞定很高层的三柱汉诺塔游戏
  6. (原创)7-1 银行业务队列简单模拟 (30 分)
  7. 快速创建springBoot
  8. python---memcache使用操作
  9. windows系统中hosts文件位置
  10. Linux Kernel系列一:开篇和Kernel启动概要