嵌入式 Linux 的一切,看這一篇就夠了

01

嵌入式Linux是什麼


嵌入式Linux跟桌面Linux一樣,是一個操作系統。從單片機走過來的童鞋往往習慣於直接控制寄存器,事必躬親,從零開始實現想要的功能。而在嵌入式Linux的世界裡,我們首先要拋棄這個思想,應把它作為最後沒辦法的辦法。

就像我們想要在windows系統中編寫一個程序,首先想到的不是操作CPU芯片的寄存器,而是學習Windows API一樣。我們在嵌入式linux編程時,首先想到的應該是使用現成的驅動或軟件或Linux API。沒有的話看看能不能修改一下現成的資源為己所用。還是不行的話才考慮自己從頭開始寫。



我們常說的嵌入式linux系統,其實與電腦端運行的linux系統本質上是一樣的,都是使用的linux內核,相同的文件系統目錄結構。區別在於嵌入式linux系統多少經過裁剪的,可能在操作時你會發現,有些命令不支持,或者有些命令的個別參數不支持!還有就是內核的功能也有裁剪。 對於不同的行業,根據需求對軟硬件進行裁剪選配,這也是嵌入式linux系統廣泛應用的特點之一,具體特點包括: 1、C語言即可入門2、使用命令行


3、強大的網絡4、遠程運維5、成本低
在學習嵌入式Linux之前,肯定要有C語言基礎。彙編基礎有沒有無所謂(就那麼幾條彙編指令,用到了一看就會)。

C語言要學到什麼程度呢?越熟當然越好,不熟的話也要具備基本技能。比如寫一個數組排序、輸入數字求和什麼的。

學C語言唯一的方法是多寫程序多練習,編譯出錯沒關係,自己去解決;執行出錯沒關係,自己去分析。以前我是用VC來練習C語言的,經常去嘗試著寫一些C語言競賽的題目。它們是純C、純數學、純邏輯的題目,不涉及界面這些東西,很適合煅煉你的編程能力。

嵌入式 Linux 的一切,看這一篇就夠了

☑ 參展請聯繫:86+755-88311535【廣告】


02

整體大概是如何的流程


第一,學習基本的裸機編程。

對於學硬件的人而言,必須先對硬件的基本使用方法有感性的認識,更必須深刻認識該硬件的控制方式,如果一開始就學linux系統、學移植那麼只會馬上就陷入一個很深的漩渦。我在剛剛開始學ARM的時候是選擇ARM7(主意是當時ARM9還很貴),學ARM7的時候還是保持著學51單片機的思維,使用ADS去編程,第一個實驗就是控制led。學過一段時間ARM的人都會笑這樣很笨,實際上也不是,我倒是覺得有這個過程會好很多,因為無論做多複雜的系統最終都會落實到這些最底層的硬件控制,因此對這些硬件的控制有了感性的認識就好很多了。
  學習裸機的編程的同時要好好理解這個硬件的構架、控制原理,這些我稱他為理解硬件。所謂的理解硬件就是說,理解這個硬件是怎麼組織這麼多資源的,這些資源又是怎麼由cpu、由編程進行控制的。比如說,s3c2410中有AD轉換器,有GPIO(通用IO口),還有nandflash控制器,這些東西都有一些寄存器來控制,這些寄存器都有一個地址,那麼這些地址是什麼意思?又怎麼通過寄存器來控制這些外圍設備的運轉?還有,norflash內部的每一個單元在這個芯片的內存中都有一個相應的地址單元,那麼這些地址與剛剛說的寄存器地址又有什麼關係?他們是一樣的嗎?而與norflash相對應的nandflash內部的儲存單元並不是線性排放的,那麼s3c2410怎麼將nandflash的地址映射在內存空間上進行使用?或者簡單地說應該怎麼用nandflash?再有,使用ADS進對ARM9行編程時都需要使用到一個初始化的彙編文件,這個文件究竟有什麼用?他裡面的代碼是什麼意思?不要這個可以嗎?  諸如此類都是對硬件的理解,理解了這些東西就對硬件有很深的理解了,這對以後更深一步的學習將有很大的幫助,如果跳過這一步,我相信越往後學越會覺得迷茫,越覺得這寫東西深不可測。因為,你的根基沒打好。  不過先聲明一下,本人並沒有使用ADS對ARM9進行編程,我是學完ARM7後直接就使用ARM9學linux系統的,因此涉及使用ADS對ARM9進行編程的問題我很難回答^_^,自己去研究研究吧。  

第二,使用linux系統進行一些基本的實驗。

在買一套板子的時候一般會提供一些linux的試驗例程,好好做一段時間這個吧,這個過程也是很有意義的,也是為進一步的學習積累感性認識,你能想象一個從沒有使用過linux系統的人能學好linux的編程嗎?好好按照手冊上的例程做一做裡面的實驗,雖然有點娃娃學走路,有點弱智,但是我想很多高手都會經歷這個過程。  

第三,研究完整的linux系統的的運行過程。

所謂完整的linux系統包括哪些部分呢?
  三部分:bootloader、linux kernel(linux內核)、rootfile(根文件系統)。  那麼這3部分是怎麼相互協作來構成這個系統的呢?各自有什麼用呢?三者有什麼聯繫?怎麼聯繫?系統的執行流程又是怎麼樣的呢?搞清楚這個問題你對整個系統的運行就很清楚了,對於下一步製作這個linux系統就打下了另一個重要的根基。介紹這方面的資料網上可以挖掘到幾噸,自己好好研究吧。  

第四,開始做系統移植。

上面說到完整的linux有3部分,而且你也知道了他們之間的關係和作用,那麼現在你要做的便是自己動手學會製作這些東西。


  當然我不可能叫你編寫這些代碼,這不實現。事實上這個3者都能在網下載到相應的源代碼,但是這個源代碼不可能下載編譯後就能在你的系統上運行,需要很多的修改,直到他能運行在你的板子上,這個修改的過程就叫移植。在進行移植的過程中你要學的東西很多,要懂的相關知識也很多,等你完成了這個過程你會發現你已經算是一個初出茅廬的高手了。  在這個過程中如果你很有研究精神的話你必然會想到看源代碼。很多書介紹你怎麼閱讀linux源代碼,我不提倡無目的地去看linux源代碼,用許三多的話說,這沒有意義。等你在做移植的時候你覺得你必須去看源代碼時再去找基本好書看看,這裡我推薦一本好書倪繼利的《linux內核的分析與編程》,這是一本針對linux-2.6.11內核的書,說得很深,建議先提高自己的C語言編程水平再去看。  至於每個部分的移植網上也可以找到好多噸的資料,自己研究研究吧,不過要提醒的是,很多介紹自己經驗的東西都或多或少有所保留,你按照他說的去做總有一些問題,但是他不會告訴你怎麼解決,這時就要靠自己,如果自己都靠不住就找學長一起研究研究吧,不能保證能解決你的問題,因為他未必遇到過你的問題,不過我相信能給你一點建議,也許有助你解決問題。  這一步的最終目的是,從源代碼的官方主頁上(都是外國的,悲哀)下載標準的源代碼包,然後進行修改,最終運行在板子上。  盜用阿基米德的一句話:“給我一根網線,我能將linux搞定”。  

第五,研究linux驅動程序的編寫。

