5.模拟线程切换
正在运行的线程在KPCR里,等待的线程在等待链表中,调度中的线程在那32个调度链表中。
创建它是从下标1的位置开始存的而不是0,因为main需要一个线程。
创建的线程还不能调度还需要初始化的环境,寄存器的值、当前线程的堆栈要确定
模拟线程切换总结:
- 线程不是被动切换的,而是主动让出CPU
- 线程切换并没有使用TSS来保存寄存器,而是使用堆栈.
- 线程切换的过程就是堆栈切换的过程
以下是代码:
ThreadSwitch.h
#pragma once #include <windows.h> #include "stdio.h" //最大支持的线程数 #define MAXGMTHREAD 100 //线程信息的结构 typedef struct { char* name; //线程名 相当于线程ID int Flags; //线程状态 int SleepMillsecondDot; //休眠时间 void* initialStack; //线程堆栈起始位置 void* StackLimit; //线程堆栈界限 void* KernelStack; //线程堆栈当前位置,也就是ESP void* lpParameter; //线程函数的参数 void(*func)(void* lpParameter); //线程函数 }GMThread_t; void GMSleep(int MilliSeconds); int RegisterGMThread(char* name, void(*func)(void*lpParameter), void* lpParameter); void Scheduling();
ThreadSwitch.c
#include "ThreadSwitch.h" //定义线程栈的大小 #define GMTHREADSTACKSIZE 0x80000 //当前线程的索引 int CurrentThreadIndex = 0; //线程的列表 GMThread_t GMThreadList[MAXGMTHREAD] = { NULL, 0 }; //线程状态的标志 enum FLAGS { GMTHREAD_CREATE = 0x1, GMTHREAD_READY = 0x2, GMTHREAD_SLEEP = 0x4, GMTHREAD_EXIT = 0x8, }; //启动线程的函数 void GMThreadStartup(GMThread_t* GMThreadp) { GMThreadp->func(GMThreadp->lpParameter); GMThreadp->Flags = GMTHREAD_EXIT; Scheduling(); return; } //空闲线程的函数 void IdleGMThread(void* lpParameter) { printf("IdleGMThread---------------\n"); Scheduling(); return; } //向栈中压入一个uint值 void PushStack(unsigned int** Stackpp, unsigned int v) { *Stackpp -= 1;//esp - 4 **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); //初始化 ZeroMemory(StackPages, GMTHREADSTACKSIZE); //初始化地址地址 GMThreadp->initialStack = StackPages + GMTHREADSTACKSIZE; //堆栈限制 GMThreadp->StackLimit = StackPages; //堆栈地址 StackDWordParam = (unsigned int*)GMThreadp->initialStack; //入栈 PushStack(&StackDWordParam, (unsigned int)GMThreadp); //通过这个指针来找到线程函数,线程参数 PushStack(&StackDWordParam, (unsigned int)0); //平衡堆栈的(不用管) PushStack(&StackDWordParam, (unsigned int)GMThreadStartup); //线程入口函数 这个函数负责调用线程函数 PushStack(&StackDWordParam, (unsigned int)5); //push ebp PushStack(&StackDWordParam, (unsigned int)7); //push edi PushStack(&StackDWordParam, (unsigned int)6); //push esi PushStack(&StackDWordParam, (unsigned int)3); //push ebx PushStack(&StackDWordParam, (unsigned int)2); //push ecx PushStack(&StackDWordParam, (unsigned int)1); //push edx PushStack(&StackDWordParam, (unsigned int)0); //push eax //当前线程的栈顶 GMThreadp->KernelStack = StackDWordParam; //当前线程状态 GMThreadp->Flags = GMTHREAD_READY; return; } //将一个函数注册为单独线程执行 int RegisterGMThread(char* name, void(*func)(void*lpParameter), void* lpParameter) { int i; for (i = 1; GMThreadList[i].name; i++) { if (0 == _stricmp(GMThreadList[i].name, name)) { break; } } initGMThread(&GMThreadList[i], name, func, lpParameter); return (i & 0x55AA0000); } //切换线程 1:当前线程结构体指针 2:要切换的线程结构体指针 __declspec(naked) void SwitchContext(GMThread_t* SrcGMThreadp, GMThread_t* DstGMThreadp) { __asm { //提升堆栈 push ebp mov ebp, esp //保存当前线程寄存器 push edi push esi push ebx push ecx push edx push eax mov esi, SrcGMThreadp mov edi, DstGMThreadp mov[esi + GMThread_t.KernelStack], esp //经典线程切换,另外一个线程复活 mov esp, [edi + GMThread_t.KernelStack] pop eax pop edx pop ecx pop ebx pop esi pop edi pop ebp ret //把startup(线程函数入口)弹到eip 执行的就是线程函数了 } } //这个函数会让出cpu,从队列里重新选择一个线程执行 void Scheduling() { int TickCount = GetTickCount(); GMThread_t* SrcGMThreadp = &GMThreadList[CurrentThreadIndex]; GMThread_t* DstGMThreadp = &GMThreadList[0]; for (int i = 1; GMThreadList[i].name; i++) { if (GMThreadList[i].Flags & GMTHREAD_SLEEP) { if (TickCount > GMThreadList[i].SleepMillsecondDot) { GMThreadList[i].Flags = GMTHREAD_READY; } } if (GMThreadList[i].Flags & GMTHREAD_READY) { DstGMThreadp = &GMThreadList[i]; break; } } CurrentThreadIndex = DstGMThreadp - GMThreadList; SwitchContext(SrcGMThreadp, DstGMThreadp); return; } void GMSleep(int MilliSeconds) { GMThread_t* GMThreadp; GMThreadp = &GMThreadList[CurrentThreadIndex]; if (GMThreadp->Flags != 0) { GMThreadp->Flags = GMTHREAD_SLEEP; GMThreadp->SleepMillsecondDot = GetTickCount() + MilliSeconds; } Scheduling(); return; }
main.c
#include "ThreadSwitch.h" extern int CurrentThreadIndex; extern GMThread_t GMThreadList[MAXGMTHREAD]; void Thread1(void*) { while (1) { printf("Thread1\n"); GMSleep(500); } } void Thread2(void*) { while (1) { printf("Thread2\n"); GMSleep(200); } } void Thread3(void*) { while (1) { printf("Thread3\n"); GMSleep(10); } } void Thread4(void*) { while (1) { printf("Thread4\n"); GMSleep(1000); } } int main() { RegisterGMThread("Thread1", Thread1, NULL); RegisterGMThread("Thread2", Thread2, NULL); RegisterGMThread("Thread3", Thread3, NULL); RegisterGMThread("Thread4", Thread4, NULL); for (;;) { Sleep(20); Scheduling(); } return 0; }