代码改变世界

第四章 进程

2018-01-27 15:04  szn好色仙人  阅读(587)  评论(0编辑  收藏  举报
//1.
一般将进程定义为一个正在运行的程序的一个实例,由两个部分组成:
(A):一个内核对象,操作系统利用内核对象来管理进程。内核对象也是系统保存进程统计信息的地方
(B):一个地址空间,包含所有可执行文件或DLL模块的代码和数据

//2.
进程要做任何事情,都必须让一个线程在其上下文中运行,该线程负责执行进程地址空间的代码,
当系统创建进程的时候,会自动为其创建一个主线程
操作系统会以轮询的方式为每个线程分配时间片供其运行,在多线程环境下,多个线程可以同时运行

//3. 关于入口函数
(A):Windows支持两种类型的应用程序:GUI程序和CUI程序
(B):前者为图像用户界面(Graphical User Interface),后者为控制台用户界面(Console User Interface)
(C):前者的连接器开关是 /SUBSYSTEM:WINDOWS 后者是 /SUBSYSTEM:CONSOLE
(D):Windows应用程序必须有一个入口点函数,应用程序开始运行时,这个函数会被调用
(E):GUI程序对应入口点函数为 WinMain ,CUI程序对应入口点函数为 main
(F):操作系统实际并不调用我们所写的入口点函数,相反他会调用由C/C++运行库实现并在链接时使用-enter:命令行选项来设置一个C/C++运行时启动函数
(G):上述函数将初始化C/C++运行库,使得我们能调用 malloc 等函数,还确保在我们的代码开始执行前,我们声明的全局变量和全局静态C++对象被正确的构造
(H):如下命令也可以用于设置入口点函数:#pragma comment(linker, "/entry:\"Fun\"")
(I):在vs2010中使用自定义入口函数并有定义全局对象则会有警告 : warning LNK4210: 存在 .CRT 节;可能有未处理的静态初始值设定项或结束符

//4. 启动函数用途
(A):获取指向新进程的完整命令行的指针
(B):获取指向新进程的环境变量的一个指针
(C):初始化C/C++运行库的全局变量,如果包含了 stdlib.h 则我们的代码就可以访问这些变量了
(D):初始化C运行库内存分配函数和其他底层I/O使用的堆
(E):调用所有全局C++类对象的构造函数
备注:自定义入口函数并不完全具备上述功能

//5.
如需访问进程的环境变量,可以使用如下形式:
int _tmain(int argc, _TCHAR* argv[], _TCHAR* pStr[])
则 pStr 中将存储环境变量

//6.
入口点函数返回后,启动函数将调用C运行库函数 exit ,向其传递返回值,此函数执行以下任务:
(A):调用 _onexit 函数调用所注册的任何一个函数
(B):调用所有全局C++类对象的析构函数
(C):在 DEBUG 生成中,如果设置了 _CRTDBG_LEAK_CHECK_DF 标志,就通过调用 _CrtDumpMemoryLeaks 函数来生成内存泄漏报告
(D):调用操作系统的 ExitProcess 函数,向其传递返回值,这回导致系统杀死我们的进程,并设置退出代码
备注: 定义在头文件 crtdbg.h 中的 _CrtDumpMemoryLeaks 函数,会检测到运行到此函数为止的所有未释放的内存并打印

//7.
加载到进程地址空间的每一个可执行文件或者DLL文件都被赋予一个独一无二的实例句柄,可执行文件的实例句柄被 WinMain 的第一个参数传入

//8.
(A):每个进程都有一个与他关联的环境块,这是在进程地址空间分配的一块内存,其中的字符串的第一部分是一个环境变量的名称后接一个等号,
	等号后是希望赋予给变量的值(备注:用=进行分割,所以空格在环境变量的值中是有意义的)
(B):用户可以在环境变量对话框中修改环境变量,为了使得这些改动生效,必须注销后重新登录
(C):通常子进程会继承一组环境变量,这些环境变量与父进程的环境变量相同。父进程可以控制继承的过程
(D):子进程继承环境变量并不会与父进程共享同一个环境块,所以子进程修改了自己环境块中的环境变量并不会影响父进程
	SetEnvironmentVariable GetEnvironmentVariable 用户改变与获取与进程绑定的环境变量,且更改不会影响其他进程
	https://msdn.microsoft.com/en-us/library/windows/desktop/ms686206(v=vs.85).aspx
(F):许多字符串的内部包含了可替换字符串,比如注册表的某个地方发现了下面这个字符:%s%\1234 则两个%之间的这一部分就是一个可替换字符串,
	这种情况下,对应环境字符串s的值就放在这里, ExpandEnvironmentStrings 可以执行以上转换
const int nLenC = 1024;
TCHAR wBuff[nLenC] = {};
GetEnvironmentVariable(TEXT("szn"), wBuff, nLenC);				//wBuff = 0x0015efe4 "big"

memset(wBuff, 0, sizeof wBuff);
GetEnvironmentVariable(TEXT("%szn%\\Hello"), wBuff, nLenC);		//wBuff = 0x0038ef98 "HelloWord"

memset(wBuff, 0, sizeof wBuff);
ExpandEnvironmentStrings(TEXT("%szn%\\Hello"), wBuff, nLenC);	//wBuff = 0x0035f4f0 "big\Hello"

//9.

GetCurrentDirectory	SetCurrentDirectory 用于获取与设置当前程序目录

//10.
GetVersionEx 用于获取系统信息

//11.
利用 CreateProcess 创建一个进程
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx

//12.
终止进程:
(A):主线程入口点函数返回(强烈建议)
(B):进程中的一个线程主动调用 ExitProcess (强烈不推荐)
(C):另一个进程的线程调用 TerminateProcess (强烈不推荐)
(D):进程中的所有线程自然死亡 (几乎不会发生)

(A):
设计一个应用程序的时候,应该保证只有主线程的入口函数返回之后,这个应用进程才终止。这样才能保证主线程所有资源都被清理
让主线程的入口点函数返回,可以保证如下操作被正确执行:
(a):该线程创建的任何C++对象都将由这些对象的析构函数正确销毁
(b):操作系统正确的释放线程栈使用的内存
(c):系统将进程的退出代码(在内核对象中维护)设为入口点函数的返回值
(d):系统递减进程内核对象的使用计数

(B):
(a):当主线程的入口函数返回时,会返回到C/C++运行库启动代码,后者将正确清理进程使用的全部C运行时资源。释放了C运行时资源之后,
	C运行时启动代码将显示调用 ExitProcess ,并将入口函数返回值传给他。所以当主线程入口点函数返回,就会终止整个进程
(b:)如果入口点函数中调用 ExitThread ,应用程序的主线程将停止执行,但只要进程中仍有其他线程运行,进程就不会终止
(c):调用 ExitProcess 或 ExitThread 会导致进程或线程直接终止运行,就操作系统而言,这样做不会有什么问题,进程或线程的资源都会被释放,
	不过对于C/C++应用程序而言应避免调用这些函数,因为C/C++运行库也许也不能执行正确的清理工作,比如C++类的析构函数可能不会被调用

(C):
(a):使用 TerminateProcess 也可以终止一个进程,任何一个线程都可以使用此函数来干掉另一个进程或自身进程。
(b):使用 TerminateProcess 时,被终止的进程得不到自己要被干掉的通知,应用程序不能正确的清理,也不能防止自己被干掉(除非通过正常的安全机制)
(c):虽然进程没有机会自己执行清理工作,但操作系统会在进程终止之后彻底清理,确保不会泄露任何操作系统资源
(d):一旦进程终止(不管如何终止),系统会保证不留他的任何部分。绝对没有任何办法知道那个进程是否运行过。进程在终止后绝对不会泄露任何东西
(e):TerminateProcess 此函数是异步的

一个进程终止时,系统一次执行以下操作:
(A):终止进程中遗留的线程
(B):释放所有用户对象与GDI对象
(C):进程的退出代码从 STILL_ACTIVE 变为传给 ExitProcess 或 TerminateProcess 函数的代码
(D):进程的内核对象的状态变为已触发
(E):进程对象的使用计数被减一

GetExitCodeProcess GetExitCodeThread 可以获取进程或线程退出代码