一个小程序背后的故事——分段

原创 2012年03月22日 16:45:34

 现在大家都在谈设计,谈架构,谈框架,套用一句流行语:“ 虽然听不懂他们在说什么,但是感觉很牛的样子。 

   

    诚然。在软件日益庞大的今天,先古时代单打独斗的天才程序员几乎销声匿迹了,程序员的成功大多是团队的成功,团队合作无非也是分层,分模块,架构设计完了就是用各种框架,高内聚,低耦合云云。很少有人听到有人在讨论编译,连接,算法,数据结构之类的东西。程序员越来越像软件集成员了。


我还是喜欢自下而上的学习方法。前天看了本书《程序员的自我修养——编译,连接和库》。又明白了一些底层的东西。准确讲应该是操作系统和高级语言(应用程序)之间那层。这本书很好。(只有在买书的时候,我才能深刻的体会到知识就是金钱。65块钱啊。才450多页。)大一的时候借了,“ 虽然看不懂作者在讲什么,但是感觉很牛的样子,大二的时候也借了,依旧是“ 虽然看不懂作者在讲什么,但是感觉很牛的样子,前几天在图书馆溜达。又一次和它邂逅。遂收入囊中,借来看看。虽然不至于看懂,但令我欣慰的是终于知道作者是讲什么的了。


初中时候觉得,写软件的人好厉害啊。高中的时候觉得,写操作系统的人好厉害啊。大一的时候觉得,写编译器的人真牛啊。大二的时候想,发明语言的才是真正的大牛。现在想想,只能对以前的想法呵呵一笑。

John Resig 22岁写了jquery 。唉各种你懂的。

先说一点编译的东西吧。


编译过程:

1.预编译prepressing展开宏#define,加载库文件#include等。

2.编译compilation 高级语言编译成汇编语言,这个过程会有编译器优化。

3.汇编assembly汇编语言编译成二进制代码。这个过程是最简单的。

4.连接linking地址和空间分配,符号决议,重定位。

个人觉得编译和连接过程是最复杂的。


编译:

    因为现代CPU已经很繁杂,各种功能很多。暂且不说CISC 和 RISC X86X86_64IA64MIPSARM等等架构的CPU,还有乱序发射,流水线优化,多核优化,寄存器优化,cacha优化等等一堆乱七八糟的东西。可谓眼花缭乱。记得GCC支持50+CPU平台的编译。恐怖的一米。乱序发射,编译器,cache优化等各种CPU的特性,也增加了程序并发多线程编写的难度。


连接:

    动态连接,静态连接,可重入,地址重定向,个人感觉也是比较难的东西。

再穿插点X86和汇编的东西。8086处理器有20跟地址线,但是只有16跟数据线。所以可以定位1M的内存空间,但是只能访问64K的地址,为了解决这个问题,Intel 的工程师很聪明的用了一个方法,段+偏移量 这种内存访问方式。写汇编的时候,一般是这样开头的。

Code segmentdata segmentstack segment 三个段。我以前一直以为没有操作系统的时候,这三个段就依次导入到8086的三个段里面。真无知啊。

    冯诺依曼果然是大神。没有计算机的时候就设计了计算机的灵魂。数据和指令分离。试想一下在混沌初开的时候,如果数据和指令是混杂在一起的话,那将是多么恐怖的事情。不过只要是一个正常的人,写汇编的时候就会很自然的把指令和数据分离。


假如不分离的话,一个指令执行完了,后面一个是数据,要怎么区分?CPU会不会把数据当做指令去执行?


解决方法之一是内存里面加标志位,0是可执行指令,1是不可执行的数据。无疑这会造成内存浪费。


另一种方法是按照 指令-数据-指令-数据-指令-数据 交叉存放(当然,也可以1指令n数据,或者n指令1数据)方式存放,CPU根据周期来判断内存内容是数据还是指令,这会出现很严重的问题,1.程序指令和数据的比例不是固定的。2.又会增加CPU和程序在二进制上的兼容难度。(关于二进制兼容,可以百度一下。)


上面一个问题就很麻烦了,姑且放在一边不考虑,如果程序员无意写错了指令,跳转到了一个存放数据的内存或者不小心修改了指令(现在的指令都是只读的,这也就是为什么每次调试的时候要编译整个程序)怎么办,那从那以后所有的程序都是坏死的(起码是不可预料的),现在我们写程序,错一句就那么一句错了,不会影响到剩下的,如果一句错了,剩下的都跟着乱了,那将是多么恐怖的事情。

所以,一个正常的人会把指令和数据分离的。冯大神果然是大神。


指令和数据混杂的坏处可谓多多,指令和数据分离有什么好处呢?可也是谓多多。


1.现代CPUcache都是指令cache和数据cache分离的,想象一下,数据和指令分离了,指令cache和数据cache也分离了,那么指令和数据的cache命中率一下子提高了50%,一个小时的程序半个小时就能执行完。这还没算上面两个问题的内存节约以及兼容性问题。

2.指令是只读的,避免了程序员无意识地对指令区域内存的修改。避免产生了很多错误。

