《Windows核心编程》学习笔记(11)– 线程的创建
CreateThread(LPSECURITY_ATTRIBUTES lpsa,DWORD cbStack,LPTHREAD_START_ROUTINE lpStartAddr,LPVOID lpvThreadParam,DWORD fdwCreate,LPDWORD ); 调用CreateThread 时,系统会创建一个线程内核对象。这个线程内核对象不是线程本身,而是一个较小的数据结构,操作系统用这个结构来管理线程。 系统将进程地址空间的内存分配给线程堆栈使用。新线程在与负责创建的那个线程相同的进程上下文中运行。因此,新线程可以访问进程内核对象的所有句柄、进程中的所有内存以及同一个进程中其他所有线程的堆栈。这样一来,同一个进程中的多个进程可以很容易地互相通信。 其中参数lpStartAddr 是指定希望新线程执行线程函数的地址。lpvThreadParam 参数是线程函数的参数。线程函数可以执行我们希望他执行的任何任务,函数原型类似于:DWORD WINAPI ThreadFunc(PVOID pvParam) {DWORD dwResult;…return (dwResult);} 线程可以通过以下4种方法来终止运行。1.线程函数返回(这是强烈推荐的)。2.线程通过调用ExitThread函数“杀死”自己(要避免使用这种方法)。3.同一个进程或另一个进程中的线程调用TerminateThread函数(要避免使用这种方法)。4.包含线程的进程终止运行(这种方法避免使用)。 Ps:TerminateThread函数是异步的。也就是说,它告诉系统你想终止线程,但在函数返回时,并不保证线程已经终止了。如果需要确定线程已终止运行,还需要调用WaitForSingleObject或类似的函数,并向其传递线程的句柄。
1.对CreateThread函数的一个调用导致系统创建一个线程内核对象。该对象最初的使用计数为2。 (除非线程终止,而且从CreateThread返回的句柄关闭, 否则线程内核对象不会被销毁。); 2.暂停计数被设为1;(因为线程的初始化需要时间,我们当然不希望在线程准备好之前就执行它。) 3.退出代码被设为STILL_ACTIVE (0x103);(线程终止运行的时候,线程退出代码从STILL_ACTIVE (0x103)变成传给ExitThread 或TerminateThread的代码); 4.对象被设为nonsignaled(未触发)状态。 5.系统分配内存,供线程堆栈使用。然后系统将两个值写入新线程堆栈的最上端。写入线程堆栈的第一个值是传给 CreateThread函数的pvParam参数的值。紧接在它下方的是传给CreateThread函数的pfnStartAddr值。 6. 每个线程都有其自己的一组CPU寄存器,称为线程的上下文(context)。上下文反映了当线程上一 次执行时,线程的CPU寄存器的状态。线程的CPU寄存器全部保存在一个CONTEXT结构(在 WinNT.h头文件中定义)。CONTEXT结构本身保存在线程内核对象中。 7.指令指针和栈指针寄存器是线程上下文中最重要的两个寄存器。当线程 的内核对象被初始化的时候,CONTEXT结构的堆栈指针寄存器被设为pfnStartAddr在线程堆栈中的地址。而指令指针寄存器被设为RtlUserThreadStart函数(该函数未见于正式文档)的 地址,此函数是NTDLL.dll模块导出的。 8.线程完全初始化好之后,系统将检查CREATE_SUSPENDED标志是否传给CreateThread函数。如果此标记没有传递,系统将线程的暂停计数递增至0;随后,线程就可以调度给一个处理器去执行。然后,系统在实际的CPU寄存器中加载上一次在线程上下文中保存的值。现在,线程可以在其进程的地址空间中执行代码并处理数据了。
页:
[1]