CSAPP要点总结 第1章 计算机系统漫谈

隐藏

前言

本来想一次把《Effective C++》系列做完的,但是在关于异常处理部分,搜索相关资料,发现我如果能掌握汇编知识就更容易理解了,包括C++语言本身,如果学习汇编相关,因此,虽然现在时间紧迫,还是先把CSAPP啃完吧。

CSAPP为CMU的教材,其地位和价值毋庸置疑,英文有第三版,中文目前是第二版

资源:

书中插图 书中源码 书中实验练习

实验练习包括:

  • 数据试验
  • 二进制炸弹试验
  • 缓冲区溢出试验
  • 体系结构试验
  • 性能试验
  • 外壳试验
  • malloc试验
  • 代理试验

GDB入门手册 官方GDB手册 GDB用法汇总

程序的生命周期,从源码到可执行文件

对于$ gcc -o hello hello.c而言,从文本格式(ASCII)的源文件转换为机器码的目标文件。

该过程分为四个步骤:

    hello.c         hello.i        hello.s         hello.o          hello
    ------>[预处理器]----->[编译器]------->[汇编器]-------->[链接器]------>
              (cpp)         (ccl)          (as)            (ld)

其中: hello.c: 源代码 预处理阶段 hello.i: 经过预处理后的源代码,依然是文本格式。 编译阶段:生成通用的汇编语言 hello.s: 汇编程序,依然是文本格式 汇编阶段:汇编器将hello.s翻译成机器语言指令,将其打包为可重定位目标程序(relocation object program) hello.o: 目标程序,二进制格式 链接阶段hello : 最终的可执行程序。

系统硬件组成

1. 总线

传输数据用,传送固定长度的字节(byte)块,称之为字(word),通常是4字节或者8字节。总线每次只传送1个word

2. I/O 设备

输入设备和输出设备,键盘、鼠标、硬盘等。每个I/O设备通过一个控制器或者适配器I/O总线相连。 控制器通常位于I/O设备本身或者印刷电路板上的芯片组。 适配器则是插在主板上的卡(例如显卡)

3. 主存

主存是由一组动态随机存取存储器(DRAM)芯片组成,逻辑上DRAM是线性的字节数组,每个字节拥有唯一的地址(数组索引),地址从零开始。

需要说明的是,通常机器指令都有不同数量的字节byte构成,例如,对于运行在Linux的IA32机器上的C程序,short需要2字节,int、float、和long需要4字节,double需要8字节。

4. 处理器

中央处理器CPU的核心是一个word字长的存储设备(或寄存器),称为程序计数器(PC)。任何时候PC都指向主存中某条机器指令(即内含机器指令的地址)。

执行过程是:CPU从PC所指向的程序那里读取指令,解释指令上的位,执行该指令的简单操作,更新PC执行下一条指令(不一定在物理内存中相连)。

指令围绕主存、寄存器文件(register file)和算术/逻辑单元(ALU)进行。

寄存器文件: 小的存储设备,有一些1字长的寄存器组成(注意,一个寄存器1字长,多个寄存器构成寄存器文件),每个寄存器都有唯一的名字。

例如以下简单操作:

加载:把一个byte或一个word从主存复制到寄存器,覆盖寄存器原有内容。 存储:把一个byte或一个word从寄存器复制到主存的某个位置,覆盖主存原有内容。 操作:把两个寄存器的内容复制到ALU,ALU对这两个word(注意是word)作算术操作,结果存放到一个寄存器,覆盖其原有内容。 跳转:从指令本身抽取一个word,将这个word复制到程序计算器(PC)中,以覆盖PC原来的值。

补充:1个word字长通常是32位或者64位,程序计数器PC就是用来指位置的。

以上的简单操作只是示意性的,实际过程现代处理器使用非常复杂的机制优化加速。

hello的执行过程