3.指令和数据分离,所以指令可以重复利用。也就是后面将要讨论的静态连接以及动态链接的东西。


    综上所述,计算机要指令和数据分离。我估计冯大神的论文也就这么写的内容吧。改天到网上找来膜拜一下。


    冯骨开天之后,程序猿们看到了光明。沐浴在神的思想之中,程序猿们感到了快乐。可是有些程序猿不满足于神既有优化,于是有猿想:“数据中也有只读的和可修改的,我是不是可以把数据也分下段?这样,我写程序时如果手误修改了只读的内容,那么程序会报告错误。这该是多么美妙的事情啊。”于是就有了那个经典的程序:


#include<stdio.h>

int main(int argc ,char* argv[]){

char *str = "hello";

*str = 'a';

printf("$s",str);

return 0;

}


好多懂一点的C语言新手可能会以为输出结果是 "aello" ,但是实际结果是:


Linux: core dump 吐核,我一直觉得吐核这个翻译是个很Cute的翻译。其实也是段错误,陷入内核。终止程序,把控制权交给内核。应该是个CPU异常中断或者错误中断。

Windows 下面好像是段错误。既对只读段进行赋值操作的段错误。

所以,正确的写法是:

#include<stdio.h>

int main(int argc ,char* argv[]){

char *str []= "hello";

*str = 'a';

printf("$s",str);

return 0;

}

应该用字符串数组。字符串编译完了以后是放在数据段的只读区域的。而字符串数组是放在程序上下文中。既代码段。(有同学可能会问,这不是数据么。怎么去了代码段?额。这个我也很有点迷茫。)但是执行到printf("$s",str);的时候,是进行了n次压栈操作,一方面保存当前寄存器(程序上下文)以及其他要保存的信息,另一方面把参数(str指针)压栈,传递给printf函数。这个过程有个准业术语,叫调用惯例(参数传递方式-栈、寄存器、内存 堆维护方式,参数顺序,名字修饰符策略等)也是一套标准,详细情况请自行百度。好。在这儿打住。如果这样写:


#include<stdio.h>

char *str []= "hello";

*str = 'a';

int main(int argc ,char* argv[]){

printf("$s",str);

return 0;

}


"hello" 和 str 是放在数据段里了。


    刚才说到分段,无到有是冯大神指出的光明道路,那么有到多则是无数程序猿的智慧总结。


    自从有程序猿发现可以根据实际需求来再次分段之后,各种分段就如雨后春笋般破土而出。


    比如注释信息段,调试段,编译器信息段,目标平台段,动态连接信息段,符号哈希表段,等等。Linux 下面有查看可执行文件(以及so动态/静态链接库)的工具包:binutilswindows下面也有。自己百度。


    马克思告诉我们,事物总是不断发展的。各种段之后,慢慢的内存大了,有了内存管理的概念,分段的好处是内存可以充分利用。比如1M的内存划分为512个段,内存利用更充分了。再后来慢慢的有了OS的概念,于是又有了堆(heap)段的概念。又有了分页方式的内存管理,每次分配给程序的内存颗粒更小了,所以内存利用率得到了极大的提高。既:生产力气得到了极大的发展。

相关文章推荐

微信小程序-文字超出限制如何在末尾加省略号

微信小程序-文字超出限制如何在末尾加省略号特意为了这一个功能点来记笔记。当文字超出一行时会自动换行 那如何让文字不自动换行并在末尾加上省略号呢?...
  • YanzYan
  • YanzYan
  • 2017年01月16日 12:47
  • 7930

【干货】微信小程序之自动化技术

使用XTest录制从体验上确实简单便捷,简单到不用插线不用PC,可以躺着录走着录,即使撩妹都不耽误测试,跟平时操作App无异。对比早期录制脚本又抓控件又摸路径受的罪,幸福感大增。录制很容易上手,就是在...

微信小程序显示cms里的html文章

在cms模版中将html文章转化为json数据,过滤掉样式和标签,标签转换为 段落和换行转换为,其他标签和附带样式都过滤掉。这里是用php的正则表达式函数来实现的。 ...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

小程序上拉加载更多数据,分类切换状态等实例

发现很多人对小程序的文章比较好奇,购物车那篇居然占了快三分一的访问量,因此写多篇关于小程序的常用功能。 上拉加载更多其实很简单,关键点只是知道上拉加载是变相的分页加载,然后通过初始化记录值和通过数组...

说说Android桌面(Launcher应用)背后的故事(七)——又是一个附属品(可以转动的绚烂饼图)

博客搬家啦——为了更好地经营博客,本人已经将博客迁移至www.ijavaboy.com。这里已经不再更新,给您带来的不便,深感抱歉!这篇文章的新地址:点击我  本来这一篇应该还是写Lau...

在浏览器中输入一个网址,解析其背后发生的故事

在浏览器中输入一个网址,解析其背后发生的故事很多公司都喜欢问这个问题,当初考研时也会问到这个问题,所以浅层次地总结一下。1. http协议 URL在浏览器中输入一个网址,比如http://www.go...

加密程序PGP背后的故事

本文转载自 Solidot,链接:Click Me paopao 写道 " 我不知道有多少人听说过PGP(Pretty  Good Privacy):这是一个加密程序,被广泛用于对文件和邮件...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一个小程序背后的故事——分段
举报原因:
原因补充:

(最多只允许输入30个字)