标签 Compile 下的文章

学习虚存-自上而下

如果它不存在,但是你能看见它 — 它是虚拟的(IBM宣传虚拟内存之用语)。虚拟内存技术是计算机发展史上的一项重要的技术,它帮助应用程序摆脱了“体积”的限制。

记得上大学时,有一本书好像叫做“计算机网络 – 自顶向下”,全名记不太清了。书中从人们接触最多也最熟悉的“应用层”开始讲,一直讲到“物理层”,看完这本书后感觉效果不错。所以按照这种方法我也尝试着自上而下的去学习“虚存”,从我们最熟悉的C库接口调用说起,一直谈到底层的硬件支持设施。

1、初学者的疑惑
初学者往往都会写出以下这样的例子程序来学习malloc和free的使用。
int main() {
        int *p = malloc(10000);
        printf("p's address is 0x%p\n", p);
        free(p);
        return 0;
}
但往往结果让这些初学者们感到疑惑。比如上述的例子,在SUN SPARC 64编译后其输出如下:
p's address is 0x100100dc0
看到这样的结果,初学者往往心里嘀咕,“这台机器物理内存才4G,其地址空间总共才4294967296(dec),而0x100100dc0转换十进制为4296019392(dec),这个地址明显已经超出了我的物理内存的限制,这是怎么回事呢?”。其实这里的解释很简单:因为我们看到的都是“虚拟内存地址”。

2、“堆”为何物
malloc是个极其常见的内存分配接口函数,它主要负责运行时在“堆”上为程序动态分配内存空间。我们总是在口头上谈论着“堆”,那么“堆”到底为何物呢?我们已经知道了有“虚拟地址”这个东西的存在,想必“堆”和“虚拟地址”有着千丝万缕的联系^_^。我们来翻看一些经典书籍中的描述。在CSAPP[注1]中的描述是这样的:“堆是进程地址空间中的一段“虚拟地址”空间。在大多数的Unix系统中,堆是映射“二进制零区域(demand-zero)”实现的。其位置在bss段后,其增长方向为高地址方向”。

3、内存映射
前面谈到“demand-zero”这个新名词,那么什么叫“映射到demand-zero”呢?这里蕴含着一个极其重要的概念“内存映射”。内存映射好似一道桥梁,将放在物理磁盘上的对象和一段进程“虚拟地址”空间连接起来。磁盘上的对象,主要指的就是文件,在多数Unix的实现中支持两种文件的内存映射,分别为Regular File和匿名文件(如demand-zero)。映射的过程大致为将文件分成若干“虚拟内存基本单元(页)”大小存于“交换区”,直到CPU指令第一次访问到某个单元时,这个单元才真正被加载到物理内存中。

4、虚拟内存,何方神圣
看到这是不是有些“云里雾里”的感觉亚^_^。其实对于用户进程来说,它是看不到CPU和OS是如何相互配合完成内存管理的。它只认为它面前的是一个这样的情景:“一个完全被我拥有的CPU、一个从拥有M地址空间的物理内存(M = 2的n次方,n为地址总线宽度)…”。这里的用户进程眼中的“物理内存”实际就是“虚拟内存”。虚拟意味着假象,我们知道一个用户进程运行时可能仅仅占用的物理内存的一小部分。看来用户进程被欺骗了。而这个骗局是由操作系统和CPU共同布置的。为了让这个骗局一直维持下去,CPU和OS还是做了很多工作的,究竟有哪些工作呢?我们一一来看看。

1) 交换区(swap)
为了支持虚拟内存,操作系统在物理内存、磁盘之间交换数据的基本单元为“页”。页的大小是固定的,其因操作系统而异。这样一个用户进程在被加载之前首先要被分成若干个“页”,这些页存储在磁盘上。那么是不是进程启动后所有的页都被加载到物理内存中呢?答案是NO。在当前的Unix操作系统中,都有一个叫“交换区”的地方,“交换区”在磁盘上,它存储的是“已分配的虚拟内存页”。又有些糊涂是吧,什么叫已分配的页呢?一个进程虚拟内存页的加载流程大致是这样的:一旦用户进程一虚拟页需要被加载,则操作系统会在“交换区”中为该页分配一个页,一旦CPU访问的虚拟地址落入该页地址空间,则该页才被换入到物理内存中。在这个过程中虚拟页有多个状态,分别如下:
未分配的 - 进程虚拟页未得到加载指令,仍安静的待在磁盘上;
未缓存的 - OS为该进程虚拟页在交换区分配了一个空间,但是该虚拟页还未被引用;
已缓存的 - 该虚拟页被引用,被载入到物理内存中。

