线程调度

Wrk

KiSwapThread(OldThread, CurrentPrcb)
{
       //单核:从Prcb拿一个新线程,如果没有就选(KiSelectReadyThread)一个,如果再没有就拿Idle
       NewThread = CurrentPrcb->NextThread
       //多核:自己还是没有可执行的线程就从其他核去拿,如果有就得看是不是Idle,要是Idle那还得去其他核上找,不是的话就算拿到了
       //切换上下文,FASTCALL KiSwapContext这个函数是个纯汇编函数
       KiSwapContext(OldThread, NewThread)
       {
            ... ...
            mov     ebx, PCR[PcSelfPcr]
            mov     edi, ecx                        ; set old thread address
            mov     esi, edx                        ; set new thread address
            movzx   ecx, byte ptr [edi].ThWaitirql  ; set APC interrupt bypass disable
            call SwapContext     ;这块就把下一条指令的地址压栈了,对应新线程内核栈中的SwitchFrame->RetAddress,不过新线程内核栈存的是KiThreadStartup
            ... ...
       }
}

SwapContext     proc

;
; Save the APC disable flag.
;
        push    ecx                     ; save APC bypass disable ;这条指令就把APC bypass disable的状态压栈了,也能和新线程内核栈中的SwitchFrame->ApcBypassDisable对应上,而新线程是TRUE
cPublicFpo 0, 1

;
; Wait for context to be swapped for the target thread.
;

ifndef NT_UP

sc00:   cmp     byte ptr [esi].ThSwapBusy, 0 ; check if context swap busy
        je      short sc01              ; if e, context swap idle
        YIELD                           ; yield execution for SMT system
        jmp     short sc00              ;

endif

;
; Increment the number of context switches on this processor.
;
; N.B. This increment is done here is force the cache block containing the
;      context switch count into the cache as write exclusive. There are
;      several other references to this cache block in the following code.
;

sc01:   inc     es:dword ptr [ebx]+PcContextSwitches ; processor count

;
; Save the thread exception list head.
;

        push    [ebx]+PcExceptionList   ; save thread exception list head  ;这条指令就把ExceptionList压栈,也和新线程内核栈SwitchFrame->ExceptionList对应上,到这新线程内核栈不会再有东西,那也说明OldThread内核栈不会再增长,要把esp切到NewThread的内核栈了
cPublicFpo 0, 2

;
; Check for context swap logging.
;

        cmp     [ebx]+PcPerfGlobalGroupMask, 0 ; check if logging enable
        jne     sc92                    ; If not, then check if we are enabled
sc03:

ifndef NT_UP

if DBG

        mov     cl, [esi]+ThNextProcessor ; get current processor number
        cmp     cl, [ebx]+PcPrcbData+PbNumber ; same as running processor?
        jne     sc_error2               ; if ne, processor number mismatch

endif

endif

;
; On a uniprocessor system the NPX state is swapped in a lazy manner.
; If a thread whose state is not in the coprocessor attempts to perform
; a coprocessor operation, the current NPX state is swapped out (if needed),
; and the new state is swapped in during the fault.  (KiTrap07)
;
; On a multiprocessor system we still fault in the NPX state on demand, but
; we save the state when the thread switches out (assuming the NPX state
; was loaded).  This is because it could be difficult to obtain the thread's
; NPX in the trap handler if it was loaded into a different processor's
; coprocessor.
;

        mov     ebp, cr0                ; get current CR0
        mov     edx, ebp                ;

ifndef NT_UP

        cmp     byte ptr [edi]+ThNpxState, NPX_STATE_LOADED ; check if NPX state
        je      sc_save_npx_state       ; if e, NPX state not loaded

endif

;
; Save the old stack pointer and compute the new stack limits.
;

sc05:   mov     [edi]+ThKernelStack, esp ; save old kernel stack pointer   ;OldThread的esp保存下来
        mov     eax, [esi]+ThInitialStack ; get new initial stack pointer


;
; (eax) = Initial Stack
; (ebx) = PCR
; (edi) = OldThread
; (esi) = NewThread
; (ebp) = Current CR0
; (edx) = Current CR0
;

.errnz (NPX_STATE_NOT_LOADED - CR0_TS - CR0_MP)
.errnz (NPX_STATE_LOADED - 0)

ifdef NT_UP
;
; On UP systems floating point state might be being changed by an ISR so we
; block interrupts.
;
        cli
endif
        movzx   ecx, byte ptr [esi]+ThNpxState ; new NPX state is (or is not) loaded
        and     edx, NOT (CR0_MP+CR0_EM+CR0_TS) ; clear thread settable NPX bits
        or      ecx, edx                ; or in new thread's cr0
        or      ecx, [eax]+FpCr0NpxState-NPX_FRAME_LENGTH ; merge new thread settable state
        cmp     ebp, ecx                ; check if old and new CR0 match
        jne     sc_reload_cr0           ; if ne, change in CR0
sc06:

ifdef NT_UP
        sti
endif

if DBG
        mov     eax, [esi]+ThKernelStack ; set new stack pointer
        cmp     esi, dword ptr [eax-4]
        je      @f
        int     3
@@:
        xchg    esp, eax
        mov     [eax-4], edi             ; Save thread address on stack below stack pointer
else
        mov     esp, [esi]+ThKernelStack ; set new stack pointer     ;把NewThread的内核栈给esp,完成切换
endif


;
; Check if the old process is the same as the new process.
;

        mov     ebp, [esi].ThApcState.AsProcess ; get old process address     ;这块就是看OldThrad和NewThread是不是属于一个进程,因为要牵扯切换Cr3
        mov     eax, [edi].ThApcState.AsProcess ; get old process address
        cmp     ebp, eax                        ; check if process match
        jz      short sc23                      ; if z, process match

;
; Set the processor bit in the new process and clear the old.
;

ifndef NT_UP

        mov     ecx, [ebx]+PcSetMemberCopy ; get processor set member
   lock xor     [ebp]+PrActiveProcessors, ecx ; set bit in new processor set
   lock xor     [eax]+PrActiveProcessors, ecx ; clear bit in old processor set

if DBG

        test    [ebp]+PrActiveProcessors, ecx ; test if bit set in new set
        jz      sc_error5               ; if z, bit not set in new set
        test    [eax]+PrActiveProcessors, ecx ; test if bit clear in old set
        jnz     sc_error4               ; if nz, bit not clear in old set
endif

endif

;
; LDT switch, If either the target or source process have an LDT we need to
; load the ldt
;

        mov     ecx, [ebp]+PrLdtDescriptor     ;这块就看是不是要重载ldt表
        or      ecx, [eax]+PrLdtDescriptor
        jnz     sc_load_ldt             ; if nz, LDT limit
sc_load_ldt_ret:

;
; Load the new CR3 and as a side effect flush non-global TB entries.
;

        mov     eax, [ebp]+PrDirectoryTableBase ; get new directory base
        mov     cr3, eax                ; and flush TB


;
; Set context swap idle for the old thread.
;

sc23:                                   ;

ifndef NT_UP

        and     byte ptr [edi].ThSwapBusy, 0 ; clear old thread swap busy

endif

        xor     eax, eax
        mov     gs, eax

;
; Set the TEB descriptor to point to the thread TEB and set the TEB address
; in the PCR. The es override here is to force lazy segment loading to occur.
;
        mov     eax, [esi]+ThTeb        ; get user TEB address
        mov     [ebx]+PcTeb, eax        ; set user TEB address     ;把新线程的TEB设到Prcb上
        mov     ecx, [ebx]+PcGdt        ; get GDT address
        mov     [ecx]+(KGDT_R3_TEB+KgdtBaseLow), ax ;              ;改Gdt表中的Teb项
        shr     eax, 16                 ;
        mov     [ecx]+(KGDT_R3_TEB+KgdtBaseMid), al ;
        mov     [ecx]+(KGDT_R3_TEB+KgdtBaseHi), ah ;

