金融情报局网_中国金融门户网站 让金融财经离的更近

csapp笔记——第一章计算机系统漫游

当前位置:金融情报局网_中国金融门户网站 让金融财经离的更近>投稿 > 正文  2023-08-10 02:35:24 来源:博客园

计算机系统漫游

前言

csapp可以说是每一个computerer必须学习的一本书,它讲的知识点并不太晦涩难懂,但是其包含的知识广度是难以想象的,在完成本书的学习后,对计算机系统的运行原理等的理解会上一个大的台阶,让我们进入这第一章的学习,对计算机的系统会有一个大概的了解。本书首先通过了一个c语言程序在运行要经过那些步骤引入了对于计算机系统的简要描述。

这是一个简单的c语言程序


(资料图片仅供参考)

#include int main(){    printf("hello, world\n");    return 0;}

1.信息就是位+上下文

在我们计算机系统中,大多都使用ASCII码来表示文本字符,那么ASCII码是什么呢?,用一个单字节大小的二进制整数表示的字符,我们将像hello.c一样由ascii码表示的文件称为文本文件,而其他的文件则称为二进制文件,hello.c的二进制ascii表示如下

我们可以看到,每一行的末尾都有\n,这是ASCII中的不可见字符,同时由这样的例子说明了一个基本思想∶系统中所有的信息——包括磁盘文件、内存中的程序、内存中存放的用户数据以及网络上传送的数据,都是由一串比特表示的。区分不同数据对象的唯一方法是我们读到这些数据对象时的上下文。比如,在不同的上下文中,一个同样的字节序列可能表示一个整数、浮点数、字符串或者机器指令。

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

在一个c语言的程序被翻译成一个可执行文件的过程中一共经历了4个阶段,这是个阶段的统称就是编译系统(预处理器,编译器,汇编器,连接器)

我们来看运行hello.c这个文件的生命周期,首先我们通过预编译器变成了hello.i文件预处理器:预处理器(cpp)根据以字符 # 开头的命令,修改原始的 C 程序。比如 hello.c 中第 1 行的#include 命令告诉预处理器读取系统头文件 stdio.h 的内容,并把它直接插入程序文本中。结果就得到了另一个 C 程序,通常是以 .i 作为文件扩展名。然后经过编译器编译为汇编语言hello.s,汇编语言可以作为不同语言不同编译器的通用输出语言,然后通过汇编器将汇编语言翻译成晦涩难懂的二进制机器指令,最后和printf.o文件一起被连接器合并成为一个可执行文件

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

我们的hello.c被存放在内存中,我们要像运行它就必须在shell中运行该文件,该文件在被运行的过程中会被读取到cpu中,最后在通过相关的io总线输出在用户的显示屏中,我们首先要知道计算机的硬件构成:

1.cpu(处理器):中央处理单元(CPU),简称处理器,是解释(或执行)存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC)。在任何时刻,PC 都指向主存中的某条机器语言指令(即含有该条指令的地址)

2.主存(内存):

3.i/o设备:

4.总线

一个典型系统的硬件组成CPU:中央处理单元;ALU:算术/逻辑单元;PC:程序计数器;USB:通用串行总线

如图是一个hello程序的读取过程,先通过io的键盘输入hello,用户回车后程序知道了该命令结束了,(注意在shell中输入的如果不是内别指令,系统会将其当可执行文件处理)然后 shell 执行一系列指令来加载可执行的 hello 文件,这些指令将 hello 目标文件中的代码和数据从磁盘复制到主存。数据包括最终会被输出的字符串 “hello, world\n”。

1.5高速缓存的重要性

再我们的机械原理中我们知道了较大的存储设备要比较小的存储设备运行得慢,而快速设备的造价远高于同类的低速设备。比如说,一个典型系统上的磁盘驱动器可能比主存大 1000 倍,但是对处理器而言,从磁盘驱动器上读取一个字的时间开销要比从主存中读取的开销大 1000 万倍。而通过1.4的了解我们知道了一个指令再运行时候要经历很多的读写,如再读取hello指令是会将磁盘上的信息存入主存,而运行时又要将主存中的信息送入处理器,这是很麻烦又减慢了我们计算机程序的运行速度,这是我们的高速缓存存储器就出现了,它作为暂时的集结区域,存放处理器近期可能会需要的信息。这是一个高速缓存存储器的系统图

1.6储存设备形成层次结构

在处理器和一个较大较慢的设备(例如主存)之间插一个更小更快的存储设备(例如高速缓存)的想法已经成为一个普遍的观念。实际上,每个计算机系统中的存储设备都被组织成了一个存储器层次结构,如图 所示。在这个层次结构中,从上至下,设备的访问速度越来越慢、容量越来越大,并且每字节的造价也越来越便宜。

该结构的基本理念是上层作为下层的高数缓存,这样就可以保证我们越长用的指令读取也就越快。

1.7操作系统管理硬件

在之前hello可执行文件的例子中,无论是shell还是hello都没有直接对硬件进行使用,而是通过中间的操作系统层级对硬件进行调用(依靠操作系统提供的服务)我们可以将操作系统看做硬件和软件层之间的一层软件,软件只能通过它调用硬件,操作系统有两个好处:

1.防止硬件被失控的软件胡乱调用;

2.提供简单一致的机制来使软件控制复杂且功能不同的低级硬件系统

操作系统通过3个抽象概念来实现这两个功能(进程,虚拟内存和文件)

1.71进程

进程是对操作系统中正在运行的程序的抽象,对于一个cpu来说执行进程是并发运行(交错运行),操作系统保持跟踪进程运行所需的所有状态信息。这种状态,也就是上下文,包括许多信息,比如 PC 和寄存器文件的当前值,以及主存的内容。,进程间通过操作系统内核(kernel)管理的。内核是操作系统代码常驻主存的部分。当应用程序需要操作系统的某些操作时,比如读写文件,它就执行一条特殊的系统调用(system call)指令,将控制权传递给内核。然后内核执行被请求的操作并返回应用程序。注意,内核不是一个独立的进程。相反,它是系统管理全部进程所用代码和数据结构的集合。

1.72线程

一个进程可以看着一个单一的控制流,多线程是运行进程中多个执行单元,他们有同样的上下文,代码,和全局数据,使得他们可以比进程更好的实现数据共享。

1.73虚拟内存

虚拟内存同样也是个抽象的概念,它使每个进程看起来都是独占主存空间,每个进程看到的主存都是一致的他们被称为虚拟地址空间,下图是Linux的虚拟地址空间

每个进程看到的虚拟地址空间由大量准确定义的区构成,每个区都有专门的功能。补充5

1.74文件

文件就是字节序列,Linux万物皆是文件,每个I/O设备,包括磁盘、键盘、显示器,甚至网络,都可以看成是文件。系统中的所有输入输出都是通过使用一小组称为 Unix I/O 的系统函数调用读写文件来实现的。

1.8系统间的网络通信

代系统经常通过网络和其他系统连接到一起。从一个单独的系统来看,网络可视为一个 I/O 设备,如图 1-14 所示。当系统从主存复制一串字节到网络适配器时,数据流经过网络到达另一台机器,而不是比如说到达本地磁盘驱动器。相似地,系统可以读取从其他机器发送来的数据,并把数据复制到自己的主存。

小结

计算机系统是由硬件和系统软件组成的,它们共同协作以运行应用程序。计算机内部的信息被表示为一组组的位,它们依据上下文有不同的解释方式。程序被其他程序翻译成不同的形式,开始时是 ASCII 文本,然后被编译器和链接器翻译成二进制可执行文件。处理器读取并解释存放在主存里的二进制指令。因为计算机花费了大量的时间在内存、I/O 设备和 CPU 寄存器之间复制数据,所以将系统中的存储设备划分成层次结构——CPU 寄存器在顶部,接着是多层的硬件高速缓存存储器、DRAM 主存和磁盘存储器。在层次模型中,位于更高层的存储设备比低层的存储设备要更快,单位比特造价也更高。层次结构中较高层次的存储设备可以作为较低层次设备的高速缓存。通过理解和运用这种存储层次结构的知识,程序员可以优化C程序的性能。操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象∶1)文件是对 I/O 设备的抽象;2)虚拟内存是对主存和磁盘的抽象;3)进程是处理器、主存和 I/O 设备的抽象。最后,网络提供了计算机系统之间通信的手段。从特殊系统的角度来看,网络就是一种 I/O 设备。

补充

1.ASCII(发音:,American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本延伸美国标准信息交换码则可以部分支持其他西欧语言,并等同于国际标准ISO/IEC 646。

ASCII 由电报码发展而来。第一版标准发布于1963年 ,1967年经历了一次主要修订[5][6],最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(一些终端提供了扩展,使得这些字符可显示为诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符。用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。

2.上下文是从英文context翻译过来,指的是一种环境。相对于进程而言,就是进程执行时的环境;

具体来说就是各个变量和数据,包括所有的寄存器变量、进程打开的文件、内存信息等。

3. 加快处理器的运行速度比加快主存的运行速度要容易和便宜得多。 针对这种处理器与主存之间的差异,系统设计者采用了更小更快的存储设备,称为高速缓存存储器(cache memory,简称为 cache 或高速缓存),作为暂时的集结区域,存放处理器近期可能会需要的信息。图 1-8 展示了一个典型系统中的高速缓存存储器。位于处理器芯片上的 L1 高速缓存的容量可以达到数万字节,访问速度几乎和访问寄存器文件一样快。一个容量为数十万到数百万字节的更大的 L2 高速缓存通过一条特殊的总线连接到处理器。进程访问 L2 高速缓存的时间要比访问 L1 高速缓存的时间长 5 倍,但是这仍然比访问主存的时间快 5~10 倍。L1 和 L2 高速缓存是用一种叫做静态随机访问存储器(SRAM)的硬件技术实现的。比较新的、处理能力更强大的系统甚至有三级高速缓存∶L1、L2 和 L3。系统可以获得一个很大的存储器,同时访问速度也很快,原因是利用了高速缓存的局部性原理,即程序具有访问局部区域里的数据和代码的趋势。通过让高速缓存里存放可能经常访问的数据,大部分的内存操作都能在快速的高速缓存中完成。

4.进程是操作系统对一个正在运行的程序的一种抽象。在一个系统上可以同时运行多个进程,而每个进程都好像在独占地使用硬件。而并发运行,则是说一个进程的指令和另一个进程的指令是交错执行的。在大多数系统中,需要运行的进程数是多于可以运行它们的 CPU 个数的。传统系统在一个时刻只能执行一个程序,而先进的多核处理器同时能够执行多个程序。无论是在单核还是多核系统中,一个 CPU 看上去都像是在并发地执行多个进程,这是通过处理器在进程间切换来实现的。操作系统实现这种交错执行的机制称为上下文切换。为了简化讨论,我们只考虑包含一个 CPU 的单处理器系统的情况。我们会在 1.9.2 节中讨论多处理器系统

5.

程序代码和数据。对所有的进程来说,代码是从同一固定地址开始,紧接着的是和 C 全局变量相对应的数据位置。代码和数据区是直接按照可执行目标文件的内容初始化的,在示例中就是可执行文件 hello。在第 7 章我们研究链接和加载时,你会学习更多有关地址空间的内容。

堆。代码和数据区后紧随着的是运行时堆。代码和数据区在进程一开始运行时就被指定了大小,与此不同,当调用像 malloc 和 free 这样的 C 标准库函数时,堆可以在运行时动态地扩展和收缩。在第 9 章学习管理虚拟内存时,我们将更详细地研究堆。

共享库。大约在地址空间的中间部分是一块用来存放像 C 标准库和数学库这样的共享库的代码和数据的区域。共享库的概念非常强大,也相当难懂。在第 7 章介绍动态链接时,将学习共享库是如何工作的。

栈。位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。特别地,每次我们调用一个函数时,栈就会增长;从一个函数返回时,栈就会收缩。在第 3 章中将学习编译器是如何使用栈的。

内核虚拟内存。地址空间顶部的区域是为内核保留的。不允许应用程序读写这个区域的内容或者直接调用内核代码定义的函数。相反,它们必须调用内核来执行这些操作。

关键词: