微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

reactos操作系统实现(46)

在线程调度里可以看到,需要调用函数KiSwapContext来进行运行环境切换,由于每个cpu都是只能运行一个线程,而多个线程在运行过程中被中断了,那么就需要保存cpu所有寄存器,以便下一次恢复线程时可以接续运行。现在就来分析这个函数是怎么样实现这些工作的,代码如下:

#001 /*++

#002 * KiSwapContext

#003 *

#004 * The KiSwapContext routine switches context to another thread.

#005 *

#006 * Params:

#007 * TargetThread - Pointer to the KTHREAD to which the caller wishes to

#008 * switch to.

#009 *

#010 * Returns:

#011 * The WaitStatus of the Target Thread.

#012 *

#013 * Remarks:

#014 * This is a wrapper around KiSwapContextInternal which will save all the

#015 * non-volatile registers so that the Internal function can use all of

#016 * them. It will also save the old current thread and set the new one.

#017 *

#018 * The calling thread does not return after KiSwapContextInternal until

#019 * another thread switches to IT.

#020 *

#021 *--*/

#022 .globl @KiSwapContext@8

#023 .func @KiSwapContext@8,@KiSwapContext@8

#024 @KiSwapContext@8:

#025

保存寄存器的值,以便调用后返回。

#026 /* Save 4 registers */

#027 sub esp,4 * 4

#028

#029 /* Save all the non-volatile ones */

#030 mov [esp+12],ebx

#031 mov [esp+8],esi

#032 mov [esp+4],edi

#033 mov [esp+0],ebp

#034

获取处理器块KPCR,因为FS保存了KPCR的数据结构所在的段。

#035 /* Get the current KPCR */

#036 mov ebx,fs:[KPCR_SELF]

#037

获取当前线程指针。

#038 /* Get the Current Thread */

#039 mov edi,ecx

#040

获取一个将要运行的线程指针。

#041 /* Get the New Thread */

#042 mov esi,edx

#043

获取当前的IRQL

#044 /* Get the wait IRQL */

#045 movzx ecx,byte ptr [edi+KTHREAD_WAIT_IRQL]

#046

调用函数KiSwapContextInternal来切换运行环境。

#047 /* Do the swap with the registers correctly setup */

#048 call @KiSwapContextInternal@0

#049

恢复调用前的寄存器值。

#050 /* Return the registers */

#051 mov ebp,[esp+0]

#052 mov edi,[esp+4]

#053 mov esi,[esp+8]

#054 mov ebx,[esp+12]

#055

#056 /* Clean stack */

#057 add esp,4 * 4

#058 ret

#059 .endfunc

这个函数主要把C函数调用修改为合适的函数KiSwapContextInternal调用。因此接着下来分析函数KiSwapContextInternal代码,如下:

#001 /*++

#002 * KiSwapContextInternal

#003 *

#004 * The KiSwapContextInternal routine switches context to another thread.

#005 *

#006 * Params:

一个将要运行的线程。

#007 * ESI - Pointer to the KTHREAD to which the caller wishes to

#008 * switch to.

当前运行的线程。

#009 * EDI - Pointer to the KTHREAD to which the caller wishes to

#010 * switch from.

#011 *

#012 * Returns:

#013 * None.

#014 *

#015 * Remarks:

#016 * Absolutely all registers except ESP can be trampled here for maximum code flexibility.

#017 *

#018 *--*/

#019 .globl @KiSwapContextInternal@0

#020 .func @KiSwapContextInternal@0,@KiSwapContextInternal@0

#021 @KiSwapContextInternal@0:

#022

保存IRQL

#023 /* Save the IRQL */

#024 push ecx

#025

判断是否支持对称多核处理器。

#026 #ifdef CONfig_SMP

#027 GetSwapLock:

#028 /* Acquire the swap lock */

#029 cmp byte ptr [esi+KTHREAD_SWAP_BUSY],0

#030 jz NotBusy

#031 pause

#032 jmp GetSwapLock

#033 #endif

#034 NotBusy:

#035 /* Increase context switches (use ES for lazy load) */

#036 inc dword ptr es:[ebx+KPCR_CONTEXT_SWITCHES]

#037

保存当前线程的运行环境到当前线程栈里。

#038 /* Save the Exception list */

#039 push [ebx+KPCR_EXCEPTION_LIST]

#040

#041 /* Check for WMI */

#042 cmp dword ptr [ebx+KPCR_PERF_GLOBAL_GROUP_MASK],0

#043 jnz WmiTrace

#044

#045 AfterTrace:

#046 #ifdef CONfig_SMP

#047 #ifdef DBG

#048 /* Assert that we're on the right cpu */

