深入理解计算机系统(csapp)(第一章计算机系统漫游)

文章探讨了计算机系统的基础,包括信息由位和上下文构成,程序的翻译过程,处理器如何执行指令,高速缓存在性能中的关键作用,存储设备的层次结构,操作系统对硬件的管理,以及系统间的网络通信。此外,还强调了了解这些概念对于优化程序、理解错误和增强安全性的重要性。
摘要由CSDN通过智能技术生成

第一章 计算机系统漫游

1.1 信息就是位+上下文

以hello程序为例,hello程序是人通过编辑器编写的文本文件,命名为hello.c。源程序(源文件)实际上就是一个由0和1组成的位(又称为比特)序列,八个位被组成一组,称为字节
大部分现代计算机系统都使用ASCⅡ标准来表示字符,这种方式实际上就是用唯一的单字节大小的整数值表示每个字符。
下面是hello.c的ASCⅡ码表示
在这里插入图片描述
像hello.c的只由ASCⅡ字符构成的文件称为文本文件,所有其他文件都称为二进制文件。
hello.c的表示方法说明了一个基本思想:系统中所有的信息——包括磁盘文件、内存中的程序、内存中存放的用户数据以及网络上传送的数据,都是由一串比特表示的。区分不同数据对象的唯一方法是我们读到这些数据对象时的上下文。

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

hello程序的生命周期是以一个人能看懂的高级C语言程序开始的,为了在系统上运行hello.c,每条c语句都必须被其他程序转化为一系列的低级机器语言指令。这些指令按照一种名为可执行目标程序的格式打包好,并以二进制磁盘文件的形式存放起来。目标程序也称可执行目标程序。
在Unix系统上,从源文件到目标文件的过程是靠编译器驱动程序完成的:
gcc -o hello hello.c
GCC的翻译过程分为四个阶段,执行的四个程序 预处理器 编译器 汇编器 链接器 一起构成了编译系统
在这里插入图片描述

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

  • 优化程序性能
  • 理解链接时出现的错误
  • 避免安全漏洞

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

系统的硬件组成

  1. 总线
    贯穿整个系统的是一组电子管道,称为总线,它携带信息字节并负责在各个部件间传递。总线通常被设计成传送定长的字节块,也就是字。字中的字节数(即字长)是一个基本的系统参数,各个系统不尽相同。

  2. I/O设备
    I/O(输入/输出)设备时系统与外部世界的联系通道。假设系统包括四个I/O设备:作为用户输入的键盘和鼠标,作为用户输出的显示器,以及用于长期存储数据和程序的磁盘驱动器(简单的说就是磁盘)。最开始程序hello就存放在磁盘上。
    每个I/O设备都通过一个控制器或配适器与I/O总线相连。控制器和适配器之间的区别主要在于它们的封装方式。控制器是I/O设备本身或者系统的主印制电路板(通常作为主板)上的芯片组。而配适器则是一块插在主板插槽上的卡。
    在这里插入图片描述

  3. 主存
    主存是一个临时存储设备,字处理器执行程序时,用来存放程序和程序处理的数据。从物理上,主存时一组动态随机存取存储器(DRAM)芯片组成的。从逻辑上,存储器是一个线性的字节数组,每个字节都有其唯一的地址,这些地址是从零开始的。

  4. 处理器
    中央处理器(CPU),简称处理器,时解释(或执行)存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC)。在任何时刻,PC都指向主存中的某条机器语言指令(即含有该条指令的地址)。
    //pc也普遍地被用来作为个人计算机的缩写。两者区别通过上下文可以看出。
    从系统通电开始,处理器就一直执行程序计数器指向的指令,再更新程序计数器,使其指向下一条指令,下一指令未必与刚刚执行的指令相邻。
    这样的简单菜操作并不多,它们围绕着主存、寄存器文件和算法/逻辑单元(ALU)进行。寄存器文件是一个小的存储设备,由一些长的寄存器组成,每个寄存器都有唯一的名字。ALU计算新的数据和地址值。
    下面是一些CPU在指令的要求下可能会执行的操作

  • 加载:从主存复制一个字长或者一个字到寄存器,以覆盖寄存器原来的内容。
  • 存储:从寄存器复制一个字节或者一个字到主存的某个位置,以覆盖这个位置上原来的内容。
  • 操作:把两个寄存器的内容复制到ALU,ALU对这两个字做算数运算,并将结果存放到一个寄存器,以覆盖该寄存器中原来的内容。
  • 跳转:从指令本身抽出一个字,并将这个字复制到PC中,以覆盖PC中原来的值。
    运行hello程序
    初始时,shell程序执行它的指令,等待我们输入一个命令。当我们在键盘上输入字符串./hello后,shell程序将字符逐一读入寄存器,再把它存放到内存中
    在这里插入图片描述
    当我们在键盘上敲回车时,shell程序就知道了我们已经结束了命令的输入。然后shell执行一系列指令来加载可执行的hello文件,这些指令将hello目标文件中的代码和数据从磁盘复制到主存。数据包括最终会被输出的字符串hello,world\n
    利用直接存储器读取(DMA)技术,数据可以不通过处理器而直接从此到达主存。
    在这里插入图片描述
    一旦目标文件hello中的代码数据被加载到主存,处理器就开始执行hello程序的main程序中的机器语言指令。这些指令间将hello,world\n字符串从主存复制到寄存器文件,再从寄存器文件复制到显示设备,最终显示在屏幕。
    在这里插入图片描述