移植系統並不是最終的目的,最終的目的是開發產品,做項目,這些都要進行驅動程序的開發。
  Linux的驅動程序可以說是五花八門,linux2.4和linux2.6的編寫有相當大的區別,就是同為linux2.6但是不同版本間的驅動程序也有區別,因此編寫linux的驅動程序變都不是那麼容易的事情,對於最新版本的驅動程序的編寫甚至還沒有足夠的參考資料。那麼我的建議就是使用、移植一個不算很新的版本內核,這樣到時學驅動的編程就有足夠的資料了。  這部分的推薦書籍可以參考另一篇文章《推薦幾本學習嵌入式linux的書籍》。  

第六,研究應用程序的編寫。

做作品做項目除了編寫驅動程序,最後還要編寫應用程序。現在的趨勢是圖形應用程序的開發,而圖形應用程序中用得最多的還是qt/e函數庫。我一直就使用這個函數庫來開發自己的應用程序,不過我希望你能使用國產的MiniGUI函數庫。盜用周杰倫的廣告詞就是“支持國產,支持MiniGUI”。MiniGUI的編程比較相似Windows下的VC編程,比較容易上手,效果應該說是相當不錯的,我曾使用過來開發ARM7的程序。不過MiniGUI最大的不好就是沒有像qtopia這樣的圖形操作平臺,這大大限制了他的推廣,我曾經幻想過與北京飛漫公司(就是MiniGUI的版權擁有者)合作使用MiniGUI函數庫開發像qtopia這樣的圖形操作平臺,不過由於水平有限這隻能是幻想了,呵呵。


  完成這一步你基本就學完了嵌入式linux的全部內容了。  還有一個小小的經驗想和大家分享。我在學習嵌入式linux的過程中很少問人,客觀原因是身邊的老師、同學師兄都沒有這方面的高手,主觀原因是我不喜歡問人,喜歡自己研究解決問題。這樣做有個好處,就是可以提高自己解決問題的能力,因為做這些東西總有很多問題你難以理解,別人也沒有這方面的經驗,也不是所有問題都有人給你答案,這時必須要自己解決問題,這樣,個人的解決問題能力就顯得非常關鍵了。因此我的建議就是一般的問題到網上搜索一下,確實找不到答案了就問問高手,還是不行了就自己去研究,不要一味去等別人幫你解決問題。

嵌入式 Linux 的一切,看這一篇就夠了

☑ 參展請聯繫:86+755-88311535【廣告】


03

需要知道哪些基礎知識


一、嵌入式Linux系統的構成
1、硬件2、內核3、應用程序(形成根文件系統)


二、構建嵌入式Linux系統的主要任務
1、內核部分2、應用程序部分
嵌入式Linux的開發大致可分為三個層次:引導裝載內核、構造文件系統和圖形用戶界面。作為操作系統重要組成部分的文件系統,決定了操作系統本身的信息和用戶的數據在存儲設備上的組織形式。對嵌入式文件系統的研究、設計和開發也逐漸成為嵌入式系 統研究領域的一個方向。
三、內核精簡
在精簡內核在編譯內核之前,首先要明確需要哪些驅動和模塊,然後只選擇需要的驅動和模塊,


例如,如果系統不需要網絡支持,則可以去掉網絡模塊 。
內核一般是以壓縮方式存放的,在系統啟動時會自行解壓。
內核都是常駐內存的,當需要調用應用程序時,再把需要的程序從磁盤調入內存運行。
四、嵌入式系統的組成
1、嵌入式硬件(嵌入式處理器和嵌入式外圍設備) 2、嵌入式操作系統 3、嵌入式應用軟件
嵌入式處理器嵌入式系統的核心是各種類型的嵌入式處理器,嵌入式處理器與通用處理器最大的不同點在於,嵌入式CPU大多工作在為特定用戶群所專門設計的系統中,它將通用CPU中許多由板卡完成的任務集成到芯片內部,從而有利於嵌入式系統在設計時趨於小型化,同時還具有很高的效率和可靠性。
嵌入式處理器的體系結構經歷了從CISC(複雜指令集)至RISC(精簡指令集)和Compact RISC的轉變,位數則由4位、8位、16位、32位逐步發展到64位。目前常用的嵌入式處理器可分為低端的嵌入式微控制器(Micro Controller Unit,MCU)、中高端的嵌入式微處理器(Embedded Micro Processor Unit,EMPU)、用於計算機通信領域的嵌入式DSP處理器(Embedded Digital Signal Processor,EDSP)和高度集成的嵌入式片上系統(System On Chip,SOC)。
目前幾乎每個半導體制造商都生產嵌入式處理器,並且越來越多的公司開始擁有自主的處理器設計部門,據不完全統計,全世界嵌入式處理器已經超過1000多種,流行的體系結構有30多個系列。

嵌入式外圍設備在嵌入系統硬件系統中,除了中心控制部件(MCU、DSP、EMPU、SOC)以外,用於完成存儲、通信、調試、顯示等輔助功能的其他部件,事實上都可以算作嵌入式外圍設備。目前常用的嵌入式外圍設備按功能可以分為存儲設備、通信設備和顯示設備三類。
存儲設備主要用於各類數據的存儲,常用的有靜態易失型存儲器(RAM、SRAM)、動態存儲器(DRAM)和非易失型存儲器(ROM、EPROM、EEPROM、FLASH)三種,其中FLASH憑藉其可擦寫次數多、存儲速度快、存儲容量大、價格便宜等優點,在嵌入式領域內得到了廣泛應用。
目前存在的絕大多數通信設備都可以直接在嵌入式系統中應用,包括RS-232接口(串行通信接口)、SPI(串行外圍設備接口)、IrDA(紅外線接口)、I2C(現場總線)、USB(通用串行總線接口)、Ethernet(以太網接口)等。
由於嵌入式應用場合的特殊性,通常使用的是陰極射線管(CRT)、液晶顯示器(LCD)和觸摸板(Touch Panel)等外圍顯示設備。
嵌入式操作系統為了使嵌入式系統的開發更加方便和快捷,需要有專門負責管理存儲器分配、中斷處理、任務調度等功能的軟件模塊,這就是嵌入式操作系統。嵌入式操作系統是用來支持嵌入式應用的系統軟件,是嵌入式系統極為重要的組成部分,通常包括與硬件相關的底層驅動程序、系統內核、設備驅動接口、通信協議、圖形用戶界面(GUI)等。

嵌入式操作系統具有通用操作系統的基本特點,如能夠有效管理複雜的系統資源,能夠對硬件進行抽象,能夠提供庫函數、驅動程序、開發工具集等。但與通用操作系統相比較,嵌入式操作系統在系統實時性、硬件依賴性、軟件固化性以及應用專用性等方面,具有更加鮮明的特點。
嵌入式操作系統根據應用場合可以分為兩大類:一類是面向消費電子產品的非實時系統,這類設備包括個人數字助理(PDA)、移動電話、機頂盒(STB)等;另一類則是面向控制、通信、醫療等領域的實時操作系統,如WindRiver公司的VxWorks、QNX系統軟件公司的QNX等。
實時系統(Real Time System)是一種能夠在指定或者確定時間內完成系統功能,並且對外部和內部事件在同步或者異步時間內能做出及時響應的系統。在實時系統中,操作的正確性不僅依賴於邏輯設計的正確程度,而且與這些操作進行的時間有關,也就是說,實時系統對邏輯和時序的要求非常嚴格,如果邏輯和時序控制出現偏差將會產生嚴重後果。
實時系統主要通過三個性能指標來衡量系統的實時性,即響應時間(Response Time)、生存時間(Survival Time)和吞吐量(Throughput):
響應時間是實時系統從識別出一個外部事件到做出響應的時間。

