内核式线程同步之waitable timer

简介:

waitable timer
      
顾名思义,就是隔一段时间被signaled的一种内核对象。waitable timer跟event对象一样可以在创建的时候指定reset方式,如果是manual-reset,那么当waitable timer对象被signaled时,所有等待这个对象的wait函数都会返回。如果是auto-reset那么就只有一个wait函数会返回。

创建完waitable timer对象后,必须通过SetWaitableTimer函数对它进行时间上的设置。时间格式是个问题,看下面代码

//  Declare our local variables.
HANDLE hTimer;
SYSTEMTIME st;
FILETIME ftLocal, ftUTC;
LARGE_INTEGER liUTC;

//  Create an auto-reset timer.
hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

//  First signaling is at January 1, 2002, at 1:00 P.M. (local time).
st.wYear         = 2002;  //  Year
st.wMonth        = 1;     //  January
st.wDayOfWeek    = 0;     //  Ignored
st.wDay          = 1;     //  The first of the month
st.wHour         = 13;    //  1PM
st.wMinute       = 0;     //  0 minutes into the hour
st.wSecond       = 0;     //  0 seconds into the minute
st.wMilliseconds = 0;     //  0 milliseconds into the second

SystemTimeToFileTime(&st, &ftLocal);

//  Convert local time to UTC time.
LocalFileTimeToFileTime(&ftLocal, &ftUTC);
//  Convert FILETIME to LARGE_INTEGER because of different alignment.
liUTC.LowPart  = ftUTC.dwLowDateTime;
liUTC.HighPart = ftUTC.dwHighDateTime;

//  Set the timer.
SetWaitableTimer(hTimer, &liUTC, 6 * 60 * 60 * 1000, NULL, NULL, FALSE);

 

上面的代码查下MSDN应该很容易理解,这里要说的是CPU对齐的问题。FILETIME结构必须位于32位边界,而LARGE_INTEGER必须位于64位边界,所以不能将FILETIME直接传给SetWaitableTimer。

SetWaitableTimer也可以使用时间的绝对值,或者使用相对时间值。不过这时的值必须是负的。看下面代码:

//  Declare our local variables.
HANDLE hTimer;
LARGE_INTEGER li;

//  Create an auto-reset timer.
hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

//  Set the timer to go off 5 seconds after calling SetWaitableTimer.
//  Timer unit is 100-nanoseconds.
const  int nTimerUnitsPerSecond = 10000000;

//  Negate the time so that SetWaitableTimer knows we 
//  want relative time instead of absolute time.
//  This indicate that the timer will be signaled 5 seconds after the call to SetWaitableTimer
li.QuadPart = -(5 * nTimerUnitsPerSecond); 
 
//  Set the timer.
SetWaitableTimer(hTimer, &li, 6 * 60 * 60 * 1000, NULL, NULL, FALSE);

清除waitable timer对象需要用到CancelWaitableTimer函数。

特别提出的是waitable timer这节引出了一个新概念:APC(asynchronous procedure call)。按照我的理解,APC应该是线程特有的一个队列,里面装的是函数地址。如果一个函数地址被装入APC,如果这时线程处于待命的等待状态(alertable wait),那么这个线程就会被唤醒去调用APC里的函数;否则,APC里的函数地址就会被忽略掉。这里的这个线程指的是调用SetWaitableTimer的线程。下面的代码能说明问题

VOID APIENTRY TimerAPCRoutine(PVOID pvArgToCompletionRoutine,
ExpandedBlockStart.gif   DWORD dwTimerLowValue, DWORD dwTimerHighValue)  {

   FILETIME ftUTC, ftLocal;
   SYSTEMTIME st;
   TCHAR szBuf[256];

   // Put the time in a FILETIME structure.
   ftUTC.dwLowDateTime = dwTimerLowValue;
   ftUTC.dwHighDateTime = dwTimerHighValue;

   // Convert the UTC time to the user's local time.
   FileTimeToLocalFileTime(&ftUTC, &ftLocal);

   // Convert the FILETIME to the SYSTEMTIME structure
   
// required by GetDateFormat and GetTimeFormat.
   FileTimeToSystemTime(&ftLocal, &st);

   // Construct a string with the 
   
// date/time that the timer went off.
   GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, 
      &st, NULL, szBuf, sizeof(szBuf) / sizeof(TCHAR));
   _tcscat(szBuf, _ _TEXT(" "));
   GetTimeFormat(LOCALE_USER_DEFAULT, 0,
      &st, NULL, _tcschr(szBuf, 0), 
      sizeof(szBuf) / sizeof(TCHAR) - _tcslen(szBuf));

   // Show the time to the user.
   MessageBox(NULL, szBuf, "Timer went off at", MB_OK);
}


ExpandedBlockStart.gif void SomeFunc()  {
   // Create a timer. (It doesn't matter whether it's manual-reset 
   
// or auto-reset.)
   HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL);

   // Set timer to go off in 5 seconds.
