




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
Linux容簡介:Linux擁有現(xiàn)代操作系統(tǒng)全部的功能,如真正的搶先式多任務(wù)處理、支持多用戶,存保護(hù),虛擬存,支持SMP、UP,符合POSIX標(biāo)準(zhǔn),聯(lián)網(wǎng)、圖形用戶接口和桌面環(huán)境。具有快速性、穩(wěn)定性等特點(diǎn)。本書通過分析Linux的核源代碼,充分提醒了Linux進(jìn)程、治理存等工作的?,F(xiàn)實(shí)中,能讓人自由獵取的系統(tǒng)源代碼并不多,通過本書的學(xué)習(xí),將大大有助于讀者編寫自己的程序。第一局部Linux核源代碼arch/i386/kernel/entry.S2arch/i386/kernel/init_task.c8arch/i386/kernel/irq.c8arch/i386/kernel/irq.h19arch/i386/kernel/process.c22arch/i386/kernel/signal.c30arch/i386/kernel/smp.c38arch/i386/kernel/time.c58arch/i386/kernel/traps.c65arch/i386/lib/delay.c73arch/i386/mm/fault.c74arch/i386/mm/init.c76fs/binfmt-elf.c82fs/binfmt_java.c96fs/exec.c98include/asm-generic/smplock.h107include/asm-i386/atomic.h108include/asm-i386/current.h109include/asm-i386/dma.h109include/asm-i386/elf.h113include/asm-i386/hardirq.h114include/asm-i386/.h114include/asm-i386/pgtable.h115include/asm-i386/ptrace.h122include/asm-i386/semaphore.h123include/asm-i386/shmparam.h124include/asm-i386/sigcontext.h125include/asm-i386/siginfo.h125include/asm-i386/signal.h127include/asm-i386/smp.h130include/asm-i386/softirq.h132include/asm-i386/spinlock.h133include/asm-i386/system.h137include/asm-i386/uaccess.h139include/linux/binfmts.h146include/linux/capability.h147include/linux/elf.h150include/linux/elfcore.h156include/linux/interrupt.h157include/linux/kernel.h158include/linux/kernel_stat.h159include/linux/limits.h160include/linux/mm.h160include/linux/module.h164include/linux/msg.h168include/linux/personality.h169include/linux/reboot.h169include/linux/resource.h170include/linux/sched.h171include/linux/sem.h179include/linux/shm.h180include/linux/signal.h181include/linux/slab.h184include/linux/smp.h184include/linux/smp_lock.h185include/linux/swap.h185include/linux/swapctl.h187include/linux/sysctl.h188include/linux/tasks.h194include/linux/time.h194include/linux/timer.h195include/linux/times.h196include/linux/tqueue.h196include/linux/wait.h198init/main.c198init/version.c212ipc/msg.c213ipc/sem.c218ipc/shm.c227ipc/util.c236kernel/capability.c237kernel/dma.c240kernel/exec_domain.c241kernel/exit.c242kernel/fork.c248kernel/info.c255kernel/itimer.c255kernel/kmod.c257kernel/module.c259kernel/panic.c270kernel/printk.c271kernel/sched.c275kernel/signal.c295kernel/softirq.c307kernel/sys.c307kernel/sysctl.c318kernel/time.c330mm/memory.c335mm/mlock.c345mm/mmap.c348mm/mprotect.c358mm/mremap.c361mm/_alloc.c363mm/_io.c368mm/slab.c372mm/swap.c394mm/swap_state.c395mm/swapfile.c398mm/vmalloc.c406mm/vmscan.c409其次局部Linux核源代碼分析1Linux讓用戶很具體地了解大多數(shù)現(xiàn)有操作系統(tǒng)的實(shí)際工作方式是不行能的,由于大多數(shù)操作系統(tǒng)的源代碼都是嚴(yán)格的。除了一些爭論用的及為操作系統(tǒng)教學(xué)而設(shè)計(jì)的系統(tǒng)外。盡管爭論和教學(xué)目的都很好,但是這類系統(tǒng)很少能夠通過對正式操作系統(tǒng)的小局部實(shí)現(xiàn)來表達(dá)操作系統(tǒng)的實(shí)際功能。對于操作系統(tǒng)的一些特別問題,這種折衷系統(tǒng)所能夠表現(xiàn)的就更是少得可憐了。在以實(shí)際使用為目標(biāo)的操作系統(tǒng)中,讓任何人都可以自由獵取系統(tǒng)源代碼,無論目的是要了解、學(xué)習(xí)還是改進(jìn),這樣的現(xiàn)實(shí)系統(tǒng)并不多。本書的主題就是這些少數(shù)操作系統(tǒng)中的一個(gè):Linux。Linux的工作方式類似于Uinx,它是免費(fèi)的,源代碼也是開放的,符合標(biāo)準(zhǔn)規(guī)的32〔64位CPU64〕操作系統(tǒng)。Linux真正的搶先式多任務(wù)處理,支持多用戶。存保護(hù)。虛擬存。支持對稱多處理機(jī)SM〔symmetricmultiprocessin,即多個(gè)CPU機(jī)器以及通常的單CP〔U〕機(jī)器。符合POSIX聯(lián)網(wǎng)。圖形用戶接口和桌面環(huán)境〔實(shí)際上桌面環(huán)境并不只一個(gè)。速度和穩(wěn)定性。嚴(yán)格說來,Linux并不是一個(gè)完整的操作系統(tǒng)。當(dāng)我們在安裝通常所說的Linux時(shí),我們實(shí)際安裝的是很多工具的集合。這些工具協(xié)同工作以組成一個(gè)功能強(qiáng)大的有用系統(tǒng)。Linux本身只是這個(gè)操作系統(tǒng)的核,是操作系統(tǒng)的心臟、靈魂、指揮中心〔整個(gè)系統(tǒng)應(yīng)當(dāng)稱為GNU/Linux,其緣由在本章的后續(xù)容中將會(huì)給以介紹。核以獨(dú)占的方式執(zhí)行最底層任務(wù),保證系統(tǒng)正常運(yùn)行—協(xié)調(diào)多個(gè)并發(fā)進(jìn)程,治理進(jìn)程使用的存,使它們相互之間不產(chǎn)生沖突,滿足進(jìn)程訪問磁盤的懇求等等。在本書中,我們給大家提醒的就是Linux是如何完成這一具有挑戰(zhàn)性的工作的。LinuxUnix為了讓大家對本書所爭論的容有更清楚的了解,讓我們先來簡要回憶一下Linux的歷史。由于Linux是在UnixUnixUnixAT&T貝爾試驗(yàn)室的KenThompson和DennisRitchie1969PDP-7Thompson和Ritchie成功地說服治理部門為他們購置更的機(jī)器,以便該開發(fā)小組可以實(shí)現(xiàn)一個(gè)文本處理系統(tǒng),UnixPDP-11C編寫〔制造C語言的局部目的就在于此。它果真變成了一個(gè)文本處理系統(tǒng)—不久之后。只不過問題是他們先實(shí)現(xiàn)了一個(gè)操作系統(tǒng)而已……Unix〔以及Unix上運(yùn)行的工具〕也在AT&T得到廣泛應(yīng)用。在1973年,Thompson和Ritchie在一個(gè)操作系統(tǒng)會(huì)議上就這個(gè)系統(tǒng)發(fā)表了一篇論文,該論文引起了學(xué)術(shù)界Unix由于1956年反托拉斯法案的限制,AT&T不能涉足計(jì)算機(jī)業(yè)務(wù),但允許它象征性地收取費(fèi)用出售該系統(tǒng)。就這樣,Unix伯克利加州大學(xué)是學(xué)術(shù)用戶中的一個(gè)。在這里,Unix得到了計(jì)算機(jī)系統(tǒng)爭論小組〔CSRG〕的廣泛應(yīng)用。并且在這里所進(jìn)展的修改引發(fā)了Unix〔BSD〕UnixAT&T所供給的UnixBSD是最有影響力的UnixBSD在UnixTCP/IP網(wǎng)絡(luò),更好的用戶文件系統(tǒng)UF,工作掌握,并且改進(jìn)了AT&T的存治理代碼。多年以來,BSDUnixSystemVAT&TUnix則成為商業(yè)領(lǐng)域的領(lǐng)頭羊。從某種程度上來說,這是有社會(huì)緣由的:學(xué)校傾向于使用非正式但通常更好用的BSDUnix,而商業(yè)界則傾向于從AT&TUnix。在用戶需求和用戶編程改進(jìn)特性的促進(jìn)下,BSD風(fēng)格的Unix一般要比AT&T的UnixAT&T公布最終一個(gè)正式版本SystemVRelease4〔SVR4〕時(shí),SystemVUnix已經(jīng)吸取了BSD1984年開頭,AT&T漸漸可以將Unix商業(yè)化,而伯克利Unix1993年BSD4.4止了。然而,BSD的進(jìn)一步改進(jìn)由外界開發(fā)者連續(xù)下來,到今日還在連續(xù)進(jìn)展。正在進(jìn)展的Unix系列開發(fā)BSD4.4Unix版本,例如惠普的HP-UX,都是局部地或者全部基于BSD實(shí)際上Unix的變種并不止BSDSystemV。由于Unix主要使用CUnix的這些特點(diǎn)大受商業(yè)界硬件供給商的歡送,比方Sun、SGI、HP、IBM、DEC、AmdahlIBM還不止一次對Unix們設(shè)計(jì)開發(fā)出的硬件,并簡潔地將Unix移植到的硬件上,這樣的硬件一經(jīng)公布便具備肯定的功能。經(jīng)過一段時(shí)間之后,這些廠商都擁有了自己的專有Unix版本。而且為了占有市場,這些版本有意以不同的側(cè)重點(diǎn)公布出來,以更好地占有用戶。版本混亂的狀態(tài)促進(jìn)了標(biāo)準(zhǔn)化工作的進(jìn)展。其中最主要的就是POSIX系列標(biāo)準(zhǔn),它定義了一套標(biāo)準(zhǔn)的操作系統(tǒng)接口和工具。從理論上說,POSIX標(biāo)準(zhǔn)代碼很簡潔移植到任何遵守POSIX標(biāo)準(zhǔn)的操作系統(tǒng)中,而且嚴(yán)格的POSIX測試已經(jīng)把這種理論上的可移植性轉(zhuǎn)化為現(xiàn)實(shí)。直到今日,幾乎全部的正式操作系統(tǒng)都以支持POSIX1984RichardStallmanUnix統(tǒng),該操作系統(tǒng)具有完全的核、開發(fā)工具和終端用戶應(yīng)用程序。在GN“GNU誷NotUni”首字母的縮寫〕打算的協(xié)作下,Stallman統(tǒng)。Stallman〔fre〕Stallman〔FSF〕GNU〔FSF也在資助其他科研方面的開發(fā)工作。15GNUEmacsgcc〔GNUC、bash〔shell命令LinuxGNUHurdGNU操作系統(tǒng)的最終一個(gè)主要部件〔Hurd0.3的系統(tǒng)在什么時(shí)候能夠完成,還是未知數(shù)。Linux大受歡送,但是HurdHurd表達(dá)了Stallman關(guān)于操作系統(tǒng)工作方式的思想,例如,在運(yùn)行期間,任何用戶都可以局部地轉(zhuǎn)變或替換Hurd〔這種替換不是對每個(gè)用戶都是可見的,而是只對申請修改的用戶可見,而且還必需符合安全規(guī)。另一個(gè)緣由是據(jù)介紹Hurd對于多處理器的支持比Linux本身的核要好。還有一個(gè)簡潔的緣由是興趣的驅(qū)動(dòng),由于程序員們期望能夠自由地進(jìn)展自己所寵愛的工作。只要有人期望為Hurd工作,Hurd的開發(fā)就不會(huì)停頓。假設(shè)他們能夠如愿以償,Hurd有朝一日將成為LinuxLinux核王國里無可爭議的統(tǒng)治者。GNU1991年,一個(gè)名叫LinusTorvaldsIntelCPU—80386。他認(rèn)為比較好的學(xué)習(xí)方法是自己編寫一個(gè)操作系統(tǒng)的核。出于這種目的,加上他對當(dāng)時(shí)Unix變種80386POSIX標(biāo)準(zhǔn)的、類Unix的操作系統(tǒng)核,該系統(tǒng)吸取了BSDSystemVLinus〔雖然我知道我應(yīng)當(dāng)稱他為TorvaldsLinus〕0.02版,這個(gè)版本已經(jīng)可以運(yùn)行g(shù)cc、bash和很少的一些應(yīng)用程序。這些就是他開頭的全部工作了。后來,他又開頭在因特網(wǎng)上尋求廣泛的幫助。不到三年,Linus的Unix—Linux,已經(jīng)升級(jí)到1.0版本。它的源代碼量也呈指數(shù)形式增長,實(shí)現(xiàn)了根本的TCP/IP功能〔網(wǎng)絡(luò)局部的代碼后來重寫過,而且還可能會(huì)再次重寫。此時(shí)Linux就已經(jīng)擁有大約10萬用戶了。現(xiàn)在的Linux150Linux1000〔Linux和拷貝,獵取具體的統(tǒng)計(jì)數(shù)字是不行能的。LinuxGNU/LinuxGNUUnix50%的市場。一些公司正在把核和一些應(yīng)用程序同安裝軟件打包在一起,生產(chǎn)出Linux的發(fā)行版本,這些公司包括RedHatCaldera公司?,F(xiàn)在的GNU/LinuxSun、IBM、SGI持。SGI最近打算在其基于IntelMercedUnix變種版本IRIX,而是直接GNU/Linux;LinuxAmigaGNU中國這樣一個(gè)如此流行的操作系統(tǒng)固然值得我們學(xué)習(xí)。依據(jù)通用公共許可證(GPL,GeneralPublicLicense)的規(guī)定,Linux的源代碼可以自由獵取,這滿足了我們學(xué)習(xí)該系統(tǒng)的猛烈愿望。GPL這份非同尋常的軟件許可證,充分表達(dá)了上面提到的Stallman的思想:只要用戶所做的修改是同等自由的,用戶可以自由地使用、拷貝、查詢、重用、修改甚至重公布這個(gè)軟件。通過這種方式,GPL保證了Linux〔以及同一許可證保證下的大量其他軟件〕不僅現(xiàn)在自由可用,而且以后經(jīng)過任何修改之后都仍舊可以自由使用。請留意這里的自由并不是說沒有人靠這個(gè)軟件盈利,有一些日益興起的公司,比方發(fā)行最流行的Linux發(fā)RedHat〔RedHat元,而且這些數(shù)字還在不斷增長。但是任何人都不能限制其他用戶涉足本軟件領(lǐng)域,而且所做的修改不能削減其自由程度。本書的附錄B中收錄了GNULinux中國Linux是一個(gè)自由軟件,它可以免費(fèi)獵取以供學(xué)習(xí)爭論。Linux之所以值得學(xué)習(xí)爭論,是由于它是相當(dāng)優(yōu)秀的操作系統(tǒng)。假設(shè)Linux操作系統(tǒng)相當(dāng)糟糕,那它就根本不值得我們使用,也就沒有必要去爭論相關(guān)的書籍。Linux緣由之一在于它是基于天才的思想開發(fā)而成的。在學(xué)生時(shí)代就開頭推動(dòng)整個(gè)系統(tǒng)開發(fā)的LinusTorvaldsLinux的核是由世界上一些最優(yōu)秀的程序員開發(fā)并不斷完善的,他們通過Internet相互協(xié)作,開發(fā)抱負(fù)的操作系統(tǒng);他們享受著工Linux優(yōu)秀的另外一個(gè)緣由在于它是基于一組優(yōu)秀的概念。Unix是一個(gè)簡潔卻格外優(yōu)秀的模型。在Linux創(chuàng)立之前,Unix20LinuxUnixUnix優(yōu)點(diǎn),拋棄UnixLinux成為了Unix拋棄了歷史包袱。然而,Linux最強(qiáng)大的生命力還在于其公開的開發(fā)過程。每個(gè)人都可以自由獵取核源程序,每個(gè)人都可以對源程序加以修改,而后他人也可以自由獵取你修改后的源程序。假設(shè)你覺察了缺陷,你可以對它進(jìn)展修正,而不用去懇求不知名的公司來為你修正。假設(shè)你有什么最優(yōu)化或者特點(diǎn)的創(chuàng)意,你也可以直接在系統(tǒng)中增加功能,而不用向操作系統(tǒng)供給商解釋你的想法,希望他們將來會(huì)增加相應(yīng)的功能。當(dāng)覺察一個(gè)安全漏洞后,你可以通過編程來彌補(bǔ)這個(gè)漏洞,而不用關(guān)閉系統(tǒng)直到你的供給商為你供給修補(bǔ)程序。由于你擁有直接訪問源代碼的力量,你也可以直接閱讀代碼來查找缺陷,或是效率不高的代碼,或是安全漏洞,以防患于未然。除非你是一個(gè)程序員,否則這一點(diǎn)聽起來仿佛沒有多少吸引力。實(shí)際上,即使你不是程序員,這種開發(fā)模型也將使你受益匪淺,這主要表達(dá)在以下兩個(gè)方面:可以間承受益于世界各地成千上萬的程序員隨時(shí)進(jìn)展的改進(jìn)工作。假設(shè)你需要對系統(tǒng)進(jìn)展修改,你可以雇用程序員為你完成工作。這局部人將依據(jù)你的需求定義單獨(dú)為你效勞??梢栽O(shè)想,這在源程序不公開的操作系統(tǒng)中將是什么樣子。Linux這種獨(dú)特的自由流暢的開發(fā)模型已被命名為bazaa〔集市模型,它是相對于cathedra〔教堂〕模型而言的。在cathedral模型中,源程序代碼被鎖定在一個(gè)的小圍。只有開發(fā)者〔很多狀況下是市場〕認(rèn)EricS.Raymond〔TheCathedralandtheBazaar〕“:///~esr/writings/找到這篇文“/~esr/writings/找到這篇文章。bazaar開發(fā)模型通過重視試驗(yàn),征集并充分利用早期的反響,對巨大數(shù)量的腦力資源進(jìn)展平衡配置,可以開發(fā)出更優(yōu)秀的軟件〔順便說一下,雖然Linux是最為明顯的使用bazaar卻遠(yuǎn)不是第一個(gè)使用這個(gè)模型的系統(tǒng)〕Linux承受了雙樹系統(tǒng)。一個(gè)樹是穩(wěn)定樹stabletre,另一個(gè)樹是非穩(wěn)定樹unstabletre〕或者開發(fā)樹developmenttre。一些特性、試驗(yàn)性改進(jìn)等都在穩(wěn)定樹中將進(jìn)展一樣的改進(jìn)。依據(jù)Linus的觀點(diǎn),一旦開發(fā)樹經(jīng)過了足夠的進(jìn)展,開發(fā)樹就會(huì)成為的穩(wěn)定樹,如此周而復(fù)始的進(jìn)展下去。源程序版本號(hào)的形式為x.y.z。對于穩(wěn)定樹來說,yy〔因此,是奇數(shù)。截至到本書截稿時(shí),最的穩(wěn)定核版本號(hào)是2.2.10,最的開發(fā)核的版本號(hào)是2.3.12。對2.3〔back-propagated〕.0。〔順便說一下,這種開發(fā)會(huì)比常規(guī)慣例要快,由于每一版本所包含的轉(zhuǎn)變比以前更少了,核開發(fā)人員只需花很短的時(shí)間就能夠完成一個(gè)試驗(yàn)開發(fā)周期〕.及其鏡像站點(diǎn)供給了最的可供下載的核版本,而且同時(shí)包括穩(wěn)定和開發(fā)版本。假設(shè)你情愿的話,不需要很長時(shí)間,這些站點(diǎn)所供給的最版本中就可能包含了你的一局部源程序代碼。中國2本章首先從較高層次介紹Linux核源程序的概況,這些都是大家關(guān)心的一些根本特點(diǎn)。隨后將簡要介紹一些實(shí)際代碼。最終介紹如何編譯核。Linux在過去的一段時(shí)期,LinuxC語言和匯編語言來實(shí)現(xiàn)。這兩種語言需要肯定的平衡:C語言編寫的代碼移植性較好、易于維護(hù),而匯編語言編寫的程序則速度較快。一般只有在速度是關(guān)鍵因素或者一些因平臺(tái)相關(guān)特性而產(chǎn)生的特別要求〔例如直接和存治理硬件進(jìn)展通訊〕時(shí)才使用匯編語言。正照實(shí)際中所做的,即使核并未使用C++的對象特性,局部核也可以在g++〔GNU的C++編譯器〕下進(jìn)展編譯。同其他面對對象的編程語言相比較,相對而言C++的開銷是較低的,但是對于核開發(fā)人員來說,這已經(jīng)是太多了。核開發(fā)人員不斷進(jìn)展編程風(fēng)格,形成了Linux代碼獨(dú)有的特色。本節(jié)將爭論其中的一些問題。gccLinux核被設(shè)計(jì)為必需使用GNUCgccC時(shí)要使用gccgcc特有代碼只是簡潔地使用gcc語言擴(kuò)展,例如允許在C〔不只是C++〕中使用inline〔注一、inlineC的宏定義一例:#defineExpressionName(Var1,Var2)((Var1)+(Var2))*((Var1)-(Var2))為什么要取代這種形式呢,且聽我道來:函數(shù),但它使用預(yù)處理器實(shí)現(xiàn),沒有了參數(shù)壓棧,代碼生成等一系列的操作,因此,效率很高,這是它在C中被使用的一個(gè)主要緣由。參數(shù)有效性的檢測,也就不能享受C++編譯器嚴(yán)格類型檢查的好處,另外它的返回值也不能被強(qiáng)制轉(zhuǎn)換為可轉(zhuǎn)換的適宜的類型,這樣,它的使用就存在著一系列的隱患和局限性。thisinline點(diǎn)。1-3inline,〔像宏一樣開放〕,沒有了調(diào)用的開銷,效率也很高。確。然后進(jìn)展一系列的相關(guān)檢查,就像對待任何一個(gè)真正的函數(shù)一樣。這樣就消退了它的隱患和局限性。inlineinline首先,你可以使用inline函數(shù)完全取代表達(dá)式形式的宏定義。函數(shù)太簡單,代碼膨脹帶來的惡果很可能會(huì)大于效率的提高帶來的好處。聯(lián)函數(shù)最重要的使用地方是用于類的存取函數(shù)。inline1.在類中定義這種函數(shù):classClassName{.........GetWidth{returnm_lPicWidth;};//inline........}classClassName{.........GetWidth;//inline........}inlinereturn_typeClassName::GetWidth{returnm_lPicWidth;}二、使用inline聯(lián)函數(shù)替代宏調(diào)用可能引起不期望的副作用。例如宏:#defineabs〔a〕〔〔a〕<0?〔-a〕:〔a〕〕,abs〔i++〕時(shí),這個(gè)宏就會(huì)出錯(cuò)。inlineintAdd(inta,intb);//Add〔〕為聯(lián)函數(shù)Add函數(shù)時(shí),就不再進(jìn)展函數(shù)調(diào)用,而是直接嵌入函數(shù)代碼以加快程序的執(zhí)行?!持甘韭?lián)函數(shù)。也就是說,代碼中被調(diào)用的函數(shù)在每次函數(shù)調(diào)用時(shí)都會(huì)被擴(kuò)大,因而就可以節(jié)約實(shí)際函數(shù)調(diào)用的開銷。一般狀況下,代碼的編寫方式比較簡單。由于對于某些類型的輸入,gcc能夠產(chǎn)生比其他輸入效率更高的執(zhí)行代碼。從理論上講,編譯器可以優(yōu)化具有一樣功能的兩種對等的方法,并且得到一樣的結(jié)果。因此,代碼的編寫方式是無關(guān)緊要的。但在實(shí)際上,用某種方法編寫所產(chǎn)生的代碼要比用另外一些方法編寫所產(chǎn)生的代碼執(zhí)行速度快很多。核開發(fā)人員知道怎樣才能產(chǎn)生更高效的執(zhí)行代碼,這不斷地在他們編寫的代碼中反映出來。例如,考慮核中常常使用的goto語句—為了提高速度,核中常常大量使用這種一般要避開使用的語句。40000500goto80外,準(zhǔn)確的統(tǒng)計(jì)數(shù)字是接近每72行一個(gè)goto語句。公正地說,這是選擇偏向的結(jié)果:比例如此高的緣由之一是本書中涉及的是核源程序的核心,在這里速度比其他因素都需要優(yōu)先考慮。整個(gè)核的比例或許是每260行一個(gè)gotoBasicgoto代碼必需受特定編譯器限制的特性不僅與一般應(yīng)用程序的開發(fā)有很大不同,而且也不同于大多數(shù)核的開發(fā)。大多數(shù)的開發(fā)人員使用C語言編寫代碼來保持較高的可移植性,即使在編寫操作系統(tǒng)時(shí)也是如此。這樣做的優(yōu)點(diǎn)是顯而易見的,最為重要的一點(diǎn)是一旦消滅更好的編譯器,程序員們可以隨時(shí)進(jìn)展更換。核對于gcc特性的完全依靠使得核向的編譯器上移植更加困難。最近Linus對這一問題在有關(guān)核的列表上說明白自己的觀點(diǎn)gcc特性的一個(gè)很好的根本思想的表述:編譯器只是為了完成工作。假設(shè)通過遵守標(biāo)準(zhǔn)還不能到達(dá)工作要求,那么就不是工作要求有問題,而是對于標(biāo)準(zhǔn)的依靠有問題。在大多數(shù)狀況下,這種觀點(diǎn)是不能被人所承受的。通常狀況下,為了保證和程序語言標(biāo)準(zhǔn)的全都,開發(fā)人員可能需要犧牲某些特性、速度或者其他相關(guān)因素。其他的選擇可能會(huì)為后期開發(fā)造成很大的麻煩。但是,在這種特定的狀況下,Linus是正確的。Linux核是一個(gè)特例,由于其執(zhí)行速度要比向其他編譯器的可移植性遠(yuǎn)為重要。假設(shè)設(shè)計(jì)目標(biāo)是編寫一個(gè)可移植性好而不要求快速運(yùn)行的核,或者是編寫一個(gè)任何人都可以使用自己寵愛的編譯器進(jìn)展編譯的核,那么結(jié)論就可能會(huì)有所不同了;而這些恰好不是Linux的設(shè)計(jì)目標(biāo)。實(shí)際上,gccLinuxCPU生成代碼,因此,對于gcc可移植性的嚴(yán)峻障礙。3核代碼習(xí)慣用語核代碼中使用了一些顯著的習(xí)慣用語,本節(jié)將介紹常用的幾個(gè)。當(dāng)通讀源代碼時(shí),真正重要的問題并不在這些習(xí)慣用語本身,而是這種類型的習(xí)慣用語確實(shí)存在,而且是不斷被使用和進(jìn)展的。假設(shè)你需要編寫核代碼,你應(yīng)當(dāng)留意到核中所使用的習(xí)慣用語,并把這些習(xí)慣用語應(yīng)用到你的代碼中。當(dāng)通讀本書〔或者代碼〕時(shí),看看你還能找到多少習(xí)慣用語。為了爭論這些習(xí)慣用語,我們首先需要對它們進(jìn)展命名。為了便于爭論,筆者制造了這些名字。而在實(shí)際中,大家不肯定非要參考這些用語,它們只是對核工作方式的描述而已。一個(gè)一般的習(xí)慣用語,筆者稱之為“資源獵取〔resourceacquisitionidio。在這個(gè)用語中,一個(gè)函數(shù)必需實(shí)現(xiàn)一系列資源的獵取,包括存、鎖等等〔這些資源的類型未必一樣。只有成功地獵取當(dāng)前所需要的資源之后,才能處理后面的資源懇求。最終,該函數(shù)還必需釋放全部已經(jīng)獵取的資源,而不必考慮沒有獵取的資源。我承受“錯(cuò)誤變量”這一用語〔errorvariableidiom〕來關(guān)心說明資源獵取用語,它使用一個(gè)臨時(shí)變量來記錄函數(shù)的期望返回值。固然,相當(dāng)多的函數(shù)都能實(shí)現(xiàn)這個(gè)功能。但是錯(cuò)誤變量的不同點(diǎn)在于它通常是用來處理由于速度的因素而變得格外簡單的流程掌握中的問題。錯(cuò)誤變量有兩個(gè)典型的值,0〔表示成功〕和負(fù)數(shù)〔表示有錯(cuò)。假設(shè)執(zhí)行到標(biāo)號(hào)out2r1和r2out1〔不管是挨次執(zhí)行還是使用goto語句進(jìn)展跳轉(zhuǎn)到,則r2資源是無效的〔也可能剛被釋放,但是r1是有效的,而且必需在此將其釋放。同理,假設(shè)標(biāo)號(hào)out能被執(zhí)行,則r1r2err的是錯(cuò)誤或成功標(biāo)志。在這個(gè)簡潔的例子中,對err的一些賦值是沒有必要的。在實(shí)踐中,實(shí)際代碼必需遵守這種模式。這樣做的緣由主要在于同一行中可能包含有多種測試,而這些測試應(yīng)當(dāng)返回一樣的錯(cuò)誤代碼,因此對錯(cuò)誤變量統(tǒng)一賦值要比屢次賦值更為簡潔。雖然在這個(gè)例子中對于這種屬性的必要性并不格外迫切,但是我還是傾向于保存這種特點(diǎn)。有關(guān)的實(shí)際應(yīng)用可以參考sys_shmct〔第21654行,在第9章中還將具體介紹這個(gè)例子。削減#if#ifdef現(xiàn)在的Linux核已經(jīng)移植到不同的平臺(tái)上,但是我們還必需解決移植過程中所消滅的問題。大局部支持各種不同平臺(tái)的代碼由于包含很多預(yù)處理代碼而已經(jīng)變得格外不規(guī),例如:這個(gè)例子試圖實(shí)現(xiàn)操作系統(tǒng)的可移植性,雖然Linux關(guān)注的焦點(diǎn)很明顯是實(shí)現(xiàn)代碼在各種CPU上的可移植性,但是二者的根本原理是全都的。對于這類問題來說,預(yù)處理器是一種錯(cuò)誤的解決方式。這些雜亂的問題使得代碼晦澀難懂。更為糟糕的是,增加對平臺(tái)的支持有可能要求重遍歷這些雜亂分布的低質(zhì)量代碼段〔實(shí)際上你很難能找到這類代碼段的全部。中國與現(xiàn)有方式不同的是,Linux一般通過簡潔函數(shù)〔或者是宏〕調(diào)用來抽象出不同平臺(tái)間的差異。核的移植可以通過實(shí)現(xiàn)適合于相應(yīng)平臺(tái)的函數(shù)〔或宏〕來實(shí)現(xiàn)。這樣不僅使代碼的主體簡潔易懂,而且在移植的過程中還可以比較簡潔地自動(dòng)檢測出你沒有留意到的容:如引用未聲明函數(shù)時(shí)會(huì)消滅錯(cuò)誤。有時(shí)用預(yù)處理器來支持不同的體系構(gòu)造,但這種方式并不常用,而相對于代碼風(fēng)格的變化就更是微缺乏道了。順便說一下,我們可以留意到這種解決方法和使用用戶對象〔或者C語言中布滿函數(shù)指針的struct構(gòu)造〕來代替離散的switch語句處理不同類型的方法格外相像。在某些層次上,這些問題和解決方法是統(tǒng)一的??梢浦残缘膯栴}并不僅限于平臺(tái)和CPU的移植,編譯器也是一個(gè)重要的問題。此處為了簡化,假設(shè)Linux只使用gcc來編譯。由于Linux#if〔或者#ifdef〕來選擇不同的編譯器。核代碼主要使用#ifdef來區(qū)分需要編譯或不需要編譯的局部,從而對不同的構(gòu)造供給支持。例如,代碼常常測試SMPSMP代碼樣例了解Linux代碼風(fēng)格最好的方法就是實(shí)際爭論一下它的局部代碼。即使你不完全理解本節(jié)所爭論代碼的細(xì)節(jié)也無關(guān)緊要,到底本節(jié)的主要目的不是理解代碼,一些讀者可以只對本節(jié)進(jìn)展掃瞄。本節(jié)的主要目的是讓讀者對Linux代碼進(jìn)展初步了解,為今后的工作供給必要根底。該爭論將涉及局部廣泛使用的核代碼。printkprintk〔25836行〕是核部消息日志記錄函數(shù)。在消滅諸如核檢測到其數(shù)據(jù)構(gòu)造消滅不全都的大事時(shí),核會(huì)使用printkprintk緊急大事〔emergency〕—例如,panic〔25563〕屢次使用了printk。當(dāng)核檢測到發(fā)生不行恢復(fù)的部錯(cuò)誤時(shí)就會(huì)調(diào)用panic函數(shù),然后盡其所能地安全關(guān)閉計(jì)算機(jī)。這個(gè)函數(shù)中調(diào)用printk以提示用戶系統(tǒng)將要關(guān)閉。3816#ifdefprintkSMP〔box〕中每一個(gè)處理器的相關(guān)配置信息,但是此過程只有在使用SMP_DEBUG一般信息—例如,當(dāng)機(jī)器啟動(dòng)時(shí),核必需估量系統(tǒng)速度以確保設(shè)備驅(qū)動(dòng)程序能夠忙等待〔busy-wait〕一個(gè)準(zhǔn)確的極短周期。計(jì)算這種估量值的函數(shù)名為calibrate_dela19654行,它既在19661行使用printk聲明馬上開頭計(jì)算,又在19693行報(bào)告計(jì)算結(jié)果。另外,在第4章將具體的介紹calibrate_delay函數(shù)。printkprintfN07包括0和7在。數(shù)字區(qū)分了消息的日志等級(jí)logleve,只有當(dāng)日志等級(jí)高于當(dāng)前掌握臺(tái)定義的日志等級(jí)〔console_loglevel,25650〕時(shí),才會(huì)打印消息。root不是很緊急的消息。假設(shè)核在格式化字符串中檢測不到日志等級(jí)序列,那么就會(huì)始終打印消息〔實(shí)際上,日志等級(jí)序列并不肯定要在格式化字符串中消滅,可以在格式化文本中查找到它的代碼。14946#defineprintk簡潔地說,我稱日志等級(jí)0到45到等級(jí)67自然就是我所說〔這種分類方法并不意味著其他更好的分類方法沒有用處,而只是目前我們還不關(guān)心它而已。在上面爭論的根底上,我們爭論一下代碼本身。printk25836:參數(shù)fmtprintf好的C語言參考書〔variadicfunctio。另外,在安裝的GNU/Linux中stdargmanstdarg”就可以看到。fmt的時(shí)候還沒有類型和名字,核使用由三個(gè)宏va_start、va_arg和va_endva_list25842:msg_level記錄了當(dāng)前消息的日志等級(jí)。它是靜態(tài)的,這看起來可能會(huì)有些驚異—為什么下一次對printk〔\n〕或者賦給一個(gè)的日志等級(jí)序列printk,就保證了在多個(gè)短期沖突的狀況下,調(diào)用者只打印唯一一個(gè)長消息。25845:在SMPCPU向掌握臺(tái)同時(shí)打印信息〔有時(shí)在單處理機(jī)〔UP〕規(guī)律單元中也會(huì)發(fā)生同樣問題,但由于中斷還未被掩蓋掉,所以問題也并不格外明顯。假設(shè)不進(jìn)展任何協(xié)同的話,結(jié)果就將處于完全無法讓人了解的雜亂無章的狀態(tài),每個(gè)消息的各個(gè)局部都和其他消息的各個(gè)部分混雜交織在一起。相反,核使用旋轉(zhuǎn)鎖〔spin-lock〕來掌握對掌握臺(tái)的訪問。旋轉(zhuǎn)鎖將在第10章進(jìn)展深入介紹。假設(shè)你對flags在傳送給spin_lock_irqsave之前為什么不對它初始化感到疑心,請不要擔(dān)憂:spin_lock_irqsave〔1261412637,1271612837〕是一個(gè)宏,flagsflags〔25895flags中的信息被spin_unlock_irqrestore12616126391272812841。25846:初始化變量args,該變量代表printk25848vsprintf〔為節(jié)約空間而省略vsprintfbuf〔25634〕并返回寫入字符串的長度〔長度不包括最終一位終止字符0。很快,你將可以看到為什么這種機(jī)制會(huì)無視buf〔正如25847行的注釋中所述〕我們應(yīng)當(dāng)留意到在這里并沒有實(shí)行嚴(yán)格的措施來保證緩沖器不會(huì)過載。這1024個(gè)字符長度的buf〔參閱25634。假設(shè)核在這里能夠使用vsnprintf數(shù)的話,狀況就會(huì)好很多。然而,vsnprintf25849:計(jì)算bufva_end2585:開頭格式化消息的循環(huán)。其中存在一個(gè)部循環(huán)能夠處理更多容〔這一點(diǎn)隨后就能看到,因此,每次循環(huán)開頭,都開頭一個(gè)的打印行。由于通常狀況下printk只用于打印單行,所以在每次調(diào)用中,這種循環(huán)通常只執(zhí)行一次。25853:假設(shè)預(yù)先不知道消息的日志等級(jí),printk25860:假設(shè)不是,buf中開頭未使用的三個(gè)字符就能夠起作用了〔第一次以后的每次循環(huán),都會(huì)掩蓋局部消息文本,但是這樣并不會(huì)引起問題,由于這里的文本只是前面行中的一局部,它們已經(jīng)被打印過,而且以后也不再需要了。這樣,就可以將日志等級(jí)插入buf2586p指向日志等級(jí)序列〔消息文本緊隨其后,msg指向消息文本—請留意25865msg由于p用來指示日志等級(jí)序列的開頭—該日志等級(jí)序列可能是由函數(shù)自身所創(chuàng)立的,日志等級(jí)可以從pmsg_level25868:沒有檢測到行,清空line_feed25869:這是前面談到過的循環(huán),循環(huán)將運(yùn)行到本行完畢〔也就是檢測到行標(biāo)志〕或者緩沖器的末尾為止。25870printk還能夠記錄最近打印的長度為LOG_BUF_LEN的字符組〔LOG_BUF_LEN16K,請參看25632。假設(shè)在掌握臺(tái)翻開之前,核就已經(jīng)調(diào)用printk,則明顯不能在掌握臺(tái)上正確打印消息,但是這些消息將被盡可能地存儲(chǔ)到log_buf中〔25656行。當(dāng)掌握臺(tái)翻開以后,緩存在log_buf25988log_buflog_startlog_size〔2565725646〕分別記錄當(dāng)前緩沖器的AN〔%LOG_BUF_LEN2中國25872:保存變量跟蹤記錄循環(huán)日志的值。明顯,日志大小會(huì)不斷增長,直至到達(dá)LOG_BUF_LEN的值為止。此后,log_sizelog_start25878:請留意logged_chars〔25658行〕記錄從機(jī)器啟動(dòng)之后由printk寫入的全部字符的長度,它在每log_startlog_size方式也是一樣。這實(shí)際上是一種優(yōu)化的時(shí)機(jī),本書將在完畢對函數(shù)的介紹之后再對它進(jìn)展具體爭論。25879:消息被分為假設(shè)干行,這固然要使用行標(biāo)志符來進(jìn)展分割。一旦核檢測到行標(biāo)志符,就寫入一個(gè)完整行,從而循環(huán)的執(zhí)行也可以提前終止。25884:在這里我們先不考慮部循環(huán)是否會(huì)提前退出,從msg到p的字符序列是特地供給應(yīng)掌握臺(tái)使用的〔這種字符序列我稱之為行,但是不要忘了,這里的行可能并不意味著行終止,由于buf或許還沒有終止。假設(shè)該行的日志等級(jí)高于系統(tǒng)掌握臺(tái)定義的日志等級(jí),而且當(dāng)前又有掌握臺(tái)可供打印,那么就能夠〔記住,printk〕假設(shè)在該消息塊中沒有覺察日志等級(jí)序列,并且在前面的printk調(diào)用中也沒有對msg_level賦值,那么本行中的msg_level。由于console_loglevel總不小于1〔除非root通過sysctl接口鎖定,于是總是可以打印這些行。25886:本行應(yīng)當(dāng)能夠被打印。printk通過遍歷翻開的掌握臺(tái)驅(qū)動(dòng)鏈表告知每一個(gè)掌握臺(tái)驅(qū)動(dòng)去打印當(dāng)前行設(shè)備驅(qū)動(dòng)在本書的爭論圍之外,因此,掌握臺(tái)驅(qū)動(dòng)代碼則并不包含在。25888:請留意這里消息文本的開頭使用的是msg而不是p,這樣就在沒有日志等級(jí)序列的狀況下寫入消息了。然而,日志等級(jí)序列已經(jīng)被存儲(chǔ)到log_buf緩沖器中了。這樣就使后來能夠訪問log_buf以獵取消息日志等級(jí)的代碼〔請參看25998行,不會(huì)再產(chǎn)生顯示混亂信息序列的現(xiàn)象。25892:假設(shè)層forbuf中的剩余字符〔假設(shè)有的話〕將被認(rèn)為是的消息,因此msg_levelbuf2589:釋放在25845行獵取的掌握臺(tái)鎖consoleloc。25896:喚醒等待被寫入掌握臺(tái)日志的全部進(jìn)程。留意即使沒有文本被實(shí)際寫入任何掌握臺(tái),這個(gè)過程也仍舊會(huì)發(fā)生。這樣處理是正確的,由于無論是否要往掌握臺(tái)中寫入文本,等待進(jìn)程實(shí)際上都是在等待從log_buf25748行,進(jìn)程被轉(zhuǎn)入休眠狀態(tài)以等待log_buf列中所使用的機(jī)制將在下一節(jié)中進(jìn)展?fàn)幷摗?5897:返回日志中寫入的字符長度。假設(shè)對于每個(gè)字符的處理工作都能削減一點(diǎn),那么從25869行開頭的for循環(huán)就執(zhí)行得更快一點(diǎn)。當(dāng)循環(huán)存在時(shí),我們可以通過只在循環(huán)退出時(shí)將logged_chars更一次來略微提高運(yùn)行速度。然而我們還可以通過其他努力來提高速度。由于我們可以預(yù)知消息的長度,因此log_size和log_start可以到最終再增長。讓我們來試驗(yàn)一下這樣能否提高速度,下面是一段經(jīng)過抱負(fù)優(yōu)化的代碼:中國log_buflog_sizelog_buf〔或者當(dāng)寫入需要換行時(shí)是兩次。這時(shí)速度確實(shí)提高了,但是有兩個(gè)緣由使我們并不能這樣做。首先,核可能有自己特有的memcpy函數(shù),我們必需確保對memcpy的調(diào)用不會(huì)再次進(jìn)入對printk的調(diào)用〔有一局部核移植版定義了自己特有的速度memcpymemecpyprintk失敗,那么就有可能觸發(fā)無限循環(huán)。然而在這一點(diǎn)上也并不是真的無藥可救。使用這種解決方案的最大問題在于該核循環(huán)的形式中也要留意行標(biāo)志符,因此使用memcpylog_buf中是不正確的:假設(shè)此處存在行,我們將無法對其進(jìn)展處理。我們可以試驗(yàn)一個(gè)一箭雙雕的方法。下面這種替代的嘗試雖然可能比前面那種初步解決方法速度要慢,但是它保持了核版本的語意:〔請留意gcc的優(yōu)化器格外靈敏,它足以能檢測到循環(huán)部的表達(dá)式log_buf+LOG_BUF_LEN并沒有轉(zhuǎn)變,因此在上面的循環(huán)中試圖手工加速計(jì)算是沒有任何效果的〕不幸的是,這種方法并不能比現(xiàn)在的核版本在速度上快很多,而且那樣會(huì)使得代碼晦澀難懂〔假設(shè)你編寫過更log_size和log_start的代碼,你就能清楚地了解這一點(diǎn)。你可以自己打算這種折衷是否值得。然而無論怎樣,我們學(xué)到了一些東西,通常,不管成功與否,改進(jìn)核代碼都可以加深你對核工作原理的理解。等待隊(duì)列前一節(jié)我們曾簡要的提到進(jìn)程〔也就是正在運(yùn)行的程序〕可以轉(zhuǎn)入休眠狀態(tài)以等待某個(gè)特定大事,當(dāng)該事件發(fā)生時(shí)這些進(jìn)程能夠被再次喚醒。核實(shí)現(xiàn)這一功能的技術(shù)要點(diǎn)是把等待隊(duì)列〔waitqueue〕和每一個(gè)大事聯(lián)系起來。需要等待大事的進(jìn)程在轉(zhuǎn)入休眠狀態(tài)后插入到隊(duì)列中。當(dāng)大事發(fā)生之后,核遍歷相應(yīng)隊(duì)列,喚醒休眠的任務(wù)讓它投入運(yùn)行狀態(tài)。任務(wù)負(fù)責(zé)將自己從等待隊(duì)列中去除。等待隊(duì)列的功能強(qiáng)大得令人吃驚,它們被廣泛應(yīng)用于整個(gè)核中。更重要的是,實(shí)現(xiàn)等待隊(duì)列的代碼量并不大。中國wait_queue18662:簡潔的數(shù)據(jù)構(gòu)造就是等待隊(duì)列節(jié)點(diǎn),它包含兩個(gè)元素:task—指向structtask_struct16325行開頭的structtask_struct7next—指向隊(duì)列中下一節(jié)點(diǎn)的指針。因而,等待隊(duì)列實(shí)際上是一個(gè)單鏈表。通常,我們用指向等待隊(duì)列隊(duì)首的指針來表示等待隊(duì)列。例如,printk使用的等待隊(duì)列l(wèi)og_wait〔25647行。wait_event16840:通過使用這個(gè)宏,核代碼能夠使當(dāng)前執(zhí)行的進(jìn)程在等待隊(duì)列wq中等待直至給定condition〔可能是任何的表達(dá)式〕得到滿足。16842:假設(shè)條件已經(jīng)為真,當(dāng)前進(jìn)程明顯也就無需等待了。1684wait_event來實(shí)現(xiàn)16824行,我們將在下一節(jié)介紹它。由于wait_eventwait_event用wait_queue,而不用通過宏來進(jìn)展冗余的〔特別是在這些狀況下〕測試,實(shí)際上也沒有代碼會(huì)真正這樣處理。更為重要的是,假設(shè)條件已經(jīng)為真,wait_event留意wait_event驚異的是,這個(gè)小技巧并沒有得到應(yīng)有的重視。這里的主要思路是使被封閉的代碼能夠像一個(gè)單句一樣使用??紤]下面這個(gè)宏,該宏的目的是假設(shè)pfree:除非你在如下所述的狀況下使用FREE1,否則全部調(diào)用都是正確有效的:FREE1else就和錯(cuò)誤的if〔FREE1的if〕聯(lián)系在一起。有些程序員通過如下途徑解決這種問題:這兩種方法都不盡人意,程序員在調(diào)用宏以后自然而然使用的分號(hào)會(huì)把擴(kuò)展信息弄亂。以FREE2為例,在宏開放之后,為了使編譯器能更準(zhǔn)確地識(shí)別,我們還需要進(jìn)展肯定的縮進(jìn)調(diào)整,最終代碼如下所示:中國這樣就會(huì)引起語法錯(cuò)誤—else和任何一個(gè)if都不匹配。FREE3從本質(zhì)上講也存在同樣的問題。而且在爭論問題產(chǎn)生緣由的同時(shí),就能夠明白為什么宏體里是否包含if是無關(guān)緊要的。不管宏體部容如何,只要使用一組括號(hào)來指定宏體,就會(huì)遇到一樣的問題。引入do/while(0)技巧能夠抑制前面所消滅的全部問題,現(xiàn)在我們可以編寫FREE4。將FREE4和其他宏一樣插入一樣代碼之后,這段代碼固然可以正確執(zhí)行。編譯器能夠優(yōu)化這個(gè)偽循環(huán),舍棄循環(huán)掌握,因此執(zhí)行代碼并沒有速度的損失,我們也從而得到了能夠?qū)崿F(xiàn)抱負(fù)功能的宏。雖然這是一個(gè)可以承受的解決方案,但是我們不能不提到的是編寫函數(shù)要比編寫宏好得多。不過假設(shè)你不能供給函數(shù)調(diào)用所需的開銷,那么就需要使用聯(lián)函數(shù)。這種狀況雖然在核中常常消滅,但是在其他地方就〔C++、gccISO標(biāo)準(zhǔn)C方案只是一種選擇,就是最終為C〕 wait_event中國16824:wait_eventwqcondition1682:通過調(diào)用add_wait_queu〔16791行waitwait是在堆棧wait中移走了,因此等待隊(duì)列中指向它的指針總是有效的。16830:重復(fù)安排CPU16831:進(jìn)程被置為TASK_UNINTERRUPTIBLE狀態(tài)〔16190行。這意味著進(jìn)程處于休眠狀態(tài),不應(yīng)被喚醒,即使是信號(hào)也不能打斷該進(jìn)程的休眠。信號(hào)在第6716832:假設(shè)條件已經(jīng)滿足,則可以退出循環(huán)。請留意假設(shè)在第一次循環(huán)時(shí)條件就已經(jīng)滿足,那么前面一行的賦值就鋪張了〔由于在循環(huán)完畢之后進(jìn)程狀態(tài)會(huì)馬上被再次賦值。 wait_event假定宏開頭執(zhí)行時(shí)條件還沒有得到滿足。而且,這種對進(jìn)程狀態(tài)變量state的延遲賦值也并沒有什么害處。在某些特別狀況下,這種方法還格外有益。例如wait_event開頭執(zhí)行時(shí)條件為假,但是在執(zhí)行到16832行時(shí)就為真了。這種變化只有在為有關(guān)進(jìn)程狀態(tài)的代碼計(jì)算condition變量值時(shí)才會(huì)消滅問題。但是在代碼中這種狀況我沒有覺察。16834schedule〔266867〕CPUCPU時(shí),對schedule1683TASK_RUNNING的狀態(tài)16188行其適合CPU16837:通過調(diào)用remove_wait_queue〔16814行〕將進(jìn)程從等待隊(duì)列中移去。wait_event_interruptible和wait_event_interruptible〔1686816847〕wait_eventwait_event,但不同的是它們允許休眠的進(jìn)程可以被信號(hào)中斷。信號(hào)將在第6請留意wait_event是被如下構(gòu)造所包含的。和do/while(0)技巧一樣,這樣可以使被封閉起來的代碼能夠像一個(gè)單元一樣運(yùn)行。這樣的封閉代碼就是一個(gè)獨(dú)立的表達(dá)式,而不是一個(gè)獨(dú)立的語句。也就是說,它可以求值以供其他更簡單的表達(dá)式使用。發(fā)生這種狀況的緣由主要在于一些不行移植的gcc特有代碼的存在。通過使用這類技巧,一個(gè)程序塊中的最終一個(gè)表達(dá)式的值將定義為整個(gè)程序塊的最終值。當(dāng)在表達(dá)式中使用wait_event_interruptible時(shí),執(zhí)行宏體后賦ret〔16873Lisp概念。但是假設(shè)你僅僅了解一點(diǎn)C中國 wake_up26829:該函數(shù)用來喚醒等待隊(duì)列中正在休眠的進(jìn)程。它由wake_upwake_up_interruptible調(diào)用〔請1661216614modemode可能被喚醒。2683310〔lock〕是用來限制對資源的訪問,這在SMP尤其重要,由于在這種狀況下當(dāng)一個(gè)CPU在修改某數(shù)據(jù)構(gòu)造時(shí),另一個(gè)CPU可能正在從該數(shù)據(jù)構(gòu)造中讀取數(shù)據(jù),或者也有可能兩個(gè)CPU同時(shí)對同一個(gè)數(shù)據(jù)構(gòu)造進(jìn)展修改,等等。在這種狀況下,受保護(hù)的資源明顯是等待隊(duì)列。格外好玩的是全部的等待隊(duì)列都使用同一個(gè)鎖來保護(hù)。雖然這種方法要比為每一個(gè)等待隊(duì)列定義一個(gè)鎖簡潔得多,但是這就意味著SMP規(guī)律單元可能常常會(huì)覺察自己正在等待一個(gè)實(shí)際上并不必需的鎖。26838:本段代碼遍歷非空隊(duì)列,為隊(duì)列中正確狀態(tài)的每一個(gè)進(jìn)程調(diào)用wake_up_process〔26356行。如前所述,進(jìn)程〔隊(duì)列節(jié)點(diǎn)〕在此可能并沒有從隊(duì)列中移走。這在很大程度上是由于即使隊(duì)列中的進(jìn)程正在被喚醒,它仍舊可能期望連續(xù)存在于等待隊(duì)列中,這一點(diǎn)正如我們在 wait_event中覺察的問題一樣。核模塊整個(gè)核并不需要同時(shí)裝入存。應(yīng)當(dāng)確認(rèn),為保證系統(tǒng)能夠正常運(yùn)行,一些特定的核必需總是駐留在存中,例如,進(jìn)程調(diào)度代碼就必需常駐存。但是核其他局部,例如大局部的設(shè)備驅(qū)動(dòng)就應(yīng)當(dāng)僅在核需要的時(shí)候才裝載,而在其他狀況下則無需占用存。舉例來說,只有在核真正和CD-ROM通訊時(shí)才需要使用完成核與CD-ROM一旦代碼不再需要,就可以從存中移走。系統(tǒng)運(yùn)行過程中可以增減的這局部核稱為核模塊。核模塊的優(yōu)點(diǎn)是可以簡化核自身的開發(fā)。假設(shè)你購置了一個(gè)的高速CD-ROM驅(qū)動(dòng)器,但是現(xiàn)有的CD-ROM驅(qū)動(dòng)程序并不支持該設(shè)備。你自然就期望增加對這種高速模式的支持以提高系統(tǒng)光驅(qū)設(shè)備的性能。假設(shè)作為核模塊來編譯驅(qū)動(dòng)程序,你的工作將會(huì)便利得多:編譯驅(qū)動(dòng)程序、加載到核、測試、卸載驅(qū)動(dòng)程序、修改驅(qū)動(dòng)程序、再次加載驅(qū)動(dòng)程序到核、測試,如此周而復(fù)始。假設(shè)你的驅(qū)動(dòng)程序是直接編輯在核中的,那么你就必需重編譯整個(gè)核并且在每次修改驅(qū)動(dòng)程序之后重啟動(dòng)機(jī)器。這樣慢得很多。否則,核將只能通過訪問磁盤來裝載處理磁盤訪問的核模塊,這是不行能實(shí)現(xiàn)的。這也是我們要選擇把部分核作為模塊編譯還是直接編譯進(jìn)核使其常駐存的又一個(gè)緣由。知道自己系統(tǒng)的設(shè)置方式,因而也就可以選擇正確使用的方式〔假設(shè)為了確保安全,可以簡潔的無視核模塊系統(tǒng)的優(yōu)點(diǎn),而把全部的容都編譯到核里面。核模塊會(huì)帶來一些速度上的損失,這是由于一些必需的代碼現(xiàn)在并不在RAM中,必需要從磁盤讀入。但是整個(gè)系統(tǒng)的性能通常會(huì)有所提高,這主要是由于通過丟棄臨時(shí)不使用的模塊可以釋放出額外的RAM供給用程序使用。假設(shè)這局部存被核所占用,應(yīng)用程序?qū)⒅荒芨宇l繁地進(jìn)展磁盤交換,而這種磁盤交換會(huì)顯著地降低應(yīng)用程序的性能〔磁盤交換將在第8。中國核模塊還會(huì)帶來因簡單度的增加所造成的開銷,這是由于在系統(tǒng)運(yùn)行的過程中,移進(jìn)移出局部核需要額外的代碼。然而,簡單度的開銷是可以治理的。通過使用外部程序來代理一些必需的工作還可以更進(jìn)一步降低簡單度的開銷〔更為精準(zhǔn)的說法是,這樣做不是削減了簡單度的開銷,而是把簡單度的開銷重安排了一下。這是對核模塊原理的一個(gè)小小的擴(kuò)展:即使是核的支持模塊,對于核來說也只是外部的、局部可用的,只有在需要的時(shí)候才被裝入存。通常用于這種目的程序稱為modprobe。有關(guān)的modprobe代碼超出了本書的圍,但是在Linux的每個(gè)發(fā)行版本中都包含有它。本節(jié)的剩余局部將爭論同modproberequest_module24432:作為函數(shù)說明之前的注釋,request_module是一個(gè)函數(shù)。核的其他模塊在需要裝載其他核模塊的時(shí)候,都必需調(diào)用這個(gè)函數(shù)。就像核處理其他工作一樣,這種調(diào)用也是為當(dāng)前運(yùn)行的進(jìn)程進(jìn)展的。從進(jìn)程的角度來看,這種調(diào)用的懇求通常是隱含的—正在執(zhí)行進(jìn)程其他懇求的核可能會(huì)覺察,必需調(diào)入一個(gè)模塊10070724446:以核中的一個(gè)獨(dú)立進(jìn)程的形式執(zhí)行exec_modprobe函數(shù)〔24384行。這并不能只通過函數(shù)的簡潔exec_modprobeexec來執(zhí)行一個(gè)程序。因此,對函數(shù)exec_modprobe調(diào)用將永久不會(huì)有返回。這和使用fork以預(yù)備execkernel_threadfork,雖然兩者有很大不同。fork是從指定函數(shù)開頭執(zhí)行的進(jìn)程,而不是從調(diào)用者的當(dāng)前位置開頭運(yùn)行。正如forkkernel_thread24448:和fork一樣,從kernel_thread24455:正如函數(shù)中論述的一樣,大局部的信號(hào)將因當(dāng)前進(jìn)程而被臨時(shí)堵塞。24462:等待exec_modprobe24465:完畢運(yùn)行,恢復(fù)信號(hào)。假設(shè)exec_modprobeexec_modprobe24384:exec_modprobe運(yùn)行為核增加核模塊的程序。這里的模塊名是一個(gè)void*的指針,而不是char*的指針。緣由簡潔說來就是kernel_thread產(chǎn)生的函數(shù)通常都使用void*指針參數(shù)。24386modprobemodprobe_path〔24363〕用來定位modprobe它可以通過核的sysctl11〔30388root態(tài)選擇不同于/sbin/modprobe的程序來運(yùn)行,以適應(yīng)當(dāng)modprobe被安裝到其他地方或者使用修改正的modprobe替換掉了原有的modprobe之類的狀況。24400〔正如代碼中描述的一樣〕出于安全性考慮,丟棄全部掛起的信號(hào)和信號(hào)句柄handl-er。這里最重要的局部是對flush_signal_handlers〔28041行義的信號(hào)句柄。假設(shè)在此時(shí)有信號(hào)被傳送到核,它將獲得默認(rèn)響應(yīng)—通常是無視信號(hào)或殺死進(jìn)程。但是不管怎樣都不會(huì)引起安全風(fēng)險(xiǎn)。由于該函數(shù)從觸發(fā)它的進(jìn)程中分別出來〔如前所述在此處是否轉(zhuǎn)變其原來安排的信號(hào),句柄都不會(huì)產(chǎn)生任何影響。中國24405:關(guān)閉調(diào)用進(jìn)程翻開的全部文件。最重要的是,這意味著modprobe程序不再從調(diào)用進(jìn)程中繼承標(biāo)準(zhǔn)輸入輸出和標(biāo)準(zhǔn)錯(cuò)誤。這很有可能會(huì)引起安全漏洞〔這可能是在替代modprobe的程序中引起的問題,但modprobe。24413:modproberootrootroot使用用戶ID0ID〔capabilitysystem,在接下來的幾行中會(huì)用到〕7中國24421:試圖執(zhí)行modprobeprintk打印錯(cuò)誤消息并返回錯(cuò)誤代碼。這里是可能產(chǎn)生printkmodule_name而言,它可能長達(dá)一百萬個(gè)字符。為防止printk緩沖器過載,你必需遍歷全部對于該函數(shù)的調(diào)用〔實(shí)際上是對request_module的調(diào)用,以保證每個(gè)調(diào)用者使用足夠短的、不會(huì)為printk24427:當(dāng)execve成功執(zhí)行時(shí),它不會(huì)返回任何結(jié)果,因此本處是不行能執(zhí)行到的。但是編譯器卻并不知道這一點(diǎn),因此,此處使用了returngcc對于核的進(jìn)一步爭論將超出本章的既定圍,因此在這個(gè)問題上我們到此為止。然而本書中也包括了其他必需的核代碼。在讀完第4章和第5章之后,或許你會(huì)期望再次認(rèn)真研讀一下這局部容。有關(guān)這個(gè)問題的兩個(gè)文件是include/linux/module.h〔從15529行開頭〕和/kernel/module.c〔從24476行開頭。和sys_create_module〔24586行、sys_init_module〔24637行、sys_delete_module〔24860行〕和sys_query_module〔25148〕四個(gè)函數(shù)需要特別留意一樣,structmodule〔15581〕也要特別引起留意modprobeinsmod、lsmodrmmod和卸載。核觸發(fā)直接回調(diào)核程序的現(xiàn)象看起來很令人驚異。但是,實(shí)際上進(jìn)展的工作不止于此。例如modprobe必需實(shí)際訪問磁盤以搜尋要裝載的模塊。而且更為重要的一點(diǎn)是,這種方法賜予root對核模塊系統(tǒng)更多的掌握力量。這主要是由于root也可以運(yùn)行modproberoot載模塊,也可以由核自動(dòng)完成。配置與編譯核你可能僅僅研讀、賞識(shí)而并不修改Linux核源代碼。但是,更普遍的狀況是,用戶有猛烈的愿望去改進(jìn)核代碼并完成相應(yīng)的測試,這樣我們就需要知道如何重建核。本節(jié)就是要告知你如何實(shí)現(xiàn)這一點(diǎn),而最終則歸結(jié)于如何把你所做的修改發(fā)行給別人,以使得每個(gè)人都能從你的工作中受益。配置核編譯核的第一步就是配置核,這是增加或者削減對核特性的支持及修改核的一些特性的必要步驟。例如,你可以要求核為自己的聲卡指定一個(gè)不同的DMA通道。假設(shè)核配置和你的需要一樣,那么你可以直接跳過本節(jié),否則請連續(xù)閱讀以下容。為了完成核的配置,請先切換到root用戶,然后轉(zhuǎn)入如下核源程序名目:cd/usr/src/linux接著敲入如下命令組:makeconfigmakemenuconfigmak
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 農(nóng)村出售地皮合同范本
- 出口定金合同范本
- 業(yè)務(wù)用車租賃合同范本
- 入股果園合同范例
- 第五單元第14課文藝復(fù)興運(yùn)動(dòng)2023-2024學(xué)年九年級(jí)上冊歷史同步教學(xué)設(shè)計(jì)(部編版)
- 專利實(shí)施使用合同范本
- epc項(xiàng)目銷售合同范本
- 2024年溫州龍港農(nóng)商銀行招聘筆試真題
- 借條合同范本范文
- 保安顧問合同范本
- 北師大版二年級(jí)數(shù)學(xué)下冊各單元測試卷
- 品管圈PDCA改善案例-降低住院患者跌倒發(fā)生率
- GB/T 12996-2024電動(dòng)輪椅車
- 成人氧氣吸入療法-中華護(hù)理學(xué)會(huì)團(tuán)體標(biāo)準(zhǔn)
- 西師版二年級(jí)數(shù)學(xué)下冊全冊課件【完整版】
- 環(huán)境空氣中臭氧的測定
- 第七章 化學(xué)物質(zhì)與酶的相互作用
- 機(jī)械畢業(yè)設(shè)計(jì)論文鋼筋自動(dòng)折彎機(jī)的結(jié)構(gòu)設(shè)計(jì)全套圖紙
- 總體施工進(jìn)度計(jì)劃橫道圖
- 教科版四年級(jí)科學(xué)下冊教學(xué)計(jì)劃及進(jìn)度表(兩篇)
- 擊實(shí)試驗(yàn)EXCEL自動(dòng)計(jì)算表
評(píng)論
0/150
提交評(píng)論