摘要: 本文將分析嵌入式操作系統uClinux的內核結構、、內存管理、多進程處理、針對實時性的解決方案和開發(fā)環(huán)境,先對uCLinux有一個深刻的認識,將有利于今后進一步研究開發(fā)。
關鍵詞: uCLinux,內存管理,多進程處理,RTLinux,開發(fā)環(huán)境
1 引言
嵌入式操作系統是嵌入式系統的靈魂,而且在同一個硬件平臺上可以嵌入不同的嵌入式操作系統。比如ARM7TDMI內核,可以嵌入Nucleus、VxWorks、uClinux等操作系統。在此主要對uClinux的進行介紹,嵌入式uClinux操作系統主要有三個基本部分組成:引導程序、uClinux內核(由內存管理、進程管理和中斷處理等構成)和文件系統。uClinux可以通過定制使內核小型化,還可以加上GUI(圖形用戶界面)和定制應用程序,并將其放在ROM、RAM、FLASH或Disk On Chip中啟動。由于嵌入式uClinux操作系統的內核定制高度靈活性,開發(fā)者可以很容易地對其進行按需配置,來滿足實際應用需要。又由于uClinux是源代碼公開,因此開發(fā)人員只有了解內核原理就可以自己開發(fā)部分軟件,例如增加各類驅動程序。下面將詳細分析嵌入式操作系統uClinux。
2 嵌入式uCinux內核結構
uClinux內核結構如圖1所示:
圖1代表了內核的功能結構,與Linux基本相同,不同的只是對內存管理和進程管理進行改寫,以滿足無MMU處理器的要求。uClinux是Linux 操作系統的一種,是由Linux2.0內核發(fā)展來的,是專為沒有MMU的微處理器(如ARM7TDMI、Coldfire 等)設計的嵌入式Linux操作系統。另外,由于大多數內核源代碼都被重寫,uClinux的內核要比原Linux 2.0內核小的多, 但保留了Linux 操作系統的主要優(yōu)點:穩(wěn)定性,優(yōu)異的網絡能力以及優(yōu)秀的文件系統支持。
3 uClinux的內存管理
uClinux同標準Linux的最大區(qū)別就在于內存管理。標準Linux是針對有MMU的處理器設計的。在這種處理器上,虛擬地址被送到MMU,MMU把虛擬地址映射為物理地址。通過賦予每個任務不同的虛擬—物理地址轉換映射,支持不同任務之間的保護。對于uCLinux來說,其設計針對沒有MMU的處理器,不能使用處理器的虛擬內存管理技術。
uClinux不能使用處理器的虛擬內存管理技術(應該說這種不帶有MMU的處理器在嵌入式設備中相當普遍)。
uClinux仍采用存儲器的分頁管理,系統在啟動時把實際存儲器進行分頁。在加載應用程序時程序分頁加載。但是由于沒有MMU管理,所以實際上uClinux采用實存儲器管理策略(real memeory management)。這一點影響了系統工作的很多方面。
uClinux系統對于內存的訪問是直接的,(它對地址的訪問不需要經過MMU,而是直接送到地址線上輸出),所有程序中訪問的地址都是實際的物理地址。操作系統對內存空間沒有保護(這實際上是很多嵌入式系統的特點),各個進程實際上共享一個運行空間(沒有獨立的地址轉換表)。
一個進程在執(zhí)行前,系統必須為進程分配足夠的連續(xù)地址空間,然后全部載入主存儲器的連續(xù)空間中。與之相對應的是標準Linux系統在分配內存時沒有必要保證實際物理存儲空間是連續(xù)的,而只要保證虛存地址空間連續(xù)就可以了。此外磁盤交換空間也是無法使用的,系統執(zhí)行時如果缺少內存將無法通過磁盤交換來得到改善。
uClinux對內存的管理減少同時就給開發(fā)人員提出了更高的要求。如果從易用性這一點來說,uClinux的內存管理是一種倒退,退回了到了UNIX早期或是Dos系統時代。開發(fā)人員不得不參與系統的內存管理。從編譯內核開始,開發(fā)人員必須告訴系統這塊開發(fā)板到底擁有多少的內存(假如你欺騙了系統,那將在后面運行程序時受到懲罰),從而系統將在啟動的初始化階段對內存進行分頁,并且標記已使用的和未使用的內存。系統將在運行應用時使用這些分頁內存。
由于應用程序加載時必須分配連續(xù)的地址空間,而針對不同硬件平臺的可一次成塊(連續(xù)地址)分配內存大小限制是不同(目前針對EZ328處理器的uClinux是128k,而針對Coldfire處理器的系統內存則無此限制),所以開發(fā)人員在開發(fā)應用程序時必須考慮內存的分配情況并關注應用程序需要運行空間的大小。另外由于采用實存儲器管理策略,用戶程序同內核以及其它用戶程序在一個地址空間,程序開發(fā)時要保證不侵犯其它程序的地址空間,以使得程序不至于破壞系統的正常工作,或導致其它程序的運行異常。
從內存的訪問角度來看,開發(fā)人員的權利增大了(開發(fā)人員在編程時可以訪問任意的地址空間),但與此同時系統的安全性也大為下降。此外,系統對多進程的管理將有很大的變化,這一點將在uClinux的多進程管理中說明。
4 uClinux的多進程處理
uClinux沒有MMU管理存儲器,在實現多個進程時(fork調用生成子進程)需要實現數據保護。由于uClinux的多進程管理是通過vfork來實現,因此fork等于vfork。這意味著uClinux系統fork調用完成后,要么子進程代替父進程執(zhí)行(此時父進程已經sleep)直到子進程調用exit退出;要么調用exec執(zhí)行一個新的進程,這個時候將產生可執(zhí)行文件的加載,即使這個進程只是父進程的拷貝,這個過程也不能避免。當子進程執(zhí)行exit或exec后,子進程使用wakeup把父進程喚醒,使父進程繼續(xù)往下執(zhí)行。
uClinux的這種多進程實現機制同它的內存管理緊密相關。uClinux針對沒有mmu處理器開發(fā),所以被迫使用一種flat方式的內存管理模式,啟動新的應用程序時系統必須為應用程序分配存儲空間,并立即把應用程序加載到內存。缺少了MMU的內存重映射機制,uClinux必須在可執(zhí)行文件加載階段對可執(zhí)行文件reloc處理,使得程序執(zhí)行時能夠直接使用物理內存。
5 uCLinux針對實時性的解決方案
uClinux本身并沒有關注實時問題,它并不是為了Linux的實時性而提出的。另外有一種Linux:RT-Linux關注實時問題。RT-Linux執(zhí)行管理器把普通Linux的內核當成一個任務運行,同時還管理了實時進程。而非實時進程則交給普通Linux內核處理。這種方法已經應用于很多的操作系統用于增強操作系統的實時性,包括一些商用版UNIX系統,Windows NT等等。這種方法優(yōu)點之一是實現簡單,且實時性能容易檢驗。優(yōu)點之二是由于非實時進程運行于標準Linux系統,同其它Linux商用版本之間保持了很大的兼容性。優(yōu)點之三是可以支持硬實時時鐘的應用。uClinux可以使用RT-Linux的patch,從而增強uClinux的實時性,使得uClinux可以應用于工業(yè)控制、進程控制等一些實時要求較高的應用。
6 uClinux的開發(fā)環(huán)境
1,GNU開發(fā)套件
GNU開發(fā)套件作為通用的Linux開放套件,包括一系列的開發(fā)調試工具。主要組件:
Gcc: 編譯器,可以做成交叉編譯的形式,即在宿主機上開發(fā)編譯目標上可運行的二進制文件。
Binutils:一些輔助工具,包括objdump(可以反編譯二進制文件),as(匯編編譯器),ld(連接器)等等。
Gdb:調試器,可使用多種交叉調試方式,gdb-bdm(背景調試工具),gdbserver(使用以太網絡調試)。
2, Clinux的打印終端
通常情況下,uClinux的默認終端是串口,內核在啟動時所有的信息都打印到串口終端(使用printk函數打印),同時也可以通過串口終端與系統交互。
uClinux在啟動時啟動了telnetd(遠程登錄服務),操作者可以遠程登錄上系統,從而控制系統的運行。至于是否允許遠程登錄可以通過燒寫romfs文件系統時由用戶決定是否啟動遠程登錄服務。
3, 交叉編譯調試工具
支持一種新的處理器,必須具備一些編譯,匯編工具,使用這些工具可以形成可運行于這種處理器的二進制文件。對于內核使用的編譯工具同應用程序使用的有所不同。在解釋不同點之前,需要對gcc連接做一些說明:
ld(link description)文件:ld文件是指出連接時內存映象格式的文件。
crt0.S:應用程序編譯連接時需要的啟動文件,主要是初始化應用程序棧。
pic:position independence code ,與位置無關的二進制格式文件,在程序段中必須包括reloc段,從而使的代碼加載時可以進行重新定位。
內核編譯連接時,使用ucsimm.ld文件,形成可執(zhí)行文件映象,所形成的代碼段既可以使用間接尋址方式(即使用reloc段進行尋址),也可以使用絕對尋址方式。這樣可以給編譯器更多的優(yōu)化空間。因為內核可能使用絕對尋址,所以內核加載到的內存地址空間必須與ld文件中給定的內存空間完全相同。
應用程序的連接與內核連接方式不同。應用程序由內核加載(可執(zhí)行文件加載器將在后面討論),由于應用程序的ld文件給出的內存空間與應用程序實際被加載的內存位置可能不同,這樣在應用程序加載的過程中需要一個重新地位的過程,即對reloc段進行修正,使得程序進行間接尋址時不至于出錯。(這個問題在i386等高級處理器上方法有所不同)。
由上述討論,至少需要兩套編譯連接工具:
1) 二進制工具(Binutils)
GNU binutils包包括了匯編工具、鏈接器和基本的目標文件處理工具。對binutils包的設置定義了所需的目標文件的格式和字節(jié)順序。Binutils包種的工具都使用了二進制文件描述符(BFD)庫來交換數據。通過設置文件config.bfd,可以指定默認的二進制文件格式(例如elf little endian)和任何工具可用的格式,見例1。
例1 在config.bfd中添加的用來指定目標二進制格式的代碼
arm-*-uClinux* | armel-*-uClinux*
tag_defvec=bfd_elf32_littlearm_vec
targ_selvecs=”bfd_elf32_bigarm_vec armcoff_little_vec armcoff_big_vec”
2) C編譯器
GNU編譯器集GCC是通過使用一種叫做“寄存器轉換語言”(RTL)的方式實現的。假定現在有一種基本的機器描述性文件,它已經能滿足大家的需要?,F在要做的僅僅是設置默認情況下使用的參數和如何將文件組合成可執(zhí)行文件的方式。GNU的文檔提供了所有必需的資料,使得用戶可以為新型的處理器的指令集合提供支持。如果要針對體系的機器建立一個新的目標機器,那么就必須指定默認編譯參數和定制系統的特定參數,見例2。對于特定的目標系統,可以使用TARGET_DEFAULT宏來在target.h文件中定義編譯器的開關。目標t-makefile段指定了應該構建哪一個額外的例程和其編譯的方式。
例2 使用uClinux-arm.h來指定默認的編譯參數
#undef TARGET_DEFAULT
#define TARGET_DEFAULT(ARM_FLAG_APCS_32|ARM_FLAG_NO_GOT)
4 可執(zhí)行文件格式
先對一些名詞作一些說明:
coff(common object file format):一種通用的對象文件格式
elf(excutive linked file):一種為Linux系統所采用的通用文件格式,支持動態(tài)連接
flat:elf格式有很大的文件頭,flat文件對文件頭和一些段信息做了簡化
uClinux系統使用flat可執(zhí)行文件格式,gcc的編譯器不能直接形成這種文件格式,但是可以形成coff或elf格式的可執(zhí)行文件,這兩種文件需要coff2flt或elf2flt工具進行格式轉化,形成flat文件。當用戶執(zhí)行一個應用時,內核的執(zhí)行文件加載器將對flat文件進行進一步處理,主要是對reloc段進行修正。以下對reloc段進一步討論。
需要reloc段的根本原因是,程序在連接時連接器所假定的程序運行空間與實際程序加載到的內存空間不同。假如有這樣一條指令:
jsr app_start;
這一條指令采用直接尋址,跳轉到app_start地址處執(zhí)行,連接程序將在編譯完成是計算出app_start的實際地址(設若實際地址為0x10000),這個實際地址是根據ld文件計算出來(因為連接器假定該程序將被加載到由ld文件指明的內存空間)。但實際上由于內存分配的關系,操作系統在加載時無法保證程序將按ld文件加載。這時如果程序仍然跳轉到絕對地址0x10000處執(zhí)行,通常情況這是不正確的。一個解決辦法是增加一個存儲空間,用于存儲app_start的實際地址,設若使用變量addr表示這個存儲空間。則以上這句程序將改為:
movl addr, a0;
jsr (a0);
增加的變量addr將在數據段中占用一個4字節(jié)的空間,連接器將app_start的絕對地址存儲到該變量。在可執(zhí)行文件加載時,可執(zhí)行文件加載器根據程序將要加載的內存空間計算出app_start在內存中的實際位置,寫入addr變量。系統在實際處理時不需要知道這個變量的確切存儲位置(也不可能知道),系統只要對整個reloc段進行處理就可以了(reloc段有標識,系統可以讀出來)。處理很簡單,只需要對reloc段中存儲的值統一加上一個偏置(如果加載的空間比預想的要靠前,實際上是減去一個偏移量)。偏置由實際的物理地址起始值同ld文件指定的地址起始值相減計算出。這種reloc的方式部分是由uClinux的內存分配問題引起的。
7 總結
以上主要闡述了嵌入式操作系統uClinux的內核結構、、內存管理、多進程處理、針對實時性的解決方案和開發(fā)環(huán)境,先對uCLinux有一個深刻的認識,將有利于今后進一步研究開發(fā)。