The annotations of the code are well written, and you will understand the order carefully
main.cpp
// ThreadSwitch.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <windows.h> #include "ThreadSwitch.h" extern int CurrentThreadIndex; extern GMThread_t GMThreadList[MAXGMTHREAD]; void Thread1(void*) {<!-- --> while(1){<!-- --> printf("Thread1\\ "); GMSleep(500); } } void Thread2(void*) {<!-- --> while (1) {<!-- --> printf("Thread2\\ "); GMSleep(500); } } void Thread3(void*) {<!-- --> while (1) {<!-- --> printf("Thread3\\ "); GMSleep(500); } } void Thread4(void*) {<!-- --> while (1) {<!-- --> printf("Thread4\\ "); GMSleep(500); } } int main() {<!-- --> RegisterGMThread("Thread1", Thread1, NULL); RegisterGMThread("Thread2", Thread2, NULL); RegisterGMThread("Thread3", Thread3, NULL); RegisterGMThread("Thread4", Thread4, NULL); \t while(TRUE) {<!-- --> Sleep(20); Scheduling(); } return 0; }
ThreadSwitch.h
#pragma once //The maximum number of threads supported #define MAXGMTHREAD 100 //Structure of thread information typedef struct {<!-- --> char* name; //thread name int Flags; //thread status int SleepMillsecondDot; //sleep time \t void* initialStack; //Thread stack starting position void* StackLimit; //Thread stack limit void* KernelStack; //The current position of the thread stack, which is ESP \t void* lpParameter; //parameter of thread function void(*func)(void* lpParameter); //thread function }GMThread_t; void GMSleep(int MilliSeconds); int RegisterGMThread(char* name, void(*func)(void*lpParameter), void* lpParameter); void Scheduling(void);
ThreadSwitch.cpp
#include "StdAfx.h" #include <windows.h> #include "ThreadSwitch.h" //Define the size of the thread stack #define GMTHREADSTACKSIZE 0x80000 // index of the current thread int CurrentThreadIndex = 0; // list of threads GMThread_t GMThreadList[MAXGMTHREAD] = {<!-- -->NULL, 0}; // flag of thread state enum flags {<!-- --> GMTHREAD_CREATE = 0x1, GMTHREAD_READY = 0x2, GMTHREAD_SLEEP = 0x4, GMTHREAD_EXIT = 0x8, }; //Function to start thread void GMThreadStartup(GMThread_t* GMThreadp) {<!-- --> GMThreadp->func(GMThreadp->lpParameter);//Execute the function of the thread GMThreadp->Flags = GMTHREAD_EXIT;//Set the status of the thread to exit Scheduling();//Give up the thread return; } //Function for idle thread void IdleGMThread(void* lpParameter) {<!-- --> printf("IdleGMThread---------------\\ "); Scheduling(); return; } //Push a uint value onto the stack void PushStack(unsigned int** Stackpp, unsigned int v) {<!-- --> *Stackpp -= 1;//Address minus 4 **Stackpp = v;//Assign a value to the address \t return; } //Initialize thread information void initGMThread(GMThread_t* GMThreadp, char* name, void (*func)(void* lpParameter), void* lpParameter) {<!-- --> unsigned char* StackPages; unsigned int* StackDWordParam; GMThreadp->Flags = GMTHREAD_CREATE;//Set flag to create GMThreadp->name = name;//Set thread name GMThreadp->func = func;//Set the address of the function that the thread needs to execute GMThreadp->lpParameter = lpParameter;//Set the parameter address of the thread StackPages = (unsigned char*)VirtualAlloc(NULL, GMTHREADSTACKSIZE, MEM_COMMIT, PAGE_READWRITE);//Allocate memory (the address is automatically allocated by the system, the size is 0x8000, the allocation type is divided into address space and physical page, and the access type) ZeroMemory(StackPages, GMTHREADSTACKSIZE);//Initialized allocated memory is 0 GMThreadp->initialStack = StackPages + GMTHREADSTACKSIZE;//Set the starting position of the thread's stack, that is, the maximum address of the allocated memory StackDWordParam = (unsigned int*)GMThreadp->initialStack; //Push into the stack PushStack( & amp;StackDWordParam, (unsigned int)GMThreadp);//parameters required by the startup function PushStack( & amp;StackDWordParam, (unsigned int)0);//Are you wondering why you put 0 here? Simply speaking, it is to balance the stack, and secondly, because calling startup requires parameters, // After pop startup->eip, esp is here. After entering the function, mov ebp, esp and then ebp + 8 is the default parameter position of the function, which is why there is an extra four-byte push here. PushStack( & amp;StackDWordParam, (unsigned int)GMThreadStartup); PushStack( &StackDWordParam, (unsigned int)5); PushStack( &StackDWordParam, (unsigned int)7); PushStack( & amp;StackDWordParam, (unsigned int)6); PushStack( & amp;StackDWordParam, (unsigned int)3); PushStack( & amp;StackDWordParam, (unsigned int)2); PushStack( & amp;StackDWordParam, (unsigned int)1); PushStack( & amp;StackDWordParam, (unsigned int)0); //The top of the stack of the current thread GMThreadp->KernelStack = StackDWordParam;//The thread's current ESP position GMThreadp->Flags = GMTHREAD_READY;//Set flag to ready return; } // Register a function as a separate thread execution int RegisterGMThread(char* name, void(*func)(void*lpParameter), void* lpParameter)//thread name, function pointer, function parameter pointer {<!-- --> int i; for (i = 1; GMThreadList[i].name; i ++ ) {<!-- -->// Traverse the thread list, if there is no traverse, you will get a new list below the table if (0 == _stricmp(GMThreadList[i].name, name)) {<!-- -->//Comparing the thread names, if the names are the same, it means that the thread has been registered break; } } initGMThread( & amp;GMThreadList[i], name, func, lpParameter);//Initialize thread (initialize thread list address, thread name, function pointer, function parameter pointer) return (i & 0x55AA0000); } // switch thread __declspec(naked) void SwitchContext(GMThread_t* SrcGMThreadp, GMThread_t* DstGMThreadp) {<!-- --> __asm {<!-- --> push ebp mov ebp, esp //esp == ebp push edi push esi push ebx push ecx push edx push eax //The above are the sites that retain the current thread \t\t\t mov esi, SrcGMThreadp mov edi, DstGMThreadp mov [esi + GMThread_t.KernelStack], esp //Assign the current esp to the KernelStack of the current thread structure //Classic thread switching, another thread is resurrected mov esp, [edi + GMThread_t.KernelStack] //Give the address of the kernel stack of the target thread to esp pop eax //esp has been switched to the new thread stack above, this stack is popped again, and what you get is the saved esp (initialized esp/runtime esp) pop edx pop ecx pop ebx pop esi pop edi pop ebp ret //Pop the value at the top of the stack into eip, and what pops up here is the address of startup into eip } } //This function will let out the cpu and reselect a thread from the queue to execute void Scheduling(void) {<!-- --> int i; int TickCount; GMThread_t* SrcGMThreadp; GMThread_t* DstGMThreadp; TickCount = GetTickCount();//It returns the number of milliseconds elapsed from the operating system startup to the current SrcGMThreadp = & amp;GMThreadList[CurrentThreadIndex];//The address of the thread list of the current thread DstGMThreadp = & amp;GMThreadList[0];//The first address of the thread list for (i = 1; GMThreadList[i].name; i ++ ) {<!-- -->//Start traversal, the end condition is the current thread if (GMThreadList[i].Flags & amp; GMTHREAD_SLEEP) {<!-- -->//If it is a SLEEP thread if (TickCount > GMThreadList[i].SleepMillsecondDot) {<!-- -->//Judging time to sleep enough GMThreadList[i].Flags = GMTHREAD_READY;//If you sleep enough, set FLAG to ready } } if (GMThreadList[i].Flags & amp; GMTHREAD_READY) {<!-- -->//If it is a ready thread DstGMThreadp = & amp;GMThreadList[i];//Get the first address of the target thread break;//jump out of the loop } } CurrentThreadIndex = DstGMThreadp - GMThreadList;//The current thread index = the first address of the target thread - the first address of the thread linked list (if you don't understand, please fill in the basics) SwitchContext(SrcGMThreadp, DstGMThreadp);//Thread switching (current thread, target thread) return; } void GMSleep(int MilliSeconds) {<!-- --> GMThread_t* GMThreadp; GMThreadp = & amp;GMThreadList[CurrentThreadIndex];//current thread if (GMThreadp->Flags != 0) {<!-- -->//Fault tolerance, it cannot be 0 ha GMThreadp->Flags = GMTHREAD_SLEEP;//Set the thread state to sleep GMThreadp->SleepMillsecondDot = GetTickCount() + MilliSeconds;//Wake-up time = set sleep time + the number of milliseconds elapsed from the operating system startup to the current time } Scheduling();//You can give up the CPU return; }