uClinux系統(tǒng)分析
效。系統(tǒng)處理該失效,并將頁(yè)面加載到內(nèi)存中,這需要極耗時(shí)間的磁盤(pán)I/O操作??傊畠?nèi)存管理活動(dòng)占用了相當(dāng)一部分cpu時(shí)間(在較忙的系統(tǒng)中大約占10%)。 uClinux針對(duì)NOMMU的特殊處理 對(duì)于uClinux來(lái)說(shuō),其設(shè)計(jì)針對(duì)沒(méi)有MMU的處理器,即uClinux不能使用處理器的虛擬內(nèi)存管理技術(shù)(應(yīng)該說(shuō)這種不帶有MMU的處理器在嵌入式設(shè)備中相當(dāng)普偏)。uClinux仍然采用存儲(chǔ)器的分頁(yè)管理,系統(tǒng)在啟動(dòng)時(shí)把實(shí)際存儲(chǔ)器進(jìn)行分頁(yè)。在加載應(yīng)用程序時(shí)程序分頁(yè)加載。但是由于沒(méi)有MMU管理,所以實(shí)際上uClinux采用實(shí)存儲(chǔ)器管理策略(real memeory management)。這一點(diǎn)影響了系統(tǒng)工作的很多方面。 uClinux系統(tǒng)對(duì)于內(nèi)存的訪(fǎng)問(wèn)是直接的,(它對(duì)地址的訪(fǎng)問(wèn)不需要經(jīng)過(guò)MMU,而是直接送到地址線(xiàn)上輸出),所有程序中訪(fǎng)問(wèn)的地址都是實(shí)際的物理地址。操作系統(tǒng)對(duì)內(nèi)存空間沒(méi)有保護(hù)(這實(shí)際上是很多嵌入式系統(tǒng)的特點(diǎn)),各個(gè)進(jìn)程實(shí)際上共享一個(gè)運(yùn)行空間(沒(méi)有獨(dú)立的地址轉(zhuǎn)換表)。 一個(gè)進(jìn)程在執(zhí)行前,系統(tǒng)必須為進(jìn)程分配足夠的連續(xù)地址空間,然后全部載入主存儲(chǔ)器的連續(xù)空間中。與之相對(duì)應(yīng)的是標(biāo)準(zhǔn)Linux系統(tǒng)在分配內(nèi)存時(shí)沒(méi)有必要保證實(shí)際物理存儲(chǔ)空間是連續(xù)的,而只要保證虛存地址空間連續(xù)就可以了。另外一個(gè)方面程序加載地址與預(yù)期(ld文件中指出的)通常都不相同,這樣relocation過(guò)程就是必須的。此外磁盤(pán)交換空間也是無(wú)法使用的,系統(tǒng)執(zhí)行時(shí)如果缺少內(nèi)存將無(wú)法通過(guò)磁盤(pán)交換來(lái)得到改善。 uClinux對(duì)內(nèi)存的管理減少同時(shí)就給開(kāi)發(fā)人員提出了更高的要求。如果從易用性這一點(diǎn)來(lái)說(shuō),uClinux的內(nèi)存管理是一種倒退,退回了到了UNIX早期或是Dos系統(tǒng)時(shí)代。開(kāi)發(fā)人員不得不參與系統(tǒng)的內(nèi)存管理。從編譯內(nèi)核開(kāi)始,開(kāi)發(fā)人員必須告訴系統(tǒng)這塊開(kāi)發(fā)板到底擁有多少的內(nèi)存(假如你欺騙了系統(tǒng),那將在后面運(yùn)行程序時(shí)受到懲罰),從而系統(tǒng)將在啟動(dòng)的初始化階段對(duì)內(nèi)存進(jìn)行分頁(yè),并且標(biāo)記已使用的和未使用的內(nèi)存。系統(tǒng)將在運(yùn)行應(yīng)用時(shí)使用這些分頁(yè)內(nèi)存。 由于應(yīng)用程序加載時(shí)必須分配連續(xù)的地址空間,而針對(duì)不同硬件平臺(tái)的可一次成塊(連續(xù)地址)分配內(nèi)存大小限制是不同(目前針對(duì)ez328處理器的uClinux是128k,而針對(duì)coldfire處理器的系統(tǒng)內(nèi)存則無(wú)此限制),所以開(kāi)發(fā)人員在開(kāi)發(fā)應(yīng)用程序時(shí)必須考慮內(nèi)存的分配情況并關(guān)注應(yīng)用程序需要運(yùn)行空間的大小。另外由于采用實(shí)存儲(chǔ)器管理策略,用戶(hù)程序同內(nèi)核以及其它用戶(hù)程序在一個(gè)地址空間,程序開(kāi)發(fā)時(shí)要保證不侵犯其它程序的地址空間,以使得程序不至于破壞系統(tǒng)的正常工作,或?qū)е缕渌绦虻倪\(yùn)行異常。 從內(nèi)存的訪(fǎng)問(wèn)角度來(lái)看,開(kāi)發(fā)人員的權(quán)利增大了(開(kāi)發(fā)人員在編程時(shí)可以訪(fǎng)問(wèn)任意的地址空間),但與此同時(shí)系統(tǒng)的安全性也大為下降。此外,系統(tǒng)對(duì)多進(jìn)程的管理將有很大的變化,這一點(diǎn)將在uClinux的多進(jìn)程管理中說(shuō)明。 雖然uClinux的內(nèi)存管理與標(biāo)準(zhǔn)Linux系統(tǒng)相比功能相差很多,但應(yīng)該說(shuō)這是嵌入式設(shè)備的選擇。在嵌入式設(shè)備中,由于成本等敏感因素的影響,普偏的采用不帶有MMU的處理器,這決定了系統(tǒng)沒(méi)有足夠的硬件支持實(shí)現(xiàn)虛擬存儲(chǔ)管理技術(shù)。從嵌入式設(shè)備實(shí)現(xiàn)的功能來(lái)看,嵌入式設(shè)備通常在某一特定的環(huán)境下運(yùn)行,只要實(shí)現(xiàn)特定的功能,其功能相對(duì)簡(jiǎn)單,內(nèi)存管理的要求完全可以由開(kāi)發(fā)人員考慮。 標(biāo)準(zhǔn)Linux系統(tǒng)的進(jìn)程、線(xiàn)程 進(jìn)程:進(jìn)程是一個(gè)運(yùn)行程序并為其提供執(zhí)行環(huán)境的實(shí)體,它包括一個(gè)地址空間和至少一個(gè)控制點(diǎn),進(jìn)程在這個(gè)地址空間上執(zhí)行單一指令序列。進(jìn)程地址空間包括可以訪(fǎng)問(wèn)或引用的內(nèi)存單元的集合,進(jìn)程控制點(diǎn)通過(guò)一個(gè)一般稱(chēng)為程序計(jì)數(shù)器(program counter,PC)的硬件寄存器控制和跟蹤進(jìn)程指令序列。 fork:由于進(jìn)程為執(zhí)行程序的環(huán)境,因此在執(zhí)行程序前必須先建立這個(gè)能"跑"程序的環(huán)境。Linux系統(tǒng)提供系統(tǒng)調(diào)用拷貝現(xiàn)行進(jìn)程的內(nèi)容,以產(chǎn)生新的進(jìn)程,調(diào)用fork的進(jìn)程稱(chēng)為父進(jìn)程;而所產(chǎn)生的新進(jìn)程則稱(chēng)為子進(jìn)程。子進(jìn)程會(huì)承襲父進(jìn)程的一切特性,但是它有自己的數(shù)據(jù)段,也就是說(shuō),盡管子進(jìn)程改變了所屬的變量,卻不會(huì)影響到父進(jìn)程的變量值。 父進(jìn)程和子進(jìn)程共享一個(gè)程序段,但是各自擁有自己的堆棧、數(shù)據(jù)段、用戶(hù)空間以及進(jìn)程控制塊。換言之,兩個(gè)進(jìn)程執(zhí)行的程序代碼是一樣的,但是各有各的程序計(jì)數(shù)器與自己的私人數(shù)據(jù)。 當(dāng)內(nèi)核收到fork請(qǐng)求時(shí),它會(huì)先查核三件事:首先檢查存儲(chǔ)器是不是足夠;其次是進(jìn)程表是否仍有空缺;最后則是看看用戶(hù)是否建立了太多的子進(jìn)程。如果上述說(shuō)三個(gè)條件滿(mǎn)足,那么操作系統(tǒng)會(huì)給子進(jìn)程一個(gè)進(jìn)程識(shí)別碼,并且設(shè)定cpu時(shí)間,接著設(shè)定與父進(jìn)程共享的段,同時(shí)將父進(jìn)程的inode拷貝一份給子進(jìn)程運(yùn)用,最終子進(jìn)程會(huì)返回?cái)?shù)值0以表示它是子進(jìn)程,至于父進(jìn)程,它可能等待子進(jìn)程的執(zhí)行結(jié)束,或與子進(jìn)程各做個(gè)的。 exec系統(tǒng)調(diào)用:該系統(tǒng)調(diào)用提供一個(gè)進(jìn)程去執(zhí)行另一個(gè)進(jìn)程的能力,exec系統(tǒng)調(diào)用是采用覆蓋舊有進(jìn)程存儲(chǔ)器內(nèi)容的方式,所以原來(lái)程序的堆棧、數(shù)據(jù)段與程序段都會(huì)被修改,只有用戶(hù)區(qū)維持不變。 vfork系統(tǒng)調(diào)用:由于在使用fork時(shí),內(nèi)核會(huì)將父進(jìn)程拷貝一份給子進(jìn)程,但是這樣的做法相當(dāng)浪費(fèi)時(shí)間,因?yàn)榇蠖鄶?shù)的情形都是程序在調(diào)用fork后就立即調(diào)用exec,這樣剛拷貝來(lái)的進(jìn)程區(qū)域又立即被新的數(shù)據(jù)覆蓋掉。因此Linux系統(tǒng)提供一個(gè)系統(tǒng)調(diào)用vfork,vfork假定系統(tǒng)在調(diào)用完成vfork后會(huì)馬上執(zhí)行exec,因此vfork不拷貝父進(jìn)程的頁(yè)面,只是初始化私有的數(shù)據(jù)結(jié)構(gòu)與準(zhǔn)備足夠的分頁(yè)表。這樣實(shí)際在vfork調(diào)用完成后父子進(jìn)程事實(shí)上共享同一塊存儲(chǔ)器(在子進(jìn)程調(diào)用exec或是exit之前),因此子進(jìn)程可以更改父進(jìn)程的數(shù)據(jù)及堆棧信息,因此vfork系統(tǒng)調(diào)用完成后,父進(jìn)程進(jìn)入睡眠,直到子進(jìn)程執(zhí)行exec。當(dāng)子進(jìn)程執(zhí)行exec時(shí),由于exec要使用被執(zhí)行程序的數(shù)據(jù),代碼覆蓋子進(jìn)程的存儲(chǔ)區(qū)域,這樣將產(chǎn)生寫(xiě)保護(hù)錯(cuò)誤(do_wp_page)(這個(gè)時(shí)候子進(jìn)程寫(xiě)的實(shí)際上是父進(jìn)程的存儲(chǔ)區(qū)域), 這個(gè)錯(cuò)誤導(dǎo)致內(nèi)核為子進(jìn)程重新分配存儲(chǔ)空間。當(dāng)子進(jìn)程正確開(kāi)始執(zhí)行后,將喚醒父進(jìn)程,使得父進(jìn)程繼續(xù)往后執(zhí)行。 uClinux的多進(jìn)程處理 uClinux沒(méi)有mmu管理存儲(chǔ)器,在實(shí)現(xiàn)多個(gè)進(jìn)程時(shí)(fork調(diào)用生成子進(jìn)程)需要實(shí)現(xiàn)數(shù)據(jù)保護(hù)。 uClinux的fork和vfork:uClinux的fork等于vfork。實(shí)際上uClinux的多進(jìn)程管理通過(guò)vfork來(lái)實(shí)現(xiàn)。這意味著uClinux系統(tǒng)fork調(diào)用完程后,要么子進(jìn)程代替父進(jìn)程執(zhí)行(此時(shí)父進(jìn)程已經(jīng)sleep)直到子進(jìn)程調(diào)用exit退出,要么調(diào)用exec執(zhí)行一個(gè)新的進(jìn)程,這個(gè)時(shí)候?qū)a(chǎn)生可執(zhí)行文件的加載,即使這個(gè)進(jìn)程只是父進(jìn)程的拷貝,這個(gè)過(guò)程也不能避免。當(dāng)子進(jìn)程執(zhí)行exit或exec后,子進(jìn)程使用wakeup把父進(jìn)程喚醒,父進(jìn)程繼續(xù)往下執(zhí)行。 uClinux的這種多進(jìn)程實(shí)現(xiàn)機(jī)制同它的內(nèi)存管理緊密相關(guān)。uClinux針對(duì)nommu處理器開(kāi)發(fā),所以被迫使用一種flat方式的內(nèi)存管理模式,啟動(dòng)新的應(yīng)用程序時(shí)系統(tǒng)必須為應(yīng)用程序分配存儲(chǔ)空間,并立即把應(yīng)用程序加載到內(nèi)存。缺少了MMU的內(nèi)存重映射機(jī)制,uClinux必須在可執(zhí)行文件加載階段對(duì)可執(zhí)行文件reloc處理,使得程序執(zhí)行時(shí)能夠直接使用物理內(nèi)存。 |