Windows进程&线程
+ -

模拟线程切换ThreadSwitch源代码(X86)

2023-11-13 4 0

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);

        //实际执行的是丝程切换函数Scheduling
        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]; //最大100个

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

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 ;
}

142522519305
线程切换

总结

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

0 篇笔记 写笔记

模拟线程切换ThreadSwitch源代码(X86)
ThreadSwitch.cpp// ThreadSwitch.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include "ThreadCor......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!