ExpandedSubBlockStart.gif
   LARGE_INTEGER li = { 0 };
   SetWaitableTimer(hTimer, &li, 5000, TimerAPCRoutine, NULL, FALSE);

   // Wait in an alertable state for the timer to go off.
   SleepEx(INFINITE, TRUE);

   CloseHandle(hTimer);
}

如果指定了APC,那么就不要等待这个waitable timer对象了,因为APC队列会唤醒线程的,不需要wait函数。

目录
相关文章
|
24天前
|
消息中间件 存储 算法
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
68 0
|
3月前
|
Go 调度
go-issues#14592 runtime: let idle OS threads exit 内核线程暴增与线程回收问题
go-issues#14592 runtime: let idle OS threads exit 内核线程暴增与线程回收问题
25 0
|
3月前
|
Linux
Linux进程与线程的内核实现
task_struct称为进程描述符结构,该结构定义在文件中。进程描述符中包含一个具体进程的所有信息 进程描述符中包含的数据能完整地描述一个正在执行的程序:它打开的文件,进程的地址空间,挂起的信号,进程的状态等
36 0
Linux进程与线程的内核实现
|
3月前
|
存储 算法 Linux
一起聊聊内核中的线程:操作函数、进程状态、task_struct、举个例子、
一起聊聊内核中的线程:操作函数、进程状态、task_struct、举个例子、
62 0
|
4月前
|
存储 安全 Linux
Linux中断(tasklet,工作队列,内核线程的使用)
Linux中断(tasklet,工作队列,内核线程的使用)
35 0
|
4月前
|
监控 安全 API
6.9 Windows驱动开发:内核枚举进线程ObCall回调
在笔者上一篇文章`《内核枚举Registry注册表回调》`中我们通过特征码定位实现了对注册表回调的枚举,本篇文章`LyShark`将教大家如何枚举系统中的`ProcessObCall`进程回调以及`ThreadObCall`线程回调,之所以放在一起来讲解是因为这两中回调在枚举是都需要使用通用结构体`_OB_CALLBACK`以及`_OBJECT_TYPE`所以放在一起来讲解最好不过。
41 1
6.9 Windows驱动开发:内核枚举进线程ObCall回调
|
4月前
|
网络协议 安全 API
9.9 Windows驱动开发:内核远程线程实现DLL注入
在笔者上一篇文章`《内核RIP劫持实现DLL注入》`介绍了通过劫持RIP指针控制程序执行流实现插入DLL的目的,本章将继续探索全新的注入方式,通过`NtCreateThreadEx`这个内核函数实现注入DLL的目的,需要注意的是该函数在微软系统中未被导出使用时需要首先得到该函数的入口地址,`NtCreateThreadEx`函数最终会调用`ZwCreateThread`,本章在寻找函数的方式上有所不同,前一章通过内存定位的方法得到所需地址,本章则是通过解析导出表实现。
66 0
9.9 Windows驱动开发:内核远程线程实现DLL注入
|
4月前
|
监控 安全 API
7.1 Windows驱动开发:内核监控进程与线程回调
在前面的文章中`LyShark`一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以`监控进程线程`创建为例,在`Win10`系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现,此类函数的原理是创建一个回调事件,当有进程或线程被创建或者注销时,系统会通过回调机制将该进程相关信息优先返回给我们自己的函数待处理结束后再转向系统层。
58 0
7.1 Windows驱动开发:内核监控进程与线程回调
|
4月前
|
监控 Windows
4.4 Windows驱动开发:内核监控进程与线程创建
当你需要在Windows操作系统中监控进程的启动和退出时,可以使用`PsSetCreateProcessNotifyRoutineEx`函数来创建一个`MyCreateProcessNotifyEx`回调函数,该回调函数将在每个进程的创建和退出时被调用。PsSetCreateProcessNotifyRoutineEx 用于在系统启动后向内核注册一个回调函数,以监视新进程的创建和退出,
39 0
4.4 Windows驱动开发:内核监控进程与线程创建
|
5月前
|
存储 安全 调度
4.2 Windows驱动开发:内核中进程线程与模块
内核进程线程和模块是操作系统内核中非常重要的概念。它们是操作系统的核心部分,用于管理系统资源和处理系统请求。在驱动安全开发中,理解内核进程线程和模块的概念对于编写安全的内核驱动程序至关重要。内核进程是在操作系统内核中运行的程序。每个进程都有一个唯一的进程标识符(PID),它用于在系统中唯一地标识该进程。在内核中,进程被表示为一个进程控制块(PCB),它包含有关进程的信息,如进程状态、优先级、内存使用情况等。枚举进程可以让我们获取当前系统中所有正在运行的进程的PID和其他有用的信息,以便我们可以监视和管理系统中的进程。
63 0
4.2 Windows驱动开发:内核中进程线程与模块