上一次说到把线程链表断了,但线程仍然能够执行。这说明执行的时候查的根本不是这个链表
进程的三种状态
waiting: 被翻译为等待,也有翻译成阻塞的。通过sleep
,WaitForSingleObject
, 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;
}
老师说先看反汇编再看这个,其实我觉得应该是先看模拟,再看反汇编比较容易懂。毕竟代码可读性更好一些,更容易读懂框架