我们首先在键盘上输入:$ ./hello, shell会检测我们的输入,将其存于内存并显示在屏幕上,当输入回车符时,shell知道我们结束命令输入。 其后shell执行一系列指令加载可执行文件hello,将hello中的代码和数据从磁盘复制到内存。(利用DMA,直接存储器存取技术,可以不经过CPU直接将数据从磁盘经过I/O桥内存

当文件hello的代码和数据完全加载到主存后,处理器开始执行hello程序的main程序的机器指令。指令将”hello,world\n”字符串中的字节byte从主存复制到寄存器文件,再从寄存器文件复制到显示设备。

高速缓存至关重要

以上过程揭示了一个问题,就是系统花费大量时间将信息从一个地方挪到另一个地方。因此,系统设计的一个目标是使这些复制操作尽快完成

又由于对于存储设备,储量越大,越慢且造价越便宜,例如硬盘。 储量越小,越快、造价越贵,例如寄存器。

基于以上两点,考虑一个存储器层级结构金字塔: 上一级作为下一级的高速缓存。

大致结构是: 寄存器 <- L1缓存 <- L2缓存 <- L3缓存 <- 内存 <- 磁盘 <- 远程存储

存储器层次结构

高速缓存的位置如下图所示

高速缓存存储器

操作系统管理硬件

操作系统作用有二,一是防止应用程序恶意破环,而是在应用与各种硬件之间建立统一的桥梁。

操作系统有几个基本的抽象概念: 进程虚拟存储器文件

文件: 1. I/O设备 的抽象 虚拟存储器:1. I/O设备 2. 主存 的抽象 进程: 1. I/O设备 2. 主存 3. 处理器 的抽象

进程

进程是操作系统对一个正在运行的程序的抽象,一个系统上可以同时运行多个进程,但好像每个进程能独占地使用硬件。

CPU看上去再并发执行多个进程,实际上进程数通常多于CPU核心数,因此,这只是假象,这依靠上下文切换来实现。

操作系统保持跟踪进程运行所需的所有状态信息,这就是上下文。当操作系统决定把控制权从当前进程转移给某个新进程,就会进行上下文切换

线程

尽管通常我们认为一个进程只有一个控制流,但现代操作系统中,一个进程实际由多个称为线程的执行单元组成。每个线程都运行在进程的上下文中,共享同样的代码和全局数据

虚拟存储器

虚拟存储器是一个抽象概念,给进程提供一种独占内存的假象。

每个进程看到的是一致的存储器,称为虚拟地址空间

每个进程看到的虚拟地址空间都准确划分为不同区域:

程序代码和数据

程序代码以及静态的数据,分为 [1. 只读代码和数据] [2. 可读写数据]

用于 [3. 运行时动态分配的数据],malloc free等的数据

共享库

[4. 共享库] 例如存放C标准库和数学库的代码和数据区域

[5 用户栈] 用于实现函数调用,位于用户虚拟地址空间顶部(上面就是用户代码不可见的区域了,包括操作系统的代码和数据)

内核虚拟存储器

[6 内核虚拟存储器] 内核总是驻留在内存中,是操作系统的一部分,位于地址空间顶部。不允许应用程序读写。

文件

文件就是字节序列,仅此而已。每个I/O设备都可看做是文件。包括磁盘、键盘、显示器、网络等。

为应用程序提供统一的视角看待不同I/O设备。

系统之间利用网络通信

重要主题

并发和并行

并发(concurrency)指一个同时具有多个活动的系统。例如同时运行两个程序。

并行(parallelism)指用并发来使得一个系统运行得更快。

并行可以在计算机系统不同抽象层次上运用,主要包括:

1. 线程级并发

在较高层级抽象上,现代处理器允许一个进程有多个线程同时运行。

什么是超线程

使用线程,可以在一个进程中执行多个控制流。即超线程(hyperthreading)或者说同时多线程。它涉及某些硬件有多个备份。例如备份PC和register file,而其他硬件部分只有一份,比如执行浮点计算的单元。超线程处理器在单个时钟周期基础上决定执行哪个线程,这使得CPU更好地利用资源。

意识就是说,我现在有两个任务排队需要电脑,但是只有一个电脑(指的是ALU,例如浮点计算)。那么超线程就决定那个任务可以用“电脑”。发现一个任务此时还没准备好,那么就执行另一个。这样时间就节省了,更好地利用了CPU资源。

每个任务管理员(PC)不同,所以需要多个PC(程序计数器) 每个任务都要保持自己的临时数据,因此也需要多个register file) 但是“电脑”(ALU)可以通用。

2. 指令级并行

在较低层级抽象上,现代处理器可以同时执行多条指令

通过流水线(pipeling)技术,现代处理器每次输入多条指令,像流水线一样,将每条指令组织成不同子步骤。

打个比方,假设要炒一盘菜需要1个小时,但发现炒每盘菜都要先洗菜,那么把洗菜步骤单独拿出来,放好几个盆,几个菜放在各个盆子里一起洗,洗完再进入下一个步骤,这样分配到每个菜上的时间就省下来了,也就小于1小时。处理器使用非常聪明的技巧同时处理多达100条的指令

本来每条指令大约要20,甚至更多时钟周期,通过流水线的精心设计,实际分配到每条指令可能半个时钟周期。如果一个指令比时钟周期还快,成为超标量(superscaler)处理器。

3. 单指令、多数据并行SIMD

在最低层次上,现代处理器拥有特殊的硬件,允许一条指令产生多个可并行执行的操作。这种方式成为单指令、多数据流SIMD并行。例如。较新的Intel或AMD可以同时对4个float做加法运算。

个人理解,貌似就是ALU单元比较大,同时处理了4个32位,也就是128bit的数据。

如何实现? 有些编译器试图从C程序中抽取SIMD并行。但更可靠的方法是使用编译器支持的特殊向量数据类型来写程序(这样即使编译器不能自动SIMD并行也有效)。

-----EOF-----

Categories: csapp Tags: computer system