Windows进程与线程学习笔记(五)—— 模拟线程切换

  • ThreadSwitch代码分析
    • ThreadSwitch.cpp
    • ThreadCore.h
    • ThreadCore.cpp
  • 总结

ThreadSwitch代码分析

ThreadSwitch.cpp

// ThreadSwitch.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include "ThreadCore.h"int main(int argc, char* argv[])
{//初始化线程环境RegisterGMThread("Thread1",Thread1,NULL);RegisterGMThread("Thread2",Thread2,NULL);RegisterGMThread("Thread3",Thread3,NULL);RegisterGMThread("Thread4",Thread4,NULL);//仿Windows线程切换for (;;){Sleep(20);ThreadPolling();}return 0;
}

ThreadCore.h

// ThreadCore.h: interface for the ThreadCore class.
//
//#if !defined(AFX_THREADCORE_H__3C5DBE32_012F_4176_884F_8D9EA510122D__INCLUDED_)
#define AFX_THREADCORE_H__3C5DBE32_012F_4176_884F_8D9EA510122D__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#define MAXGMTHREAD 0x100#define GMTHREAD_CREATE        0x01
#define GMTHREAD_READAY     0x02
#define GMTHREAD_RUNING     0x04
#define GMTHREAD_SLEEP      0x08
#define GMTHREAD_SUSPEND    0x10
#define GMTHREAD_EXIT   0x100typedef struct
{char *name;                    // 线程名 相当于线程TIDint Flags;                   // 线程状态int SleepMillisecondDot; // 休眠时间void *InitialStack;          // 线程堆栈起始位置,也就是EBPvoid *StackLimit;          // 线程堆栈界限void *KernelStack;         // 线程堆栈当前位置,也就是ESPvoid *lpParameter;         // 线程函数的参数void (*func)(void *lpParameter);  // 线程函数} GMThread_t;    //---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------/* 线程结构体数组* 线程在不同状态的存储位置不同* 正在运行的线程位于KPCR* 等待中的线程位于等待链表* 就绪的线程位于调度链表中* 这里对于以上三种情况使用一个数组进行包含* main函数也是一个线程,信息存在第一个数组成员里,也就是下标为0的位置* 创建线程时,是从下标为1的位置开始分配的*/
extern GMThread_t GMThreadList[MAXGMTHREAD];

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------void IdleGMThread(void *lpParameter);void GMThreadStartup(GMThread_t *GMThreadp);
void initGMThread(GMThread_t *GMThreadp,char *name,void (*func)(void *lpParameter),void *lpParameter);
int RegisterGMThread(char *name,void (*func)(void *lpParameter),void *lpParameter);
void Scheduling(void);void GMSleep(int Milliseconds);void ThreadPolling();void Thread1(void *lpParameter);
void Thread2(void *lpParameter);
void Thread3(void *lpParameter);
void Thread4(void *lpParameter);#endif // !defined(AFX_THREADCORE_H__3C5DBE32_012F_4176_884F_8D9EA510122D__INCLUDED_)

ThreadCore.cpp

#include "stdafx.h"
#include "stdio.h"
#include "windows.h"#include "ThreadCore.h"#define _SELF        "滴水_仿Windows线程切换"//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------int CurrentThreadindex = 0;
GMThread_t GMThreadList[MAXGMTHREAD] = { NULL,0 };#define GMTHREADSTACKSIZE 0x80000void *WindowsStackLimit = NULL;//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------__declspec(naked) void SwitchContext(GMThread_t *SrcGMThreadp,GMThread_t *DstGMThreadp)
{__asm{//保存现场push ebpmov  ebp,esp//sub esp,__LOCAL_SIZEpush edipush esipush ebxpush ecxpush edxpush eaxmov esi,SrcGMThreadp //当前线程结构体指针mov edi,DstGMThreadp //目标线程结构体指针// esi + GMThread_t.KernelStack == SrcGMThreadp.KernelStackmov [esi+GMThread_t.KernelStack], esp//---------------经典堆栈切换 另一个线程复活----------------------------------// edi + GMThread_t.KernelStack == DstGMThreadp.KernelStackmov esp, [edi+GMThread_t.KernelStack]//此时,ESP为目标线程堆栈栈顶pop eaxpop edxpop ecxpop ebxpop esipop edi//add esp,__LOCAL_SIZEpop ebpret      //ebp之后是GMThreadStartup函数地址}
}//用来执行线程函数
void GMThreadStartup(GMThread_t *GMThreadp)
{//执行线程函数GMThreadp->func(GMThreadp->lpParameter);//线程函数执行结束,设置线程状态为EXITGMThreadp->Flags = GMTHREAD_EXIT;//线程切换Scheduling();return ;
}void IdleGMThread(void *lpParameter)
{Scheduling();return ;
}void PushStack(unsigned int **Stackpp,unsigned int v)
{// ESP 减去一个单位(4个字节)*Stackpp -= 1;//[ESP] = 参数v**Stackpp = v;return ;
}void initGMThread(GMThread_t *GMThreadp,char *name,void (*func)(void *lpParameter),void *lpParameter)
{unsigned char *StackPages;unsigned int *StackDWORDParam;//结构初始化赋值GMThreadp->Flags = GMTHREAD_CREATE;       //初始化线程为创建状态GMThreadp->name = name;                 //线程名GMThreadp->func = func;                    //线程函数,已经定义好GMThreadp->lpParameter = lpParameter;    //参数//申请堆栈空间StackPages = (unsigned char*)VirtualAlloc(NULL,GMTHREADSTACKSIZE,MEM_COMMIT,PAGE_READWRITE);//堆栈清零memset(StackPages,0,GMTHREADSTACKSIZE);//堆栈栈底(EBP)GMThreadp->InitialStack = (StackPages+GMTHREADSTACKSIZE-0x10);//堆栈边界地址GMThreadp->StackLimit = StackPages;StackDWORDParam = (unsigned int*)GMThreadp->InitialStack;//入栈PushStack(&StackDWORDParam,(unsigned int)GMThreadp);      //线程结构体自身指针,用来寻找 线程函数|函数参数PushStack(&StackDWORDParam,(unsigned int)9);               //平衡堆栈PushStack(&StackDWORDParam,(unsigned int)GMThreadStartup);    //函数地址,执行线程函数的入口函数//下面的值可以随便写PushStack(&StackDWORDParam,5); //push ebpPushStack(&StackDWORDParam,7); //push ediPushStack(&StackDWORDParam,6); //push esiPushStack(&StackDWORDParam,3); //push ebxPushStack(&StackDWORDParam,2); //push ecxPushStack(&StackDWORDParam,1); //push edxPushStack(&StackDWORDParam,0); //push eax//执行后,堆栈变化如下

GMThreadp->KernelStack = StackDWORDParam;    //指向当前线程的栈顶(ESP)GMThreadp->Flags = GMTHREAD_READAY;           //线程状态设置为就绪return ;
}int RegisterGMThread(char *name,void (*func)(void *lpParameter),void *lpParameter)
{int i;//为数组下标为0的成员赋值,GM Thread,相当于main函数线程if (GMThreadList[0].name==NULL){//申请堆栈初始化操作  线程数组 ,线程名字 ,函数地址 ,参数initGMThread(&GMThreadList[0], "IDLE GM Thread", IdleGMThread, NULL);}//新增的线程从下标为1开始写入for (i=1;GMThreadList[i].name;i++){//判断数组中尚未初始化的成员if (0==stricmp(GMThreadList[i].name,name)){break;}}//初始化线程结构体initGMThread(&GMThreadList[i],name,func,lpParameter);return (i|0x55AA0000);
}void Scheduling(void)
{int i;int TickCount;GMThread_t *SrcGMThreadp;GMThread_t *DstGMThreadp;TickCount = GetTickCount();SrcGMThreadp = &GMThreadList[CurrentThreadindex];       //当前线程结构体指针DstGMThreadp = &GMThreadList[0];                        //目标线程结构体指针//遍历线程数组,找到状态为就绪的线程for (i=1;GMThreadList[i].name;i++){if (GMThreadList[i].Flags&GMTHREAD_SLEEP){if (TickCount>GMThreadList[i].SleepMillisecondDot){GMThreadList[i].Flags = GMTHREAD_READAY;}}if ((GMThreadList[i].Flags&GMTHREAD_READAY)){//检测到有线程的状态为就绪,将其作为目标线程DstGMThreadp = &GMThreadList[i];break;}}CurrentThreadindex = DstGMThreadp-GMThreadList;      //得到即将执行的线程下标SwitchContext(SrcGMThreadp,DstGMThreadp);          //线程切换return ;
}//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------void GMSleep(int Milliseconds)
{GMThread_t *GMThreadp;GMThreadp = &GMThreadList[CurrentThreadindex];if ((GMThreadp->Flags&GMTHREAD_SUSPEND)==0){GMThreadp->SleepMillisecondDot = GetTickCount()+Milliseconds;GMThreadp->Flags = GMTHREAD_SLEEP;}//线程切换Scheduling();return ;
}void ThreadPolling()
{unsigned char StackPage[GMTHREADSTACKSIZE];memset(StackPage,0,GMTHREADSTACKSIZE);//模拟线程切换IdleGMThread(StackPage);return ;
}//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
void vmmprint(char *f,int l,char *fmt, ...)
{int ret;char buffer[0x100];va_list args;//----------------------------------va_start(args, fmt);_snprintf(buffer,0x80,"[%s]:",f,l);ret = _vsnprintf(buffer+strlen(buffer), 0x100-strlen(buffer), fmt, args);if (ret == -1){strcpy(buffer, "vmmprint: error.");}//----------------------------------printf("%s",buffer);//OutputDebugString(buffer);return ;
}void Thread1(void *lpParameter)
{int i;for (i=0;i<3;i++){vmmprint(_SELF,__LINE__,"Thread1 \n");GMSleep(1000);}return ;
}void Thread2(void *lpParameter)
{for (;;){vmmprint(_SELF,__LINE__,"Thread2 \n");GMSleep(500);}return ;
}void Thread3(void *lpParameter)
{for (;;){vmmprint(_SELF,__LINE__,"Thread3 \n");GMSleep(800);}return ;
}void Thread4(void *lpParameter)
{for (;;){vmmprint(_SELF,__LINE__,"Thread4 \n");GMSleep(200);}return ;
}

总结

  1. 线程不是被动切换的,而是主动让出CPU
  2. 线程切换并没有使用TSS来保存寄存器,而是使用堆栈
  3. 线程切换的过程就是堆栈切换的过程

Windows进程与线程学习笔记(五)—— 模拟线程切换相关推荐

  1. Windows进程与线程学习笔记(六)—— 线程切换

    Windows进程与线程学习笔记(六)-- 线程切换 主动切换 分析KiSwapContext 分析SwapContext 分析KiSWapThread 总结 时钟中断切换 系统时钟 分析INT 0x ...

  2. Windows进程与线程学习笔记(一)—— 进程结构体

    Windows进程与线程学习笔记(一)-- 进程结构体 进程结构体 EPROCESS +0x000 Pcb : _KPROCESS +0x1b0 Peb : Ptr32 _PEB 练习 解题步骤 第一 ...

  3. Windows进程与线程学习笔记(九)—— 线程优先级/进程挂靠/跨进程读写

    Windows进程与线程学习笔记(九)-- 线程优先级/进程挂靠/跨进程读写 要点回顾 线程优先级 调度链表 分析 KiFindReadyThread 分析 KiSwapThread 总结 进程挂靠 ...

  4. Windows进程与线程学习笔记(八)—— 线程切换与TSS/FS

    Windows进程与线程学习笔记(八)-- 线程切换与TSS/FS 要点回顾 线程切换与TSS 内核堆栈 调用API进0环 实验:分析SwapContext 线程切换与FS 段描述符结构 分析Swap ...

  5. Windows进程与线程学习笔记(七)—— 时间片管理

    Windows进程与线程学习笔记(七)-- 时间片管理 要点回顾 基本概念 CPU时间片 分析 KeUpdateRunTime 分析 KiDispatchInterrupt 备用线程 总结 要点回顾 ...

  6. Windows进程与线程学习笔记(四)—— 等待链表调度链表

    Windows进程与线程学习笔记(四)-- 等待链表&调度链表 要点回顾 33个链表 等待链表 实验:分析等待链表中的线程所属的进程 第一步:查看所属线程结构体: 第二步:查看所属进程结构体 ...

  7. Windows进程与线程学习笔记(三)—— KPCR

    Windows进程与线程学习笔记(三)-- KPCR KPCR +0x000 NtTib : _NT_TIB +0x120 PrcbData : _KPRCB KPCR 描述:CPU控制区(Proce ...

  8. Windows进程与线程学习笔记(二)—— 线程结构体

    Windows进程与线程学习笔记(二)-- 线程结构体 线程结构体 ETHREAD +0x000 Tcb : _KTHREAD 练习 线程结构体 ETHREAD 描述: 每个windows线程在0环都 ...

  9. 哈工大操作系统学习笔记五——内核级线程实现

    哈工大os学习笔记五(内核级线程实现) 文章目录 哈工大os学习笔记五(内核级线程实现) 一. 中断入口.中断出口(前后两段) 1. 从int中断进入内核(中断入口第一段) 2.中断出口(最后一段) ...

最新文章

  1. 【ACM】杭电1073:Online Judge
  2. 查询存在表1但不存在表2的所有数据
  3. 你所需要的java基础篇和提升篇大总结
  4. Hashtable源码分析
  5. spring +springmvc+mybatis组合web.xml文件配置
  6. java 正则首位8或者9的8位数字_从零开始学Python - 第025课:正则表达式的应用
  7. 51单片机之外部引脚及总线接口
  8. Java_Spring MVC_Servlet
  9. 二手机床:中国高端机床装备制造列入战略性产业
  10. Redis 的过期策略都有哪些?
  11. C#实现检测打印机状态(包括打印机是否缺纸、打印队列任务数)
  12. 3D迷宫(DirextX9)
  13. 嵌入式软件管培生每日总结-第1天
  14. Http请求状态详解
  15. 拜占庭将军问题 原文翻译
  16. c语言——游戏思路及其相关函数
  17. 企业数据安全保护规划
  18. 调用百度地图只显示网格的问题
  19. python童年_一行代码玩童年小游戏
  20. 主流七款WEB服务器软件点评

热门文章

  1. CV之IS:计算机视觉之图像分割(Image Segmentation)算法的挑战任务、算法演化、目标检测和图像分割(语义分割/实例分割/全景分割)的对比
  2. Python语言学习之文件格式后缀那些事:python和常见各种文件格式后缀介绍、使用方法之详细攻略
  3. 成功解决UserWarning: Update your `Conv2D` call to the Keras 2 API问题
  4. 成功解决TypeError: __init__() got an unexpected keyword argument 'n_iterations'
  5. TF之AutoML之AdaNet框架:AdaNet框架的简介、特点、使用方法详细攻略
  6. 蓝桥杯_算法训练_表达式计算
  7. 【Linux】一步一步学Linux——Unix发展史(02)
  8. Python IDLE启动报错
  9. red hat enterprise 6.3 change to chinese
  10. 如何利用.pdb文件去调试