;
; Adjust the initial stack address, if necessary, and store in the TSS so V86
; mode threads and 32 bit threads can share a common trapframe structure and
; the NPX save area will be accessible in the same manner on all threads.
;

        mov     eax, [esi].ThInitialStack ; get initial stack address
        sub     eax, NPX_FRAME_LENGTH
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
        test    byte ptr [eax] - KTRAP_FRAME_LENGTH + TsEFlags + 2, EFLAGS_V86_MASK / 10000h
        jnz     short sc24              ; if nz, V86 frame, no adjustment
        sub     eax, TsV86Gs - TsHardwareSegSs ; bias for missing fields
sc24:   mov     ecx, [ebx]+PcTssCopy    ; get TSS address
        mov     [ecx]+TssEsp0, eax      ; set initial kernel stack address

;
; Set the IOPM map offset value.
;
; N.B. This may be a redundant load of this value if the process did not
;      change during the context switch. However, always reloading this
;      value saves several instructions under the context swap lock.
;

        mov     ax, [ebp]+PrIopmOffset  ; set IOPM offset
        mov     [ecx]+TssIoMapBase, ax  ;
;
; Update context switch counters.
;

        inc     dword ptr [esi]+ThContextSwitches ; thread count

;
; Restore thread exception list head and get APC bypass disable.
;

        pop     [ebx].PcExceptionList   ; restore thread exception list head
        pop     ecx                     ; get APC bypass disable

;
; Check if an attempt is being made to context switch while in a DPC routine.
;

        cmp     byte ptr [ebx]+PcPrcbData+PbDpcRoutineActive, 0 ; check if DPC active
        jne     sc91                    ; bugcheck if DPC active.     ;这时候不能有Dpc

;
; If the new thread has a kernel mode APC pending, then request an APC
; interrupt.
;

        cmp     byte ptr [esi].ThApcState.AsKernelApcPending, 0 ; APC pending?
        jne     short sc80              ; if ne, kernel APC pending
        xor     eax, eax                ; set return value
        ret                             ; return

;
; The new thread has an APC interrupt pending.
;
; If the the special APC disable count is nonzero, then return no kernel APC
; pending. An APC will be requested when the special APC disable count reaches
; zero.
;
; If APC interrupt bypass is not enabled, then request a software interrupt
; at APC_LEVEL and return no kernel APC pending. Otherwise, return kernel APC
; pending.
;

sc80:   cmp     word ptr [esi].ThSpecialApcDisable, 0 ; check if special APC disable
        jne     short sc90              ; if ne, special APC disable
        test    cl, cl                  ; test for APC bypass disable
        jz      short sc90              ; if z, APC bypass enabled
        mov     cl, APC_LEVEL           ; request software interrupt level
        fstCall HalRequestSoftwareInterrupt ;
        or      eax, esp                ; clear ZF flag
sc90:   setz    al                      ; set return value
        ret                             ; return


;
; Set for new LDT value
;

sc_load_ldt:
        mov     eax, [ebp+PrLdtDescriptor] ;
        test    eax, eax
        je      @f
        mov     ecx, [ebx]+PcGdt        ; get GDT address
        mov     [ecx+KGDT_LDT], eax     ;
        mov     eax, [ebp+PrLdtDescriptor+4] ;
        mov     [ecx+KGDT_LDT+4], eax   ;

;
; Set up int 21 descriptor of IDT.  If the process does not have an Ldt, it
; should never make any int 21 calls.  If it does, an exception is generated. If
; the process has an Ldt, we need to update int21 entry of LDT for the process.
; Note the Int21Descriptor of the process may simply indicate an invalid
; entry.  In which case, the int 21 will be trapped to the kernel.
;

        mov     ecx, [ebx]+PcIdt        ;
        mov     eax, [ebp+PrInt21Descriptor] ;
        mov     [ecx+21h*8], eax        ;
        mov     eax, [ebp+PrInt21Descriptor+4] ;
        mov     [ecx+21h*8+4], eax      ;
        mov     eax, KGDT_LDT
