CSAPP之阅读笔记-计算机系统漫游(1)

最近在看CSAPP(深入理解计算机系统第二版),其实最新版是第三版。但是,我看了一下价格100多大洋,于是去老夫子旧书网上买了本第二版的,花了30多块钱。哈哈。

网上看了一些关于此书的书评,都说是本好书,唯一缺点就是比较难懂。没关系,就姑且先啃一啃,实在不行,实在不行,再退而求其次,看那本《编码:隐匿在计算机软硬件背后的语言》吧。

以下是书中笔记部分与自己理解部分,由于是自学,误解或者错误肯定是不可避免的,希望能给予指出。

第一章  计算机系统漫游

计算机系统是由硬件跟软件共同组成的,它们是一起工作来运行应用程序的。如果你深入地去了解这些硬件跟软件的组件,那么你将会成为凤毛麟角的“权威”程序员。在这本书里,你会学到很多实用的技巧来优化代码,避免漏洞,设计shell,甚至web服务器。

先从hello程序的例子来看看当你在系统上执行这个程序时,系统会发生什么。

code/intro/hello.c

#include <stdio.h>

int main(){

  printf("hello, world");

}

1.1 信息就是位+上下文

hello程序其实就是一个文本文件,文件名为hello.c。这个文本是由0和1组成的位序列。8个位被组织成一组,称为字节。了解ASCII码的,就会明白,每一个字符都是根据ASCII表被编码成8位的二进制位。例如,hello.c程序就是以字节序列的方式存储在文件中,每个字节(也就是一个字符)对应于一个整数值。所以,hello.c程序用ASICC文本来表示就是下图的这个样子。

当然,在计算机中,所有的表示ASCII文本的整数都是以二进制方式存储的。像这些只有ASCII字符构成的文件称之为文本文件(可以从键盘上获得的字符组成的文件),而其他所有的文件称为二进制文件。

通过这个hello程序,我们就能明白一个思想:系统中所有的信息——包括磁盘文件(比如视频,图片资料)、存储器中的程序(比如hello.c程序)、存储器中存放的用户数据以及网络上传送的数据,其实都是一串位表示的。而区分不同数据对象的唯一方法就是我们督导这些数据对象时的上下文。

1.2 程序被其他程序翻译成不同的格式

hello程序在执行之前,会被翻译成不同格式的程序。在unix系统上,从源文件到目标文件的转化是由编译器驱动程序完成的:

gcc -o hello hello.c

这条命令会将它翻译成一个可执行目标文件hello,在这个翻译的过程中经过了四个阶段。

1、预处理阶段。在这个阶段预处理器(cpp)会根据以#开头的命令,修改原始的程序。例如hello程序中,是将文件stdio.h的内容插入到程序文本中。这样就得到了另外的一个程序。这个程序通常是以.i作为文件扩展名的。

2、编译阶段。在这个阶段编译器(ccl)会将hello.i翻译成hello.s。它是一个包含汇编语言的程序。汇编语言程序中的每条语句都是以一种标准的文本格式确切地描述了一条低级机器语言指令。

3、汇编阶段。在这个阶段汇编器(as)将hello.s翻译成机器语言指令,并把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在牧宝文件hello.o中。这里的hello.o文件是一个二进制文件,它的字节编码是机器语言指令,而不是字符。

4、链接阶段。在这个阶段会将预编译好的目标文件加载到程序文件中。例如hello程序中,printf函数存在与一个名为printf.o的单独预编译好的慕白文件中,而这个printf.o必须通过链接器(ld)来合并并到我们的hello.o程序中。这样,最终就到了hello文件,它是一个可执行目标文件,可以被加载到内存中,有系统执行。

1.3 了解编译系统是如何工作是大有益处的 

我们完全可以依靠编译器来生成正确有效的机器代码,为什么还要费尽心思来了解编译系统是如何工作的?

1、可以优化程序的性能。如果了解编译器的内部工作,就能明白一个switch语句总是比一系列的if-then-else要更加的高效。

2、理解链接时出现的错误。根据以往的经验,一些困扰已经的程序错误往往都是与连接器操作有关。

3、避免安全漏洞。缓冲区溢出错误是造成大多数我拿过来和internet服务器上安全漏洞的主要原因,这些内容会在第3章中进行介绍。学习安全编程的第一步就是理解数据和控制信息存储在程序栈上的方式引起的后果。

1.4 处理器读并解释存储在存储器中的指令

回想一下hello.c程序,它此时已经被翻译称为了可执行的目标文件hello,并存放在了磁盘上。我们通过shell程序来运行它。

1.4.1 系统的硬件组成

那么在运行hello程序时发生了什么,先看一个计算机的硬件图。

1、总线。它是整个系统的电子管道,相当于城市的公路,它将字节在各个部件进行传递。通常总线被设计成为传送定长的字节块,也就是字(word)。这里的定长是一个系统的参数,各个机器的情况不一样,如果是32位的,那么机器的字长就是4个字节。如果是64位的,那么机器的字长就是8个字节。这里,假设字长是4个字节,并且总线每次只传送一个字。

2、I/O设备。I/O设备是系统与外部世界的联系通道。这里有4个I/O设备:键盘、鼠标、显示器、磁盘。每个I/O设备都是通过一个控制器或者适配器来与I/O总线相连。

3、主存。主存是一个临时存储设备,在处理器执行程序的时候,用来存放程序和程序处理的数据

4、处理器。处理器(CPU)是解释(或者执行)存储在主存中指令的引擎。它的核心是一个字长的存储设备(或寄存器),称为程序计数器,在任何时刻,PC都指向主存中的某条机器语言指令(即含有该条指令的地址)。这些内容会在第4章中有详细的介绍。这里只做一个区分,就是处理器的指令集结构和微体系结构。指令集结构描述的是每条机器代码指令的效果。而微体系结构描述的是处理器实际上是如何实现的。

1.4.2 运行hello程序

当对计算机的硬件有大致的了解之后。我们在来看执行hello程序之后,发生了什么事。

1、当我们在shell命令行中输入字符串“./hello”之后,shell程序会将这些字符通过键盘读入到寄存器,然后在存放到存储器中。

2、输入回车之后,shell程序知道我们结束了命令,然后它加载可执行文件hello,将文件中的代码和数据(这里的数据包括最终会被输出的字符串“hello, world\n”)从磁盘复制到主存。利用直接存储器存取的技术(DMA),数据可以不通过处理器而直接从磁盘到达主存。

3、当慕白文件hello中代码和数据被加载到主存之后,处理器就开始执行hello程序的main程序中的机器语言指令。执行完毕后,这些指令将“hello, world\n”字符串中的字节从主存复制到寄存器文件,再从寄存器文件中复制到显示设备,最终显示在屏幕上。

1.5 高速缓存至关重要

从上面的例子可以看到,系统会花费了很多的时间把信息从一个地方复制到另外一个地方。从程序员的角度来看,这些复制就是开销,它减缓了程序的工作。因此,系统设计者的一个主要的目标就是使这些复制尽快的完成。而一个高速缓存存储器,会让处理器更快地访问经常需要处理的数据。

本书的一个重要的结论,就是意识到高速缓存存在的应用程序员可以利用高速缓存将他们的程序的性能提高一个数量级。

1.6 存储设备形成层次结构

在处理器和内存之间插入一个更小更快的存储设备已经称为了一个比较普遍的观念。实际上,每一个计算机系统中的存储设备都被组成成了一个存储器层次结构。

posted @ 2019-02-27 10:33  walle_zhao  阅读(204)  评论(0编辑  收藏  举报