天天看點

裸機程式設計與OS環境程式設計的有關思考

這裡的所謂的裸機程式設計指的是為“無OS支援的硬體系統程式設計”,而實際的程式設計工作肯定需要一個環境,通常這樣的情況中,程式設計和編譯的環境叫做“主控端”,最終的程式在“目标機”上運作(交叉編譯)。而OS環境程式設計指的是最終運作的程式是在有作業系統支援的環境中運作,而程式設計和編譯的環境,可能是運作程式的機器(本地編譯),也可能不是(交叉編譯)。

裸機程式設計現在主要是正對低端的嵌入式系統,如SCM(single chip machine)、各式MCU、DSP等。當然,編寫PC的bootloader肯定也屬于裸機程式設計。

裸機程式設計的最原始辦法是用彙編語言(一種機器指令的一一對應的記法,和加上一些簡單的彙編僞指令),隻能使用很有限的指令集,每行代碼隻能做微小的事情。是以現在裸機程式設計也普遍使用更進階的語言(通常是C語言),那麼從C語言轉換到彙編語言這個過程就叫做編譯。編譯器根據不同的機器,将通用的C代碼轉化為特定的機器代碼,隻有十分少量的機器代碼仍然需要用到彙編,這其實是一種混合程式設計的模式。那麼,編譯器實在是一種十分重要的工具,編譯的理論和實踐知識也會十分豐富。

裸機程式設計與OS環境程式設計的有關思考

在有OS支援的環境中程式設計則更加便利了。首先OS管理并擴充了整個機器資源,提供了一個通用的API系統調用接口,程式員通過這個接口與硬體資源打交道,是以在OS上程式設計更加不需要考慮機器的特性,換句話說就是移植性最佳。作為資源的擴充,OS提供了大量的機制(包括程序、記憶體管理、裝置操作等等)和庫檔案(這些庫檔案屬于可重用的代碼),讓編寫實用程式更加便利。

其次,編譯器與OS之間的關系非常緊密,OS環境程式設計很少有人用彙編代碼,而是可以使用各種層次和類型的進階語言。很容易看出來OS環境程式設計使用的編譯器,其功能要比裸機程式設計的編譯器廣泛的多,盡管它們是有緊密的聯系。舉例而言,gcc編譯器能夠為多種的硬軟體平台編譯C/C++程式:可以用gcc編譯本地程式,也可以用xxx-xxx-gcc在主控端上交叉編譯目标機器的程式;可以用gcc編譯裸機程式,也可以編譯OS環境下的程式。總的來說,gcc編譯出來的OS環境可執行檔案,是裸機環境可執行檔案的“超集”。看一下下面這段最簡單的makefile代碼:

裸機程式設計與OS環境程式設計的有關思考
裸機程式設計與OS環境程式設計的有關思考

這段代碼編寫了一個ARM目标機器上用按鍵控制LED的程式。gcc的編譯首先将其編譯連結成elf格式的檔案,然後用objcopy工具轉換成裸機代碼bin檔案。因為elf的可執行檔案格式是linux系統的标準支援格式,這種檔案中不僅含有二進制機器碼,而且(如果有需要的話)會含有大量的符号與控制資訊(往往是文本格式的),這些符号與控制資訊能讓這個程式與OS互動,并得到OS的支援。在裸機上執行程式時,僅僅需要機器直接能識别的二進制機器碼bin檔案,這是一種純淨的二進制機器碼檔案。從這個過程可以看出來,編譯器與OS的關系的确非常緊密。

 ===================================================================

下面考察實際的linux/UNIX系統中的程式描述(based on linux programing 4th edition)。

概略

linux應用程式表現為兩種類型的檔案:可執行檔案和腳本檔案。可執行檔案能夠直接運作,它們包含二進制的機器代碼和一些OS必須了解的控制資訊。腳本檔案被解釋器一句句的執行,事先并沒有編譯成最底層的機器碼。腳本檔案有很多種,如python、shell等,java虛拟機類似一種解釋器,但是它解釋java中間碼,性能比一般的解釋器好。