@@:     lldt    ax
        jmp     sc_load_ldt_ret

;
; Cr0 has changed (ie, floating point processor present), load the new value.
;

sc_reload_cr0:

if DBG

        test    byte ptr [esi]+ThNpxState, NOT (CR0_TS+CR0_MP)
        jnz     sc_error                ;
        test    dword ptr [eax]+FpCr0NpxState-NPX_FRAME_LENGTH, NOT (CR0_PE+CR0_MP+CR0_EM+CR0_TS)
        jnz     sc_error3               ;

endif

        mov     cr0,ecx                 ; set new CR0 NPX state
        jmp     sc06

;
; Save coprocessor's current context.  FpCr0NpxState is the current thread's
; CR0 state.  The following bits are valid: CR0_MP, CR0_EM, CR0_TS.  MVDMs
; may set and clear MP & EM as they please and the settings will be reloaded
; on a context switch (but they will not be saved from CR0 to Cr0NpxState).
; The kernel sets and clears TS as required.
;
; (ebp) = Current CR0
; (edx) = Current CR0
;

ifndef NT_UP

sc_save_npx_state:
        and     edx, NOT (CR0_MP+CR0_EM+CR0_TS) ; we need access to the NPX state

        mov     ecx, [edi].ThInitialStack        ; get NPX save save area address
        sub     ecx, NPX_FRAME_LENGTH

        cmp     ebp, edx                        ; Does CR0 need reloading?
        je      short sc_npx10

        mov     cr0, edx                        ; set new cr0
        mov     ebp, edx                        ; (ebp) = (edx) = current cr0 state

sc_npx10:

;
; The fwait following the fnsave is to make sure that the fnsave has stored the
; data into the save area before this coprocessor state could possibly be
; context switched in and used on a different (co)processor.  I've added the
; clocks from when the dispatcher lock is released and don't believe it's a
; possibility.  I've also timed the impact this fwait seems to have on a 486
; when performing lots of numeric calculations.  It appears as if there is
; nothing to wait for after the fnsave (although the 486 manual says there is)
; and therefore the calculation time far outweighed the 3clk fwait and it
; didn't make a noticable difference.
;

;
; If FXSR feature is NOT present on the processor, the fxsave instruction is
; patched at boot time to start using fnsave instead
;

_ScPatchFxb:
;       fxsave  [ecx]                   ; save NPX state
        db      0FH, 0AEH, 01
_ScPatchFxe:

        mov     byte ptr [edi]+ThNpxState, NPX_STATE_NOT_LOADED ; set no NPX state
        mov     dword ptr [ebx].PcPrcbData+PbNpxThread, 0  ; clear npx owner
        jmp     sc05
endif

;
; This code is out of line to optimize the normal case with tracing is off.
;

sc92:   mov     eax, [ebx]+PcPerfGlobalGroupMask ; Load the ptr into eax
        cmp     eax, 0                  ; catch race condition on pointer here
        jz      sc03                    ; instead of above in mainline code
        mov     edx, esi                ; pass the new ETHREAD object
        mov     ecx, edi                ; pass the old ETHREAD object
        test    dword ptr [eax+PERF_CONTEXTSWAP_OFFSET], PERF_CONTEXTSWAP_FLAG
        jz      sc03                    ; return if our flag is not set

        fstCall WmiTraceContextSwap     ; call the Wmi context swap trace
        jmp     sc03                    ;

;
; A context switch was attempted while executing a DPC - bugcheck.
;

.fpo (2, 0, 0, 0, 0, 0)
sc91:
        mov     eax, [edi]+ThInitialStack ; get the old stack so it can
                                          ; be saved in the minidump
        stdCall _KeBugCheckEx <ATTEMPTED_SWITCH_FROM_DPC, edi, esi, eax, 0>
        ret                             ; return

if DBG
sc_error5:  int 3
sc_error4:  int 3
sc_error3:  int 3
sc_error2:  int 3
sc_error:   int 3
endif

SwapContext     endp
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值