开篇:润墨网以专业的文秘视角,为您筛选了一篇Windows的进程与线程范文,如需获取更多写作素材,在线客服老师一对一协助。欢迎您的阅读与分享!
摘 要 对于线程,进程的概念一直都是搞开发程序的人员必须搞清的概念,其实总结起来就是,线程是进程的一部分,进程是程序的一部分。这个说法可能不太准确,但是可以指出期间的差别。
【关键词】进程 线程 句柄
进程是正在执行中的应用程序,磁盘上存储的可执行文件只能称之为文件而不能称为进程,内存中正在执行的文件才叫做进程。一个进程是一个执行中的文件使用资源的总和,包括虚拟地址空间、代码、数据、对象句柄、环境变量和执行单元等。当一个应用程序同时被多次执行时,产生的是多个进程,因为虽然它们由同一个文件执行而来,但是它们的地址空间等资源是互相隔离的,这与不同文件在执行的情况是一样的。
进程和线程的关系可以看做是“容器”和“内容物”的关系,进程是线程的容器,线程总是在某个进程的环境中被创建,它不可以脱离进程单独存在,而且线程的整个生命周期都存在于进程中,如果进程被结束,其中的线程也就自然结束了。
通常情况下,用户在应用程序中会使用win32 API调用相关函数,对大部分函数的调用最终都转到ntdll.dll中,ntdll.dll是连接用户模式代码和内核模式系统服务的桥梁,对于内核模式中提供的每一个服务,在ntdll.dll中都有一个相应的存根函数,该存根没有代码的具体实现,只提供参数传递和跳转功能,大部分函数的运行都转到了内核模式的ntoskml.exe中进行。
2 进程与线程的创建
·1.1内核模式下进程与线程的创建过程
(1)在内核中,一个进程的创建是从函数NtCreateProcess开始的。该函数位于文件ntoskml.exe中,该文件位于system32。它对用户传进的部分参数进行简单处理,然后交给函数NtCreateProcessEx。该函数的函数原型如下:
NTSTATUS __stdcall NtCreateProcessEx(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
代码省略
该函数检查句柄ProcessHandle是否可写,然后将创建工作交给函数。PspCreateProcess系统中所有进程的创建工作均由该函数负责完成。其创建进程的步骤大致如下:
步骤1 调用函数ObCreateObject创建Windows执行体内核对象EPROCESS,该对象为新进程的进程对象,该对象归系统所有,并由系统统一管理;
步骤2
调用函数ObReferenceObjectByHandle获取内存区对象的指针SectionHandle;
步骤3 根据传入的端口参数内容初始化新进程对象的相应字段;
步骤4 如果父进程不为空则创建一个全新的地址空间;
通过以上步骤,进程对象创建完成,但是进程是有“惰性”的,离开了线程,进程的空间只是一个固定在内存中的死的空间而已,直到它的第一个线程被创建和初始化以后进程中的代码才能被真正地运作起来。
(2)与进程的创建类似,内核中线程的创建是从函数NtCreateThread开始的。
内核中创建线程的步骤大致如下;
步骤1 根据进程句柄获得进程对象并将其放到局部变量中;
步骤2 调用函数ObCreateThread创建线程对象ETHREAD,并初始化;
步骤3 获取进程的RunDownProtect锁,以免线程创建过程中该进程被当掉;
·2、用户模式下进程与线程的创建过程
在用户层,创建一个进程通常使用kernel32.dll中的函数CreateProcess来完成。该函数一旦返回成功,新的进程和进程中的第一个线程就建立起来了。从用户层的角度看进程创建的大致步骤如下:
步骤1 调用函数CreateProcessW打开指定的可执行映像文件;
步骤2 调用ntdll.dll中的存根函数NtCreateProcessEx,该函数利用处理器的陷阱机制切换到内核模式下。
至此,进程与线程全部创建完成,开始执行用户空间中的代码。
3 进程环境块PEB、线程环境块TEB
3.1 进程环境块PEB
操作系统为每个进程设置一个数据结构,用来记录进程的相关信息。在NT中,该结构可以从进程空间的FS:[0x30]处找到。PEB描述的信息主要包括:进程状态、进程堆、PE映像信息等,其中有一个很重要的字段Ldr,该字段指向的结构中记录了进程加载进内存的所有模块的基地址,通过Ldr的三个链表就可以找到kernel32.dll的基地址。
typedef struct _PEB {
/*0A4*/ ULONG OSMajor;
/*0A8*/ ULONG OSMinor;}
其中,偏移40h处为指向一个RTL_BITMAP数据结构的指针,该结构的数据定义如下:
typedef struct _RTL_BITMAP {
ULONG SizeOfBitMap;
PULONG Buffer; }
显然其目的是要提供一个缓冲区,而真正的缓冲区在Buffer所指的地方。通常情况下,该值为PEB中的TlsBitmapBits[0x2],但是有时候也不排斥采用别的缓冲区,两个32位长的字只能提供64个标志位。
3.2 线程环境块TEB
typedef struct _TEB {
NT_TIB Tib;
代码省略}
偏移E10处的TlsSlots[0x40]字段是个无类型的指针数组(这个指针数组称为TLS存储槽),其大小为40h字节。也就是说一个线程同时存在的动态TLS不能超过64项。如果某一项动态TLS数据的大小不超过4个字节(PVOID数据类型占用4个字节),那么就可以直接存储在这个数组中,作为这个数组的一个元素。
现在还剩下一个关键的问题:到哪里找TIB呢?答案是:TIB永远放在fs段选择器指定的数据段的0偏移处,所以,fs:[0]的地方就是TIB结构的ExceptionList字段,这个答案对于Windows 9x系统和Windows NT系统都是有效的。由于一个进程中的不同线程可以有不同的环境,所以,在不同线程中fs段选择器可以使用不同的值,这种特征使每个线程都可以设置不同的回调函数。
也正是因为使用了fs段选择器,所以使SEH变得与硬件平台相关。
作者单位
天津机电职业技术学院 天津市 300131