1.5 高速缓存至关重要

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

1.6 存储设备形成层次解结构

每个计算机系统中的存储设备都被组织成了一个存储器层次结构,如图1-9所示,在这个结构中,从上至下,设备的访问速度越来越慢,容量也越来越大,每字节的造价也越来越便宜。
在这里插入图片描述

1.7 操作系统管理硬件

回到hello程序例子,当shell加载运行hello程序时,包括hello程序输出自己的消息时,shell和hello程序都没有直接访问键盘、显示器、磁盘或者主存。取而代之的是,它们依靠操作系统提供的服务。我们可以把它理解为一个软件,如图1-10所示。所有的应用程序对硬件的操作尝试都必须通过操作系统。
在这里插入图片描述

操作系统的两个基本功能:
1.防止硬件被失控的应用程序滥用;
2.向应用程序提供一种简单一致的机制来控制复杂而又通常大不相同的低级硬件设备。操作系统通过几个基本抽象概念(进程、虚拟内存和文件)来实现这两个功能。
进程
像hello这样的程序在现代系统上运行时,操作系统会显示一种假象,就好像系统上只有这个程序在运行。程序看上去是在独占地使用处理器、主存和I/O设备。这种假象是通过进程的概念来实现的。
进程是操作系统对一个正在运行的程序的一种抽象。在一个系统上可以同时运行多个进程,而且像是并发运行。其实是指令交错执行。在大多数系统中,需要运行的进程数是多于可以运行它们的CPU个数的。传统系统在一个时刻只能运行一个程序,而先进的多核处理器同时能执行多个程序。无论是单核还是多核系统中,一个CPU看上去都像是在并发执行多个进程,这是通过处理器在进程间切换来实现的。操作系统实现这种交错执行的机制称为上下文切换。我们先讨论一个CPU的单核处理器情况。
操作系统保持跟踪进程运行所需的所有状态信息。这种状态,也就是上下文,包括许多信息,比如PC和寄存器文件当前的值,以及主存的内容。在任何一个时刻,单处理器系统都只能执行一个进程的代码。即保存当前进程的上下文、恢复新进程的上下文,然后将控制权传递到新进程。新进程就会从它上次停止的地方开始。图1-12展示hello程序运行场景的基本理念。
两个进程:hello和shell,最初在shell进程,当执行hello时,shell调用一个系统调用函数,执行我们的指令,系统调用会将控制权传递给操作系统。操作系统保存shell进程的上下文,创建一个新的hello进程和上下文,再将控制权交给hello进程,hello进程结束后,操作系统恢复shell的上下文,并将控制权传回给它。
从一个进程到另一个进程是由操作系统内核管理的。内核是操作系统代码常驻主存的部分。内核不是一个独立的进程,它是系统管理全部进程所用代码和数据结构的集合。
在这里插入图片描述
线程
在现代系统中,一个进程可以由多个被称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。多线程之间比多进程之间更容易共享数据。
虚拟内存
它为进程提供了一个假象,即每个进程都在独占主存。
每个进程看到的内存都是一致的,称为虚拟地址空间。
Linux进程的虚拟地址空间:
在这里插入图片描述
地址空间最上面的区域是保留给操作系统中代码和数据的,这对所有进程来说都一样。地址空间底部存放用户进程定义的代码和数据。图中的地址是从下往上增大的。
每个进程看到的虚拟地址空间由大量准确定义的区构成,每个区都有专门的功能。
从下往上:

  • 程序代码和数据。
    对所有进程来说,代码是从同一固定地址开始,紧接着的是和C全局变量相对应的数据位置。代码和数据区是直接按照可执行目标文件的内容初始化的,在示例中就是可执行文件hello。
  • 堆。
    代码和数据区后紧随着的是运行时堆。代码和数据区在进程一开始运行时就被指定了大小,与此不同,当调用malloc和free这样的C标准库函数时,堆可以在运行时动态地扩张和收缩。
  • 共享库。
    大约在地址空间的中部是一块用来存放像C标准库和数学库这样的共享库的代码和数据的区域。
  • 栈。
    位于用户虚拟地址空间顶部的用户栈,编译器用他来实现调用函数。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。特别地,每次我们调用一个函数时,栈就会增长;从一个函数返回时,栈就会收缩。
  • 内核虚拟内存。
    地址空间顶部的区域是为内核保留的。不允许应用程序读写这个区域的内容或者直接调用内核代码定义函数。相反,它们必须调用内核来执行这些操作。
    文件
    文件就是字节序列。每个I/O设备,包括磁盘、键盘、显示器、甚至网络、都可以看成文件。