生存時間 是數據的有效等待時間,數據只有在這段時間內才是有效的。
吞吐量   是在給定的時間內系統能夠處理的事件總數,吞吐量通常比平均響應時間的倒數要小一點。
嵌入式應用軟件嵌入式應用軟件是針對特定應用領域,基於某一固定的硬件平臺,用來達到用戶預期目標的計算機軟件,由於用戶任務可能有時間和精度上的要求,因此有些嵌入式應用軟件需要特定嵌入式操作系統的支持。嵌入式應用軟件和普通應用軟件有一定的區別,它不僅要求其準確性、安全性和穩定性等方面能夠滿足實際應用的需要,而且還要儘可能地進行優化,以減少對系統資源的消耗,降低硬件成本。
五、如何構建嵌入式Linux系統的設計步驟
嵌入式系統目前主要有:Windows CE、VxWorks、QNX等,它們都具較好的實時性、系統可靠性、任務處理隨機性等優點。但是它們的價格普遍偏高,很多開發商承受不起。因而,Linux操作系統成為嵌入式操作系統的首選。


六、Linux操作系統成為嵌入式操作系統首選的原因
在精簡內核在編譯內核之前,首先要明確需要那些驅動和模塊,然後只選擇需要的驅動和模塊,例如,如果系統不需要網絡支持,則可以去掉網絡模塊 。內核一般是以壓縮方式存放的,在系統啟動時會自行解壓。內核都是常駐內存的,當需要調用應用程序時,再把需要的程序從磁盤調入內存運行。


嵌入式 Linux 的一切,看這一篇就夠了

☑ 參展請聯繫:86+755-88311535【廣告】


04

嵌入式工程師需要哪些工具


總會看到有人說Linux上的應用程序開發是高手才可以完成的,而且這種“迷信”在目前似乎還很普遍。
然而,情況並不是這樣的,從程序庫的支持方面,Linux平臺為用戶級應用程序的開發提供了很多功能強大且豐富的程序庫,而且它們大部分是跨平臺的(Boost、OpenGL、STL、Qt、Java等)和基於POSIX標準的(glibc等),同時Linux內核還為驅動程序的開發提供了功能完備的內核接口,從開發工具方面,Linux提供了功能強大的編譯器GCC和調試器GDB,藉助它們的幫助,我們可以很輕鬆的在Linu x上開發出可移植性的應用程序。
既然如此,“迷信”又源於何來呢?一方面由於詳細介紹Linux各種開發的書籍較少,各種Linux應用在國內仍不普及,另一方面則是由於很多人在安裝好一個Linux後,苦於找不到一個得心應手的IDE環境,從而感到不知所措,畢竟,我們很多人都習慣了寫好程序後,按下F5,剩下的任務就讓IDE全權代理了。其實想在Linux下如此這般當然也沒問題。既然說到了IDE,就讓我們從它開始吧,相信選擇一個好的IDE環境是你整個學習過程的一個不錯的開始。

工欲善其事 必先利其器——IDE篇

其實Linux下有許多功能強大的IDE環境,因為從某種意義上說,Linux是專為開發者準備的操作系統,這個東西當然少不了,在這裡為讀者介紹一些比較常用的IDE。


KDevelop這是一個用Qt開發的IDE,其主要支持的語言是C / C++,
Eclipse近年來,eclipse可以說發展極為迅速,它不僅是一個以java為主的開發平臺,其功能強大的插件體系結構使得它可以被當作各種應用程序來使用。作為各種插件的載體,eclipse提供了完整的GUI接口,用戶完全可以藉助eclipse來只關心自己想做的工作。

山高月曉 水落石出——IDE後臺的故事 GCC篇

前面我們簡要介紹了一些IDE環境,其中所有C/C++相關程序的編譯都是由GCC來完成的,而IDE只不過起到了一個收集編譯信息和為我們的項目生成makefile等作用(後面我們會提到)。出於目前Linux開發的特點,C仍是系統開發的主流語言。所以,對GCC有一個全面的瞭解是很有必要的,一旦IDE不能滿足你的需求,我們要有手工打造程序的能力,而且出於學習的目的,我們往往不需要IDE生成的那些複雜的文件,為一個Hello world生成2M多的文件顯然是多餘的。
GCC的全稱是GNU Compiler Collection,從這個名字我們不難看出,GCC代表著一個編譯器的集合,目前GCC可以支持C, C++, Objective-C, Objective-C++, Fortran, Java, and Ada等語言。但是出於一般性考慮,我們這裡只討論GCC中的C/C++部分。
目前GCC的最新發布版是4.0.0,但是這個版本由於使用了新技術和新的編碼規範,很多舊的代碼都需要修改才可以通過編譯,所以並不推薦使用這個版本。而相對穩定的新版本目前是3.4.4,大家可以到GNU的主頁上更新下載。那麼究竟GCC強大在哪裡,如何使用?下面我就通過幾個簡單而實際的例子帶你看看GCC提供的強大功能。


通過Helloworld的編譯熟悉GCC的基本使用方法

似乎為所有新語言提供一個Hello World樣本程序已經成為了一種不成文的標準,人們通過它來認識語言的一些基本要素。在這裡,我們使用一個Hello World來看看如何用GCC生成可執行文件。
把上面的文件存成helloworld.c,之後打開控制檯,輸入如下的命令gcc helloworld.c –o helloworld
如果一切正常的話,你的控制檯上應該沒有任何輸出。用ls查看你的工作目錄,你會發現目錄下多了一個名為helloworld的可執行文件,之後,執行./hellworld就會看到這個程序的輸出了。
很簡單不是嗎?但是學過計算機的朋友都應該知道,程序的編譯過程要分為下圖所示的過程而GCC的強大之處就在於它允許你在上面所示的任何一個過程中停下來查看中間結果,並對其加以控制。
1. 預處理首先是預處理過程,GCC的-E選項可以讓GCC在預處理後停止編譯,並向標準輸出打印預處理過後的文件。下面的-o用於指定輸出文件的文件名。
gcc –E hellowrold.c –o helloworld.cpp
下面是helloworld.cpp的一部分的內容,我們看到,文件已經包含了stdio.h中的內容。
如果我們想執行下一步的編譯過程,可以繼續使用GCC的-x 選項,該選項用於顯示指定文件的後綴名(而不是讓編譯器根據後綴來自行判斷)。我們比較常用的language type有如下幾種,(如果讀者想獲得更為完整參數說名,請參考GCC manual):l c c-header c-cpp-outputl c++ c++-header c++-cpp-outputl assembler assembler-with-cpp



另外,下表列出了常用的GCC後綴名

嵌入式 Linux 的一切,看這一篇就夠了


當然,你也可以省略掉language type的部分,這時候GCC會根據文件的後綴名自行判斷,就像你沒有使用該選項一樣。

下面繼續我們的編譯過程
2. 編譯如果我們想獲得編譯後的源文件可以使用-S選項,該選項讓gcc只執行編譯(生成彙編文件)而不進行彙編(生成目標文件),此時,我們可以用-o選項指定輸出的彙編文件的名稱。gcc –S helloworld.cpp –o hellowrld.S


