IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Windows线程(模拟线程切换、主动切换、时钟中断切换、时间片管理、TSS、FS、线程优先级) -> 正文阅读

[数据结构与算法]Windows线程(模拟线程切换、主动切换、时钟中断切换、时间片管理、TSS、FS、线程优先级)

ThreadSwitch源码分析

线程结构体(ThreadSwitch源码仿照EHREAD)

typedef struct {
	char *name;//线程名 相当于线程TID
	int Flags;//线程状态
	int SleepMillisecondDot;//休眠时间
	void *InitialStack;//线程堆栈起始位置
	void *StackLimit;//线程堆栈界限
	void *KernelStack;//线程堆栈当前位置,也就是ESP
	void *lpParameter;//线程函数的参数
	void (*func)(void *lpParameter);//线程函数		
}GMThread_t;

调度链表(ThreadSwitch)

//线程结构体数组 extern GMThread_t GMThreadList[MAXGMTHREAD];
所谓创建线程,就是创建一个结构体,并且挂到这个数组中,此时的线程状态为:创建
这个ThreadSwitch进行简化,在Windows下正在运行的线程在KPCR里,等待的线程
在等待链表表里,就绪线程在就绪链表里。
ThreadSwitch分析发现只有一个线程结构体数组,其中的Flags线程状态便是代表了
线程不同的状态。
在ThreadSwitch线程结构体中其余线程是从下标1开始的,而下标0是当前线程,
main线程运行在0下标。

经典线程堆栈切换

__asm{
	push ebp
	push ebp,esp
	push edi
	push esi
	push ebx
	push ecx
	push edx
	push eax
	mov esi,当前线程结构体指针
	mov edi,你要切换线程的结构体指针
	mov [esi+KernelStack当前线程结构体中esp的位置属性进行保存],esp
	//经典堆栈切换,另一个线程复活
	mov esp,[edi+KernelStack将要切换的线程esp拿出来赋值给esp]
	pop eax
	pop edx
	pop ecx
	pop ebx
	pop esi
	pop edi
	pop ebp
	ret
}

模型线程切换总结

1.线程切换不是被动切换的,而是主动让出CPU。
2.线程切换并没有使用TSS来保存寄存器,而是使用堆栈。
3.线程的切换本质上就是堆栈的切换。
4.可是使用任意的堆模拟栈的机制,将寄存器保存后进行堆栈的切换即可实现线程的变换。

主动切换

线程切换

在ThreadSwitch项目中有一个重要的函数,
SwitchContext调用这个函数后会触发经典的堆栈切换,
导致线程的切换,在Windows中也有类似的函数:
KiSwapContext。

主动线程切换总结

1.Windows中绝大部分API都调用了SwapContext函数,
也就是说,当线程只要调用了API,也就是导致了线程切换。
2.线程切换时会比较是否属于同一个进程,如果不是,切换CR3
,CR3换了,进程也就切换了。
3.调用过程:KiSwapContext的父函数,KiSwapThread

时钟中断切换

SwapContext

绝大多数的内核函数都会使用SwapContext来实现线程的切换。

如何中断一个正在执行的程序

1.异常 比如缺页,或者INT N指令。
2.中断 比如时钟中断。

系统时钟

(IDT表)中断号	|IRQ	|说明
       0x30		 IRQ0        时钟中断
Windows系列操作系统:10-20毫秒
如要获取当前时钟间隔值,可以使用WIN32 API:
GetSystemTimeAdjustment

时钟中断要执行流程

时钟中断执行流程:
KiStartUnexpectedRange
KiEndUnexpectedRange
KiUnexpectedInterruptTail
HalBeginSystemInterrupt
HalEndSystemInterrupt
KiDispatchInterrupt
SwapContext

时钟中断切换总结

线程切换的几种情况:
1.主动调用API函数
2.时钟中断
3.异常处理
如果一个线程不调用API,在代码中屏蔽中断(CLI指令),并且
不会出现异常,那么当前线程将永久占有CPU,单核占有率100%,
2核就是50%。

时间片管理

时钟与线程切换

