Windows进程与线程学习笔记(五)—— 模拟线程切换
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 ;
}
总结
- 线程不是被动切换的,而是主动让出CPU
- 线程切换并没有使用TSS来保存寄存器,而是使用堆栈
- 线程切换的过程就是堆栈切换的过程
Windows进程与线程学习笔记(五)—— 模拟线程切换相关推荐
- Windows进程与线程学习笔记(六)—— 线程切换
Windows进程与线程学习笔记(六)-- 线程切换 主动切换 分析KiSwapContext 分析SwapContext 分析KiSWapThread 总结 时钟中断切换 系统时钟 分析INT 0x ...
- Windows进程与线程学习笔记(一)—— 进程结构体
Windows进程与线程学习笔记(一)-- 进程结构体 进程结构体 EPROCESS +0x000 Pcb : _KPROCESS +0x1b0 Peb : Ptr32 _PEB 练习 解题步骤 第一 ...
- Windows进程与线程学习笔记(九)—— 线程优先级/进程挂靠/跨进程读写
Windows进程与线程学习笔记(九)-- 线程优先级/进程挂靠/跨进程读写 要点回顾 线程优先级 调度链表 分析 KiFindReadyThread 分析 KiSwapThread 总结 进程挂靠 ...
- Windows进程与线程学习笔记(八)—— 线程切换与TSS/FS
Windows进程与线程学习笔记(八)-- 线程切换与TSS/FS 要点回顾 线程切换与TSS 内核堆栈 调用API进0环 实验:分析SwapContext 线程切换与FS 段描述符结构 分析Swap ...
- Windows进程与线程学习笔记(七)—— 时间片管理
Windows进程与线程学习笔记(七)-- 时间片管理 要点回顾 基本概念 CPU时间片 分析 KeUpdateRunTime 分析 KiDispatchInterrupt 备用线程 总结 要点回顾 ...
- Windows进程与线程学习笔记(四)—— 等待链表调度链表
Windows进程与线程学习笔记(四)-- 等待链表&调度链表 要点回顾 33个链表 等待链表 实验:分析等待链表中的线程所属的进程 第一步:查看所属线程结构体: 第二步:查看所属进程结构体 ...
- Windows进程与线程学习笔记(三)—— KPCR
Windows进程与线程学习笔记(三)-- KPCR KPCR +0x000 NtTib : _NT_TIB +0x120 PrcbData : _KPRCB KPCR 描述:CPU控制区(Proce ...
- Windows进程与线程学习笔记(二)—— 线程结构体
Windows进程与线程学习笔记(二)-- 线程结构体 线程结构体 ETHREAD +0x000 Tcb : _KTHREAD 练习 线程结构体 ETHREAD 描述: 每个windows线程在0环都 ...
- 哈工大操作系统学习笔记五——内核级线程实现
哈工大os学习笔记五(内核级线程实现) 文章目录 哈工大os学习笔记五(内核级线程实现) 一. 中断入口.中断出口(前后两段) 1. 从int中断进入内核(中断入口第一段) 2.中断出口(最后一段) ...
最新文章
- 【ACM】杭电1073:Online Judge
- 查询存在表1但不存在表2的所有数据
- 你所需要的java基础篇和提升篇大总结
- Hashtable源码分析
- spring +springmvc+mybatis组合web.xml文件配置
- java 正则首位8或者9的8位数字_从零开始学Python - 第025课:正则表达式的应用
- 51单片机之外部引脚及总线接口
- Java_Spring MVC_Servlet
- 二手机床:中国高端机床装备制造列入战略性产业
- Redis 的过期策略都有哪些?
- C#实现检测打印机状态(包括打印机是否缺纸、打印队列任务数)
- 3D迷宫(DirextX9)
- 嵌入式软件管培生每日总结-第1天
- Http请求状态详解
- 拜占庭将军问题 原文翻译
- c语言——游戏思路及其相关函数
- 企业数据安全保护规划
- 调用百度地图只显示网格的问题
- python童年_一行代码玩童年小游戏
- 主流七款WEB服务器软件点评
热门文章
- CV之IS:计算机视觉之图像分割(Image Segmentation)算法的挑战任务、算法演化、目标检测和图像分割(语义分割/实例分割/全景分割)的对比
- Python语言学习之文件格式后缀那些事:python和常见各种文件格式后缀介绍、使用方法之详细攻略
- 成功解决UserWarning: Update your `Conv2D` call to the Keras 2 API问题
- 成功解决TypeError: __init__() got an unexpected keyword argument 'n_iterations'
- TF之AutoML之AdaNet框架:AdaNet框架的简介、特点、使用方法详细攻略
- 蓝桥杯_算法训练_表达式计算
- 【Linux】一步一步学Linux——Unix发展史(02)
- Python IDLE启动报错
- red hat enterprise 6.3 change to chinese
- 如何利用.pdb文件去调试