(22)[进程与线程] 线程的三种状态(waiting,running,ready)

上一次说到把线程链表断了,但线程仍然能够执行。这说明执行的时候查的根本不是这个链表

进程的三种状态

waiting: 被翻译为等待,也有翻译成阻塞的。通过sleepWaitForSingleObject, SuspendThread都会使线程进入waiting的状态 (下面我会使用waiting来强调是链表结构,用阻塞使句子流畅)
running: 正在执行的线程
ready: 就绪状态,将要执行的线程

waiting链表

一个全局变量,能直接找到waiting链表

dd KiWaitListHead 

但是奇怪的是

struct _KTHREAD{
	...
	union {
	    +0x060 WaitListEntry    : _LIST_ENTRY			//waiting链表
	    +0x060 SwapListEntry    : _SINGLE_LIST_ENTRY	//ready链表
    }
    ...
}

这俩东西一个是指向waiting链表,一个指向ready链表
但却是一个联合体,也就是说他们是同一个链表
就绪态的线程会挂到SwapListEntry上,而阻塞态的挂到WaitListEntry上,这似乎很不可思议

他们同时只能有一种状态,要么就绪,要么阻塞,这设计似乎非常合理

猜测,也许他们没有本质上的差别。就绪态的也要排队等待;阻塞态的也许要回到线程探测一下是否恢复到就绪态,最容易理解的就是WaitForSingleObject

调度链表

dd KiDispatcherReadyListHead

在这里插入图片描述

有32个之多,每一个代表一个优先级。

如何判断链表是否为空?

假设 List{
	PDWORD prev, next;
} list;
if      (list.prev == list.next && &list == list.prev) 为空
else if (list.prev == list.next && &list!= list.prev) 有一个
else    有好几个

模拟线程

将视频中的代码抄写下来,并研究懂
抄写的代码与原版可能会有些不同,但逻辑都是一样的

我也不知道这玩意能不能叫协程,反正感觉上是很像的

//-------------------------------thread.h---------------------------------
#pragma once
#pragma warning(disable: 4996)

#include <Windows.h>
#include <stdio.h>

#if 1
#define GMTHREAD_CREATE		0x01
#define GMTHREAD_READY		0x02
#define GMTHREAD_RUNNING	0x04
#define GMTHREAD_SLEEP		0x08
#define GMTHREAD_EXIT		0x100
#else
//如果不是C代码的话,可以用这个
enum  { //其实写成小写比较好看,但这么写能通用
    GMTHREAD_CREATE     = 0x01,
    GMTHREAD_READY      = 0x02,
    GMTHREAD_RUNNING    = 0x04,
    GMTHREAD_SLEEP      = 0x08,
    GMTHREAD_EXIT       = 0x100
};
#endif

typedef unsigned char UCHAR;
typedef unsigned int UINT;

typedef void (*callbackFunc)(void*);

typedef struct {
    char* name;                 //线程名,相当于线程TID
    int Flags;                  //线程状态
    int SleepMillisecondDot;    //休眠时间

    void* InitalStack;          //线程堆栈起始位置
    void* StackLimit;           //线程堆栈界限
    void* KernelStack;          //线程堆栈当前位置 (EPS)

    void* arg;           //线程函数的参数
    callbackFunc func;         //线程函数
}GMThread_t;

extern const size_t GMThread_max_size;
extern const size_t GMThread_stack_size;
extern const char* _self;
extern int CurrentThreadIndex;
extern void* WindowsStackLimit;
extern GMThread_t GMThreadList[];

void InitGMThread(GMThread_t *pGMTHread, char* name, callbackFunc func, void* arg);
int RegisterGMThread(char* name, callbackFunc func, void* arg);
void PushStack(UINT** stack, UINT data);
void GMThreadStartup(GMThread_t* GMThreadp);
void Scheduling();
void SwitchContext(GMThread_t* OldGMThreadp, GMThread_t* NewGMThreadp);
void IdleGMThread(void* arg);
void GMSleep(int Milliseconds);
void coroutine_test();

//-----------------------------------thread.cpp----------------------------------------
#include "coroutine.h"

const size_t GMThread_max_size = 12;
const size_t GMThread_stack_size = 0x80000;
const char* _self = "仿windows线程切换";

int CurrentThreadIndex = 0;
void* WindowsStackLimit = NULL;
GMThread_t GMThreadList[GMThread_max_size] = { 0 };


