簡介
Linux是一種很受歡迎的作業系統,它與UNIX系統兼容,
開放原始碼。它原本被設計為桌面系統,現在廣泛套用於伺服器領域。而更大的影響在於它正逐漸的套用於
嵌入式設備。uClinux正是在這種氛圍下產生的。在uClinux這個英文單詞中u表示Micro,小的意思,C表示Control,控制的意思,所以uClinux就是Micro-Control-Linux,字面上的理解就是"針對微控制領域而設計的Linux系統"。
uClinux是嵌入式Linux領域非常重要的分支,已成功套用於路由器、機頂盒、PDA等領域,與標準Linux在記憶體管理方面有著本質的區別。
uCLinux是一種優秀的嵌入式Linux版本,是micro-Controller-Linux的縮寫。它秉承了標準Linux的優良特性, 經過各方面的小型化改造,形成了一個高度最佳化的、代碼緊湊的嵌入式Linux。雖然它的體積很小,卻仍然保留了Linux的大多數的優點:穩定、良好的移植性、優秀的網路功能、對各種檔案系統完備的支持和標準豐富的API。它專為
嵌入式系統做了許多小型化的工作,目前已支持多款CPU。 其編譯後目標檔案可控制在幾百KB數量級,並已經被成功地移植到很多平台上。
uClinux從Linux 2.0/2.4
核心派生而來,沿襲了Linux的絕大部分特性。它是專門針對沒有MMU(記憶體管理單元)的CPU,並且為嵌入式系統做了許多小型化的工作。它通常用於具有很少記憶體或Flash的
嵌入式作業系統。在GNU通用許可證的保證下,運行uClinux作業系統的用戶可以使用幾乎所有的Linux API函式。由於經過了裁剪和最佳化,它形成了一個高度最佳化,代碼緊湊的嵌入式Linux。它具有體積小、穩定、良好的移植性、優秀的網路功能、完備的對各種檔案系統的支持,以及豐富的API函式等優點。uClinux與Linux在兼容性方面表現出色,uClinux除了不能實現fork()外,其餘uClinux的API函式與標準Linux完全相同。
針對沒有MMU的CPU
全球每年生產的CPU的數量大概在二十億顆左右,其中大部分是套用於專用性很強的各類
嵌入式系統。大部分嵌入式系統為了減少系統複雜程度、降低硬體及開發成本和運行功耗,在硬體設計中取消了記憶體管理單元(MMU)模組。最初,運行於這類沒有MMU的CPU之上的都是一些很簡單的單任務作業系統,或者更簡單的控制程式,甚至根本就沒有作業系統而直接運行應用程式。在這種情況下,系統無法運行複雜的應用程式,或者效率很低,並且所有的應用程式需要重新開發,還要求開發人員十分了解硬體特性。這些都阻礙了不含MMU的嵌入式產品開發的速度和套用水平。
uClinux專門針對沒有MMU的CPU,並且為
嵌入式系統做了許多小型化的工作。uClinux是一個完全符合GNU/GPL公約的項目,完全開放代碼。
最初的uClinux僅僅支持Palm
硬體系統,基於Linux 2.0
核心。隨著系統的日益改進,支持的
核心版本從2.0、2.2、2.4一直到現在最新的2.6。系統的開發人員從兩人增加到了目前的12人,支持的硬體系統也從一種增加到了目前的十餘種(支持的硬體平台如Motorola公司的M68328、M68EN322、MC68360、DragonBall系列如68EZ328、68VZ328,ColdFire系列的如5272、5307,ARM 7TDMI、MC68EN302、ETRAX、Intel i960、PRISMA、Atari 68k等等。)
根據Linuxdevices網站2004年3月的調查,uClinux在全球嵌入式Linux市場所占的份額已位居第二,僅僅落後於定製Linux(即自己下載
源碼進行修改定製)。同時Linux在全球
嵌入式作業系統的市場份額依然處於統治地位(占40%以上),領先第二名微軟公司的嵌入式作業系統三倍以上(市場份額約13%)。
特點
標準Linux可能採用的小型化方法
Linux核心採用模組化的設計,即很多功能塊可以獨立的加上或卸下,開發人員在設計核心時把這些核心模組作為可選的選項,可以在
編譯系統核心時指定。因此一種較通用的做法是對Linux核心重新編譯,在編譯時仔細的選擇
嵌入式設備所需要的功能支持模組,同時刪除不需要的功能。通過對核心的重新配置,可以使系統運行所需要的核心顯著減小,從而縮減資源使用量。
2. 製作root檔案系統映象
Linux系統在啟動時必須載入根(root)檔案系統,因此剪裁系統同時包括root file system的剪裁。在x86系統下,Linux可以在Dos下,使用Loadlin檔案載入啟動,
uClinux採用的小型化方法
1.uClinux的核心載入方式
uClinux的
核心有兩種可選的運行方式:可以在flash上直接運行,也可以載入到記憶體中運行。這種做法可以減少記憶體需要。
Flash運行方式:把核心的可執行映象燒寫到flash上,系統啟動時從flash的某個地址開始逐句執行。這種方法實際上是很多
嵌入式系統採用的方法。
核心載入方式:把核心的壓縮檔案存放在flash上,系統啟動時讀取壓縮檔案在記憶體里解壓,然後開始執行,這種方式相對複雜一些,但是運行速度可能更快(ram的存取速率要比flash高)。同時這也是標準Linux系統採用的啟動方式。
2.uClinux的根(root)檔案系統
uClinux系統採用romfs檔案系統,這種檔案系統相對於一般的ext2檔案系統要求更少的空間。空間的節約來自於兩個方面,首先
核心支持romfs檔案系統比支持ext2檔案系統需要更少的代碼,其次romfs檔案系統相對簡單,在建立檔案系統超級塊(superblock)需要更少的存儲空間。Romfs檔案系統不支持動態擦寫保存,對於系統需要動態保存的數據採用虛擬ram盤的方法進行處理(ram盤將採用ext2檔案系統)。
3.uClinux的應用程式庫
uClinux小型化的另一個做法是重寫了應用程式庫,相對於越來越大且越來越全的glibc庫,uClibc對libc做了精簡。
uClinux對
用戶程式採用靜態連線的形式,這種做法會使應用程式變大,但是基於
記憶體管理的問題,不得不這樣做(這將在下文對uClinux記憶體管理展開分析時進行說明),同時這種做法也更接近於通常
嵌入式系統的做法。
系統特點
嵌入式作業系統比較
由表1可以看出,對於嵌入式套用,高端平台可直接採用Linux系統,其兼容性和可移植度都較高,但對硬體處理速度和存儲空間要求較高。
低端平台的最佳選擇是uClinux,其性能穩定、移植性好、功能強大。
低端平台如果對實時性要求較高、套用相對簡單,則可採用uc/os或其他作業系統。
基本架構
uClinux的系統與標準Linux的架構完全一致。
檔案系統
uClinux系統多採用Romfs檔案系統,Romfs是一種相對簡單、占用空間較少的檔案系統。空間的節約來自於兩個方面:首先
核心支持Romfs檔案系統比支持ext2檔案系統需要更少的代碼;其次romfs檔案系統相對簡單,在建立檔案系統超級塊(Superblock)需要更少的存儲空間。Romfs是唯讀的檔案系統,禁止寫操作,因此系統同時需要
虛擬盤(RAMDISK)支持
臨時檔案和數據檔案的存儲。
隨著技術的發展,近年來日誌檔案系統在uClinux系統上得到了較多的套用,其中以支持NOR FLASH的JFFS、JFFS2檔案系統和支持NAND FLASH的YAFFS最為流行。這些檔案系統都支持掉電檔案保護,同時支持標準的MTD驅動。
開發環境
GNU開發套件
Gnu開發套件作為通用的Linux開放套件,包括一系列的開發調試工具。主要組件:
Binutils:一些
輔助工具,包括objdump(可以反編譯二進制檔案),as(彙編編譯器),ld(連線器)等等。
Gdb:調試器,可使用多種交叉調試方式,gdb-bdm(背景調試工具),gdbserver(使用乙太網絡調試)。
uClinux的列印終端
通常情況下,uClinux的默認終端是串口,
核心在啟動時所有的信息都列印到串口終端(使用printk函式列印),同時也可以通過串口終端與系統互動。
uClinux在啟動時啟動了telnetd(
遠程登錄服務),操作者可以遠程登錄上系統,從而控制系統的運行。至於是否允許遠程登錄可以通過燒寫romfs檔案系統時有用戶決定是否啟動遠程登錄服務。
交叉編譯調試工具
支持一種新的處理器,必須具備一些編譯,彙編工具,使用這些工具可以形成可運行於這種處理器的二進制檔案。對於核心使用的編譯工具同應用程式使用的有所不同。在解釋不同點之前,需要對gcc連線做一些說明:
.ld(link description)檔案:ld檔案是指出連線時記憶體映象格式的檔案。
crt0.S:應用程式編譯連線時需要的啟動檔案,主要是初始化應用程式棧。
pic:position independence code ,與位置無關的二進制格式檔案,在
程式段中必須包括reloc段,從而使的代碼載入時可以進行重新定位。
核心編譯連線時,使用ucsimm.ld檔案,形成
執行檔映象,所形成的
代碼段既可以使用間接
定址方式(即使用reloc段進行定址),也可以使用絕對定址方式。這樣可以給
編譯器更多的最佳化空間。因為核心可能使用
絕對定址,所以核心載入到的記憶體地址空間必須與ld檔案中給定的記憶體空間完全相同。
應用程式的連線與核心連線方式不同。應用程式由核心載入(執行檔載入器將在後面討論),由於應用程式的ld檔案給出的記憶體空間與應用程式實際被載入的記憶體位置可能不同,這樣在應用程式載入的過程中需要一個重新地位的過程,即對reloc段進行修正,使得程式進行
間接定址時不至於出錯。(這個問題在i386等高級處理器上方法有所不同,本文將在後面進一步分析)。
由上述討論,至少需要兩套編譯連線工具。在討論過uClinux的記憶體管理後本文將給出整個系統的工作流程以及系統在flash和ram中的空間分布。
執行檔格式
先對一些名詞作一些說明:
coff(common object file format):一種通用的對象檔案格式
elf(excutive linked file):一種為Linux系統所採用的通用檔案格式,支持動態連線
flat:elf格式有很大的
檔案頭,flat檔案對檔案頭和一些段信息做了簡化
uClinux系統使用flat
執行檔格式,gcc的
編譯器不能直接形成這種檔案格式,但是可以形成coff或elf格式的執行檔,這兩種檔案需要coff2flt或elf2flt工具進行格式轉化,形成flat檔案。
當用戶執行一個套用時,
核心的執行檔案載入器將對flat檔案進行進一步處理,主要是對reloc段進行修正(執行檔載入器的詳見fs/binfmt_flat.c)。以下對reloc段進一步討論。
需要reloc段的根本原因是,程式在連線時連線器所假定的程式運行空間與實際程式載入到的記憶體空間不同。假如有這樣一條指令:
jsr app_start;
這一條指令採用
直接定址,跳轉到app_start地址處執行,
連線程式將在編譯完成是計算出app_start的
實際地址(設若實際地址為0x10000),這個實際地址是根據ld檔案計算出來(因為連線器假定該程式將被載入到由ld檔案指明的記憶體空間)。但實際上由於
記憶體分配的關係,作業系統在載入時無法保證程式將按ld檔案載入。這時如果程式仍然跳轉到
絕對地址0x10000處執行,通常情況這是不正確的。一個解決辦法是增加一個存儲空間,用於存儲app_start的
實際地址,設若使用變數addr表示這個存儲空間。則以上這句程式將改為:
movl addr, a0;
jsr (a0);
增加的變數addr將在
數據段中占用一個4位元組的空間,連線器將app_start的絕對地址存儲到該變數。在
執行檔載入時,執行檔載入器根據程式將要載入的記憶體空間計算出app_start在記憶體中的實際位置,寫入addr變數。系統在實際處理是不需要知道這個變數的確切存儲位置(也不可能知道),系統只要對整個reloc段進行處理就可以了(reloc段有標識,系統可以讀出來)。處理很簡單只需要對reloc段中存儲的值統一加上一個偏置(如果載入的空間比預想的要靠前,實際上是減去一個
偏移量)。偏置由實際的
物理地址起始值同ld檔案指定的地址起始值相減計算出。
這種reloc的方式部分是由uClinux的
記憶體分配問題引起的,這一點將在uClinux
記憶體管理分析時說明。
針對實時性的解決方案
uClinux本身並沒有關注實時問題,它並不是為了Linux的實時性而提出的。另外有一種Linux--Rt-linux關注實時問題。Rt-linux執行管理器把普通Linux的
核心當成一個任務運行,同時還管理了實時進程。而非實時進程則交給普通Linux核心處理。這種方法已經套用於很多的作業系統用於增強作業系統的實時性,包括一些商用版UNIX系統,Windows NT等等。這種方法優點之一是實現簡單,且實時性能容易檢驗。優點之二是由於非實時進程運行於標準Linux系統,同其它Linux商用版本之間保持了很大的兼容性。優點之三是可以支持硬
實時時鐘的套用。uClinux可以使用Rt-linux的patch,從而增強uClinux的實時性,使得uClinux可以套用於工業控制、進程控制等一些實時要求較高的套用。
記憶體管理
應該說uClinux同標準Linux的最大區別就在於記憶體管理,同時也由於uClinux的記憶體管理引發了一些標準Linux所不會出現的問題。本文將把uClinux記憶體管理同標準Linux的記憶體管理部分進行比較分析。
標準Linux使用的虛擬存儲器技術
標準Linux使用
虛擬存儲器技術,這種技術用於提供比計算機系統中實際使用的
物理記憶體大得多的記憶體空間。使用者將感覺到好像程式可以使用非常大的記憶體空間,從而使得編程人員在寫程式時不用考慮計算機中的物理記憶體的實際容量。為了支持虛擬存儲管理器的管理,Linux系統採用
分頁(paging)的方式來載入進程。所謂分頁既是把實際的
存儲器分割為相同大小的段,例如每個段1024個位元組,這樣1024個位元組大小的段便稱為一個頁面(page)。
虛擬存儲器由
存儲器管理機制及一個大容量的快速
硬碟存儲器支持。它的實現基於
局部性原理,當一個程式在運行之前,沒有必要全部裝入記憶體,而是僅將那些當前要運行的那些部分頁面或段裝入記憶體運行(copy-on-write),其餘暫時留在硬碟上程式運行時如果它所要訪問的頁(段)已存在,則程式繼續運行,如果發現不存在的頁(段),作業系統將產生一個頁錯誤(page fault),這個錯誤導致作業系統把需要運行的部分載入到記憶體中。必要時作業系統還可以把不需要的記憶體頁(段)交換到磁碟上。利用這樣的方式管理
存儲器,便可把一個進程所需要用到的存儲器以化整為零的方式,視需求分批載入,而核心程式則憑藉屬於每個頁面的頁碼來完成定址各個存儲器區段的工作。
標準Linux是針對有記憶體管理單元的處理器設計的。在這種處理器上,
虛擬地址被送到記憶體管理單元(MMU),把虛擬
地址映射為物理地址。
通過賦予每個任務不同的虛擬--物理地址轉換映射,支持不同任務之間的保護。地址轉換函式在每一個任務中定義,在一個任務中的虛擬地址空間映射到
物理記憶體的一個部分,而另一個任務的虛擬地址空間映射到物理存儲器中的另外區域。計算機的
存儲管理單元(MMU)一般有一組
暫存器來標識當前運行的進程的轉換表。在當前進程將CPU放棄給另一個進程時(一次
上下文切換),
核心通過指向新進程地址轉換表的指針載入這些暫存器。MMU暫存器是有特權的,只能在核心態才能訪問。這就保證了一個進程只能訪問自己
用戶空間內的地址,而不會訪問和修改其它進程的空間。當
執行檔被載入時,載入器根據預設的ld檔案,把程式載入到
虛擬記憶體的一個空間,因為這個原因實際上很多程式的虛擬地址空間是相同的,但是由於轉換函式不同,所以實際所處的記憶體區域也不同。而對於多
進程管理當處理器進行
進程切換並執行一個新任務時,一個重要部分就是為新任務切換任務轉換表。我們可以看到Linux系統的
記憶體管理至少實現了以下功能:
運行比記憶體還要大的程式。理想情況下應該可以運行任意大小的程式
◇可以運行只載入了部分的程式,縮短了程式啟動的時間
◇可以使多個程式同時駐留在記憶體中提高CPU的利用率
◇可以運行
重定位程式。即程式可以方於記憶體中的任何一處,而且可以在執行過程中移動。
◇寫機器無關的代碼。程式不必事先約定機器的配置情況。
◇減輕程式設計師分配和管理記憶體資源的負擔。
◇可以進行共享--例如,如果兩個進程運行同一個程式,它們應該可以共享程式代碼的同一個副本。
◇提供記憶體保護,進程不能以非授權方式訪問或修改頁面,
核心保護單個進程的數據和代碼以防止其它進程修改它們。否則,
用戶程式可能會偶然(或惡意)的破壞核心或其它用戶程式。
虛存系統並不是沒有代價的。
記憶體管理需要地址轉換表和其他一些數據結構,留給程式的記憶體減少了。地址轉換增加了每一條指令的執行時間,而對於有額外記憶體操作的指令會更嚴重。當進程訪問不在記憶體的頁面時,
系統發生失效。系統處理該失效,並將頁面載入到記憶體中,這需要極耗時間的磁碟I/O操作。總之記憶體管理活動占用了相當一部分cpu時間(在較忙的系統中大約占10%)。
uClinux針對NOMMU的特殊處理
對於uClinux來說,其設計針對沒有MMU的處理器,即uClinux不能使用處理器的
虛擬記憶體管理技術(應該說這種不帶有MMU的處理器在
嵌入式設備中相當普偏)。uClinux仍然採用
存儲器的
分頁管理,系統在啟動時把實際存儲器進行分頁。在載入應用程式時程式分頁載入。但是由於沒有MMU管理,所以實際上uClinux採用實
存儲器管理策略(real memeory management)。這一點影響了系統工作的很多方面。
uClinux系統對於記憶體的訪問是直接的,(它對地址的訪問不需要經過MMU,而是直接送到地址線上輸出),所有程式中訪問的地址都是實際的物理地址。作業系統對記憶體空間沒有保護(這實際上是很多
嵌入式系統的特點),各個進程實際上共享一個運行空間(沒有獨立的地址轉換表)。
一個進程在執行前,系統必須為進程分配足夠的連續
地址空間,然後全部載入
主存儲器的連續空間中。與之相對應的是標準Linux系統在分配記憶體時沒有必要保證實際物理存儲空間是連續的,而只要保證
虛存地址空間連續就可以了。另外一個方面程式載入地址與預期(ld檔案中指出的)通常都不相同,這樣relocation過程就是必須的。此外磁碟交換空間也是無法使用的,系統執行時如果缺少記憶體將無法通過磁碟交換來得到改善。
uClinux對記憶體的管理減少同時就給開發人員提出了更高的要求。如果從易用性這一點來說,uClinux的
記憶體管理是一種倒退,退回了到了UNIX早期或是Dos系統時代。開發人員不得不參與系統的記憶體管理。從編譯
核心開始,開發人員必須告訴系統這塊
開發板到底擁有多少的記憶體(假如你欺騙了系統,那將在後面運行程式時受到懲罰),從而系統將在啟動的初始化階段對記憶體進行分頁,並且標記已使用的和未使用的記憶體。系統將在運行套用時使用這些
分頁記憶體。
由於應用程式載入時必須分配連續的
地址空間,而針對不同硬體平台的可一次成塊(連續地址)分配記憶體大小限制是不同(目前針對ez328處理器的uClinux是128k,而針對coldfire處理器的
系統記憶體則無此限制),所以開發人員在開發應用程式時必須考慮記憶體的分配情況並關注應用程式需要運行空間的大小。另外由於採用實
存儲器管理策略,
用戶程式同核心以及其它用戶程式在一個地址空間,程式開發時要保證不侵犯其它程式的地址空間,以使得程式不至於破壞系統的正常工作,或導致其它程式的運行異常。
從記憶體的訪問角度來看,開發人員的權利增大了(開發人員在編程時可以訪問任意的
地址空間),但與此同時系統的安全性也大為下降。此外,系統對
多進程的管理將有很大的變化,這一點將在uClinux的多
進程管理中說明。
雖然uClinux的記憶體管理與標準Linux系統相比功能相差很多,但應該說這是
嵌入式設備的選擇。在嵌入式設備中,由於成本等敏感因素的影響,普偏的採用不帶有MMU的處理器,這決定了系統沒有足夠的硬體支持實現虛擬
存儲管理技術。從嵌入式設備實現的功能來看,嵌入式設備通常在某一特定的環境下運行,只要實現特定的功能,其功能相對簡單,記憶體管理的要求完全可以由開發人員考慮。
進程:進程是一個運行程式並為其提供執行環境的實體,它包括一個
地址空間和至少一個控制點,進程在這個地址空間上執行單一指令序列。進程地址空間包括可以訪問或引用的
記憶體單元的集合,進程控制點通過一個一般稱為
程式計數器(program counter,PC)的硬體
暫存器控制和跟蹤進程指令序列。
fork:由於進程為執行程式的環境,因此在執行程式前必須先建立這個能"跑"程式的環境。Linux系統提供系統調用拷貝現行進程的內容,以產生新的進程,調用fork的進程稱為
父進程;而所產生的新進程則稱為子進程。子進程會承襲父進程的一切特性,但是它有自己的
數據段,也就是說,儘管子進程改變了所屬的變數,卻不會影響到父進程的變數值。
當
核心收到fork請求時,它會先查核三件事:首先檢查存儲器是不是足夠;其次是進程表是否仍有空缺;最後則是看看用戶是否建立了太多的子進程。如果上述說三個條件滿足,那么作業系統會給子進程一個進程識別碼,並且設定cpu時間,接著設定與
父進程共享的段,同時將父進程的inode拷貝一份給子進程運用,最終子進程會返回數值0以表示它是子進程,至於父進程,它可能等待子進程的執行結束,或與子進程各做個的。
exec
系統調用:該系統調用提供一個進程去執行另一個進程的能力,exec系統調用是??序的
堆疊、
數據段與
程式段都會被修改,只有用戶區維持不變。
vfork系統調用:由於在使用fork時,
核心會將父進程拷貝一份給子進程,但是這樣的做法相當浪費時間,因為大多數的情形都是程式在調用fork後就立即調用exec,這樣剛拷貝來的進程區域又立即被新的數據覆蓋掉。因此Linux系統提供一個系統調用vfork,vfork假定系統在調用完成vfork後會馬上執行exec,因此vfork不拷貝
父進程的頁面,只是初始化私有的數據結構與準備足夠的分頁表。這樣實際在
vfork調用完成後父子進程事實上共享同一塊存儲器(在子進程調用exec或是exit之前),因此子進程可以更改父進程的數據及
堆疊信息,因此vfork系統調用完成後,父進程進入睡眠,直到子進程執行exec。當子進程執行exec時,由於exec要使用被執行程式的數據,代碼覆蓋子進程的存儲區域,這樣將產生
防寫錯誤(do_wp_page)(這個時候子進程寫的實際上是
父進程的存儲區域),
這個錯誤導致
核心為子進程重新分配存儲空間。當子進程正確開始執行後,將喚醒父進程,使得父進程繼續往後執行。
uClinux的多進程處理
uClinux沒有mmu管理存儲器,在實現多個進程時(fork調用生成子進程)需要實現數據保護。
uClinux的fork和
vfork:uClinux的fork等於vfork。實際上uClinux的多
進程管理通過vfork來實現。這意味著uClinux系統fork調用完程後,要么子進程代替父進程執行(此時父進程已經sleep)直到子進程調用exit退出,要么調用exec執行一個新的進程,這個時候將產生
執行檔的載入,即使這個進程只是父進程的拷貝,這個過程也不能避免。當子進程執行exit或exec後,子進程使用wakeup把
父進程喚醒,父進程繼續往下執行。
uClinux的這種
多進程實現機制同它的
記憶體管理緊密相關。uClinux針對nommu處理器開發,所以被迫使用一種flat方式的記憶體管理模式,啟動新的應用程式時系統必須為應用程式分配存儲空間,並立即把應用程式載入到記憶體。缺少了MMU的記憶體重映射機制,uClinux必須在執行檔載入階段對執行檔reloc處理,使得程式執行時能夠直接使用
物理記憶體。
uClinux是專門針對沒有MMU的處理器而設計的,即uClinux無法使用處理器的虛擬記憶體管理技術。實際上uClinux採用實
存儲器管理策略,通過
地址匯流排對物理記憶體進行直接訪問。所有程式中訪問的地址都是實際的
物理地址,所有的進程都在一個運行空間中運行(包括
核心進程),這樣的運行機制給程式設計師帶來了不小的挑戰,在作業系統不提供保護的情況下必需小心設計程式和數據空間,以免引起應用程式進程甚至是核心的崩潰。
uClinux仍然採用存儲器的分頁管理,系統在啟動時把實際存儲器進行分頁,在載入應用程式時程式分頁載入。一個進程在執行前,系統必須為進程分配足夠的連續地址空間,然後全部載入主存儲器的連續空間中。系統不含MMU帶來的另外一個問題是磁碟交換空間無法使用,對於資源有限的
嵌入式系統而言,系統執行時如果缺少記憶體將無法通過磁碟交換來得到改善。
MMU的省略雖然帶來了系統及應用程式開發的限制,但對於成本和體積敏感的
嵌入式設備而言,其套用環境和套用需求並不要求複雜和相對昂貴的硬體體系,對於功能簡單的專用嵌入式設備,記憶體的分配和管理完全可以由開發人員考慮。
多進程管理
由於uClinux沒有MMU管理存儲器,在實現多個進程時需要實現數據保護。uClinux的雖然支持fork函式,但其實質是和
vfork:實際上uClinux所有的多進程管理都通過vfork來實現。
vfork不拷貝父進程的頁面,只是初始化私有的數據結構與準備足夠的分頁表。調用完成後父子進程事實上共享同一塊存儲器,因此子進程可以更改父進程的數據及
堆疊信息,所有父進程進入睡眠,直到子進程執行exec。當子進程正確開始執行後,將喚醒父進程,使得父進程繼續往後執行。這意味著uClinux系統fork調用完成後,要么子進程代替父進程執行(此時父進程已經休眠)直到子進程調用exit退出,要么調用exec執行一個新的進程。
vfork是uClinux與標準Linux應用程式的開發中最重要的不同之處,只有對vfork與fork兩個函式的差異和程式處理有詳細的了解才能順利地完成從Linux到uClinux的程式移植。
缺點
正如中國古語云“人無完人”,uClinux也有一些不足之處:
文檔的不足
與Linux及其他自由軟體類似,uClinux的文檔十分不足:缺乏組織和一致的文檔、熱門技術和分類文檔眾多而雜亂無章、非熱點部分文檔缺失甚至沒有文檔。對於開發人員而言,往往要深入程式的原始碼找尋有用的資料。
Bug問題
uClinux與硬體平台直接相關。對於有商業公司贊助的硬體平台,其相關代碼和Bug更新較快,編譯和執行都十分順利;但對於非商業支持的硬體平台,其核心和應用程式代碼都得不到及時更新和排錯。這種現象在核心原始碼樹還不是十分普遍,但在uClinux自帶的應用程式庫中卻經常發生編譯錯誤,往往是增加了一個應用程式或改變了運行庫便導致無法編譯。這就需要開發者投入足夠的時間和精力進行排錯和修改,也會導致開發進度的延誤。
實時性討論
與Linux一樣,uClinux本身並不支持實時性套用,但通過實時性的修改(RTLinux或RTAI)可以提供基於
核心空間和
用戶空間的硬實時和軟實時的系統調用。