一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

嵌入式Linux系統(tǒng)及如何開發(fā)自己的嵌入式Linux系統(tǒng)

 guitarhua 2015-11-23

嵌入式Linux系統(tǒng)及如何開發(fā)自己的嵌入式Linux系統(tǒng)

------------------------------------------------------------------ByJoelR.Williams

多數(shù)Linux系統(tǒng)是在pC平臺上運行,然而Linux作為嵌入式系統(tǒng)也是非常穩(wěn)定的。本文描繪了一個嵌入式系統(tǒng)的概覽,并展示嵌入式系統(tǒng)產(chǎn)品是如何使用Linux的。

嵌入式系統(tǒng)比摩西還老的故事

電腦用于控制設備或嵌入系統(tǒng)的歷史幾乎電腦自身的歷史一樣長。在通訊領域,六十年代晚期,電腦被用于電子電話交換機,稱為“存儲程序控制”系統(tǒng)?!半娔X”這詞那時并不普遍,存儲程序指內(nèi)存裝有程序和例程信息。存儲控制邏輯,而不是將其固化在硬件中,在當時確實是突破性的。今天,我們認為它本來就應如此。

那時的電腦是為每一個應用而定制的,按今天的標準,它們是一些不正常的、由奇怪的特殊指令和I/O設備集成在一部電腦中。

微處理器通過提供構(gòu)建大系統(tǒng)模塊的小型、低成本、CpU引擎改變了這一切。它提出了外設通過總線聯(lián)接的固定硬件架構(gòu)及稱為編程的一般編程模型。

軟件也隨著硬件提出。最初,編寫和測試軟件只有簡單的編程開發(fā)工具。每個項目實際運行的軟件通常來自于草稿的修改。編程常用匯編語言或宏語言,因為編譯器常常有缺陷和缺乏完善的調(diào)試工具。軟件構(gòu)建模塊和標準化庫只是到了七十年代才流行起來的概念。

嵌入式系統(tǒng)的商品化操作系統(tǒng)在1970年代后期才出現(xiàn),許多是用匯編語言寫成的,并且只能用于特定的微處理器,當微處理器被淘汰時,它的操作系統(tǒng)除非為新處理器重寫,否則也要被淘汰。今天,許多這類早期的系統(tǒng)成了些模糊的記憶;還有誰記得MTOS嗎?當C語言出現(xiàn)時,操作系統(tǒng)編寫的效率、穩(wěn)定性、可移植性都提高了很多。這一點在管理上立刻表現(xiàn)出來,它為微處理器被淘汰時保護軟件投資帶來了希望。對于市場來說這是一個好消息。用C語言寫成的操作系統(tǒng)今天越來越普遍。一般來說,可重復使用的軟件已經(jīng)占主導并越做越好。

在八十年代早期,我最喜歡的操作系統(tǒng)是Wendon操作系統(tǒng),大約150美元就可以得到一個C源碼庫。它是一個包,你可以通過選擇部件建立自己的操作系統(tǒng),類似在菜單上點菜。例如,你可以在庫清單上點工作排程安排和內(nèi)存管理方案。很多嵌入式系統(tǒng)的商品化操作系統(tǒng)是在八十年代出現(xiàn)的。這一熱潮持續(xù)到現(xiàn)在,今天,有很多可行的商品化操作系統(tǒng)可供選擇。一些大佬出現(xiàn)了,如VxWorks,pSOS,Neculeus和WindowsCE.

許多嵌入式系統(tǒng)根本沒有操作系統(tǒng),只有循環(huán)控制。對于一些簡單設備這是足夠的,但是隨著系統(tǒng)越來越復雜,操作系統(tǒng)就很必要了或軟件變得不可思議的復雜。不幸的是,有些復雜得可怕的嵌入式系統(tǒng)只因為設計者堅持不要操作系統(tǒng)才那么復雜。

漸漸地,更多嵌入式系統(tǒng)需要與各類網(wǎng)絡聯(lián)接,因此需要網(wǎng)絡功能。即便是酒店的門把手也嵌入了微處理器與網(wǎng)絡相聯(lián)。對于僅僅是編碼控制循環(huán)的嵌入式系統(tǒng),增加網(wǎng)絡功能將導致系統(tǒng)復雜程度提高以致要求操作系統(tǒng)。

除了商品化操作系統(tǒng),還有大量專用操作系統(tǒng)。其中大部分來自于草案,如CISCO的IOS;還有是從其他操作系統(tǒng)中派生出來的。例如,許多操作系統(tǒng)是從同一版本的BerkeleyUnix系統(tǒng)派生,因為它有完整的網(wǎng)絡功能。其他是基于主要操作系統(tǒng)的如KA9Q來自philKarn。

Linux作為嵌入式系統(tǒng)是一個帶有很多優(yōu)勢的新成員。它對許多CpU和硬件平臺都是可移植的、穩(wěn)定、功能強大、易于開發(fā)。

工具包突破ICE的障礙

開發(fā)嵌入式系統(tǒng)的關鍵的是可用的工具包。像任何工作一樣,好的工具使得工作更快更好。開發(fā)的不同階段需要不同的工具。

傳統(tǒng)上,首先用于開發(fā)嵌入式系統(tǒng)工具是內(nèi)部電路仿真器(ICE),它是一個相對昂貴的部件,用于植入微處理器與總線之間的電路中,允許使用者監(jiān)視和控制微處理器所有信號的進出。這有點難做,因為它是異體,可能會引起不穩(wěn)定。但是它提供了總線工作的清晰狀況,免了許多對硬件軟件底層工作狀況的猜測。

過去,一些工作依賴ICE為主要調(diào)試工具,用于整個開發(fā)過程。但是,一旦初始化軟件對串口支持良好的話,多數(shù)的調(diào)試可以不用ICE而用其他方法進行。較新的嵌入式系統(tǒng)利用非常清晰的微處理器設計。有時,相應工作初始碼已經(jīng)有了能夠快速獲得串口工作。這意味著沒有ICE人們也能夠方便地工作。省去ICE降低了開發(fā)的成本。一旦串口開始工作,它可以支持各種專業(yè)開發(fā)工具。