#049 mov cl,[esi+KTHREAD_NEXT_PROCESSOR]

#050 cmp cl,[ebx+KPCR_PROCESSOR_NUMBER]

#051 jnz Wrongcpu

#052 #endif

#053 #endif

#054

#055 /* Get CR0 and save it */

#056 mov ebp,cr0

#057 mov edx,ebp

#058

#059 #ifdef CONfig_SMP

#060 /* Check NPX State */

#061 cmp byte ptr [edi+KTHREAD_NPX_STATE],NPX_STATE_LOADED

#062 jz NpxLoaded

#063 #endif

#064

#065 SetStack:

保存当前线程的栈。

#066 /* Set new stack */

#067 mov [edi+KTHREAD_KERNEL_STACK],esp

#068

#069 /* Checking NPX,disable interrupts Now */

#070 mov eax,[esi+KTHREAD_INITIAL_STACK]

#071 cli

#072

#073 /* Get the NPX State */

#074 movzx ecx,byte ptr [esi+KTHREAD_NPX_STATE]

#075

#076 /* Clear the other bits,merge in CR0,merge in FPU CR0 bits and compare */

#077 and edx,~(CR0_MP + CR0_EM + CR0_TS)

#078 or ecx,edx

#079 or ecx,[eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)]

#080 cmp ebp,ecx

#081 jnz NewCr0

#082

#083 StackOk:

开中断,并切换到新的栈上。

#084 /* Enable interrupts and set the current stack */

#085 sti

#086 mov esp,[esi+KTHREAD_KERNEL_STACK]

#087

检查当前线程和下一个运行线程是否同一个进程空间。

#088 /* Check if address space switch is needed */

#089 mov ebp,[esi+KTHREAD_APCSTATE_PROCESS]

#090 mov eax,[edi+KTHREAD_APCSTATE_PROCESS]

#091 cmp ebp,eax

跳到同一个进程处理。

#092 jz SameProcess

#093

#094 #ifdef CONfig_SMP

#095 /* Get the active processors and XOR with the process' */

#096 mov ecx,[ebx+KPCR_SET_MEMBER_copY]

#097 lock xor [ebp+KPROCESS_ACTIVE_PROCESSORS],ecx

#098 lock xor [eax+KPROCESS_ACTIVE_PROCESSORS],ecx

#099

#100 /* Assert change went ok */

#101 #ifdef DBG

#102 test [ebp+KPROCESS_ACTIVE_PROCESSORS],ecx

#103 jz WrongActivecpu

#104 test [eax+KPROCESS_ACTIVE_PROCESSORS],ecx

#105 jz WrongActivecpu

#106 #endif

#107 #endif

#108

检查是否需要加载LDT

#109 /* Check if we need an LDT */

#110 mov ecx,[ebp+KPROCESS_LDT_DESCRIPTOR0]

#111 or ecx,[eax+KPROCESS_LDT_DESCRIPTOR0]

#112 jnz LdtReload

#113

更新CR3寄存器,以便更新进程的地址空间。其实就是更新内存的页寄存目录。

#114 UpdateCr3:

#115 /* Switch address space */

#116 mov eax,[ebp+KPROCESS_DIRECTORY_TABLE_BASE]

#117 mov cr3,eax

#118

一个进程地址空间。

#119 SameProcess:

#120

#121 #ifdef CONfig_SMP

#122 /* Release swap lock */

#123 and byte ptr [edi+KTHREAD_SWAP_BUSY],0

#124 #endif

#125

#126 /* Clear gs */

#127 xor eax,eax

#128 mov gs,ax

#129

设置下一个线程运行的TEB

#130 /* Set the TEB */

#131 mov eax,[esi+KTHREAD_TEB]

#132 mov [ebx+KPCR_TEB],eax

#133 mov ecx,[ebx+KPCR_GDT]

#134 mov [ecx+0x3A],ax

#135 shr eax,16

#136 mov [ecx+0x3C],al

#137 mov [ecx+0x3F],ah

#138

获取一个线程的栈指针。

#139 /* Get stack pointer */

#140 mov eax,[esi+KTHREAD_INITIAL_STACK]

#141

计算下一个线程运行的栈空间大小。

#142 /* Make space for the NPX Frame */

#143 sub eax,NPX_FRAME_LENGTH

#144

检查是否为虚拟86的运行模式。

#145 /* Check if this isn't V86 Mode,so we can bias the Esp0 */

#146 test dword ptr [eax - KTRAP_FRAME_SIZE + KTRAP_FRAME_EFLAGS],EFLAGS_V86_MASK

#147 jnz NoAdjust

#148

#149 /* Bias esp */

#150 sub eax,KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS

#151

#152 NoAdjust:

#153