3. 彙編另外,我們還可以使用GCC的-c選項來編譯和彙編源文件而不鏈接,此時-o指定的輸出文件就是編譯後的目標文件名:gcc –x c++ -c helloworld.cpp –o helloworld.o
4. 鏈接最後,我們可以利用GCC來把我們剛才生成的.o文件鏈接成可執行程序gcc helloworld.o –o helloworld
這一次,我們使用了-o選項指定了可執行文件名,也就是說,根據輸入文件類型的不同,-o有著不同的含義。
5. 函數庫的鏈接和包含文件對於我們編寫的任和一個程序,沒有庫函數的支持是不可想象的,而當我們要使用的頭文件和函數庫不在GCC默認的搜索路徑下的時候(例如OpenGL、Qt、KDE、Boost等),我們就需要手工來告訴GCC他們的位置。
先來看頭文件路徑的指定。我們可以利用-I來指定我們希望GCC去搜索的頭文件目錄,例如我們要使用X11的程序,我們就要使用下面的選項再來看庫函數的設置:我們通過-L和-l兩個命令行選項完成任務。其中-L用於告訴GCC在中去尋找函數庫,而-l選項則告訴GCC使用用戶指定的程序庫。在Linux中,函數庫的命名是遵循UNIX約定的,即lib{lib name},例如libsocket.so,所以當你需要告訴GCC使用這些庫的時候,你就可以使用-lsocket選項。通常,這兩個命令是結合在一起使用的,例如引用X11程序庫的時候,我們可以這樣:–L/usr/X11R6/lib –lX11
另外,GCC在默認情況下使用共享庫來鏈接程序,而當你想鏈接靜態庫的時候,一定要使用-static選項,例如-lncurses -static

在這一部分的最後,我們對編譯時用到的GCC常用命令做一個簡要的總結

嵌入式 Linux 的一切,看這一篇就夠了
上面,我們提到了關於GCC編譯的常用命令,這裡另外補充一些幫助性的常用命令,他們可以讓你對GCC的基本配置和使用作一個瞭解。
在這部分的最後,我們來談一談關於構建軟件時鏈接參數的設定問題。在上面的第5部分我們已經提到了,函數庫的使用是需要-L和-l一起配合來使用的,但實際上,往往一個像樣的程序需要很多庫的支持,例如,如果你需要編寫一個GTK程序,我們需要下面的鏈接參數:

-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lglib -ldl -lXi -lXext -lX11 –lm,看上去有些嚇人,你可能會問,我如何知道需要這些呢,如果我想編寫KDE的程序呢,還有OpenGL呢?其實,情況比你想象的要好很多,在/usr/bin目錄下,有很多名為xxx-config的腳本,它們的作用就是向用戶顯示編譯鏈接程序時使用的參數的。這些腳本可以接受一些參數,比較常用的有—libs用於列出鏈接特定程序時使用的程序庫,另外--cflags用於生成頭文件的包含目錄,也就是上面我們提到的-I參數。於是,對於GTK程序,我們可以使用下面的命令來編譯:gcc gtksource.c `gtk-config –libs --cflags`
當然,為每一種程序寫一個config顯然不是一個好辦法,目前新的開發包都使用pkg-config這個腳本來生成鏈接參數。你可以使用pkg-config –list-all查看pkg-config支持的所有鏈接參數
當你在上面這份列表中查到了自己想要程序包時,就可以使用下面的命令來編譯程序了gcc .suffix `pkg-config --libs --cflags`

上面,我們提到了關於GCC編譯的常用命令,這裡另外補充一些幫助性的常用命令,他們可以讓你對GCC的基本配置和使用作一個瞭解。在這部分的最後,我們來談一談關於構建軟件時鏈接參數的設定問題。在上面的第5部分我們已經提到了,函數庫的使用是需要-L和-l一起配合來使用的,但實際上,往往一個像樣的程序需要很多庫的支持,例如,如果你需要編寫一個GTK程序,我們需要下面的鏈接參數:-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lglib -ldl -lXi -lXext -lX11 –lm,看上去有些嚇人,你可能會問,我如何知道需要這些呢,如果我想編寫KDE的程序呢,還有OpenGL呢?其實,情況比你想象的要好很多,在/usr/bin目錄下,有很多名為xxx-config的腳本,它們的作用就是向用戶顯示編譯鏈接程序時使用的參數的。這些腳本可以接受一些參數,比較常用的有—libs用於列出鏈接特定程序時使用的程序庫,另外--cflags用於生成頭文件的包含目錄,也就是上面我們提到的-I參數。於是,對於GTK程序,我們可以使用下面的命令來編譯:gcc gtksource.c `gtk-config –libs --cflags`當然,為每一種程序寫一個config顯然不是一個好辦法,目前新的開發包都使用pkg-config這個腳本來生成鏈接參數。你可以使用pkg-config –list-all查看pkg-config支持的所有鏈接參數當你在上面這份列表中查到了自己想要程序包時,就可以使用下面的命令來編譯程序了gcc .suffix `pkg-config --libs --cflags`

讓GCC幫助你更好的工作

上面我們簡單介紹了GCC的常用命令行選項,其實GCC的功能比上面提到的那些要豐富得多,GCC對代碼的警告、優化、調試等方面提供了豐富的支持,下面我們就從一些例子來看看GCC提供的這些功能。
1.對問題代碼提出警告GCC對程序代碼提供了完整的檢查功能,由於C/C++語言本身的特點,很多錯誤都是程序員無意間犯下的,例如使用了未定義的變量、在bool表達式中使用了=而不是==等等問題,利用GCC提供的代碼檢查功能,我們可以讓編譯器為我們找到這些問題,避免運行時發生災難。
首先,我們來看一個“問題代碼”
/* test_warning.c We use this file to check the warning facilities provided by GCC*/#include#includevoid main() { /* main should return int*/int a, b;long long l = 2.2; /* long long type is GNU extension, not standard ANSI / ISO type*/miss_decl(); /* We call an undeclared function*/if (a = 0) /* May be we want == here instead of =*/printf (“a really equals to 0?/n”);if (b != 0) /* We used uninitialized variables*//* %d and “We should put b here” don’t match*/printf(“We make a mistake again! b = %d/n”, “We should put b here”);};void miss_decl() {/* /* This type of annotation is prohibited*/printf(“We should put the declaration before it’s been used!/n”);}


上面這些代碼故意製造了很多編程中出現的常見問題,接下來,我們就用這段代碼來檢測一下GCC提供的各種常用的警告設施。
首先,我們不使用任何警告設施編譯上面的程序gcc test_warning.c –o test_warning
默認情況下,GCC會給出輸出,其中GCC識別出了main函數不標準(warning)以及使用了未聲明的函數(error)兩個問題,但是其他的GCC並未察覺。
利用-pedantic找出不符合ANSI / ISO標準的代碼執行下面的命令:gcc –pedantic test_warning.c –o test_warning


可以看到,這次GCC以警告的形式報告了代碼中long long的使用,但是要說明的是我們並不能依賴這個選項來保證我們的代碼完全符合ANSI / ISO標準,因為該選項只報告ANSI C要求編譯器進行檢察的內容。另外,你還可以使用-pedantic-errors讓GCC把所有的警告都變成錯誤。
利用-Wformat檢查printf中的參數不匹配問題執行下面的命令:gcc –Wformat test_warning.c –o test_warning
利用-WComment找出註釋中的錯誤執行下面的命令:gcc –WComment test_warning.c –o test_warning
利用-Wparentheses查找bool表達式中的=錯誤執行下面的命令:gcc –Wparentheses test_warning.c –o test_warning
用-Wuninitialized查找未初始化變量的使用執行下面的命令:gcc –O –Wuninitialized test_warning.c –o test_warning值得說明的是,在使用這個選項的時候,一定要配合上-O(後面我們會提到)選項
利用-Wimplicit-function-declaration / -Werror-implicit-function-declaration檢查未聲明函數的使用。
執行下面的命令:gcc -Wimplicit-function-declaration test_warning.c –o test_warning
另外-Werror-implicit-function-declaration和-Wimplicit-function-declaration作用是類似的,只是如果你使用了未聲明的函數,前者會把它認為是一個錯誤。

如果你只是想對你的代碼進行全面的檢查,你大可不必把上面的選項一併列出來,GCC提供了-Wall選項,含義就是列出所有代碼中的警告
執行下面的命令:gcc –Wall test_warning.c –o test_warning
如果你想走另一個極端,也就是不想讓gcc輸出任何警告,那麼使用-w選項,該選項禁止所有的警告執行下面的命令:gcc –w test_warning.c –o test_warnin

對於上面所有的選項,你都可以把它們和-Werror選項一起使用,這樣就可以把所有的警告都變成錯誤。另外,如果你只是想對代碼進行檢查而並不執行編譯的話,可使用-fsyntax-only選項,像下面的命令這樣gcc –fsyntax-only test_warning.c
基本上來說,我們常用的一些警告選項就是這些,而其中-Wall更是我們極為常用的功能。
2. 優化選項這一部分的內容可以分成兩部分,一部分是讓編譯器對代碼進行分析後,進行的代碼優化,另一部分是我們可以為編譯器制定一些關於硬件的信息,讓他生成對硬件結合的更好的代碼,而我們之所以要用源代碼來編譯程序,很多情況下,是出於這方面的原因。
首先來看代碼優化,從代碼的整體優化上,GCC提供了下面的選項
-O –O1這兩個選項的含義是一樣的,GCC將執行減少代碼尺寸和執行時間的優化,對於那些會嚴重影響編譯時間的優化選項,這個級別的優化並不會執行。
-O2在這一級別GCC將會提供所有支持的優化,但這其中並不包括以空間換時間的優化手段,例如編譯器不會使用循環展開和函數內聯。和-O相比,該選項進一步加快了編譯時間和生成代碼的性能。
-O3除了-O2提供的優化選項外,還指定了-finline-functions,-funswitch-loops和-fgcse-afer-reload選項,目的只有一個就是全力執行代碼優化。

-Os這個選項是專門用來優化代碼尺寸的,-Os打開了所有-O2級別中不會顯著增長代碼尺寸的優化選項
-O0該選項代表不執行優化
在這裡要說明的是,儘管GCC提供了1~3和s這4個整體優化選項,但從實際的優化效果上來看,往往O3優化出來的程序的效率並不是最高的,而大部分情況下我們都在使用-O2,如果你希望獲得最高的效率利益,那麼不妨這4個選項都試試。另外,其實這些選項只不過是GCC提供的很多單方面優化的一個組合,如果你想了解更為具體的優化內容,可以去查看GCC手冊,出於篇幅限制,這裡不細談了。最後要記住的一點是,如果你的程序是用於高精度數值計算的,那麼記住不要使用上面任何的優化選項。
下面來看基於硬件優化,由於這部分和計算機硬件相關,這裡僅用Intel的CPU做一些說明。
對於所有為Intel和AMD x86-64提供的優化選項都是用m開頭的,下面寫一些常用的選項:-march該選項用來指定CPU的類型,常用的有i386 / i486 / i586 / pentium-mmx / i686 / pentium2 / pentium3 / pentium-m / pentium4 / prescott / k6 / athlon / athlon-4 / k8等等,讀者可以根據自己的情況進行指定。
-mfpmath該選項用於指定浮點運算單元的類型。包括:387
使用標準的數學協處理器:sse
使用SSE指令集提供的標量浮點運算。在Pentium3 / Athlon-4以及更新的芯片上支持這個特性。另外,在pentium4以及AMD x86-64處理器上,SSE2還可以進行雙精度浮點計算。

sse,387混合使用387數學協處理器和SSE指令集,該選項可以充分的利用CPU的浮點寄存器和xmm寄存器,但是該選項還處在試驗階段。-malign-double
該選項使得GCC把double / long double / long long類型的變量在4字節或2字節地址上對齊,在Pentium級的CPU上,這會使得代碼的執行速度更快,當然帶來的代價是需要更多的內存來執行程序。
-mmmx –msse –msse2 –msse3 –m3dnow
這些選項用來啟動內置函數直接使用這些處理器擴展指令的功能。在編譯3D或多媒體程序的時候,使用他們是非常有效的。
3. 對調試的支持當程序出錯的時候,我們可以在Visual Studio中輕鬆的進行調試,而在Linux中,一旦出現Segmentation Fault,似乎我們除了用眼睛去看代碼就沒有更好的選擇了,其實情況不然,用GCC向程序加入一些適當的調試信息,我們可以利用GDB去調試程序。在這裡,我們介紹最為常用的-g和-ggdb選項。
先來看-g。該選項可以利用操作系統的“原生格式(native format)”生成調試信息。GDB可以直接利用這個信息。儘管我們可以把-O和-g放在一起使用,但是,這種做法是極為不推薦的。
如果你想用GDB來調試程序,那麼你可以使用-ggdb來讓GCC為GDB生成更為豐富的調試信息,但是,此時你就不能用其他的調試器來進行調試了。
最後要說明的是,上面這兩個選項都可以接受一個輸出調試信息的級別,默認的級別是2。如果你指定1級(-g1),那麼GCC會生成最少的調試信息,這包括函數和全局變量的描述信息,但是對於局部變量和行號等信息,在這個級別是不會輸出的。另外一個級別是3級(-g3),在這一級別上,GCC會為程序中的所有宏定義和符號生成調試信息。

最後
通過閱讀今天的這篇文章,希望童鞋們能過對想學習Linux開發中用到的一些基本的技術和知識有一個瞭解,並且能夠自己動手開始做些試驗性的工作,其實,這裡還有很多問題沒有談到,例如利用GDB進行調試、利用make管理工程、利用autoconf為程序生成配置腳本、利用CVS管理程序源文件等等,這些問題有待在今後一起交流。

嵌入式 Linux 的一切,看這一篇就夠了

☑ 參展請聯繫:86+755-88311535【廣告】


05

嵌入式Linuxer進階之路



Linux開發工程師

01

首先成長為一名Linux開發工程師,這是最基本的目標,他應該具備如下的素質:
(1)瞭解Linux基本概念和基本操作(歷史,文化,起源)(命令行,配置,操作,開發模式)(2)會使用Linux平臺下的編輯器vi(默認安裝,佔內存小,方便向嵌入式移植)(3)會使用Linux平臺下的編譯器GCC(相應的十幾個命令行參數)(4)會使用Linux平臺下的管理工具Make(與Makefile相配合,尤其用於大項目中多平臺移植的情況下)(5)會使用Linux平臺下的調試器GDB(可以瞭解底層硬件,及彙編指令)


嵌入式工程師

02

他更關心的是將來在哪一目標機上運行,不關心操作系統,進行的更多與編程相關的工作:(1)合格的C程序的程序員(理解基本的控制結構:循環、分支、數組、指針)(2)基本的編程思想(編程實踐中總結算法)(3)程序的編譯和鏈接(4)可執行文件內部(段、起始地址、如何加載執行)(5)目標機處理器體系結構(6)知道處理器的外設即System on Chip(內部存儲結構及外設驅動)(7)彙編語言及指令(主要是用在加載過程中)



