经过了很长时间的学习,我们对计算机的各个模块都有了响应的了解,但是分散的知识是很难发挥作用的,需要一些东西它们串联起来。
今天,我们通过一个简单的问题,将之前接触到的部分知识点整合起来。
问题
程序/代码是如何运行起来的?
对我们程序员来说,需要写一些源代码,以CPP为例,会生成一些.cpp或.c后缀的源文件,例如以下这个文件:
Cmain.c
#include <stdio.h>
int main() {
printf("Hello,World\n");
return 0;
}
在写完代码后需要进行编译链接
,然后在命令行下执行
。
bash# gcc main.c -o hello
# ./hello
Hello,World
编译器从宏观来说就是一个强大的“文本处理程序”,其就是将“CPP文件”处理为“目标文件”。而处理的过程分为多个步骤
词法分析。编译器会将文件中的内容分割为一个一个的类似于“class”的符号,这一步称为提取符号,词法分析。
//
语法分析。在词法分析的基础上,编译器会对每个符号进行解析,例如:当编译器遇到了while,此时就会有( 接下来就会是一个布尔表达式,之后是 ),然后会是 { ,循环体,} 之后就结束。此时就形成了一个具有语法意义的语法树。
语义分析。已经生成了语法树,但不确定是否正确,因此需要进行检查。
生成汇编。根据语法树进行转换,生成汇编指令。
生成机器指令。
此时,生成的机器指令存于目标文件。
对于目标文件,其内容以段的形式存储信息。
目标文件将所有使用的符号存于符号表,**整个符号表就做两件事:定义了那些符号;用了哪些外部符号。**同时对于不能确定的变量,单独分离出来,为将来链接器的决议指明。
链接器从宏观上来看也相当于一个强大的“文本处理程序”。其对目标文件以及库文件进行链接时,也分为多个步骤。
符号决议。符号就是变量,符号决议就是为所有外部的符号找到其所对应的定义。
生成可执行文件。当所有符号都有定义后,链接器会将所有代码区与数据区整合起来。//
重定位。生成基本的可执行文件后,需要对之前.relo.text以及.relo.data中的地址进行重新定位,因为在编译时,无法确定的地址会赋予0,因此在整合之后需要将其改为正确的地址。
是否经常看到这样的分区:
此时有一个疑问,此分区的起始地址是0x400000,如果我们有两个程序AB,CPU在执行程序时,取0x400000时,取的是A的起始,还是B的起始?
答案是,CPU执行程序A时,取的是A的代码区,CPU执行程序B时,取的是B的代码区。原因是虚拟内存
我们所看到的结构都是虚拟内存,其为虚拟的,抽象出来的结构。而物理内存就是一块空间,没有分区,没有种类。而它们之间的映射关系 依靠于页表。
通过页表,不同进程在同一虚拟地址上所存的信息分布在不同的物理内存上。如图所示:
此时我们看到:
虚拟内存技术,使得所有程序有了统一的格式成为可能。
程序:运行于某种目标计算机体系结构上,用某种程序设计语言编写,具有信息处理能力或判断的指令的集合。
进程:进程是具有一定功能的程序动态执行的过程, 是操作系统进行资源分配和调度的一个独立单位。进程是抽象出来的。 进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。
线程: 线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。 一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。
本文作者:流浪的将军
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!