CSAPP读书笔记

第一章:计算机系统的漫游

程序的生命周期:从它被程序员创建开始,到在系统上运行,输出简单的消息,然后终止

hello.c程序

#include<stdio.h>
int main()
{
	printf("hello world\n");
	return 0;
}

信息就是位+上下文

hello程序的生命周期是从一个源程序开始的,即程序员通过编辑器创建并保存的文本文件,文件名是hello.c

源程序实际上就是由值0和1组成的位(又称比特)序列

每8个位被组织成一组,称为字节

每个字节表示程序中的某些文本字符(ASCII码)

hello.c程序是以字节序列的方式存储在文件中的。每个字节都有一个整数值,对应于某些字符(ASCII码)

只有ASCII字符构成的文件称为文本文件,所有其他文件都称为二进制文件

系统中的所有信息(文件、程序、数据)都是由一串比特表示的。区别不同数据对象的唯一方法是我们读到这些数据对象时的上下文

上下文的作用就是用来区分不同的数据对象

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

为了在系统上运行hello.c程序,每条C语句都必须被其他程序转化为一系列的低级机器语言指令。然后这些指令按照一种称为可执行目标程序的格式打包好,并以二进制磁盘文件的形式存放起来。目标程序也称为可执行目标文件

从源文件到目标文件的转化是由编译器驱动程序完成的

编译器驱动过程分为四个阶段完成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N0FuGxZs-1651566090251)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220321155832791.png)]

执行这四个阶段的程序(预处理器,编译器,汇编器,连接器)一起构成了编译系统

预处理阶段:预处理器(C Pre-Processor)根据以字符#开头的命令(即头文件),修改原始的C程序(修改的过程是读取头文件的内容,并把它直接插入到程序文本中),通常是以.i作为文件扩展名。

编译阶段:编译器(compiler)将文本文件hello.i翻译成文本文件hello.s(一个汇编语言程序)

汇编阶段:汇编器(Assembler)将hello.s翻译成机器语言指令,并把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中,hello.o文件是一个二进制文件

链接阶段:在hello程序中调用了printf函数,它是标准库中的一个函数,printf函数存在于一个名为printf.o的单独的预编译好了的目标文件(二进制文件)中,而这个文件必须以某种方式合并到hello.o程序中。链接器(Linker)就负责处理这种合并。结果就得到hello文件,它是一个可执行目标文件,可以被加载到内存中,由系统执行

系统的硬件组成

总线

贯穿整个系统的是一组电子管道,称作总线,它携带信息字节并负责在各个部件间传递

通常总线被设计成传送定长的字节块,也就是字(word)

I/O设备

I/O设备是系统与外部世界的联系通道,每个I/O设备都通过一个控制器或适配器与I/O总线相连

控制器与适配器之间的区别主要在于它们的封装方式。控制器是I/O设备本身或者主板上的芯片组,适配器则是一块插在主板插槽上的卡,它们的功能都是在I/O总线和I/O设备之间传递信息

主存(内存)

主存是一个临时存储设备,用来存放程序和程序处理的数据,它是由一组动态随机存取存储器(DRAM)芯片组成。存储器是一个线性的字节数组,每个字节都有其唯一的地址

组成程序的每条机器指令都由不同数量的字节构成,short类型的数据需要2个字节,int和float类型需要4个字节,long和double类型需要8个字节

处理器

处理器是解释或执行存储在主存中指令的引擎。

处理器的核心是一个大小为一个字的寄存器,称为程序计数器(PC),在任何时刻PC都指向主存中的某条机器语言指令

只要系统有电,处理器就一直执行程序计数器指向的指令,再更新程序计数器,使其指向下一条指令

而执行一条指令包含执行一系列的步骤(指令、解释指令、更新计数器指向下一条指令):处理器从程序计数器指向的内存处读取指令,解释指令中的位,执行该指令指示的操作,更新PC使其指向下一条指令

寄存器文件

寄存器文件是一个小的存储设备,由一些单个自称的寄存器组成

ALU

ALU:算术逻辑处理单元,计算新的数据和地址值

指令操作

加载(主存复制到寄存器):从主存复制一个字节或一个字到寄存器,以覆盖寄存器原来的内容

存储(寄存器复制到主存):从寄存器复制一个字节或一个字到主存的某个位置,以覆盖这个位置原来的内容

操作(寄存器复制到ALU,然后将运算结果放到寄存器里):把两个寄存器的内容复制到ALU,ALU对这两个字做算术运算,并将结果存放到寄存器中,以覆盖寄存器原来的内容

跳转(从指令本身复制到PC中):从指令本身中抽取一个字,并将这个字复制到PC中,以覆盖PC中原来的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LkAUR2bD-1651566090253)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220321163502545.png)]

我们将处理器的指令集架构处理器的微体系结构区分开来

指令集架构描述的是每条机器代码指令的效果

微体系结构描述的是处理器实际上如何实现的

运行hello程序的过程

shell程序加载和运行hello程序是依靠操作系统提供的进程来完成的,即:shell程序就是一个进程,执行进程的就是CPU

初始时,shell程序等待我们输入一个命令,输入./hello后,shell程序执行进程,将./hello先逐一读入CPU的寄存器,再把它放到内存中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NgQBZydY-1651566090253)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220321235109677.png)]

敲击回车时,shell程序结束命令的输入。

然后shell程序执行一系列指令来加载可执行的hello文件(shell程序利用系统调用创建hello进程并执行),这些执行将hello目标文件中的代码和数据从磁盘复制到内存。

数据包括最终在内存输出的字符串:hello world/n

利用直接存储器存取,数据可以不通过处理器而直接从磁盘到内存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8EwOtCCr-1651566090254)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220321235527053.png)]

目标文件hello中的代码和数据被加载到内存之后,会把main函数中的指令从主存复制到处理器,处理器就开始执行hello程序的main程序中的机器语言指令,这些指令将hello world\n字符串中的字节从主存复制到寄存器文件,再从寄存器文件中复制到显示设备,最终显示在屏幕上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aHSEHMyU-1651566090255)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220322000717378.png)]

执行过程概述为:1.在shell程序中输入./hello,shell程序将./helllo逐一的传入CPU的寄存器,然后传入主存

2.输入回车代表结束输入

3.shell执行一系列的指令来加载可执行的hello文件,这些指令将hello目标文件中的代码和数据从磁盘复制到主存,也包括最终输出的hello world/n

4.把main函数中的指令从主存复制到处理器,处理器就开始执行hello程序的main程序中的机器语言指令,这些指令将hello world\n字符串中的字节从主存复制到寄存器文件,再从寄存器文件中复制到显示设备,最终显示在屏幕上

高速缓存至关重要

hello程序的机器指令最初是存放在磁盘上的,程序被加载时,它们被复制到主存,处理器运行时,指令又从主存复制到处理器

针对这种处理器与主存之间的差异,系统设计者采用了更小更快的存储设备,称为高速缓存存储器(cache memory),它作为暂时的集结区域,存放处理器近期可能会需要的信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-08KdKZa7-1651566090255)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220322003044741.png)]

高速缓存器分为L1高速缓存器和L2高速缓存器,L1高速缓存存储器在CPU内部L2高速缓存器通过一条特殊的总线连接到处理器

L1和L2高速缓存是用一种叫做静态随机访问存储器(SRAM)的硬件技术实现的

存储设备形成层次结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGZTX6y9-1651566090256)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220322003514486.png)]

操作系统

在hello程序的例子中,当shell加载和运行hello程序时,shell和hello程序都没有直接访问键盘、显示器、磁盘和主存,它们都是依靠操作系统提供的服务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tPSgqefc-1651566090257)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220322003821691.png)]

进程

进程时操作系统对一个正在运行的程序的一种抽象

并发执行:一个进程的指令和另一个进程的指令交错执行

当操作系统进行进程切换时会用到上下文切换,即保存当前进程的上下文,恢复新进程的上下文,将控制权传递到新进程。新进程就会从它上次停止的地方开始

系统调用:系统调用会将控制权短暂的由进程传递给操作系统。操作系统保存原进程的上下文,创建一个新的进程及其上下文,然后将控制权传给新进程。新进程终止后,操作系统恢复原进程的上下文,并将控制权传回给它,原进程继续执行

线程

一个进程实际上可以由多个线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据

虚拟内存

虚拟内存是一个抽象概念,它为每个进程提供了一个假象,即每个进程都在独自占有使用主存。每个进程看到的内存都是一致的,称为虚拟地址空间

图为虚拟地址空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q86zrFPG-1651566090258)(C:\Users\83989\AppData\Roaming\Typora\typora-user-images\image-20220322005014537.png)]

代码和数据:对所有的进程来说,代码是从同一固定地址开始,紧接着的是和C全局变量相对应的数据位置

堆:代码和数据区在进程一开始运行时就被制定了大小,与此不同,当调用malloc和free这样的函数时,堆可以在运行时动态的扩展和收缩

共享库:存放C标准库

栈:编译器用栈来实现函数调用,和堆一样,栈在在运行时动态的扩展和收缩。每次我们调用一个函数时,栈就会增长,从一个函数返回时,栈就会收缩

文件

文件就是字节序列,每个I/O设备都可以被抽象成文件

并发和并行

并发:一个同时具有多个活动的系统

并行:用并发来使一个系统运行的更快

线程级并发

线程能够在一个进程中执行多个控制流

超线程(同时多线程),是一项允许一个CPU执行多个控制流的技术

指令级并行

8086处理器需要多个时钟周期来执行一条指令,现代处理器可以保持每个时钟周期2~4条指令的执行速率

超标量:处理器可以达到比一个周期一条指令更快的执行速率

大多数现代处理器都支持超标量操作

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值