//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 "bigHello" //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 可以获取进程或线程退出代码