Linux是基于GNU的C編譯器,作為GNU工具鏈的一部分,與gdb源調(diào)試器一起工作。它提供了開發(fā)嵌入式Linux系統(tǒng)的所有軟件工具。這有些典型的、用于在新硬件上開發(fā)嵌入式Linux系統(tǒng)的調(diào)試工具。

1.寫入或植入引導碼

2.向串口打印字符串的編碼,如“HelloWorld”(事實上我更喜歡“Watson,ComehreIneedyou”,電話上常用的第一個詞。)

3.將gdb目標碼植入工作串口,這可與另一臺運行gdb程序的Linux主機系統(tǒng)對話。只要簡單地告訴gdb通過串口調(diào)試程序。它通過串口與測試機的gdb目標碼對話,你可以進行C源代碼調(diào)試,也可以用這個功能將更多的碼載入RAM或FlashMemory中。

4.利用gdb讓硬件和軟件初始化碼在Linux內(nèi)核啟動時工作。

5.一旦Linux內(nèi)核啟動,串口成為Linux控制口并可用于后續(xù)開發(fā)。利用kgdb,內(nèi)核調(diào)試版的gdb,這步常常不作要求,如果你與網(wǎng)絡聯(lián)接,如10BaseT,下一步你可能要啟動它。

6.如果在你的目標硬件上運行了完整的Linux內(nèi)核,你可以調(diào)試你的應用進程。利用其他的gdb或覆蓋gdb的圖形如xgdb。

什么是實時系統(tǒng)?

嵌入式系統(tǒng)常常被錯誤地分為實時系統(tǒng),盡管多數(shù)系統(tǒng)一般并不要求實時功能。實時是一個相對的詞,純化論者常常嚴格地定義實時為對一事件以預定的方式在極短的時間如微秒作出響應漸漸地,在如此短暫時間間隔內(nèi)的嚴格實時功能在專用DSp芯片或ASIC上實現(xiàn)了。只有在設計低層硬件FIFO、分散/聚集DMA引擎和定制硬件時才會有這樣的要求。

許多設計人員因為對真實的要求設有清晰的理解而對實時的要求焦慮不安。對于大多數(shù)的系統(tǒng),在一至五微秒的近似實時響應已經(jīng)足夠。同樣軟需求也是可以接受的。如Windows98已經(jīng)崩潰的中斷必須在4毫秒內(nèi)(±98%)內(nèi)、或20毫秒(±0)內(nèi)進行處理。

這種軟要求是比較容易滿足的,包括環(huán)境轉(zhuǎn)換時間、中斷等待時間、任務優(yōu)先級和排序。環(huán)境轉(zhuǎn)換時間曾是操作系統(tǒng)的一個熱門話題??傊?,多數(shù)CpU這些要求處理得很好,而且CpU的速度現(xiàn)在已經(jīng)快了很多,這個問題也就不重要了。

嚴格的實時要求通常由中斷例程或其他內(nèi)核環(huán)境驅(qū)動程序功能處理,以確保穩(wěn)定的表現(xiàn),等待時間,一旦請求出現(xiàn)要求服務的時間很大程度上取決于中斷的優(yōu)先及其他能暫時掩蓋中斷的軟件。

中斷必須進行處理和管理以確保時間要求能符合,如同許多其他的操作系統(tǒng)。在IntelX86處理器中,這工作很容易由Linux實時擴展處理。這是提供了一個以后臺任務方式運行Linux的中斷處理調(diào)度。關鍵的中斷響應不必通知Linux。因此可以得到許多對于關鍵時鐘的控制。在實時控制級和時間限制寬松的基本Linux級之間提供接口,這提供了與其他嵌入式操作系統(tǒng)相似的實時框架。因此,實時關鍵代碼是隔開的、并“設計”成滿足要求的。代碼處理的結(jié)果是以更一般的方法也許只在應用任務級。

嵌入式系統(tǒng)定義

一個觀點是如果一個應用沒有用戶界面,它必須是嵌入式的,因為用戶不能直接與之交互。當然這是簡單化的。一個電梯控制的電腦被認為是嵌入式的:按鍵選擇樓層指示燈顯示電梯的停層。對于聯(lián)網(wǎng)的嵌入式系統(tǒng),如果系統(tǒng)包含監(jiān)視和控制的網(wǎng)絡瀏覽器,這種界限就更加模糊了。更好些的定義注重系統(tǒng)的集中的功能和主要的目的。

因為Linux提供了完成嵌入功能的基本的內(nèi)核和你所需要的所有用戶界面,它是多面的。它能處理嵌入式任務和用戶界面。將Linux看作是連續(xù)的統(tǒng)一體,從一個具有內(nèi)存管理、任務切換和時間服務及其他的分拆的、微內(nèi)核到完整的服務器,支持所有的文件系統(tǒng)和網(wǎng)絡服務。

一個小型的嵌入式Linux系統(tǒng)只需要下面三個基本元素:

引導工具

Linux微內(nèi)核,由內(nèi)存管理、進程管理和事務處理構(gòu)成

初始化進程

如果要讓它能干點什么且繼續(xù)保持小型化,還得加上:

硬件驅(qū)動程序

提供所需功能的一個或更多應用程序。

再增加功能,或許需要這些

一個文件系統(tǒng)(也許在ROM或RAM中)

TCp/Ip網(wǎng)絡堆棧

存儲半過渡數(shù)據(jù)和交換用的磁盤。

硬件平臺

選擇最好的硬件是一個復雜的工作、充滿了公司其他項目的政治、偏見、傳統(tǒng),缺乏完整或精確的信息。成本經(jīng)常是關鍵的議題。當考慮成本時、確信你在考慮產(chǎn)品的整個成本、不僅是CpU。有時快的、便宜的CpU一旦加上總線邏輯和時延使之與外設一起工作,能變成一個昂貴的狗的產(chǎn)品。如果你在尋找軟件,首先是硬件已經(jīng)有產(chǎn)品了。如果你是系統(tǒng)設計者,由你決定制定實時的預算及硬件的工作是否滿意。

