计算机系统漫游-了解Hello Word如何实现的

247 阅读6分钟

最近在读<<深入理解计算机系统>>,虽然只是第一章总纲,但也是收获良多,分享记录一下个人认为的知识点,技术上没有门槛,适合基础薄弱的人士阅读

计算机系统由硬件和系统软件组成

计算机系统由硬件和系统软件组成,下面我们通过 C 语言的hello.c程序来说明

#include <stdio.h>

int main()
{
    printf("hello world\n")
}

生命周期从源程序开始,即程序员通过编辑器创建并保存的hello.c文件,源程序是由0和1组成的位序列,8个位(bit)称为字节,每个字节表示程序中某个文本字符,这里就得提到 ASCLL

  • ASCLL 标准
    大部分现代系统都用 ASCLL 标准表示文本字符,这种方式就是用唯一的单字节大小的整数表示每个字符,由 ASCLL 字符构成的文件称为文本文件,其他文件都称为二进制文件
    下面是hello.c文件对应的 ASCLL 码

而上面的表示方法也告诉我们了一个基本思想:

系统信息(包括磁盘文件,存储器的程序,网络传输数据)都由一串位(Bit)表示

区分不同数据对象的唯一方法是我们读取到这串位时的上下文(context),不同的上下文中,同样的字节序列表示的可能不同
ps:上下文的概念比较模糊,很多人认为就是环境,其实上下文本身就是个抽象词,有个很有意思的说法,就是当你有一个 Object 需要保存状态时,你想不到好名字就可以命名为Context

小结 : 信息等于上下文(Context) + 位(Bit)

C 语言编译过程

hello.c的代码是要执行的指令,计算机可不知道ASCLL是什么,计算机只懂0和1的信息,所以我们的hello.c要想得到预期效果,就必须转换为可执行文件,在 Java 中就是编译,从java文件转为class文件,JVM 才能明白你编写的代码要做啥,而 C 是通过 GCC 编译器
unix> gcc -o hello hello.c
将我们的 C 代码转为一系列低级机器语言指令,由下面四个阶段组成编译系统

  • cpp / 预处理器
    根据 # 命令, 对源码进行处理(插入、替换,等等),得到新的 C 程序,以.i为文件扩展名
  • cc1 / 编译器
    通过编译将 C 程序转为汇编语言程序,得到文本文件hello.s
  • as / 汇编器
    hello.s翻译成机器语言,打包成可重定位目标程序 的格式,将结果保存到二进制文件hello.o中,所以我们用编辑器查看文件,只能看到一堆乱码
  • ld / 链接器
    hello.c中有调用printf("hello world\n"),该函数在 C 编译器都会提供的C标准库中(printf函数在预编译文件printf.o中)
    将预编译文件合并到可重定位目标程序/hello.o里面,得到可执行目标程序/hello,也就是计算机可以读懂的可执行文件

上面的步骤都做完以后,我们可以调用可执行文件,得到程序结果!

那么我们了解编译系统有啥好处呢?

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

内存中的可执行文件是怎么调用的

运行磁盘上的可执行文件,可执行文件被加载到内存中,由系统调用执行

shell

下面的代码块,是通过 shell 执行的,shell 是一个命令行解释器,输出一个提示符,等待你输入命令;如果命令行第一个单词不是内置的 shell 命令,那么默认是可执行文件,shell 将加载并运行这个文件
下面的例子就是加载并运行 hello 程序,直到程序终止,然后再输出一个提示符,等待新命令

unix> ./hello
hello,world
unix> 

系统的硬件组成

涉及到了内存,那么我们就需要了解下一个典型系统的硬件组织

  • 总线
    贯穿整个系统的是一组电子管道,也叫做总线:携带信息字节负责在各个部件间传递

  • I/O 设备
    I/O 设备是系统与外部世界联系的通道,上面的图例中有四个 I/O 设备:鼠标,键盘,显示器,磁盘

  • 主存 主存是一个临时存储设备
    由动态随机存取存储器(DRAM)芯片组成,在处理器执行程序时,用来存放程序和程序处理的数据

  • 处理器 中央处理单元(CPU),简称处理器,是解释(或执行)主存中指令的引擎
    从系统通电开始,直到停电,处理器只执行两个的动作:

    1. 执行 PC 所指向地址里保存的指令
    2. 更新 PC 指向地址,指向下一条命令

CPU的核心是一个字长的存储设备(寄存器),称为程序计数器(PC),任何时刻,PC都指向主存中的某条机器指令,即PC含有该条指令的地址;

指令通常围绕主存,寄存器文件和算术/逻辑单元(ALU)进行,都是一些简单操作

寄存器文件是一个小的存储设备,由多个1字长的寄存器组成
ALU 计算新的数据和地址值

下面是指令操作集合:

  1. 载入:从内存中复制一个字节或是一个字长的数据到寄存器,覆盖寄存器原来的值
  2. 保存:将寄存器中的一个字节或是一个字长的数据复制到内存的某个位置,覆盖该位置原有的值
  3. 计算:取出两个寄存器的值,复制它们到 ALU ,然后 ALU 对这两个值进行计算,并将结果保存在某个寄存器中,覆盖该寄存器原来的值
  4. 跳转:从指令中取出一个字,并将该字放到 PC 上,覆盖 PC 原来的值
  • 扩展
    处理器和指令集结构是两个概念,现代处理器使用了复杂的机制加速程序的执行

指令集结构描述的是每条机器代码指令的效果
微体系结构描述的是处理器实际上如何实现的

系统对 hello 文件都做了什么呢?

前面简要了解了计算机的硬件的组成,那么现在来了解 hello 程序在系统中都发生了什么

以上面 shell 的代码块为例,我们在键盘上敲回车,系统知道了我们要执行 hello 这个可执行文件,将 hello 程序加载到内存

通过直接存储器存取的技术,数据可以不通过处理器执行存储操作,到达主存中

当hello 程序的代码和数据被加载到内存中后,处理器就开始执行程序的main函数,系统最终通过显示器输出字符串"hello,worle"

ok,分享就到这里