Linux內(nèi)核源代碼漫游
藍(lán)森林
http://www. 2000年6月7日 09:42
作 者: 趙炯 Alessandro Rubini著, rubini@pop.systemy.it 趙炯 譯,gohigh@shtdu.edu.cn 本 章試圖以順序的方式來解釋Linux源代碼,以幫助讀者對(duì)源代碼的體系結(jié)構(gòu)以及很多相關(guān)的unix特性的實(shí)現(xiàn)有一個(gè)很好的理解。目標(biāo)是幫助對(duì)Linux不 甚了解的有經(jīng)驗(yàn)的C程序員對(duì)整個(gè)Linux的設(shè)計(jì)有所了解。這也就是為什么內(nèi)核漫游的入點(diǎn)選擇為內(nèi)核本身的啟始點(diǎn):系統(tǒng)引導(dǎo)(啟動(dòng))。 這份材料需要對(duì)C語言以及對(duì)Unix的概念和PC機(jī)的結(jié)構(gòu)有很好的了解,然而本章中并沒有出現(xiàn)任何的C代碼,而是直接參考(指向)實(shí)際的代碼的。有關(guān)內(nèi)核設(shè)計(jì)的最佳篇幅是在本手冊的其它章節(jié)中,而本章仍趨向于是一個(gè)非正式的概述。 本章中所參閱的任何文件的路徑名都是指主源代碼目錄樹,通常是/usr/src/linux。 這里所給出的大多數(shù)信息都是取之于Linux發(fā)行版1.0的源代碼。雖然如此,有時(shí)也會(huì)提供對(duì)后期版本的參考。這篇漫游中開頭有 圖標(biāo)的任何小節(jié)都是強(qiáng)調(diào)1.0版本后對(duì)內(nèi)核的新的改動(dòng)。如果沒有這樣的小節(jié)存在,則表示直到版本1.0.9-1.1.76,沒有作過改動(dòng)。 有時(shí)候本章中會(huì)有象這樣的小節(jié),這是指向正確的代碼以對(duì)剛討論過的主題取得更多信息的指示符。當(dāng)然,這里是指源代碼。 引導(dǎo)(啟動(dòng))系統(tǒng)
Linux的最最前面部分是用8086匯編語言編寫的(boot/bootsect.S),它將 由BIOS讀入到內(nèi)存0x7C00處,當(dāng)它被執(zhí)行時(shí)就會(huì)把自己移到絕對(duì)地址0x90000處,并將啟動(dòng)設(shè)備(boot/setup.S)的下2kB字節(jié)的 代碼讀入內(nèi)存0x90200處,而內(nèi)核的其它部分則被讀入到地址0x10000處。在系統(tǒng)加載期間將顯示信息"Loading..."。然后控制權(quán)將傳遞 給boot/Setup.S中的代碼,這是另一個(gè)實(shí)模式匯編語言程序。 啟動(dòng)部分識(shí)別主機(jī)的某些特性以及vga卡的類型。如果需要,它會(huì)要求用戶為控制臺(tái)選擇顯示模式。然后將整個(gè)系統(tǒng)從地址0x10000移至0x1000處,進(jìn)入保護(hù)模式并跳轉(zhuǎn)至系統(tǒng)的余下部分(在0x1000處)。 下 一步是內(nèi)核的解壓縮。0x1000處的代碼來自于zBoot/head.S,它初始化寄存器并調(diào)用decompress_kernel(),它們依次是由 zBoot/inflate.c、zBoot/unzip.c和zBoot/misc.c組成。被解壓的數(shù)據(jù)存放到了地址0x10000處(1兆),這也 是為什么Linux不能運(yùn)行于少于2兆內(nèi)存的主要原因。[在1兆內(nèi)存中解壓內(nèi)核的工作已經(jīng)完成,見 Memory Savers--ED] 將內(nèi)核封裝在一個(gè)gzip文件中的工作是由zBoot目錄中的Makefile以及工具完成的。它們是值得一看的有趣的文件。 內(nèi)核發(fā)行版1.1.75將boot和zBoot目錄下移到了arch/i386/boot中了,這個(gè)改動(dòng)意味著對(duì)不同的體系結(jié)構(gòu)允許真正的內(nèi)核建造,不過我將仍然只講解有關(guān)i386的信息。 解壓過的代碼是從地址0x10100處開始執(zhí)行的[這里我可能忘記了具體的物理地址了,因?yàn)槲覍?duì)相應(yīng)的代碼不是很熟],在那里,所有32比特的設(shè)置啟動(dòng)被完成: IDT、GDT以及LDT被加載,處理器和協(xié)處理器也已確認(rèn),分頁工作也設(shè)置好了;最終調(diào)用start_kernel子程序。上述操作的源代碼是在boot/head.S中的,這可能是整個(gè)內(nèi)核中最有訣竅的代碼了。 注意如果在前述任何一步中出了錯(cuò),計(jì)算機(jī)就會(huì)死鎖。在操作系統(tǒng)還沒有完全運(yùn)轉(zhuǎn)之前是處理不了出錯(cuò)的。 start_kernel()是位于init/main.c中的,并且沒有任何返回結(jié)果。從現(xiàn)在起的任何代碼都是用C語言編制的,除了中斷管理和系統(tǒng)調(diào)用的入/出代碼(當(dāng)然,還有大多數(shù)的宏都嵌入了匯編代碼)。 讓輪子轉(zhuǎn)動(dòng)起來 設(shè)置內(nèi)存邊界和調(diào)用paging_init(); 接著初始進(jìn)程(init process)嘗試著運(yùn)行/etc/init、/bin/init或者/sbin/init。 如果它們沒有一個(gè)運(yùn)行成功的,就會(huì)去執(zhí)行代碼“/bin/sh /etc/rc”并且在第一個(gè)終端上生成一個(gè)根命令解釋程序(root shell)。這段代碼回溯至Linux 0.01,當(dāng)時(shí)操作系統(tǒng)只有一個(gè)內(nèi)核,并且沒有登錄進(jìn)程。 在 從一個(gè)標(biāo)準(zhǔn)的地方(讓我們假定我們有)用exec()執(zhí)行了init初始化程序之后,內(nèi)核就對(duì)程序的執(zhí)行沒有了直接的控制。從現(xiàn)在起它的規(guī)則是提供對(duì)系統(tǒng) 調(diào)用的處理,以及為異步事件服務(wù)(比如硬件中斷等)。多任務(wù)的環(huán)境已經(jīng)建立,從現(xiàn)在起是init程序通過fork()派生出的系統(tǒng)進(jìn)程和登錄進(jìn)程來管理多 用戶的訪問了。 由于內(nèi)核是負(fù)責(zé)提供服務(wù)的,這個(gè)漫游文章將通過觀察這些服務(wù)(“系統(tǒng)調(diào)用”)以及通過提供基本數(shù)據(jù)結(jié)構(gòu)的原理和代碼的組織結(jié)構(gòu)繼續(xù)討論下去。 內(nèi)核是如何看見一個(gè)進(jìn)程的 而 進(jìn)程表以及各個(gè)內(nèi)存管理表和緩沖存儲(chǔ)器則是系統(tǒng)中最為重要的數(shù)據(jù)結(jié)構(gòu)。進(jìn)程表中的各個(gè)單項(xiàng)是task_struct結(jié)構(gòu),是定義在 include/linux/sched.h中的非常大的數(shù)據(jù)結(jié)構(gòu)。在task_struct中保留著從低層到高層的信息,范圍從某些硬件寄存器的拷貝到 進(jìn)程工作目錄的inode信息。 進(jìn)程表既是一個(gè)數(shù)組和雙鏈表,也是一個(gè)樹結(jié)構(gòu)。它的物理實(shí)現(xiàn)是一個(gè)靜態(tài)的指針數(shù)組,它的長度是定 義在include/linux/tasks.h中的常量NR_TASKS,并且每個(gè)結(jié)構(gòu)都位于一個(gè)保留內(nèi)存頁中。這個(gè)列表結(jié)構(gòu)是通過指針 next_task和pre_task構(gòu)成的,而樹結(jié)構(gòu)則是非常復(fù)雜的并且我們在此將不加以討論。你可能希望改動(dòng)NR_TASKS的默認(rèn)值128,但你要 保證所有源文件中相關(guān)的適當(dāng)文件都要被重新編譯過。 在啟動(dòng)引導(dǎo)過程結(jié)束后,內(nèi)核將總是代表某個(gè)進(jìn)程而工作,并且全局變量current --- 一個(gè)指向某個(gè)task_struct條目的指針 --- 被用于記錄正在運(yùn)行的進(jìn)程。current僅能通過在kernel/sched.c中的調(diào)度程序來改變。然而,由于所有的進(jìn)程都必須訪問它,所以使用了宏for_each_task。當(dāng)系統(tǒng)負(fù)荷很輕時(shí),它要比數(shù)組的順序掃描快得多。 進(jìn)程總是運(yùn)行于“用戶模式”或“內(nèi)核模式”。用戶程序的主體是運(yùn)行于用戶模式而其中的系統(tǒng)調(diào)用則運(yùn)行于內(nèi)核模式中。在這兩種執(zhí)行模式中進(jìn)程所用的堆棧是不一樣的 -- 常規(guī)的堆棧段用于用戶模式,而一個(gè)固定大小的堆棧(一頁,由該進(jìn)程所有)則用于內(nèi)核模式。內(nèi)核堆棧頁是從不交換出去的,因?yàn)槊慨?dāng)一個(gè)系統(tǒng)調(diào)用進(jìn)入時(shí)它就必須存在著。 內(nèi)核中的系統(tǒng)調(diào)用(system calls)是作為C語言函數(shù)存在的,它們的‘正規(guī)’名稱是以‘sys_’開頭的。例如一個(gè)名為burnout的系統(tǒng)調(diào)用將調(diào)用內(nèi)核函數(shù)sys_burnout()。 系統(tǒng)調(diào)用機(jī)制在本手冊的第三章中進(jìn)行了討論。觀看在include/linux/sched.h中的for_each_task和SET_LINKS能夠幫助理解進(jìn)程表中的列表和樹結(jié)構(gòu)。 創(chuàng)建和結(jié)束進(jìn)程
取得一個(gè)空閑內(nèi)存頁面來保存task_struct
從 一個(gè)進(jìn)程中退出是比較有竅門的,因?yàn)楦高M(jìn)程必須被通告有關(guān)任何子進(jìn)程的退出。而且,一個(gè)進(jìn)程可以由另外一個(gè)進(jìn)程使用kill()而退出(這些是Unix的 特性),所以除了sys_exit()之外,sys_kill()以及sys_wait()的各種特性也存在于exit.c之中了。 這里不對(duì)exit.c的代碼加以討論---因?yàn)樗稽c(diǎn)也不令人感興趣。為了以一致的狀態(tài)退出系統(tǒng),它涉及到許多細(xì)節(jié)。而POSIX標(biāo)準(zhǔn)對(duì)于信號(hào)則是要求相當(dāng)嚴(yán)格的,所以這里必須對(duì)其加以敘述。 執(zhí)行程序
新近的1.1內(nèi)核內(nèi)嵌了一個(gè)修訂的msdos文件系統(tǒng),它支持mmap()。而且linux_binfmt結(jié)構(gòu)已是一個(gè)鏈表而不是一個(gè)數(shù)組了,以允許以一個(gè)內(nèi)核模塊的方式加載一個(gè)新的二進(jìn)制格式。最后,結(jié)構(gòu)的本身也已經(jīng)被擴(kuò)展成能夠訪問與格式相關(guān)的核心轉(zhuǎn)儲(chǔ)程序了。 訪問文件系統(tǒng)
我 將假設(shè)讀者早已知道基本的Unix文件系統(tǒng)的原理--訪問(權(quán)限)許可、i節(jié)點(diǎn)(inode)、超級(jí)塊、加載(mount)和卸載(umount)文件系 統(tǒng)。這些概念在標(biāo)準(zhǔn)的Unix文獻(xiàn)中由比我聰明的作者給出了很好的解釋,所以我就不重復(fù)他們的工作并且我將只專注于有關(guān)Linux方面的問題。 早期的Unix通常只支持一個(gè)文件系統(tǒng)(fs)類型,它的代碼散布于整個(gè)內(nèi)核中,現(xiàn)今的實(shí)現(xiàn)是在 內(nèi)核和fs之間使用一個(gè)標(biāo)準(zhǔn)的接口,以便于在不同的體系結(jié)構(gòu)中進(jìn)行數(shù)據(jù)的交換。Linux本身提供了一個(gè)標(biāo)準(zhǔn)層以在內(nèi)核和每種fs模塊之間傳遞數(shù)據(jù)。這個(gè) 接口層稱為VFS,即“虛擬文件系統(tǒng)”("virtual filesystem")。 因而文件系統(tǒng)的代碼被分割成了兩層:上層是關(guān)于內(nèi)核表格的管理和數(shù)據(jù)結(jié)構(gòu)的,而低層是由與各文件系統(tǒng)相關(guān)的函數(shù)集構(gòu)成的,并且是由VFS數(shù)據(jù)結(jié)構(gòu)進(jìn)行調(diào)用的。 所有與文件系統(tǒng)獨(dú)立的資料都位于fs/*.c文件中。它們涉及如下的問題: 管理緩沖寄存器(buffer.c);
到實(shí)際文件系統(tǒng)的內(nèi)核入口點(diǎn)是數(shù)據(jù)結(jié)構(gòu)file_system_type。 file_system_types的一個(gè)數(shù)組包含在fs/filesystems.c中,并且每當(dāng)發(fā)出了一個(gè)加載(mount)命令時(shí)都會(huì)引用它。然 后,相應(yīng)fs類型的函數(shù)read_super就負(fù)責(zé)填寫結(jié)構(gòu)super_block的一個(gè)項(xiàng),而該項(xiàng)又內(nèi)嵌了結(jié)構(gòu)super_struct和結(jié)構(gòu) type_sb_info。前者為當(dāng)前的fs類型提供了指向一般fs操作的指針,而后者對(duì)相應(yīng)fs類型內(nèi)嵌了特定的信息。 文件系統(tǒng)類型數(shù)組已經(jīng)轉(zhuǎn)換成了一個(gè)鏈表,以允許用內(nèi)核模塊的形式加載新的fs類型。函數(shù)(un-)register_filesystem代碼包含在fs/super.c中。 一個(gè)文件系統(tǒng)類型的快速剖析
每一個(gè)fs類型除了它自己的源代碼目錄以外,是由下列各項(xiàng)組成的: file_systems[]數(shù)組中的一個(gè)條目(項(xiàng)) (fs/filesystems.c);
本手冊中有關(guān)procfs的章節(jié),揭示了所有有關(guān)那種fs類型的低層代碼和VFS接口。在閱讀過那個(gè)章節(jié)之后,fs/procfs中的源代碼就顯得非常容易理解了。 現(xiàn) 在我們來觀察VFS機(jī)制的內(nèi)部工作情況,并以minix文件系統(tǒng)的代碼作為一個(gè)實(shí)際例子。我選擇minix類型是因?yàn)樗容^短小但卻是完整的;而且, Linux中的所有其它的fs類型都衍生于它。在最近Linux安裝中的事實(shí)上的標(biāo)準(zhǔn)文件系統(tǒng)類型ext2,要比它復(fù)雜得多,對(duì)ext2這個(gè)文件系統(tǒng)的探 索就留給聰明的讀者作為一個(gè)練習(xí)了。 當(dāng)一個(gè)minix-fs被加載后,minix_read_super就會(huì)把從被加載的設(shè)備中讀取的數(shù)據(jù)添入super_block數(shù)據(jù)結(jié)構(gòu)中。此時(shí),該結(jié)構(gòu)中的s_op域?qū)⒈A粲幸粋€(gè)指向minix_sops的指針,該指針將被一般文件系統(tǒng)代碼用于分派超級(jí)塊的操作。 在全局系統(tǒng)樹結(jié)構(gòu)中鏈接新加載的fs依賴于下列各數(shù)據(jù)項(xiàng)(假設(shè)sb是超級(jí)塊數(shù)據(jù)結(jié)構(gòu),而dir_i是指向加載點(diǎn)的inode的指針): sb->s_mounted指向被加載文件系統(tǒng)的根目錄i節(jié)點(diǎn)(MINIX_ROOT_INO);
每 當(dāng)訪問一個(gè)文件時(shí),minix_read_inode就會(huì)開始執(zhí)行;它會(huì)使用minix_inode各字段中的數(shù)據(jù)填寫系統(tǒng)范圍的inode數(shù)據(jù)結(jié)構(gòu)。 inode->i_op字段是依照inode->i_mode來填寫的,它將負(fù)責(zé)該文件的任何其它操作。上述minix函數(shù)的代碼可以從 fs/minix/inode.c中找到。 inode_operations數(shù)據(jù)結(jié)構(gòu)是用于把inode操作分派給特定fs類型的內(nèi)核函 數(shù);該數(shù)據(jù)結(jié)構(gòu)的第一項(xiàng)是一個(gè)指向file_operations項(xiàng)的指針,它等同于數(shù)據(jù)管理的i_op。minix文件系統(tǒng)類型允許有inode操作集 中的三種方式(用于目錄、文件和符號(hào)鏈接)和文件操作集中的兩種(符號(hào)鏈接不需要文件操作)。 目錄操作(僅minix_readdir)位于fs/minix/dir.c中;文件操作(讀read和寫write)位于fs/minix/file.c中而符號(hào)操作(讀取并跟隨著鏈)位于fs/minix/symlink.c。 minix源代碼目錄中的其余部分用于實(shí)現(xiàn)以下任務(wù): bitmap.c用于管理i節(jié)點(diǎn)與塊的分配和釋放(而ext2文件系統(tǒng)卻有兩個(gè)不同的代碼文件);
控制臺(tái)的初始化是由tty_io.c中的tty_init()函數(shù)來執(zhí)行的。這個(gè)函數(shù)僅僅涉及取得每個(gè)設(shè)備集的主設(shè)備號(hào)并調(diào)用每個(gè)設(shè)備集的init函數(shù)。而con_init()則是與控制臺(tái)相關(guān)的函數(shù),并存在于console.c中。 在 內(nèi)核1.1的開發(fā)中,控制臺(tái)的初始化已經(jīng)有了很大的變化。console_init()已經(jīng)從tty_init()中脫離出來了,并且是由../.. /main.c直接調(diào)用的?,F(xiàn)在虛擬控制臺(tái)是動(dòng)態(tài)分配的,其代碼也已有了很大的變化。所以我將跳過初始化、分配等等的詳細(xì)討論。 文件操作是如何分派給控制臺(tái)的 毫無疑問,Unix設(shè)備是通過文件系統(tǒng)來訪問的。本節(jié)將詳細(xì)描述從設(shè)備文件到實(shí)際控制臺(tái)函數(shù)的所有步驟,而且,以下的信息是從內(nèi)核的1.1.73源代碼中抽取來的,它與1.0的代碼可能少許有點(diǎn)不同。 當(dāng) 打開一個(gè)設(shè)備i節(jié)點(diǎn)時(shí),在../../fs/devices.c中的chrdev_open()函數(shù)(或者是blkdev_open(),但我只專注于字 符設(shè)備)將被執(zhí)行。這個(gè)函數(shù)是通過數(shù)據(jù)結(jié)構(gòu)def_chr_fops取得的,而它又是被chrdev_inode_operations引用的,是被所有 文件系統(tǒng)類型使用的(見前面有關(guān)文件系統(tǒng)的部分)。 chrdev_open通過在當(dāng)前操作中替換具體設(shè)備的file_operations表并且調(diào) 用特定的open()函數(shù)來管理指定的設(shè)備操作的。具體設(shè)備的表結(jié)構(gòu)是保存在數(shù)組chrdevs[]中的,并由主設(shè)備號(hào)作為索引,位于同一個(gè)../.. /devices.c中。 如果該設(shè)備是一個(gè)tty類型的(我們不是只關(guān)注控制臺(tái)嗎?),我們就來討論tty的設(shè)備驅(qū)動(dòng)程 序,它們的函數(shù)在tty_io.c之中,由tty_fops作為索引。這樣,tty_open()就會(huì)調(diào)用init_dev(),而init_dev() 就會(huì)根據(jù)次設(shè)備號(hào)為設(shè)備分配任何所需的數(shù)據(jù)結(jié)構(gòu)。 次設(shè)備號(hào)也用于檢索已經(jīng)使用tty_register_driver()注冊登記過的設(shè)備的實(shí)際 驅(qū)動(dòng)程序。而且,該驅(qū)動(dòng)程序仍是另一個(gè)用于分派計(jì)算的數(shù)據(jù)結(jié)構(gòu),正如file_ops一樣;它是與設(shè)備的寫操作和控制有關(guān)的。最后一個(gè)用于管理tty的數(shù) 據(jù)結(jié)構(gòu)是線路規(guī)程,這將在后面敘述??刂婆_(tái)(以及任何其它的tty設(shè)備)的線路規(guī)程是由initialize_tty_struct()設(shè)置的,并由 init_dev調(diào)用的。 在這一節(jié)中我們所涉及的所有事情都是與設(shè)備無關(guān)的,僅有與特定控制臺(tái)相關(guān)的是console.c,在con_init()操作期間已經(jīng)注冊了自己的驅(qū)動(dòng)程序。相反,線路規(guī)程是與設(shè)備無關(guān)的。 The tty_driver 數(shù)據(jù)結(jié)構(gòu)在
上述信息是從1.1.73源代碼中取得的。它是有可能與你的內(nèi)核有所不同的(“如信息有所變動(dòng)將不另行通知”)。 控制臺(tái)寫操作
因而,con_write()主要是由轉(zhuǎn)換語句組成的,用于處理每一次一個(gè)字符的有限長狀態(tài)自動(dòng) 換碼序列的解釋。在正常方式下,所打印的字符是使用當(dāng)前屬性直接寫到顯示內(nèi)存中的。在console.c中,數(shù)據(jù)結(jié)構(gòu)vc的所有域使用宏都是可訪問的,所 以(例如)任何對(duì)attr的引用,只要currcons是所指的控制臺(tái)的號(hào)碼,確實(shí)是引證了數(shù)據(jù)結(jié)構(gòu)vc_cons[currcons]中的域。 實(shí)際上,新內(nèi)核中的vc_cons已不再是一個(gè)數(shù)據(jù)結(jié)構(gòu)數(shù)組了,現(xiàn)在它是指針的數(shù)組,其內(nèi)容是用kmalloc()操作的。宏的使用大大地簡化了代碼修改的工作,因?yàn)樵S多代碼都不需要被重寫。 控 制臺(tái)內(nèi)存到屏幕內(nèi)存的實(shí)際映射和非映射是由函數(shù)set_scrmem()(它把控制臺(tái)緩沖區(qū)中的數(shù)據(jù)拷貝到顯示內(nèi)存中)和get_srcmem()(它把 數(shù)據(jù)拷貝回控制臺(tái)緩沖區(qū)中)執(zhí)行的。為了減少數(shù)據(jù)傳輸?shù)拇螖?shù),當(dāng)前控制臺(tái)的私有緩沖區(qū)是物理地映射到實(shí)際顯示RAM上的。這意味著console.c中的 get-和set-_scrmem()是靜態(tài)的,并且僅在一個(gè)控制臺(tái)轉(zhuǎn)換期間才被調(diào)用。 控制臺(tái)讀操作
線路規(guī)程中的讀(read)函數(shù)稱為read_chan(),它讀取tty的緩沖區(qū)而不管數(shù)據(jù)是從哪里來的。原因是通過一個(gè)tty來到的字符是由異步硬件中斷管理的。 線路規(guī)程N(yùn)_TTY也同樣在tty_io.c中,盡管以后出的內(nèi)核都使用一個(gè)不同的n_tty.c源程序。 控制臺(tái)輸入的最底層是鍵盤管理的一部分,因此它是在keyboard.c的keyboard_interrupt()中處理的。 鍵盤管理
我將不對(duì)keyboard.c進(jìn)行深入討論,因?yàn)槠渲袥]有與內(nèi)核研究者有關(guān)的相關(guān)信息。 對(duì)于那些對(duì)Linux的鍵盤編程確實(shí)感興趣的人,最好的方法是從keyboard.c的最后一行往回看起。最底層的細(xì)節(jié)是在該文件的上半部分。 轉(zhuǎn)換當(dāng)前控制臺(tái)
實(shí) 際的轉(zhuǎn)換過程是分兩步來執(zhí)行的,函數(shù)complete_change_console()處理其中的第二部分。轉(zhuǎn)換的分裂意味著在一個(gè)與控制著我們正在離 開的tty的進(jìn)程的可能的握手以后完成任務(wù)。如果控制臺(tái)不在進(jìn)程控制之下,change_console()就會(huì)自己調(diào)用 complete_change_console()。進(jìn)程需要足夠的能力來成功地完成從圖形到文本控制臺(tái)或從文本到圖形控制臺(tái)的轉(zhuǎn)換,并且X服務(wù)器(例 如)是其圖形控制臺(tái)的控制進(jìn)程。 選擇機(jī)制
選擇是一個(gè)非常低級(jí)的功能,因而它工作是任何其它內(nèi)核活動(dòng)所看不見的。這意味著許多的#ifdef只是屏幕在以任何方式作修改之前簡單地移動(dòng)加亮部分。 新內(nèi)核特性改善了選擇的代碼,鼠標(biāo)指針的加亮可以與被選擇的文本獨(dú)立(內(nèi)核1.1.23或更高)。而且,從1.1.73版起,被選擇的文本使用了動(dòng)態(tài)的緩沖區(qū)而不是靜態(tài)的了,使得內(nèi)核小了4KB。 使用ioctl()操作設(shè)備
控制臺(tái)設(shè)備的ioctl資料是位于vt.c中的,因?yàn)榭刂婆_(tái)驅(qū)動(dòng)程序要將ioctl請(qǐng)求分派給vt_ioctl()。 上述信息是關(guān)于內(nèi)核1.1.7x的。1.0內(nèi)核是沒有“驅(qū)動(dòng)程序”表的,而且vt_ioctl()是直接由file_operations()表指向的。 Ioctl的資料確實(shí)是相當(dāng)讓人混淆的。有些請(qǐng)求是與設(shè)備相關(guān)的,而有些卻是與線路規(guī)程相關(guān)的。我將試圖對(duì)1.0和1.1.7x內(nèi)核之間發(fā)生的任何事概要總結(jié)一下。 1.1.7x 系列內(nèi)核有如下的特性:tty_ioctl.c只實(shí)現(xiàn)了線路規(guī)程請(qǐng)求(也就是n_tty_ioctl(),這是唯一在n_tty.c外面的n_tty函 數(shù)),而file_operations字段指向tty_io.c中的tty_ioctl()。如果請(qǐng)求號(hào)沒有被tty_ioctl()解析出來,它就會(huì) 被傳到tty->driver.ioctl或者,如果它失敗時(shí),就到tty->ldisc.ioctl??刂婆_(tái)的與驅(qū)動(dòng)程序相關(guān)的資料可以從 vt.c中找到,而線路規(guī)程方面的資料則在tty_ioctl.c中。 在1.0內(nèi)核中,tty_ioctl()是在tty_ioctl.c中的并有一般tty的file_operations所指向。未被解析出的請(qǐng)求將用與1.1.7x相似的方法被傳送到特定的ioctl函數(shù)或到線路規(guī)程代碼去。 注 意,在這兩種情況中,TIOCLINUX請(qǐng)求是在與設(shè)備無關(guān)的代碼中的,這暗示著控制臺(tái)選擇操作可以通過ioctl對(duì)任何tty進(jìn)行操作來設(shè)置 (set_selection()總是在控制臺(tái)前臺(tái)上操作的),而這是一個(gè)安全上的漏洞。這也是轉(zhuǎn)移到一個(gè)更新的內(nèi)核的很好理由,在新內(nèi)核中,通過僅允許 超級(jí)用戶來處理選擇彌補(bǔ)了這個(gè)漏洞。 有很多請(qǐng)求可以被發(fā)給控制臺(tái)設(shè)備,而知道它們的最好方法是瀏覽源程序文件vt.c。
|
|