• 5.模拟线程切换


    模拟Windows线程切换(ThreadSwitch)

    正在运行的线程在KPCR里,等待的线程在等待链表中,调度中的线程在那32个调度链表中。

    创建它是从下标1的位置开始存的而不是0,因为main需要一个线程。

    创建的线程还不能调度还需要初始化的环境,寄存器的值、当前线程的堆栈要确定

    模拟线程切换总结:

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

    以下是代码:

    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---------------
    ");
        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
    ");
            GMSleep(500);
        }
    }
    
    void Thread2(void*) 
    {
        while (1) 
        {
            printf("Thread2
    ");
            GMSleep(200);
        }
    }
    
    void Thread3(void*) 
    {
        while (1) 
        {
            printf("Thread3
    ");
            GMSleep(10);
        }
    }
    
    void Thread4(void*) 
    {
        while (1) 
        {
            printf("Thread4
    ");
            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;
    }
    
    
    
    
  • 相关阅读:
    ASP.NET网站和ASP.NET应用程序的区别
    客户端状态的优缺点
    Web.config配置文件的优点
    ASP.NET中视图状态的优点和缺点
    java常见集合及其用途
    java Socket编程(一)
    java Soket编程(三)
    浏览器到服务器端进行通信的简单理解
    ant 常用命令
    切记,切忌!——转载(收藏以备用)
  • 原文地址:https://www.cnblogs.com/weekbo/p/10437340.html
Copyright © 2020-2023  润新知