设置下一个运行线程的任务段TSS

#154 /* Set new ESP0 */

#155 mov ecx,[ebx+KPCR_TSS]

#156 mov [ecx+KTSS_ESP0],eax

#157

#158 /* Set current IOPM offset in the TSS */

#159 mov ax,[ebp+KPROCESS_IOPM_OFFSET]

#160 mov [ecx+KTSS_IOMAPBASE],ax

#161

#162 /* Increase context switches */

#163 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]

#164

从下一个线程的栈里获取将要运行的环境。

#165 /* Restore exception list */

#166 pop [ebx+KPCR_EXCEPTION_LIST]

#167

#168 /* Restore IRQL */

#169 pop ecx

#170

#171 /* DPC shouldn't be active */

#172 cmp byte ptr [ebx+KPCR_PRCB_DPC_ROUTINE_ACTIVE],0

#173 jnz BugCheckDpc

#174

#175 /* Check if kernel APCs are pending */

#176 cmp byte ptr [esi+KTHREAD_PENDING_KERNEL_APC],0

#177 jnz CheckApc

#178

如果没有异步调用APC,就直接返回。

#179 /* No APCs,return */

#180 xor eax,eax

#181 ret

#182

下面检查异步调用APC

#183 CheckApc:

#184

#185 /* Check if they're disabled */

#186 cmp word ptr [esi+KTHREAD_SPECIAL_APC_disABLE],0

#187 jnz ApcReturn

#188 test cl,cl

#189 jz ApcReturn

#190

#191 /* Request APC Delivery */

#192 mov cl,APC_LEVEL

#193 call @HalRequestSoftwareInterrupt@4

#194 or eax,esp

#195

#196 ApcReturn:

#197

#198 /* Return with APC pending */

#199 setz al

#200 ret

#201

需要重新加局部描述符表LDT

#202 LdtReload:

#203 /* Check if it's empty */

#204 mov eax,[ebp+KPROCESS_LDT_DESCRIPTOR0]

#205 test eax,eax

#206 jz LoadLdt

#207

#208 /* Write the LDT Selector */

#209 mov ecx,[ebx+KPCR_GDT]

#210 mov [ecx+KGDT_LDT],eax

#211 mov eax,[ebp+KPROCESS_LDT_DESCRIPTOR1]

#212 mov [ecx+KGDT_LDT+4],eax

#213

#214 /* Write the INT21 handler */

#215 mov ecx,[ebx+KPCR_IDT]

#216 mov eax,[ebp+KPROCESS_INT21_DESCRIPTOR0]

#217 mov [ecx+0x108],eax

#218 mov eax,[ebp+KPROCESS_INT21_DESCRIPTOR1]

#219 mov [ecx+0x10C],eax

#220

#221 /* Save LDT Selector */

#222 mov eax,KGDT_LDT

#223

#224 LoadLdt:

#225 lldt ax

#226 jmp UpdateCr3

#227

需要重新计算CR0寄存器值。

#228 NewCr0:

#229

#230 #ifdef DBG

#231 /* Assert NPX State */

#232 test byte ptr [esi+KTHREAD_NPX_STATE],~(NPX_STATE_NOT_LOADED)

#233 jnz InvalidNpx

#234 test dword ptr [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)],~(CR0_PE + CR0_MP + CR0_EM + CR0_TS)

#235 jnz InvalidNpx

#236 #endif

#237

#238 /* Update CR0 */

#239 mov cr0,ecx

#240 jmp StackOk

#241

#242 #ifdef CONfig_SMP

#243 NpxLoaded:

#244

#245 /* FIXME: Todo */

#246 int 3

#247

#248 /* Jump back */

#249 jmp SetStack

#250 #endif

#251

下面出错处理。

#252 WmiTrace:

#253

#254 /* No WMI support yet */

#255 int 3

#256

#257 /* Jump back */

#258 jmp AfterTrace

#259

#260 BugCheckDpc:

#261

#262 /* Bugcheck the machine,printing out the threads being switched */

#263 mov eax,[edi+KTHREAD_INITIAL_STACK]

#264 push 0

#265 push eax

#266 push esi

#267 push edi

#268 push ATTEMPTED_SWITCH_FROM_DPC

#269 call _KeBugCheckEx@20

#270

#271 #ifdef DBG

#272 InvalidNpx:

#273 int 3

#274 WrongActivecpu:

#275 int 3

#276 Wrongcpu:

#277 int 3

#278 #endif

#279 .endfunc

通过上面的函数分析,可以了解到线程的环境切换,主要就是线程的页面切换(CR3),线程的环境块切换(TEB),任务段切换ESP0TSS)。

原文地址:https://www.jb51.cc/react/308487.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