- 信息是什麼
- 文件
- Hello World程序的生命週期
- 開始運行Hello World
- 虛擬地址空間
- 總結
信息就是位+上下文。html
怎麼理解呢?其實計算機系統中的全部信息都是一個一個的二進制位,不管是硬盤上的文件、內存中的代碼仍是網絡上傳輸的數據,毫無例外。它們惟一的區別就是所處不一樣的上下文,可什麼又是上下文呢?作過應用程序開發的應該很熟悉context對象,當你建立一個新的控件的時候,每每要向構造方法中傳入上下文對象,咱們通常會傳入this指針,這個上下文對象就是用來告訴新的控件它所處的位置,或者說它所處的環境。在計算機系統中,二進制數據所處的環境決定了它們表達的含義,一樣的一段數據,做爲整型、浮點型、字符串型或是做爲機器指令時,表達的含義是徹底不一樣的。程序員
一般來講,文件分爲兩種,文本文件和二進制文件。起初我是不理解的,難道文本文件不是二進制組成的?文本文件固然也是二進制組成的,只不過比純粹的二進制文件多了點上下文特徵,即編碼。以ASCII編碼的文本文件來講,每一個字節表示一個字符,因而這些二進制數據在這個上下文環境中表現爲一個個字符,成了能夠閱讀的文本,這就是文本文件的特殊之處。shell
先來看一個程序員再熟悉不過的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中出錯的狀況,我就曾遇到過這種錯誤,但願你們注意。工具
好啦,有了可執行文件,咱們就能夠運行它,在命令行中敲以下命令:
unix> ./hello
顯而易見,運行的結果爲打印了一行字符串
hello, world
但是在咱們發出命令和打印出結果期間都發生了什麼呢?這就不得不提計算機系統的硬件結構了。下圖是計算機系統的硬件結構圖,我用紅線標出了當咱們在shell中輸入hello命令時,計算機中的信息流向。
當咱們想要輸入命令時,其實CPU中已經有一個正在運行的程序,那就是shell。shell程序一直在等待咱們的輸入,因此咱們隨時能夠在鍵盤上輸入內容。先看左下角的USB控制器,它負責全部USB接口,因此這裏有鼠標、鍵盤等外設。咱們在鍵盤上輸入「./hello」命令時,該命令經過USB控制器向上通過I/O總線傳遞給I/O橋,也就是咱們日常所說的南橋北橋,它是CPU和外界溝通的橋樑。再通過系統總線傳遞給寄存器,到了寄存器後還不是終點,由於shell程序須要把用戶輸入的內容做爲一個變量使用,而這個變量必定在內存中有個地址,因此它最終會到達內存。
以後,shell程序解析咱們的命令內容,知道了咱們但願運行hello這個程序。因而shell程序開始從硬盤加載hello文件到內存中。但是此次,這些數據不會通過CPU,而是直接從硬盤到內存,這種方式稱爲DMA。DMA(直接存儲器訪問)有利於減輕CPU的負荷,使CPU能夠在數據轉移的同時作其它任務。數據轉移路線以下圖:
加載完hello文件後,CPU將會開始從hello程序的主函數處執行指令。因而hello中的print語句將要打印的字符串傳遞給CPU,CPU再將它傳遞給顯示器,這一過程字符串「hello, world」通過的路徑以下圖所示:
終於,咱們在屏幕上看到了「hello, world」這一字符串。過程很複雜,但卻只是一瞬間的事情,可見計算機運行速度之快!
hello程序咱們分析透徹了嗎,彷佛沒有。不少時候咱們還會關心程序運行時內存的變化,當啓動一個新進程的時候,操做系統是否是要爲這個進程分配內存空間呢?答案是確定的。
這就是咱們要講的虛擬地址空間。虛擬地址空間是操做系統中一個很是複雜的概念,操做系統負責建立進程,同時爲該進程分配內存。在現代操做系統中,出於進程間互不干擾,以及保護操做系統內核安全的考慮,每一個進程享有徹底獨立的一套完整地址空間。對於32位計算機來講,虛擬地址空間大小爲2GB,範圍從 0x00000000 至 0x7FFFFFFF;對於64位計算機來講,虛擬地址空間大小爲8TB,範圍從0x000'00000000 至 0x7FF'FFFFFFFF。這就是說,每一個進程均可以隨意使用這2GB或8TB的內存空間,可是,因爲是虛擬地址空間,這些地址映射到真實物理內存的時候是打亂的,用戶沒法得知本身進程的數據到底存在物理內存的什麼地方。接下來,咱們來看看用戶進程的這2GB或8TB虛擬地址空間是怎麼用的。
上圖將虛擬地址空間分爲了若干個部分,並用箭頭表示該部分的擴展方向。最下端地址爲0,向上地址逐漸增加。每一個部分做用以下:
malloc
函數申請的動態內存空間,能夠向上擴展。printf
的代碼和數據。結束了Hello World的旅程,在後面的章節中,咱們將一步步深刻,探索計算機系統那些鮮爲人知的奧祕。
文中如有錯誤或不當之處,懇請各位讀者指出。
關注做者或文集《深刻理解計算機系統》,第一時間獲取最新發布文章。