現(xiàn)實中需要多快的CpU來完成一項工作,然后放大三倍。奇怪,CpU理論上的速度竟與現(xiàn)實中一樣,別忘了應用程序?qū)浞掷胏ache。

想象總線的速度需要多快,如果有其他總線如pCI總線,包括進來。慢的總線或產(chǎn)生DMA阻塞的總線會降低CpU的速度造成擁擠。有集成設備的CpU是好的,因為只須調(diào)試很少的設備,并且支持通用CpU的驅(qū)動程序通常都很容易獲得。在我的項目中,芯片與外設的聯(lián)接經(jīng)常出問題或不滿足我們所需的兼容性。因為外設是集成的,不要認為這會便宜。

將10斤重的Linux塞入只能裝5斤的袋中。

對于Linux一個共同的認識是它用于嵌入式系統(tǒng)簡直是神奇極了。這可能不大對,典型的pC上的Linux對pC用戶來說功能有多。

對初學者而言,可以將內(nèi)核與任務分開,標準的Linux內(nèi)核通常駐留在內(nèi)存中,每一個應用程序都是從磁盤運到內(nèi)存上執(zhí)行。當程序結(jié)束后,它所占用的內(nèi)存就被釋放,程序就被下載了。

在一個嵌入式系統(tǒng)里,可能沒有磁盤。有兩種途徑可以消除對磁盤的依賴,這要看系統(tǒng)的復雜性和硬件的設計。

在一個簡單的系統(tǒng)里,當系統(tǒng)啟動后,內(nèi)核和所有的應用程序都在內(nèi)存里。這就是大多數(shù)傳統(tǒng)的嵌入式系統(tǒng)工作模式,它同樣可以被Linux支持。

有了Linux,就有了第二種可能性。因為Linux已經(jīng)有能力“加載”和“卸載”程序,一個嵌入式系統(tǒng)就可以利用它來節(jié)省內(nèi)存。試想一個典型的包括一個大概8MB到16MB的Flash

Memory和8MB內(nèi)存的系統(tǒng)。FlashMemory可以作為一個文件系統(tǒng)。FlashMemory驅(qū)動程序用來連接Flash

Memory和文件系統(tǒng)。作為替代,可使用FlashDisk。這Flash部件用軟件仿真磁盤。其中一個例是M-Systems的DiskOnChip,可以達到160MB。所有的程序都以文件形式存儲在Flash文件中,需要時可以裝入內(nèi)存。這種動態(tài)的、“根據(jù)需要加載”的能力是支持其它一系列功能的重要特征:

它使初始化代碼在系統(tǒng)引導后被釋放。Linux同樣有很多內(nèi)核外運行的公用程序。這些通常程序在初始化時運行一次,以后就不再運行。而且,這些公用程序可以用它們相互共有的方式,一個接一個按順序運行。這樣,相同內(nèi)存空間可以被反復使用以“召入”每一個程序,就象系統(tǒng)引導一樣。這的確可以節(jié)省內(nèi)存,特別是那些配置一次以后就不再更改的網(wǎng)絡堆棧如果Linux可加載模塊的功能包括在內(nèi)核里,驅(qū)動程序和應用程序就都可以被加載。它可以檢查硬件環(huán)境并且為硬件裝上相應的軟件。這就消除了用一個程序占用許多Flash

Memory來處理多種硬件的復雜性。

軟件的升級更模塊化。你可以在系統(tǒng)運行的時候在Flash上升級應用程序和可加載驅(qū)動程序。

配置信息和運行時間參數(shù)可以作為數(shù)據(jù)文件儲存在Flash上。

非虛擬內(nèi)存

標準Linux的另一個待征是虛擬內(nèi)存的能力。正是這種神奇的特征使應用程序員可以狂熱的編寫代碼而不計后果,不管程序有多大。程序溢出到了磁盤交換區(qū)。在沒有磁盤的嵌入式系統(tǒng)里,通常不能這么做。

在嵌入式系統(tǒng)里不需要這種強大的功能。實際上,你可能不希望它在實時的關鍵系統(tǒng)里,因為它會帶來無法控制的時間因素。這個軟件必須設計得更加精悍,以適合市面上物理內(nèi)存,就象其它嵌入式系統(tǒng)一樣。

注意由于CpU的原因,通常在Linux中保存虛擬內(nèi)存代碼是明智的,因為將它清除很費事。而且還有另外一個原因是它支持共享文本,這樣就可以使許多程序共享一個軟件。沒有這個,每一個程序都要有它自己的庫,就象printf一樣。

虛擬內(nèi)存的調(diào)入功能可以被關掉,只要將交換空間的大小設置為零。然后,如果你寫的程序比實際的內(nèi)存大,系統(tǒng)就會當作你的運行用盡了交換空間來處理;這個程序?qū)⒉粫\行,或者malloc將會失靈。

在許多CpU上,虛擬內(nèi)存提供的內(nèi)存管理可以將不同程序分開,防止它們寫到其它地址的空間上。這在嵌入式系統(tǒng)上通常不可能,因為它只支持一個簡單、扁平的地址空間。Linux的這種功能有助于其發(fā)展。它減少了胡亂的編寫程序造成系統(tǒng)崩潰的可能性。許多嵌入式系統(tǒng)基于效率方面的原因有意識使用程序間可以共享的“全局”數(shù)據(jù)。這也可以通過Linux共享內(nèi)存功能來支持,共享的只是指定的內(nèi)存部分。

文件系統(tǒng)

許多嵌入式系統(tǒng)沒有磁盤或者文件系統(tǒng)。Linux不需要它們也能運行。如前所述,應用程序任務可以和內(nèi)核一起編寫,并且在引導時作為一個影像加載。對于簡單的系統(tǒng)來說,這就夠了。然而,它缺乏前面所說的靈活性。

實際上,許多商業(yè)性嵌入式系統(tǒng),提供文件系統(tǒng)作為選項。許多或者是專用的文件系統(tǒng)或者是MS-DOS-Compatible文件系統(tǒng)。Linux提供MS-DOS-Compatible文件系統(tǒng),同時還有其它多種選擇。之所以提供其它選擇是因為它們更加強大而且具有容錯功能。Linux還具有檢查和維護的功能,商業(yè)性供應商往往不提供這些。這對于Flash系統(tǒng)來說尤其重要,因為它是通過網(wǎng)絡更新的。如果系統(tǒng)在升級過程中失去了能力,那它就沒有用了。維護的功能通??梢越鉀Q這類問題。

文件系統(tǒng)可以被放在傳統(tǒng)的磁盤驅(qū)動器、FlashMemory或其它這類的介質(zhì)上。而且,用于暫時保存文件,一個小RAM盤就足夠了。FlashMemories被分割成塊。這些塊中也許包括一個含有當CpU啟動時運行的最初的軟件的引導塊。這可能包括Linux引導代碼。剩余的Flash可以用作文件系統(tǒng)。Linux的內(nèi)核可以通過引導代碼從Flash復制到RAM,或者還有一個選擇,內(nèi)核可以被存儲在Flash的一個獨立部分,并且直接從那里執(zhí)行。

另外對于一些系統(tǒng)來說還有一個有趣的選擇,那就是將一個便宜的CD-ROM包含在內(nèi)。這比FlashMemory便宜,而且通過交換CD-ROM支持簡單的升級。有了這個,Linux只要從CD-ROM上引導,并且象從硬盤上一樣從CD-ROM上獲得所有的程序。

最后,對于聯(lián)網(wǎng)的嵌入式系統(tǒng)來說,Linux支持NFS(NetworkFileSystem)。這為實現(xiàn)聯(lián)網(wǎng)系統(tǒng)的許多增值功能打開了大門。第一,它允許通過網(wǎng)絡上加載應用程序。這是控制軟件修改的基礎,因為每一個嵌入式系統(tǒng)的軟件都可以在一個普通的服務器上加載。它在運行的時候也可以用來輸入或輸出大量的數(shù)據(jù)、配置和狀態(tài)信息。這對用戶監(jiān)督和控制來說是一個非常強大的功能。舉例來說,嵌入式系統(tǒng)可以建立一個小的RAM磁盤,包含的文件中有與當前狀態(tài)信息同步的內(nèi)容。其它系統(tǒng)可以簡單的把這個RAM磁盤設置為基于網(wǎng)絡的遠程磁盤,并且空中存取狀態(tài)文件。這就允許另一個機器上的Web服務器通過簡單的CGIScript存取狀態(tài)信息。在其它電腦上運行的其它應用程序包可以很容易的存取數(shù)據(jù)。對更復雜的監(jiān)控,應用程序包如Matlab引導LILO和BIOS在哪里

當一個微處理器第一次啟動的時候,它開始在預先設置的地址上執(zhí)行指令。通常在那里有一些只讀內(nèi)存,包括初始化或引導代碼。在pC上,這是BIOS。它執(zhí)行了一些低水平的CpU初始化和其它硬件的配置。BIOS繼續(xù)辨認哪個磁盤里有操作系統(tǒng),把操作系統(tǒng)復制到RAM并且轉(zhuǎn)向它。實際上,這非常復雜,但對我們的目標來說也非常重要。在pC上運行的Linux依靠pC的BIOS來提供這些配置和OS加載功能。

在一個嵌入式系統(tǒng)里經(jīng)常沒有這種BIOS。這樣你就要提供同等的啟動代碼。幸運的是,嵌入式系統(tǒng)并不需要pCBIOS引導程序那樣的靈活性,因為它通常只需要處理一個硬件的配置。這個代碼更簡單也更枯燥。它只是一指令清單,將固定的數(shù)字塞到硬件寄存器中去。然而,這是關鍵的代碼,因為這些數(shù)值要與你的硬件相符而且要按照特定的順序進行。所以在大多數(shù)情況下,一個最小的通電自檢模塊,可以檢查內(nèi)存的正常運行、讓LED閃爍,并且驅(qū)動其它必須的硬件以使主LinuxOS啟動和運行。這些啟動代碼完全根據(jù)硬件決定,不可隨意移動。

幸運的是,許多系統(tǒng)都有為核心微處理器和內(nèi)存所定制的菜單式硬件設計。典型的是,芯片制造商有一個樣本主板,可以用來作為設計的參考或多或少與新設計相同。通常這些菜單式設計的啟動代碼是可以獲得的,它可以根據(jù)你的需要輕易的修改。在少數(shù)情況下,啟動代碼需要重新編寫。為了測試這些代碼,你可以使用一個包含‘模擬內(nèi)存’的電路內(nèi)置模擬器,它可以代替目標內(nèi)存。你把代碼裝到模擬器上并通過模擬器調(diào)試。如果這樣不行,你可以跳過這一步,但這樣就要一個更長的調(diào)試周期。

這個代碼最終要在較為穩(wěn)定的內(nèi)存上運行,通常是Flash或EpROM芯片。你需要使用一些方法將代碼放在芯片上。怎么做,要根據(jù)“目標”硬件和工具來定。

一種流行的方法是把Flash或EpROM芯片插入EpROM或Flash燒制器。這將把你的程序“燒”(存)入芯片。然后,把芯片插入你的目標板的插座,打開電源。這個方法需要板上配有插座,但有些設備是不能配插座的。另一個方法是通過一個JTAG界面。一些芯片有JTAG界面可以用來對芯片進行編程。這是最方便的方法。芯片可以永遠被焊在主板上,一個小電纜從板上的JTAG連接器,通常是一個pC卡,聯(lián)到JTAG界面。下面是pC運行JTAG界面所需的一些慣用程序。這個設備還可以用來小量生產(chǎn)。

健壯性比政治家的承諾更可靠。

在pC硬件上運行時,Linux是非??煽亢头€(wěn)定的,特別是和現(xiàn)在流行的一些操作系統(tǒng)相比。嵌入式內(nèi)核本身有多穩(wěn)定呢?對大多數(shù)微處理器來說,Linux非常好。移植到新微處理器家族的Linux內(nèi)核運行起來與本微處理器一樣穩(wěn)定。它經(jīng)常被移植到一個或多個特定的主板上。這些板包括特定的外圍設備和CpU。

