NT kernel process scheduling Analysis notes

Source: Internet
Author: User
Tags apc

Author: sinister
Information Source: white blood cells

Author: sinister
Email: sinister@whitecell.org


Homepage: http://www.whitecell.org


Date: 2005-11-16

2005-2-15

As we all know, nt kernel runs in a multi-task Preemptive test mode. On a non-SMP system, each process assigns a unique
Set the CPU time slice to achieve the execution purpose, it looks like multiple tasks are running at the same time. Nt kernel
It also takes thread scheduling as the core, so that thread switching means switching of the current process. Repeated Systems
This process enables each process to run. Before introducing the process, you must first understand the internal structure of the system: KPC
R, ETHREAD, EPROCESS, it can be said that the functions involved in process scheduling are basically for these important structures
Set and fill. People familiar with the system kernel are naturally familiar with the details of each structure, so I don't know much about it.
Nonsense. Here is a combination of process scheduling. The KPCR structure stores the processes of the current CPU.
Information, including the ETHREAD structure of the currently running thread. The ETHREAD structure and EPROC
The ESS structure is correlated. This means that many core functions start with the address 0xFFDFF000 + through KPCR.
The reason for running threads and processes can be obtained through migration, such as KeGetCurrentThread () and IoGetCu.
R1_process. This also indicates that in a non-SMP system, the current CPU processes only
There may be one. During process scheduling, the system selects the thread ETHR based on the currently alive process.
The EAD structure replaces ETHREAD in KPCR and changes it to a running state. How to trigger
Can a process scheduling request be executed by all processes? As mentioned at the beginning, each process allocates a specific CPU
Time slice for execution, and the CPU clock interruption of the system determines the time slice allocated by each process. That's it.
It is a process scheduling request generated when the system CPU clock is interrupted. Analyze the scheduling process and functions in detail
Before the branch, let's take a look at the general process. First, when the CPU clock is interrupted, the system calls KiDi
SpatchInterrupt (): Compares the time slice allocated by the current process. If it is used up, KiQuantumEnd () is called ()
Select a new thread (ETHREAD) based on the thread priority and other information with a specific scheduling algorithm, and then return it
Value, an ETHREAD structure as the parameter, to call SwapContext () to set ETHREAD, in EPROCESS
And replace the corresponding structure in KPCR to complete thread switching and schedule it to another process (EPROCESS)
(ETHREAD. (When the thread waits for an Event or semaphores (Semaphore)
Will automatically discard the current time slice ). Through the above rough analysis, we can see that it is closely related to process scheduling.
KiDispatchInterrupt () and SwapContext (). Next we will discuss these two key functions.
The scheduling function is analyzed in detail.

First, let's take a look at the KiDispatchInterrupt () function. When calling this function, first obtain the current KPCR itself.
Structure and DCP linked list header, and compare whether DPC is currently processing, if DPC is processing, Set
Abnormal DPC refining. If no value exists, the QuantumEnd value in KPCR is directly compared. QuantumEnd indicates
The total number of time slices being processed in the current KPCR. This value is based on the current running thread ETHREAD-> Quant
The value in um is filled, that is, if the QuantumEnd value in KPCR is not 0, it does not mean that the current thread cannot be switched.
In this case, you also need to call KiQuantumEnd () to determine whether to allow switching. So when the current value is not 0
Jump to the KiQuantumEnd () function for further judgment. The KiQuantumEnd () function retrieves the current thread.
The Quantum value in the (ETHREAD) structure is used to determine whether the value is 0. KiFind is called based on parameters such as thread priority.
ReadyThread () function to select a new thread to fill the NextThread in KPCR, and
As the return value of the function, jump to the corresponding address and continue thread switching. If the returned value is null
Line switch, function return. However, if the QuantumEnd in KPCR is already 0, it indicates the current thread ETHREAD
If the allocated time slice is used up, you can change the thread, so continue to compare whether there is any next thread in KPCR (offset
NextThread). If the offset of the next thread (offset NextThread) in KPCR is null, the thread is not ready.
You can switch to the return address to complete the function call. If it is not empty, you can continue thread switching.
All the required basic parameters are ready. The following is to set the Next thread in KPCR (offset Next
Thread), replace it with the current Thread (offset CurrentThread) in KPCR, and (
Offset NextThread) Clear 0, call KiReadyThread () ready for the next thread (offset NextTh
Read), that is, the current thread (offset CurrentThread ). Finally, the SwapContext () function is called.
Number to complete the final switch.

Next let's take a look at the implementation of the SwapContext () function. The above mentioned SwapContext () is in the KiDispatch
Called in the Interrupt () function to complete the final thread switching. The function first sets the new thread to be switched.
Status (NextThread-> Status) is the running Status. Next, determine whether DCP columns are currently running (
Whether or not the DpcRoutineActive value in KPCR is 0. If it is not 0, it indicates that DPC processing is currently in progress.
During row-based thread scheduling, DPC columns are not allowed to run. Otherwise, the system will crash. In fact, this is not a must, but only Microsoft
If yes, the system will crash when the KeBugCheck () function is called. If not, continue
Obtain the Debug flag status of the new thread (NextThread-> DebugActive) to be switched and assign it to Debug in KPCR.
Active. Save ESP to the kernel stack of the old thread (CurrentThread-> KernelStack) to be switched,
And the stack in the new thread (NextThread-> InitalStack, NextThread-> StackLimit) to be switched
The start address and size are assigned to the corresponding location in KPCR. Continue to get the new thread (NextThread-> NpxSta
The NPX status in te) is compared with the NPX status of CR0. For example, if the status is not equal, the system jumps to the position where the CR0 is reset.
Processing. After refreshing CR0, the system jumps back and continues the following operations. (The NPX bit status in CR0 is
Several commands trigger an exception and enter a special State to process floating point commands. In this case, thread switching is not allowed,
Therefore, you need to refresh the CR0 status in different cases. ) Next, determine the mode to be switched.
Whether the thread (NextThread) is running in V86 mode. If it is to continue to adjust the kernel stack space. If not
Skip adjustment. Obtain the KTSS address from KPCR and save the NPX flag to ESP0 in KTSS (
The threads running in V86 mode can be shared ). At this time, all the logos, structures, and parameters are ready,
The following is a specific switchover process. First, take the new thread (NextThread-> Kernel
Stack), and set the user Stack (TEB) in KPCR
User stack (TEB) of the new thread (NextThread-> TEB ). Then put the user stack (TEB) into KPCR
In the corresponding structure of GDT. Compare the processes in the old thread (CurrentThread-> EPROCESS) to be switched,
Is it equal to the process in the new thread (NextThread-> EPROCESS) to be switched? That is, to determine the inbound
Whether the process is the current process. If so, the system jumps directly to the current page instead of refreshing the directory table () and other related values of the current page.
Add the current process switch count and determine whether the current thread has a Pending APC call, and then exit.
To switch. If the thread to be switched is not the current process, it will be switched from the new thread (NextThread-> EPROCESS)
Obtain the current process from the current process (EPROCESS-> DirectoryTableBase) and obtain the page Directory table to update.
In KPCR, The TSSCR3 value in KTSS is the same as the value in the CR 3 register.
The directory address on the previous process page) continues to attach the IOPM value in the current process (EPROCESS-> IopmOffset) to the KPCR
IOPM in KTSS. Then compare whether the LDT in the current process (EPROCESS-> LdtDescriptor) is empty. If
If it is not empty, the location of KGDT is obtained from KPCR, And the LDT is obtained from KGDT, and the current process (EPROCESS
-> LdtDescriptor) and LDT in KPCR. Then obtain the location of the KIDT in KPCR
INT 21 interrupt in process (EPROCESS-> Int21Descriptor) is assigned the corresponding location in KIDT in KPCR,
Enables the current process to call INT 21. Finally, call LLDT to make all current settings take effect. (It is reasonable to say that the NT kernel
The 32-bit application does not use LDT, but why is LDT set in thread switching? This is
The application is backward compatible with 16 bits. When it is scheduled to a 16-bit application, it will be specially assigned LDT and
Make INT 21 in IDT valid. Anyone who has played DOS knows that INT 21 is a system call in DOS.
To try to run a 16-bit DOS program, and then observe the IDT table, we will find that INT
21 will be set to a 16-bit TrapGate) Otherwise, if it is null, It will be set to do not use LDT, and
The number of switches in the new thread (NextThread-> ContextSwitches) and the total number of switches in KPCR are added one by one,
Recover the exception chain and compare the APC calls in the new thread (NextThread-> KernelApcPending) to be switched.
Whether the call is complete. If the current APC status is incomplete, it determines whether the call can be processed,
If not, set the return flag to Pending to complete all the work of thread switching and return. Otherwise, set the current
IRQL is apc level and calls the HalRequestSoftwareInterrupt () function to process APC Pending
Status. After processing, the Pending status is cleared, and all tasks of thread switching are completed and returned. If the current APC
If the status is complete, the values of registers and Mark registers are restored and returned to complete all tasks of thread switching.

When calling the KiDispatchInterrupt () function,

: U KiDispatchInterrupt l 1000
Ntoskrnl! KiDispatchInterrupt
0008: 80467DD0 mov ebx, [FFDFF01C]
0008: 80467DD6 lea eax, [EBX + 00000800]

Obtain the current KPCR structure and DCP linked list header, EAX = DPC, EBX = KPCR

0008: 80467DDC CLI
0008: 80467DDD cmp eax, [EAX]
0008: 80467DDF JZ 80467DFE

And compare whether the current DPC is empty. If it is empty, Jump directly to compare KPCR
Whether the QuantumEnd value and comparison have the structure of the next thread (ETHREAD)

0008: 80467DE1 PUSH EBP
0008: 80467DE2 push dword ptr [EBX]
0008: 80467DE4 mov dword ptr [EBX], FFFFFFFF
0008: 80467DEA mov edx, ESP
0008: 80467DEC mov esp, [EBX + 0000081C] Note: Get the DPC Stack
0008: 80467DF2 PUSH EDX
0008: 80467DF3 mov ebp, EAX
0008: 80467DF5 CALL 804633E7 Note: KiRetireDpcList () function
0008: 80467DFA POP ESP
0008: 80467DFB pop dword ptr [EBX]
0008: 80467DFD POP EBP

Set a DPC exception chain

0008: 80467DFE STI
0008: 80467DFF cmp dword ptr [EBX + 00000870], 00 Note: QuantumEnd thread time slice
0008: 80467E06 JNZ 80467E5A

If the QuantumEnd value in KPCR is not 0, it indicates that the current thread (ETHREAD) time slice may not be used up,
Jump to determine whether the current thread (ETHREAD) can be switched.

0008: 80467E08 cmp dword ptr [EBX + 00000128], 00 comment: NextThread (ETHREAD structure)
0008: 80467E0F JZ 80467E59

Whether the next thread exists. If the NextThread (ETHREAD) offset of the next thread in KPCR is null, skip
Go to the return address to complete the function call.

0008: 80467E11 mov eax, [EBX + 00000128]

In this case, EAX = NextThread (ETHREAD structure)

0008: 80467E17 sub esp, 0C
0008: 80467E1A MOV [ESP + 08], ESI
0008: 80467E1E MOV [ESP + 04], EDI
0008: 80467E22 MOV [ESP], EBP
0008: 80467E25 mov esi, EAX
0008: 80467E27 mov edi, [EBX + 00000124] Note: CurrentThread (ETHREAD structure)
0008: 80467E2D mov dword ptr [EBX + 00000128], 00000000
0008: 80467E37 MOV [EBX + 00000124], ESI
0008: 80467E3D mov ecx, EDI
0008: 80467E3F CALL 8042F944 Note: KiReadyThread Function

Replace the NextThread (ETHREAD) Structure of the next thread in KPCR with the Current
Thread (ETHREAD) Thread structure, and the next Thread NextThread (ETHREAD) offset in KPCR is cleared 0,
Call KiReadyThread () to get ready the next thread NextThread (ETHREAD) structure just set. Now
Is the current thread CurrentThread (ETHREAD) structure.

0008: 80467E44 mov cl, 01

Set the apc irql flag. When SwapContext () is called, the current IRQL is set to APC_LEVEL.

0008: 80467E46 CALL 80467E70 Note: SwapContext Function
0008: 80467E4B mov ebp, [ESP]
0008: 80467E4E mov edi, [ESP + 04]
0008: 80467E52 mov esi, [ESP + 08]
0008: 80467E56 add esp, 0C

Call SwapContext () to complete thread switching. (_ Fastcall call specification to balance the stack)

0008: 80467E59 RET Note: After the KiDispatchInterrupt function is called, return.

0008: 80467E5A mov dword ptr [EBX + 00000870], 00000000 Note: QuantumEnd
0008: 80467E64 CALL 803476d2 Note: KiQuantumEnd
0008: 80467E69 or eax, EAX Note: EAX = NextThread (ETHREAD)
0008: 80467E6B JNZ 80467E17
0008: 80467E6D RET Note: After the KiDispatchInterrupt function is called, return.

Set the QuantumEnd value in KPCR to 0 and call the KiQuantumEnd function to handle the problem.
The Quantum value in the structure of the current thread (ETHREAD) is used to determine whether the value is 0. If yes, the thread can be switched.
In other words, call the KiFindReadyThread () function based on thread priority and other parameters to select a new thread to fill the KPC.
In R, the NextThread (ETHREAD structure) is assigned to EAX and
Jump to the thread to switch the address. If EAX returns an empty value, it indicates that the switchover fails and the function returns.

_______________________________________________________________________________________

The above basically completes Thread Scheduling in each part. We can see that a process runs alternately. below
SwapContext function to complete the actual switch.

SwapContext ()

EBX = KPCR
ESI = NextThread
EDI = CurrentThread

0008: 80467E6E mov edi, EDI
0008: 80467E70 or cl, CL
0008: 80467E72 mov byte ptr es: [ESI + 2D], 02 (ETHREAD-> State)

Set the new thread NextThread (ETHREAD) to the running state.

0008: 80467E77 PUSHFD
0008: 80467E78 mov ecx, [EBX]
0008: 80467E7A cmp dword ptr [EBX + 0000080C], 00
0008: 80467E81 PUSH ECX
0008: 80467E82 JNZ 80467F7D

During thread switching, DPC columns are not allowed. Therefore, compare
Whether DpcRoutineActive is 0. If it is not 0, DPC processing is currently in progress,
Go to the KeBugCheck () function and display a blue screen.

0008: 80467E88 mov ebp, CR0 Note: EBP = CR0
0008: 80467E8B mov edx, EBP Note: EDX = CR0
0008: 80467E8D mov cl, [ESI + 2C]
0008: 80467E90 MOV [EBX + 50], CL

Obtain the status of the new thread NextThread (ETHREAD) debugging flag to be switched and assign it to KPCR
DebugActive flag.

0008: 80467E93 CLI
0008: 80467E94 MOV [EDI + 28], ESP

Assign the stack pointer to the KernelStack in the CurrentThread (ETHREAD) of the old thread to be switched.

0008: 80467E97 mov eax, [ESI + 18]
0008: 80467E9A mov ecx, [ESI + 1C]
0008: 80467E9D sub eax, 00000210
0008: 80467EA2 MOV [EBX + 08], ECX
0008: 80467EA5 MOV [EBX + 04], EAX

The initialization stack (InitalStack) and
Stack size (StackLimit) is assigned to the corresponding location in KPCR for processing.

0008: 80467EA8 xor ecx, ECX
0008: 80467EAA mov cl, [ESI + 31]
0008: 80467EAD and edx,-0F
0008: 80467EB0 or ecx, EDX
0008: 80467EB2 or ecx, [EAX + 0000020C]
0008: 80467EB8 cmp ebp, ECX
0008: 80467EBA JNZ 80467F75

Obtain the NPX bit in the new thread NextThread (ETHREAD) to be switched and the NPX bit in the CR0.
For comparison, if not equal, jump to reset the CR0 for processing.

0008: 80467EC0 test dword ptr [EAX-1C], 00020000
0008: 80467EC7 JNZ 80467ECC

Determine whether the current V86 mode is used. If not, directly jump to the obtained KTSS.

0008: 80467EC9 sub eax, 10

In V86 mode, the kernel stack space is adjusted.

0008: 80467ECC mov ecx, [EBX + 40] Note: ECX = KPCR-> KTSS
0008: 80467ECF MOV [ECX + 04], EAX
0008: 80467ED2 mov esp, [ESI + 28]
0008: 80467ED5 mov eax, [ESI + 20]
0008: 80467ED8 MOV [EBX + 18], EAX Note: EAX = TEB

Obtain the KTSS address from KPCR and save the NPX bit to ESP0 in KTSS.
Kernel stack (KernelStack) of the new thread NextThread (ETHREAD) to be switched
ESP, and set the user stack (TEB) in KPCR to the new thread NextThread to be switched
(ETHREAD) User stack (TEB ).

0008: 80467EDB STI
0008: 80467EDC mov ecx, [EBX + 3C] Note: ECX = KPCR-> GDT
0008: 80467EDF MOV [ECX + 3A], AX
0008: 80467EE3 shr eax, 10
0008: 80467EE6 MOV [ECX + 3C], AL
0008: 80467EE9 shr eax, 08
0008: 80467EEC MOV [ECX + 3F], AL
0008: 80467EEF mov eax, [EDI + 44]
0008: 80467EF2 cmp eax, [ESI + 44]
0008: 80467EF5 JZ 80467F20

Put the user stack (TEB) into the corresponding structure of GDT in KPCR. Compare
The EPROCESS in the old thread CurrentThread (ETHREAD) and the new line to be switched
Are the eprocesses in the process NextThread (ETHREAD) equal? That is, to determine whether to switch
Is the current process? If yes, the current page Directory table (MCM) and other related
To add the current process switch count and determine whether the current thread has Pending
And then exit to complete the switchover.

0008: 80467EF7 mov edi, [ESI + 44] Note: EDI = NextThread-> EPROCESS
0008: 80467EFA xor eax, EAX
0008: 80467EFC mov gs, AX
0008: 80467EFF mov eax, [EDI + 18] Note: EAX = EPROCESS-> DirectoryTableBase
0008: 80467F02 mov ebp, [EBX + 40] Note: EBP = KPCR-> KTSS
0008: 80467F05 mov ecx, [EDI + 30] Note: EDI = EPROCESS-> IopmOffset
0008: 80467F08 MOV [EBP + 1C], EAX
0008: 80467F0B MOV 32a, EAX Note: 32a = EPROCESS-> DirectoryTableBase
0008: 80467F0E MOV [EBP + 66], CX
0008: 80467F12 xor eax, EAX
0008: 80467F14 CMP [EDI + 20], AX
0008: 80467F18 JNZ 80467F47
0008: 80467F1A LLDT AX
0008: 80467F1D lea ecx, [ECX + 00]

If the thread to be switched is not the current process, it will be switched from the new thread NextThread (ETHREAD)
Obtain the current process (EPROCESS), and obtain the page Directory table from the current process (EPROCESS)
The TSSCR3 value in the new KPCR-> KTSS and the value in the CR 3 register continue
IOPM value is associated with iopm in the KPCR-KTSS. Then compare whether the LDT in the current process (EPROCESS) is
Null. If it is not null, the execution will jump to the set LDT. Otherwise, LDT is set to null.

0008: 80467F20 inc dword ptr [ESI + 4C]
0008: 80467F23 inc dword ptr [EBX + 000005C0]
0008: 80467F29 POP ECX
0008: 80467F2A MOV [EBX], ECX
0008: 80467F2C cmp byte ptr [ESI + 49], 00
0008: 80467F30 JNZ 80467F36
0008: 80467F32 POPFD
0008: 80467F33 xor eax, EAX
0008: 80467F35 RET Note: After the SwapContext function is called, return.

Replace ContextSwitches in the new thread NextThread (ETHREAD) to be switched
Each KeContextSwitches is added with one value, which indicates the number of thread switches. Restore the exception chain,
And compare the KernelApcPending in the new thread NextThread (ETHREAD) to be switched. If the current
If the APC status is Pending, it will jump to the processing APC Pending address to continue. Not Pending
Then, the values of registers and Mark registers are restored and returned to complete all tasks of thread switching.

0008: 80467F36 POPFD
0008: 80467F37 JNZ 80467F3C
0008: 80467F39 mov al, 01
0008: 80467F3B RET

Determines whether APC calls can be processed currently. If not, sets the return flag to Pending to complete thread switching.
And return. Otherwise, jump to the Soft Interrupt address to continue.

0008: 80467F3C mov cl, 01 Note: IRQL = APC LEVEL
0008: 80467F3E CALL [HAL! HalRequestSoftwareInterrupt]
0008: 80467F44 xor eax, EAX
0008: 80467F46 RET

Set the current IRQL to apc level and call the HalRequestSoftwareInterrupt () function for processing.
APC Pending status. After processing, the Pending status is cleared, and all tasks of thread switching are completed and returned.

0008: 80467F47 mov ebp, [EBX + 3C] Note: EBP = KPCR-> KGDT
0008: 80467F4A mov eax, [EDI + 20] Note: EAX = EPROCESS-> LdtDescriptor
0008: 80467F4D MOV [EBP + 48], EAX
0008: 80467F50 mov eax, [EDI + 24]
0008: 80467F53 MOV [EBP + 4C], EAX
0008: 80467F56 mov eax, 00000048
0008: 80467F5B mov ebp, [EBX + 38] Note: EBP = KPCR-> KIDT
0008: 80467F5E mov ecx, [EDI + 28] Note: ECX = EPROCESS-> Int21Descriptor
0008: 80467F61 MOV [EBP + 00000108], ECX
0008: 80467F67 mov ecx, [EDI + 2C]
0008: 80467F6A MOV [EBP + 0000010C], ECX
0008: 80467F70 LLDT AX
0008: 80467F73 JMP 80467F20

Obtain the location of KGDT from KPCR, and import it to LDT from KGDT
LDT is associated with LDT in KPCR. Then obtain the location of the KIDT in KPCR, and
INT 21 interrupt is assigned the corresponding location in the KIDT in KPCR so that the current process can call INT 21. Last call
LLDT takes effect for all current settings and jumps back to the number of added thread switches and determines whether the current APC Pending continues to run
Line.

0008: 80467F75 MOV CR0, ECX
0008: 80467F78 JMP 80467EC0

Reset the NPX bit of CR0, and jump up to determine whether it is in V86 mode.

0008: 80467F7D PUSH 000000B8
0008: 80467F82 CALL ntoskrnl! KeBugCheck
0008: 80467F87 RET

Call the blue screen function and restart the system after a crash.

Note was written during the Spring Festival this year. At that time, only half of the analysis found that the original WIN2K source code has
After this part is included, the compilation has been simply annotated and simply written. Errors
Inevitably, I hope you can correct it.

Reference resources: Windows 2000 Source Code
Thanks to FlashSky and SoBeIt for discussing with me.

WSS (Whitecell Security Systems) is a non-profit civil technology organization dedicated to the research of various system Security technologies. Stick to the traditional hacker spirit and pursue the pure technology.
WSS home: http://www.whitecell.org/


WSS Forum: http://www.whitecell.org/forums/

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.