2) 换入换出
物理内存容量有限,当物理内存无空间存储新的内存页的时候,就需要将某些内存页从物理内存中移出以为新页腾出空间。这个过程对于那些被移出的页来说,就叫“换出”;相反对于那些新加入到物理内存中的页来说就叫做“换入”。

5、从缓存角度看虚存
现代计算机的存储体系是呈金字塔状的。越接近顶层,速度越快,容量越小,价格越贵;越接近底层,速度越慢,容量越大,价格越低。这样就形成了一个逐级缓存的机制。第K层设备永远是第K+1层设备的缓存。按照这种说法,在早期计算机中,主存是磁盘的缓存,CPU内的高级Cache是主存的缓存。现代计算机基本都支持虚拟内存机制,而虚存页是存储在磁盘上的,虚存页在主存中换入换出。按照缓存的概念,虚存属于容量大,速度慢的第K+1层,而处于第K层的主存就可以看作是虚拟内存的缓存。那么一切缓存理论就都可以应用在虚存和物理内存之间了,比如换入换出算法等。

6、硬件支持
在支持虚拟内存机制的计算机中,CPU都是以虚拟地址形式生成指令地址或者数据地址的,而这个虚拟地址对于物理内存来说是不可见的,那么是谁来屏蔽这个差异的呢?答案是MMU(Memory Management Unit)。MMU负责将CPU发出的虚拟地址转换成相应的物理内存地址。MMU不是孤立工作的,OS为其提供了很好的支持,OS在物理内存中为MMU维护着一张全局的页表,来帮助MMU找到正确地物理内存地址。

7、小结
这里简短而概要的对虚存进行了说明,虚存机制很复杂,不是一句两句能说清楚的,还需要慢慢探索^_^

[注1]
CS.APP – 《computer systems a programmer's perspective》 中文名:《深入理解计算机系统》。

走马观花ANSI C标准-环境

标准都是条条框框的,以严谨著称,语言晦涩难懂。这也是大多数人不愿意“接近”它的原因。但它吸引我的最重要原因恰恰是“标准”二字,我觉得我能从这个标准中找到一些“闪光点”,而这些“闪光点”又恰恰是能让我有所提高的地方。

1、翻译环境
C Source文件是以文本形式存在的,将之转变成可执行程序的过程,我们管之叫“翻译”。C语言的翻译不是一蹴而就的,一般需要两遍才能达到目的,第一遍称为“预处理”,预处理的基本单位是“预处理翻译单元”。第二遍叫“编译”,编译的基本单位是“翻译单元”

a)何为“预处理翻译单元”?
其实很多人之前对这个的理解都很模糊,标准中是如是说的“一个预处理翻译单元是由一个Source file和该Source file通过#include指示符包含的Header files和Source files组成的”,不妨多看几次这个说明,标准就是标准,理解起来还真有些费事^_^。注:#include "xx.c"是没有错误的,只是不常用。

b)“预处理翻译单元”和“翻译单元”之间的关系
预处理后的“预处理翻译单元”叫“翻译单元”。

c)“翻译单元”之间的关系
首先翻译单元相互独立编译;
其次一个完整的可执行程序是由众多翻译单元链接而成的;
最后翻译单元之间是“external linkage(外部链接)”的关系。

2、执行环境
经过复杂的翻译后,现在我们拥有了可以执行的程序了。我们要关心的就是我们的“执行环境”了。执行环境分为两类:“独立的”和“宿主的”。都很好理解。前者在实现时没占一点操作系统的便宜。后者则恰恰相反,利用了很多操作系统的特性和现成的东东。

我们所使用的执行环境多为后者。启动函数(Startup function)一般为main,main有两种原型形式:
int main(void);
int main(int argc, char *argv[]);
当然对第二个main的参数有些限制。如argc >= 0等。

3、其他考虑
a)字符集
源文件字符集:用于书写源文件的字符集。
执行字符集:在执行环境中被解释的字符集。
基础字符集:包括26个小写拉丁字母、26个大写拉丁字母、数字0~9等,这些字符的值都能用8位bit(即一个字节)编码表示。我的理解如ASCII字符集
扩展字符集:一些非基础字符集的区域相关(locale-specific)的字符。我的理解如Unicode字符集
null字符:一个所有位都为0的byte被称为null字符,用于标识字符串结尾。

4、环境限制
没有绝对的自由,无论是“翻译环境”还是“执行环境”都有一定的限制。
这里不想列举那么多的数字。
那我们该怎么得到这些限制呢?这里有几个头文件:
:int相关长度限制
:浮点数相关限制
:额外的一些关于int的限制。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 AI原生开发工作流实战 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com
这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats