csapp-chapter1
本书精髓:旨在帮助读者了解,当在系统上执行以下hello程序时,系统发生了什么,以及为什么。
1.1一切都是二进制
在计算机中,要表示宇宙之一切信息,其本质上都是用二进制位,如10101011;
举个例子,假如我们编写了一个用来打印hello world的C语言源程序,然后将这个源程序保存到hello.c文件中。
这样,该文件就被保存到了计算机的硬盘上,其中的的每个字符,在底层都是用二进制来表示和存储的。
下面,就让我们一起来深入了解下,文件中的内容在计算机中是如何被表示或存储的。
/root/hello.c
查看hello.c源码文件二进制表示
在Linux系统下,可以直接使用xxd工具来查看某文件的内容,默认打开后是16进制的。
通过以上输出,我们清楚地看到,我们编写的源代码源代码文件本质上也是以一系列的二进制序列被存储起来的。
比如第一个字符#,它对应的ASCII码值的十进制是35,转换为16进制就是23,其对应的二进制是00100011,即表示第一个字符#
同样地,第二个字节是69,我们算得它对应的十进制是(6 * 16 + 9 )105,查看ASCII码表,105表示的是字符i,以此类推。
下面我们自己手动来实现另外一个工具类程序,该程序的功能是读取/root/hello.c源文件的内容,
并按不同的格式输出,如二进制,十六进制,ASCII十进制等等
运行该程序
得到如下结果:
我们清晰地看到,我们手动写程序打印的hello.c这个文件的二进制表示,跟上述直接用xxd工具生成的二进制序列是一模一样的。
换句话说,假如换一个其他文件,比如:hello.txt,或者一个图片文件hello.png,或者一个视频文件hello.mp4等等,本质均如此。
通过以上的分析和学习,我们建立了一个初步的印象:在宇宙万物中,一切事物都可以用阴和阳来表示;
同样地,在计算机中,一切信息都可以用0 和 1来表示,其本质均是二进制位(bit)。
1.2
在上小节中,我们通过工具和自定义程序简单的分析了一个源代码文件中的信息(即每一行源代码)在计算机中是如何被表示和存储的。
仅知道了这个,一切还远未开始。试想,我们辛苦编写一个源程序,其目的肯定并不是让它永久的呆在硬盘上,我们编写它的目的是为了
让源程序最终能运行起来,然后让程序我们人类做事情。
当然现在提运行
二字为时还过早,本小节我们先来概括性的了解下,在让计算机运行
我们的程序指令前,如何先让计算机认识
我们编写的指令。
我们的hello.c程序编写完毕了,通过学习我们也知道了这个程序在计算机上其实是以二进制的形式被表示和保存的。
但是现在如何让计算机执行这个程序,貌似计算机根本就不认识我们写的什么呀。比如第一行中的#include
,我们人类知道,这是我们发明的
C语言中包含头文件的一行程序,但计算机才不关心这是C语言还是D语言呢,也不认识#include是干嘛的呢。计算机只能认识0,1位,用我们宇宙语
来表示,也可以理解为阴阳,宇宙的一切都是由阴和阳构成。既然计算机只认识0和1,那我们就得想办法让计算机最终运行
由0和1组成的另一种特殊
的可运行文件,也叫可执行文件,我们暂且这么理解。但这个文件目前并不存在呀,也不会凭空产生,怎么办?于是,人类编写完了源程序的同时就
得想办法把自己编写的源代码转换为(或者叫翻译为)计算机能理解和执行的代码,人类给这一过程起了个名字,就暂且统称为“编译”吧。
并试图想办法发明一种能实现这一转换的工具,我们就暂且可称该工具为编译程序或编译器吧。
随你怎么叫,叫翻译器,转换器等等,都可以奥。比如在类unix系统下,我们常使用的gcc工具,就是人类发明的一个伟大大编译器工具,
用它可以把我们刚才编写的hello.c源程序转换为计算机可以“认识”并执行的一种特殊的可执行程序。让我们先来迫不及待的尝试一把吧,^_^
看到在当前目录下,已经生产了一个可执行的文件hello,我们先了运行一下,让它帮我们打印出hello world
嗯,这一切看起来很简单呀,用一个-o参数,一步就搞定了。但真相真是如此吗?其实看似简单的背后,其实包含了好几个连续的步骤。人类将这几个步骤简单的划分为了4步,并对每个步骤进行了命名,它们分别是:预处理(Preprocess)、编译(compile)、汇编(assemble)、链接(link)。
首先让我们来简单了解下编译器gcc的使用帮助文档:
Preprocess
-E Preprocess only; do not compile, assemble or link
gcc -E hello.c -o hello.i
compile
gcc -S hello.c -o hello.s
assemble
-c Compile and assemble, but do not link
link
通过汇编阶段我们生成了n个源文件对应的n个.o目标文件,接下来让想让这n个程序真正组合起来,然后通过统一的入口main()函数执行。
那么我们就需要将这n个.o文件链接起来
通过最后的link步骤,我们就得到了计算机可执行文件hello
让我们来运行可执行文件hello看一下输出结果:
1.3 掌握编译器的工作原理、特性的意义何在?
上面我们已经分步骤的完整透析学习了编译器的伟大之处,它可以将我们人类写的源代码 最终 转换为计算机可以执行的可执行文件,
从而完成了从初心到梦想的跨越。这样看来,假如我们能更深入的了解编译器的工作过程、原理以及已经编译过程中编译器的优化行为,
这样岂不能更好的帮助我们写出规范的、高效的源程序?何乐而不为?恩,的确如此,假如掌握了编译器的原理和特性,我们的确从以下
多方面入手,写出更好的代码来。
尽力优化我们的程序指令,使其更加符合编译器的特性,从而是程序的运行效率达到相对最优。
帮助我们快速定位在编译过程中出现的各种莫名其妙问题的原因,进而快速解决它们。
帮助我们更深入的了解如何可以让我们的指令和数据变的更加安全。
1.4 计算机硬件的组成
计算机是人类在20世纪最伟大的发明之一。试着了解下它的任何一个零部件的工作原理(如处理器、内外部存储器、显示器等等),都会让你觉得不可思议,实在是伟大的优点过头了。
1.4.1总线
这里主要明白32和64位的来源:
我们首先来学习下面的一段描述:
贯穿整个系统的是一组电子管道,称做总线,它携带信息字节并负责在各个部件间传递。
通常总线被设计成传送定长的字节块,也就是字(word)。字中的字节数(即字长)是一个基本的系统参数,在各个系统中的情况都不尽相同。
现在的大多数机器字长有的是4个字节(32位),有的是8个字节(64位)。为了讨论的方便,假设字长为4个字节,并且总线每次只传送1个字。
版权声明: 本文为 InfoQ 作者【卓丁】的原创文章。
原文链接:【http://xie.infoq.cn/article/d3f8f9676ba26e2e0df0b7dcc】。文章转载请联系作者。
评论