系統工程師

04

系統工程師夾在驅動工程師及應用工程師這兩個層次之間,他要對系統有一個整體的概念,系統主要是指操作系統,往往理論有餘而實踐不足:(1)進程管理與調度(知道進程間的切換與調度,多任務)(2)內存管理(建立MMU頁表,知道從虛地址到物理地址的映射)(3)驅動管理(管理設備,編寫設備驅動,加載設備驅動)(4)文件系統(製作文件系統,文件系統的讀寫,甚至驅動有時也被當成文件系統來管理)(5)網絡協議知識(協議實現作為上層應用與底層驅動之間的銜接)(6)圖形系統知識(支持多窗口及管理窗口,包括窗口切換,窗口移動和覆蓋)




嵌入式Linux系統工程師

05

(1)嵌入式Linux基本概念(2)交叉編譯(3)Linux內核開發和調試工具(4)啟動代碼移植(修改Uboot,Vivi等)(5)內核交叉編譯(Linux不支持芯片時要芯片級的移植,支持芯片時要板級的移植)(6)根文件系統製作(7)設備驅動程序開發
下面針對一個簡單的"hello world"程序來區分一下以上所講的工程師之間的區別:#includeint main(void){printf("hello world!\n");}
初級程序員看到的是:a、頭文件b、主函數mainc、子函數printfd、函數調用e、入口參數f、參數返回
高級程序員看到的是:a、頭文件---預處理過程b、主函數main---程序入口c、子函數printf---標準庫函數d、函數調用---跳轉指令e、入口參數---棧空間的參數傳遞f、函數返回---返回指令
嵌入式程序員看到的是:a、頭文件---預處理過程---宏展開/條件編譯b、主函數main---程序入口---啟動代碼c、子函數printf---標準庫函數---鏈接過程d、函數調用---跳轉指令---PC寄存器e、入口參數---棧空間的參數傳遞---尋址方式f、函數返回---返回指令---LR寄存器
嵌入式程序員還能看到的是:

  • 可執行文件的內部組織結構

  • 代碼段

  • 指令格式和類型

  • 地址無關代碼

  • 數據段

  • RWdata讀寫數據段

  • ROdata只讀數據段

  • BSS未初始化數據段


  • 系統工程師看到的是:

  • 子函數printf---標準庫函數---鏈接過程

  • 動態鏈接(Dynamic Linking)(需要系統的支持)

  • 靜態鏈接(Static Linking)(嵌入式系統常採用靜態鏈接,以適應不同系統)

  • 標準C庫

  • 系統調用(System Call)

  • 軟件中斷(Software Interrupt)


  • Linux系統工程師看到的是:

  • 可執行文件的加載過程(不用瞭解系統調用這一級)

  • Shell進程--fork系統調用---exec系統調用

  • 進程狀態(就緒、運行、阻塞)

  • 進程調度和調度算法

  • 進程的上下文切換


  • 嵌入式Linux系統工程師看到的是:

  • 用戶空間和系統空間(分別運行用戶模式和管理模式)

  • ARM---用戶模式和管理模式(執行權限不同)

  • SWI---軟中斷指令(系統調用就是由用戶空間向系統空間切換)

  • MMU---虛地址和實地址(不同進程如何實現自己獨立的地址空間)

  • BUS---地址總線、數據總線和控制總線(程序如何獲得數據,系統如何取指)

  • SOC---片上系統(整個嵌入式系統如何運行)



嵌入式 Linux 的一切,看這一篇就夠了

☑ 參展請聯繫:86+755-88311535【廣告】


06

一位資深工程師的學習路線


我們平時使用Linux系統的話,最常用的工具就是Shell(或者用windows中常見的說法:命令行),初學者接觸Linux的第一個東西往往也是shell。也許你已經知道,把shell命令組合起來寫成一個文件,亦即shell編程,也是一門大學問,它能做的事很多很強大,但僅限於對Linux系統的操作。
我們一定不會用shell命令去編寫一個顯示屏程序,或者一個GPS導航程序。而且作為嵌入式Linux開發來說,shell不可能作為最終產品工作的平臺,因為我們不能要求用戶在屏幕中輸入代碼來實現功能。因此我認為對嵌入式開發來說,shell命令無需深究,掌握基本操作就夠了。
shell基本操作主要包括:獲取命令幫助,到達指定目錄,查看目錄內容,權限修改,文件的複製粘貼等基本操作,文件搜索,文件內容查看和編輯,系統關機重啟……(這些只是最基本的,後面再慢慢學別的命令,比如學習進程編程時,再學習進程相關的命令;學習C語言編程時,再學各種編譯和調試命令也不遲) 學習嵌入式Linux,我們的最終目的是製作一套嵌入式系統來實現功能。往往需要用C/C++或Python等其他語言來編寫程序,但是編程之前我們要先明確一些基本概念。
最基本的,當我們編寫程序時,首先要明確嵌入式Linux分為用戶空間和內核空間。用戶空間是應用程序運行的空間,內核空間就是操作系統和驅動程序運行的空間。這是從軟件的角度來說的,對應於ARM芯片來說,就是芯片的不同“工作模式”。這兩個空間是通過“地理隔離”實現互相完全獨立的,它們各自的程序使用不同的內存地址區間,各自使用自己的頭文件(有些頭文件在兩個空間內甚至是重名的,要注意區分)、各自調用屬於自己空間的函數(哪怕實現的功能相同,比如printf()和printk()),而且不能互相直接訪問(用指針也不行)。(意味著學習這兩部分的編程時要學習兩套獨立的知識體系)內核空間相關的東西有:Linux內核源碼、內核編譯和配置、內核移植、文件系統、Busybox、設備驅動程序編寫、中斷編程……

用戶空間相關的東西有:Shell、應用程序編譯和調試、進程、線程、文件IO編程、網絡通信相關、Qt圖形界面編程……如果你僅僅要開發應用程序,那你就可以遠離內核空間那些東西了。對你來說,驅動程序、底層硬件、操作系統的工作方式等都是透明的,你寫的程序在別的芯片上也能跑得很好。
但如果你想要開發驅動程序,或者定製自己的操作系統,或者你想向一片“全裸”芯片中寫入操作系統,並使它正常運行起來,那就得學習內核空間的知識了。 如果你想讓“全裸”芯片運行起來,還會遇到一塊比內核更底層的東西,Bootloader。它是在內核啟動前運行的一段程序,用來初始化硬件、建立內存空間映射等,與芯片的品牌、型號極其相關。我們通常對一些現成的Bootloader進行修改來滿足需求,常見的Bootloader有U-Boot、Vivi等。
再多說一句,如果想從零開始做一個嵌入式設備,還有更底層的問題需要解決和學習:電路設計、PCB佈線等。 因此,我們看到的嵌入式Linux書籍就可以粗略分成兩個方向:一類講嵌入式Linux應用程序編程,另一類講如何搭建一個完整的嵌入式Linux平臺。分別對應的就是用戶空間和內核空間的事情。 雖然用戶空間和內核空間是獨立的,但就像Windows提供了API,允許我們對系統進行操作一樣,用戶空間的程序也可以通過系統調用來訪問內核(就是一些的C語言函數)。但由於系統調用非常基礎,所以有時使用起來很麻煩。比如說一個簡單的給變量分配內存空間的操作,就需要動用多個系統調用。Linux定義一些庫函數(API)來將系統調用組合成某些常用的功能,以方便我們編程(同樣是C語言函數)。因此,我們在讀別人的程序時,就要區分其中的函數是系統調用,還是庫函數,還是C/C++標準庫中的函數,還是用戶自己定義的函數。如果是前三者,就可以到各個地方搜索相應的資料,這樣學習起來就快很多。

