第一章:计算机系统漫游

一、计算机系统

    计算机系统是由硬件和系统软件组成的,他们共同工作来运行应用程序。在本章将借用经典的hello.c程序来说明在执行hello.c时,系统发生了什么和为什么会这样。

    093221_cgdn_3052654.png

二、信息就是位+上下文

    hello程序的生命周期是从一个源程序开始的,即程序员利用编辑器创建并保存的文本文件,文件名是hello.c。源程序实际上就是一个由0和1组成的位序列,8个位组成一组,为一个字节。下面是hello.c的ascii文本表示。

    094214_6t46_3052654.png

    上图说明了什么呢?即系统中所有的信息--包括磁盘文件、存储器中的程序、存储中存放的数据以及网络上传输的数据,都是由一串位来表示的。那么如何区分不同的数据对象呢?根据读到数据对象时的上下文来区分不同的数据。比如在不同的上下文中,一个同样的字节序列可能表示一个整数,浮点数,字符串或者机器指令。

三 、编译系统

    3.1 hello.c文件是使用高级语言C来写的,计算机并不认识它,那么计算机怎样才可以运行hello程序呢?

    为了可以在系统上运行hello.c程序,每条c语句都必须转换为机器认识的语言,那就转换为一系列的低级机器语言指令了。然后这些指令按照可执行目标程序的格式打好包,并以二进制文件的形式存放起来。目标程序也称为可执行目标文件。之后我们运行的就是一条条机器语言指令组成的可执行目标文件了。

    3.2 编译系统的四道程序 

    将高级语言转换为低级机器语言指令的动作称为编译,这是由编译器驱动程序来完成的。编译动作会经过四个步骤。预处理器、编译器、汇编器和链接器这四道程序一起构成了编译系统(complilation system)。如下图所示:

    161233_YwW8_3052654.png

    预处理阶段:修改源程序,将文件头包含的库文件源程序写入源程序,变成一个新的源程序。

        161645_fK9L_3052654.png

    编译器阶段:将预处理后的文件翻译为一个包含汇编语言程序的后缀名为s的文本文件。

   162039_YbE3_3052654.png

    汇编阶段:将包含有汇编语言程序的文件翻译为机器语言指令,并将这些指令打包成可重定位目标程序格式的二进制文件。

    162403_GESE_3052654.png

    链接阶段:将调用了别的目标文件的函数的目标文件合并到汇编阶段得到的可重定位目标程序中。然后得到可执行文件。编译工作到此完成。

     162735_KChP_3052654.png

    3.3 了解编译系统是如何工作的好处

        a. 优化程序性能

        b. 理解链接时出现的错误

        c. 避免安全漏洞

            164745_2etu_3052654.png

 

四、处理器加载并解释存储器中的指令

    经过以上步骤,hello.c文件已经被编译系统翻译成可执行目标文件hello.并存储在磁盘上。想要在Unix系统上运行该可执行文件,我们将它的文件名输入到称为外壳(shell)的应用程序中:

   151608_qEZx_3052654.png

    外壳是一个命令行解释器,如果输入的不是内置的外壳命令,那么外壳文件就会假设这是一个可执行文件,它将加载并运行这个文件。通过小小的一行命令,就能将hello,world的显示出来的,这时候计算机发生了什么?让我们先了解下计算机系统硬件的组成。

    4.1 计算机硬件组成

        下图是Intel Pentium系统产品系列的模型

   153546_Sx3H_3052654.png

   1. 总线----信息传输的载体

        贯穿整个系统的是一组电子管道,称做总线,它携带信息字节并负责在各个部件间传递。通常总线都是传送固定长度的字节块,也就是字,如32位(4个字节),64位(8个字节),字节是一个基本的系统参数。

    2. I/O 设备----系统与外部世界的联系通道。

        上图中包括了四个4个I/O设备:输入设备鼠标和键盘,输出设备显示器,长期存储数据和程序的磁盘驱动器(磁盘)。

          每个I/O设备都通过一个控制器或者适配器与I/O总线相连。控制器和适配器的区别在于它们的封装方式的不同。控制器是置于I/O本身的或者系统的主印制电路板(主板)上的芯片组,而适配器则是一块插在主板插槽上的卡。不管怎样,控制器和适配器都是负责在I/O总线和I/O设备之间传递信息。

    3. 主存----处理器处理执行程序时,存放程序和程序处理的数据的容器。

        主存是一个临时的存储设备。从物理上来说,主存是由一组动态随机存取存储器(DRAM)芯片组成的。逻辑上来说,存储器是一个线性的数组,每个字节都有其唯一的地址(即数组索引),这些地址是从零开始的。一般来说,组成程序的每条机器指令都是由不同数量的字节构成的。

    4.处理器----执行者

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

        从系统通电开始,直到系统断电,处理器一直在不断地执行PC指向的指令,再更新PC,使其指向下一条指令,然后再执行。处理器好像就是按照这样一个简单的模型执行。然而这样简单的操作并不多。

        处理器执行的时候通常是围绕着主存,寄存器文件(register file)和算术/逻辑单元(ALU)进行的。寄存器文件是一个小的存储设备,由一些一字长的寄存器组成,每一个寄存器都有唯一的名字。ALU负责计算新的数据和地址值。这样的操作可能会执行以下步骤:

  •         加载:把一个字节或者一个字(几个字节)从主存复制到寄存器,以覆盖寄存器原来的内容。
  •         存储:把一个字节或者一个字从寄存器复制到主存的某个位置,以覆盖这个位置原来的内容。
  •         操作:把两个寄存器的内容复制到ALU,ALU对这两个字做算术操作,并将结果存放到一个寄         存器中,以覆盖原来寄存器中的内容。
  •        跳转:从指令本身中抽取一个字,并将这个字复制到程序计数器(PC)中,以覆盖原来的值。

        处理器 看上去只是它的指令集结构的简单实现,但是实际上现代处理器使用了非常复杂的机制来加速程序的执行。处理器的指令集结构和微体系结构的区别:指令集结构描述的是每条机器代码指令的效果;二微体系结构描述的是处理器实际上是如何实现的。

