三、进程与线程
3.1 NT系统中的进程和线程
前面已经介绍过,Windows NT系统下,每个进程都拥有属于自己的地址空间。不仅如此,在NT系统中,进程是程序运行的基本单位。一个应用程序被运行后,系统会为其创建一个进程。每个进程在运行时独享各种硬件资源(但并不意味着它一直占有这些资源,进程会被打断或者进入等待状态)。
至于线程,在NT系统中则必定属于某一个进程,属于同一进程的线程共享该进程的地址空间。在多线程(含多处理器)系统运行时,线程为每个逻辑核心所运行的基本单位,因此理论上每个线程都享有独立的全套寄存器资源(IA32架构下每个核心都拥有自己的全套寄存器,不过同步多线程技术模拟出的“逻辑核心”是否拥有全部的独立寄存器资源这里还不能肯定)。因此,一个进程必然包含一个线程(称为“主线程”)。
NT系统通过“时间片”来设置每个进程或是线程执行的时间,因此即使在仅拥有1个单核心单线程的处理器的系统上,仍然能使得用户感觉程序是“同步执行”的。这是因为每个线程运行的时间都非常短暂,用户感觉不到进程的切换。被切换出去的线程处于“等待”状态,系统会把与之相关的数据存储起来,在下次被切换回去的时候再次载入。有时候,这些相关的数据(主要是各寄存器的值)被称为“执行环境”。
还有一线程无法继续执行的情况就是引发了中断。Windows NT中,用户层线程的中断级别最低,任何中断都可以打断它的运行,直到中断处理结束(有可能中断处理会把线程杀死,这样这个线程就再也没有机会继续执行代码了)。关于中断的内容,请参考第四章。
需要明确的是,系统调度的对象是线程而不是进程。在一个多逻辑处理器的系统中,可能存在多个不同进程中的线程同时在执行的情况。
3.2NT系统的进程管理
系统用EPROCESS结构的对象描述描述一个进程,该结构的定义如下(这里给出了完整的定义,包含IA32E模式下的情况):
typedef struct _EPROCESS {
KPROCESS Pcb;
//
// Lock used to protect:
// The list of threads in the process.
// Process token.
// Win32 process field.
// Process and thread affinity setting.
//
EX_PUSH_LOCK ProcessLock;
LARGE_INTEGER CreateTime;
LARGE_INTEGER ExitTime;
//
// Structure to allow lock free cross process access to the process
// handle table, process section and address space. Acquire rundown
// protection with this if you do cross process handle table, process
// section or address space references.
//
EX_RUNDOWN_REF RundownProtect;
HANDLE UniqueProcessId;
//
// Global list of all processes in the system. Processes are removed
// from this list in the object deletion routine. References to
// processes in this list must be done with ObReferenceObjectSafe
// because of this.
//
LIST_ENTRY ActiveProcessLinks;
//
// Quota Fields.
//
SIZE_T QuotaUsage[PsQuotaTypes];
SIZE_T QuotaPeak[PsQuotaTypes];
SIZE_T CommitCharge;
//
// VmCounters.
//
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
LIST_ENTRY SessionProcessLinks;
PVOID DebugPort;
PVOID ExceptionPort;
PHANDLE_TABLE ObjectTable;
//
// Security.
//
EX_FAST_REF Token;
PFN_NUMBER WorkingSetPage;
KGUARDED_MUTEX AddressCreationLock;
KSPIN_LOCK HyperSpaceLock;
struct _ETHREAD *ForkInProgress;
ULONG_PTR HardwareTrigger;
PMM_AVL_TABLE PhysicalVadRoot;
PVOID CloneRoot;
PFN_NUMBER NumberOfPrivatePages;
PFN_NUMBER NumberOfLockedPages;
PVOID Win32Process;
struct _EJOB *Job;
PVOID SectionObject;
PVOID SectionBaseAddress;
PEPROCESS_QUOTA_BLOCK QuotaBlock;
PPAGEFAULT_HISTORY WorkingSetWatch;
HANDLE Win32WindowStation;
HANDLE InheritedFromUniqueProcessId;
PVOID LdtInformation;
PVOID VadFreeHint;
PVOID VdmObjects;
PVOID DeviceMap;
PVOID Spare0[3];
union {
HARDWARE_PTE PageDirectoryPte;
ULONGLONG Filler;
};
PVOID Session;
UCHAR ImageFileName[ 16 ];
LIST_ENTRY JobLinks;
PVOID LockedPagesList;
LIST_ENTRY ThreadListHead;
//
// Used by rdr/security for authentication.
//
PVOID SecurityPort;
#ifdef _WIN64
PWOW64_PROCESS Wow64Process;
#else
PVOID PaeTop;
#endif
ULONG ActiveThreads;
ACCESS_MASK GrantedAccess;
ULONG DefaultHardErrorProcessing;
NTSTATUS LastThreadExitStatus;
//
// Peb
//
PPEB Peb;
//
// Pointer to the prefetches trace block.
//
EX_FAST_REF PrefetchTrace;
LARGE_INTEGER ReadOperationCount;
LARGE_INTEGER WriteOperationCount;
LARGE_INTEGER OtherOperationCount;
LARGE_INTEGER ReadTransferCount;
LARGE_INTEGER WriteTransferCount;
LARGE_INTEGER OtherTransferCount;
SIZE_T CommitChargeLimit;
SIZE_T CommitChargePeak;
PVOID AweInfo;
//
// This is used for SeAuditProcessCreation.
// It contains the full path to the image file.
//
SE_AUDIT_PROCESS_CREATION_INFO SeAuditProcessCreationInfo;
MMSUPPORT Vm;
#if !defined(_WIN64)
LIST_ENTRY MmProcessLinks;
#else
ULONG Spares[2];
#endif
ULONG ModifiedPageCount;
#define PS_JOB_STATUS_NOT_REALLY_ACTIVE 0x00000001UL
#define PS_JOB_STATUS_ACCOUNTING_FOLDED 0x00000002UL
#define PS_JOB_STATUS_NEW_PROCESS_REPORTED 0x00000004UL
#define PS_JOB_STATUS_EXIT_PROCESS_REPORTED 0x00000008UL
#define PS_JOB_STATUS_REPORT_COMMIT_CHANGES 0x00000010UL
#define PS_JOB_STATUS_LAST_REPORT_MEMORY 0x00000020UL
#define PS_JOB_STATUS_REPORT_PHYSICAL_PAGE_CHANGES 0x00000040UL
ULONG JobStatus;
//
// Process flags. Use interlocked operations with PS_SET_BITS, etc
// to modify these.
//
#define PS_PROCESS_FLAGS_CREATE_REPORTED 0x00000001UL // Create process debug call has occurred
#define PS_PROCESS_FLAGS_NO_DEBUG_INHERIT 0x00000002UL // Don't inherit debug port
#define PS_PROCESS_FLAGS_PROCESS_EXITING 0x00000004UL // PspExitProcess entered
#define PS_PROCESS_FLAGS_PROCESS_DELETE 0x00000008UL // Delete process has been issued
#define PS_PROCESS_FLAGS_WOW64_SPLIT_PAGES 0x00000010UL // Wow64 split pages
#define PS_PROCESS_FLAGS_VM_DELETED 0x00000020UL // VM is deleted
#define PS_PROCESS_FLAGS_OUTSWAP_ENABLED 0x00000040UL // Outswap enabled
#define PS_PROCESS_FLAGS_OUTSWAPPED 0x00000080UL // Outswapped
#define PS_PROCESS_FLAGS_FORK_FAILED 0x00000100UL // Fork status
#define PS_PROCESS_FLAGS_WOW64_4GB_VA_SPACE 0x00000200UL // Wow64 process with 4gb virtual address space
#define PS_PROCESS_FLAGS_ADDRESS_SPACE1 0x00000400UL // Addr space state1
#define PS_PROCESS_FLAGS_ADDRESS_SPACE2 0x00000800UL // Addr space state2
#define PS_PROCESS_FLAGS_SET_TIMER_RESOLUTION 0x00001000UL // SetTimerResolution has been called
#define PS_PROCESS_FLAGS_BREAK_ON_TERMINATION 0x00002000UL // Break on process termination
#define PS_PROCESS_FLAGS_CREATING_SESSION 0x00004000UL // Process is creating a session
#define PS_PROCESS_FLAGS_USING_WRITE_WATCH 0x00008000UL // Process is using the write watch APIs
#define PS_PROCESS_FLAGS_IN_SESSION 0x00010000UL // Process is in a session
#define PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE 0x00020000UL // Process must use native address space (Win64 only)
#define PS_PROCESS_FLAGS_HAS_ADDRESS_SPACE 0x00040000UL // This process has an address space
#define PS_PROCESS_FLAGS_LAUNCH_PREFETCHED 0x00080000UL // Process launch was prefetched
#define PS_PROCESS_INJECT_INPAGE_ERRORS 0x00100000UL // Process should be given inpage errors - hardcoded in XXXXXXXm too #define PS_PROCESS_FLAGS_VM_TOP_DOWN 0x00200000UL // Process memory allocations default to top-down
#define PS_PROCESS_FLAGS_IMAGE_NOTIFY_DONE 0x00400000UL // We have sent a message for this image
#define PS_PROCESS_FLAGS_PDE_UPDATE_NEEDED 0x00800000UL // The system PDEs need updating for this process (NT32 only)
#define PS_PROCESS_FLAGS_VDM_ALLOWED 0x01000000UL // Process allowed to invoke NTVDM support
#define PS_PROCESS_FLAGS_SMAP_ALLOWED 0x02000000UL // Process allowed to invoke SMAP support
#define PS_PROCESS_FLAGS_CREATE_FAILED 0x04000000UL // Process create failed
#define PS_PROCESS_FLAGS_DEFAULT_IO_PRIORITY 0x38000000UL // The default I/O priority for created threads. (3 bits)
#define PS_PROCESS_FLAGS_PRIORITY_SHIFT 27
#define PS_PROCESS_FLAGS_EXECUTE_SPARE1 0x40000000UL //
#define PS_PROCESS_FLAGS_EXECUTE_SPARE2 0x80000000UL //
union {
ULONG Flags;
//
// Fields can only be set by the PS_SET_BITS and other interlocked
// macros. Reading fields is best done via the bit definitions so
// references are easy to locate.
//
struct {
ULONG CreateReported : 1;
ULONG NoDebugInherit : 1;
ULONG ProcessExiting : 1;
ULONG ProcessDelete : 1;
ULONG Wow64SplitPages : 1;
ULONG VmDeleted : 1;
ULONG OutswapEnabled : 1;
ULONG Outswapped : 1;
ULONG ForkFailed : 1;
ULONG Wow64VaSpace4Gb : 1;
ULONG AddressSpaceInitialized : 2;
ULONG SetTimerResolution : 1;
ULONG BreakOnTermination : 1;
ULONG SessionCreationUnderway : 1;
ULONG WriteWatch : 1;
ULONG ProcessInSession : 1;
ULONG OverrideAddressSpace : 1;
ULONG HasAddressSpace : 1;
ULONG LaunchPrefetched : 1;
ULONG InjectInpageErrors : 1;
ULONG VmTopDown : 1;
ULONG ImageNotifyDone : 1;
ULONG PdeUpdateNeeded : 1; // NT32 only
ULONG VdmAllowed : 1;
ULONG SmapAllowed : 1;
ULONG CreateFailed : 1;
ULONG DefaultIoPriority : 3;
ULONG Spare1 : 1;
ULONG Spare2 : 1;
};
};
NTSTATUS ExitStatus;
USHORT NextPageColor;
union {
struct {
UCHAR SubSystemMinorVersion;
UCHAR SubSystemMajorVersion;
};
USHORT SubSystemVersion;
};
UCHAR PriorityClass;
MM_AVL_TABLE VadRoot;
ULONG Cookie;
} EPROCESS, *PEPROCESS;
这个结构比较复杂,这里只介绍其中的一部分。
首先看其中的pcb域和相关的KPROCESS结构:
typedef struct _KPROCESS {
//
// The dispatch header and profile listhead are fairly infrequently
// referenced.
//
DISPATCHER_HEADER Header;
LIST_ENTRY ProfileListHead;
//
// The following fields are referenced during context switches.
//
ULONG_PTR DirectoryTableBase[2];
#if defined(_X86_)
KGDTENTRY LdtDescriptor;
KIDTENTRY Int21Descriptor;
USHORT IopmOffset;
UCHAR Iopl;
BOOLEAN Unused;
#endif
#if defined(_AMD64_)
USHORT IopmOffset;
#endif
volatile KAFFINITY ActiveProcessors;
//
// The following fields are referenced during clock interrupts.
//
ULONG KernelTime;
ULONG UserTime;
//
// The following fields are referenced infrequently.
//
LIST_ENTRY ReadyListHead;
SINGLE_LIST_ENTRY SwapListEntry;
#if defined(_X86_)
PVOID VdmTrapcHandler;
#else
PVOID Reserved1;
#endif
LIST_ENTRY ThreadListHead;
KSPIN_LOCK ProcessLock;
KAFFINITY Affinity;
//
// N.B. The following bit number definitions must match the following
// bit field.
//
// N.B. These bits can only be written with interlocked operations.
//
#define KPROCESS_AUTO_ALIGNMENT_BIT 0
#define KPROCESS_DISABLE_BOOST_BIT 1
#define KPROCESS_DISABLE_QUANTUM_BIT 2
union {
struct {
LONG AutoAlignment : 1;
LONG DisableBoost : 1;
LONG DisableQuantum : 1;
LONG ReservedFlags : 29;
};
LONG ProcessFlags;
};
SCHAR BasePriority;
SCHAR QuantumReset;
UCHAR State;
UCHAR ThreadSeed;
UCHAR PowerState;
UCHAR IdealNode;
BOOLEAN Visited;
union {
KEXECUTE_OPTIONS Flags;
UCHAR ExecuteOptions;
};
#if !defined(_X86_) && !defined(_AMD64_)
PALIGNMENT_EXCEPTION_TABLE AlignmentExceptionTable;
#endif
ULONG_PTR StackCount;
LIST_ENTRY ProcessListEntry;
} KPROCESS, *PKPROCESS, *PRKPROCESS;
本文并不详细讲解NT系统的代码,所以不介绍整个结构,但下面会介绍其中一些重要的部分。
DirectoryTableBase[2]数组中的第一项记录着该进程页目录表的地址(PAE模式下为页目录指针表的地址),在进程切换时该地址中的一部分会被载入CR3控制寄存器(涉及地址转移,具体请参考第二张);第二项与该进程的超空间地址有关,关于超空间本文不介绍。
LdtDescriptor是该进程的局部描述符,Windows NT并不使用LDT。
IopmOffset指向IO权限表的位置,IO权限表可以用于控制进程的IO权限。
BasePriority为该进程的优先级,优先级的高低会影响分配到的时间片。
State则描述进程的状态(是否在内存中或者正在被交换)。
下面介绍EPROCESS结构中的其它部分:
DebugPort是该进程的调试端口句柄,某些反调试程序通过在此重复写入无效值来达到阻止单一进程被调试。
ExceptionPort则是异常端口句柄。
ObjectTable则是该进程的句柄表。句柄记录着一个进程已打开的对象,Windows经常通过句柄机制使用户层的应用程序可以访问一些内核对象。另外句柄表比较复杂,本文不作详细介绍。
Win32Process是指向Windows子系统管理的进程的指针。当此进程为控制台(CUI)进程时,此指针为NULL。通过这个指针可以判断一个进程是CUI进程还是GUI进程。
ImageFileName数组包含进程对应的映象文件的文件名中的前16个字符(不足16个字符时用0填充)。
ThreadListHead是该进程的线程链表的头节点,这个列表记录了该进程的所有线程,指向系统用于描述线程的ETHREAD中的ThreadListEntry域。
SeAuditProcessCreationInfo记录着进程被创建时完整的映象路径名。
3.3NT系统的线程管理
NT系统使用ETHREAD结构来描述线程,同样,该结构嵌套这KTHREAD结构
typedef struct _ETHREAD {
KTHREAD Tcb;
LARGE_INTEGER CreateTime;
union {
LARGE_INTEGER ExitTime;
LIST_ENTRY LpcReplyChain;
LIST_ENTRY KeyedWaitChain;
};
union {
NTSTATUS ExitStatus;
PVOID OfsChain;
};
//
// Registry
//
LIST_ENTRY PostBlockList;
//
// Single linked list of termination blocks
//
union {
//
// List of termination ports
//
PTERMINATION_PORT TerminationPort;
//
// List of threads to be reaped. Only used at thread exit
//
struct _ETHREAD *ReaperLink;
//
// Keyvalue being waited for
//
PVOID KeyedWaitValue;
};
KSPIN_LOCK ActiveTimerListLock;
LIST_ENTRY ActiveTimerListHead;
CLIENT_ID Cid;
//
// Lpc
//
union {
KSEMAPHORE LpcReplySemaphore;
KSEMAPHORE KeyedWaitSemaphore;
};
union {
PVOID LpcReplyMessage; // -> Message that contains the reply
PVOID LpcWaitingOnPort;
};
//
// Security
//
//
// Client - If non null, indicates the thread is impersonating
// a client.
//
PPS_IMPERSONATION_INFORMATION ImpersonationInfo;
//
// Io
//
LIST_ENTRY IrpList;
//
// File Systems
//
ULONG_PTR TopLevelIrp; // either NULL, an Irp or a flag defined in FsRtl.h
struct _DEVICE_OBJECT *DeviceToVerify;
PEPROCESS ThreadsProcess;
PVOID StartAddress;
union {
PVOID Win32StartAddress;
ULONG LpcReceivedMessageId;
};
//
// Ps
//
LIST_ENTRY ThreadListEntry;
//
// Rundown protection structure. Acquire this to do cross thread
// TEB, TEB32 or stack references.
//
EX_RUNDOWN_REF RundownProtect;
//
// Lock to protect thread impersonation information
//
EX_PUSH_LOCK ThreadLock;
ULONG LpcReplyMessageId; // MessageId this thread is waiting for reply to
ULONG ReadClusterSize;
//
// Client/server
//
ACCESS_MASK GrantedAccess;
//
// Flags for cross thread access. Use interlocked operations
// via PS_SET_BITS etc.
//
//
// Used to signify that the delete APC has been queued or the
// thread has called PspExitThread itself.
//
#define PS_CROSS_THREAD_FLAGS_TERMINATED 0x00000001UL
//
// Thread create failed
//
#define PS_CROSS_THREAD_FLAGS_DEADTHREAD 0x00000002UL
//
// Debugger isn't shown this thread
//
#define PS_CROSS_THREAD_FLAGS_HIDEFROMDBG 0x00000004UL
//
// Thread is impersonating
//
#define PS_CROSS_THREAD_FLAGS_IMPERSONATING 0x00000008UL
//
// This is a system thread
//
#define PS_CROSS_THREAD_FLAGS_SYSTEM 0x00000010UL
//
// Hard errors are disabled for this thread
//
#define PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED 0x00000020UL
//
// We should break in when this thread is terminated
//
#define PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION 0x00000040UL
//
// This thread should skip sending its create thread message
//
#define PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG 0x00000080UL
//
// This thread should skip sending its final thread termination message
//
#define PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG 0x00000100UL
union {
ULONG CrossThreadFlags;
//
// The following fields are for the debugger only. Do not use.
// Use the bit definitions instead.
//
struct {
ULONG Terminated : 1;
ULONG DeadThread : 1;
ULONG HideFromDebugger : 1;
ULONG ActiveImpersonationInfo : 1;
ULONG SystemThread : 1;
ULONG HardErrorsAreDisabled : 1;
ULONG BreakOnTermination : 1;
ULONG SkipCreationMsg : 1;
ULONG SkipTerminationMsg : 1;
};
};
//
// Flags to be accessed in this thread's context only at PASSIVE
// level -- no need to use interlocked operations.
//
union {
ULONG SameThreadPassiveFlags;
struct {
//
// This thread is an active Ex worker thread; it should
// not terminate.
//
ULONG ActiveExWorker : 1;
ULONG ExWorkerCanWaitUser : 1;
ULONG MemoryMaker : 1;
//
// Thread is active in the keyed event code. LPC should not run above this in an APC.
//
ULONG KeyedEventInUse : 1;
};
};
//
// Flags to be accessed in this thread's context only at APC_LEVEL.
// No need to use interlocked operations.
//
union {
ULONG SameThreadApcFlags;
struct {
//
// The stored thread's MSGID is valid. This is only accessed
// while the LPC mutex is held so it's an APC_LEVEL flag.
//
BOOLEAN LpcReceivedMsgIdValid : 1;
BOOLEAN LpcExitThreadCalled : 1;
BOOLEAN AddressSpaceOwner : 1;
BOOLEAN OwnsProcessWorkingSetExclusive : 1;
BOOLEAN OwnsProcessWorkingSetShared : 1;
BOOLEAN OwnsSystemWorkingSetExclusive : 1;
BOOLEAN OwnsSystemWorkingSetShared : 1;
BOOLEAN OwnsSessionWorkingSetExclusive : 1;
BOOLEAN OwnsSessionWorkingSetShared : 1;
#define PS_SAME_THREAD_FLAGS_OWNS_A_WORKING_SET 0x000001F8UL
BOOLEAN ApcNeeded : 1;
};
};
BOOLEAN ForwardClusterOnly;
BOOLEAN DisablePageFaultClustering;
UCHAR ActiveFaultCount;
#if defined (PERF_DATA)
ULONG PerformanceCountLow;
LONG PerformanceCountHigh;
#endif
} ETHREAD, *PETHREAD;
typedef struct _KPROCESS {
//
// The dispatch header and profile listhead are fairly infrequently
// referenced.
//
DISPATCHER_HEADER Header;
LIST_ENTRY ProfileListHead;
//
// The following fields are referenced during context switches.
//
ULONG_PTR DirectoryTableBase[2];
#if defined(_X86_)
KGDTENTRY LdtDescriptor;
KIDTENTRY Int21Descriptor;
USHORT IopmOffset;
UCHAR Iopl;
BOOLEAN Unused;
#endif
#if defined(_AMD64_)
USHORT IopmOffset;
#endif
volatile KAFFINITY ActiveProcessors;
//
// The following fields are referenced during clock interrupts.
//
ULONG KernelTime;
ULONG UserTime;
//
// The following fields are referenced infrequently.
//
LIST_ENTRY ReadyListHead;
SINGLE_LIST_ENTRY SwapListEntry;
#if defined(_X86_)
PVOID VdmTrapcHandler;
#else
PVOID Reserved1;
#endif
LIST_ENTRY ThreadListHead;
KSPIN_LOCK ProcessLock;
KAFFINITY Affinity;
//
// N.B. The following bit number definitions must match the following
// bit field.
//
// N.B. These bits can only be written with interlocked operations.
//
#define KPROCESS_AUTO_ALIGNMENT_BIT 0
#define KPROCESS_DISABLE_BOOST_BIT 1
#define KPROCESS_DISABLE_QUANTUM_BIT 2
union {
struct {
LONG AutoAlignment : 1;
LONG DisableBoost : 1;
LONG DisableQuantum : 1;
LONG ReservedFlags : 29;
};
LONG ProcessFlags;
};
SCHAR BasePriority;
SCHAR QuantumReset;
UCHAR State;
UCHAR ThreadSeed;
UCHAR PowerState;
UCHAR IdealNode;
BOOLEAN Visited;
union {
KEXECUTE_OPTIONS Flags;
UCHAR ExecuteOptions;
};
#if !defined(_X86_) && !defined(_AMD64_)
PALIGNMENT_EXCEPTION_TABLE AlignmentExceptionTable;
#endif
ULONG_PTR StackCount;
LIST_ENTRY ProcessListEntry;
} KPROCESS, *PKPROCESS, *PRKPROCESS;
//
// Thread object
//
typedef enum _ADJUST_REASON {
AdjustNone = 0,
AdjustUnwait = 1,
AdjustBoost = 2
} ADJUST_REASON;
#define ASSERT_THREAD(object) ASSERT((object)->Header.Type == ThreadObject)
//
// Define the number of times a user mode SLIST pop fault is permitted to be
// retried before raising an exception.
//
#define KI_SLIST_FAULT_COUNT_MAXIMUM 1024
typedef struct _KTHREAD {
//
// The dispatcher header and mutant listhead are fairly infrequently
// referenced.
//
DISPATCHER_HEADER Header;
LIST_ENTRY MutantListHead;
//
// The following fields are referenced during context switches and wait
// operatings. They have been carefully laid out to get the best cache
// hit ratios.
//
PVOID InitialStack;
PVOID StackLimit;
PVOID KernelStack;
KSPIN_LOCK ThreadLock;
union {
KAPC_STATE ApcState;
struct {
UCHAR ApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
BOOLEAN ApcQueueable;
volatile UCHAR NextProcessor;
volatile UCHAR DeferredProcessor;
UCHAR AdjustReason;
SCHAR AdjustIncrement;
};
};
KSPIN_LOCK ApcQueueLock;
#if !defined(_AMD64_)
ULONG ContextSwitches;
volatile UCHAR State;
UCHAR NpxState;
KIRQL WaitIrql;
KPROCESSOR_MODE WaitMode;
#endif
LONG_PTR WaitStatus;
union {
PKWAIT_BLOCK WaitBlockList;
PKGATE GateObject;
};
BOOLEAN Alertable;
BOOLEAN WaitNext;
UCHAR WaitReason;
SCHAR Priority;
UCHAR EnableStackSwap;
volatile UCHAR SwapBusy;
BOOLEAN Alerted[MaximumMode];
union {
LIST_ENTRY WaitListEntry;
SINGLE_LIST_ENTRY SwapListEntry;
};
PRKQUEUE Queue;
#if !defined(_AMD64_)
ULONG WaitTime;
union {
struct {
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};
#endif
PVOID Teb;
union {
KTIMER Timer;
struct {
UCHAR TimerFill[KTIMER_ACTUAL_LENGTH];
//
// N.B. The following bit number definitions must match the
// following bit field.
//
// N.B. These bits can only be written with interlocked
// operations.
//
#define KTHREAD_AUTO_ALIGNMENT_BIT 0
#define KTHREAD_DISABLE_BOOST_BIT 1
union {
struct {
LONG AutoAlignment : 1;
LONG DisableBoost : 1;
LONG ReservedFlags : 30;
};
LONG ThreadFlags;
};
};
};
union {
KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + 1];
struct {
UCHAR WaitBlockFill0[KWAIT_BLOCK_OFFSET_TO_BYTE0];
BOOLEAN SystemAffinityActive;
};
struct {
UCHAR WaitBlockFill1[KWAIT_BLOCK_OFFSET_TO_BYTE1];
CCHAR PreviousMode;
};
struct {
UCHAR WaitBlockFill2[KWAIT_BLOCK_OFFSET_TO_BYTE2];
UCHAR ResourceIndex;
};
struct {
UCHAR WaitBlockFill3[KWAIT_BLOCK_OFFSET_TO_BYTE3];
UCHAR LargeStack;
};
#if defined(_AMD64_)
struct {
UCHAR WaitBlockFill4[KWAIT_BLOCK_OFFSET_TO_LONG0];
ULONG ContextSwitches;
};
struct {
UCHAR WaitBlockFill5[KWAIT_BLOCK_OFFSET_TO_LONG1];
volatile UCHAR State;
UCHAR NpxState;
KIRQL WaitIrql;
KPROCESSOR_MODE WaitMode;
};
struct {
UCHAR WaitBlockFill6[KWAIT_BLOCK_OFFSET_TO_LONG2];
ULONG WaitTime;
};
struct {
UCHAR WaitBlockFill7[KWAIT_BLOCK_OFFSET_TO_LONG3];
union {
struct {
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};
};
#endif
};
LIST_ENTRY QueueListEntry;
//
// The following fields are accessed during system service dispatch.
//
PKTRAP_FRAME TrapFrame;
PVOID CallbackStack;
PVOID ServiceTable;
#if defined(_AMD64_)
ULONG KernelLimit;
#endif
//
// The following fields are referenced during ready thread and wait
// completion.
//
UCHAR ApcStateIndex;
UCHAR IdealProcessor;
BOOLEAN Preempted;
BOOLEAN ProcessReadyQueue;
#if defined(_AMD64_)
PVOID Win32kTable;
ULONG Win32kLimit;
#endif
BOOLEAN KernelStackResident;
SCHAR BasePriority;
SCHAR PriorityDecrement;
CHAR Saturation;
KAFFINITY UserAffinity;
PKPROCESS Process;
KAFFINITY Affinity;
//
// The below fields are infrequently referenced.
//
PKAPC_STATE ApcStatePointer[2];
union {
KAPC_STATE SavedApcState;
struct {
UCHAR SavedApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
CCHAR FreezeCount;
CCHAR SuspendCount;
UCHAR UserIdealProcessor;
UCHAR CalloutActive;
#if defined(_AMD64_)
BOOLEAN CodePatchInProgress;
#elif defined(_X86_)
UCHAR Iopl;
#else
UCHAR OtherPlatformFill;
#endif
};
};
PVOID Win32Thread;
PVOID StackBase;
union {
KAPC SuspendApc;
struct {
UCHAR SuspendApcFill0[KAPC_OFFSET_TO_SPARE_BYTE0];
SCHAR Quantum;
};
struct {
UCHAR SuspendApcFill1[KAPC_OFFSET_TO_SPARE_BYTE1];
UCHAR QuantumReset;
};
struct {
UCHAR SuspendApcFill2[KAPC_OFFSET_TO_SPARE_LONG];
ULONG KernelTime;
};
struct {
UCHAR SuspendApcFill3[KAPC_OFFSET_TO_SYSTEMARGUMENT1];
PVOID TlsArray;
};
struct {
UCHAR SuspendApcFill4[KAPC_OFFSET_TO_SYSTEMARGUMENT2];
PVOID BBTData;
};
struct {
UCHAR SuspendApcFill5[KAPC_ACTUAL_LENGTH];
UCHAR PowerState;
ULONG UserTime;
};
};
union {
KSEMAPHORE SuspendSemaphore;
struct {
UCHAR SuspendSemaphorefill[KSEMAPHORE_ACTUAL_LENGTH];
ULONG SListFaultCount;
};
};
LIST_ENTRY ThreadListEntry;
PVOID SListFaultAddress;
#if defined(_WIN64)
LONG64 ReadOperationCount;
LONG64 WriteOperationCount;
LONG64 OtherOperationCount;
LONG64 ReadTransferCount;
LONG64 WriteTransferCount;
LONG64 OtherTransferCount;
#endif
} KTHREAD, *PKTHREAD, *PRKTHREAD;
这个结构更加复杂。其中的各个域即使按重要性介绍,也可能需要介绍很多。请读者自学参考相关资料。
3.4线程的调度
为了保证资源不被某一线程独占,操作系统会为每个线程分配时间片。当某个线程的CPU时间消耗完时,系统会进行调度,让处于队列中等待的下一个线程得到执行。
事实上,系统将优先级不同的线程存储在不同的链表中,在调度时会优先选择优先级较高的线程。
线程的调度涉及大量操作系统代码和实现细节,本文不作介绍,图3-0A介绍了大致的情况。