深刻理解計算機操做系統(二)

閱讀經典——《深刻理解計算機系統》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 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。
相關文章
相關標籤/搜索