标签 Linux 下的文章

差异学习

看了dreamhead的那篇“差异程序员”,又恰逢在今天dreamhead在一封邮件中谈到其继续深入“向下学习”的想法,心里突然有了本篇题目这样的一个话题。

“差异程序员”(http://dreamhead.blogbus.com/logs/2005/12/1676755.html)

最近自己也在“向下走”,这和dreamhead的想法和做法不谋而合。dreamhead在其blog和邮件中都谈了其在“向下走”过程中的体会,“差异程序员”一篇dreamhead也是在告诉大家其不满足于只做应用一层的程序员,这也同样是我的一些想法。

想法有了,那我们是如何做的呢?mail中dreamhead谈到了他下一步的学习目标和计划,在我的回复mail中我也谈了我将在linux内核方面和编译技术方面作些努力的初步想法。dreamhead也马上谈了他关于这两方面的看法,这里引用少许,目的在对比:
“编译器的前端相对来说都是简单的,技术基本都是现成的,而且有现成的工具供人使用,当然,为了学习,还是自己尝试写一遍比较好。后端的东西才是真正复杂的,不同的平台不同的OS都会有不同的要求,之后,还有优化的技术,复杂度直线上升。… … 如果想了解OS运作,Linux内核显然过于庞大了,那本《自己动手写操作系统》则更为简单。一些基础的东西是类似的,只要理解了那个小内核,理解Linux只是更好的去体会更好的设计而已,因为所有的知识已经都各就其位了,一旦架构形成,剩下的只是不断完善了。”

看到这样的回复,我马上意识到:虽然都是向下走,但是我和dreamhead的目的不同,形成的想法就有了些差异。

我之所以想啃下编译,原因起初有二:一是在前不久的工作中想采用编译中的技术来完成项目中一个模块的功能;二是填补大学时没有学懂编译的遗憾,就是想弄懂那些三元式、四元式,让自己更专业一些。上星期看了“dragon book[注1]”作者的一席话又为我学习编译增加了一枚筹码,其大致意思就是:大多数学习编译的人一生都可能没有机会去创造一门语言,那么我们为什么还学习编译呢?其中一个原因就是编译技术中某些原理可以适用于一般应用软件的设计。比如词法分析器中的字符串匹配技术可用于文本编辑器、信息获取系统或者模式识别程序中。

对于学习linux内核我有以下几点考虑:首先一直在用户层使用内核提供的系统调用,比如fork,在很多Unix编程书中会讲到调用fork后子进程与父进程的异同,这些几乎就是应用程序员必须牢记的东西,一直很讨厌强记,遂想刨根问底的去看看fork的一些实现,这样弄清了其来龙去脉就再无须强记了,而弄清了这些后反过来又会让你更好的使用这些系统调用;其次,想在用户层程序中借鉴内核的优秀设计思想,比如缓冲技术,在内核中有在应用层也有,应用层完全可以参考内核中的某些优秀设计来实现;最后,了解操作系统内核会让你对计算机的体系的理解有一个质的飞跃。即使是计算机本科毕业的人又有多少敢说自己完全理解了计算机呢。

dreamhead以了解OS运作为目的向我们推荐《自己动手写操作系统》一书自然没错,带着这样的目的来学习这本书效果肯定也不错;但是对于我上面所说的那些目的,这本书也仅能满足一小部分,该书可以作为整个学习过程中的一个参考资料。这就是由于学习目的不同带来的一些差异性的东西。

差异学习没有对错之分,也没有好坏之分,只是因目的不同而已罢了。目的不同,学习的关注点和着重点就会不同,这样即使学习同一样的技术效果也不同。另外学习不是孤立的,沿着学习的主线方向会有很多旁支,如学习linux内核,你将会了解到CPU体系结构、存储器管理和算法理论等多方面的知识。

最近看到某一电视台播放的央视版“笑傲江湖”,情节中提到华山剑宗与气宗之争,当时自己就考虑“为何两派不二者兼修以达到前所未有之境界呢”,在后来的令狐冲无心插柳却达到了这一境界,想必那些在两宗相争中冤死的华山前辈们看到这一结局都后悔之极了吧。(开个玩笑,其实从古自今大凡争斗之事多源自“权势之争”)。令狐冲成为了绝顶高手又让我想到了“差异程序员”,要想成为“程序界”的令狐冲又何尝不需要“上下”兼修呢?起码dreamhead已经为我们作出了表率。

[注1]
“dragon book” — 《Compilers: Principles, Techniques and Tools》by Aho, Sethi and Ullman

学习虚存-自上而下

如果它不存在,但是你能看见它 — 它是虚拟的(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》 中文名:《深入理解计算机系统》。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 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