那麼shell程序和我們用C/C++編寫的程序有什麼區別呢?事實上,我們在shell中寫的每一個命令,都對應了一個程序,在程序內部就是通過調用各種API來實現相應功能的。因此用shell能實現的功能,理論上都能用C語言實現。 作為嵌入式Linux開發初學者,簡單熟悉了shell以後,就可以開始進行一些C語言編程的嘗試了。
我們最早接觸編程一般都是在大學的編程課上,而且往往用的是Visual C++ 6.0。竊以為這是讓我對編程原理長期困惑不解的罪魁禍首!啥是環境變量?為啥要設置include路徑,lib路徑?為啥一點編譯按鈕就會出來那麼多後綴名不同的文件?這些很基礎很重要的問題都被VC6.0這個外殼掩蓋了。但哪怕你在Linux中使用gcc編譯一個最簡單程序,一定就會像我一樣馬上明白把一個.c的源文件變成一個可執行文件,中間究竟發生了什麼事情。如果你再用gdb調試一個程序,就會明白得更多一點。
關於C/C++編程的基本工具,我們需要學習的有:vim等代碼編輯器、diff等文件比較的shell命令、gcc等編譯器、gdb等調試工具、交叉編譯等。這裡需要特別提到一個重要工具(網站):github,根據百度的解釋,它是一個“分佈式的版本控制系統”,初學者還用不到版本控制,那就可以單純把它當成一個開放的源代碼庫。這個網站裡有大量優秀的源代碼供學習和使用。 學習了基本的編程方法,我們就該接觸Linux的API等內容了。畢竟,我們的嵌入式系統要與設備進行交互,只用C/C++標準庫是不夠的。在此之前,需要建立一個Linux的重要概念:一切皆文件。甚至硬件設備對Linux系統來說,也是文件。這樣對設備的操作就等同於對文件進行讀、寫,或讀寫以外的操作。這部分內容在各種書籍資料中通常以“文件IO編程”命名,作為一個章節來寫。我覺得這是應當第一個來學的東西,因為看到自己能隨意操控文件和外設是一件讓人很振奮的事情!成就感是繼續學習的一大動力! 另外一個重要內容是,理解進程和線程。通過學習這個部分,能管中窺豹地大致領略到Linux系統如何進行調度,你的程序是怎麼在Linux中運行的。這是操作系統原理的內容,但作為非軟件專業出身的人,沒辦法,只能自學了。 其他應用程序編程如網絡編程、Qt圖形編程等就不一一說明了。 驅動程序可能是我們將來接觸內核空間遇到的第一個內容。不過暫時還沒什麼特別想說的。內核空間距離初學者還是有點遠的……以後再來學這部分內容。

嵌入式 Linux 的一切,看這一篇就夠了

☑ 參展請聯繫:86+755-88311535【廣告】


07

另一位資深工程師的學習之路


首先我們要明白你的目的是什麼,大概來說所謂嵌入式Linux可以分為兩部分:

底層系統

應用開發
如果你是想做應用開發,那麼你去把C語言、數據結構、JAVA什麼的學好吧。嵌入式應用開發和PC上的應用開發並沒有什麼特別要注意的。也許你說在嵌入式上要做些優化,是的,要優化,但是未經優化的程序和PC上的程序開發沒什麼差別。另外,當你有能力去優化時,你已經不用來問這個問題了。具體到某個例子,比如說開發界面,在PC上我們用VC;在嵌入式Linux裡也許我們用QT也許用Android,這個時候你應該去學學QT、Android的編程。但是基礎還是C或JAVA,在此基礎上去熟悉它們的接口。你學過VC的話,也是要花時間去了解那些類、控件的。

如果你的目的是想學習底層系統,這是我的專長,倒是可以說一點。

在回答這個問題之前,我先回答:不少人問我,到底是學驅動還是學應用?

我只能說憑興趣,並且驅動和應用並不是截然分開的

1. 我們說的驅動,其實並不侷限於硬件的操作,還有操作系統的原理、進程的休眠喚醒調度等概念。


想寫出一個好的應用,想比較好的解決應用碰到的問題,這些知識你應該懂

2. 做應用門檻低,特別是現在的ANDROID,純JAVA。做應用的發展路徑個人認為就是業務純熟。

比如在通信行業、IPTV行業、手機行業,你瞭解行業的需求。所以,當領導的人,多是做應用的。

3. 做驅動,其實我不想稱為“做驅動”,而是想稱為“做底層系統”,做好了這是通殺各行業。我工作幾年,做過手機、IPTV、會議電視,但是這些產品對我毫無差別,因為我只做底層。他們的業務跟我沒關係。

當應用出現問題,他們解決不了時,我就會從內核角度給他們出主意,給他們提供工具。

做底層的發展方向,個人認為是技術專家。

4. 其實,做底層還是做應用,之間並沒有一個界線,有底層經驗,再去做應用,你會感覺很踏實。

有了業務經驗,你再瞭解一下底層,很快就可以組成一個團隊。

回到怎麼學的問題上。嵌入式Linux底層系統包含哪些東西?不要急,舉一個例子你就知道了。


1. 電腦一開機,那些界面是誰顯示的?是BIOS,它做什麼?一些自檢,然後從硬盤上讀入windows,並啟動它。

類似的,這個BIOS對應於嵌入式Linux裡的bootloader。這個bootloader要去Flash上讀入Linux內核,並啟動它。

2. 啟動windows的目的是什麼?當然是上網聊天什麼的了。這些上網、聊天工具在哪?

在C盤、D盤上。所以,windows要先識別出C盤、D盤。在Linux下我們稱為根文件系統。

3. windows能識別出C盤、D盤,那麼肯定能讀寫硬盤才行。這涉及的東西稱為驅動程序。當然不僅僅是硬盤,還有網卡、USB等等。

嵌入式Linux能從Flash上讀出並執行應用程序,肯定也得有Flash的驅動程序啊,當然也不僅僅是Flash。

先說到這裡吧,嵌入式LINUX裡含有bootloader,內核,驅動程序、根文件系統這4大塊。

一、bootloader:

它就是一個稍微複雜的裸板程序。但是要把這裸板程序看懂寫好一點都不容易。Windows下好用的工具弱化了我們的編程能力。



很多人一玩嵌入式就用ADS、KEIL。你能回答這幾個問題嗎?

1.一上電,CPU從哪裡取指令執行?
答:一般從Flash上指令。

2.但是Flash一般是隻能讀不能直接寫的,如果我用到全局變量,這些全局變量在哪裡?
答:全局變量應該在內存裡

3.那麼誰把全局變量放到內存裡去?
答:長期用ADS、KEIL的朋友,你能回答嗎?這需要"重定位"。在ADS或KEIL裡,重定位的代碼是製作這些工具的公司幫你寫好了。
你可曾去閱讀過?

4.內存那麼大,我怎麼知道把"原來存在Flash上的內容"讀到內存的"哪個地址去"?
答:這個地址用"鏈接腳本"決定,在ADS裡有scatter文件,KEIL裡也有類似的文件。但是,你去研究過嗎?

