`

hello 程序执行背后的故事

阅读更多
        源文件 hello.c 的代码如下:
#include <stdio.h>

int main(){
    printf("hello world!");
    return 0;
}

        要运行该程序,需要编译器驱动程序将其翻译成可执行的目标文件 hello,这个过程可分为如下图所示的四个阶段,执行这四个阶段的程序(预处理器、编译器、汇编器和链接器)一起构成了编译系统。

        其中各阶段所做的事情如下。
        * 预处理阶段:预处理器 cpp 会根据以字符“#”开头的命令来修改 C 程序,比如“#include <stdio.h>”命令会告诉预处理器读取头文件 stdio.h 的内容,并把它直接插入到程序文本中,于是得到一个通常以“.i”作为扩展名的 C 程序。
        * 编译阶段:编译器 ccl 将文本文件 hello.i 翻译成汇编语言程序文本文件 hello.s。每条汇编语句都以一种标准的文本格式确切地描述了一条低级机器语言指令。
        * 汇编阶段:汇编器 as 将 hello.s 翻译成机器语言指令,并将这些指令打包成一种叫做可重定位目标程序(relocatable object program)的格式,最后将结果保存在二进制的目标文件 hello.o 中。
        * 链接阶段:链接器 ld 负责将使用到的其他目标文件(如 hello.c 中调用了 printf 函数,而该函数位于另一个目标文件 printf.o 中)合并到 hello.o 中,之后才能得到可执行的目标文件 hello,它可被系统加载到内存中执行。
        翻译后的可执行文件 hello 存放于磁盘上,为理解运行该程序时发生了什么,需要先了解下图所示的一个典型系统的硬件组织。

        在此需要明确图中的几个概念。
        * 总线:贯穿整个系统的一组电子管道,用于在各个部件间传递字节信息。一般被设计成传送定长的字节块,也就是字。字中的字节数(即字长)是一个基本的系统参数,多为 4 个字节或是 8 个字节。
        * I/O 设备:输入/输出(I/O)设备是系统与外界的联系通道,如图中作为用户输入的键盘和鼠标,作为用户输出的显示器,以及用于长期存储数据的磁盘等。每个 I/O 设备都通过一个控制器或适配器与 I/O 总线相连,两者的区别主要在于封装方式的不同。控制器是置于 I/O 设备本身的或者系统的主印制电路板(通常也称主板)上的芯片组,而适配器则是一块插在主板插槽上的卡。但无论如何,它们的功能都是在 I/O 总线和 I/O 设备之间传递信息。
        * 主存:处理器执行程序时用来存放程序和数据的一个临时存储设备。从物理上来说,它是由一组动态随机存取存储器(DRAM)芯片组成的。从逻辑上来说,它是一个线性的字节数组,每个字节都有唯一的从零开始的地址(即数组索引)。一般组成程序的每条机器指令都由不同数量的字节所构成。
        * 处理器:即中央处理单元(CPU),是解释或执行存储在主存中指令的引擎。它的核心是一个字长的存储设备(或寄存器),称为程序计数器(PC),PC 在任何时刻都指向主存中的某条机器语言指令。
        从系统通电开始,直到系统断电,处理器一直在不断地执行 PC 指向的指令,然后再更新 PC,使其指向下一条指令,而这条指令并不一定与刚执行的那条相邻。这样的操作是围绕着主存、寄存器文件和算术/逻辑单元(ALU)进行的。寄存器文件是一个小的存储设备,由一些 1 字长的寄存器组成,每个寄存器都有唯一的名字。ALU 计算新的数据和地址值。
        CPU 在指令的要求下可能会执行以下操作:
        * 加载:把一个字节或者一个字从主存复制到寄存器。
        * 存储:把一个字节或者一个字从寄存器复制到主存的某个位置。
        * 操作:把两个寄存器的内容复制到 ALU,以对这两个字做算术操作,并将结果存放到一个寄存器中。
        * 跳转:从指令本身中抽取一个字,并将其复制到 PC 中,以覆盖 PC 中原来的值。
        了解这些后,再来看一下执行 hello 程序时所发生的事情。
        当在 Shell 中使用键盘输入“./hello”时,Shell 会逐一将字符读入寄存器,再把它存放到存储器中。这个过程如下图所示:

        Shell 会在用户敲了回车键后结束命令的输入,然后执行一系列指令来加载可执行的 hello 文件,并将其中的代码和数据从磁盘复制到主存。这里的数据包括最终会被输出的字符串“hello, world\n”。利用直接存储器存取(DMA)技术,数据可以不通过处理器而直接从磁盘到达主存。这个过程如下图:

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

        从这个例子中还可以看出,系统花了大量的时间把信息从一个地方复制到另一个地方。从程序员的角度来看,这些复制就是开销,减缓了程序“真正”的工作。再加上处理器访问寄存器、主存和磁盘之间存在巨大的速率差异,因此很有必要加快这些复制操作的完成。这也是高速缓存存储器(简称高速缓存)得以发展的原因。
        高速缓存一般是用一种叫做静态随机访问存储器(SRAM)的硬件技术实现的。常用的高速缓存一般分为三级:L1、L2 和 L3,它们的大小逐级递增,但处理器访问它们的速度却逐级递减。系统可以获得一个很大的存储器,同时访问速度也很快,就是利用了高速缓存的局部性原理,即程序具有访问局部区域里的数据和代码的趋势。通过在高速缓存里存放经常访问的数据的方法,大部分的存储器操作都能在快速的高速缓存中完成。下图就是一个典型的存储器层次结构。其中,从上到下,设备的访问速度变得越来越慢、容量越来越大,价格也越来越便宜。每一层都可作为低一层的高速缓存,比如,寄存器文件作为 L1 的高速缓存,L1 作为 L2 的高速缓存,依次类推。


参考书籍:
1、《深入理解计算机系统》第一章——计算机系统漫游。
  • 大小: 23.4 KB
  • 大小: 45.5 KB
  • 大小: 44.5 KB
  • 大小: 43.5 KB
  • 大小: 47 KB
  • 大小: 46.9 KB
分享到:
评论

相关推荐

    openwrt可用helloworld程序

    4. 交叉编译与安装:回到OpenWrt的顶层目录,运行`make menuconfig`配置你的开发环境,然后执行`make`进行编译,最后使用`make install`将程序安装到模拟器或目标设备。 四、运行Hello, World程序 编译完成后,...

    hello模块驱动程序

    "hello模块"是一个经典的入门级驱动程序,主要用于帮助初学者理解Linux内核模块的基本工作原理和开发流程。这个压缩包文件包含了一个简单的"hello模块"Linux驱动程序及其对应的Makefile文件,用于在Fedora 14环境下...

    C++ Hello World程序

    "程序是每个程序员学习新语言时的第一个传统示例,它简单地在控制台上打印出“Hello, World!”的文本,帮助初学者了解如何编译和运行一个基本的程序。 在C++中,编写一个"Hello, World!"程序非常直观。让我们详细...

    一个C语言的helloworld程序

    然后,你可以通过运行`./hello`来执行程序,屏幕上会显示出"Hello, World!"。 标签“c”表明这个压缩包可能包含了与C语言相关的资源,而“helloworld”则意味着它是关于“Hello, World!”程序的。不过,文件列表中...

    Hello程序编写

    至于压缩包文件"Hello-1",很可能是包含上述代码文件或编译后的可执行文件。在学习过程中,将代码保存在本地,然后编译运行,观察结果,是加深理解的好方法。使用Qt Creator这样的集成开发环境(IDE)可以简化这个...

    hello程序的编写

    `main`函数是程序执行的起点,`return 0`表示程序正常结束。 接下来是Python,一种更易上手的高级语言: ```python print("Hello, World!") ``` 这里的`print`函数是Python的基本输出函数,直接用于打印字符串。 ...

    hello源程序代码

    hello源程序几乎是每一本JAVA入门书籍介绍JAVA的第一个程序,相当经典!

    Hello world程序

    "Hello World"程序是计算机编程领域的一个传统,它通常是初学者接触编程的第一课。这个简单的程序在各种编程语言中都存在,它的主要作用是验证一个编程环境是否已经正确设置,同时向新学习者展示基本的代码结构和...

    哈工大csapp大作业hello程序人生

    在计算机系统基础课程报告中,进程管理是指操作系统如何管理 hello 程序的执行,包括进程的创建、执行和结束等方面。 存储管理是操作系统中的一种机制,它可以管理计算机的存储资源。在计算机系统基础课程报告中,...

    hello的简单程序

    简单描述HELLOworld public class HelloWorld{ public static void main (String[]args){ System.out.println("HelloWorld!"); } }

    C++ hello world 程序源码

    总结来说,这个压缩包中的"C++ Hello World 程序源码"提供了学习C++语言的良好起点,通过对比不同的实现方式,可以深入理解C++的基础语法和编程技巧。同时,它也是实践编程、熟悉编译与运行流程的绝佳案例。无论是...

    OpenWrt之helloworld程序

    在这个"OpenWrt之helloworld程序"中,我们将探讨如何在OpenWrt环境中编写、编译和运行一个简单的 HelloWorld 程序,这将帮助初学者了解OpenWrt的开发流程。 首先,`helloworld` 文件通常是一个C或C++源代码文件,...

    编写 hello world web 程序

    ### 编写 Hello World Web 程序 #### 任务目标 本任务旨在通过一系列步骤指导初学者如何创建一个简单的 Node.js 程序,并在命令行中输出 "Hello World!" 文本。此过程不仅涉及基本的文件系统操作,还包括了 Node.js...

    计算机系统-hello程序的一生

    计算机系统的基础知识涉及众多领域,本论文以"hello.c"程序为例,详细探讨了它在Linux系统中的生命周期,包括从源代码到可执行文件的全过程。这个过程主要包括预处理、编译、汇编、链接以及程序的加载、运行和结束。...

    hello程序的rpm包

    这个列表中只有一个文件名“hello”,这可能是“hello”程序的源代码文件或者编译后的可执行文件。在创建RPM包时,这个文件会被包含在RPM包内,作为最终用户安装时运行的程序。如果是源代码,那么在构建RPM包时,会...

    Hello程序.mm

    Hello程序.mm

    带有表单的Hello程序,struts 2学习

    带有表单的Hello程序,struts 2学习,一步步教你如何实现一个示例

    linux环境下用makefile编译简单的helloworld程序

    在这个“linux环境下用makefile编译简单的helloworld程序”的主题中,我们将深入理解如何创建并使用`Makefile`来编译一个基本的C或C++程序,例如“helloworld”。 首先,`helloworld`程序是一个经典的入门示例,...

    C#编写17种Hello_World程序

    在`Main`方法中,创建了一个`HelloWorld`类的新实例,并调用了`helloWorld`方法来执行输出操作。 #### 6. From Another Class从另一个类调用 此示例介绍了如何在一个类中定义方法,并在另一个类中调用这些方法。 ...

    经典的Hello world程序

    经典的Hello world程序,c语言学习的初步。很简单,适合刚接触c的童鞋们。

Global site tag (gtag.js) - Google Analytics