在线程调度里可以看到,需要调用函数@H_404_5@KiSwapContext来进行运行环境切换,由于每个@H_404_5@cpu都是只能运行一个线程,而多个线程在运行过程中被中断了,那么就需要保存@H_404_5@cpu所有寄存器,以便下一次恢复线程时可以接续运行。现在就来分析这个函数是怎么样实现这些工作的,代码如下:@H_404_5@
@H_404_5@#001 /*++
@H_404_5@#002 * KiSwapContext
@H_404_5@#003 *
@H_404_5@#004 * The KiSwapContext routine switches context to another thread.
@H_404_5@#005 *
@H_404_5@#006 * Params:
@H_404_5@#007 * TargetThread - Pointer to the KTHREAD to which the caller wishes to
@H_404_5@#008 * switch to.
@H_404_5@#009 *
@H_404_5@#010 * Returns:
@H_404_5@#011 * The WaitStatus of the Target Thread.
@H_404_5@#012 *
@H_404_5@#013 * Remarks:
@H_404_5@#014 * This is a wrapper around KiSwapContextInternal which will save all the
@H_404_5@#015 * non-volatile registers so that the Internal function can use all of
@H_404_5@#016 * them. It will also save the old current thread and set the new one.
@H_404_5@#017 *
@H_404_5@#018 * The calling thread does not return after KiSwapContextInternal until
@H_404_5@#019 * another thread switches to IT.
@H_404_5@#020 *
@H_404_5@#021 *--*/
@H_404_5@#022 .globl @KiSwapContext@8
@H_404_5@#023 .func @KiSwapContext@8,@KiSwapContext@8
@H_404_5@#024 @KiSwapContext@8:
@H_404_5@#025
@H_404_5@
@H_404_5@#026 /* Save 4 registers */
@H_404_5@#027 sub esp,4 * 4
@H_404_5@#028
@H_404_5@#029 /* Save all the non-volatile ones */
@H_404_5@#030 mov [esp+12],ebx
@H_404_5@#031 mov [esp+8],esi
@H_404_5@#032 mov [esp+4],edi
@H_404_5@#033 mov [esp+0],ebp
@H_404_5@#034
@H_404_5@
获取处理器块@H_404_5@KPCR,因为@H_404_5@FS保存了@H_404_5@KPCR的数据结构所在的段。@H_404_5@
@H_404_5@#035 /* Get the current KPCR */
@H_404_5@#036 mov ebx,fs:[KPCR_SELF]
@H_404_5@#037
@H_404_5@
@H_404_5@#038 /* Get the Current Thread */
@H_404_5@#039 mov edi,ecx
@H_404_5@#040
@H_404_5@
@H_404_5@#041 /* Get the New Thread */
@H_404_5@#042 mov esi,edx
@H_404_5@#043
@H_404_5@
@H_404_5@#044 /* Get the wait IRQL */
@H_404_5@#045 movzx ecx,byte ptr [edi+KTHREAD_WAIT_IRQL]
@H_404_5@#046
@H_404_5@
调用函数@H_404_5@KiSwapContextInternal来切换运行环境。@H_404_5@
@H_404_5@#047 /* Do the swap with the registers correctly setup */
@H_404_5@#048 call @KiSwapContextInternal@0
@H_404_5@#049
@H_404_5@
@H_404_5@#050 /* Return the registers */
@H_404_5@#051 mov ebp,[esp+0]
@H_404_5@#052 mov edi,[esp+4]
@H_404_5@#053 mov esi,[esp+8]
@H_404_5@#054 mov ebx,[esp+12]
@H_404_5@#055
@H_404_5@#056 /* Clean stack */
@H_404_5@#057 add esp,4 * 4
@H_404_5@#058 ret
@H_404_5@#059 .endfunc
@H_404_5@
这个函数主要把@H_404_5@C函数调用修改为合适的函数@H_404_5@KiSwapContextInternal调用。因此接着下来分析函数@H_404_5@KiSwapContextInternal的代码,如下:@H_404_5@
@H_404_5@#001 /*++
@H_404_5@#002 * KiSwapContextInternal
@H_404_5@#003 *
@H_404_5@#004 * The KiSwapContextInternal routine switches context to another thread.
@H_404_5@#005 *
@H_404_5@#006 * Params:
@H_404_5@
下一个将要运行的线程。@H_404_5@
@H_404_5@#007 * ESI - Pointer to the KTHREAD to which the caller wishes to
@H_404_5@#008 * switch to.
@H_404_5@
当前运行的线程。@H_404_5@
@H_404_5@#009 * EDI - Pointer to the KTHREAD to which the caller wishes to
@H_404_5@#010 * switch from.
@H_404_5@#011 *
@H_404_5@#012 * Returns:
@H_404_5@#013 * None.
@H_404_5@#014 *
@H_404_5@#015 * Remarks:
@H_404_5@#016 * Absolutely all registers except ESP can be trampled here for maximum code flexibility.
@H_404_5@#017 *
@H_404_5@#018 *--*/
@H_404_5@#019 .globl @KiSwapContextInternal@0
@H_404_5@#020 .func @KiSwapContextInternal@0,@KiSwapContextInternal@0
@H_404_5@#021 @KiSwapContextInternal@0:
@H_404_5@#022
@H_404_5@
@H_404_5@#023 /* Save the IRQL */
@H_404_5@#024 push ecx
@H_404_5@#025
@H_404_5@
@H_404_5@#026 #ifdef CONFIG_SMP
@H_404_5@#027 GetSwapLock:
@H_404_5@#028 /* Acquire the swap lock */
@H_404_5@#029 cmp byte ptr [esi+KTHREAD_SWAP_BUSY],0
@H_404_5@#030 jz NotBusy
@H_404_5@#031 pause
@H_404_5@#032 jmp GetSwapLock
@H_404_5@#033 #endif
@H_404_5@#034 NotBusy:
@H_404_5@#035 /* Increase context switches (use ES for lazy load) */
@H_404_5@#036 inc dword ptr es:[ebx+KPCR_CONTEXT_SWITCHES]
@H_404_5@#037
@H_404_5@
保存当前线程的运行环境到当前线程栈里。@H_404_5@
@H_404_5@#038 /* Save the Exception list */
@H_404_5@#039 push [ebx+KPCR_EXCEPTION_LIST]
@H_404_5@#040
@H_404_5@#041 /* Check for WMI */
@H_404_5@#042 cmp dword ptr [ebx+KPCR_PERF_GLOBAL_GROUP_MASK],0
@H_404_5@#043 jnz WmiTrace
@H_404_5@#044
@H_404_5@#045 AfterTrace:
@H_404_5@#046 #ifdef CONFIG_SMP
@H_404_5@#047 #ifdef DBG
@H_404_5@#048 /* Assert that we're on the right cpu */
@H_404_5@#049 mov cl,[esi+KTHREAD_NEXT_PROCESSOR]
@H_404_5@#050 cmp cl,[ebx+KPCR_PROCESSOR_NUMBER]
@H_404_5@#052 #endif
@H_404_5@#053 #endif
@H_404_5@#054
@H_404_5@#055 /* Get CR0 and save it */
@H_404_5@#056 mov ebp,cr0
@H_404_5@#057 mov edx,ebp
@H_404_5@#058
@H_404_5@#059 #ifdef CONFIG_SMP
@H_404_5@#060 /* Check NPX State */
@H_404_5@#061 cmp byte ptr [edi+KTHREAD_NPX_STATE],NPX_STATE_LOADED
@H_404_5@#062 jz NpxLoaded
@H_404_5@#063 #endif
@H_404_5@#064
@H_404_5@#065 SetStack:
@H_404_5@
保存当前线程的栈。@H_404_5@
@H_404_5@#066 /* Set new stack */
@H_404_5@#067 mov [edi+KTHREAD_KERNEL_STACK],esp
@H_404_5@#068
@H_404_5@#069 /* Checking NPX,disable interrupts now */
@H_404_5@#070 mov eax,[esi+KTHREAD_INITIAL_STACK]
@H_404_5@#071 cli
@H_404_5@#072
@H_404_5@#073 /* Get the NPX State */
@H_404_5@#074 movzx ecx,byte ptr [esi+KTHREAD_NPX_STATE]
@H_404_5@#075
@H_404_5@#076 /* Clear the other bits,merge in CR0,merge in FPU CR0 bits and compare */
@H_404_5@#077 and edx,~(CR0_MP + CR0_EM + CR0_TS)
@H_404_5@#078 or ecx,edx
@H_404_5@#079 or ecx,[eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)]
@H_404_5@#080 cmp ebp,ecx
@H_404_5@#081 jnz NewCr0
@H_404_5@#082
@H_404_5@#083 StackOk:
@H_404_5@
开中断,并切换到新的栈上。@H_404_5@
@H_404_5@#084 /* Enable interrupts and set the current stack */
@H_404_5@#085 sti
@H_404_5@#086 mov esp,[esi+KTHREAD_KERNEL_STACK]
@H_404_5@#087
@H_404_5@
检查当前线程和下一个运行线程是否同一个进程空间。@H_404_5@
@H_404_5@#088 /* Check if address space switch is needed */
@H_404_5@#089 mov ebp,[esi+KTHREAD_APCSTATE_PROCESS]
@H_404_5@#090 mov eax,[edi+KTHREAD_APCSTATE_PROCESS]
@H_404_5@#091 cmp ebp,eax
@H_404_5@
跳到同一个进程处理。@H_404_5@
@H_404_5@#092 jz SameProcess
@H_404_5@#093
@H_404_5@#094 #ifdef CONFIG_SMP
@H_404_5@#095 /* Get the active processors and XOR with the process' */
@H_404_5@#096 mov ecx,[ebx+KPCR_SET_MEMBER_COPY]
@H_404_5@#097 lock xor [ebp+KPROCESS_ACTIVE_PROCESSORS],ecx
@H_404_5@#098 lock xor [eax+KPROCESS_ACTIVE_PROCESSORS],ecx
@H_404_5@#099
@H_404_5@#100 /* Assert change went ok */
@H_404_5@#101 #ifdef DBG
@H_404_5@#102 test [ebp+KPROCESS_ACTIVE_PROCESSORS],ecx
@H_404_5@#103 jz WrongActivecpu
@H_404_5@#104 test [eax+KPROCESS_ACTIVE_PROCESSORS],ecx
@H_404_5@#105 jz WrongActivecpu
@H_404_5@#106 #endif
@H_404_5@#107 #endif
@H_404_5@#108
@H_404_5@
检查是否需要加载@H_404_5@LDT。@H_404_5@
@H_404_5@#109 /* Check if we need an LDT */
@H_404_5@#110 mov ecx,[ebp+KPROCESS_LDT_DESCRIPTOR0]
@H_404_5@#111 or ecx,[eax+KPROCESS_LDT_DESCRIPTOR0]
@H_404_5@#112 jnz LdtReload
@H_404_5@#113
@H_404_5@
更新@H_404_5@CR3寄存器,以便更新进程的地址空间。其实就是更新内存的页寄存目录。@H_404_5@
@H_404_5@#114 UpdateCr3:
@H_404_5@#115 /* Switch address space */
@H_404_5@#116 mov eax,[ebp+KPROCESS_DIRECTORY_TABLE_BASE]
@H_404_5@#117 mov cr3,eax
@H_404_5@#118
@H_404_5@
同一个进程地址空间。@H_404_5@
@H_404_5@#119 SameProcess:
@H_404_5@#120
@H_404_5@#121 #ifdef CONFIG_SMP
@H_404_5@#122 /* Release swap lock */
@H_404_5@#123 and byte ptr [edi+KTHREAD_SWAP_BUSY],0
@H_404_5@#124 #endif
@H_404_5@#125
@H_404_5@#126 /* Clear gs */
@H_404_5@#127 xor eax,eax
@H_404_5@#128 mov gs,ax
@H_404_5@#129
@H_404_5@
设置下一个线程运行的@H_404_5@TEB。@H_404_5@
@H_404_5@#130 /* Set the TEB */
@H_404_5@#131 mov eax,[esi+KTHREAD_TEB]
@H_404_5@#132 mov [ebx+KPCR_TEB],eax
@H_404_5@#133 mov ecx,[ebx+KPCR_GDT]
@H_404_5@#134 mov [ecx+0x3A],ax
@H_404_5@#135 shr eax,16
@H_404_5@#136 mov [ecx+0x3C],al
@H_404_5@#137 mov [ecx+0x3F],ah
@H_404_5@#138
@H_404_5@
@H_404_5@#139 /* Get stack pointer */
@H_404_5@#140 mov eax,[esi+KTHREAD_INITIAL_STACK]
@H_404_5@#141
@H_404_5@
计算下一个线程运行的栈空间大小。@H_404_5@
@H_404_5@#142 /* Make space for the NPX Frame */
@H_404_5@#143 sub eax,NPX_FRAME_LENGTH
@H_404_5@#144
@H_404_5@
检查是否为虚拟@H_404_5@86的运行模式。@H_404_5@
@H_404_5@#145 /* Check if this isn't V86 Mode,so we can bias the Esp0 */
@H_404_5@#146 test dword ptr [eax - KTRAP_FRAME_SIZE + KTRAP_FRAME_EFLAGS],EFLAGS_V86_MASK
@H_404_5@#147 jnz NoAdjust
@H_404_5@#148
@H_404_5@#149 /* Bias esp */
@H_404_5@#150 sub eax,KTRAP_FRAME_V86_GS - KTRAP_FRAME_SS
@H_404_5@#151
@H_404_5@#152 NoAdjust:
@H_404_5@#153
@H_404_5@
设置下一个运行线程的任务段@H_404_5@TSS。@H_404_5@
@H_404_5@#154 /* Set new ESP0 */
@H_404_5@#155 mov ecx,[ebx+KPCR_TSS]
@H_404_5@#156 mov [ecx+KTSS_ESP0],eax
@H_404_5@#157
@H_404_5@#158 /* Set current IOPM offset in the TSS */
@H_404_5@#159 mov ax,[ebp+KPROCESS_IOPM_OFFSET]
@H_404_5@#160 mov [ecx+KTSS_IOMAPBASE],ax
@H_404_5@#161
@H_404_5@#162 /* Increase context switches */
@H_404_5@#163 inc dword ptr [esi+KTHREAD_CONTEXT_SWITCHES]
@H_404_5@#164
@H_404_5@
@H_404_5@#165 /* Restore exception list */
@H_404_5@#166 pop [ebx+KPCR_EXCEPTION_LIST]
@H_404_5@#167
@H_404_5@#168 /* Restore IRQL */
@H_404_5@#169 pop ecx
@H_404_5@#170
@H_404_5@#171 /* DPC shouldn't be active */
@H_404_5@#172 cmp byte ptr [ebx+KPCR_PRCB_DPC_ROUTINE_ACTIVE],0
@H_404_5@#173 jnz BugCheckDpc
@H_404_5@#174
@H_404_5@#175 /* Check if kernel APCs are pending */
@H_404_5@#176 cmp byte ptr [esi+KTHREAD_PENDING_KERNEL_APC],0
@H_404_5@#177 jnz CheckApc
@H_404_5@#178
@H_404_5@
如果没有异步调用@H_404_5@APC,就直接返回。@H_404_5@
@H_404_5@#179 /* No APCs,return */
@H_404_5@#180 xor eax,eax
@H_404_5@#181 ret
@H_404_5@#182
@H_404_5@
下面检查异步调用@H_404_5@APC。@H_404_5@
@H_404_5@#183 CheckApc:
@H_404_5@#184
@H_404_5@#185 /* Check if they're disabled */
@H_404_5@#186 cmp word ptr [esi+KTHREAD_SPECIAL_APC_DISABLE],0
@H_404_5@#187 jnz ApcReturn
@H_404_5@#188 test cl,cl
@H_404_5@#189 jz ApcReturn
@H_404_5@#190
@H_404_5@#191 /* Request APC Delivery */
@H_404_5@#192 mov cl,APC_LEVEL
@H_404_5@#193 call @HalRequestSoftwareInterrupt@4
@H_404_5@#194 or eax,esp
@H_404_5@#195
@H_404_5@#196 ApcReturn:
@H_404_5@#197
@H_404_5@#198 /* Return with APC pending */
@H_404_5@#199 setz al
@H_404_5@#200 ret
@H_404_5@#201
@H_404_5@
需要重新加局部描述符表@H_404_5@LDT。@H_404_5@
@H_404_5@#202 LdtReload:
@H_404_5@#203 /* Check if it's empty */
@H_404_5@#204 mov eax,[ebp+KPROCESS_LDT_DESCRIPTOR0]
@H_404_5@#205 test eax,eax
@H_404_5@#206 jz LoadLdt
@H_404_5@#207
@H_404_5@#208 /* Write the LDT Selector */
@H_404_5@#209 mov ecx,[ebx+KPCR_GDT]
@H_404_5@#210 mov [ecx+KGDT_LDT],eax
@H_404_5@#211 mov eax,[ebp+KPROCESS_LDT_DESCRIPTOR1]
@H_404_5@#212 mov [ecx+KGDT_LDT+4],eax
@H_404_5@#213
@H_404_5@#214 /* Write the INT21 handler */
@H_404_5@#215 mov ecx,[ebx+KPCR_IDT]
@H_404_5@#216 mov eax,[ebp+KPROCESS_INT21_DESCRIPTOR0]
@H_404_5@#217 mov [ecx+0x108],eax
@H_404_5@#218 mov eax,[ebp+KPROCESS_INT21_DESCRIPTOR1]
@H_404_5@#219 mov [ecx+0x10C],eax
@H_404_5@#220
@H_404_5@#221 /* Save LDT Selector */
@H_404_5@#222 mov eax,KGDT_LDT
@H_404_5@#223
@H_404_5@#224 LoadLdt:
@H_404_5@#225 lldt ax
@H_404_5@#226 jmp UpdateCr3
@H_404_5@#227
@H_404_5@
需要重新计算@H_404_5@CR0寄存器值。@H_404_5@
@H_404_5@#228 NewCr0:
@H_404_5@#229
@H_404_5@#230 #ifdef DBG
@H_404_5@#231 /* Assert NPX State */
@H_404_5@#232 test byte ptr [esi+KTHREAD_NPX_STATE],~(NPX_STATE_NOT_LOADED)
@H_404_5@#233 jnz InvalidNpx
@H_404_5@#234 test dword ptr [eax - (NPX_FRAME_LENGTH - FN_CR0_NPX_STATE)],~(CR0_PE + CR0_MP + CR0_EM + CR0_TS)
@H_404_5@#235 jnz InvalidNpx
@H_404_5@#236 #endif
@H_404_5@#237
@H_404_5@#238 /* Update CR0 */
@H_404_5@#239 mov cr0,ecx
@H_404_5@#240 jmp StackOk
@H_404_5@#241
@H_404_5@#242 #ifdef CONFIG_SMP
@H_404_5@#243 NpxLoaded:
@H_404_5@#244
@H_404_5@#245 /* FIXME: TODO */
@H_404_5@#246 int 3
@H_404_5@#247
@H_404_5@#248 /* Jump back */
@H_404_5@#249 jmp SetStack
@H_404_5@#250 #endif
@H_404_5@#251
@H_404_5@
下面出错处理。@H_404_5@
@H_404_5@#252 WmiTrace:
@H_404_5@#253
@H_404_5@#254 /* No WMI support yet */
@H_404_5@#255 int 3
@H_404_5@#256
@H_404_5@#257 /* Jump back */
@H_404_5@#258 jmp AfterTrace
@H_404_5@#259
@H_404_5@#260 BugCheckDpc:
@H_404_5@#261
@H_404_5@#262 /* Bugcheck the machine,printing out the threads being switched */
@H_404_5@#263 mov eax,[edi+KTHREAD_INITIAL_STACK]
@H_404_5@#264 push 0
@H_404_5@#265 push eax
@H_404_5@#266 push esi
@H_404_5@#267 push edi
@H_404_5@#268 push ATTEMPTED_SWITCH_FROM_DPC
@H_404_5@#269 call _KeBugCheckEx@20
@H_404_5@#270
@H_404_5@#271 #ifdef DBG
@H_404_5@#272 InvalidNpx:
@H_404_5@#273 int 3
@H_404_5@#275 int 3
@H_404_5@#277 int 3
@H_404_5@#278 #endif
@H_404_5@#279 .endfunc
@H_404_5@
通过上面的函数分析,可以了解到线程的环境切换,主要就是线程的页面切换(@H_404_5@CR3),线程的环境块切换(@H_404_5@TEB),任务段切换@H_404_5@ESP0(@H_404_5@TSS)。