void InitGMThread(GMThread_t* pGMThread, char* name, callbackFunc func, void* arg) {
    UCHAR* StackPages;
    UINT* t_ESP;
    pGMThread->Flags = GMTHREAD_CREATE;
    
    pGMThread->name = name;
    pGMThread->func = func;
    pGMThread->arg = arg;
    //申请空间
    StackPages = (UCHAR*)VirtualAlloc(NULL, GMThread_stack_size, MEM_COMMIT, PAGE_READWRITE);
    //StackPages置零
    memset(StackPages, 0, GMThread_stack_size);
    //堆栈初始化地址
    pGMThread->InitalStack = StackPages + GMThread_stack_size;
    //堆栈限制
    pGMThread->StackLimit = StackPages;
    //堆栈地址
    t_ESP = (UINT*)pGMThread->InitalStack;

    PushStack(&t_ESP, (UINT)pGMThread);         //通过这个指针来找到:线程函数,函数参数
    PushStack(&t_ESP, (UINT)9);                 //平衡堆栈,无意义
    PushStack(&t_ESP, (UINT)GMThreadStartup);   //函数入口 eip
    PushStack(&t_ESP, 5);                       //ebp,无意义(下同)
    PushStack(&t_ESP, 7);                       //edi
    PushStack(&t_ESP, 6);                       //esi
    PushStack(&t_ESP, 3);                       //ebx
    PushStack(&t_ESP, 2);                       //ecx
    PushStack(&t_ESP, 1);                       //edx   
    PushStack(&t_ESP, 0);                       //eax

    pGMThread->KernelStack = t_ESP;

    pGMThread->Flags = GMTHREAD_READY;

    return;
}

int RegisterGMThread(char* name, callbackFunc func, void* arg) {
    int i;
    for (i = 1; GMThreadList[i].name; i++) {
        if (stricmp(GMThreadList[i].name, name) == 0) {
            break;
        }
    }
    InitGMThread(&GMThreadList[i], name, func, arg);
    return (i | 0x55AA0000);
}


void PushStack(UINT** stack, UINT data) {
    *stack -= 1;
    **stack = data;
    return;
}

void GMThreadStartup(GMThread_t* pGMThread) {
    pGMThread->func(pGMThread->arg);
    pGMThread->Flags = GMTHREAD_EXIT;
    Scheduling();
    return;
}

void Scheduling() {

    int tickCount = GetTickCount();
    GMThread_t* pOldGMThread = &GMThreadList[CurrentThreadIndex];
    GMThread_t* pNewGMThread = &GMThreadList[0];

    for (int i = 1; GMThreadList[i].name; i++) {
        if (GMThreadList[i].Flags & GMTHREAD_SLEEP) {
            //sleep结束?
            if (tickCount > GMThreadList[i].SleepMillisecondDot) {
                GMThreadList[i].Flags = GMTHREAD_READY;
            }
        }
        //找到ready线程,退出循环
        if (GMThreadList[i].Flags & GMTHREAD_READY) {
            pNewGMThread = &GMThreadList[i];
            break;
        }
    }
    CurrentThreadIndex = pNewGMThread - GMThreadList;
    SwitchContext(pOldGMThread, pNewGMThread);
}


__declspec(naked) void SwitchContext(GMThread_t* OldGMThreadp, GMThread_t* NewGMThreadp) {
    _asm {
        //保存当前线程 现场
        push ebp
        mov ebp, esp
        push edi
        push esi
        push ebp
        push ecx
        push edx
        push eax

        mov esi, OldGMThreadp
        mov edi, NewGMThreadp

        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 //pop eip
    }
}

void IdleGMThread(void* arg) {
    printf("Idle GMtrhead...\n");
    Scheduling();
    return;
}


void GMSleep(int Milliseconds) {
    GMThread_t* GMThreadp;
    GMThreadp = &GMThreadList[CurrentThreadIndex];

    if ((GMThreadp->Flags) != 0)
    {
        GMThreadp->SleepMillisecondDot = GetTickCount() + Milliseconds;
        GMThreadp->Flags = GMTHREAD_SLEEP;
    }

    Scheduling();
    return;
}

//------------------------------测试代码-----------------------------------
void Thread(void* arg) {
    int i = (int)arg;
    while (1) {
        printf("thrad: %d\n", i/100);
        GMSleep(i);
    }
    return;
}

void main() {
    char name_1[] = "Thread_1";
    char name_2[] = "Thread_2";
    char name_3[] = "Thread_3";
    char name_4[] = "Thread_4";
    RegisterGMThread(name_1, Thread, (void*)100);
    RegisterGMThread(name_2, Thread, (void*)200);
    RegisterGMThread(name_3, Thread, (void*)300);
    RegisterGMThread(name_4, Thread, (void*)400);

    for (;;) {
        Sleep(20);
        Scheduling();
    }
    return;
}

老师说先看反汇编再看这个,其实我觉得应该是先看模拟,再看反汇编比较容易懂。毕竟代码可读性更好一些,更容易读懂框架

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值