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

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

前言

一、学习自滴水编程达人中级班课程,官网:https://bcdaren.com
二、海东老师牛逼!

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	0x100

typedef struct
{
	char *name;					// 线程名 相当于线程TID
	int Flags;					// 线程状态
	int SleepMillisecondDot;	// 休眠时间
	
	void *InitialStack;			// 线程堆栈起始位置,也就是EBP
	void *StackLimit;			// 线程堆栈界限
	void *KernelStack;			// 线程堆栈当前位置,也就是ESP
	
	void *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 0x80000

void *WindowsStackLimit = NULL;

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

__declspec(naked) void SwitchContext(GMThread_t *SrcGMThreadp,GMThread_t *DstGMThreadp)
{
	__asm
	{
		//保存现场
		push ebp
		mov  ebp,esp
		//sub esp,__LOCAL_SIZE
		push edi
		push esi
		push ebx
		push ecx
		push edx
		push eax
		
		mov esi,SrcGMThreadp	//当前线程结构体指针
		mov edi,DstGMThreadp	//目标线程结构体指针

		// esi + GMThread_t.KernelStack == SrcGMThreadp.KernelStack
		mov [esi+GMThread_t.KernelStack], esp
		//---------------经典堆栈切换 另一个线程复活----------------------------------
		// edi + GMThread_t.KernelStack == DstGMThreadp.KernelStack
		mov esp, [edi+GMThread_t.KernelStack]

		//此时,ESP为目标线程堆栈栈顶
		pop eax
		pop edx
		pop ecx
		pop ebx
		pop esi
		pop edi
		//add esp,__LOCAL_SIZE
		pop ebp
		ret		//ebp之后是GMThreadStartup函数地址
	}
}

//用来执行线程函数
void GMThreadStartup(GMThread_t *GMThreadp)
{
	//执行线程函数
	GMThreadp->func(GMThreadp->lpParameter);
	//线程函数执行结束,设置线程状态为EXIT
	GMThreadp->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 ebp
	PushStack(&StackDWORDParam,7); //push edi
	PushStack(&StackDWORDParam,6); //push esi
	PushStack(&StackDWORDParam,3); //push ebx
	PushStack(&StackDWORDParam,2); //push ecx
	PushStack(&StackDWORDParam,1); //push edx
	PushStack(&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. 线程切换的过程就是堆栈切换的过程
发布了45 篇原创文章 · 获赞 2 · 访问量 1839

猜你喜欢

转载自blog.csdn.net/qq_41988448/article/details/103098367