shell是linux的人機互動界面,本質上來說它是個解釋器,解釋使用者輸入的每條指令,當然也可以解釋使用者編寫的腳本。linux标準的程式執行搜尋路徑有:

/bin   : /usr/bin   : /usr/local/bin   : /sbin   : /usr/sbin

可選的作業系統元件和第三方應用程式可能被安裝在/opt目錄下,使用者通過PATH環境變量來添加預設搜尋的目錄。從shell執行的程式會繼承shell的環境變量,但程式的修改不會反向影響到父程式shell。

linux中的文本編輯器可以選擇vim、emacs或者更可視化的eclipse等工具。

開發環境:

1、應用程式。/bin: /usr/bi: /sbin: /usr/sbin 這幾個目錄一般存放最常用的系統程式,而後來添加的程式往往存放在/opt:/usr/local中,它們分離的系統原本的程式和後續添加的程式。對于個人的程式或開發程式,最好在/home目錄中存放它。(usr的意思不是user啊騷年!!是Unix Software Resource !!!)

gcc的驅動程式通常存放在/usr/bin 或者/usr/local/bin中,它調用gcc編譯器的其它應用程式,可能存放在/usr/local/bin中,或者其它gcc知道的位置。

至于哪些程式是系統程式,這很難界定,linux kernel本身不帶有任何程式,僅僅提供一個API接口。一個最小的簡化版系統程式配置如busybox包含常用的如shell及其指令等,僅僅至于數百K的體積。

2、頭檔案。用C語言或者其它語言進行設計時,需要頭檔案的目的一般是需要常量的定義,或者需要對系統函數及庫函數調用的聲明(這些定義和聲明可以是來源于使用者,也可以是來源于标準庫、擴充庫)。linux中C語言的頭檔案總是位于/usr/include,依賴特定版本的頭檔案通常位于/usr/include/sys或者/usr/include/linux 。

3、庫檔案。庫,是一組預先編譯好的函數的集合,這些函數按照可重用的原則編寫。标準系統庫檔案存放于/lib和/usr/lib目錄中,其它擴充庫可能存放在其它lib目錄中。庫的名字必須是以lib開頭,随後部分指明庫的功能,.a代表靜态庫,.so代表動态庫,可能有 .la檔案(*注)。雖然庫檔案和頭檔案一般存放在标準位置,但可以也編譯時用”-L”搜尋一個特殊的位置。在程式中如果引用動态庫,在編譯時要說明動态庫的位置,程式運作态也需要庫在指定的位置存在。

對于linux來說,負責裝載動态庫并解析客戶程式所引用的函數的程式(動态裝載器)是ld.so,或者是ld-linux.so.2/  ld-lsb.so.2/  ld-lsb.so.3。程式運作中搜尋動态庫的額外位置,可以再檔案/etc/ld.so.etc中配置。也可以用ldd程式來檢視一個程式運作時需要的動态庫。

總結一下就是:

1、可以隐藏具體作業系統的共享庫實作的不同,比如Linux是so,Windows是dll。有了.la,統一連接配接.la檔案就可以了

2、連接配接靜态庫的時候可以從它來擷取靜态庫的依賴關系。動态庫(ELF based .so file)裡本身儲存了這個資訊,不需要.la來擷取這個資訊。

.la檔案理論上是可以不要的,而且如果要實作真正的multilib支援,是一定不能要.la檔案。

如果不用.la檔案,連接配接靜态庫的時候理論上說可以用pkg-config –static來獲得依賴關系(庫)。

但是現在,不是所有的庫都提供相應的.pc檔案

現在有些Gentoo開發者和使用者正在嘗試移除系統裡的.la檔案,并修複随之帶來的問題,以求為就将來portage完美的multilib支援打下堅持的現實基礎。

有關這方面的詳細内容可以參考:

《深入了解Linux核心 · 第三版》第20章 程式的執行

《Linux程式設計 · 第四版》第4章 linux環境

有關OS環境程式設計的标準化内容可以參考:

《Linux程式設計 · 第四版》第18章 linux标準

《UNIX環境進階程式設計 · 第二版》第2章 UNIX标準化及實作

from:http://www.cnblogs.com/andrew-wang/archive/2012/12/13/2816897.html