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 笔记——进程的更多相关文章

  1. Windows Internals 笔记——进程的权限

    1.大多数用户都用一个管理员账户来登录Windows,在Vista之前,这样的登录会创建一个安全令牌.每当有代码试图使用一个受保护的安全资源时,操作系统就会出示这个令牌.从包括Windows资源管理器 ...

  2. Windows Internals 笔记——终止进程

    1.进程可以通过以下四种方式终止: 主线程的入口点函数返回(强烈推荐的方式) 进程中的一个线程调用ExitProcess函数(避免这种方式) 另一个进程中的线程调用TerminateProcess函数 ...

  3. Windows Internals 笔记——关联性

    1.默认情况下,Windows Vista在给线程分配处理器时,使用软关联.意思是如果其他因素都一样,系统将使线程在上一次运行的处理器上运行.让线程始终在同一个处理器上运行有助于重用仍在处理器高速缓存 ...

  4. Windows Internals 笔记——线程优先级

    1.每个线程都被赋予0(最低)~31(最高)的优先级数.当系统确定给哪个线程分配CPU时,它会首先查看优先级为31的线程,并以循环的方式进行调度.如果有优先级为31的线程可供调度,那么系统就会将CPU ...

  5. Windows Internals 笔记——线程调度

    1.线程内核对象中的CONTEXT反应了线程上一次执行时CPU寄存器的状态.大约每隔20ms,Windows都会查看所有当前存在的线程内核对象.Windows在可调度的线程内核对象中选择一个,并将上次 ...

  6. Windows Internals 笔记——线程

    1.进程有两个组成部分,一个进程内核对象和一个地址空间.线程也有两个组成部分: 一个是线程的内核对象,操作系统用它管理线程.系统还用内核对象来存放线程统计信息的地方. 一个线程栈,用于维护线程执行时所 ...

  7. Windows Internals 笔记——作业

    1.Windows提供了一个作业内核对象,它允许我们将进程组合在一起并创建一个“沙箱”来限制进程能够做什么.创建只包含一个进程的作业同样非常有用,因为这样可以对进程施加平时不能施加的限制. 2.如果进 ...

  8. Windows Internals 笔记——CreateProcess

    1.一个线程调用CreateProcess时,系统将创建一个进程内核对象,其初始使用计数为1.然后系统为新进程的主线程创建一个线程内核对象(使其计数为1). 2.CreateProcess在进程完全初 ...

  9. Windows Internals 笔记——内核对象

    1.每个内核对象都只是一个内存块,它由操作系统内核分配,并只能由操作系统内核访问.这个内存块是一个数据结构,其成员维护着与对象相关的信息. 2.调用一个会创建内核对象的函数后,函数会返回一个句柄,它标 ...

随机推荐

  1. Laravel 框架结构 以及目录文件解读(学习笔记)

    composer下载Laravel 5.4(由于PHP版本仅7.0,故未下载5.6) composer create-project laravel/laravel your-project-name ...

  2. WPF中利用控件的DataContext属性为多个TextBox绑定数据

    工作上需要从给定的接口获取数据,然后显示在界面的编辑框中,以往肯定会一个一个的去赋值,但这样太麻烦而且效率很低,不利于维护,于是想到了数据绑定这一方法,数据绑定主要利用INotifyPropertyC ...

  3. SpringBoot2.0的CacheManager配置

    http://rickgong.iteye.com/blog/2414263 @Configurationpublic class RedisConfig extends CachingConfigu ...

  4. 三、数据API-3

    预备 返回格式需要包括: // Code 状态码(200,400等) // Msg 提示信息(邮箱格式不正确:数据返回成功等) // Result 返回数据 一.WebAPI与传统MVC的区别是 MV ...

  5. [NOIp2016] 换教室

    题目类型:期望\(DP\) 传送门:>Here< 题意:现有\(N\)个时间段,每个时间段上一节课.如果不申请换教室,那么时间段\(i\)必须去教室\(c[i]\)上课,如果申请换课成功, ...

  6. position:sticky

    使用sticky定位可以简洁的实现固定功能 例如,左右布局页面,左侧菜单,右侧内容,内容区域滚动时,不希望菜单区域滚动,而是固定不动 以往要实现这个功能,需要使用fixed定位菜单,菜单脱离文档流,布 ...

  7. Zabbix通过Orabbix监控Oracle数据库

    一.背景 公司业务使用的是一直Oracle数据库,因为多次出现表空间满的时候不能及时发现,每次都是业务组的人员通知处理,这样下来DBA这边就比较被动,所以老大要求监控表空间剩余大小并且当剩余过小时能够 ...

  8. restful设计规范

    什么是restful? REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移” REST从资源的角度类 ...

  9. Springboot配置多数据源(Mysql和Orcale)--(Idea Maven JDBCTemplate支持下的)

    1.配置 orcale jdbc 对于一个Maven项目,使用Mysql时,可直接添加如下依赖: <dependency> <groupId>mysql</groupId ...

  10. Nginx常用的平滑重启

    之前在做运维工作中,经常需要添加虚拟主机,或者添加修改配置文件,但是测试环境还好,随便玩,如果是生产环境的话,既要保证配置不出问题,有不能中断服务.如果是这样的话,就需要对配置文件进行语法检测以及平滑 ...