uClinux系統(tǒng)分析 簡介 Linux是一種很受歡迎的操作系統(tǒng),它與Unix系統(tǒng)兼容,開放源代碼。它原本被設(shè)計(jì)為桌面系統(tǒng),現(xiàn)在廣泛應(yīng)用于服務(wù)器領(lǐng)域。而更大的影響在于它正逐漸的應(yīng)用于嵌入式設(shè)備。uClinux正是在這種氛圍下產(chǎn)生的。在uClinux這個(gè)英文單詞中u表示Micro,小的意思,C表示Control,控制的意思,所以uClinux就是Micro-Control-Linux,字面上的理解就是"針對微控制領(lǐng)域而設(shè)計(jì)的Linux系統(tǒng)"。 uClinux小型化的做法 標(biāo)準(zhǔn)Linux可能采用的小型化方法 1、重新編譯內(nèi)核 Linux內(nèi)核采用模塊化的設(shè)計(jì),即很多功能塊可以獨(dú)立的加上或卸下,開發(fā)人員在設(shè)計(jì)內(nèi)核時(shí)把這些內(nèi)核模塊作為可選的選項(xiàng),可以在編譯系統(tǒng)內(nèi)核時(shí)指定。因此一種較通用的做法是對Linux內(nèi)核重新編譯,在編譯時(shí)仔細(xì)的選擇嵌入式設(shè)備所需要的功能支持模塊,同時(shí)刪除不需要的功能。通過對內(nèi)核的重新配置,可以使系統(tǒng)運(yùn)行所需要的內(nèi)核顯著減小,從而縮減資源使用量。 2、制作root文件系統(tǒng)映象 Linux系統(tǒng)在啟動時(shí)必須加載根(root)文件系統(tǒng),因此剪裁系統(tǒng)同時(shí)包括root file system的剪裁。在x86系統(tǒng)下,Linux可以在Dos下,使用Loadlin文件加載啟動。 uClinux采用的小型化方法 1、uClinux的內(nèi)核加載方式 uClinux的內(nèi)核有兩種可選的運(yùn)行方式:可以在flash上直接運(yùn)行,也可以加載到內(nèi)存中運(yùn)行。這種做法可以減少內(nèi)存需要。 Flash運(yùn)行方式:把內(nèi)核的可執(zhí)行映象燒寫到flash上,系統(tǒng)啟動時(shí)從flash的某個(gè)地址開始逐句執(zhí)行。這種方法實(shí)際上是很多嵌入式系統(tǒng)采用的方法。 內(nèi)核加載方式:把內(nèi)核的壓縮文件存放在flash上,系統(tǒng)啟動時(shí)讀取壓縮文件在內(nèi)存里解壓,然后開始執(zhí)行,這種方式相對復(fù)雜一些,但是運(yùn)行速度可能更快(ram的存取速率要比flash高)。同時(shí)這也是標(biāo)準(zhǔn)Linux系統(tǒng)采用的啟動方式。 2、uClinux的根(root)文件系統(tǒng) uClinux系統(tǒng)采用romfs文件系統(tǒng),這種文件系統(tǒng)相對于一般的ext2文件系統(tǒng)要求更少的空間。空間的節(jié)約來自于兩個(gè)方面,首先內(nèi)核支持romfs文件系統(tǒng)比支持ext2文件系統(tǒng)需要更少的代碼,其次romfs文件系統(tǒng)相對簡單,在建立文件系統(tǒng)超級塊(superblock)需要更少的存儲空間。Romfs文件系統(tǒng)不支持動態(tài)擦寫保存,對于系統(tǒng)需要?jiǎng)討B(tài)保存的數(shù)據(jù)采用虛擬ram盤的方法進(jìn)行處理(ram盤將采用ext2文件系統(tǒng))。 3、uClinux的應(yīng)用程序庫 uClinux小型化的另一個(gè)做法是重寫了應(yīng)用程序庫,相對于越來越大且越來越全的glibc庫,uClibc對libc做了精簡。 uClinux對用戶程序采用靜態(tài)連接的形式,這種做法會使應(yīng)用程序變大,但是基于內(nèi)存管理的問題,不得不這樣做(這將在下文對uClinux內(nèi)存管理展開分析時(shí)進(jìn)行說明),同時(shí)這種做法也更接近于通常嵌入式系統(tǒng)的做法。 uClinux的開發(fā)環(huán)境 GNU開發(fā)套件 Gnu開發(fā)套件作為通用的Linux開放套件,包括一系列的開發(fā)調(diào)試工具。主要組件: Gcc: 編譯器,可以做成交叉編譯的形式,即在宿主機(jī)上開發(fā)編譯目標(biāo)上可運(yùn)行的二進(jìn)制文件。 Binutils:一些輔助工具,包括 objdump(可以反編譯二進(jìn)制文件),as(匯編編譯器),ld(連接器)等等。 Gdb:調(diào)試器,可使用多種交叉調(diào)試方式,gdb-bdm (背景調(diào)試工具),gdbserver(使用以太網(wǎng)絡(luò)調(diào)試)。 uClinux的打印終端 通常情況下,uClinux的默認(rèn)終端是串口,內(nèi)核在啟動時(shí)所有的信息都打印到串口終端(使用printk函數(shù)打印),同時(shí)也可以通過串口終端與系統(tǒng)交互。 uClinux在啟動時(shí)啟動了telnetd(遠(yuǎn)程登錄服務(wù)),操作者可以遠(yuǎn)程登錄上系統(tǒng),從而控制系統(tǒng)的運(yùn)行。至于是否允許遠(yuǎn)程登錄可以通過燒寫romfs文件系統(tǒng)時(shí)有用戶決定是否啟動遠(yuǎn)程登錄服務(wù)。 交叉編譯調(diào)試工具 支持一種新的處理器,必須具備一些編譯,匯編工具,使用這些工具可以形成可運(yùn)行于這種處理器的二進(jìn)制文件。對于內(nèi)核使用的編譯工具同應(yīng)用程序使用的有所不同。在解釋不同點(diǎn)之前,需要對gcc連接做一些說明: .ld(link description)文件:ld文件是指出連接時(shí)內(nèi)存映象格式的文件。 crt0.S:應(yīng)用程序編譯連接時(shí)需要的啟動文件,主要是初始化應(yīng)用程序棧。 pic:position independence code ,與位置無關(guān)的二進(jìn)制格式文件,在程序段中必須包括reloc段,從而使的代碼加載時(shí)可以進(jìn)行重新定位。 內(nèi)核編譯連接時(shí),使用ucsimm.ld文件,形成可執(zhí)行文件映象,所形成的代碼段既可以使用間接尋址方式(即使用reloc段進(jìn)行尋址),也可以使用絕對尋址方式。這樣可以給編譯器更多的優(yōu)化空間。因?yàn)閮?nèi)核可能使用絕對尋址,所以內(nèi)核加載到的內(nèi)存地址空間必須與ld文件中給定的內(nèi)存空間完全相同。 應(yīng)用程序的連接與內(nèi)核連接方式不同。應(yīng)用程序由內(nèi)核加載(可執(zhí)行文件加載器將在后面討論),由于應(yīng)用程序的ld文件給出的內(nèi)存空間與應(yīng)用程序?qū)嶋H被加載的內(nèi)存位置可能不同,這樣在應(yīng)用程序加載的過程中需要一個(gè)重新地位的過程,即對reloc段進(jìn)行修正,使得程序進(jìn)行間接尋址時(shí)不至于出錯(cuò)。(這個(gè)問題在i386等高級處理器上方法有所不同,本文將在后面進(jìn)一步分析)。 由上述討論,至少需要兩套編譯連接工具。在討論過uClinux的內(nèi)存管理后本文將給出整個(gè)系統(tǒng)的工作流程以及系統(tǒng)在flash和ram中的空間分布。 可執(zhí)行文件格式 先對一些名詞作一些說明: coff(common object file format):一種通用的對象文件格式 elf(excutive linked file):一種為Linux系統(tǒng)所采用的通用文件格式,支持動態(tài)連接 flat:elf格式有很大的文件頭,flat文件對文件頭和一些段信息做了簡化 uClinux系統(tǒng)使用flat可執(zhí)行文件格式,gcc的編譯器不能直接形成這種文件格式,但是可以形成coff或elf格式的可執(zhí)行文件,這兩種文件需要coff2flt或elf2flt工具進(jìn)行格式轉(zhuǎn)化,形成flat文件。 當(dāng)用戶執(zhí)行一個(gè)應(yīng)用時(shí),內(nèi)核的執(zhí)行文件加載器將對flat文件進(jìn)行進(jìn)一步處理,主要是對reloc段進(jìn)行修正(可執(zhí)行文件加載器的詳見fs/binfmt_flat.c)。以下對reloc段進(jìn)一步討論。 需要reloc段的根本原因是,程序在連接時(shí)連接器所假定的程序運(yùn)行空間與實(shí)際程序加載到的內(nèi)存空間不同。假如有這樣一條指令: jsr app_start; 這一條指令采用直接尋址,跳轉(zhuǎn)到app_start地址處執(zhí)行,連接程序?qū)⒃诰幾g完成是計(jì)算出app_start 的實(shí)際地址(設(shè)若實(shí)際地址為0x10000),這個(gè)實(shí)際地址是根據(jù)ld文件計(jì)算出來(因?yàn)檫B接器假定該程序?qū)⒈患虞d到由ld文件指明的內(nèi)存空間)。但實(shí)際上由于內(nèi)存分配的關(guān)系,操作系統(tǒng)在加載時(shí)無法保證程序?qū)磍d文件加載。這時(shí)如果程序仍然跳轉(zhuǎn)到絕對地址0x10000處執(zhí)行,通常情況這是不正確的。一個(gè)解決辦法是增加一個(gè)存儲空間,用于存儲app_start的實(shí)際地址,設(shè)若使用變量addr表示這個(gè)存儲空間。則以上這句程序?qū)⒏臑椋?br> movl addr, a0; jsr (a0); 增加的變量addr將在數(shù)據(jù)段中占用一個(gè)4字節(jié)的空間,連接器將app_start的絕對地址存儲到該變量。在可執(zhí)行文件加載時(shí),可執(zhí)行文件加載器根據(jù)程序?qū)⒁虞d的內(nèi)存空間計(jì)算出app_start在內(nèi)存中的實(shí)際位置,寫入addr變量。系統(tǒng)在實(shí)際處理是不需要知道這個(gè)變量的確切存儲位置(也不可能知道),系統(tǒng)只要對整個(gè)reloc段進(jìn)行處理就可以了(reloc段有標(biāo)識,系統(tǒng)可以讀出來)。處理很簡單只需要對reloc段中存儲的值統(tǒng)一加上一個(gè)偏置(如果加載的空間比預(yù)想的要靠前,實(shí)際上是減去一個(gè)偏移量)。偏置由實(shí)際的物理地址起始值同ld文件指定的地址起始值相減計(jì)算出。 這種reloc的方式部分是由uClinux的內(nèi)存分配問題引起的,這一點(diǎn)將在uClinux內(nèi)存管理分析時(shí)說明。 針對實(shí)時(shí)性的解決方案 uClinux本身并沒有關(guān)注實(shí)時(shí)問題,它并不是為了Linux的實(shí)時(shí)性而提出的。另外有一種Linux--Rt-linux關(guān)注實(shí)時(shí)問題。Rt-linux執(zhí)行管理器把普通 Linux 的內(nèi)核當(dāng)成一個(gè)任務(wù)運(yùn)行,同時(shí)還管理了實(shí)時(shí)進(jìn)程。而非實(shí)時(shí)進(jìn)程則交給普通Linux 內(nèi)核處理。這種方法已經(jīng)應(yīng)用于很多的操作系統(tǒng)用于增強(qiáng)操作系統(tǒng)的實(shí)時(shí)性,包括一些商用版UNIX系統(tǒng),Windows NT 等等。這種方法優(yōu)點(diǎn)之一是實(shí)現(xiàn)簡單,且實(shí)時(shí)性能容易檢驗(yàn)。優(yōu)點(diǎn)之二是由于非實(shí)時(shí)進(jìn)程運(yùn)行于標(biāo)準(zhǔn)Linux系統(tǒng),同其它Linux商用版本之間保持了很大的兼容性。優(yōu)點(diǎn)之三是可以支持硬實(shí)時(shí)時(shí)鐘的應(yīng)用。uClinux可以使用Rt-linux的patch,從而增強(qiáng)uClinux的實(shí)時(shí)性,使得uClinux可以應(yīng)用于工業(yè)控制、進(jìn)程控制等一些實(shí)時(shí)要求較高的應(yīng)用。 uClinux的內(nèi)存管理 應(yīng)該說uClinux同標(biāo)準(zhǔn)Linux的最大區(qū)別就在于內(nèi)存管理,同時(shí)也由于uClinux的內(nèi)存管理引發(fā)了一些標(biāo)準(zhǔn)Linux所不會出現(xiàn)的問題。本文將把uClinux內(nèi)存管理同標(biāo)準(zhǔn)Linux的那內(nèi)存管理部分進(jìn)行比較分析。 1、標(biāo)準(zhǔn)Linux使用的虛擬存儲器技術(shù) 標(biāo)準(zhǔn)Linux使用虛擬存儲器技術(shù),這種技術(shù)用于提供比計(jì)算機(jī)系統(tǒng)中實(shí)際使用的物理內(nèi)存大得多的內(nèi)存空間。使用者將感覺到好像程序可以使用非常大的內(nèi)存空間,從而使得編程人員在寫程序時(shí)不用考慮計(jì)算機(jī)中的物理內(nèi)存的實(shí)際容量。 為了支持虛擬存儲管理器的管理,Linux系統(tǒng)采用分頁(paging)的方式來載入進(jìn)程。所謂分頁既是把實(shí)際的存儲器分割為相同大小的段,例如每個(gè)段1024個(gè)字節(jié),這樣1024個(gè)字節(jié)大小的段便稱為一個(gè)頁面(page)。 虛擬存儲器由存儲器管理機(jī)制及一個(gè)大容量的快速硬盤存儲器支持。它的實(shí)現(xiàn)基于局部性原理,當(dāng)一個(gè)程序在運(yùn)行之前,沒有必要全部裝入內(nèi)存,而是僅將那些當(dāng)前要運(yùn)行的那些部分頁面或段裝入內(nèi)存運(yùn)行(copy-on-write), 其余暫時(shí)留在硬盤上程序運(yùn)行時(shí)如果它所要訪問的頁(段)已存在,則程序繼續(xù)運(yùn)行,如果發(fā)現(xiàn)不存在的頁(段),操作系統(tǒng)將產(chǎn)生一個(gè)頁錯(cuò)誤(page fault),這個(gè)錯(cuò)誤導(dǎo)致操作系統(tǒng)把需要運(yùn)行的部分加載到內(nèi)存中。必要時(shí)操作系統(tǒng)還可以把不需要的內(nèi)存頁(段)交換到磁盤上。利用這樣的方式管理存儲器,便可把一個(gè)進(jìn)程所需要用到的存儲器以化整為零的方式,視需求分批載入,而核心程序則憑借屬于每個(gè)頁面的頁碼來完成尋址各個(gè)存儲器區(qū)段的工作。 標(biāo)準(zhǔn)Linux是針對有內(nèi)存管理單元的處理器設(shè)計(jì)的。在這種處理器上,虛擬地址被送到內(nèi)存管理單元(MMU),把虛擬地址映射為物理地址。 通過賦予每個(gè)任務(wù)不同的虛擬--物理地址轉(zhuǎn)換映射,支持不同任務(wù)之間的保護(hù)。地址轉(zhuǎn)換函數(shù)在每一個(gè)任務(wù)中定義,在一個(gè)任務(wù)中的虛擬地址空間映射到物理內(nèi)存的一個(gè)部分,而另一個(gè)任務(wù)的虛擬地址空間映射到物理存儲器中的另外區(qū)域。計(jì)算機(jī)的存儲管理單元(MMU)一般有一組寄存器來標(biāo)識當(dāng)前運(yùn)行的進(jìn)程的轉(zhuǎn)換表。在當(dāng)前進(jìn)程將CPU放棄給另一個(gè)進(jìn)程時(shí)(一次上下文切換),內(nèi)核通過指向新進(jìn)程地址轉(zhuǎn)換表的指針加載這些寄存器。MMU寄存器是有特權(quán)的,只能在內(nèi)核態(tài)才能訪問。這就保證了一個(gè)進(jìn)程只能訪問自己用戶空間內(nèi)的地址,而不會訪問和修改其它進(jìn)程的空間。當(dāng)可執(zhí)行文件被加載時(shí),加載器根據(jù)缺省的ld文件,把程序加載到虛擬內(nèi)存的一個(gè)空間,因?yàn)檫@個(gè)原因?qū)嶋H上很多程序的虛擬地址空間是相同的,但是由于轉(zhuǎn)換函數(shù)不同,所以實(shí)際所處的內(nèi)存區(qū)域也不同。而對于多進(jìn)程管理當(dāng)處理器進(jìn)行進(jìn)程切換并執(zhí)行一個(gè)新任務(wù)時(shí),一個(gè)重要部分就是為新任務(wù)切換任務(wù)轉(zhuǎn)換表。我們可以看到Linux系統(tǒng)的內(nèi)存管理至少實(shí)現(xiàn)了以下功能: 運(yùn)行比內(nèi)存還要大的程序。理想情況下應(yīng)該可以運(yùn)行任意大小的程序 ◇可以運(yùn)行只加載了部分的程序,縮短了程序啟動的時(shí)間 ◇可以使多個(gè)程序同時(shí)駐留在內(nèi)存中提高CPU的利用率 ◇可以運(yùn)行重定位程序。即程序可以方于內(nèi)存中的任何一處,而且可以在執(zhí)行過程中移動。 ◇寫機(jī)器無關(guān)的代碼。程序不必事先約定機(jī)器的配置情況。 ◇減輕程序員分配和管理內(nèi)存資源的負(fù)擔(dān)。 ◇可以進(jìn)行共享--例如,如果兩個(gè)進(jìn)程運(yùn)行同一個(gè)程序,它們應(yīng)該可以共享程序代碼的同一個(gè)副本。 ◇提供內(nèi)存保護(hù),進(jìn)程不能以非授權(quán)方式訪問或修改頁面,內(nèi)核保護(hù)單個(gè)進(jìn)程的數(shù)據(jù)和代碼以防止其它進(jìn)程修改它們。否則,用戶程序可能會偶然(或惡意)的破壞內(nèi)核或其它用戶程序。 虛存系統(tǒng)并不是沒有代價(jià)的。 內(nèi)存管理需要地址轉(zhuǎn)換表和其他一些數(shù)據(jù)結(jié)構(gòu), 留給程序的內(nèi)存減少了。地址轉(zhuǎn)換增加了每一條指令的執(zhí)行時(shí)間,而對于有額外內(nèi)存操作的指令會更嚴(yán)重。當(dāng)進(jìn)程訪問不在內(nèi)存的頁面時(shí),系統(tǒng)發(fā)生失效。系統(tǒng)處理該失效,并將頁面加載到內(nèi)存中,這需要極耗時(shí)間的磁盤I/O操作??傊畠?nèi)存管理活動占用了相當(dāng)一部分cpu時(shí)間(在較忙的系統(tǒng)中大約占10%)。 |
|