Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)

时间:2022-06-09 15:33:58
线程定义
线程也是由两个部分组成的:

• 一个是线程的内核对象,操作系统用它来对线程实施管理。内核对象也是系统用来存放线程统计信息的地方。

• 另一个是线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量(第1 6章将进一步介绍系统如何管理线程堆栈)。

如何创建线程:


Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)HANDLE CreateThread(   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)PSECURITY_ATTRIBUTES psa,   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
// 安全结构体如果希望内核对象能被子进程继承必须将b I n h e r i t H a n d l e成员被初始化为T R U E   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
DWORD cbStack,   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
// 线程堆栈大小   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
PTHREAD_START_ROUTINE pfnStartAddr,   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
// 线程函数地址   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
PVOID pvParam,   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
// 线程参数地址   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
DWORD fdwCreate,   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
// 创建标示(是否立即执行在这指定)   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
PDWORD pdwThreadID   
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
// 线程ID   );

线程创建时发生了什么:
1.系统创建内核对象,使用计数为2、暂停计数为1、退出码为 STILL_ACTIVE(0 x 1 0 3)。
2.分配线程堆栈内存,并将pvParam,和ptnStarAddr压入堆栈,SP指向ptnStarAddr,IPBaseThreadStart函数所在地址(也就是说代码从此处执行)。
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex) 

 

基于C/C++运行库的线程创建函数
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)unsigned  long  _beginthreadex(
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
void   * security,
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)unsigned stack_size,
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)unsigned 
* start_address)( void   * ),
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)
void   * arglist,
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)unsigned initflag,
Windows核心编程笔记第十篇(线程创建的过程和基于C/C++运行库的 _beginthreadex)unsigned 
* thrdaddr);

此函数为每个线程单独分配数据块解决了一部分问题,其实也是在内部调用CreateThread来实现创建新线程的,只不过在应当传递线程函数地址的地方传递了_threadstartex,在应该传递线程参数的地方传递了(PVOID)ptd。看一 下_threadstartex的伪码
static  unsigned  long  WINAPI threadstartex ( void *  ptd){  
// Note:ptd is the address of this thread's tiddata block.
 
// Associate the tiddata block with this thread.  
TlsSetValue(__tlsindex, ptd);
 
// Save this thread ID in the tiddata block.  
((_ptiddata)ptd) -> _tid  =  GetCurrentThreadId();
 
// Initialize floating-point support (code not shown).  
 
// wrap desired thread function in SEH frame to  
 
// handle run-time errors and signal support.
 __try  {     
// Call desired thread function, passing it the desired parameter.     
// Pass thread's exit code value to _endthreadex.     
_endthreadex(       
 ((unsigned(WINAPI 
* )( void   * ))(((_ptiddata)ptd) -> _initaddr))
          (((_ptiddata)ptd)
-> _initarg));  
}  
__except(_XcptFilter(GetExceptionCode(), GetExceptionInformation()))
 {    
 
// The C run-time's exception handler deals with run-time errors     
// and signal support; we should never get it here.    
 _exit(GetExceptionCode());  }
 
// We never get here; the thread dies in this function.
  return ( 0L );
}

注意到这里((unsigned(WINAPI *)(void *))(((_ptiddata)ptd)->_initaddr))(((_ptiddata)ptd)->_initarg));
在_threadstartex函数里面启动了新的线程函数....
后面的代码就是加入一个SEH链处理异常了
前面的代码呢主要是将PTD和线程做关联.
这个过程相当于在新线程外面加了一层包装吧...
在这个包装的过程中系统为线程关联了一个独立的PTD数据块。
在C/C++运行库的基础上线程的创建过程是这样的:
_beginthreadex()
----(将线程的启动地址、参数地址存入PTD,替换CreateThread()的线程函数地址参数,和线程参数参数,为启动threadstartex()做准备)--------->
 CreateThread()
---(按照正常的过程建立内核对象、分配线程堆栈、并将beginthreadex函数中替换过得参数压入栈,置CS与IP寄存器指向BaseThreadStart)---->
BaseThreadStart()
---------------->
threadstartex
----------(为线程关联PTD)--------------->
pfnStartAddr()
欢迎各位大牛、小牛批评指正