Windows Internals – 1.3 概念与工具之进程、线程、虚拟内存

转载请注明出处:https://cyc.wiki/index.php/2017/07/14/windows-internals-1-3/


本文表格图片均出自《Windows Internals, Seventh Edition, Part 1》,侵删。
All tables and figures are copyrighted by Windows Internals, Seventh Edition, Part 1. Please contact me to delete if there’s any violation.


Windows进程

一个Windows进程包括以下内容:
– 一个私有的虚拟地址空间,该进程可以使用的一组虚拟内存地址
– 一个可执行程序,该进程最初的代码及数据,它会被映射到虚拟地址空间中
– 一组打开的句柄,对应于各种系统资源,如信号量、同步对象、文件等
– 一个安全上下文,这是一个访问令牌,标识了用户、安全组、权限等信息
– 一个进程ID,这个进程的唯一标识符
– 至少一个执行的线程

在Windows 10中,查看Windows正在运行的进程一般使用任务管理器。
在“进程”一页可以看到正在运行的应用、后台任务、系统服务等所对应的进程,以及其所包含的子进程。
而“详细”一页对应于Windows 7时代的任务管理器,用最原始的方式显示了进程名、进程号、状态等信息。

值得一提的是,Windows进程没有像Linux系统中进程树的组织形式,对于任何的Windows进程,系统只会记住它的父进程ID,其它的关系并不会保留。
例如进程A中创建了进程B,进程B中创建了进程C。如果先kill掉进程B,然后对进程A进行“结束进程树”操作,进程C将不受影响。所以Windows中描述的“进程树”含义并不准确,更准确的描述应该是进程的“父子对”。

线程

一个线程包括以下内容:
– 一组CPU寄存器的内容,表示了当前的处理器状态
– 两个栈,分别用于内核态和用户态
– 一个私有存储空间(thread-local storage,TLS),供子系统、运行时库、动态链接库等使用
– 一个线程ID,线程ID也不会与进程ID冲突

前三项也被称为线程的上下文(Context),对于不同的硬件架构会有不同的实现。

通常来说,线程调度是在内核态完成,频繁切换线程是一个很费资源的操作。为此Windows还提供了两种用户态的线程调度机制:纤程(Fiber)和用户态调度(User-mode scheduling, UMS)线程。这两种技术都不常用,所以就不详细介绍了。

进程与线程的组织结构如下图:

上面的VAD是虚拟地址描述符(virtual address descriptors),是用于记录这个进程用到的虚拟地址段。
中间的Handle Table就是句柄列表,指向了各种内核对象,如信号量、同步对象、文件等。

虚拟内存

Windows基于线性的地址空间实现了虚拟内存机制,使得每个进程仿佛能拥有自己私有的大的地址空间。在运行的时候,内存管理器会将每一页(通常大小为4KB)的虚拟内存空间对应到物理内存或磁盘文件上,并在缺页时把文件中的页调回物理内存中。

对于32位系统,总共的虚拟地址空间理论上是4GB(因为sizeof(void*) == 4,2^32 = 4GB),高地址段的2GB会被留给系统本身使用,低地址段的2GB才是留着应用使用。所以在32位系统中,应用进程所能拥有的最大内存空间就是2GB。

对于64位系统,理论的虚拟地址空间达到了16EB(因为sizeof(void*) == 8,2^64 = 16EB),在Windows 8.1+系统中,高地址段的128TB会保留给系统使用,低地址段的128TB会给应用使用,中间还有一大段是未映射的地址段。当然,要用上这些地址段至少你得有256TB的内存,所以目前来说就不用考虑了。


这一章剩下的内容比较零散,我自己能写的心得也不多,所以就不往下写了。
下一篇直接进入正题写第二章。

Windows Internals – 1.2 概念与工具之Windows API

转载请注明出处:https://cyc.wiki/index.php/2017/07/11/windows-internals-1-2/


本文表格图片均出自《Windows Internals, Seventh Edition, Part 1》,侵删。
All tables and figures are copyrighted by Windows Internals, Seventh Edition, Part 1. Please contact me to delete if there’s any violation.


Windows API

Windows API是Windows操作系统的用户态系统编程接口。在64位系统诞生以前,32位Windows操作系统的API被称作Win32 API,以区别之前的16位Windows API。现在有了64位的API之后,还沿用了Win32 API的叫法,32位和64位的Windows API都习惯被称为Win32 API

Win32 API是一组C语言的函数,数量多达上千条。Windows API Index列出了Windows API的所有类别,而Windows 8.1 API Sets则列出了Windows 8.1所支持的所有Windows API,以及它所依赖的链接库文件名。这些列出的Windows API(包括一些暴露出接口但没有向外公布的内部API)就涵盖了开发者在用户态所能实现的所有系统功能,其它任何形式的系统编程接口基本上都是基于这一组最底层最基本的Win32 API进行封装而实现的。

COM

正因为C风格的Win32 API过于庞杂,缺乏合适的组织结构,命名风格也不统一,后来出现的COM API取代了Win32 API成为新一代的系统编程接口。

COM(Component Object Model,组件对象模型)基于以下两个设计准则:
1. 对象Client端(调用者)通过一组定义好的接口协议与Server端(实现的dll库)进行通信,这些接口通过类似虚函数表的机制将接口调用映射到一组逻辑相关的方法实现。
2. 实现是通过dll动态加载,而不是静态链接到Client端。

第一点非常关键,它使得方法的调用成为了一种范式,这种范式可以以任何合适的编程语言实现,从而使得组件被COM的接口封装后,可以用C、C++、VB、C#等任何一种.NET平台支持的语言进行调用,大大地增加了系统组件的扩展性。
同时,在调用的范式中可以预先对要调用的方法进行检查其存在性,使得版本兼容性大大提升。举个例子,有一个库A1,客户端C2调用了库A1的新版本A2的一个新加接口,而运行环境中还是库A1的dll。如果使用C++直接链接的方法调用,客户端C2因为在库A1的dll中找不到新加接口的符号而崩溃退出。但如果使用COM封装后的库,客户端C2在调用不存在的接口时,会优雅的抛出Exception,只要接住进行处理,就能继续运行下去。

The Windows Runtime (WinRT)

Windows 8引入了当时称为Metro App的磁贴应用,后来更名为Windows Store App,在Windows 10中就叫做UWP App。支持这种应用的开发而引入的API就被称作WinRT API。Windows Runtime (WinRT)跟Windows RT要区分开,后者是那个命短的ARM操作系统(Surface 3)。

WinRT是基于COM技术开发的,加入了一些新的扩展信息,最主要的是将接口的定义写在了的一种元数据文件.WINMD里。WINMD文件是二进制文件,可以用VS中的对象查看器打开,能看到里面所定义的WinRT接口。

跟COM一样,WinRT接口也可以在各种语言中进行调用,除了C++、VB、C#这.NET三剑客之外,还支持JavaScript,这使得可以用一些前端的技术写UWP App。注意这里面的C++并不是纯C++,而是一种叫做C++/CX的语言,专为WinRT编程设计。如果你看到一个.cpp文件里面出现的不是*而是^,那这就是一个C++/CX的文件。我在微软里的第一个项目就碰上了C++/CX,当时整得一头雾水。后来发现,其实可以先用C#写一遍,然后对应翻译过去就可以了。

 
在Windows 10的系统中,可以存在两种应用,一种是经典桌面应用,也就是我们一直以来熟知的那种应用;另一种是UWP应用,也就是在Windows 8只能全屏显示的磁贴、到了Windows 10基本跟经典应用区分不开的那种应用。
对于这两种应用,经典桌面应用一般使用Win32 API和COM API,但也可以使用一部分WinRT API。而UWP应用一般使用WinRT API,但也可以使用一部分Win32 API和COM API。

The .NET Framework

.NET Framework应该是继MFC之后,编写带界面的Windows应用最常用的框架。它主要由两部分组成:
1. 公共语言运行时(CLR) 这是.NET Framework的引擎部分,负责将抽象的中间语言翻译成在硬件上运行的指令,实现其实是一个COM组件的Server端,里面的功能由底层的Windows API提供。
2. .NET Framework类库(FCL) 这是.NET Framework API的封装部分,向上提供.NET Framework API,向下调用CLR层提供的COM接口。

.NET Framework和WinRT在上层API技术实现上非常相像,但是底层跟Win32 API的对接不一样,因为.NET Framework是面向经典桌面应用,而WinRT是短期面向UWP应用,长期面向OneCore系统。

Windows Internals – 1.1 概念与工具之Windows版本

转载请注明出处:https://cyc.wiki/index.php/2017/07/09/windows-internals-1-1/


本文表格图片均出自《Windows Internals, Seventh Edition, Part 1》,侵删。
All tables and figures are copyrighted by Windows Internals, Seventh Edition, Part 1. Please contact me to delete if there’s any violation.


Windows 版本号

第一章开头主要讲解了Windows操作系统的版本更迭以及未来的更新计划。

下面的表格列出了Windows操作系统发布的产品名称以及其对应的内部版本号:

Product Name Internal Version Number Release Date
Windows NT 3.1 3.1 Jul 1993
Windows NT 3.5 3.5 Sep 1994
Windows NT 3.51 3.51 May 1995
Windows NT 4.0 4.0 Jul 1996
Windows 2000 5.0 Dec 1999
Windows XP 5.1 Aug 2001
Windows Server 2003 5.2 Mar 2003
Windows Server 2003 R2 5.2 Dec 2005
Windows Vista 6.0 Jan 2007
Windows Server 2008 6.0 (SP1) Mar 2008
Windows 7 6.1 Oct 2009
Windows Server 2008 R2 6.1 Oct 2009
Windows 8 6.2 Oct 2012
Windows Server 2012 6.2 Oct 2012
Windows 8.1 6.3 Oct 2013
Windows Server 2012 R2 6.3 Oct 2013
Windows 10 10.0.10240 Jul 2015
Windows 10 version 1511 10.0.10586 Nov 2015
Windows 10 version 1607 10.0.14393 Jul 2016
Windows Server 2016 10.0.14393 Oct 2016

发现规律没?
在Windows 7以前,Windows的产品名称和内部版本号是对应的,NT时代的系统命名很直接,就是Windows NT x.y(其中4.0就是大名鼎鼎的Windows 95);到了Windows 2000,XP,Vista,也基本上是按照架构的更迭去更新大版本号或小版本号。
但是Windows 7为什么还叫做6.1,后面的也都不再更新大版本号,直到Windows 10发布呢?作者告诉我们,原来是因为Windows XP太火了,有大量的软件为Windows XP平台设计而开发,其中检测操作系统版本的条件很多都是大版本号>=5且小版本号>=1,于是Vista的版本号升至6.0时,有大量的XP系统上开发的软件无法运行,成为了Vista这个背锅王背的其中一个锅。为了避免这类事情再次发生,Windows就再也不敢随便改版本号了,每次就在小版本号上加一点就好了。
这个局面一直持续到了Windows 10发布。至于Windows 10为什么叫10不叫9,有人说是因为判断Windows 9x系统的版本条件是匹配字段以”Windows 9″开头,于是就会跟Windows 9重复,其实看了上面这个表你就明白这就是一个段子而已,真正的程序员都是读NT的版本的。

Windows 10

说说我跟Windows 10的结缘吧。我在2015年夏天进入微软实习,加入操作系统工程院,那时候正好是Windows 10发布和升级的时候。有一天我就发现,电梯口对着的那间会议室被装扮成了这个模样:

经常可以看到一些大佬们在里边开会,还有投影幕布上跳动的数字。亲身处在这个环境中,心情还是有点激动的。出了升级失败的bug之后,也会跟着担心这个bug啥时候能解决。虽然我的实习内容跟这个没什么关系,但是能感受到这样紧张的氛围。

等我正式入职微软以后,确实就在参与Windows 10操作系统的开发工作了。每天来到公司,点下测试机的系统升级的重启按钮,pull下最新代码敲下编译命令,然后出去泡杯咖啡,就这样开始每天的工作。以前觉得重装系统费事,现在每天重装一遍,美滋滋。

书上提到了Windows 10两个重要特点:

Windows as a service

在PC时代,软件迭代的速度比较慢,人们学习和接受新的软件功能的速度也比较慢,传统软件工程的迭代周期会比较长,以前Windows大概两年左右出一个新版本。有点像是攒气憋大招,当然也有空大的状况(又说你了,Vista)。
后来进入互联网时代了,软件都往云上部署,想上什么新功能直接就上线,过两天出bug了马上又能撤回来。Windows这种步伐已经完全赶不上用户的需求,于是从Windows 10开始,Windows将不再开发全新的版本,而是直接在Windows 10上进行迭代更新,目前是每年有两次大的更新。其实就是在向互联网产品看齐,将Windows作为一种服务,通过云端更灵活地进行更新迭代和个人定制(Office 365就是传统软件上云的一个成功例子)。
所以为啥Windows 10刚出来的时候bug这么多,还不是你们逼的(手动滑稽)。另外插句题外话,Windows Update劝大家还是打开吧,很多我们搞的bug都得在Update里面修。。

我自己画了个表列了一下目前Windows 10的版本和内部代号:

产品版本号 内部代号
Windows 10 TH1
Windows 10 Version 1511 TH2
Windows 10 Version 1607: Anniversary Update RS1
Windows 10 Version 1703: Creator Update RS2

OneCore

