作者:B站搜“九曲阑干”
视频链接:https://www.bilibili.com/video/BV1cD4y1D7uR?p=1

1、编译系统

  首先,我们看一下这个hello 程序:

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

  保存得到一个后缀名为.c的文件 hello.c ,至于是用Vim还是Emacs,或是其他编辑器,全凭个人喜好。

  接下来,通过一条简单的命令即可完成对源代码的编译,生成可执行程序 hello

linux> gcc -o hello hell.c

  这个过程虽然是通过一条命令完成的,然而实际上编译系统的处理过程却是非常复杂的,大致可以分为四个阶段,分别为预处理、编译、汇编以及链接。

1.1 预处理

  首先我们来看一下第一阶段:预处理。

  预处理器会根据以#开头的代码,来修改原始程序。例如hello程序中引入了头文件 stdio.h ,预处理器会读取该头文件中的内容,将其中的内容直接插入到源程序中,结果就得到了另外一个 C 程序,这个经过预处理器处理后得到的文件通常以 .i 结尾,说白了就是, hello.c 经过预处理器后得到 hello.i,这个 hello.i 仍旧是一个文本文件。

1.2 编译

  第二个阶段是编译阶段。

  编译器将 hello.i 文件翻译成 hello.s 文件,这一过程我们称为编译。

  其中编译这一阶段包括词法分析、语法分析、语义分析、中间代码生成以及优化等等一系列的中间操作。感兴趣的可以了解一下《编译原理》。

1.3 汇编

  第三个阶段是汇编。

  汇编器根据指令集将汇编程序 hello.s 翻译成机器指令,并且把这一系列的机器指令按照固定的规则进行打包,得到可重定位目标文件 hello.o 。此时 hello.o 虽然是一个二进制的文件,但是还不能执行,还要经历最后一个阶段:链接。

1.4 链接

  在 hello 这个程序中,我们调用了printf 函数,这个函数是标准C库中的一个函数,每一个C语言的编译器都会提供。通俗的讲就是当你调用 printf 这个函数时,编译器就知道你要在屏幕上打印输出内容,它会将这行代码翻译成计算机可以理解的指令。

  这个printf 函数在是在名为 printf.o 的文件中,这个文件呢是一个提前编译好的目标文件,链接器(ld)负责把 hello.oprintf.o 进行合并,当然这个合并是要遵循一定规则的。正是因为链接器要对 hello.oprintf.o 的进行调整,所以 hello.o 才会被称之为可重定位目标文件。最终经过链接阶段可以得到可执行目标文件 hello

  此时,得到的 hello 就可以被加载到内存中执行了。

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

  1、理解编译系统可以优化程序的性能

  现代编译器是非常成熟的工具,通常可以生成很好的代码,作为一个程序员,我们没有必要为了写出高效的代码,而去研究编译器的内部是如何工作的,但是,我们还是需要对机器执行的代码有一个基本的了解,这样我们就知道编译器把不同的C代码转换成的机器代码是什么。

  我们在写代码的时候可能会有这样的困惑,或者面试中会被问到以下类似的问题:

  例如:一个switch语句是不是要比一连串的if-else要高效的多?个函数调用的开销有多大?while循环比for循环更高效么?

  2、理解编译系统可以帮助我们理解链接过程中出现的错误

  如果所有的程序都像 helloworld 一样简单,那的确没有必要去理解编译系统,但是当你试图去构建大型程序的时候,往往涉及到各种函数库的调用,根据以往的经验,一些奇奇怪怪的错误往往都是与链接器有关的。

  例如:静态变量和全局变量的区别是什么?静态库和动态库的区别是什么?

  更严重的是,还有一些链接错误直到程序运行的时候才会出现。

  3、避免安全漏洞。

  多年以来,缓冲区溢出(buffer overflow)是导致互联网安全漏洞的主要原因,如何避免写出的代码存在安全漏洞,第一步就是要理解数据和控制信息在程序栈上是如何存储的,了解不严谨不规范的书写方式会引起什么样的后果。

3、系统的硬件组成

  在介绍执行文件的流程之前,得了解计算机的组成结构。

3.1 CPU

  中央处理单元(Central Processing Unit , CPU),也称处理器。实际上CPU的内部结构是非常复杂的,此处为了介绍hello 程序的运行,采用简化的框图来大概示意一下整个CPU的结构。

  首先我们看一个这个 Program Count ,简称PC。很多中文资料将 PC ( Program Count )翻译成程序计数器,个人认为,这种直译的方式并没有很好表述 PC 的真实含义。这个PC 实质上是一个大小为一个字的存储区域。对于32位的机器,一个字是4个字节;对于64位的机器,一个字就是8个字节。说白了,PC就是一个4字节或是8字节的存储空间,里面存放的是某一条指令的地址。从系统上电的那一瞬间,直到系统断电,处理器就不断地在执行 PC 指向的指令,然后更新 PC,使其指向下一条要执行的指令(注意:这个下一条指令与刚刚执行的指令不一定是相邻的)

  接下来,我们看一下寄存器文件,它就是 CPU 内部的一个存储设备,寄存器文件是由一些单字长的寄存器构成,每个寄存器都有自己唯一的名字,通俗的讲,寄存器可以理解为一个临时存放数据的空间。例如我们计算两个变量 a+b 的和,处理器从内存中读取 a 的值暂存在寄存器 X 中,读取 B 的值暂存在寄存器 Y 中,这个操作会覆盖寄存器中原来的数值,处理器完成加载的操作后,ALU(Arithmatic/logic Unit)会从复制寄存器 X 和 Y 中保存的数值,然后进行算术运算,得到的结果会保存到寄存器 X 或者寄存器 Y 中,此时寄存器中原来的数值会被新的数值覆盖。

3.2 内存

  主存,也称为内存,处理器在执行程序时,内存主要存放程序指令以及数据。从物理上讲,内存是由随机动态存储器芯片组成;从逻辑上讲,内存可以看成一个从零开始的大数组,每个字节都有相应地址。

  内存和处理器之间通过总线来进行数据传递。实际上,总线贯穿了整个计算机系统,它负责将信息从一个部件传递到另外一个部件。通常总线被设计成传送固定长度的字节块,也就是字(word),至于这个字到底是多少个字节,各个系统中是不一样的,32位的机器,一个字长是4个字节;而64位的机器,一个字长是8个字节。

3.3 输入输出设备

  除了处理器,内存以及总线,计算机系统还包含了各种输入输出设备,例如键盘、鼠标、显示器以及磁盘等等。每一个输入输出设备都通过一个控制器或者适配器与 IO 总线相连。

  控制器与适配器主要区别是在于它们的封装方式,无论是控制器还是适配器,它们的功能都是在 IO 设备与 IO 总线之间传递数据。

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

4.1 运行hello程序

  hello.c 经过编译系统得到可执行目标文件 hello,此时可执行目标文件 hello 已经存放在系统的磁盘上,那么,如何运行这个可执行文件呢?

  在 linux 系统上运行可执行程序:打开一个 shell 程序,然后在 shell 中输入相应可执行程序的文件名。

linux> ./hello

  shell 是一个命令解释程序,它输出一个提示符>来等待一个命令行的输入,然后执行这个命令,如果该命令行的第一个单词不是内置的 shell 命令,那么 shell 就会假设这是一个可执行文件的名字,对这个文件进行加载并运行。

  在这个例子中,shell 加载并且运行 hello 程序,屏幕上显示 hello,world 内容,hello 程序运行结束并退出,shell 继续等待下一个命令的输入。

4.2 程序执行流程

  首先我们通过键盘输入"./hello" 的字符串,shell 程序会将输入的字符逐一读入寄存器,处理器会把 hello 这个字符串放入内存中。

  当我们完成输入,按下回车键时,shell 程序就知道我们已经完成了命令的输入,然后执行一系列的指令来来加载可执行文件 hello ,这些指令将 hello 中的数据和代码从磁盘复制到内存。数据就是我们要显示输出的 "hello , world\n" ,这个复制的过程将利用DMA(Direct Memory Access)技术,数据可以不经过处理器,从磁盘直接到达内存。当可执行文件 hello 中的代码和数据被加载到内存中,处理器就开始执行 main 函数中的代码,main 函数非常简单,只有一个打印功能。

  CPU会将 "hello , world\n" 这个字符串从内存复制到寄存器文件。然后再从寄存器文件复制到显示设备,最终hello , world显示在屏幕上。

  从 hello 程序执行的过程来看,系统即使执行如此简单的程序,数据信息仍旧需要在磁盘、内存、处理器以及 IO 设备之间进行搬运。

  数据从一个地方搬运到另外一个地方需要花费时间,系统设计人员的一个主要任务就是缩短信息搬运所花费的时间。