4.2 运行hello程序---这时候经过哪些步骤

    开始时,外壳程序执行它的指令,等待我们输入一个命令。当我们在键盘上输入字符串"./hello"后,外壳程序将字符逐一读入寄存器,再把它放到主存中,如下图所示:

 172551_9LZY_3052654.png

    当我们在键盘上敲下回车键时,外壳程序就知道我们已经结束了命令的输入。然后外壳执行一系列指令来加载可执行的hello文件,将hello目标文件中的代码和数据从磁盘复制到主存。

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

 173100_f1fI_3052654.png

 

 

 

 五、高速缓存至关重要

        一个hello程序的简单示例揭示了一个重要的问题,即系统花费了大量的时间将程序从一个地方复制到另一个地方。这些不断复制的过程就是开销,减缓了程序"真正"的工作时间。因此,系统设计者的一个重要的目标就是使这些复制尽快完成。

        根据机械原理,较大的存储设备要比较小的运行得慢,而快速设备的造价要高于同类的低速设备:处理器从磁盘上读取一个字的时间开销要比从主存中读取的开销大1000万倍。处理器从寄存器文件中读取数据比从主存中读取数据几乎快1000倍。更麻烦的是,处理器和主存的速度差距越来越大。那么该怎么办呢?

        根据这种差距,系统设计者在两者之间添加了一种更小、更快的存储设备,即高速缓存存储器(高速缓存),作为暂时的集结区域,用来存放处理器近期可能会需要的信息。如下图所示:

   090133_3hUY_3052654.png

        位于处理器芯片上的L1高速缓存容量可以达到数万字节,访问速度集合和寄存器文件一样快。一个容量为数十万到百万字节的L2高速缓存通过一条特殊的总线连接到处理器。进程访问L2的速度要比L1要多5倍。

        L1和L2高速缓存是使用一种静态随机访问存储器(SRAM)的硬件实现的。现在,通常有L3的高速缓存。

        高速缓存的局部性原理:

            时间局部性:最近被访问的内存内容(指令或数据)很快还会被访问

            空间局部性:靠近当前正在被访问内存的内存内容很快也会被访问。

            系统通过局部性原理可以获得很大的存储空间和很快的速度,因为通过让高速缓存存放可能经常访问的数据,那么以前在存储器中的操作就可以在高速缓存中完成了。

    六、存储设备的层次结构

        机器的机械原理告诉我们:空间越大的存储设备越慢,速度越快的造价越大。而主存和cpu之间的速度差距也越来越大,那么在两者之间添加更小、更快的设备(高速缓存)是至关重要的了。存储设备的层次图。

        093242_ezBa_3052654.png

 

           上图结构的主要思想就是上一层作为低一层的高速缓存。因此,L0是L1的高速缓存,L1是L2的高速缓存,L2是L3的高速缓存,以此类推。数据被使用的可能性越大,就越能接近寄存器的层次。