能够谈这个问题的公司估计目前只有微软一个。Windows基本覆盖了我们日常生活中使用到的大部分电子设备,PC、平板、手机(这个虽然没人用但是可以有)、游戏主机Xbox、混合现实设备HoloLens,还有地铁上、公交上、自动售货机上偶尔能看到的蓝屏。它们都是Windows,但是应用却不能通用。
从Windows 8开始,Windows开始了它的内核一体化进程。Windows 8和Windows Phone 8实现了同一套内核共享。Windows 8.1和Windows Phone 8.1实现了同一套WinRT API共享。到了Windows 10,真正实现了OneCore平台,实现了在PC、手机、Xbox、HoloLens、IoT设备上共享同一个操作系统平台。Windows 10上的Univeral Windows Apps就是这个共享平台的产物,它可以在所有的Windows 10设备上运行。
那OneCore的进程是不是就结束了?当然没有,实现了共享之后下一步就是把非共享的部分清除出去,这样才能真正的实现纯的OneCore Windows。最新发布在Surface Laptop上的Windows 10 S就是这样一个纯OneCore的Windows(这么描述其实还不够准确,系统本身的OneCore化就没有完全),只能运行UWP应用。经典桌面应用中这种粗犷的运行方式具有太多的问题,安全性不好,系统文件容易被损坏,兼容性也存在问题。未来终将是UWP应用的天下,只是改革历程会持续比较长的时间。

Windows Internals – 0 开篇

开篇

一直就想开一个技术博客,但是总是想不到从哪写起。从大二开始,我就接触各种各样的项目,写过大大小小的软件,带界面的、不带界面的,单机的、分布式的,手机上的、服务器上的,Windows的、Linux的,使用了各种各样的技术,踩过各种各样的坑,但是都没有把它们记录下来,这些经历有些能化成简历上的一条项目经历,有些就随着时间消逝了,后来想想多少有点可惜。我可以找各种各样的借口,但是最后都归结为一条——懒,现在的人喜欢叫拖延症。

从接触编程开始,我就一直对大型的软件架构很感兴趣,我一贯不赞同所谓快猛糙的软件开发方法,你可以敏捷,但也要保证质量和可扩展性,不择手段地实现一些丑陋的功能只是杀鸡取卵,这样不能建成软件的大厦。我加入微软写Windows,这也是其中的一条原因吧。不是每个人都有幸能看到这个人类迄今为止最大规模的软件之一的源码,虽然也不是每个人都稀罕。但是起码我自己感觉在这个极其庞大的系统中工作,了解它的运作原理,以及亲身经过它的开发、构建、测试、发布,本身是一件挺有趣的事。

这段时间刚好是stablization阶段,事情不是太多,就从公司图书馆借了一本书,Windows Internals (7th),中文译名叫深入解析Windows操作系统,目前中文译版只出到第六版。在借出成功过去一个星期后,我问图书馆我的书怎么还没有收到,图书馆说你借的书正在从美国寄过来的路上,下个星期就能到了。我顿时感叹,我司的图书馆好良心啊!于是觉得这么借来的书,不仔细看看好像对不起它长途跋涉的旅程。何奈这本书跟Windows本身一样巨厚,估计看过的东西很难记下来,就趁此机会开这个博客,把读书心得记录于此。

关于这本书

Windows Internals, Part 1: System architecture, processes, threads, memory management, and more, 7th Edition
By Pavel Yosifovich, Alex Ionescu, Mark E. Russinovich, David A. Solomon
https://www.microsoftpressstore.com/store/windows-internals-part-1-system-architecture-processes-9780735684188

目前Part 2还没有正式出版。

以下是这本书之前的版本历史以及其对应描述的Windows版本:

年代 版本 名称 Windows版本
1992 1 Inside Windows NT Windows NT 3.1
1998 2 Inside Windows NT, Second Edition Windows NT 4.0
2000 3 Inside Windows 2000, Third Edition Windows 2000
2004 4 Windows Internals, Fourth Edition Windows XP, Windows Server 2003
2009 5 Windows Internals, Fifth Edition Windows Vista, Windows Server 2008
2012 6 Windows Internals, Sixth Edition Windows 7, Windows Server 2008 R2
2017 7 Windows Internals, Seventh Edition Windows 10, Windows Server 2016

从这个列表以及书的版本更替多少也能看出来Windows在哪个版本经历了大的架构改变。

这本书讲的是Windows的运作原理,并不是面向使用者和应用开发者去描述如何去使用Windows或如何在Windows平台上开发应用。我应该是这本书更适合的受众之一,它上面讲到的每一个技术点我甚至都能直接从Windows源码中找到对应。因此我会尽可能地结合我工作的经历和内容,把我的理解写下来。但是受限于我的技术水平和知识储备,会有很多地方理解不到位或者有误,希望读者能包容并提醒我改正。

这本书目前的借期是3个月,我也没有雄心壮志3个月一定读完。但是前两章一定会仔细读完,在这里写下心得,以此为证。