5、存储设备

  通常情况下,大容量的存储设备的存取速度要比小容量的慢,运行速度更快的设备的价格相对于低速设备要更贵。例如:在一个系统上,磁盘的容量一般为 TB 级,内存的容量一般为 GB 级,磁盘的容量大概是内存的 1000 倍。

5.1 高速缓存至关重要

  对于处理器而言,从磁盘上读取一个字所花费的时间开销比从内存中读取的开销大1000万倍。寄存器文件的只能存储几百个字节的信息,而内存的可以存放几十亿的字节信息(GB级),从寄存器文件读取数据比从内存读取差不多要快 100 倍。

  随着半导体技术的发展,处理器与内存之间的差距还在持续增大,针对处理器和内存之间的差异,系统设计人员在寄存器文件和内存之间引入了高速缓存(cache),比较新的,处理能力比较强的处理器,一般有三级高速缓存,分别为 L1 cache , L2 cache 以及L3 cache。

  L1 cache 的访问速度与访问寄存器文件几乎一样快,容量大小为数万字节(KB 级别);L2 cache 的访问速度是 L1 cache 的五分之一,容量大小为数十万到数百万字节之间;L3 cache 的容量更大,同样访问速度与 L2 cache 相比也更慢。

5.2 存储设备形成层次结构

  整个计算机系统的信息存储可以用一个层次结构来表示。

  从这个层次结构来看,从上到下,设备的访问速度越来越慢,容量越来越大,每字节的造价也越来越便宜。这个层次结构的主要思想就是:上一层存储设备是下一层存储设备的高速缓存。例如,寄存器文件就是 L1 的高速缓存,L1 是 L2 的高速缓存,内存是磁盘的高速缓存等等。

  程序员深入理解计算机系统中的高速缓存,可以利用存储器的层次结构来提升程序的性能

6、操作系统管理硬件

6.1 操作系统的作用

  无论是 shell 程序还是 hello 程序都没有直接访问键盘、显示器、磁盘这些硬件设备,真正操挫硬件的是操作系统,我们可以把操作系统看成是应用程序和硬件之间的中间层,所有的应用程序对硬件的操作必须通过操作系统来完成。

  这样设计的目的主要有两个:一是防止硬件被失控的应用程序滥用;另外一个目的就是操作系统提供统一的机制来控制这些复杂的底层硬件。

  为了实现上述的功能,操作系统引人了几个抽象的概念。例如:文件是对 IO 设备的抽象;虚拟内存是对内存和磁盘 IO 的抽象;进程是对处理器、内存以及 IO 设备的抽象。

6.2 进程

  假设示例场景中只有两个并发的进程:shell 进程和 hello 进程。

  最开始的时候,只有 shell 进程在运行,即 shell 在等待命合行的输入。

  当我们通过 shell 进程加载 hello 进程时,shell 进程通过系统调用来执行我们的请求,系统调用会将控制权从 shell 进程传递给操作系统,操作系统保存 shell 进程的上下文,然后创建一个新的 hello 进程及其上下文,然后将控制权转交给新的 hello 进程。

  Hello进程执行完之后,操作系统就会恢复 shell 进程的上下文,并将控制权交给 shell 进程,之后 shell 进程继续等待下一个命令的输入。

  操作系统会跟踪进程运行所需要的所有状态信息,这种状态,称为上下文(Context)。例如当前 PC 和寄存器的值,以及内存中的内容等等。

  现代操作系统中,一个进程实际上由多个线程组成,每个线程都运行在进程的上下文中,共享代码和数据。由于网络服务器对并行处理的需求,线程成为越来越重要的编程模型

6.3 虚拟内存

  操作系统为每个进程提供了一个假象,就是每个进程都在独自占用整个内存空间,每个进程看到的内存都是一样的,我们称之为虚拟地址空间。

  下图为 Linux 的虚拟地址空间,从下往上看,地址是增大的。最下面是 0 地址。

  第一个区域是用来存放程序的代码和数据的,这个区域的内容是从可执行目标文件中加载而来的,例如我们多次提到的 hello 程序。对所有的进程来讲,代码都是从固定的地址开始。至于这个读写数据区域放的是什么数据呢?例如在C语言中,全局变量就是存放在这个区域

  顺着地址增大的方向,继续往上看就是堆(heap),学过C语言的同学应该用过 malloc 函数,程序中malloc所申请的内存空间就在这个堆中。程序的代码和数据区在程序一开始的时候就被指定了大小,但是堆可以在运行时动态的扩展和收缩。

  接下来,就是共享库的存放区域。这个区域主要存放像C语言的标准库和数学库这种共享库的代码和数据,例如 hello 程序中的 printf 函数就是存放在这里。

  继续往上看,这个区域称为用户栈(user stack),我们在写程序的时候都使用过函数调用,实际上函数调用的本质就是压栈。这句话的意思是:每一次当程序进行函数调用的时候,栈就会增长,函数执行完毕返回时,栈就会收缩。需要注意的是∶栈的增长方向是从高地址到低地址

  最后,我们看一下地址空间的最顶部的区域,这个区域是为内核保留的区域,应用程序代码不能读写这个区域的数据,也不能直接调用内核中定义的函数,也就是说,这个区域对应用程序是不可见的。

6.4 文件

  Linux 系统的哲学思想是:一切皆为文件。

  所有的 IO 设备,包括键盘,磁盘,显示器,甚至网络,这些都可以看成文件,系统中所有的输人和输出都可以通过读写文件来完成。

  虽然文件的概念非常简单,但却非常强大。例如︰当程序员需要处理读写磁盘上的文件时,他们不需要了解具体的磁盘技术,同一个程序,可以在不同磁盘技术上的不同系统上运行

7、系统之间利用网络通信

  系统来看,网络也可以视为一个 IO 设备。

  随着互联网的发展,从一台计算机发送消息到另外一台计算机已经成为非常普遍的应用。《深入理解计算机系统》中讲述了如何使用本地计算机上的 telnet 客户端连接远程主机上的 telnet 服务器。

  由于 telnet 的安全性问题,目前 ssh 的连接方式的更加普遍。当我们在 ssh 的客户端中输人 hello 字符串并且敲下回车之后,客户端的软件就会通过网络将字符串发送到 ssh 服务端,ssh 服务端从网络端接收到这个字符串以后,会将这个字符串传递给远程主机上的 shell 程序,然后 shell 负责 hello 程序的加载,运行结果返回给 ssh 的服务端,最后 ssh 的服务端通过网络将程序的运行结果发送给 ssh 的客户端,ssh 客户端在屏幕上显示运行结果。

  这类客户端与服务端之间交互的应用是非常普遍的。

8、系统加速

8.1 加速比

  首先介绍一下阿姆达尔定律: 当我们对系统的某一部分进行加速时,被加速部分的重要性和加速程度是影响整体系统性能的关键因素。

  在加速前,假设一个应用程序的执行所需要的全部时间用 Told 来表示,为了方便描述,我们可以笼统的将这个程序分为两部分,部分是不可加速的,另外一部分是可加速的。其中可以加速的部分执行花费的时间为 αTold ,不可加速部分的执行时间为 Told - αTold

  程序经过优化后,可加速部分性能提升比例为 k ,那么经过加速后,这个可加速部分所花费的时间就是 αToldk\frac{αT~old~}{k}kαT old ​ ,因此我们可以计算出,程序经过加速后所花费的时间,这里用 Tnew 来表示。

  Tnew = (1-α)Told + αToldk\frac{αT~old~}{k}kαT old ​ = Told[(1-α) + αk\frac{α}{k}kα​]

  此时,我们可以得到加速比 S = ToldTnew\frac{T~old~}{T~new~}T new T old ​ = 1(1−α)+α/k\frac{1}{(1-α) + α/k}(1−α)+α/k1​

  当 α = 0.6 ,加速因子 k = 3。加速比 S = 1(1−0.6)+0.6/3\frac{1}{(1-0.6) + 0.6/3}(1−0.6)+0.6/31​ = 1.67

  考虑一个极限情况,k 趋向于无穷大,也就是我们可以把这 60% 的部分加速到几乎不花时间的程度,此时的加速比 S 可以简化成 11−α\frac{1}{1-α}1−α1​ ,那么整个系统的获得的净加速比也只有 2.5 。因此,在计算机的世界里,如果我们需要把系统的性能提高2倍或者更多,只有通过优化大部分的组件才能获得。

8.2 并发和并行

  如何获得更高的计算能力呢?可以通过以下三种途径:

  1.线程级并发
  2.指令级并行
  3.单指令多数据并行

8.2.1 线程级并发

  首先我们看一个多核处理器的组织结构,图中的处理器芯片具有四个 CPU 核心,由于篇幅限制,另外两个用省略号代替了。每个CPU核心都有自己的 Ll cache 和 L2 cache ,四个 CPU 核心共享 L3 cache,这 4 个 CPU 核心集成在一颗芯片上

  对于许多高性能的服务器芯片,单颗芯片集成的 CPU 数量高达几十个,甚至上百个。通过增加 CPU 的核心数,可以提高系统的性能。

  除此之外,还有一个技术就是超线程(hyperthreading),也称同时多线程。如果每个 CPU 核心可以执行两个线程,那么四个核心就可以并行的执行 8 个线程,那么,单个CPU核心是如何实现超线程的呢?

  在 CPU 内部,像程序计数器和寄存器文件这样的硬件部件有多个备份,而像浮点运算部件这个样的硬件还是只有一份,常规单线程处理器在做线程切换时,大概需要20000个时钟周期,而超线程处理器可以在单周期的基础上决定执行哪一个线程,这样一来,CPU可以更好地利用它的处理资源。当一个线程因为读取数据而进人等待状态时,CPU可以去执行另外一个线程,其中线程之间的切换只需要极少的时间代价.

8.2.2 指令级并行

  现代处理器可以同时执行多条指令的属性称为指佘级并行,每条指令从开始到结束大概需要20个时钟周期或者更多,但是处理器采用了非常多的技巧可以同时处理多达100条指命,因此,近几年的处理器可以保持每个周期2~4条指令的执行速率。

8.2.3 单指令多数据并行

  现代处理器拥有特殊的硬件部件,允许一条指令产生多个并行的操作,这种方式称为单指令多数据(Single Instruction Multiple Data)。SIMD的指令多是为了提高处理视频、以及声音这类数据的执行速度,比较新的Intel以及AMD的处理器都是支持SIMD指令加速。

9、虚拟机

  虚拟机是对整个计算机系统的抽象,包括操作系统、处理器以及程序。虽然虚拟机的思想在20世纪60年代就提出来了,但是最近才突显出优势来。

