hello从编写到运行
编写程序
当编写一个hello.c的时候,hello.c是源文件,存储在硬盘里。具体的表示形式是Bits,计算机中所有的信息都是各种Bits或Bytes组成,其具体的含义则需要根据上下文(Context)去理解。即用ASCII码去查看hello.c的字符串,可以将bits转化成我们看得懂的字符串,也就是文本形式。而如果用ASCII去解析一个二进制的可执行文件,则只能看到一堆乱码。-
编译程序
将hello.c编译成可执行文件,语句很简单:
gcc -o hello hello.c
但是gcc在背后做了很多事情:- 预处理:将hello.c文件中的以
#
开头的指令进行处理。例如将#include<stdio.h>语句用系统头文件stdio.h的内容进行替换。预处理之后生成hello.i文件。 - 编译:将hello.i文件转化成汇编语句。生成hello.s文件。
- 汇编:将hello.s中的汇编语句变成机器指令,生成hello.o文件,此文件为二进制文件,非文本文件。
- 链接:将hello.c中调用的printf函数所在的单独的预编译对象文件printf.o和hello.o合并,生成hello可执行文件。
- 预处理:将hello.c文件中的以
运行程序
当我们通过键盘输入./hello的时候,shell程序会读取字符到寄存器,然后存储在内存中。当按下enter键的时候,shell通过执行一系列指令,从hello文件中读取代码和数据并加载到内存。
通过DMA(直接内存访问)技术,可以将数据直接从硬盘传输到内存,而无需通过处理器。
一旦hello文件的代码和数据加载到内存,处理器便会开始执行hello程序的机器指令,将hello,world\n 字符串从内存拷贝到寄存器,然后显示到屏幕。
缓存
一个简单的程序要花费大量的时间去传输数据,处理器读取寄存器的值要比读取内存的值快100倍,所以为了处理 cpu与内存之间的鸿沟,加入了缓存技术。
L1级缓存大小为几十KB,读取速度和寄存器相当。
L2级缓存大小为几百KB到几兆,可能比L1慢5倍,但比直接访问内存快5-10倍。
L1与L2的实现为SRAM(静态随机存取存储器)。
存储设备层次结构
层次可以分为:
- 寄存器
- L1 cache
- L2 cache
- L3 cache
- Main memory
- Local disks
- Remote secondary storage
从上到下,每层的访问速度越来越慢,上一层可作为下一层的缓存。如内存可以作为磁盘的缓存,磁盘可以作为服务器资源的缓存。
操作系统是介于应用程序与硬件之间的管理程序,主要有两个目的:
- 防止硬件被应用程序误用
- 提供简单并统一的机制给应用程序去操作低层次的硬件。
主要通过进程、虚拟内存、文件去实现上述功能。
进程:进程是正在运行的程序在操作系统中的一种抽象。
线程:线程是可执行单元,共享进程的上下文,并共享同一段代码和全局数据。线程间通信比进程间通信更简单,且通常比进程更高效。
文件:一串bytes,仅此而已。所有的I/O设备都可以看作是文件。对I/O设备进行输入与输出,相当于读写文件。
虚拟内存:每个进程都有同样的内存视图,叫虚拟内存空间。主要分为:- 程序代码和数据区
- 堆区
- 共享库
- 栈区
- 内核虚拟内存:操作系统占有,应用程序代码无法直接访问。
并发:
三种层级的并发:
- 线程级别:多核处理器,每个核心可以同时运行多个线程。比如四核八线程处理器。
- 指令级别并发:一个时钟周期可以运行多个指令。
- 单指令,多数据(SIMD)并发:单指令进行多个操作实现并行。