七、操作系统管理硬件

        回到hello程序的运行的例子,当外壳加载和运行hello程序,及hello程序输出自己的消息时,外壳和程序都没有直接访问键盘、显示器、磁盘或主存;它们依靠的是操作系统提供的服务。操作系统可以看做是横插在程序和硬件之间的一层软件,如图所示:

  100040_v6jE_3052654.png

        操作系统的作用:

  • 防止硬件被失控的应用程序滥用
  • 向应用程序提供简单一致的机制控制复杂而又通常大相庭径的低级硬件设备。为应用程序提供统一的接口。

        操作系统实现以上两个作用的基石: 操作系统通过几个基本的抽象概念(进程,虚拟存储器和文件)来实现上述两个作用。如下图所示:文件是对I/O设备的抽象表示,虚拟存储器是对主存和磁盘I/O设备的抽象表示,进程则是对处理器、主存和I/O设备的抽象表示。

     100809_hxDv_3052654.png

    7.1 进程

        像hello这样的程序在现代系统上运行时,操作系统会提供一种假象,就好像系统上只有这个程序在运行,看上去只有这个程序在使用处理器、主存和I/O设备。处理器看上去就像在不间断地一条接一条地执行程序中的指令,即该程序的代码和数据是系统存储器中唯一的对象。这些假象是通过进程的概念来实现的,进程是计算机科学中最重要和最成功的概念之一。

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

        并发运行,一个进程和另一个进程交错的执行。在多数情况下,进程的数量远比cpu的数量要多的多,这时候就需要进行并发运行了。在传统系统中,单核cpu在一个时刻只能执行一个程序,而先进的多核系统则可以执行多个程序。无论如何,cpu在运行多个程序的时候都像是在并发的执行多个进程,这是通过进程间的切换来实现的,这就是上下文切换。

        上下文是指进程运行所需的所有状态信息。它包括许多的信息:如PC和寄存器文件的当前值及主存的内容。上下文切换的工作步骤:当操作系统决定要把控制权从当前进程转换到另一个进程时,1. 保存当前进程的上下文 ,2. 恢复新进程的上下文,3.将控制权传递到新的进程, 新进程就会从上次停止的地方开始。

         下图展示了hello程序运行场景的基本理念:

        105010_76sX_3052654.png

        1.开始进程A(外壳程序)等待用户输入

        2.输入完命令,准备执行hello程序时,外壳通过调用一个专门的函数(系统调用),执行请求,系统会将控制权传递给操作系统。操作系统保存外壳进程的上下文,然后将控制权传递给新的hello进程。

        3.hello进程执行完,操作系统恢复外壳进程的上下文,并等待下个命令的输入。

    7.2 线程

        在现代系统中,一个进程实际上可以由多个线程组成,每个线程都运行咋线程的上下文中,并共享同样的代码和全局数据。多线程之间比多进程间共享数据更容易。

    7.3 虚拟存储器---为进程工作

        虚拟存储器是一个抽象的概念,它为每个进程提供了一个假象,即每个进程都在独占地使用主存。每个进程看到的是一直的存储器,称为虚拟地址空间。下图是Linux进程的虚拟地址空间。在Linux系统中,地址空间最上面的区域是操作系统中的代码和数据保留的,这对所有进程都是一样的。地址空间的底部存放用户进程定义的代码和数据。下图的地址是从下往上增大的。

        110811_4S4i_3052654.png

    虚拟地址空间由不同的区构成的,以下是各个区的简单介绍:

        程序代码和数据区:对于所有的进程来说,代码是从同一固定地址开始的,紧接着的是和C全局变量相对应的数据位置。代码和数据区是直接按照可执行目标文件的内容初始化的。

        堆:代码和数据区之后的就是运行时堆,代码和数据区是在进程一开始时就被规定了大小。与此不同的是当调用malloc和free这样的C标准库函数时,堆可以在运行时动态地扩展和收缩。(存放的是数据吗)

        共享库:大约在地址空间的中间部分是一块用来存放C标准库和数学库这样共享库的代码和数据的区域。

        栈:位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。特别是每次我们调用一个函数时,栈就会增长;从一个函数返回时,栈就会收缩。(存放的是地址吗)

        内核虚拟存储器:内核总是驻留在内存中的,是操作系统的一部分。地址空间顶部的区域是为内核保留的,不允许应用程序读写这个区域的内容或者直接调用内核定义的函数。

    7.4 文件

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

        文件这个简单而精致的概念向应用程序提供了一个统一的视角,来看待系统中可能含有的所有格式的各式各样的I/O设备。不用关心底层的具体I/O设备了。。

八、系统间利用网络通信----互联网

        上述的系统只是一个孤单的硬件和软件的集合体,要是系统间能够相互沟通交流那就很爽了。现代系统经常通过网络和其他系统连接到一起。

         网络也是一种I/O设备,如下图所示:当系统从主存将一串字节复制到网络适配器时,数据流经过网络到达另一台机器;相似地,系统也可以读取从其他机器发送来的数据,并把数据复制到主存中。

        114646_yEsr_3052654.png

 

         随着互联网的出现,将一台主机的信息复制到另一台主机已经成为计算机系统最重要的用途之一了。如:电子邮件、即时通信、万维网、FTP和telnet这样的应用都是基于网络复制信息的功能。

        假如我们想远程执行hello程序,该通过哪些步骤呢?我们可以通过telnet应用在一个远程主机上运行hello程序。假设使用本地主机上的telnet客户端连接远程主机上的telnet服务器,在我们登录到远程主机并运行外壳后,远端的外壳就在等待输入的命令。以下是基本步骤:

115304_yXfE_3052654.png

        1. 用户输入hello

        2. 客户端发送hello到服务器

        3. 服务器外壳程序执行hello程序,并将结果返回给服务器

        4.服务器把结果发送给客户端。

 

 

    

 

   

        

 

 

转载于:https://my.oschina.net/u/3052654/blog/828182

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值