C Runtime & Windows

Written on July 11, 2015 View on GitHub

调整了一下博客行距和颜色,原来增加行高也可以让眼睛更舒适。

来微软近两个月,博客也停了两个月。不是因为在微软忙,而是我业余时间都去研究一些内部的东西了,是否涉密我拿捏不准,就干脆没写。能写的都是我确认过没问题的。

CRT (C Runtime)

Runtime 是个很底层的概念,很多程序员甚至都没意识到它的存在。 程序编译之后是机器码,我们写得所有 C、C++ 代码都会转换成机器码。加减乘数循环都会对应的变为几个汇编指令,操作内存和寄存器之类。这些机器码甚至是可以跟操作系统无关的,只跟 CPU 指令集有关。 但 printf,malloc,strcmp 这样的函数呢,我们知道,其实是链接到预先编译好的 lib 库中。

好,我们从 lib 库的角度出发,我们要实现 malloc 为例,一个 4G 的虚拟内存摆在那,我怎么知道从哪到哪可以分配呢?虚拟内存是被操作系统管理的,分配的之后,操作系统怎么才能知道我分配了那块内存呢?我怎么感觉我在干一些原本应该操作系统负责的任务? 实际的解决方案,是 malloc 是调用操作系统的内部函数来分配内存。 printf 也是一样,有一些逻辑判断,然后调用操作系统的接口实现输出。 strcmp 这种函数就不一样了,完全跟操作系统无关,只是预先写好而已。

所以,C 语言的这些函数都是预先写好的,有的是操作系统包装,有的是功能实现。 这就是一个 Runtime 的概念,所有语言都有一个 Runtime 的概念。只不过现在操作系统大多是 C、C++ 编写的,因此 C Runtime 显得过于底层而被忽视了。

在 Windows 里 C Runtime 包含了 C、C++ 的基础实现,接下来以 Windows 为例介绍 CRT 的相关情况。

静态编译 CRT vs 动态编译 CRT

在 Windows,静态编译 CRT 时,那些 LIB 里的机器码会编译到 EXE 内部,动态编译则使用 DLL 的方式调用。 Windows 处理 DLL 是通过名字来识别,然后根据一定规则到多个目录查找。这就是为什么 MSVCRXXX.DLL 找不到时,只要复制一个到程序目录或者 System32 目录就可以。

CRT 几乎全是 C、C++ 实现

也许你在想,这些 C、C++ 程序肯定不依赖 CRT 里的函数。。。因为他就是 CRT 呀。 但仔细想想,似乎只要不递归就可以。。。 实际是内部实现全部是使用内部函数,完全不依赖 CRT 函数,然后映射一部分出来,作为 CRT 函数。 个别函数是汇编编写,有的是 C 无法编写,有的应该是性能考虑。

Windows 自己也依赖 CRT

先有鸡还是先有蛋?如果你还在纠结这个问题,就可以直接点右上角的 × 了。 CRT 依赖一些内核功能,是通过 Load DLL 的方式调用,只要最终不会形成递归即可。 而实际上操作系统都是先编译各种功能性 LIB,再链接到一起,CRT 的 LIB 只是茫茫 LIB 中的一个。

Windows 里有多个 CRT

从操作系统的角度,CRT 的 DLL 跟其他 DLL 并没有什么不同。实际上我们现在的操作系统里面是由很多 CRT 的,同一个版本的 CRT 也会复制到很多程序里。

Visual Studio 在使用自己的 CRT 专门给应用程序使用

是的,你的程序跟 notepad.exe 使用的不是一个 CRT。 每个版本的 VS 在 CRT 上都有变化,有的是支持语言新特性,有的是功能性改进(如对 C++ 虚函数做一些安全性防护)。

微软一直在优化 CRT 的使用方式

无论是从他自己开发维护的角度,还是从操作系统节约资源的角度,目前的 CRT 使用方式都有不妥。 微软从 Windows 7 开始变逐步改变 CRT 的使用方式,8.1 开始实装。 Windows 10 里,原来的 CRT 都被称为 Legacy CRT,而现在使用 UCRT + VSRUNTIME 的方式。

CRTs

以 C 为例,C++ 以及特殊场景下的 C 的 CRT 都在不同的 DLL 里。

MSVCRT.DLL

这大概是最被人熟知的 CRT 了。Legacy 中的 Legacy。 操作系统以及组件会直接使用 MSVCRT.DLL 作为 CRT。 VC6 什么的编译出来的程序就是使用 MSVCRT.DLL 作为 CRT。

MSVCR120.DLL

这大概是 Legacy CRT 的最后一个版本,120 代表 VS12,也就是 VS 2013。 无论静态编译还是动态编译,只要是 VS 2013 的默认 profile 编译的,都是使用 MSVCR120 作为 CRT。

UCRTBASE.DLL & VCRUNTIME140.DLL

UCRTBASE 将包含 CRT 中确定是稳定不变的内容,长期保持向前和向后兼容。 VCRUNTIME140 将包含 VS 里的一些编译相关的特性功能,比如我上面提到的语言新特性,或者功能性改进。 此外还可以说的是。。。 UCRT 以后由 OSG(Windows 组)维护。 VCRUNTIME 将由 DevDiv(Visual Studio 组)维护。 只能说这么多了。。。