windows核心编程(笔记二)

来源:互联网 发布:手机家具设计软件 编辑:程序博客网 时间:2024/04/26 21:52

第4章 进程

进程通常被定义为一个正在运行的程序的实例,它由两个部分组成:
1.一个是操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。
2.另一个是地址空间,它包含所有可执行模块或D L L 模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。

4.3 终止进程的运行
若要终止进程的运行,可以使用下面四种方法:
1.主线程的进入点函数返回(最好使用这个方法)。 
2.进程中的一个线程调用ExitProcess函数(应该避免使用这种方法)。 
3.另一个进程中的线程调用TerminateProcess函数(应该避免使用这种方法)。 
4.进程中的所有线程自行终止运行(这种情况几乎从未发生)。

4.3.1 主线程的进入点函数返回
线程的进入点函数返回,可以确保下列操作的实现:
1.该线程创建的任何C + +对象将能使用它们的析构函数正确地撤消。
2.操作系统将能正确地释放该线程的堆栈使用的内存。 
3.系统将进程的退出代码(在进程的内核对象中维护)设置为进入点函数的返回值。
4.系统将进程内核对象的返回值递减1。

4.3.2 ExitProcess函数
当进程中的一个线程调用ExitProcess函数时,进程便终止运行.
调用ExitProcess或ExitThread可使进程或线程在函数中就终止运行。就操作系统而言,这很好,进程或线程的所有操作系统资源都将被全部清除。但是,C/C++应用程序应该避免调用这些函数,因为C/ C++运行期也许无法正确地清除。

class CSomeObj
{
public:
CSomeObj() 

printf("Constructor\r\n");
}

~CSomeObj() 

printf("Destructor\r\n");
}
};
CSomeObj g_GlobalObj;
int main(int argc, char* argv[])
{
CSomeObj LocalObj;
ExitProcess(0);
return 0;
}

当上面的代码运行时,将会看到:
Constructor
Constructor
它创建了两个对象,一个是全局对象,另一个是局部对象。不过决不会看到Destructor这个单词出现, C++对象没有被正确地撤消,因为ExitProcess函数强制进程在现场终止运行,C/C++运行期没有机会进行清除。
如前所述,决不应该显式调用ExitProcess函数。如果在上面的代码中删除了对ExitProcess的调用,那么运行该程序产生的结果如下:
Constructor
Constructor
Destructor
Destructor
只要让主线程的进入点函数返回,C/C++运行期就能够执行它的清除操作,并且正确地撤消任何或所有的C++对象。顺便讲一下,这个说明不仅仅适用于C++对象。C++运行期能够代表进程执行许多操作,最好允许运行期正确地将它清除。

4.3.3 TerminateProcess函数
该函数与ExitProcess有一个很大的差别,那就是任何线程都可以调用TerminateProcess来终止另一个进程或它自己的进程的运行。hProcess参数用于标识要终止运行的进程的句柄。当进程终止运行时,它的退出代码将成为你作为fuExitCode参数来传递的值。

只有当无法用另一种方法来迫使进程退出时,才应该使用TerminateProcess。终止运行的进程绝对得不到关于它将终止运行的任何通知,因为应用程序无法正确地清除,并且不能避免自己被撤消(除非通过正常的安全机制)。例如,进程无法将内存中它拥有的任何信息迅速送往磁盘。
虽然进程确实没有机会执行自己的清除操作,但是操作系统可以在进程之后进行全面的清除,使得所有操作系统资源都不会保留下来。这意味着进程使用的所有内存均被释放,所有打开的文件全部关闭,所有内核对象的使用计数均被递减,同时所有的用户对象和GDI对象均被撤消。


一旦进程终止运行(无论采用何种方法),系统将确保该进程不会将它的任何部分遗留下来。绝对没有办法知道该进程是否曾经运行过。进程一旦终止运行,它绝对不会留下任何蛛丝马迹。希望这是很清楚的。

注意TerminateProcess函数是个异步运行的函数,也就是说,它会告诉系统,你想要进程终止运行,但是当函数返回时,你无法保证该进程已经终止运行。因此,如果想要确切地了解进程是否已经终止运行,必须调用WaitForSingleObject函数(第9章介绍)或者类似的函数,并传递进程的句柄。

4.3.4 进程终止运行时出现的情况
当进程终止运行时,下列操作将启动运行:
1) 进程中剩余的所有线程全部终止运行。
2) 进程指定的所有用户对象和GDI对象均被释放,所有内核对象均被关闭(如果没有其他进程打开它们的句柄,那么这些内核对象将被撤消。但是,如果其他进程打开了它们的句柄,内核对象将不会撤消(其他对象共享了改进程的内核对象))。
3) 进程的退出代码将从STILL_ACTIVE改为传递给ExitProcess或TerminateProcess的代码。
4) 进程内核对象的状态变成收到通知的状态(关于传送通知的详细说明,参见第9章)。系统中的其他线程可以挂起,直到进程终止运行。
5) 进程内核对象的使用计数递减1。
注意,进程的内核对象的寿命至少可以达到进程本身那么长,但是进程内核对象的寿命可能大大超过它的进程寿命。当进程终止运行时,系统能够自动确定它的内核对象的使用计数。如果使用计数降为0,那么没有其他进程拥有该对象打开的句柄,当进程被撤消时,对象也被撤消。


第七章 线程的调度、优先级和亲缘性
7.9 程序的优先级
进程是如何被赋予优先级类的呢?当调用CreateProcess时,可以在fdwCreate参数中传递需要的优先级类。一旦子进程运行,它就能够通过调用SetPriorityClass来改变它自己的优先级类:
BOOL SetPriorityClass(HANDLE hProcess,DWORD fdwPriority);
一般来说,进程将试图改变它自己的优先级类。下面是如何使一个进程将它自己的优先级类设置为空闲的例子:
BOOL SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS);
当一个线程刚刚创建时,它的相对线程优先级总是设置为正常优先级。我总感到有些奇怪,CreateThread没有为调用者提供一个设置新线程的相对优先级的方法。若要设置和获得线程的相对优先级,必须调用下面的这些函数:
BOOL SetThreadPriority(HANDLE hThread,int nPriority);

若要创建一个带有相对优先级为空闲的线程,可以执行类似下面的代码:
DWORD dwThreadID;
HANDLE hThread = CreateThread(NULL, 0, ThreadFunc,NULL, CREATE_SUSPENDED, &dwThreadID);
SetThreadPriority(hThread, THREAD_PRIORITY_IDLE);
ResumeThread(hThread);
CloseHandle(hThread);

原创粉丝点击