幸運的是,許多代碼是與處理器的,所以移植集中在差異上。其中大多數(shù)是在內(nèi)存管理和中斷控制領域。一旦成功移植,它們就非常穩(wěn)定。前面我們討論過,引導策略廣泛依賴于硬件要求,而且你必須有計劃地做一些定制的工作。

設備驅(qū)動程序更加混亂:有些穩(wěn)定有些不穩(wěn)定。而且選擇很有限;一旦你離開了通用的pC平臺,你需要自己編寫。幸運的是,周圍有許多驅(qū)動程序,你可能可以找到一個與你的需求相近的修改一下。這種驅(qū)動程序界面已定義好。許多類的驅(qū)動程序都非常相近,所以把磁盤、網(wǎng)絡或一系列的端口驅(qū)動程序從一個設備移植到另一個設備上通常并不難。我發(fā)現(xiàn)許多驅(qū)動程序都寫得很好,很容易理解,但你還是要準備一本關于內(nèi)核結(jié)構(gòu)的書在手頭。依我的經(jīng)驗,Linux至少和我用過的著名的商業(yè)性操作系統(tǒng)一樣穩(wěn)定。總之,這些操作系統(tǒng)和Linux的問題在于對工作過程微秒之處的誤解,而不在于代碼的難度或基本的設計錯誤。任何操作系統(tǒng)都有很多爭論不休的故事,這里不需要重復。Linux的優(yōu)勢在于源代碼是公開、注釋清晰和文檔齊全的。這樣,你就可以控制和處理所出現(xiàn)的任何問題。

伴隨著基本內(nèi)核和驅(qū)動程序,還有其它問題。如果系統(tǒng)有一個硬盤,那么文件系統(tǒng)的可靠性就成問題。我們有用磁盤進行Linux系統(tǒng)設計超過兩年的經(jīng)驗。這些系統(tǒng)幾乎從未正常關閉過。電源隨時都可能被中斷。感覺非常好,使用的是標準(EXT2)文件系統(tǒng)。標準Linux初始化腳本運行fsck程序,它在檢查和清除不穩(wěn)定的inodes方面非常有效。將默認的每隔30秒運行更新程序改為每隔5或10秒運行是比較明智的。這樣縮短了數(shù)據(jù)在進入磁盤之前,待在高速緩沖存儲器內(nèi)的時間,降低了丟失數(shù)據(jù)的可能性。

如何發(fā)展

嵌入式Linux的確有它的缺陷。比如,雖然它并不比某些商業(yè)競爭對手差多少,但它的確是個貪婪的存儲器。這可以通過減少一些不必要的功能來彌補,但這可能會花很長的時間,而且如果不仔細的話,還可能帶來很大的困擾。

許多Linux的應用程序都要用到虛擬內(nèi)存,在許多嵌入式系統(tǒng)中,是沒有價值的,所以不要以為一個沒有磁盤的嵌入式系統(tǒng)可以運行任何Linux應用程序。

內(nèi)核調(diào)試工具都不怎么好,特別是在較底層的。kgdb可以使錯誤定位非常容易,你只要重新啟動。不幸的是,打印語句更麻煩。

然而,對我來說最糟糕的是心理上的問題。Linux非常的靈活。嵌入式系統(tǒng)總的來說卻不靈活;而且它們完全是為最有效實現(xiàn)預定功能而嚴格設計的?,F(xiàn)在的趨勢是保持靈活性、保持總體目標功能、盡量少做修改。這個目標是崇高的,但是,所付出的代價將是針對具體的工作做出巨大的調(diào)整。保持靈活性將導致額外的工作,帶著額外的軟件包,而且有時還要降低性能。一個反復出現(xiàn)的例子就是配置??紤]在一個網(wǎng)絡界面配置Ip地址,這通常是通過從啟動script上運行ifconfig程序來完成的。這是一個28K的程序,從配置文件上調(diào)用數(shù)據(jù),可以用幾行代碼代替,初始化合適的結(jié)構(gòu)。然而,即使這非常合理,但它仍然有害,因為它用一種從未使用過的方法扭曲了軟件。

想成為嵌入式程序員應知道的0x10個基本問題 [轉(zhuǎn)]

C語言測試:想成為嵌入式程序員應知道的0x10個基本問題

C語言測試是招聘嵌入式系統(tǒng)程序員過程中必須而且有效的方法。這些年,我既參加也組織了許多這種測試,在這過程中我意識到這些測試能為帶面試者和被面試者提供許多有用信息,此外,撇開面試的壓力不談,這種測試也是相當有趣的。

從被面試者的角度來講,你能了解許多關于出題者或監(jiān)考者的情況。這個測試只是出題者為顯示其對ANSI標準細節(jié)的知識而不是技術技巧而設計嗎?這個愚蠢的問題嗎?如要你答出某個字符的ASCII值。這些問題著重考察你的系統(tǒng)調(diào)用和內(nèi)存分配策略方面的能力嗎?這標志著出題者也許花時間在微機上而不上在嵌入式系統(tǒng)上。如果上述任何問題的答案是"是"的話,那么我知道我得認真考慮我是否應該去做這份工作。

從面試者的角度來講,一個測試也許能從多方面揭示應試者的素質(zhì):最基本的,你能了解應試者C語言的水平。不管怎么樣,看一下這人如何回答他不會的問題也是滿有趣。應試者是以好的直覺做出明智的選擇,還是只是瞎蒙呢?當應試者在某個問題上卡住時是找借口呢,還是表現(xiàn)出對問題的真正的好奇心,把這看成學習的機會呢?我發(fā)現(xiàn)這些信息與他們的測試成績一樣有用。

有了這些想法,我決定出一些真正針對嵌入式系統(tǒng)的考題,希望這些令人頭痛的考題能給正在找工作的人一點幫住。這些問題都是我這些年實際碰到的。其中有些題很難,但它們應該都能給你一點啟迪。

這個測試適于不同水平的應試者,大多數(shù)初級水平的應試者的成績會很差,經(jīng)驗豐富的程序員應該有很好的成績。為了讓你能自己決定某些問題的偏好,每個問題沒有分配分數(shù),如果選擇這些考題為你所用,請自行按你的意思分配分數(shù)。

預處理器(preprocessor)

1.用預處理指令#define聲明一個常數(shù),用以表明1年中有多少秒(忽略閏年問題)

#defineSECONDS_pER_YEAR(60*60*24*365)UL

我在這想看到幾件事情:

;#define語法的基本知識(例如:不能以分號結(jié)束,括號的使用,等等)

;懂得預處理器將為你計算常數(shù)表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。

;意識到這個表達式將使一個16位機的整型數(shù)溢出-因此要用到長整型符號L,告訴編譯器這個常數(shù)是的長整型數(shù)。

;如果你在你的表達式中用到UL(表示無符號長整型),那么你有了一個好的起點。記住,第一印象很重要。

2.寫一個"標準"宏MIN,這個宏輸入兩個參數(shù)并返回較小的一個。

#defineMIN(A,B)((A)<=(B)?(A):(B))

這個測試是為下面的目的而設的:

;標識#define在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符變?yōu)闃藴蔆的一部分,宏是方便產(chǎn)生嵌入代碼的唯一方法,對于嵌入式系統(tǒng)來說,為了能達到要求的性能,嵌入代碼經(jīng)常是必須的方法。

;三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產(chǎn)生比if-then-else更優(yōu)化的代碼,了解這個用法是很重要的。

;懂得在宏中小心地把參數(shù)用括號括起來

;我也用這個問題開始討論宏的副作用,例如:當你寫下面的代碼時會發(fā)生什么事?

least=MIN(*p++,b);

3.預處理器標識#error的目的是什么?

如果你不知道答案,請看參考文獻1。這問題對區(qū)分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種問題的答案。當然如果你不是在找一個書呆子,那么應試者最好希望自己不要知道答案。

死循環(huán)(Infiniteloops)

4.嵌入式系統(tǒng)中經(jīng)常要用到無限循環(huán),你怎么樣用C編寫死循環(huán)呢?

這個問題用幾個解決方案。我首選的方案是:

while(1)

{

}

一些程序員更喜歡如下方案:

for(;

{

}

這個實現(xiàn)方式讓我為難,因為這個語法沒有確切表達到底怎么回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的基本原理。如果他們的基本答案是:"我被教著這樣做,但從沒有想到過為什么。"這會給我留下一個壞印象。

第三個方案是用goto

Loop:

...

gotoLoop;

應試者如給出上面的方案,這說明或者他是一個匯編語言程序員(這也許是好事)或者他是一個想進入新領域的BASIC/FORTRAN程序員。

數(shù)據(jù)聲明(Datadeclarations)

5.用變量a給出下面的定義

a)一個整型數(shù)(Aninteger)

b)一個指向整型數(shù)的指針(Apointertoaninteger)

c)一個指向指針的的指針,它指向的指針是指向一個整型數(shù)(Apointertoapointertoanintege)r

d)一個有10個整型數(shù)的數(shù)組(Anarrayof10integers)

e)一個有10個指針的數(shù)組,該指針是指向一個整型數(shù)的。(Anarrayof10pointerstointegers)

f)一個指向有10個整型數(shù)數(shù)組的指針(Apointertoanarrayof10integers)

g)一個指向函數(shù)的指針,該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)(Apointertoafunctionthattakesanintegerasanargumentandreturnsaninteger)

h)一個有10個指針的數(shù)組,該指針指向一個函數(shù),該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)(Anarrayoftenpointerstofunctionsthattakeanintegerargumentandreturnaninteger)

答案是:

a)inta;//Aninteger

b)int*a;//Apointertoaninteger

c)int**a;//Apointertoapointertoaninteger

d)inta[10];//Anarrayof10integers

e)int*a[10];//Anarrayof10pointerstointegers

f)int(*a)[10];//Apointertoanarrayof10integers

g)int(*a)(int);//Apointertoafunctionathattakesanintegerargumentandreturnsaninteger

h)int(*a[10])(int);//Anarrayof10pointerstofunctionsthattakeanintegerargumentandreturnaninteger

人們經(jīng)常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應試者如果不知道所有的答案(或至少大部分答案),那么也就沒有為這次面試做準備,如果該面試者沒有為這次面試做準備,那么他又能為什么出準備呢?

Static

6.關鍵字static的作用是什么?

這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用:

;在函數(shù)體,一個被聲明為靜態(tài)的變量在這一函數(shù)被調(diào)用過程中維持其值不變。

;在模塊內(nèi)(但在函數(shù)體外),一個被聲明為靜態(tài)的變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問。它是一個本地的全局變量。

;在模塊內(nèi),一個被聲明為靜態(tài)的函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用。那就是,這個函數(shù)被限制在聲明它的模塊的本地范圍內(nèi)使用。

大多數(shù)應試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化數(shù)據(jù)和代碼范圍的好處和重要性。

Const

7.關鍵字const有什么含意?

我只要一聽到被面試者說:"const意味著常數(shù)",我就知道我正在和一個業(yè)余者打交道。去年DanSaks已經(jīng)在他的文章里完全概括了const的所有用法,因此ESp(譯者:EmbeddedSystemsprogramming)的每一位讀者應該非常熟悉const能做什么和不能做什么.如果你從沒有讀到那篇文章,只要能說出const意味著"只讀"就可以了。盡管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)

如果應試者能正確回答這個問題,我將問他一個附加的問題:

下面的聲明都是什么意思?

constinta;

intconsta;

constint*a;

int*consta;

intconst*aconst;

/******/

前兩個的作用是一樣,a是一個常整型數(shù)。第三個意味著a是一個指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。第四個意思a是一個指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。最后一個意味著a是一個指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時指針也是不可修改的)。如果應試者能正確回答這些問題,那么他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字const,也還是能很容易寫出功能正確的程序,那么我為什么還要如此看重關鍵字const呢?我也如下的幾下理由:

;關鍵字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數(shù)為常量是為了告訴了用戶這個參數(shù)的應用目的。如果你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多余的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的。)

