windows中的程序和執行緒
在討論windows下的程序和執行緒時,我們先回顧下通用作業系統的程序和執行緒。之所以稱之為通用是因為一貫的本科或者其他教材都是這麼說的:
1、程序是系統分配資源的最小單位。
2、執行緒是處理器排程的最小單位。
3、一個程序可以包含很多執行緒,且這些執行緒共享程序內的所有資源。
然後又有大致三種執行緒模型:程序模型、使用者級執行緒、核心級執行緒,三種模型如圖所示
把執行緒模型按嚴格意義上的劃分就是這樣,但是事實上作業系統在真正執行過程中使用的要遠比這些複雜的多,但是這些就想事網路中的OSI模型一樣,雖然在使用的時候有不少問題,但是作為學習仍然是很經典的一種提法。
那麼為什麼上面三種模型都不能滿足需求呢?
先說程序模型,程序模型下,沒有執行緒的概念,即不論是資源的分配還是CPU的排程都是基於程序的。這種情況下完成一個很小的任務也需要建立一個程序,但是程序的生命週期從建立到排程再到銷燬需要消耗比較多的資源。為了更加充分的利用資源,這裡就提出了執行緒的概念,在這種情況下,使用者級執行緒和核心級執行緒可以說各有優劣。
首先說使用者級執行緒,CPU的排程和資源的分配仍然是按照程序來走,執行緒只存在於使用者空間,核心無法感知。這樣使用者就可以根據需要自己定義執行緒排程演算法,執行緒的切換因為不需要模式的切換,故比較高效。其缺點就是對應用程式的要求比較高,雖然現在高階語言都有執行緒池的概念,但是這種情況下一個致命缺點就是如果程序內一個執行緒被阻塞,那麼整個程序就會被阻塞,這很顯然無法充分利用多處理器的優勢;而如果一個執行緒異常,那麼整個程序也就完蛋了。這就像一顆老鼠屎,壞了一鍋粥的思想。而核心級執行緒就不同了,其CPu根據執行緒進行排程,這樣使用者應用程式就可以專心做自己的事情,不用考慮排程演算法。但是這樣使用者就無法自定義排程演算法。這種情況下執行緒的管理全部由核心接管。執行緒的建立、排程、回收都有核心處理。建立和回收暫且不說,執行緒的排程由於是核心管理,其必然會涉及到使用者模式到核心模式的切換,而在此被排程就需要從核心模式切換回使用者模式。也就是說一個執行緒從被執行->就緒->執行,至少需要兩次模式切換,這樣雖然相對於程序來講,可以避免在不同程序切換時更多的上下文的儲存(同一程序中的執行緒切換就可以避免),但是要不就說時代在發展,社會在進步呢,工程師總要一點點榨取CPU以便獲取最大的效能,有句話說沒有最好只有更好,所以即使是這種開銷也是無法忍受的。
windows中的程序 和執行緒
windows中嚴格意義上來說算是核心級的執行緒模型。在windows中程序和執行緒有明確的界限,程序就是程序,只是分配資源的單位,有自己獨立的資料結構。而執行緒就是執行緒,作為CPU排程的基本單位,也有自己的資料結構。這點和LInux有著本質的區別, 但是今天我們不討論具體的資料結構!
windows中的程序涉及到兩個結構EPROCESS和KPROCESS兩個結構EPROCESS是執行體層的物件而KPROCESS是核心層的物件。相對應的ETHREAD是執行體物件,而KTHREAD是核心層物件。關於執行體層和核心層,這點參考《windows核心原理與實現》相關章節。
熟悉這些結構的都應該清楚,核心層物件EPROCESS和ETHREAD都是相應執行體物件的首個內嵌結構,即實際上程序和執行緒的核心層物件和執行體層物件在核心中的地址是相同的。
核心部分實現的基本是和程序或者執行緒本身相關的屬性,而執行體層更多的注重於管理。
下面還是從執行緒的建立開始一步步觀察下windows建立執行緒是如何被一步步建立起來的,並看看執行緒包含了哪些東西
建立一個執行緒首先從使用者程式呼叫使用者函式CreateThread開始,該函式直接呼叫了函式CreateRemoteThread,CreateRemoteThread函式主要完成以下工作:
a)建立使用者空間堆疊
b)初始化CONTEXT結構體
c)初始化OBJECT_ATTRIBUTES結構體,此結構體在建立執行緒物件的時候使用。
d)呼叫NtCreateThread,進入核心空間。
需要注意這裡Context結構作為引數傳遞給NtCreateThread,建立系統執行緒時,該引數為空。使用者執行緒則在初始化系統堆疊的時候複製Context內容到堆疊。
看NtCreateThread
NTSTATUS NtCreateThread( __out PHANDLE ThreadHandle, __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in HANDLE ProcessHandle, __out PCLIENT_ID ClientId, __in PCONTEXT ThreadContext, __in PINITIAL_TEB InitialTeb, __in BOOLEAN CreateSuspended ) /*++ Routine Description: This system service API creates and initializes a thread object. Arguments: ThreadHandle - Returns the handle for the new thread. DesiredAccess - Supplies the desired access modes to the new thread. ObjectAttributes - Supplies the object attributes of the new thread. ProcessHandle - Supplies a handle to the process that the thread is being created within. ClientId - Returns the CLIENT_ID of the new thread. ThreadContext - Supplies an initial context for the new thread. InitialTeb - Supplies the initial contents for the thread's TEB. CreateSuspended - Supplies a value that controls whether or not a thread is created in a suspended state. --*/ { NTSTATUS Status; INITIAL_TEB CapturedInitialTeb; PAGED_CODE(); // // Probe all arguments // try { if (KeGetPreviousMode () != KernelMode) { ProbeForWriteHandle (ThreadHandle); if (ARGUMENT_PRESENT (ClientId)) { ProbeForWriteSmallStructure (ClientId, sizeof (CLIENT_ID), sizeof (ULONG)); } if (ARGUMENT_PRESENT (ThreadContext) ) { ProbeForReadSmallStructure (ThreadContext, sizeof (CONTEXT), CONTEXT_ALIGN); } else { return STATUS_INVALID_PARAMETER; } ProbeForReadSmallStructure (InitialTeb, sizeof (InitialTeb->OldInitialTeb), sizeof (ULONG)); } CapturedInitialTeb.OldInitialTeb = InitialTeb->OldInitialTeb; if (CapturedInitialTeb.OldInitialTeb.OldStackBase == NULL && CapturedInitialTeb.OldInitialTeb.OldStackLimit == NULL) { // // Since the structure size here is less than 64k we don't need to reprobe // CapturedInitialTeb = *InitialTeb; } } except (ExSystemExceptionFilter ()) { return GetExceptionCode (); } Status = PspCreateThread (ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, NULL, ClientId, ThreadContext, &CapturedInitialTeb, CreateSuspended, NULL, NULL); return Status; }
該函式在做有些常規檢查後就直接呼叫了另個函式PspCreateThread
NTSTATUS PspCreateThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ProcessHandle, IN PEPROCESS ProcessPointer, OUT PCLIENT_ID ClientId OPTIONAL, IN PCONTEXT ThreadContext OPTIONAL,//使用者空間執行緒執行上下文 IN PINITIAL_TEB InitialTeb OPTIONAL, IN BOOLEAN CreateSuspended, IN PKSTART_ROUTINE StartRoutine OPTIONAL, IN PVOID StartContext ) /*++ Routine Description: This routine creates and initializes a thread object. It implements the foundation for NtCreateThread and for PsCreateSystemThread. Arguments: ThreadHandle - Returns the handle for the new thread. DesiredAccess - Supplies the desired access modes to the new thread. ObjectAttributes - Supplies the object attributes of the new thread. ProcessHandle - Supplies a handle to the process that the thread is being created within. ClientId - Returns the CLIENT_ID of the new thread. ThreadContext - Supplies a pointer to a context frame that represents the initial user-mode context for a user-mode thread. The absence of this parameter indicates that a system thread is being created. InitialTeb - Supplies the contents of certain fields for the new threads TEB. This parameter is only examined if both a trap and exception frame were specified. CreateSuspended - Supplies a value that controls whether or not a user-mode thread is created in a suspended state. StartRoutine - Supplies the address of the system thread start routine. StartContext - Supplies context for a system thread start routine. --*/ { HANDLE_TABLE_ENTRY CidEntry; NTSTATUS Status; PETHREAD Thread; PETHREAD CurrentThread; PEPROCESS Process; PTEB Teb; KPROCESSOR_MODE PreviousMode; HANDLE LocalThreadHandle; BOOLEAN AccessCheck; BOOLEAN MemoryAllocated; PSECURITY_DESCRIPTOR SecurityDescriptor; SECURITY_SUBJECT_CONTEXT SubjectContext; NTSTATUS accesst; LARGE_INTEGER CreateTime; ULONG OldActiveThreads; PEJOB Job; AUX_ACCESS_DATA AuxData; PACCESS_STATE AccessState; ACCESS_STATE LocalAccessState; PAGED_CODE(); CurrentThread = PsGetCurrentThread (); if (StartRoutine != NULL) { PreviousMode = KernelMode; } else { PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb); } Teb = NULL; Thread = NULL; Process = NULL; if (ProcessHandle != NULL) { // // Process object reference count is biased by one for each thread. // This accounts for the pointer given to the kernel that remains // in effect until the thread terminates (and becomes signaled) // Status = ObReferenceObjectByHandle (ProcessHandle, PROCESS_CREATE_THREAD, PsProcessType, PreviousMode, &Process, NULL); } else { if (StartRoutine != NULL) { ObReferenceObject (ProcessPointer); Process = ProcessPointer; Status = STATUS_SUCCESS; } else { Status = STATUS_INVALID_HANDLE; } } if (!NT_SUCCESS (Status)) { return Status; } // // If the previous mode is user and the target process is the system // process, then the operation cannot be performed. // if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess)) { ObDereferenceObject (Process); return STATUS_INVALID_HANDLE; } //建立ethread物件 Status = ObCreateObject (PreviousMode, PsThreadType, ObjectAttributes, PreviousMode, NULL, sizeof(ETHREAD), 0, 0, &Thread); if (!NT_SUCCESS (Status)) { ObDereferenceObject (Process); return Status; } RtlZeroMemory (Thread, sizeof (ETHREAD)); // // Initialize rundown protection for cross thread TEB refs etc. // ExInitializeRundownProtection (&Thread->RundownProtect); // // Assign this thread to the process so that from now on // we don't have to dereference in error paths. // Thread->ThreadsProcess = Process; Thread->Cid.UniqueProcess = Process->UniqueProcessId; CidEntry.Object = Thread; CidEntry.GrantedAccess = 0; //從控制代碼表中分配一個CidEntry Thread->Cid.UniqueThread = ExCreateHandle (PspCidTable, &CidEntry); if (Thread->Cid.UniqueThread == NULL) { ObDereferenceObject (Thread); return (STATUS_INSUFFICIENT_RESOURCES); } // // Initialize Mm // Thread->ReadClusterSize = MmReadClusterSize; // // Initialize LPC // KeInitializeSemaphore (&Thread->LpcReplySemaphore, 0L, 1L); InitializeListHead (&Thread->LpcReplyChain); // // Initialize Io // InitializeListHead (&Thread->IrpList); // // Initialize Registry // InitializeListHead (&Thread->PostBlockList); // // Initialize the thread lock // PspInitializeThreadLock (Thread); KeInitializeSpinLock (&Thread->ActiveTimerListLock); InitializeListHead (&Thread->ActiveTimerListHead); if (!ExAcquireRundownProtection (&Process->RundownProtect)) { ObDereferenceObject (Thread); return STATUS_PROCESS_IS_TERMINATING; } //如果ThreadContext不為null表明這是一個使用者執行緒,否則是系統執行緒 if (ARGUMENT_PRESENT (ThreadContext)) { // // User-mode thread. Create TEB etc // Status = MmCreateTeb (Process, InitialTeb, &Thread->Cid, &Teb); if (!NT_SUCCESS (Status)) { ExReleaseRundownProtection (&Process->RundownProtect); ObDereferenceObject (Thread); return Status; } try { // // Initialize kernel thread object for user mode thread. // Thread->StartAddress = (PVOID)CONTEXT_TO_PROGRAM_COUNTER(ThreadContext); #if defined(_AMD64_) Thread->Win32StartAddress = (PVOID)ThreadContext->Rdx; #elif defined(_X86_) Thread->Win32StartAddress = (PVOID)ThreadContext->Eax; #else #error "no target architecture" #endif } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } if (NT_SUCCESS (Status)) { Status = KeInitThread (&Thread->Tcb, NULL, PspUserThreadStartup, (PKSTART_ROUTINE)NULL, Thread->StartAddress, ThreadContext, Teb, &Process->Pcb); } } else { Teb = NULL; // // Set the system thread bit thats kept for all time // PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_SYSTEM); // // Initialize kernel thread object for kernel mode thread. // Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine; Status = KeInitThread (&Thread->Tcb, NULL, PspSystemThreadStartup, StartRoutine, StartContext, NULL, NULL, &Process->Pcb); } if (!NT_SUCCESS (Status)) { if (Teb != NULL) { MmDeleteTeb(Process, Teb); } ExReleaseRundownProtection (&Process->RundownProtect); ObDereferenceObject (Thread); return Status; } PspLockProcessExclusive (Process, CurrentThread); // // Process is exiting or has had delete process called // We check the calling threads termination status so we // abort any thread creates while ExitProcess is being called -- // but the call is blocked only if the new thread would be created // in the terminating thread's process. // if ((Process->Flags&PS_PROCESS_FLAGS_PROCESS_DELETE) != 0 || (((CurrentThread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) != 0) && (ThreadContext != NULL) && (THREAD_TO_PROCESS(CurrentThread) == Process))) { PspUnlockProcessExclusive (Process, CurrentThread); KeUninitThread (&Thread->Tcb); if (Teb != NULL) { MmDeleteTeb(Process, Teb); } ExReleaseRundownProtection (&Process->RundownProtect); ObDereferenceObject(Thread); return STATUS_PROCESS_IS_TERMINATING; } OldActiveThreads = Process->ActiveThreads++; InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry); KeStartThread (&Thread->Tcb); PspUnlockProcessExclusive (Process, CurrentThread); ExReleaseRundownProtection (&Process->RundownProtect); // // Failures that occur after this point cause the thread to // go through PspExitThread // if (OldActiveThreads == 0) { PERFINFO_PROCESS_CREATE (Process); if (PspCreateProcessNotifyRoutineCount != 0) { ULONG i; PEX_CALLBACK_ROUTINE_BLOCK CallBack; PCREATE_PROCESS_NOTIFY_ROUTINE Rtn; for (i=0; i<PSP_MAX_CREATE_PROCESS_NOTIFY; i++) { CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]); if (CallBack != NULL) { Rtn = (PCREATE_PROCESS_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack); Rtn (Process->InheritedFromUniqueProcessId, Process->UniqueProcessId, TRUE); ExDereferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i], CallBack); } } } } // // If the process has a job with a completion port, // AND if the process is really considered to be in the Job, AND // the process has not reported, report in // // This should really be done in add process to job, but can't // in this path because the process's ID isn't assigned until this point // in time // Job = Process->Job; if (Job != NULL && Job->CompletionPort && !(Process->JobStatus & (PS_JOB_STATUS_NOT_REALLY_ACTIVE|PS_JOB_STATUS_NEW_PROCESS_REPORTED))) { PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NEW_PROCESS_REPORTED); KeEnterCriticalRegionThread (&CurrentThread->Tcb); ExAcquireResourceSharedLite (&Job->JobLock, TRUE); if (Job->CompletionPort != NULL) { IoSetIoCompletion (Job->CompletionPort, Job->CompletionKey, (PVOID)Process->UniqueProcessId, STATUS_SUCCESS, JOB_OBJECT_MSG_NEW_PROCESS, FALSE); } ExReleaseResourceLite (&Job->JobLock); KeLeaveCriticalRegionThread (&CurrentThread->Tcb); } PERFINFO_THREAD_CREATE(Thread, InitialTeb); // // Notify registered callout routines of thread creation. // if (PspCreateThreadNotifyRoutineCount != 0) { ULONG i; PEX_CALLBACK_ROUTINE_BLOCK CallBack; PCREATE_THREAD_NOTIFY_ROUTINE Rtn; for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) { CallBack = ExReferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i]); if (CallBack != NULL) { Rtn = (PCREATE_THREAD_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack); Rtn (Thread->Cid.UniqueProcess, Thread->Cid.UniqueThread, TRUE); ExDereferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i], CallBack); } } } // // Reference count of thread is biased once for itself and once for the handle if we create it. // ObReferenceObjectEx (Thread, 2); if (CreateSuspended) { try { KeSuspendThread (&Thread->Tcb); } except ((GetExceptionCode () == STATUS_SUSPEND_COUNT_EXCEEDED)? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { } // // If deletion was started after we suspended then wake up the thread // if (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) { KeForceResumeThread (&Thread->Tcb); } } AccessState = NULL; if (!PsUseImpersonationToken) { AccessState = &LocalAccessState; Status = SeCreateAccessStateEx (NULL, ARGUMENT_PRESENT (ThreadContext)?PsGetCurrentProcessByThread (CurrentThread) : Process, AccessState, &AuxData, DesiredAccess, &PsThreadType->TypeInfo.GenericMapping); if (!NT_SUCCESS (Status)) { PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) { (VOID) KeResumeThread (&Thread->Tcb); } KeReadyThread (&Thread->Tcb); ObDereferenceObjectEx (Thread, 2); return Status; } } Status = ObInsertObject (Thread, AccessState, DesiredAccess, 0, NULL, &LocalThreadHandle); if (AccessState != NULL) { SeDeleteAccessState (AccessState); } if (!NT_SUCCESS (Status)) { // // The insert failed. Terminate the thread. // // // This trick is used so that Dbgk doesn't report // events for dead threads // PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) { KeResumeThread (&Thread->Tcb); } } else { try { *ThreadHandle = LocalThreadHandle; if (ARGUMENT_PRESENT (ClientId)) { *ClientId = Thread->Cid; } } except(EXCEPTION_EXECUTE_HANDLER) { PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) { (VOID) KeResumeThread (&Thread->Tcb); } KeReadyThread (&Thread->Tcb); ObDereferenceObject (Thread); ObCloseHandle (LocalThreadHandle, PreviousMode); return GetExceptionCode(); } } KeQuerySystemTime(&CreateTime); ASSERT ((CreateTime.HighPart & 0xf0000000) == 0); PS_SET_THREAD_CREATE_TIME(Thread, CreateTime); if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == 0) { Status = ObGetObjectSecurity (Thread, &SecurityDescriptor, &MemoryAllocated); if (!NT_SUCCESS (Status)) { // // This trick us used so that Dbgk doesn't report // events for dead threads // PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_DEADTHREAD); if (CreateSuspended) { KeResumeThread(&Thread->Tcb); } KeReadyThread (&Thread->Tcb); ObDereferenceObject (Thread); ObCloseHandle (LocalThreadHandle, PreviousMode); return Status; } // // Compute the subject security context // SubjectContext.ProcessAuditId = Process; SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process); SubjectContext.ClientToken = NULL; AccessCheck = SeAccessCheck (SecurityDescriptor, &SubjectContext, FALSE, MAXIMUM_ALLOWED, 0, NULL, &PsThreadType->TypeInfo.GenericMapping, PreviousMode, &Thread->GrantedAccess, &accesst); PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken); ObReleaseObjectSecurity (SecurityDescriptor, MemoryAllocated); if (!AccessCheck) { Thread->GrantedAccess = 0; } Thread->GrantedAccess |= (THREAD_TERMINATE | THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION); } else { Thread->GrantedAccess = THREAD_ALL_ACCESS; } KeReadyThread (&Thread->Tcb); ObDereferenceObject (Thread); return Status; }
首先說下該函式的幾個引數,
ThreadHandle是一個輸出引數,建立成功會包含建立執行緒的控制代碼。
DesiredAccess包含了對新執行緒的訪問許可權。
OBjectAttributes是可選引數,代表了執行緒的屬性。
ProcessHandle指向一個程序的控制代碼,建立好的執行緒將執行在該程序的環境中。
ProcessPointer指向所屬程序的Eprocess物件,該引數在建立系統執行緒時指向PsInitialSystemProcess物件,在建立使用者執行緒的時候為null。
ClientId返回新執行緒的ClientID結構。
ThreadCOntext提供了新執行緒的執行環境,它代表了使用者執行緒的初始環境,如果為null則表示為系統執行緒。
InitialTeb引數為新執行緒的Teb結構提供初始值,系統執行緒沒有使用者空間堆疊也沒有Teb,所以如果建立的是系統執行緒,則該引數為null
CreateSuspended引數指明新執行緒建立後是否被掛起,如果為true,則建立新執行緒後不會立刻執行,而必須等到顯示呼叫NtResumeThread函式讓執行緒執行。
StartRoutine引數指定了系統執行緒啟動函式地址,所以使用者執行緒情況下,該引數為null。
StartContext引數指定了系統執行緒啟動函式執行環境,使用者執行緒下同樣為null。
下面分析下具體的程式碼:
1、建立並初始化EThread物件
之前有提到,Ethread物件時執行緒的執行體物件,且第一個欄位就是Kthread物件,windows採用的核心執行緒模型,每個執行緒都有一個這樣的結構。而初始化主要是設定執行緒的所屬程序,以及設定CID中程序ID,從全域性控制代碼表PspCidTable分配一個可用的項來表示此執行緒,並返回執行緒ID,然後就是初始化執行緒的一些連結串列。
2、判斷引數ThreadContext是否為空。
正如前面所提到的,這裡若非空就表示建立的是使用者執行緒,那麼就需要建立一個Teb,這裡呼叫了MmCreateTeb函式。此函式後面再說。然後就是一個try,嘗試為使用者執行緒初始化核心執行緒物件Ethread,實際上是初始化了Thread的startaddress為ThreadContext中的RiP,並將ThreadContext中的eax設定到執行緒的win32StartAddress域,eax儲存的是使用者指定的執行緒執行函式。然後呼叫KeInitThread函式對執行緒進行初始化,這裡主要就是根據程序的屬性對執行緒做設定,包括優先順序,親和性,serviceTable等,注意這裡設定的核心層thread物件Kthread.
3、如果上面引數為空
就表示這是一個核心執行緒,那麼設定TeB=NULL.設定thread->startAddress=引數startRoutine,最後仍然是呼叫KeInitThread函式初始化執行緒
4、接下來就設定程序的活動執行緒數加一,呼叫InsertTailList把執行緒假如到程序的執行緒連結串列中,這裡是站在執行體層的角度,操作的是EProcess和Ethread。
5、判斷CreateSuspended引數,為true則把程序掛起。
6、最後經過一系列的設定,如執行緒的建立時間,引用計數等就調動keReadyThread設定執行緒就緒態,然後就準備被排程執行了。
其實上面說的還不是很詳細,不少細節都忽略了,但是大體上幾個重要步驟基本就是這樣。理解了這些,windows執行緒也就基本瞭解了!!