5.你說重定位是把程序從Flash複製到內存,那麼這個程序可以讀Flash啊?
答:是的,要能操作Flash。當然不僅僅是這些,還有設置時鐘讓系統運行得更快等等。

先自問自答到這裡吧,bootloader這一個裸板程序,其實有3部分要點:

1.對硬件的操作
2.對ARM體系處理器的瞭解
3.程序的基本概念:重定位、棧、代碼段數據段BSS段什麼的。

對硬件的操作,需要看原理圖、芯片手冊。這需要一定的硬件知識,不求你能設計硬件,但是至少能看懂;不求能看懂模擬電路,但是要能看懂數字電路。這方面的能力我是在學校裡學到的,微機原理、數字電路這2本書(書名忘了)就足夠了。但是我懷疑你有無耐心把這2本書看完。我不知道現在有沒有更快捷的書。想速成的話,就先放掉這塊吧,不懂就問GOOGLE、發貼。

另外,芯片手冊是肯定要讀的,別去找中文的,就看英文的。開始是非常痛苦,以後就會發現那些語法、詞彙一旦熟悉後,讀任何芯片手冊都很容易。
對ARM體系處理器的瞭解,看杜春蕾的吧,裡面講有彙編指令,有異常模式、MMU等。也就這3塊內容需要你瞭解。

程序的基本概念,王道當然是去看編譯原理了。可惜,這類書絕對是天書級別的。勸你若非超級天才還是別去看了。就看我寫的和第1期視頻吧,別擔心,不用花錢。照著視頻把硬件相關的實驗做了,這些概念就清楚了。我還沒有發現第2套講這些概念的書或視頻,允許我盲目吹噓一回。


對於bootloader,我學習時是先看了,然後自己寫程序把各個硬件的實驗都做了一遍,比如GPIO、時鐘、SDRAM、UART、NAND。把它們都弄清楚了,組臺在一起就很容易看懂u-boot了。

總結一下,看懂硬件原理圖、看芯片手冊,這需要你自己去找資料。剩下的,就按和第1期視頻的章節目錄去學習吧。

二、內核:

想速成的人,先跨過內核的學習,直接學習怎麼寫驅動。

想成為高手,內核必須深刻了解。注意,我說的是瞭解,我沒奢望去寫出一個內核。

要對裡面的調度機制、內存管理機制、文件管理機制等等有所瞭解。

推薦兩本書:

1.通讀,請看薄的那本(浮燥的社會講求速度,呵),
2.選讀,想了解哪一塊就讀哪一節

三、驅動:

驅動包含兩部分:硬件本身的操作、驅動程序的框架。



又是硬件,還是要看得懂原理圖、讀得懂芯片手冊,多練吧。

說到驅動框架,有一些書介紹一下。LDD3,即,老外寫的那本,裡面介紹了不少概念,值得一讀。但是,它的作用也就限於介紹概念了。我基本上是入門之前用它來熟悉一下概念,入門後就扔掉了。

驅動方面比較全的介紹,應該是宋寶華的了,老實說我只看過目錄,有不少人說好,這裡推薦一下。

要想深入瞭解某一塊,絕對是超5星級推薦。你別指望把它讀完,1800多頁,上下兩冊呢。我是某一塊不清楚時,就去翻一下它。任何一部分,這書都可以講上2、3百頁,非常詳細。並且是以某個目標來帶你分析內核源碼。它以linux2.4為例,但是原理相通,同樣適用於其它版本的linux。

還有沒有其他介紹?呵呵,當然有了,韋東山Linux視頻第2期。裡對驅動講得不多,不夠深入。

於是我錄製了這期視頻。不僅僅教你怎麼寫怎麼改驅動,還教你為什麼這樣寫這樣改驅動。

每一個驅動都是現場編寫:

1.用繪圖板畫圖講解──相當於學校里老師在黑板上畫圖講解,很直觀絕對不是對著PPT念。


2.用sourceinsight當場寫程序,從第1行開始寫,每一課都是這樣。我講了20多個驅動,就寫了20多個程序。


3.寫完就編譯、測試。


4.很全面,字符設備驅動、塊設備、網卡驅動3大類齊全,硬件介紹、驅動框架分析、測試3大類齊全。

培訓機構裡教的內容,遠不及這期視頻豐富。我在多個培訓機構講過課,從沒看到哪個老師敢每一課都當場講解當場編寫代碼當場測試,除我之外!也沒看到哪個培訓機構講完這些內容──因為時間不夠,講完起碼要一個月,但是這部分基本只有2周授課時間。

把你手上的開發板所涉及的硬件,都去嘗試寫一個驅動吧。有問題就先"痛苦地思考",思考的過程中你會把很多不相關的知識
串聯起來,最終貫通。

四、根文件系統:

大家有沒有想過這2個問題:

1.對於Linux做出來的產品,有些用作監控、有些做手機、有些做平板。那麼內核啟動後,掛載根文件系統後,應該啟動哪一個應用程序呢?
答:內核不知道也不管應該啟動哪一個用戶程序。它只啟動init這一個應用程序,它對應/sbin/init。

顯然,這個應用程序就要讀取配置文件,根據配置文件去啟動用戶程序(監控、手冊界面、平板界面等等)

這個問題提示我們,文件系統的內容是有一些約定的,比如要有/sbin/init,要有配置文件



2.你寫的hello,world程序,有沒有想過裡面用到的printf是誰實現的?
答:這個函數不是你實現的,是庫函數實現的。它運行時,得找到庫。

這個問題提示我們,文件系統裡還要有庫。

簡單的自問自答到這裡,要想深入瞭解,可以看一下busybox的init.c,就可以知道init進程做的事情了。



當然,也可以看裡構建根文件系統那章。

說一下我的學習經歷吧。


1. 我在學校時讀的是物理電子專業,其實課程裡沒有教怎麼設計電路,只是教了些電子電路方面的知識。PCB的設計是在實驗室裡自學的,只設計過2層板,現在忘記得差不多了。但是保留了看原理圖、看芯片手冊的能力。

2. 選修了軟件學位,對軟件設計挺感興趣,但是也只是學了C語言、數據庫而已。憑著興趣做了不少競賽題。沒能力去參加競賽,但是把C語言練得很紮實。

3. 在實驗室、在第1家公司,就是設計些簡單的PCI卡,寫一下windows的驅動程序

4. 在第2家公司,用51單片機做車載電話,開始走上純軟件的道路。

5. 開始感到單片機的不足,辭職半年閉門學Linux,從redhat怎麼操作開始。步驟就是先看,再自己寫裸板程序操作硬件,接著到分析u-boot。同時看,對LINUX框架有所瞭解。

在寫裸板時,建議各位加強對中斷的理解,內核就是用中斷來完成各種功能的。


6. 分析完u-boot,就開始進行簡單的驅動編程了,這時候,能力還很弱。

7. 開始去中興上班,工作2年,編寫各類驅動、解決各類問題(驅動問題、幫助定位應用問題),能力得到煅煉。

相關書籍推薦


1. 硬件方面的書:微機原理、數字電路,高校裡的教材。畢業多年,忘名了。
2. Linux方面的書: ,老外寫的那本

本文由付斌整理,參考自網絡資料,圖片源自網絡;

免責聲明:整理文章為傳播相關技術,版權歸原作者所有,如有侵權,請聯繫刪除。


嵌入式 Linux 的一切,看這一篇就夠了



分享到:


相關文章: