011.11.21.31.41.51.61.71.81.91.10234567891010.110.210.310.410.510.610.710.810.9目錄介紹第1章 计算机漫游1.1 信息就是位+上下文1.2 程序被其他程序翻译成不同的格式1.3 了解编译系统如何工作是大有益处1.4 处理器读并解释存储在存储器中的指令1.5 高速缓存至关重要1.6 存储设备形成层次结构1.7 操作系统管理硬件1.8 系统之间利用网络通信1.9 重要主题1.10 小结第2章 信息的表示和处理第3章 程序的机器级表示第4章 处理器体系结构第5章 优化程序性能第6章 存储器层次结构第7章 链接第8章 异常控制流第9章 虚拟存储器第10章 系统级I/O10.1 Unix I/O10.2 打开和关闭文件10.3 读和写文件10.4 用RIO包健壮地读写10.5 读取文件元数据10.6 共享文件10.7 I/O重定向10.8 标准I/O10.9 综合:我该使用哪些I/O函数《深入理解计算机系统》笔记210.1010.11111210.10 小结10.11 家庭作业第11章 网络编程第12章 并发编程《深入理解计算机系统》笔记3CSAPP《深入理解计算机系统》笔记章节列表第1章 计算机漫游 (完成)第2章 信息的表示和处理 (进行中)第3章 程序的机器级表示第4章 处理器体系结构第5章 优化程序性能第6章 存储器层次结构第7章 链接第8章 异常控制流第9章 虚拟存储器第10章 系统级I/O (完成)第11章 网络编程第12章 并发编程资源代码: CSAPP《深入理解计算机系统》笔记4介紹第1章 计算机漫游本书是为了让程序员了解计算机系统中的硬件和软件是如何工作的,以及它是如何影响程序的正确性和性能的,以此来提高自身的技能。如果能完全理解底层计算机系统以及它对应用程序的影响,将成为优秀的程序员。从本书中你将会学习一些实践技巧。比如:你将会学到如何避免由计算机表示数字的方式导致的奇怪的数字错误。你将学会怎样通过一些聪明的小窍门来优化你的C代码,以充分利用现代处理器和存储器系统的设计。你将了解到编译器是如何实现过程调用的,以及如何利用这些知识避免缓冲区溢出错误带来的安全漏洞,这些弱点会给网络和因特网软件带来了巨大的麻烦。你将学会如何识别和避免链接时那些令人讨厌的错误,它们困扰着普通的程序员。你将学会如何编写自己的Unix外壳、自己的动态存储分配包,甚至是自己的Web服务器。你会认识到并发带来的希望和陷阱,当单个芯片上集成了多个处理器核时,这个主题变得越来越重要。以上涉及到了很多方面,能够读透这本书,收获肯定不少。每次逃不开的helloworld1 #include stdio.h23 int main()4 {5 printf(“hello,world\n”);6 }本书跟踪hello程序的生命周期来开始对计算机系统进行深入的学习。本章目录1.1 信息就是位+上下文1.2 程序被其他程序翻译成不同的格式1.3 了解编译系统如何工作是大有益处1.4 处理器读并解释存储在存储器中的指令1.5 高速缓存至关重要1.6 存储设备形成层次结构《深入理解计算机系统》笔记5第1章 计算机漫游1.7 操作系统管理硬件1.8 系统之间利用网络通信1.9 重要主题1.10 小结《深入理解计算机系统》笔记6第1章 计算机漫游1.1 信息就是位+上下文hello程序的生命周期是从一个源程序开始的,而且这个源程序是一个文本文件。源程序都是由0和1组成的bit序列,而这些序列代表的文本字符都是通过ASCII标准来表示的。实际上就是用一个唯一的单字节大小的整数值来表示每个字符。我们通过OD打印hello.c的所有字符的ASCII码:其实系统中所有的信息———包括磁盘文件、存储器中的程序、存储器中存放的用户数据以及网络上传送的数据,都是由一串位表示的。区分不同数据对象的唯一方法是我们读到这些数据对象时的上下文。如果我们查看编译后的可执行文件:root@kali:~/Desktop/c/csapp/chapter1# od -Ax -tx1 hello001190 6c 5f 64 74 6f 72 73 5f 61 75 78 5f 66 69 6e 690011a0 5f 61 72 72 61 79 5f 65 6e 74 72 79 00 66 72 610011b0 6d 65 5f 64 75 6d 6d 79 00 5f 5f 66 72 61 6d 650011c0 5f 64 75 6d 6d 79 5f 69 6e 69 74 5f 61 72 72 610011d0 79 5f 65 6e 74 72 79 00 68 65 6c 6c 6f 2e 63 000011e0 5f 5f 46 52 41 4d 45 5f 45 4e 44 5f 5f 00 5f 5f0011f0 4a 43 52 5f 45 4e 44 5f 5f 00 5f 5f 69 6e 69 74001200 5f 61 72 72 61 79 5f 65 6e 64 00 5f 44 59 4e 41也会发现所有的数据都是一串位。从机器码层说明一下:比如内存中的二进制信息1000100111011000 计算机可以把它当做数据处理也可以当作指令来执行1000100111011000 —— 89D8H(数据) 1000100111011000 ——mov ax,bx(程序)《深入理解计算机系统》笔记71.1 信息就是位+上下文决定是指令还是数据的判断就是:传输总线的类型当然这里的数据和指令都属于前面说的位本节提到两个概念;字节序列这个关于大端小端的概念以后详细解释关于整数和实数的存储在后面会通过汇编来说明问题:在C语言程序的起源中,谈到了C语言为什么会成功的,也提到了C语言的指针是造成困惑和程序错误的常见原因,请问,为什么?详细答案请阅读《C和指针》《深入理解计算机系统》笔记81.1 信息就是位+上下文1.2 程序被其他程序翻译成不同的格式hello程序的生命周期是从高级C语言程序开始,然后为了在系统上运行该程序,须被其他程序转化为低级的机器语言指令,而存储程序的格式是二进制磁盘文件。 转化 存储源程序 ————————机器语言————————可执行文件 (编译器编译) (指令) 文件系统(二进制)现在在Linux系统下,我们梳理一下这个流程。1.预处理阶段预处理阶段,预处理器cpp会根据#开头的命令,修改原始的C程序。例子: root@kali:~/Desktop/c/csapp/chapter1# cpp hello.c -o hello.i 查看生成的hello.i1 # 1 hello.c2 # 1 command-line3 # 1 hello.c4 # 1 /usr/include/stdio.h 1 3 45 # 27 /usr/include/stdio.h 3 4 ......省略......842 extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __lea f__));843 # 943 /usr/include/stdio.h 3 4844845 # 2 hello.c 2846847 int main()848 {849 printf(hello,world\n);850 }《深入理解计算机系统》笔记91.2 程序被其他程序翻译成不同的格式因为hello.c代码中只包括了文件包含命令,所以程序只把stdio.h的内容 插入到源程序中。下面我们会用更多的例子来说明预处理器做的工作。主要的预处理命令有:第一类:文件包含文件包含包括#include 和#include xxx.h#include 表示被包含的文件在系统目录#include xxx.h 表示被包含的文件在引号里面的目录,如果没路径则表示为当前目录。对于自己写的头文件,适合用双引号,系统头文件两个都可用,但尖括号免去了在用户目录搜索。在预处理阶段,预处理器把包含的头文件的内容展开到源文件中。如果hello.c中去掉#include使用gcc编译提示信息:hello.c: In function 'main':hello.c:4:2: warning: incompatible implicit declaration of built-in function 'printf' [enabled by default]hello.i 中的内容就变成:《深入理解计算机系统》笔记101.2 程序被其他程序翻译成不同的格式 1 # 1 hello.c 2 # 1 command-line 3 # 1 hello.c 4 5 int main() 6 { 7 printf(hello,world\n); 8 }编译出来的hello也可输出hello,world,为什么呢? 这是因为如果发现调用一个函数时并没有找到它的声明时,编译器会创建一个假的声明,这个声明使用你传递的参数并返回int。在这个例子中,因为printf()定义在libc.so中,在动态链接的时候会在默认的库文件libc.so中找到函数的定义。以下为hello加载的动态链接库 root@kali:~/Desktop/c/csapp/chapter1# ldd hello linux-gate.so.1 (0xb77a1000) libc.so.6 = /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb75d4000) /lib/ld-linux.so.2 (0xb77a2000)如果不链接libc的话,则会报错:root@kali:~/Desktop/c/csapp/chapter1# gcc -o hello -nostdlib hello.chello.c: In function 'main':hello.c:4:2: warning: incompatible implicit declaration of built-in function 'printf' [enabled by default]/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 00000000080480d8/tmp/cc5NiZBs.o: In function `main':hello.c:(.text+0x11): undefined reference to `puts'collect2: error: ld returned 1 exit status这里提示找不到_start 和不能引用puts。 实际上,不链接libc,编译器不能够调用C库函数也不能得到C的引导代码。 _start函数是什么呢?Linux在启动时,它起了什么作用呢?怎么样重写该函数呢?跑题太远,这些在以后会讲到。而puts则是编译器优化的结果,把printf替换为puts。 查看汇编:《深入理解计算机系统》笔记111.2 程序被其他程序翻译成不同的格式 8 main: 9 .LFB0: 10 .cfi_startproc 11 pushl %ebp 12 .cfi_def_cfa_offset 8 13 .cfi_offset 5, -8 14 movl %esp, %ebp 15 .cfi_def_cfa_register 5 16 and