1.計算機 = 硬件 + 操作系統 + 應用程序
有人說過,計算機世界的絕大部分問題都可以通過分層的方法來解決。從一個程序員的角度,我比較喜歡將計算機分為三層,自底向上分別為:
1) 硬件(Hardware),按照馮氏的結構定義,一個處理器由5個部分構成,分別為:存儲器;控制器;運算器;輸入設備;輸出設備。對於常見的計算機而言,存儲器如常見的外存儲設備;CPU則包含運算器、控制器和內存儲;輸入輸出(I/O)很好理解,這里就不寫了。
2) 操作系統:Operation System,操作系統主要負責管理計算機硬件資源,控制其他程序運行並為用戶提供交互操作界面的系統軟件的集合。講的具體一點如進程、線程管理;內存管理;信號量機制;IO管理等。最常見OS的如Windows,又如HP-UX、SUSE、VxWorks(嵌入式)。
3) 應用程序層:Application,應用層,指使用各種不同的編程語言(C、C++、JAVA、Ruby、Python,太多了,寫不完)、通過各種開發工具並基於各種操作系統開發的——軟件。該部分是大部分程序員工作的一層,也是構成我們豐富多彩的生活最直接的一部分。常見的如Word是軟件,QQ亦是軟件。
2.那些圍繞在我們身邊的“計算機”
根據上面的分層,對應一下常見的計算機:
最常見的計算機莫過於個人PC,其硬件部分(主要講CPU)當前最主流的當屬Intel和AMD的產品,操作系統對應的如Windows(現在應該是Windows8了,當然如果自己裝Linux也有可能),對於基於Windows的應用程序開發,其IDE如微軟提供的Virual Studio, VC 6.0等。APP如Word,還有當前用來寫文章的Maxthon。
再如華為公司的ATAE,其CPU主要采用Intel的x84_64,操作系統采用SUSE10 Linux,編譯器為gcc/g++,其應用程序如FTP。
在服務器世界里,還有惠普公司的HP-UX、IBM的工作站,其對應的操作系統分別為HP Unix、AIX,編譯器分別為aCC與xLC。還有那個曾經的SUN也有對應的產品,這里就不提了。
3.程序員在干啥?
對於一個程序員而言,主要在從事APP層的開發工作,如果主要從事C/C++,最直接接觸的當屬OS層。所以要寫好程序,必須去了解操作系統的機制,不同的操作系統都有着不同的限制和調度機制。首當其沖的編譯器的機制必須要去了解,比如gcc和g++,其常用的參數有哪些,每個參數代表什么含義,有什么限制?Makefile如何編寫?基於該OS開發的程序如何進行調試?操作系統的信號量機制有哪些?同理,對於JAVA的開發而言,最直接的打交道的應該是JVM,可以理解為一個簡單的操作系統,所以必須去了解JVM的調度機制,如內存管理、GC機制如何運作?調試工具如jvisualVM、jMap如何使用?
再深一層,就要了解到硬件的限制,如CPU的調度機制、匯編指令、IO等。比如代碼a = a+1和a++轉變成匯編指令有什么區別,怎樣寫才是高效的?HP Unix的aCC編譯器,“+u1”參數后對生成的匯編指令有何影響?大尾端和小尾端有什么區別,與網絡字節序又有何關系?再如,為什么要使用多線程編程,在編碼時引入多線程的目的是什么?
上面主要簡單描述了一個程序員眼中的計算機分層,那么對於我們的產品——APP或者叫做“軟件”,內部又是怎樣構成的呢?
4.軟件/程序 是什么?
從技術角度而言,軟件在運行態可以理解為一個個的進程,其內部可能有幾個線程構成。上過操作系統課的同學應該有兩句話印象深刻(上課睡覺,作業抄襲的除外):“進程是擁有資源的最小單位,線程是CPU調度的最小單位。”
畫個圖吧:
每個運行態的應用程序,可以稱其為Process,每個Process對應有一個身份標識(PID)。其內部包含1到N個線程,每個線程也有自己的線程句柄。CPU以線程為單位進行調度,資源如MEMORY, CPU, IO等統一由進程擁有,換句話講Thread1至Thread N均可以直接訪問進程所擁有的資源。因此對於多線程程序而言,其資源存在並發訪問的可能性,比如對於同一個變量a,Thread1在進行a = 1的時候,Thread 2可能“同時”在進行a = 2的操作,這個操作的結果就是a的賦值結果不可預知。如果Thread1在前,Thread 2在后,那么a最終等於2,反之a最終等於1。對於類似於a這類資源,我們可以定義其為“臨界資源”,對於臨界資源的保護,成為多線程編程必須面臨而又必須解決的問題。
由於進程擁有着計算機的資源,因而其內部線程可以方便的訪問其內部的各種資源。比如對於內存而言,Thread1可以通過地址訪問到Process內部相應的內存塊,Thread2也可以訪問到Process內部的對應地址。
5.程序森林
每個計算機上面都不可能僅僅存在一個Process,運行期間,必然是有各種各樣的Process構成,如下圖:
但問題來了,如果Process1想訪問Process2內部的數據,該如何處理呢?進程與進程間的數據訪問,通過進程間通信(IPC)來解決,Linux下常見的進程間通信方式如共享內存、管道、Socket等(參見《Linux環境高級編程》,這里不多寫了,后面再詳細寫)。
進程的構造弄清楚了,那進程/程序究竟在做什么?我理解的,程序無非是對於輸入的信息/數據進行一定的處理,然后提供一定的輸出。
對於輸入的數據,可能是數據庫的數據存儲、可能是網絡上的一串碼流、抑或是我們單擊了一下鼠標。邏輯處理即是對這些輸入進行了一系列的數學運算,比如點擊了12306上面的“訂票”按鈕,后台就要計算當前的余票信息,提交的訂票信息,最終告訴你有沒有票。“有沒有票”即是程序的輸出。更廣泛意義的講,輸出可能是一個網絡消息或修改數據庫的某個字段的值(軟件的所有輸出均限制於計算機世界中,記得當年還曾懷疑過究竟人會不會被計算機病毒感染)。所以有人說了:“程序其實很簡單,無非是找個編程語言,寫串邏輯,處理下數據,偶爾連下數據庫,或跟其它進程通通信”。