深刻理解计算机操做系统(二)

阅读经典——《深刻理解计算机系统》01

  1. 信息是什么
  2. 文件
  3. Hello World程序的生命周期
  4. 开始运行Hello World
  5. 虚拟地址空间
  6. 总结

<h3 id="what_is_information">信息是什么?</h3>

信息就是+上下文html

怎么理解呢?其实计算机系统中的全部信息都是一个一个的二进制位,不管是硬盘上的文件、内存中的代码仍是网络上传输的数据,毫无例外。它们惟一的区别就是所处不一样的上下文,可什么又是上下文呢?作过应用程序开发的应该很熟悉context对象,当你建立一个新的控件的时候,每每要向构造方法中传入上下文对象,咱们通常会传入this指针,这个上下文对象就是用来告诉新的控件它所处的位置,或者说它所处的环境。在计算机系统中,二进制数据所处的环境决定了它们表达的含义,一样的一段数据,做为整型、浮点型、字符串型或是做为机器指令时,表达的含义是彻底不一样的。程序员

<h3 id="file">文件</h3>

一般来讲,文件分为两种,文本文件二进制文件。起初我是不理解的,难道文本文件不是二进制组成的?文本文件固然也是二进制组成的,只不过比纯粹的二进制文件多了点上下文特征,即编码。以ASCII编码的文本文件来讲,每一个字节表示一个字符,因而这些二进制数据在这个上下文环境中表现为一个个字符,成了能够阅读的文本,这就是文本文件的特殊之处。shell

<h3 id="lifecycle">Hello World程序的生命周期</h3>

先来看一个程序员再熟悉不过的Hello World程序编程

#include <stdio.h> int main() { print("hello, world\n"); } 

这是用高级编程语言C语言写的程序,这个程序须要转换成低级的机器语言才可以被计算机识别并执行。咱们能够经过运行一条命令安全

unix> gcc -o hello hello.c网络

来生成可执行文件。(以上命令是在unix环境下调用的gcc编译器的命令,本书将常常采用unix环境。)可是,gcc编译器实际上作的工做不仅如此,下图为从hello.c源程序到生成hello可执行程序的完整过程:编程语言

 
编译系统

首先通过预处理器预处理,而后通过编译器编译获得汇编程序hello.s,再通过汇编器汇编获得可重定位目标程序hello.o,最后,连接器将目标程序和标准库中的printf.o程序连接成为可执行目标程序hello。每一步的详细过程将在后面的章节中叙述,此处只作简要介绍。编辑器

须要补充的是,gcc来自于赫赫有名的GNU项目,该项目为Linux的开发提供了全面的开发工具,包括GCC编译器、GDB调试器、EMACS编辑器、汇编器、连接器等等。有兴趣的朋友能够搜索一下这方面的知识。函数

另外,咱们常常用的另外一款编译器是微软提供的MSVC,当咱们使用Visual Studio时,用的就是它自带的编译器。它和gcc在语法要求等方面有所不一样,因此会出现gcc正常编译的代码在MSVC中出错的状况,我就曾遇到过这种错误,但愿你们注意。工具

<h3 id="excute">开始运行Hello World</h3>

好啦,有了可执行文件,咱们就能够运行它,在命令行中敲以下命令:

unix> ./hello

显而易见,运行的结果为打印了一行字符串

hello, world

但是在咱们发出命令和打印出结果期间都发生了什么呢?这就不得不提计算机系统的硬件结构了。下图是计算机系统的硬件结构图,我用红线标出了当咱们在shell中输入hello命令时,计算机中的信息流向。

 
从键盘读取hello命令

当咱们想要输入命令时,其实CPU中已经有一个正在运行的程序,那就是shell。shell程序一直在等待咱们的输入,因此咱们随时能够在键盘上输入内容。先看左下角的USB控制器,它负责全部USB接口,因此这里有鼠标、键盘等外设。咱们在键盘上输入“./hello”命令时,该命令经过USB控制器向上通过I/O总线传递给I/O桥,也就是咱们日常所说的南桥北桥,它是CPU和外界沟通的桥梁。再通过系统总线传递给寄存器,到了寄存器后还不是终点,由于shell程序须要把用户输入的内容做为一个变量使用,而这个变量必定在内存中有个地址,因此它最终会到达内存。

以后,shell程序解析咱们的命令内容,知道了咱们但愿运行hello这个程序。因而shell程序开始从硬盘加载hello文件到内存中。但是此次,这些数据不会通过CPU,而是直接从硬盘到内存,这种方式称为DMA。DMA(直接存储器访问)有利于减轻CPU的负荷,使CPU能够在数据转移的同时作其它任务。数据转移路线以下图:

 
hello可执行程序从磁盘加载到内存

加载完hello文件后,CPU将会开始从hello程序的主函数处执行指令。因而hello中的print语句将要打印的字符串传递给CPU,CPU再将它传递给显示器,这一过程字符串“hello, world”通过的路径以下图所示:

 
从内存输出到显示器

终于,咱们在屏幕上看到了“hello, world”这一字符串。过程很复杂,但却只是一瞬间的事情,可见计算机运行速度之快!

<h3 id="virtual_memory">虚拟地址空间</h3>

hello程序咱们分析透彻了吗,彷佛没有。不少时候咱们还会关心程序运行时内存的变化,当启动一个新进程的时候,操做系统是否是要为这个进程分配内存空间呢?答案是确定的。

这就是咱们要讲的虚拟地址空间。虚拟地址空间是操做系统中一个很是复杂的概念,操做系统负责建立进程,同时为该进程分配内存。在现代操做系统中,出于进程间互不干扰,以及保护操做系统内核安全的考虑,每一个进程享有彻底独立的一套完整地址空间。对于32位计算机来讲,虚拟地址空间大小为2GB,范围从 0x00000000 至 0x7FFFFFFF;对于64位计算机来讲,虚拟地址空间大小为8TB,范围从0x000'00000000 至 0x7FF'FFFFFFFF。这就是说,每一个进程均可以随意使用这2GB或8TB的内存空间,可是,因为是虚拟地址空间,这些地址映射到真实物理内存的时候是打乱的,用户没法得知本身进程的数据到底存在物理内存的什么地方。接下来,咱们来看看用户进程的这2GB或8TB虚拟地址空间是怎么用的。

 
进程虚拟地址空间

上图将虚拟地址空间分为了若干个部分,并用箭头表示该部分的扩展方向。最下端地址为0,向上地址逐渐增加。每一个部分做用以下:

  • 只读程序数据区和静态数据区:这一部分用来存放可执行程序代码和代码中的全局变量。
  • :用于动态申请的内存变量,好比malloc函数申请的动态内存空间,能够向上扩展。
  • 共享库内存映射区:位于虚拟内存空间的中部,用于存放C语言库函数的代码和数据。本例中即printf的代码和数据。
  • :位于虚拟地址空间的顶部,用于函数调用、存放局部变量等。当咱们调用一个函数时,栈会向下扩展,返回时,向上收缩。
  • 内核虚拟地址空间:这个东西前面没提到过,可是它占据了栈向上直到4GB或256TB的全部空间。这个空间是保留给操做系统内核用的,用户进程无权访问这些地址。但是它究竟是干什么用的,要等到后面的章节才能解开谜底。

总结

结束了Hello World的旅程,在后面的章节中,咱们将一步步深刻,探索计算机系统那些鲜为人知的奥秘。

文中如有错误或不当之处,恳请各位读者指出。

关注做者文集《深刻理解计算机系统》,第一时间获取最新发布文章。

参考资料

做者:金戈大王 连接:https://www.jianshu.com/p/5c33a0bcf244 來源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。
相关文章
相关标签/搜索