這里的所謂的裸機編程指的是為“無OS支持的硬件系統編程”,而實際的編程工作肯定需要一個環境,通常這樣的情況中,編程和編譯的環境叫做“宿主機”,最終的程序在“目標機”上運行(交叉編譯)。而OS環境編程指的是最終運行的程序是在有操作系統支持的環境中運行,而編程和編譯的環境,可能是運行程序的機器(本地編譯),也可能不是(交叉編譯)。
裸機編程現在主要是正對低端的嵌入式系統,如SCM(single chip machine)、各式MCU、DSP等。當然,編寫PC的bootloader肯定也屬於裸機編程。
裸機編程的最原始辦法是用匯編語言(一種機器指令的一一對應的記法,和加上一些簡單的匯編偽指令),只能使用很有限的指令集,每行代碼只能做微小的事情。因此現在裸機編程也普遍使用更高級的語言(通常是C語言),那么從C語言轉換到匯編語言這個過程就叫做編譯。編譯器根據不同的機器,將通用的C代碼轉化為特定的機器代碼,只有十分少量的機器代碼仍然需要用到匯編,這其實是一種混合編程的模式。那么,編譯器實在是一種十分重要的工具,編譯的理論和實踐知識也會十分豐富。
在有OS支持的環境中編程則更加便利了。首先OS管理並擴展了整個機器資源,提供了一個通用的API系統調用接口,程序員通過這個接口與硬件資源打交道,因此在OS上編程更加不需要考慮機器的特性,換句話說就是移植性最佳。作為資源的擴展,OS提供了大量的機制(包括進程、內存管理、設備操作等等)和庫文件(這些庫文件屬於可重用的代碼),讓編寫實用程序更加便利。
其次,編譯器與OS之間的關系非常緊密,OS環境編程很少有人用匯編代碼,而是可以使用各種層次和類型的高級語言。很容易看出來OS環境編程使用的編譯器,其功能要比裸機編程的編譯器廣泛的多,盡管它們是有緊密的聯系。舉例而言,gcc編譯器能夠為多種的硬軟件平台編譯C/C++程序:可以用gcc編譯本地程序,也可以用xxx-xxx-gcc在宿主機上交叉編譯目標機器的程序;可以用gcc編譯裸機程序,也可以編譯OS環境下的程序。總的來說,gcc編譯出來的OS環境可執行文件,是裸機環境可執行文件的“超集”。看一下下面這段最簡單的makefile代碼:
key_led.bin : crt0.S key_led.c arm-linux-gcc -g -c -o crt0.o crt0.S arm-linux-gcc -g -c -o key_led.o key_led.c arm-linux-ld -Ttext 0x0000000 -g crt0.o key_led.o -o key_led_elf arm-linux-objcopy -O binary -S key_led_elf key_led.bin clean: rm -f key_led.dis key_led.bin key_led_elf *.o
這段代碼編寫了一個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程序來查看一個程序運行時需要的動態庫。
*注:.la文件的詳解 http://blog.flameeyes.eu/2008/04/what-about-those-la-files
總結一下就是:
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