您的位置:首页 > 编程语言 > C语言/C++

Windows Via C/C++ 读书笔记 3

2009-05-25 20:37 363 查看
Windows Via C/C++ 读书笔记 3

1. Job

JOB,翻译成工作或者任务。JOB是管理多个进程的集合体。如果你需要一次关闭多个进程,并且要在所有进程退出后得到通知,那么可以使用JOB这种对象。

1.1. Job的使用流程

1. 创建JOB或查找一个已有的JOB
2. 把进程加入到JOB中
3. 关闭JOB
4. 等待JOB结束(JOB结束后会处于Signaled状态,因此可以用WaitForSingleObject 等待)

2. 线程

线程含有两个东西:
一个内核对象,用于操作系统管理,含有线程的静态信息。和进程内核对象类似。
线程这个东西用了太久了,就不废话了。拣点以前没注意的死角说说。

2.1. 线程栈(Thread`s Stack)。

线程栈是有大小的,默认是1M。超过的时候会有"overflow"异常,但是操作系统会捕捉异常,给栈更多的空间,使栈能动态增长。
创建线程的时候可以通过参数设置栈大小,也可以在link选项/STACK中设置,最终取两者中大的一个。

2.2. 线程结束

和进程结束太类似了。有点要注意:如果用"TerminateThread" 杀死另外一个线程,操作系统是不会销毁它的栈,直到这个进程结束。
"GetExitCodeThread"可以得到线程的退出码,如果线程没有退出,那么这个值是"STILL_ACTIVE"

2.3. 线程内部

线程内核对象:



SP(Stack Pointer)指向栈的头,栈顶是线程函数的参数,接着是执行代码的起始地址。
IP(Instruction Pointer) 指向函数"RtluserThreadStart",该函数在"NTDLL.dll"中导出。
还保留了线程执行时CPU寄存器的状态,用于线程切换的时候,可以恢复切换前的执行状态。

VOID RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {
__try {
ExitThread((pfnStartAddr)(pvParam));
}

__except(UnhandledExceptionFilter(GetExceptionInformation())) {
ExitProcess(GetExceptionCode());
}
// NOTE: We never get here.

}
RtlUserThreadStart原型
线程执行肯定会在RtlUserThreadStart中结束,正常结束调用ExitThread(线程结束),异常调用ExitProcess(进程结束),并且会弹出窗口报错。
该函数的第一个参数,函数地址参数是操作系统显示写入线程stack中,使该函数看起来像是被别人调用,实际上它是起始执行函数。

2.4. _beginthreadex & _endthreadex

2.4.1. 用_beginthreadex代替CreateThread

多线程的时候,有些全局变量的访问会冲突。如:errno, _doserrno, strtok, _wcstok, strerror, _strerror, tmpnam, tmpfile, asctime, _wasctime, gmtime, _ecvt, and _fcvt。
为了让各个线程有自己的变量,用_beginthreadex代替CreateThread函数。
在_beginthreadex函数中会给每个线程创建一个变量块(类型为_tiddata,这个结构存了这些errno什么的,还有线程函数的入口地址)。
下面都是伪代码,描述函数主要过程:
_beginthreadex
{
......
分配一块内存,结构为_tiddata,变量名为pid;
创建一个线程(CreateThread),函数为_threadstartex,把pid作为参数传给这个函数;
......
}

_threadstartex
{
......
把pid与TSL关联,变成与线程相关的变量

Try{
执行线程函数,线程函数地址存在pid中
执行_endthreadex结束线程,把线程函数执行退出结果作为_endthreadex的参数。
}catch
{
.......
}
......
}

_endthreadex
{
释放TSL变量pid
调用ExitThread退出线程
}

如过用CreateThread代替_beginthreadex,会有什么问题呢?
1.操作系统会在第一次使用pid的时候检查是否已经分配,如果没有会创建一个新的并关联。但是因为没有调用_endthreadex,这个变量不会被释放直到进程退出。
2.原文是First, if the thread uses the C/C++ run-time library's signal function, the entire process terminates because the structured exception handling frame has not been prepared.
这块还不清楚,在_beginthreadex的try块会处理一些异常,如果用CreateThread这种异常没有捕捉会使整个进程退出。

2.5. 不要使用_beginthread 和 _endthread

因为这两个函数的参数少,因此功能会比ex版本的函数功能弱,比如不能带安全参数,不能带suspend标志,不能返回线程号。_endthread只能使退出码硬编码为0。
_endthread会关闭线程句柄,因此线程结束后线程句柄不能访问,而ex版本的不会,例如下面这段代码,线程句柄在_beginthread后可能已经关闭了,后面的访问会出错。

DWORD dwExitCode;
HANDLE hThread = _beginthread(...);
GetExitCodeThread(hThread, &dwExitCode); //error!
CloseHandle(hThread);

2.6. 用DuplicateHandle产生真实句柄

The GetCurrentThread function retrieves a pseudo handle for the current thread.
因此需要用函数转换一下,否则不能被其它函数使用,也不能传递给其它线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: