Windows Internals 笔记——进程
1.一般将进程定义成一个正在运行的程序的一个实例,由以下两部分构成:
- 一个内核对象,操作系统用它来管理进程,内核对象也是系统保存进程统计信息的地方。
- 一个地址空间,其中包含所有可执行文件或DLL模块的代码和数据。此外它还包含动态内存分配,比如线程堆栈和堆的分配。
2.进程要做任何事情,都必须让一个线程在它的上下文中运行。该线程负责执行进程地址空间包含的代码。
3.每个线程都有它自己的一组CPU寄存器和它自己的堆栈。对于所有要运行的线程,操作系统会轮流为每一个线程调度一些CPU时间,它会采取轮询的方式为每个线程都分配时间片,从而营造出所有线程都在并发运行的假象。
4.用户运行应用程序时,操作系统的加载程序会检查可执行文件映像的文件头,并获取这个子系统。如果此值表明是一个CUI程序,加载程序会自动确保有一个可用的文本控制台窗口(从命令提示符启动),或者创建一个新窗口(从windows资源管理器启动)。如果此值表明是一个GUI程序,加载器只是加载这个程序。一旦程序开始运行,操作系统就不再关心应用程序的界面是什么类型。
5.Windows应用程序必须有一个入口点函数,应用程序开始运行时,这个函数会被调用。操作系统实际并不调用我们所写的入口点函数。相反,它会调用由C/C++运行库实现并在链接时使用-entry:命令行选项来设置的一个C/C++运行时启动函数。该函数将初始化C/C++运行库,使我们能调用malloc和free之类的函数。它还确保了在我们的代码开始执行之前,我们声明的任何全局和静态C++对象都被正确地构造。
6.在链接可执行文件时,将根据链接器开关 /SUBSYSTEM:WINDOWS或者 /SUBSYSTEM:CONSOLE以及UNICODE和ANSI来选择正确地C/C++运行库启动函数。
7.一旦链接器开关/SUBSYSTEM被移除,链接器会检查代码中的函数(WinMain,main...)是四个中的哪一个来推算。
8.所有C/C++运行库启动函数所做的事情基本都一样,区别在于它们要处理的是ANSI还是UNICODE字符串。以及在初始化C运行库之后,它们调用的是哪一个入口点函数。这些启动函数的用途简单总结如下。
- 获取指向新进程的完整命令行的一个指针
- 获取指向新进程的环境变量的一个指针
- 初始化C/C++运行库的全局变量。如果包含了StdLib.h,我们的代码就可以访问这些变量。
- 初始化C运行库内存分配函数(malloc和calloc)和其他底层I/O例程使用的堆(heap)。
- 调用所有全局和静态C++类对象的构造函数。
9.完成上述所有的初始化工作之后,C/C++启动函数就会调用应用程序的入口点函数。
// 如果我们写了_tWinMain & 定义了_UNICODE
GetStartupInfo(&StartupInfo);
int nMainRetVal = wWinMain((HINSTANCE)&__ImageBase, NULL, pszCommandLineUnicode,
(StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ? StartupInfo.wShowWindow : SW_SHOWDEFAULT);
// 如果我们写了_tWinMain & 没有定义_UNICODE
GetStartupInfo(&StartupInfo);
int nMainRetVal = WinMain((HINSTANCE)&__ImageBase, NULL, pszCommandLineAnsi,
(StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ? StartupInfo.wShowWindow : SW_SHOWDEFAULT);
注意,__ImageBase是一个链接器定义的伪变量,表明可执行文件被映射到应用程序内存中的什么位置。
// 如果我们写了_tmain & 定义了_UNICODE
int nMainRetval = wmain(argc, argv, envp);
// 如果我们写了_tmain & 没有定义_UNICODE
int nMainRetval = main(argc, argv, envp);
注意,用VS向导生成应用程序时,CUI应用程序的入口中没有定义第三个参数(环境变量块),如果需要访问进程的环境变量,只需将上述调用换成下面这一行:
int _tmain(int argc, TCHAR* argv[], TCHAR* env[])
这个env参数指向一个数组,数组中包含所有环境变量及其值,两者用等号(=)分隔。
10.入口点函数返回后,启动函数将调用C运行库函数exit,向其传递返回值(nMainRetVal)。exit函数执行以下任务。
- 调用_onexit函数调用所注册的任何一个函数
- 调用所有全局和静态C++类对象的析构函数。
- 在DEBUG生成中,如果设置了_CRTDBG_LEAK_CHECK_DF标志,就通过调用_CrtDumpMemoryLeaks函数来生成内存泄漏报告。
- 调用操作系统的ExitProcess函数,向其传入nMainRetVal。这会导致操作系统“杀死”我们的进程,并设置它的退出代码。
11.加载到进程地址空间的每一个可执行文件或DLL文件都被赋予了一个独一无二的实例句柄。可执行文件的实例被当作WinMain函数的第一个参数hInstanceExe传入。
12.事实上,HMOUDLE和HINSTANCE完全是一回事。之所以有两种数据类型,是由于在16位Windows中,HMOUDLE和HINSTANCE表示不同类型的数据。
13.WinMain的hInstanceExe参数的实际值是一个内存基地址,系统将可执行文件的映像加载到进程地址空间中的这个位置。具体加载到哪一个基地址,是由连接器决定的。不同的链接器使用不同的默认基地址。可以使用GetModuleHandle函数来返回一个句柄/基地址。
14.C/C++运行库启动代码总是向WinMain的hPrevInstance参数传递NULL。该参数用于16位Windows系统。VS在向导生成的C++ GUI项目中利用UNREFERENCED_PARAMETER宏来消除这种警告。
15.C运行库的启动代码开始执行一个GUI应用程序的时候,会调用Windows函数GetCommandLine来获取进程的完整命令行,忽略可执行文件的名称,然后将指向命令行剩余部分的一个指针传给WinMain的pszCmdLine参数。
16.GetCommandLine函数返回一个缓冲区指针,缓冲区中包含完整的命令行(包括已执行的文件的完整路径名),而且这个函数返回的总是同一个缓冲区的地址,所以最好不要向其中写入数据。
17.每个进程都有一个与它关联的环境块,这是在进程地址空间内分配的一块内存,其中包含字符串和下面相似:
注意空格是有意义的。
18.用户登录Windows时,系统会创建外壳(shell)进程,并将一组环境字符串与其关联。系统通过检查注册表中的两个注册表项来获得初始的环境字符串。
- 第一个注册表项包含应用于系统的所有环境变量的列表:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
- 第二个注册表项包含应用于当前登录用户的所有环境变量的列表:
HKEY_CURRENT_USER\Environment
19.通常子进程会继承(不共享同一个环境块)一组环境变量,这些环境变量和父进程的环境变量相同,不过,父进程可以控制哪些环境变量允许子进程继承。
20.每个进程都关联了一组标志,作用时让系统知道进程如何响应严重错误,包括磁盘介质错误、未处理的异常、文件查找错误以及数据对齐错误等。进程可以调用SetErrorMode(UINT fuErrorMode)函数来告诉系统如何处理这些错误。
默认情况下,子进程会继承父进程的错误模式标志,例如一个进程已经打开了一个错误标志,并生成了一个子进程,则子进程也会打开这个标志。不过子进程并不知道这一点。父进程可以阻止子进程继承其错误模式。
21.系统跟踪记录着进程的当前驱动器和目录,但没有记录每个驱动器的当前目录,操作系统通过进程的环境字符串来提供多个驱动器的当前目录。例如:
=C:C:\Utility\Bin
=D:=D:\Program Files
如果变量没有找到,系统就假定指定驱动器的当前目录是它的根目录。
Windows Internals 笔记——进程的更多相关文章
- Windows Internals 笔记——进程的权限
1.大多数用户都用一个管理员账户来登录Windows,在Vista之前,这样的登录会创建一个安全令牌.每当有代码试图使用一个受保护的安全资源时,操作系统就会出示这个令牌.从包括Windows资源管理器 ...
- Windows Internals 笔记——终止进程
1.进程可以通过以下四种方式终止: 主线程的入口点函数返回(强烈推荐的方式) 进程中的一个线程调用ExitProcess函数(避免这种方式) 另一个进程中的线程调用TerminateProcess函数 ...
- Windows Internals 笔记——关联性
1.默认情况下,Windows Vista在给线程分配处理器时,使用软关联.意思是如果其他因素都一样,系统将使线程在上一次运行的处理器上运行.让线程始终在同一个处理器上运行有助于重用仍在处理器高速缓存 ...
- Windows Internals 笔记——线程优先级
1.每个线程都被赋予0(最低)~31(最高)的优先级数.当系统确定给哪个线程分配CPU时,它会首先查看优先级为31的线程,并以循环的方式进行调度.如果有优先级为31的线程可供调度,那么系统就会将CPU ...
- Windows Internals 笔记——线程调度
1.线程内核对象中的CONTEXT反应了线程上一次执行时CPU寄存器的状态.大约每隔20ms,Windows都会查看所有当前存在的线程内核对象.Windows在可调度的线程内核对象中选择一个,并将上次 ...
- Windows Internals 笔记——线程
1.进程有两个组成部分,一个进程内核对象和一个地址空间.线程也有两个组成部分: 一个是线程的内核对象,操作系统用它管理线程.系统还用内核对象来存放线程统计信息的地方. 一个线程栈,用于维护线程执行时所 ...
- Windows Internals 笔记——作业
1.Windows提供了一个作业内核对象,它允许我们将进程组合在一起并创建一个“沙箱”来限制进程能够做什么.创建只包含一个进程的作业同样非常有用,因为这样可以对进程施加平时不能施加的限制. 2.如果进 ...
- Windows Internals 笔记——CreateProcess
1.一个线程调用CreateProcess时,系统将创建一个进程内核对象,其初始使用计数为1.然后系统为新进程的主线程创建一个线程内核对象(使其计数为1). 2.CreateProcess在进程完全初 ...
- Windows Internals 笔记——内核对象
1.每个内核对象都只是一个内存块,它由操作系统内核分配,并只能由操作系统内核访问.这个内存块是一个数据结构,其成员维护着与对象相关的信息. 2.调用一个会创建内核对象的函数后,函数会返回一个句柄,它标 ...
随机推荐
- CodeForces 91B Queue
题目链接:http://codeforces.com/contest/91/problem/B 题目大意: 有n头大象排队买票,第i头大象的年龄为ai,如果有比他年轻的大象排在他前面,这头大象就会非常 ...
- 其它综合-使用Putty远程连接管理Linux实践
使用Putty远程连接管理Linux实践 1.获取putty 获取 putty有很多方法,以下是我为大家提供的下载地址: 个人网盘地址,提取码:tz83 官方下载地址 解释: 官方下载的是 zip 压 ...
- 五、Java多人博客系统-2.0版本-数据库设计
数据库设计表如下:文章类别表.文章表.评论表.留言表. 文章列表表:存放文章类别,首页菜单生成也是从这个表取的. 文章表:存放文章标题.发表时间.内容等信息. 评论表:文章评论内容. 留言表:用户发表 ...
- Git冲突:You have not concluded your merge
You have not concluded your merge. (MERGE_HEAD exists) Git本地有修改如何强制更新 我尝试过用git pull -f,总是提示 You have ...
- POJ1509 Glass Beads 【后缀自动机】
题目分析: 模板练手.看最长能走多远. 代码: #include<iostream> #include<cstdio> #include<cstdlib> #inc ...
- Python【第二篇】运算符及优先级、数据类型及常用操作、深浅拷贝
一.运算符及优先级 Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 1.算数运算符 运算符 描述 实例,a=20,b=10 + 加 a+b输出结果30 - 减 a-b输出结果 ...
- luogu4770 [NOI2018]你的名字 (SAM+主席树)
对S建SAM,拿着T在上面跑 跑的时候不仅无法转移要跳parent,转移过去不在范围内也要跳parent(注意因为范围和长度有关,跳的时候应该把长度一点一点地缩) 这样就能得到对于T的每个前缀,它最长 ...
- c# 获取端口的连接数,网站的连接数
端口连接数: public static int PortTcpConnection(int port) { IPGlobalProperties properti = IPGlobalPropert ...
- 微信小程序之 3d轮播(swiper来实现)
以前写过一篇3d轮播,就是这篇,使用的方法比较笨拙,而且代码不简洁.这次发现swiper也能实现同样的效果.故记录一下. 先看看效果: wxml: <swiper previous-margin ...
- Oracle Database 10g安装
前言 oracle_10g_32位的安装包 链接:https://pan.baidu.com/s/1v1ZWYSjWLzKo3GnDuV5nrg 密码:88yd PLSQL Developer 32位 ...