从Boot.S文件里初始化系统基本组件后,就跳到空闲函数处理,就成为一个空闲处理的线程,其实也是一个管理系统的任务。下面就来分析KiIdleLoop函数的代码,如下:
#001 .globl @KiIdleLoop@0
#002 .func @KiIdleLoop@0, @KiIdleLoop@0
#003 @KiIdleLoop@0:
#004
获取KPCR的指针地址,这样就可以方便访问KPCR里的成员。
#005 /* Set EBX */
#006 mov ebx, fs:[KPCR_SELF]
#007
跳到主循环开始位置。
#008 /* Jump into mainline code */
#009 jmp MainLoop
#010
#011 CpuIdle:
#012 /* Call the CPU's idle function */
#013 lea ecx, [ebx+KPCR_PRCB_POWER_STATE_IDLE_FUNCTION]
#014 call [ecx]
#015
主循环开始位置。
#016 MainLoop:
#017 /* Cycle interrupts for 1 cycle */
打开中断以便响应中断任务处理。
#018 sti
#019 nop
#020 nop
下面代码运行需要关闭中断,以便打乱线程调度处理。
#021 cli
#022
先检查是否有DPC请求或者时间请求,如果有就需要处理。
#023 /* Check if we have to deliver DPCs, timers, or deferred threads */
#024 mov eax, [ebx+KPCR_PRCB_DPC_QUEUE_DEPTH]
#025 or eax, [ebx+KPCR_PRCB_TIMER_REQUEST]
#026 #ifdef CONFIG_SMP
#027 or eax, [ebx+KPCR_PRCB_DEFERRED_READY_LIST_HEAD]
#028 #endif
#029 jz CheckSchedule
#030
下面处理DPC或时钟请求处理。
清除软件中断。
#031 mov cl, DISPATCH_LEVEL
#032 call @HalClearSoftwareInterrupt@4
#033
#034 /* Handle the above */
处理DPC队列的请求。
#035 lea ecx, [ebx+KPCR_PRCB_DATA]
#036 call @KiRetireDpcList@4
#037
检查是否有线程可以调度运行。
#038 CheckSchedule:
#039 /* Check if a next thread is queued */
检查下一个线程是否准备好。
#040 cmp dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
#041 #ifdef CONFIG_SMP
#042 jz NoNextThread
#043 #else
如果没有准备好的线程,就跳到让CPU空闲处理。
#044 jz CpuIdle
#045 #endif
#046
#047 #ifdef CONFIG_SMP
#048 /* There is, raise IRQL to synch level */
#049 call _KeRaiseIrqlToSynchLevel@0
#050 #endif
打开CPU中断。
#051 sti
#052
#053 /* Set the current thread to ready */
获取当前线程数据指针。
#054 mov edi, [ebx+KPCR_CURRENT_THREAD]
#055 #ifdef CONFIG_SMP
#056 mov byte ptr [edi+KTHREAD_SWAP_BUSY], 1
#057
#058 /* Acquire the PRCB Lock */
#059 lock bts dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
#060 jnb CheckNext
#061 lea ecx, [ebx+KPCR_PRCB_PRCB_LOCK]
#062 call @KefAcquireSpinLockAtDpcLevel@4
#063 #endif
#064
#065 CheckNext:
#066 /* Check if the next thread is the current */
获取下一个线程数据指针。
#067 mov esi, [ebx+KPCR_PRCB_NEXT_THREAD]
#068 #ifdef CONFIG_SMP
#069 cmp esi, edi
#070 jz SameThread
#071 #endif
#072
#073 /* Clear the next thread and set this one instead */
清空下一个线程标志。
#074 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
设置当前线程为下一个运行线程数据指针。
#075 mov [ebx+KPCR_CURRENT_THREAD], esi
#076
设置下一个线程为运行状态。
#077 /* Set the thread as running */
#078 mov byte ptr [esi+KTHREAD_STATE_], Running
#079
#080 #ifdef CONFIG_SMP
#081 /* Disable the idle scheduler and release the PRCB lock */
#082 and byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
#083 and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
#084 #endif
#085
开始切换线程的运行环境。
#086 SwapContext:
#087 /* ReactOS Mm Hack */
切换线程内存空间。
#088 mov ecx, esi
#089 call @MiSyncForContextSwitch@4
#090
从当前线程任务状态切换到即将运行的线程。
#091 /* Swap context at APC_LEVEL */
#092 mov ecx, APC_LEVEL
#093 call @KiSwapContextInternal@0
#094
#095 #ifdef CONFIG_SMP
#096 /* Lower to DPC level */
#097 mov ecx, DISPATCH_LEVEL
#098 call @KfLowerIrql@4
#099 #endif
跳到主循环开始新的处理。
#100 jmp MainLoop
#101
#102 #ifdef CONFIG_SMP
#103 SameThread:
#104 /* Clear the next thread, and put the thready as ready after lock release */
#105 and dword ptr [ebx+KPCR_PRCB_NEXT_THREAD], 0
#106 and dword ptr [ebx+KPCR_PRCB_PRCB_LOCK], 0
#107 and byte ptr [edi+KTHREAD_STATE_], Ready
#108 jmp MainLoop
#109
#110 NoNextThread:
#111 /* Check if the idle scheduler is enabled */
#112 cmp byte ptr [ebx+KPCR_PRCB_IDLE_SCHEDULE], 0
#113 jz CpuIdle
#114
#115 /* It is, so call the scheduler */
#116 lea ecx, [ebx+KPCR_PRCB_DATA]
#117 call @KiIdleSchedule@4
#118 test eax, eax
#119
#120 /* Get new thread pointers and either swap or idle loop again */
#121 mov esi, eax
#122 mov edi, [ebx+KPCR_PRCB_IDLE_THREAD]
#123 jnz SwapContext
#124 jmp MainLoop
#125 #endif
#126 .endfunc
空闲函数主要处理DPC的队列和时间钟请求。如果有线程切换,就改变线程运行环境。