• 《Windows核心编程》学习笔记(7)– 详解线程


    线程的挂起和恢复

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

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

    SuspendThread ResumeThread 都返回之前的挂起计数。

    一个线程最多可以挂起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_FULLCONTEXT_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,而CPU1CPU2CPU3却无所事事。我们想让一个线程运行在一个CPU上,但是同时系统也允许他移到另一个空闲的CPU,那就更好了。要给线程设置一个理想的CPU,可以调用如下:

    DWORD SetThreadIdealProcessor(

             HANDLE  hThread,

             DWORD  dwIdealProcessor);

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

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

  • 相关阅读:
    Docker初识
    57、android 应用内全局通知的实现方法
    56、使用android studio(v1.3.*)修改包名 (rename package name)
    55、android app借助友盟实现微信授权登录
    54、edittext输入类型限制为ip,inputType应该如何设置
    53、listview、expandableListview如何选中时保持高亮?
    52、sb犯的错误
    51、如何提取android代码中的字符串为系统资源文件 (I18N)
    50、转自知乎上android开发相见恨晚的接口
    49、android studio 使用技巧记录
  • 原文地址:https://www.cnblogs.com/forlina/p/2133438.html
Copyright © 2020-2023  润新知