时钟中断会导致线程切换,但并不是说
只要有时钟中断就一定会切换线程,时钟中断时,两种情况会导致线程切换。
1.当前的线程CPU时间片到期
2.有备用线程(KPCR.PrcbData.NextThread)
有备用线程就算当前线程的时间片没有到期仍然会切换线程,
备用线程优先级高于时间片。

CPU时间片

1.当一个新的线程开始执行时,初始化程序会在_KTHREAD.Quantum
赋初始值,该值的大小由_KPROCESS.ThreadQuantum决定。
2.每次时钟中断会调用KeUpdateRunTime函数,该函数每次将当前线程
Quantum减少3个单位,如果减到0,则将KPCR,PrcbData.QuantumEnd的值设置为0
3.KiDispatchInterrupt判断时间片到期:
调用KiQuantumEnd(重新设置时间片,找到要运行的线程)。

线程切换的3种情况

1.当前线程主动调用API:
API函数->KiSwapThread->KiSwapContext->SwapContext
2.当前线程时间片到期:
KiDispatchInterrupt->KiQuantumEnd->SwapContext
3备用线程
KiDispatchInterrupt->SwapContext

线程与TSS

SwapContext

SwapContext这个函数是Windows线程切换的核心,
无论是主动切换还是系统时钟导致的线程切换,最终都会调用这个函数。
该函数除了切换堆栈以外,还做了很多其他的事情。

调用API进0环

普通调用:通过TSS.ESP0得到0环堆栈。
快速调用:从MSR得到一个临时0环栈,代码执行后仍然通过
TSS.ESP0得到当前线程0环堆栈。

TSS

Intel设计TSSS的目的是为了任务切换(线程切换),但Windows和
Linux并没有使用。而是采用堆栈保存线程的各种寄存器。

线程与FS

线程切换与FS寄存器之间的关系

FS:[0]寄存器在3环时指向TEB,进入0环后FS:[0]指向KPCR,
系统中同时存在多个线程,这就意味着FS:[0]在3环时指向的TEB要有多个(每个线程一份)。
但是在实现中3环查看不同线程的FS寄存器时,FS的段选择子都是相同的,具体是操作系统
将新的TEB地址从KPCR里读出来之后,写到当前GDT表段描述符的Base基址里面,因此
在3环中FS的段选择子是不同修改的,改变的FS对应的基址,变成的了新的线程的TEB的基址。

线程优先级

三种情况导致线程切换

(1)当前线程主动调用API
API函数:KiSwapThread、KiSwapContext、SwapContext
(2)当前线程时间片到期
KiDispatchInterrupt: KiQuantumEnd、SwapContext	
(3)有备用线程(KPCR.PrcbData.NextTread)
KiDispatchInterrupt SwapContext

线程调用链表

在xp中调度链表有32个,优先级最高的位于0号链表其次位于1号链表,
以此类推,类似于KiFindReadyThread查找线程级别,是一种比较暴力
的线程优先级查找方式,比如先从31查看是否有线程,在查看30..29..28是否有线程,
如果31有线程则不会再去查找30是否有线程,需要注意的是只有在本次查找中,当
高级别的线程在执行的时候,使用了API或者时间片到期了,低级别的线程仍然有机会运行。

Windows调度链表算法

在xp中调度链表有32个,每次都重都开始查找效率较低,Windows使用DWORD变量
来进行记录,当向调度链表(32个)中挂入或者摘除某个线程时,会判断当前级别的链表
是否为空,为空将DWORD变量对应位置0,否则置1(具有等待调度线程),DWORD正好是32位。
多CPU会随机寻找KiDispatcherReadyListHead指向的数组中的线程,线程可以绑定某个
CPU(使用API:setThreadAffinityMask)。

如果没有就绪线程如何处理

在KPCR中有一个子结构体叫做PrcbData有3个重要成员
+0x004 CurrentThread:Ptr32_KTHREAD	当前线程
+0x008 NextThread:Ptr32_KTHREAD	备用线程
+0x00C ldleThread:Ptr32_KTHREAD	空闲线程
空闲线程:当CPU查找调度链表如果没有其他需要调度的线程,
这就会调度空闲线程,每个KPCR都会指定一个空闲线程。
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 18:07:50  更:2022-04-18 18:10:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:45:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码