windows中的进程和线程
今天咱们就聊聊windows中的进程和线程
2016-09-30
在讨论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),
,
,
&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 = ;
//从句柄表中分配一个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) != ||
(((CurrentThread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) != ) &&
(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 == ) {
PERFINFO_PROCESS_CREATE (Process); if (PspCreateProcessNotifyRoutineCount != ) {
ULONG i;
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
PCREATE_PROCESS_NOTIFY_ROUTINE Rtn; for (i=; 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 != ) {
ULONG i;
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
PCREATE_THREAD_NOTIFY_ROUTINE Rtn; for (i = ; 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, ); 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, ); return Status;
}
} Status = ObInsertObject (Thread,
AccessState,
DesiredAccess,
,
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) == );
PS_SET_THREAD_CREATE_TIME(Thread, CreateTime); if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == ) {
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,
,
NULL,
&PsThreadType->TypeInfo.GenericMapping,
PreviousMode,
&Thread->GrantedAccess,
&accesst); PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken); ObReleaseObjectSecurity (SecurityDescriptor,
MemoryAllocated); if (!AccessCheck) {
Thread->GrantedAccess = ;
} 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线程也就基本了解了!!
感觉还是有很多不足的地方,后面再慢慢补充吧,不想写了!!
windows中的进程和线程的更多相关文章
- Java中的进程与线程(总结篇)
详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...
- c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
- 5、CPU 的线程与操作系统的线程有何关系?操作系统中的进程和线程是什么关系?
CPU中的线程和操作系统(OS)中的线程即不同,在调度的时候又有些关联.CPU中的线程,我们叫它们Thread,和OS中的线程的名字一样.它来自同步多线程(SMT,Simultaneous Multi ...
- Java中的进程和线程
Java中的进程与线程 一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是 ...
- python中的进程、线程(threading、multiprocessing、Queue、subprocess)
Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...
- Node.js 中的进程和线程
线程和进程是计算机操作系统的基础概念,在程序员中属于高频词汇,那如何理解呢?Node.js 中的进程和线程又是怎样的呢? 一.进程和线程 1.1.专业性文字定义 进程(Process),进程是计算机中 ...
- (转)java中的进程与线程
(转自地址http://www.ibm.com/developerworks/cn/java/j-lo-processthread/) Java 进程的建立方法 在 JDK 中,与进程有直接关系的类为 ...
- liunx中的进程与线程
1. 进程和线程 进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作(比如,创建,销毁等)都是有内核来实现的. Linux中的进程于Windows相比是很轻量级的,而且不严格区分进程和线程 ...
- java中的进程与线程及java对象的内存结构【转】
原文地址:http://rainforc.iteye.com/blog/2039501 1.实现线程的三种方式: 使用内核线程实现 内核线程(Kernel Thread, KLT)就是 ...
随机推荐
- How to clone a brach from github
git clone address.git -b brach_name destination_floder
- redhat6.8服务器版本 yum 源的配置
阿里云的源地址: http://mirrors.aliyun.com/ 打开后点击帮助: 有如下提示: 不过不能直接使用这个源,因为自己使用的是服务器版本,要修改一个变量,先将源文件下载下来. wge ...
- 应用DataAdapter对象更新数据库中的数据
using System.Data.SqlClient; namespace WindowsFormsApplication1 { public partial class Form1 : Form ...
- 《C++ Primer》笔记-#include,#ifndef
1.理解 #include 指示是怎样工作的 #include 设 施是 C++ 预处理器的一部分.预处理器处理程序的源代码,在编译器之前运行. C++ 继承了 C 的非常精细的预处理器.现在的 C+ ...
- 使ie9以下版本支持canvas,css3等主流html5技术的方法
1.前言. ie6,7,8支持html5,看起来比较难,其实有一种方法很通用,就是引入js和css,这种可插拔的引入对开发很有帮助.比如,下面是一个让网页支持canvas和css3的例子. 2.例 ...
- JAVA学习资源网站
中文java技术网——http://www.cn-java.com/ 灰狐动力(http://www.huihoo.com/)—— 该站点有许多的开源的项目的介绍和学习,涉及操作系统,数据库等许多方向 ...
- mysql -- 创建存储过程 往数据表中新增字段
需求: 往某数据库的某个表中新增一个字段(若该字段已存在,则不做操作:若该字段不存在,则新增) 百度了n久,没有符合要求的例子,只有参考加自己琢磨,最终终于给弄出来了,以下是几个版本的更迭 第一版: ...
- 【BZOJ】1057: [ZJOI2007]棋盘制作(单调栈)
http://www.lydsy.com/JudgeOnline/problem.php?id=1057 同某一题差不多?记不清是哪题了.. 就是每一行进行单调栈维护递增的高度,在进栈和出栈维护一下长 ...
- 【BZOJ】1607: [Usaco2008 Dec]Patting Heads 轻拍牛头(特殊的技巧)
http://www.lydsy.com/JudgeOnline/problem.php?id=1607 其实题目描述不清楚,应该是 别人拿的数能整除自己拿的数 数据范围很大,n<=100000 ...
- 复习及总结--.Net线程篇(1)
老是没耐心写这些东西,最近想想也工作两年了,该对自己的东西做个整理了,不知道这次能坚持写几篇,总得来说尽量督促自己吧 言归正传,.net中的多线程主要可以使用两种方法进行调用 1,异步调用 2,Thr ...