[计算机系统-01] 计算机系统漫游相关推荐

  1. 【深入理解计算机系统 01】计算机系统漫游

    01计算机系统漫游 本书的主要作用:帮助我们了解当我们在系统上执行hello程序时,系统发生了什么以及为什么会这样 #include <stdio.h>int main() {printf ...

  2. 3.2计算机系统教案,计算机系统教案.ppt

    计算机系统教案 计算机联锁系统概述 计算机联锁是车站信号控制系统的发展方向 计算机联锁系统的发展概况 第一章 计算机系统简介 非安全接口是全数不涉及行车安据的接口,均可采用通用的标准接口. 对传输安全 ...

  3. 深入理解计算机系统1——计算机系统漫游

    计算机系统漫游 计算机系统=硬件+系统软件构成的. 它们共同工作来运行应用程序.系统的具体实现方式随时间变化,但是系统的内在概念却没有改变. 深入了解计算机系统的组件如何影响程序的正确性和性能. == ...

  4. 计算机组成原理笔记|01计算机系统概论

    内容概要 计算机发展历程.计算机硬件的基本组成.硬件的工作原理.计算机系统的层次结构.计算机性能指标.相关习题 计算机发展历程 什么是计算机系统 计算机系统=硬件+软件 计算机性能的好坏取决于软.硬件 ...

  5. 01——计算机系统基础

    计算机系统基础知识 计算机系统基础 一.计算机系统的基本组成 1 计算机硬件系统 二.计算机的类型 三.计算机的组成和工作原理 1 计算机的组成 2 总线的基本概念 2.1 总线的定义与分类 3 系统 ...

  6. 计算机英语第4版音频,计算机英语会话01:计算机系统(1)(音频)

    Computer System 计算机系统 Dialogue 1 句型1 A: Hey,Bill. Can you tell what's wrong with my computer? 嘿,比尔.你 ...

  7. 王道论坛计算机系统01——操作系统的定义和特征

    1. 认识操作系统 没有装任何软件系统的称之为裸机,操作系统是对硬件的拓展(否则你需要对底层很熟悉,封装的思想) 操作系统是最接近硬件的系统软件 回顾一下: 2. 操作系统的特征 2.1 并发和并行 ...

  8. 专用计算机系统是,计算机系统是什么

    计算机系统由计算机硬件和软件两部分组成.硬件包括中央处理机.存储器和外部设备等:软件是计算机的运行程序和相应的文档.计算机系统具有接收和存储信息.按程序快速计算和判断并输出处理结果等功能.常见的系统有 ...

  9. 计算机系统计算机,计算机系统与计算机化系统的区别

    2010版GMP正文中提到"计算机"的地方一共三处,两处用的是计算机化,一处用的是"计算机".在GMP的术语条款,则只提到了"计算机化系统" ...

  10. 计算机系统基础 计算机系统的基本组成与基本功能

    基础知识点 1.1946年第一台通用电子计算机ENIAC诞生 2.冯.诺依曼结构: 组成:输入设备,输出设备,存储器,运算器,控制器 3.现代计算机结构模型: 组成 CPU中央处理器 PC程序计数器 ...

最新文章

  1. STL之hashtable源代码剖析
  2. 微信授权(Net Mvc)
  3. JavaScript编程知识
  4. ASP截取字符 截取字符之间的字符
  5. presentViewController 动画处理
  6. TikTok推出招聘服务、 沃尔玛收购虚ekit、开源圆桌、AI新创Poised|拟试穿公司ZeDecode the Week...
  7. 【英语学习】【WOTD】horticulture 释义/词源/示例
  8. [2018.10.18 T3] 小 G 的线段树
  9. 建模与仿真matlab论文,基于MATLAB的无线信道建模与仿真.doc
  10. 视频教程-EOS 入门实战-区块链
  11. 【GZH逸佳君】简约ppt模板-答辩ppt模板-毕业季ppt模板-说课ppt模板-ppt模板免费下载-ppt模板下载免费版
  12. 麒麟操作系统配置web服务器,银河麒麟服务器设置
  13. matlab画图不想显示x轴数据,Matlab - 情节; 修改X轴值而不修改图形
  14. php加波浪线不解析,给文字加波浪线效果
  15. 开通共享毛巾机小程序
  16. 智能合约vote部署
  17. android程序运行时总出现“Unfortunately,“程序名” has stopped” 的问题
  18. curl_exec函数
  19. 从来不敷面膜的人_从来不敷面膜的人会怎么样,突然敷了会怎样
  20. MOSFET(二):米勒效应

热门文章

  1. 网管维修必备工具_7手机维修必备工具
  2. 龙果学院从无到有构建亿级微服务秒杀系统
  3. 计算机网络课后作业习题1
  4. C. Spell Checker
  5. 开源KVM管理工具和平台
  6. wps中word转html乱码,wps转word格式乱码了怎么办
  7. 《暗时间》读书笔记与读后感
  8. 矩阵的转置例题MATLAB,MATLAB特殊矩阵以及矩阵转置
  9. 1stopt拟合步骤_1stopt快速公式拟合
  10. win10 网卡驱动正常搜索不到WiFi信号 解决方法