线程的挂起和恢复

DWORD SuspendThread ( HANDLE hThread );   //挂起线程

DWORD ResumeThread ( HANDLE hThread );   //恢复线程

SuspendThreadResumeThread 都返回之前的挂起计数。

一个线程最多可以挂起MAXIMUM_SUSPEND_COUNT (WinNT.h中定义为127次)。

进程的挂起和恢复

对于Windows来说,不存在暂停或恢复进程的概念,因为进程从来不会被安排获得cpu时间。

但是我们可以创建一个函数,用来挂起或者恢复进程中的全部线程,这样就能挂起或者恢复一个进程了。

参考代码如下:

#include <Windows.h>

#include <stdio.h>

#include <Tlhelp32.h>

//dwProcessID参数为需要挂起或者恢复的进程ID

// bSuspend参数如果为TRUE就挂起进程,否则恢复进程

void SuspendProcess(DWORD dwProcessID, BOOL bSuspend)

{

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);                    //获得系统内所以线程

if (hSnapshot != INVALID_HANDLE_VALUE)

{

THREADENTRY32 te;

ZeroMemory(&te, sizeof(te));

te.dwSize = sizeof(te);

BOOL bOK = Thread32First(hSnapshot, &te);

for (; bOK; bOK = Thread32Next(hSnapshot, &te))

{

if (te.th32OwnerProcessID == dwProcessID)          //必须制定,否则程序会尝试挂起系统内所有的线程,就会导致死机

{

HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);

if (hThread != NULL)

{

if (bSuspend)

{

SuspendThread(hThread);

}

else

ResumeThread(hThread);

}

CloseHandle(hThread);

}

}

}

CloseHandle(hSnapshot);

}

int main(void)

{

SuspendProcess(9636, FALSE);

return 0;

}

睡眠

VOID Sleep (DWORD dwMilliseconds);

这个函数将使线程自己挂起 dwMilliseconds 长的时间。

1.       调用Sleep,可使线程自愿放弃它剩余的时间片。

2. 系统将在大约的指定毫秒数内使线程不可调度。不错,如果告诉系统,想睡眠 100ms,那么可以睡眠大约这么长时间,但是也可能睡眠数秒钟或者数分钟。记住,Windows不是个实时操作系统。虽然线程可能在规定的时间被唤醒,但是它能否做到,取决于系统中还有什么操作正在进行。

3.可以调用Sleep,并且为dwMilliseconds参数传递INFINITE。这将告诉系统永远不要调度该线程。这不是一件值得去做的事情。最好是让线程退出,并还原它的堆栈和内核对象。

4. 可以将0传递给Sleep。这将告诉系统,调用线程将释放剩余的时间片,并迫使系统调度另一个线程。但是,系统可以对刚刚调用 Sleep的线程重新调度。Sleep(0)是指CPU交出当前线程的执行权,让CPU去执行其他线程。也就是放弃当前线程的时间片,转而执行其他线程

切换到另一个线程

BOOL SwitchToThread (void);

当调用这个函数的时候,系统要查看是否存在一个迫切需要CPU时间的线程。如果没有线程迫切需要CPU时间,SwitchToThread就会立即返回。如果存在一个迫切需要 CPU时间的线程,SwitchToThread就对该线程进行调度(该线程的优先级可能低于调用 SwitchToThread的线程)。

这个迫切需要CPU时间的线程可以运行一个时间段,然后系统调度程序照常运行。该函数允许一个需要资源的线程强制另一个优先级较低、而目前却拥有该资源的线程放弃该资源。如果调用 SwitchToThread函数时没有其他线程能够运行,那么该函数返回 FALSE,否则返回一个非0值。

Sleep():时间片只能让给优先级相同或更高的线程;

SwitchToThread():只要有可调度线程,即便优先级较低,也会让其调度。

在实际上下文中谈CONTEXT结构

CONTEXT结构包括以下部分:

  CONTEXT_CONTROL:包含CPU的控制寄存器,比如指今指针,堆栈指针,标志和函数返回地址..AX, BX, CX, DX, SI, D
     CONTEXT_INTEGER:用于标识CPU的整数寄存器.DS, ES, FS, GS
   CONTEXT_FLOATING_POINT:用于标识CPU的浮点寄存器.
     CONTEXT_SEGMENTS:用于标识CPU的段寄存器.SS:SP, CS:IP, FLAGS, BP
   CONTEXT_DEBUG_REGISTER:用于标识CPU的调试寄存器. 
   CONTEXT_EXTENDED_REGISTERS:用于标识CPU的扩展寄存器I
   CONTEXT_FULL:相当于CONTEXT_CONTROL or CONTEXT_INTEGER or   CONTEXT_SEGMENTS,即这三个标志的组合

我们可以使用GetThreadContext函数来查看线程内核对象的内部,并获取当前CPU寄存器状态的集合。

BOOL GetThreadContext (

HANDLE  hThread,

PCONTEXT  pContext)

若要调用该函数,只需指定一个CONTEXT结构,对某些标志(该结构的ContextFlags成员)进行初始化,指明想要收回哪些寄存器,并将该结构的地址传递给GetThreadContext 。然后该函数将数据填入你要求的成员。

在调用GetThreadContext函数之前,应该调用SuspendThread,否则,线程可能刚好被调度,这样一来,线程的上下文就和所获取的信息不一致了。

示例代码如下:

CONTEXT Context;                  //定义一个CONTEXT结构

Context.ContextFlags = CONTEXT_CONTROL;    //告诉系统我们想获取线程控制寄存器的内容

GetThreadContext(hThread, &Context);      //调用GetThreadContext获取相关信息

Ps:在调用GetThreadContext函数之前,必须首先初始化CONTEXT结构的ContextFlags成员。

要获得线程的所有重要的寄存器(也就是微软认为最常用的寄存器),应该像下面一样初始化ContextFlags:

Context.ContextFlags = CONTEXT_FULL;

在WinNT. h头文件中,定义了CONTEXT_FULL为CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS

当然,我们还可以通过调用SetThreadContext函数来改变结构中的成员,并把新的寄存器值放回线程的内核对象中

BOOL SetThreadContext (

HANDLE  hThread,

CONST CONTEXT  *pContext)

同样,如果要改变哪个线程的上下文,应该先暂停该线程。

CONTEXT Context;      //定义一个CONTEXT结构

SuspendThread(hThread);  //挂起线程

Context.ContextFlags = CONTEXT_CONTROL;   //获取当前上下文的值

GetThreadContext(hThread, &Context);

Context.Eip = 0x00010000;      //Eip字段存储的是指令指针,现在让指令指针指向地址 0x00010000;

Context.ContextFlags = CONTEXT_CONTROL;

SetThreadContext(hThread, &Context);   //重新设置线程上下文

ResumeThread(hThread);         //恢复线程,现在线程开始从0x00010000这个地方开始执行指令

线程的优先级

优先级为0的线程:系统启动时,会创建一个优先级为0的“页面清零线程”,它只有在系统中没有其他可调度线程时,才能调度,用来清除内存中的闲置页面。

优先级在1 ~ 15之间的线程:一般用户模式下,线程的优先级都在该范围。

优先级在16 ~ 30之间的线程:一般是内核线程。

一旦进程运行,便可以通过调用SetPriorityClass来改变自己的优先级

BOOL SetPriorityClass(

         HANDLE  hProcess

         DWORD  fdwPriority);

用来获取进程优先级:

DWORD GetPriorityClass( HANDLE  hProcess );

设置和获取线程的相对优先级:

BOOL SetThreadPriority (

         HANDLE  hThread,

         Int  nPriority);

Int GetThreadPriority(HANDLE  hThread);


允许或者禁止进程或者线程动态提升自己的优先级:

BOOL SetProcessPriorityBoost(

         HANDLE  hProcess,

         BOOL  bDisablePriorityBoost);

 BOOL SetThreadPriorityBoost(

         HANDLE  hThread,

         BOOL  bDisablePriorityBoost);

判断当前是不是启用优先级提升:

BOOL GetProcessPriorityBoost(

         HANDLE  hProcess,

         PBOOL  pbDisablePriorityBoost);

 BOOL GetThreadPriorityBoost(

         HANDLE  hThread,

         PBOOL  pbDisablePriorityBoost);

在多CPU的情况下,我们可以限制某些线程只在可用的cpu的一个子集上运行:

BOOL SetProcessAffinityMask(

         HANDLE  hProcess

         DWORD_PTR  dwProcessAffinityMask);

第一个参数hProcess代表要设置的进程。第二个参数dwProcessAffinityMask是一个位掩码,代表线程可以在哪些CPU上运行。例如,传入0x00000005意味着这个进程中的线程可以在CPU 0 和 CPU 2上运行,但是不能在CPU 1 和 CPU 3~31上运行。

获取进程的关联性掩码:

BOOL GetProcessAffinityMask(

         HANDLE  hProcess

         PDWORD_PTR  pdwProcessAffinityMask,

PDWORD_PTR  pdwSystemAffinityMask);

设置线程的关联性掩码:

BOOL SetThreadAffinityMask(

         HANDLE  hThread

         DWORD_PTR  dwThreadAffinityMask);


有时候强制一个线程只是用特定的某个CPU并不是什么好主意。例如,如果有三个线程都只能使用CPU0,而CPU1,CPU2和CPU3却无所事事。我们想让一个线程运行在一个CPU上,但是同时系统也允许他移到另一个空闲的CPU,那就更好了。要给线程设置一个理想的CPU,可以调用如下:

DWORD SetThreadIdealProcessor(

         HANDLE  hThread,

         DWORD  dwIdealProcessor);

hThread用于指明要为哪个线程设置首选CPU。

dwIdealProcessor函数不是个位掩码,它是个从0到31/63,用于指明供线程希望使用的首选CPU 。可以传递一个MAXIMUM_PROCESSORS的值(在WinNT.h中定义,在32位操作系统中定义为32,64位操作系统中定义为64),表明线程没有理想的CPU。如果没有为该线程设置理想的CPU,那么该函数返回前一个理想的CPU或MAXIMUM_PROCESSORS。

《Windows核心编程》学习笔记(7)– 详解线程相关推荐

  1. 转 windows核心编程 学习笔记 目录

    windows核心编程--SEH(结构异常处理) SEH 的工作原理.         Windows 程序设计中最重要的理念就是消息传递,事件驱动.当GUI应用程序触发一个消息时,系统将把该消息放入 ...

  2. Windows核心编程(3)字符编码详解

    一.字符编码 详细介绍 1.字节(Byte)是一种计量单位,他是计算机信息技术存储容量的一种单位 2.字符的意义: 字符数计算机文字中使用的文字和符号,比如1,2,3,4,~,@,!,%,^等待 3. ...

  3. Windows核心编程学习笔记

    字符串处理 strcpy和wcscpy不安全,是因为无法判断缓冲区最大长度的参数,不知道是否会破坏内存 一个进程-> 一个内核对象+一个地址空间 每个对象只是一个内存块 文件地址包括字节偏移量. ...

  4. windows核心编程学习笔记(六)动态链接库

    动态链接库有很多优点: •扩展了应用程序的特性 •可以用许多种编程语言来编写 •简化了软件项目的管理 •有助于节省内存 •有助于资源的共享 •有助于应用程序的本地化 •有助于解决平台差异 •可以用于一 ...

  5. windows核心编程学习笔记(八)结构化异常处理(Structured Exception Handling)

    首先要要知道,结构化异常处理(SEH)和C++提供的异常处理不相同. 一.Termination HandlersTermination Handlers使用很简单.在想使用SEH处理的地方使用 __ ...

  6. 线程的调度、优先级和亲缘性——Windows核心编程学习手札系列之七

    线程的调度.优先级和亲缘性 --Windows核心编程学习手札系列之七 每个线程都拥有一个上下文结构,在线程的内核对象中,记录线程上次运行时该线程的CPU寄存器状态.Windows会每隔20ms左右查 ...

  7. wringPi 初始化GPIO 为上拉_敏矽微电子Cortex-M0学习笔记04-GPIO详解及应用实例

    前面我们已经对敏矽微电子的基于cortex m0内核的ME32F030R8T6的基本功能做了介绍,然后详细讲解了开发环境MDK的安装,pack包的安装,工程的建立及程序的仿真,紧接着讲解了ME32F0 ...

  8. 窗口消息——Windows核心编程学习手札之二十六

    窗口消息 --Windows核心编程学习手札之二十六 Windows允许一个进程至多建立10000个不同类型的用户对象(user object):图符.光标.窗口类.菜单.加速键表等,当一个线程调用一 ...

  9. 未处理异常和C++异常——Windows核心编程学习手札之二十五

    未处理异常和C++异常 --Windows核心编程学习手札之二十五 当一个异常过滤器返回EXCEPTION_CONTINUE_SEARCH标识符时是告诉系统继续上溯调用树,寻找另外的异常过滤器,但当每 ...

  10. 异常处理程序和软件异常——Windows核心编程学习手札之二十四

    异常处理程序和软件异常 --Windows核心编程学习手札之二十四 CPU负责捕捉无效内存访问和用0除一个数值这种错误,并相应引发一个异常作为对错误的反应,CPU引发的异常称为硬件异常(hardwar ...

最新文章

  1. oracle 数据回滚,恢复误删的数据,闪回表功能的使用
  2. 因 Redis 分布式锁造成的 P0 级重大事故,整个项目组被扣了绩效。。。
  3. SAP上线前数据重置方式总结
  4. js中的局部变量和全局变量
  5. ACM的输入输出总结
  6. 2020年,.NET Core起飞在即,最强日志分析ELK还不会?
  7. Android 应用开发(43)---开关按钮ToggleButton和开关Switch
  8. ​我们为何需要更安全的系统编程语言?
  9. java 算数运算符
  10. 数据中心已死:2025 年 80% 将关闭,目前 10%
  11. 如何用jsp和mysql实现简单的登陆功能
  12. C语言学习资料----快速排序
  13. 超全!JAVA基础知识点总结
  14. eXeScope 使用中的小技巧
  15. STM32入门100步
  16. 学习资料 AND ORR
  17. 【maven】Unable to find javadoc command: The environment variable JAVA_HOME is not correctly set.
  18. 汇编3-计算机程序是如何运行的
  19. c++实现经典游戏——贪吃蛇
  20. 电脑远程开机控制实现 免拆机安装

热门文章

  1. 实战系列-使用Mybatis-Plus生成器生成代码
  2. Python精通-Python入门基础
  3. Java高并发编程详解系列-不可变对象设计模式
  4. revit模型怎么在手机上看_沙盘模型应该怎么看?一定要警惕这些问题
  5. Redis进阶高可用之主从复制
  6. qcow2磁盘格式分析
  7. 学习channel设计:从入门到放弃
  8. 如何安装dubbo的管理控制台
  9. 关于TCP协议的几个问题
  10. Spring Validation校验