;通過給優(yōu)化器一些附加的信息,使用關鍵字const也許能產(chǎn)生更緊湊的代碼。

;合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數(shù),防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現(xiàn)。

Volatile

8.關鍵字volatile有什么含意?并給出三個不同的例子。

一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:

;并行設備的硬件寄存器(如:狀態(tài)寄存器)

;一個中斷服務子程序中會訪問到的非自動變量(Non-automaticvariables)

;多線程應用中被幾個任務共享的變量

回答不出這個問題的人是不會被雇傭的。我認為這是區(qū)分C程序員和嵌入式系統(tǒng)程序員的最基本的問題。搞嵌入式的家伙們經(jīng)常同硬件、中斷、RTOS等等打交道,所有這些都要求用到volatile變量。不懂得volatile的內(nèi)容將會帶來災難。

假設被面試者正確地回答了這是問題(嗯,懷疑是否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。

;一個參數(shù)既可以是const還可以是volatile嗎?解釋為什么。

;一個指針可以是volatile嗎?解釋為什么。

;下面的函數(shù)有什么錯誤:

intsquare(volatileint*ptr)

{

return*ptr**ptr;

}

下面是答案:

;是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。

;是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。

;這段代碼有點變態(tài)。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:

intsquare(volatileint*ptr)

{

inta,b;

a=*ptr;

b=*ptr;

returna*b;

}

由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結(jié)果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:

longsquare(volatileint*ptr)

{

inta;

a=*ptr;

returna*a;

}

位操作(Bitmanipulation)

9.嵌入式系統(tǒng)總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼,第一個設置a的bit3,第二個清除a的bit3。在以上兩個操作中,要保持其它位不變。

對這個問題有三種基本的反應

;不知道如何下手。該被面者從沒做過任何嵌入式系統(tǒng)的工作。

;用bitfields。Bitfields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時也保證了的你的代碼是不可重用的。我最近不幸看到Infineon為其較復雜的通信芯片寫的驅(qū)動程序,它用到了bitfields因此完全對我無用,因為我的編譯器用其它的方式來實現(xiàn)bitfields的。從道德講:永遠不要讓一個非嵌入式的家伙粘實際硬件的邊。

;用#defines和bitmasks操作。這是一個有極高可移植性的方法,是應該被用到的方法。最佳的解決方案如下:

#defineBIT3(0x1<<3)

staticinta;

voidset_bit3(void){

a|=BIT3;

}

voidclear_bit3(void){

a&=~BIT3;

}

一些人喜歡為設置和清除值而定義一個掩碼同時定義一些說明常數(shù),這也是可以接受的。我希望看到幾個要點:說明常數(shù)、|=和&=~操作。

訪問固定的內(nèi)存位置(Accessingfixedmemorylocations)

10.嵌入式系統(tǒng)經(jīng)常具有要求程序員去訪問某特定的內(nèi)存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。

這一問題測試你是否知道為了訪問一絕對地址把一個整型數(shù)強制轉(zhuǎn)換(typecast)為一指針是合法的。這一問題的實現(xiàn)方式隨著個人風格不同而不同。典型的類似代碼如下:

int*ptr;

ptr=(int*)0x67a9;

*ptr=0xaa55;

Amoreobscureapproachis:

一個較晦澀的方法是:

*(int*const)(0x67a9)=0xaa55;

即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。

中斷(Interrupts)

11.中斷是嵌入式系統(tǒng)中重要的組成部分,這導致了很多編譯開發(fā)商提供一種擴展—讓標準C支持中斷。具代表事實是,產(chǎn)生了一個新的關鍵字__interrupt。下面的代碼就使用了__interrupt關鍵字去定義了一個中斷服務子程序(ISR),請評論一下這段代碼的。

__interruptdoublecompute_area(doubleradius)

{

doublearea=pI*radius*radius;

printf("\\nArea=%f",area);

returnarea;

}

這個函數(shù)有太多的錯誤了,以至讓人不知從何說起了:

;ISR不能返回一個值。如果你不懂這個,那么你不會被雇用的。

;ISR不能傳遞參數(shù)。如果你沒有看到這一點,你被雇用的機會等同第一項。

;在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。

;與第三點一脈相承,printf()經(jīng)常有重入和性能上的問題。如果你丟掉了第三和第四點,我不會太為難你的。不用說,如果你能得到后兩點,那么你的被雇用前景越來越光明了。

*****

代碼例子(Codeexamples)

12.下面的代碼輸出是什么,為什么?

voidfoo(void)

{

unsignedinta=6;

intb=-20;

(a+b>6)?puts(">6":puts("<=6";

}

這個問題測試你是否懂得C語言中的整數(shù)自動轉(zhuǎn)換原則,我發(fā)現(xiàn)有些開發(fā)者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是">6"。原因是當表達式中存在有符號類型和無符號類型時所有的操作數(shù)都自動轉(zhuǎn)換為無符號類型。因此-20變成了一個非常大的正整數(shù),所以該表達式計算出的結(jié)果大于6。這一點對于應當頻繁用到無符號數(shù)據(jù)類型的嵌入式系統(tǒng)來說是豐常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。

13.評價下面的代碼片斷:

unsignedintzero=0;

unsignedintcompzero=0xFFFF;

/*1\scomplementofzero*/

對于一個int型不是16位的處理器為說,上面的代碼是不正確的。應編寫如下:

unsignedintcompzero=~0;

這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經(jīng)驗里,好的嵌入式程序員非常準確地明白硬件的細節(jié)和它的局限,然而pC機程序往往把硬件作為一個無法避免的煩惱。

到了這個階段,應試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那么這個測試就在這里結(jié)束了。但如果顯然應試者做得不錯,那么我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優(yōu)秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧...

動態(tài)內(nèi)存分配(Dynamicmemoryallocation)

14.盡管不像非嵌入式計算機那么常見,嵌入式系統(tǒng)還是有從堆(heap)中動態(tài)分配內(nèi)存的過程的。那么嵌入式系統(tǒng)中,動態(tài)分配內(nèi)存可能發(fā)生的問題是什么?

這里,我期望應試者能提到內(nèi)存碎片,碎片收集的問題,變量的持行時間等等。這個主題已經(jīng)在ESp雜志中被廣泛地討論過了(主要是p.J.plauger,他的解釋遠遠超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應試者進入一種虛假的安全感覺后,我拿出這么一個小節(jié)目:

下面的代碼片段的輸出是什么,為什么?

char*ptr;

if((ptr=(char*)malloc(0))==

NULL)

else

puts("Gotanullpointer";

puts("Gotavalidpointer";

這是一個有趣的問題。最近在我的一個同事不經(jīng)意把0值傳給了函數(shù)malloc,得到了一個合法的指針之后,我才想到這個問題。這就是上面的代碼,該代碼的輸出是"Gotavalidpointer"。我用這個來開始討論這樣的一問題,看看被面試者是否想到庫例程這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。

Typedef

:

15Typedef在C語言中頻繁用以聲明一個已經(jīng)存在的數(shù)據(jù)類型的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子:

#definedpSstructs*

typedefstructs*tpS;

以上兩種情況的意圖都是要定義dpS和tpS作為一個指向結(jié)構(gòu)s指針。哪種方法更好呢?(如果有的話)為什么?

這是一個非常微妙的問題,任何人答對這個問題(正當?shù)脑颍┦菓敱还驳?。答案是:typedef更好。思考下面的例子:

dpSp1,p2;

tpSp3,p4;

第一個擴展為

structs*p1,p2;

.

上面的代碼定義p1為一個指向結(jié)構(gòu)的指,p2為一個實際的結(jié)構(gòu),這也許不是你想要的。第二個例子正確地定義了p3和p4兩個指針。

晦澀的語法

16.C語言同意一些令人震驚的結(jié)構(gòu),下面的結(jié)構(gòu)是合法的嗎,如果是它做些什么?

inta=5,b=7,c;

c=a+++b;

這個問題將做為這個測驗的一個愉快的結(jié)尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實際上會爭論這個問題,根據(jù)最處理原則,編譯器應當能處理盡可能所有合法的用法。因此,上面的代碼被處理成:

c=a+++b;

因此,這段代碼持行后a=6,b=7,c=12。

如果你知道答案,或猜出正確答案,做得好。如果你不知道答案,我也不把這個當作問題。我發(fā)現(xiàn)這個問題的最大好處是這是一個關于代碼編寫風格,代碼的可讀性,代碼的可修改性的好的話題。

好了,伙計們,你現(xiàn)在已經(jīng)做完所有的測試了。這就是我出的C語言測試題,我懷著愉快的心情寫完它,希望你以同樣的心情讀完它。如果是認為這是一個好的測試,那么盡量都用到你的找工作的過程中去吧。天知道也許過個一兩年,我就不做現(xiàn)在的工作,也需要找一個。

NigelJones是一個顧問,現(xiàn)在住在Maryland,當他不在水下時,你能在多個范圍的嵌入項目中找到他。他很高興能收到讀者的來信,他的email地址是:NAJones@compuserve.com。

References

;Jones,Nigel,"Inpraiseofthe#errordirective,"EmbeddedSystemsprogramming,September1999,p.114.

;Jones,Nigel,"EfficientCCodeforEight-bitMCUs,"EmbeddedSystemsprogramming,November1998,p.66.

2006-5-8

1 學習嵌入式過程中的主要課程 [原][原]embedded system & embedded linux

這是一個新的開端,我將開始一個新的歷程,一個我不知道是不是會讓自己重新站起來的路途,一切是未知的,可它是全新的。

我將自己的網(wǎng)頁將全部改向技術類的,而向嵌入式系統(tǒng),尤其是嵌入式LINUX。嵌入式軟件的開發(fā),并不同于現(xiàn)在桌面軟件的開發(fā),它最大的特點就是采用交叉編譯環(huán)境進行開發(fā),而且它涉及到的問題牽扯到軟硬件整體的效能,如果前期的系統(tǒng)分析做不好,在后來的應用開發(fā)會遇到很大的困難。當然,把各種問題都涉及到是不太可能的,可如果把前期工作做的更好一些,你后來的工作會感到很輕松。做開發(fā)前,應該對應用層面做具體的分析.

在將到的一個星期里面,我將寫一些自己這半年來學嵌入式的問題總結(jié)下來。先做一個計劃,先寫三篇,題目擬了如下:

1學習嵌入式過程中的主要課程

2嵌入式開發(fā)環(huán)境的搭建(入門)

3嵌入式LINUX開發(fā)環(huán)境的建立

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    九九热精彩视频在线播放| 国产日韩在线一二三区| 日韩精品中文字幕在线视频| 精品国产亚洲av成人一区| 日韩成人动画在线观看 | 亚洲精品av少妇在线观看| 丝袜视频日本成人午夜视频| 日本熟女中文字幕一区| 日韩日韩日韩日韩在线| 中文字幕乱码亚洲三区| 日韩和欧美的一区二区三区| 91精品日本在线视频| 激情偷拍一区二区三区视频 | 国产女高清在线看免费观看| 国产精品一区二区三区激情| 国产精品午夜小视频观看| 91欧美一区二区三区成人| 欧美日韩精品综合在线| 国产精品亚洲精品亚洲| 中文字幕一区二区三区大片| 欧美一区二区三区性视频 | 日韩黄色一级片免费收看| 美女激情免费在线观看| 国产高清视频一区不卡| 国产亚洲精品一二三区| 九九热视频经典在线观看| 国产成人午夜在线视频| 日本高清二区视频久二区| 丰满少妇被猛烈插入在线观看| 国产亚洲欧美另类久久久| 美女被后入福利在线观看| 香蕉尹人视频在线精品| 日韩人妻精品免费一区二区三区 | 国产乱人伦精品一区二区三区四区| 欧美精品二区中文乱码字幕高清 | 亚洲中文在线观看小视频| 日韩精品中文在线观看| 午夜精品黄片在线播放| 日本欧美在线一区二区三区| 热久久这里只有精品视频| 免费观看日韩一级黄色大片|