1.8 系统之间利用网络通信

从单独的系统来看,网络可视为一个I/O设备,当系统从主存复制一串字节到网络配适器时,数据流经过网络到达另一台机器。
在这里插入图片描述
以hello为例,假设用本地主机上的telnet客户端连接远程主机上的telnet服务器。在我们登录到远程主机并运行shell后,远端的shell就在等待接收输入命令。此后在远端运行hello程序包括以下步骤
在这里插入图片描述

1.9 重要主题

结论:系统是硬件和系统软件相互交织的集合体,它们必须共同协作以达到运行应用程序的最终目的。
Amdahl 定律
主要思想:当我们对系统的某个部分加速时,其对系统整体性能的影响取决于该部分的重要性和加速程度。
若系统执行某应用程序需要时间Told。假设系统某部分所需执行时间与该时间的比例为α,而该性能提升比例为k。即该部分初始所需时间为αTold,现在所需时间为(αTold)/k。因此,总的执行时间应为:
Tnew=(1-α)Told+(αTold)/k=Told[(1-α)+α/k]
由此,可以计算加速度比S=Told/Tnew为
S= 1/[(1-α)+α/k]
在这里插入图片描述
并发与并行
并发:指一个同时具有多个活动的系统;
并行:用并发来使一个系统运行得更快。

  1. 线程级并发
    构建在进程这个抽象之上,我们能够设计出同时有多个程序执行的系统,这就导致了并发。在以前,即使处理器必须在多个任务间切换,大多数实际的计算也都是由一个处理器来完成的。这种配置称为单处理器系统。
    当构建一个由单操作系统内核控制的多处理器组成的系统时,我们就得到了一个多处理器系统。
    多核处理器是将多个CPU(称为“核”)集成到一个集成电路板。
    在这里插入图片描述
    超线程:有时称为同时多线程,是一项允许一个CPU执行多个控制流的技术。它涉及CPU某些硬件有多个备份,比如程序计数器和寄存器文件,而其他的硬件部分只有一份,比如执行浮点算数运算的单元。超线程的处理器可以在单个周期的基础上决定要执行哪一个线程。这使得CPU能更好的利用他的资源。

  2. 指令级并行
    在较低的抽象层次上,现代处理器可以同时执行多条指令的属性称为指令级并行。如果处理器可以达到比一个周期一条指令更快的执行速率,就称之为超标量处理器。

  3. 单指令、多数据并行
    在最低层次上,许多现代处理器拥有特殊的硬件,允许一条指令产生多个可以并行执行的操作,这种方式称为单指令、多数据,即SIMD并行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值