版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Linux內(nèi)核探秘深入解析文件系統(tǒng)和設(shè)備驅(qū)動(dòng)的架構(gòu)與設(shè)計(jì)目錄\h第1章內(nèi)核的基礎(chǔ)層和應(yīng)用層\h1.1內(nèi)核基礎(chǔ)層提供的服務(wù)\h1.1.1內(nèi)核中使用內(nèi)存\h1.1.2內(nèi)核中的任務(wù)調(diào)度\h1.1.3軟中斷和tasklet\h1.1.4工作隊(duì)列\(zhòng)h1.1.5自旋鎖\h1.1.6內(nèi)核信號(hào)量\h1.1.7原子變量\h1.2內(nèi)核基礎(chǔ)層的數(shù)據(jù)結(jié)構(gòu)\h1.2.1雙向鏈表\h1.2.2hash鏈表\h1.2.3單向鏈表\h1.2.4紅黑樹\h1.2.5radix樹\h1.3內(nèi)核應(yīng)用層\h1.4從Linux內(nèi)核源碼結(jié)構(gòu)縱覽內(nèi)核\h1.5內(nèi)核學(xué)習(xí)和應(yīng)用的四個(gè)階段\h1.6本章小結(jié)\h第2章文件系統(tǒng)\h2.1文件系統(tǒng)的基本概念\h2.1.1什么是VFS\h2.1.2超級(jí)塊super_block\h2.1.3目錄項(xiàng)dentry\h2.1.4索引節(jié)點(diǎn)inode\h2.1.5文件\h2.2文件系統(tǒng)的架構(gòu)\h2.2.1超級(jí)塊作用分析\h2.2.2dentry作用分析\h2.2.3inode作用分析\h2.2.4文件作用分析\h2.3從代碼層次深入分析文件系統(tǒng)\h2.3.1一個(gè)最簡(jiǎn)單的文件系統(tǒng)aufs\h2.3.2文件系統(tǒng)如何管理目錄和文件\h2.3.3文件系統(tǒng)的掛載過程\h2.3.4文件打開的代碼分析\h2.4本章小結(jié)\h第3章設(shè)備的概念和總體架構(gòu)\h3.1設(shè)備的配置表\h3.2訪問設(shè)備寄存器和設(shè)備內(nèi)存\h3.3設(shè)備中斷和DMA\h3.4總線對(duì)設(shè)備的掃描\h3.5設(shè)備驅(qū)動(dòng)管理\h3.6本章小結(jié)\h第4章為設(shè)備服務(wù)的特殊文件系統(tǒng)sysfs\h4.1文件和目錄的創(chuàng)建\h4.1.1sysfs文件系統(tǒng)的初始化\h4.1.2sysfs文件系統(tǒng)目錄的創(chuàng)建\h4.1.3普通文件的創(chuàng)建\h4.2sysfs文件的打開操作\h4.2.1real_lookup函數(shù)詳解\h4.2.2為文件創(chuàng)建inode結(jié)構(gòu)\h4.2.3為dentry結(jié)構(gòu)綁定屬性\h4.2.4調(diào)用文件系統(tǒng)中的open函數(shù)\h4.3sysfs文件的讀寫\h4.3.1讀文件的過程分析\h4.3.2寫文件的過程分析\h4.4kobject結(jié)構(gòu)\h4.4.1kobject和kset的關(guān)系\h4.4.2kobject實(shí)例:總線的注冊(cè)\h4.5本章小結(jié)\h第5章字符設(shè)備和input設(shè)備\h5.1文件如何變成設(shè)備\h5.1.1init_special_inode函數(shù)\h5.1.2def_chr_fops結(jié)構(gòu)\h5.2input設(shè)備的注冊(cè)\h5.2.1主從設(shè)備號(hào)\h5.2.2把input設(shè)備注冊(cè)到系統(tǒng)\h5.2.3設(shè)備區(qū)間的登記\h5.2.4注冊(cè)字符設(shè)備\h5.2.5打開input設(shè)備\h5.3input設(shè)備架構(gòu)\h5.3.1注冊(cè)input設(shè)備的驅(qū)動(dòng)\h5.3.2匹配input管理的設(shè)備和驅(qū)動(dòng)\h5.3.3注冊(cè)input設(shè)備\h5.4本章小結(jié)\h第6章platform總線\h6.1從驅(qū)動(dòng)發(fā)現(xiàn)設(shè)備的過程\h6.1.1驅(qū)動(dòng)的初始化\h6.1.2注冊(cè)驅(qū)動(dòng)\h6.1.3為總線增加一個(gè)驅(qū)動(dòng)\h6.1.4驅(qū)動(dòng)加載\h6.1.5遍歷總線上已經(jīng)掛載的設(shè)備\h6.2從設(shè)備找到驅(qū)動(dòng)的過程\h6.2.1注冊(cè)設(shè)備和總線類型\h6.2.2注冊(cè)設(shè)備的資源\h6.2.3增加一個(gè)設(shè)備對(duì)象\h6.3本章小結(jié)\h第7章serio總線\h7.1什么是總線適配器\h7.2向serio總線注冊(cè)設(shè)備\h7.2.1注冊(cè)端口登記事件\h7.2.2遍歷總線的驅(qū)動(dòng)\h7.2.3注冊(cè)input設(shè)備\h7.3虛擬鍵盤驅(qū)動(dòng)\h7.3.1鍵盤驅(qū)動(dòng)的初始化\h7.3.2與設(shè)備建立連接\h7.3.3啟動(dòng)鍵盤設(shè)備\h7.3.4輸入設(shè)備和主機(jī)系統(tǒng)之間的事件\h7.4鍵盤中斷\h7.4.1q40kbd設(shè)備的中斷處理\h7.4.2serio總線的中斷處理\h7.4.3驅(qū)動(dòng)提供的中斷處理\h7.5本章小結(jié)\h第8章PCI總線\h8.1深入理解PCI總線\h8.1.1PCI設(shè)備工作原理\h8.1.2PCI總線域\h8.1.3PCI資源管理\h8.1.4PCI配置空間讀取和設(shè)置\h8.2PCI設(shè)備掃描過程\h8.2.1掃描0號(hào)總線\h8.2.2掃描總線上的PCI設(shè)備\h8.2.3掃描多功能設(shè)備\h8.2.4掃描單個(gè)設(shè)備\h8.2.5掃描設(shè)備信息\h8.3本章小結(jié)\h第9章塊設(shè)備\h9.1塊設(shè)備的架構(gòu)\h9.1.1塊設(shè)備、磁盤對(duì)象和隊(duì)列\(zhòng)h9.1.2塊設(shè)備和通用磁盤對(duì)象的綁定\h9.1.3塊設(shè)備的隊(duì)列和隊(duì)列處理函數(shù)\h9.2塊設(shè)備創(chuàng)建的過程分析\h9.2.1nbd驅(qū)動(dòng)的初始化\h9.2.2為通用磁盤對(duì)象創(chuàng)建隊(duì)列成員\h9.2.3將通用磁盤對(duì)象加入系統(tǒng)\h9.3塊設(shè)備文件系統(tǒng)\h9.3.1塊設(shè)備文件系統(tǒng)的初始化\h9.3.2塊設(shè)備文件系統(tǒng)的設(shè)計(jì)思路\h9.4塊設(shè)備的打開流程\h9.4.1獲取塊設(shè)備對(duì)象\h9.4.2執(zhí)行塊設(shè)備的打開流程\h9.5本章小結(jié)\h第10章文件系統(tǒng)讀寫\h10.1pagecache機(jī)制\h10.1.1bufferI/O和directI/O\h10.1.2bufferhead和塊緩存\h10.1.3pagecache的管理\h10.1.4pagecache的狀態(tài)\h10.2文件預(yù)讀\h10.3文件鎖\h10.4文件讀過程代碼分析\h10.5讀過程返回\h10.6文件寫過程代碼分析\h10.7本章小結(jié)\h第11章通用塊層和scsi層\h11.1塊設(shè)備隊(duì)列\(zhòng)h11.1.1scsi塊設(shè)備隊(duì)列處理函數(shù)\h11.1.2電梯算法和對(duì)象\h11.2硬盤HBA抽象層\h11.3I/O的順序控制\h11.4I/O調(diào)度算法\h11.4.1noop調(diào)度算法\h11.4.2deadline調(diào)度算法\h11.5I/O的處理過程\h11.5.1I/O插入隊(duì)列的過程分析\h11.5.2I/O出隊(duì)列的過程分析\h11.5.3I/O返回路徑\h11.6本章小結(jié)\h第12章內(nèi)核回寫機(jī)制\h12.1內(nèi)核的觸發(fā)條件\h12.2內(nèi)核回寫控制參數(shù)\h12.3定時(shí)器觸發(fā)回寫\h12.3.1啟動(dòng)定時(shí)器\h12.3.2執(zhí)行回寫操作\h12.3.3檢查需要回寫的頁面\h12.3.4回寫超級(jí)塊內(nèi)的inode\h12.4平衡寫\h12.4.1檢查直接回寫的條件\h12.4.2回寫系統(tǒng)臟頁面的條件\h12.4.3檢查計(jì)算機(jī)模式\h12.5本章小結(jié)\h第13章一個(gè)真實(shí)文件系統(tǒng)ext2\h13.1ext2的硬盤布局\h13.2ext2文件系統(tǒng)目錄樹\h13.3ext2文件內(nèi)容管理\h13.4ext2文件系統(tǒng)讀寫\h13.5本章小結(jié)第1章內(nèi)核的基礎(chǔ)層和應(yīng)用層前言中提到,內(nèi)核分為內(nèi)核基礎(chǔ)層和內(nèi)核應(yīng)用層。這既有對(duì)整個(gè)操作系統(tǒng)軟件架構(gòu)的分析和理解,也有現(xiàn)實(shí)應(yīng)用情況的支持。操作系統(tǒng)對(duì)應(yīng)用軟件提供了統(tǒng)一的編程接口,操作系統(tǒng)的系統(tǒng)調(diào)用是穩(wěn)定的、向下兼容的,但是在內(nèi)核中,并不提供這種穩(wěn)定且兼容的保證。實(shí)際上,同樣的代碼在不同的內(nèi)核版本經(jīng)??赡芫幾g失敗。內(nèi)核的這種開發(fā)模式,造成了學(xué)習(xí)內(nèi)核時(shí)版本眾多而且不穩(wěn)定的特點(diǎn),也大大增加了學(xué)習(xí)的困難。在長(zhǎng)期對(duì)內(nèi)核代碼的分析和應(yīng)用中,筆者注意到一個(gè)事實(shí):內(nèi)核中提供了大量的軟件基礎(chǔ)設(shè)施。這些基礎(chǔ)設(shè)施既包括內(nèi)核中對(duì)內(nèi)存的使用,對(duì)進(jìn)程調(diào)度的控制,也包括自旋鎖、信號(hào)量等內(nèi)核提供的同步函數(shù),同時(shí)還包括內(nèi)核提供的數(shù)據(jù)結(jié)構(gòu),比如鏈表、hash鏈表、紅黑樹等。這些軟件基礎(chǔ)設(shè)施如同操作系統(tǒng)提供的系統(tǒng)調(diào)用一樣,是理解內(nèi)核代碼和編寫內(nèi)核代碼的基礎(chǔ)。而這些軟件基礎(chǔ)設(shè)施在各個(gè)內(nèi)核版本中基本是穩(wěn)定的?,F(xiàn)實(shí)情況提供了另一方面的支持。學(xué)習(xí)的動(dòng)力來自于應(yīng)用,傳統(tǒng)的操作系統(tǒng)教科書全面,但也很少有人能完全讀懂并且結(jié)合代碼進(jìn)行實(shí)戰(zhàn)應(yīng)用。大多數(shù)程序員在工作中應(yīng)用到內(nèi)核的部分,絕大多數(shù)是設(shè)備驅(qū)動(dòng),而講操作系統(tǒng)的書多數(shù)不會(huì)關(guān)注到設(shè)備驅(qū)動(dòng)層面。除了設(shè)備驅(qū)動(dòng)之外,內(nèi)核中文件系統(tǒng)也有較多的應(yīng)用。要做到快速流暢地閱讀內(nèi)核代碼,前提是了解內(nèi)核中的軟件基礎(chǔ)設(shè)施。這些知識(shí)使用范圍很廣,分布在內(nèi)核代碼的各個(gè)部分,如果不了解,在內(nèi)核代碼的理解上就容易出現(xiàn)障礙。1.1內(nèi)核基礎(chǔ)層提供的服務(wù)操作系統(tǒng)通常提供的服務(wù)是內(nèi)存管理、進(jìn)程管理、設(shè)備管理和文件系統(tǒng)。本書將內(nèi)存管理、進(jìn)程管理以及其他內(nèi)核提供的基礎(chǔ)軟件設(shè)施,比如工作隊(duì)列、tasklet以及信號(hào)量和自旋鎖都作為內(nèi)核的基礎(chǔ)層。本書并不分析和探討這些基礎(chǔ)層的原理和實(shí)現(xiàn),本章只介紹如何使用這些基礎(chǔ)軟件設(shè)施。1.1.1內(nèi)核中使用內(nèi)存簡(jiǎn)單說,內(nèi)核提供了兩個(gè)層次的內(nèi)存分配接口。一個(gè)是從伙伴系統(tǒng)分配,另一個(gè)是從slab系統(tǒng)分配?;锇橄到y(tǒng)是最底層的內(nèi)存管理機(jī)制,提供頁式的內(nèi)存管理,而slab是伙伴系統(tǒng)之上的內(nèi)存管理,提供基于對(duì)象的內(nèi)存管理。從伙伴系統(tǒng)分配內(nèi)存的調(diào)用是alloc_pages,注意此時(shí)得到的是頁面地址,如果要獲得能使用的內(nèi)存地址,還需要用page_address調(diào)用來獲得內(nèi)存地址。如果要直接獲得內(nèi)存地址,需要使用__get_free_pages。__get_free_pages其實(shí)封裝了alloc_pages和page_address兩個(gè)函數(shù)。alloc_pages申請(qǐng)的內(nèi)存是以頁為單元的,最少要一個(gè)頁。如果只是申請(qǐng)一小塊內(nèi)存,一個(gè)頁就浪費(fèi)了,而且內(nèi)核中很多應(yīng)用也希望一種對(duì)象化的內(nèi)存管理,希望內(nèi)存管理能自動(dòng)地構(gòu)造和析構(gòu)對(duì)象,這都很接近面向?qū)ο蟮乃悸妨?,這就是slab內(nèi)存管理。要從slab申請(qǐng)內(nèi)存,需要?jiǎng)?chuàng)建一個(gè)slab對(duì)象,使用kmem_cache_create創(chuàng)建slab對(duì)象。kmem_cache_create可以提供對(duì)象的名字和大小、構(gòu)造函數(shù)和析構(gòu)函數(shù)等,然后通過kmem_cache_alloc和kmem_cache_free來申請(qǐng)和釋放內(nèi)存。內(nèi)核中常用的kmalloc其實(shí)也是slab提供的對(duì)象管理,只不過內(nèi)核已經(jīng)構(gòu)建了一些固定大小的對(duì)象,用戶通過kmalloc申請(qǐng)的時(shí)候,就使用了這些對(duì)象。一個(gè)內(nèi)核中創(chuàng)建slab對(duì)象的例子如代碼清單1-1所示。代碼清單1-1創(chuàng)建slab對(duì)象bh_cachep=kmem_cache_create("buffer_head",sizeof(structbuffer_head),0,(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD),init_buffer_head,NULL);創(chuàng)建一個(gè)slab對(duì)象時(shí)指定了slab對(duì)象的大小,用以下代碼申請(qǐng)一個(gè)slab對(duì)象:structbuffer_head*ret=kmem_cache_alloc(bh_cachep,gfp_flags);內(nèi)核中還有一個(gè)內(nèi)存分配調(diào)用:vmalloc。vmalloc的作用是把物理地址不連續(xù)的內(nèi)存頁面拼湊為邏輯地址連續(xù)的內(nèi)存區(qū)間。理解了以上幾個(gè)函數(shù)調(diào)用之后,閱讀內(nèi)核代碼的時(shí)候就可以清晰內(nèi)核中對(duì)內(nèi)存的使用方式。1.1.2內(nèi)核中的任務(wù)調(diào)度內(nèi)核中經(jīng)常需要進(jìn)行進(jìn)程的調(diào)度。首先看一個(gè)例子,如代碼清單1-2所示。代碼清單1-2使用wait的任務(wù)調(diào)度#definewait_event(wq,condition)do{if(condition)break;__wait_event(wq,condition);}while(0)#define__wait_event(wq,condition)do{DEFINE_WAIT(__wait);for(;;){prepare_to_wait(&wq,&__wait,TASK_UNINTERRUPTIBLE);if(condition)break;schedule();}finish_wait(&wq,&__wait);}while(0)上文定義了一個(gè)wait結(jié)構(gòu),然后設(shè)置進(jìn)程睡眠。如果有其他進(jìn)程喚醒這個(gè)進(jìn)程后,判斷條件是否滿足,如果滿足,刪除wait對(duì)象,否則進(jìn)程繼續(xù)睡眠。這是一個(gè)很常見的例子,使用wait_event實(shí)現(xiàn)進(jìn)程調(diào)度的實(shí)例在內(nèi)核中很多,而且內(nèi)核中還實(shí)現(xiàn)了一系列函數(shù),簡(jiǎn)單介紹如下。?wait_event_timeout:和wait_event的區(qū)別是有時(shí)間限制,如果條件滿足,進(jìn)程恢復(fù)運(yùn)行,或者時(shí)間到達(dá),進(jìn)程同樣恢復(fù)運(yùn)行。?wait_event_interruptible:和wait_event類似,不同之處是進(jìn)程處于可中斷的睡眠。而wait_event設(shè)置進(jìn)程處于不可中斷的睡眠。兩者區(qū)別何在?可中斷的睡眠進(jìn)程可以接收到信號(hào),而不可中斷的睡眠進(jìn)程不能接收信號(hào)。?wait_event_interruptible_timeout:和wait_event_interruptible相比,多個(gè)時(shí)間限制。在規(guī)定的時(shí)間到達(dá)后,進(jìn)程恢復(fù)運(yùn)行。?wait_event_interruptible_exclusive:和wait_event_interruptible區(qū)別是排他性的等待。注意何謂排他性的等待?有一些進(jìn)程都在等待隊(duì)列中,當(dāng)喚醒的時(shí)候,內(nèi)核是喚醒所有的進(jìn)程。如果進(jìn)程設(shè)置了排他性等待的標(biāo)志,喚醒所有非排他性的進(jìn)程和一個(gè)排他性進(jìn)程。1.1.3軟中斷和taskletLinux內(nèi)核把對(duì)應(yīng)中斷的軟件執(zhí)行代碼分拆成兩部分。一部分代碼和硬件關(guān)系緊密,這部分代碼必須關(guān)閉中斷來執(zhí)行,以免被后面觸發(fā)的中斷打斷,影響代碼的正確執(zhí)行,這部分代碼放在中斷上下文中執(zhí)行。另一部分代碼和硬件關(guān)系不緊密,可以打開中斷執(zhí)行,這部分代碼放在軟中斷上下文執(zhí)行。需要指出的是,這種劃分是一種粗略、大概的劃分。中斷是計(jì)算機(jī)系統(tǒng)的寶貴資源,關(guān)閉中斷意味著系統(tǒng)不響應(yīng)中斷,這常常是代價(jià)高昂的。所以為了避免關(guān)閉中斷的不利影響,即使在中斷上下文中,也有很多代碼的執(zhí)行是打開中斷的。而在軟中斷上下文,甚至進(jìn)程上下文的內(nèi)核代碼中,有的時(shí)候也是需要關(guān)閉中斷的。哪些地方需要關(guān)閉中斷,而哪些地方又可以打開中斷,需要仔細(xì)地考慮,既要盡可能打開中斷以防止關(guān)閉中斷的不利影響,又要在需要的時(shí)候關(guān)閉中斷以避免出現(xiàn)錯(cuò)誤。Linux內(nèi)核定義了幾個(gè)默認(rèn)的軟中斷,網(wǎng)絡(luò)設(shè)備有自己的發(fā)送和接收軟中斷,塊設(shè)備也有自己的軟中斷。為了方便使用,內(nèi)核還定義了一個(gè)tasklet軟中斷。tasklet是一種特殊的軟中斷,同一時(shí)刻一個(gè)tasklet只能有一個(gè)CPU執(zhí)行,不同的tasklet可以在不同的CPU上執(zhí)行。這和軟中斷不同,軟中斷同一時(shí)刻可以在不同的CPU并行執(zhí)行,因此軟中斷必須考慮重入的問題。內(nèi)核中很多地方使用了tasklet。分析一個(gè)例子,代碼如下所示:DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet,hil_mlcs_process,0);tasklet_schedule(&hil_mlcs_tasklet);上面的例子首先定義了一個(gè)tasklet,它的執(zhí)行函數(shù)是hil_mlcs_process。當(dāng)程序中調(diào)用tasklet_schedule,會(huì)把要執(zhí)行的結(jié)構(gòu)插入到一個(gè)tasklet鏈表,然后觸發(fā)一個(gè)tasklet軟中斷。每個(gè)CPU都有自己的tasklet鏈表,內(nèi)核會(huì)根據(jù)情況確定在何時(shí)執(zhí)行tasklet??梢钥吹?,tasklet使用起來很簡(jiǎn)單,本節(jié)只需要了解在內(nèi)核如何使用即可。1.1.4工作隊(duì)列工作隊(duì)列和tasklet相似,都是一種延緩執(zhí)行的機(jī)制。不同之處是工作隊(duì)列有自己的進(jìn)程上下文,所以工作隊(duì)列可以睡眠,也可以被調(diào)度,而tasklet不可睡眠。代碼清單1-3是工作隊(duì)列的例子。代碼清單1-3工作隊(duì)列INIT_WORK(&ioc->sas_persist_task,mptsas_persist_clear_table,(void*)ioc);schedule_work(&ioc->sas_persist_task);使用工作隊(duì)列很簡(jiǎn)單,schedule_work把用戶定義的work_struct加入系統(tǒng)的隊(duì)列中,并喚醒系統(tǒng)線程去執(zhí)行。那么是哪個(gè)系統(tǒng)線程執(zhí)行用戶的work_struct呢?實(shí)際上,內(nèi)核初始化的時(shí)候,就要?jiǎng)?chuàng)建一個(gè)工作隊(duì)列keventd_wq,同時(shí)為這個(gè)工作隊(duì)列創(chuàng)建內(nèi)核線程(默認(rèn)是為每個(gè)CPU創(chuàng)建一個(gè)內(nèi)核線程)。內(nèi)核同時(shí)還提供了create_workqueue和create_singlethread_workqueue函數(shù),這樣用戶可以創(chuàng)建自己的工作隊(duì)列和執(zhí)行線程,而不用內(nèi)核提供的工作隊(duì)列。看內(nèi)核的例子:kblockd_workqueue=create_workqueue("kblockd");intkblockd_schedule_work(structwork_struct*work){returnqueue_work(kblockd_workqueue,work);}kblockd_workqueue是內(nèi)核通用塊層提供的工作隊(duì)列,需要由kblockd_workqueue執(zhí)行的工作就要調(diào)用kblockd_schedule_work,其實(shí)就是調(diào)用queue_work把work插入到kblockd_workqueued的任務(wù)鏈表。create_singlethread_workqueue和create_workqueue類似,不同之處是,像名字揭示的一樣,create_singlethread_workqueue只創(chuàng)建一個(gè)內(nèi)核線程,而不是為每個(gè)CPU創(chuàng)建一個(gè)內(nèi)核線程。1.1.5自旋鎖自旋鎖用來在多處理器的環(huán)境下保護(hù)數(shù)據(jù)。如果內(nèi)核發(fā)現(xiàn)數(shù)據(jù)未鎖,就獲取鎖并運(yùn)行;如果數(shù)據(jù)被鎖,就一直旋轉(zhuǎn)(其實(shí)是一直反復(fù)執(zhí)行一條指令)。之所以說自旋鎖用在多處理器環(huán)境,是因?yàn)樵趩翁幚砥鳝h(huán)境(非搶占式內(nèi)核)下,自旋鎖其實(shí)不起作用。在單處理器搶占式內(nèi)核的情況下,自旋鎖起到禁止搶占的作用。因?yàn)楸蛔孕i鎖著的進(jìn)程一直旋轉(zhuǎn),而不是睡眠,所以自旋鎖可以用在中斷等禁止睡眠的場(chǎng)景。自旋鎖的使用很簡(jiǎn)單,請(qǐng)參考下面的代碼例子。spin_lock(shost->host_lock);shost->host_busy++;spin_unlock(shost->host_lock);1.1.6內(nèi)核信號(hào)量?jī)?nèi)核信號(hào)量和自旋鎖類似,作用也是保護(hù)數(shù)據(jù)。不同之處是,進(jìn)程獲取內(nèi)核信號(hào)量的時(shí)候,如果不能獲取,則進(jìn)程進(jìn)入睡眠狀態(tài)。參考代碼如下:down(&dev->sem);up(&dev->sem);內(nèi)核信號(hào)量和自旋鎖很容易混淆,所以列出兩者的不同之處。?內(nèi)核信號(hào)量不能用在中斷處理函數(shù)和tasklet等不可睡眠的場(chǎng)景。?深層次的原因是Linux內(nèi)核以進(jìn)程為單位調(diào)度,如果在中斷上下文睡眠,中斷將不能被正確處理。?可睡眠的場(chǎng)景既可使用內(nèi)核信號(hào)量,也可使用自旋鎖。自旋鎖通常用在輕量級(jí)的鎖場(chǎng)景。即鎖的時(shí)間很短,馬上就釋放鎖的場(chǎng)景。1.1.7原子變量原子變量提供了一種原子的、不可中斷的操作。如下所示:atomic_tmapped;內(nèi)核提供了一系列的原子變量操作函數(shù),如下所示。?atomic_add:加一個(gè)整數(shù)到原子變量。?atomic_sub:從原子變量減一個(gè)整數(shù)。?atomic_set:設(shè)置原子變量的數(shù)值。?atomic_read:讀原子變量的數(shù)值。1.2內(nèi)核基礎(chǔ)層的數(shù)據(jù)結(jié)構(gòu)內(nèi)核使用的數(shù)據(jù)結(jié)構(gòu)有雙向鏈表、hash鏈表和單向鏈表,另外,紅黑樹和基樹(radix樹)也是內(nèi)核使用的數(shù)據(jù)結(jié)構(gòu)。實(shí)際上,這也是程序代碼中通常使用的數(shù)據(jù)結(jié)構(gòu)。container是Linux中很重要的一個(gè)概念,使用container能實(shí)現(xiàn)對(duì)象的封裝。代碼如下所示:#definecontainer_of(ptr,type,member)({consttypeof(((type*)0)->member)*__mptr=(ptr);(type*)((char*)__mptr-offsetof(type,member));})這個(gè)方法巧妙地實(shí)現(xiàn)了通過結(jié)構(gòu)的一個(gè)成員找到整個(gè)結(jié)構(gòu)的地址。內(nèi)核中大量使用了這個(gè)方法。1.2.1雙向鏈表list是雙向鏈表的一個(gè)抽象,它定義在/include/linux目錄下。首先看看list的結(jié)構(gòu)定義:structlist_head{structlist_head*next,*prev;};list庫提供的list_entry使用了container,通過container可以從list找到整個(gè)數(shù)據(jù)對(duì)象,這樣list就成為了一種通用的數(shù)據(jù)結(jié)構(gòu):#definelist_entry(ptr,type,member)container_of(ptr,type,member)內(nèi)核定義了很多對(duì)list結(jié)構(gòu)操作的內(nèi)聯(lián)函數(shù)和宏。?LIST_HEAD:定義并初始化一個(gè)list鏈表。?list_add_tail:加一個(gè)成員到鏈表尾。?list_del:刪除一個(gè)list成員。?list_empty:檢查鏈表是否為空。?list_for_each:遍歷鏈表。?list_for_each_safe:遍歷鏈表,和list_for_each的區(qū)別是可以刪除遍歷的成員。?list_for_each_entry:遍歷鏈表,通過container方法返回結(jié)構(gòu)指針。1.2.2hash鏈表hash鏈表和雙向鏈表list很相似,它適用于hash表。看一下hash鏈表的頭部定義:structhlist_head{structhlist_node*first;};和通常的list比較,hlist只有一個(gè)指針,這樣就節(jié)省了一個(gè)指針的內(nèi)存。如果hash表非常龐大,每個(gè)hash表頭節(jié)省一個(gè)指針,整個(gè)hash表節(jié)省的內(nèi)存就很可觀了。這就是內(nèi)核中專門定義hashlist的原因。hashlist庫提供的函數(shù)和list相似,具體如下。?HLIST_HEAD:定義并初始化一個(gè)hashlist鏈表頭。?hlist_add_head:加一個(gè)成員到hash鏈表頭。?hlist_del:刪除一個(gè)hash鏈表成員。?hlist_empty:檢查hash鏈表是否為空。?hlist_for_each:遍歷hash鏈表。?hlist_for_each_safe:遍歷hash鏈表,和hlist_for_each的區(qū)別是可以刪除遍歷的成員。?hlist_for_each_entry:遍歷hash鏈表,通過container方法返回結(jié)構(gòu)指針。1.2.3單向鏈表內(nèi)核中沒有提供單向鏈表的定義。但實(shí)際上,有多個(gè)地方使用了單向鏈表的概念,看代碼清單1-4的例子。代碼清單1-4單向鏈表for(i=0,p-=n;i<n;i++,p++,index++){structprobe**s=&domain->probes[index%255];while(*s&&(*s)->range<range)s=&(*s)->next;p->next=*s;*s=p;}上面的例子是字符設(shè)備的map表,probe結(jié)構(gòu)其實(shí)就是單向鏈表。這種結(jié)構(gòu)在內(nèi)核中應(yīng)用很廣泛。1.2.4紅黑樹紅黑樹是一種自平衡的二叉樹,代碼位于/lib/rbtree.c文件。實(shí)際上,紅黑樹可以看做折半查找。我們知道,排序的快速做法是取隊(duì)列的中間值比較,然后在剩下的隊(duì)列中再次取中間值比較,迭代進(jìn)行,直到找到最合適的數(shù)據(jù)。紅黑樹中的“紅黑”代表什么意思呢?紅黑的顏色處理是避免樹的不平衡。舉個(gè)例子,如果1、2、3、4、5五個(gè)數(shù)字依次插入一顆紅黑樹,那么五個(gè)值都落在樹的右側(cè),如果再將6插入這顆紅黑樹,要比較五次。為避免這種情況,“紅黑規(guī)則”就要將樹旋轉(zhuǎn),樹的根部要落在3這個(gè)節(jié)點(diǎn),這樣就避免了樹的不平衡導(dǎo)致的問題。內(nèi)核中的I/O調(diào)度算法和內(nèi)存管理中都使用了紅黑樹。紅黑樹也有很多介紹的文章,讀者可以結(jié)合代碼分析一下。1.2.5radix樹內(nèi)核提供了一個(gè)radix樹庫,代碼在/lib/radix-tree.c文件。radix樹是一種空間換時(shí)間的數(shù)據(jù)結(jié)構(gòu),通過空間的冗余減少了時(shí)間上的消耗。radix樹的形象圖如圖1-1所示。圖1-1radix樹的形象圖如圖1-1所示,元素空間總數(shù)為256,但元素個(gè)數(shù)不固定。如果用數(shù)組存儲(chǔ),好處是插入查找只用一次操作,但是存儲(chǔ)空間需要256,這是不可思議的。如果用鏈表存儲(chǔ),存儲(chǔ)空間節(jié)省了,但是極限情況下查找元素的次數(shù)等于元素的個(gè)數(shù)。而采用一顆高度為2的基樹,第一級(jí)最多16個(gè)冗余成員,代表元素前四位的索引,第二級(jí)代表元素后四位的索引。只要兩級(jí)查找就可以找到特定的元素,而且只有少量的冗余數(shù)據(jù)。圖中假設(shè)只有一個(gè)元素10001000,那么只有樹的第一級(jí)有元素,而且樹的第二級(jí)只有1000這個(gè)節(jié)點(diǎn)有數(shù)據(jù),其他節(jié)點(diǎn)都不必分配空間。這樣既可以快速定位查找,也減少了冗余數(shù)據(jù)。radix樹很適合稀疏的數(shù)據(jù),內(nèi)核中文件的頁緩存就采用了radix樹。關(guān)于radix樹的文章很多,讀者可以結(jié)合內(nèi)核radix樹的實(shí)現(xiàn)代碼分析一下。1.3內(nèi)核應(yīng)用層內(nèi)核應(yīng)用層是建立在基礎(chǔ)層之上的功能性系統(tǒng)。在本書中,內(nèi)核應(yīng)用層指的是文件系統(tǒng)、設(shè)備、驅(qū)動(dòng)以及網(wǎng)絡(luò)。內(nèi)核代碼雖然龐雜,但是核心的基礎(chǔ)層并不龐大,主要是應(yīng)用層占據(jù)了大部分代碼量。圖1-2展示了內(nèi)核各部分的代碼量統(tǒng)計(jì)數(shù)據(jù)。圖1-2內(nèi)核代碼的統(tǒng)計(jì)數(shù)據(jù)從圖1-2可以計(jì)算得出,驅(qū)動(dòng)、文件系統(tǒng)和網(wǎng)絡(luò)占據(jù)了內(nèi)核代碼的絕大部分,而代表基礎(chǔ)層的kernel和內(nèi)存管理實(shí)際上只有很少的代碼量。Architectures屬于內(nèi)核的基礎(chǔ)層,它是為適配不同的CPU結(jié)構(gòu)提供了不同的代碼,對(duì)某種CPU來說(如讀者最關(guān)注的x86CPU),實(shí)際的代碼量也大大減少了。1.4從Linux內(nèi)核源碼結(jié)構(gòu)縱覽內(nèi)核本節(jié)通過Linux內(nèi)核源碼的各個(gè)目錄來分析內(nèi)核代碼的數(shù)量和閱讀難度。如圖1-3所示。從圖1-3可以發(fā)現(xiàn),Architectures的子目錄是各個(gè)CPU架構(gòu)的名字,為各種不同的CPU架構(gòu)服務(wù)。雖然總體量很大,但是對(duì)讀者關(guān)注的x86或者ARM來說,也只占很小的一部分。圖1-4展示了內(nèi)核中drivers目錄的分類。drivers目錄分類為各種不同的設(shè)備驅(qū)動(dòng),而設(shè)備驅(qū)動(dòng)雖然五花八門,但是它們的結(jié)構(gòu)是高度相似的,讀者可以根據(jù)工作需要閱讀分析驅(qū)動(dòng)代碼。在理解設(shè)備驅(qū)動(dòng)架構(gòu)的基礎(chǔ)上,這個(gè)工作具有高度重復(fù)性,可以在短時(shí)間內(nèi)掌握驅(qū)動(dòng)的精髓。圖1-5展示了內(nèi)核中fs目錄的分類。fs目錄分類為各種不同的文件系統(tǒng)。圖1-3內(nèi)核中Architectures目錄的分類C(左)圖1-4內(nèi)核中drivers目錄的分類(中)圖1-5內(nèi)核中fs目錄的分類(右)Linux為文件系統(tǒng)統(tǒng)一提供了一個(gè)VFS架構(gòu),各種文件系統(tǒng)都要按照這個(gè)架構(gòu)來設(shè)計(jì)。因此,各種不同的文件系統(tǒng)都具有重復(fù)的部分,讀者不需要逐一分析所有的文件系統(tǒng)代碼,只選擇幾種文件系統(tǒng)重點(diǎn)閱讀即可。1.5內(nèi)核學(xué)習(xí)和應(yīng)用的四個(gè)階段如何深入學(xué)習(xí)內(nèi)核?或者更進(jìn)一步,如果把內(nèi)核知識(shí)應(yīng)用到具體的工作中,對(duì)工作產(chǎn)生價(jià)值?根據(jù)所有內(nèi)核核心開發(fā)人員的觀點(diǎn),閱讀代碼、理解代碼是最重要的步驟。對(duì)于操作系統(tǒng)這種龐大復(fù)雜的基礎(chǔ)軟件來說,只學(xué)習(xí)操作系統(tǒng)教程之類的書籍,遠(yuǎn)遠(yuǎn)不能達(dá)到理解和應(yīng)用的目的。這是所有工程實(shí)踐類學(xué)科的特點(diǎn),“只在岸上學(xué)習(xí)是永遠(yuǎn)不可能掌握游泳技巧的”。所以必須以代碼為依托,以代碼為依歸。其次,是如何選擇代碼的問題。內(nèi)核代碼非常龐大,如何在開始階段選擇代碼,既能覆蓋主要方面,同時(shí)又不至于難度太高,是分析和學(xué)習(xí)的主要問題。本書把內(nèi)核代碼分為了基礎(chǔ)層和應(yīng)用層,對(duì)基礎(chǔ)層突出內(nèi)核API的概念,在應(yīng)用層的分析中,希望通過突出重點(diǎn)架構(gòu)和選擇典型例子來加深理解。為了便于閱讀,筆者將簡(jiǎn)單的代碼注釋直接加在代碼清單里,需要重點(diǎn)解釋的部分,在正文加以說明。學(xué)以致用,任何學(xué)習(xí)的堅(jiān)實(shí)基礎(chǔ)都不只是單純的興趣,而是要在實(shí)踐中得到檢驗(yàn)。所以檢驗(yàn)學(xué)習(xí)效果,要看實(shí)際的應(yīng)用,而不能只是單方面地閱讀分析代碼。實(shí)際的應(yīng)用和學(xué)習(xí)過程,筆者認(rèn)為,可以粗略地分為四個(gè)階段。(1)起步階段結(jié)合中國(guó)當(dāng)前的應(yīng)用現(xiàn)狀,起步階段基本都是從驅(qū)動(dòng)入手。這一階段的表現(xiàn)是,實(shí)際做過幾個(gè)驅(qū)動(dòng),能夠移植驅(qū)動(dòng)到不同的系統(tǒng)平臺(tái),對(duì)驅(qū)動(dòng)能夠做一定的修改,能夠裁剪內(nèi)核,以適應(yīng)具體的需求;對(duì)Linux的bootloader能夠根據(jù)需求做修改。根據(jù)筆者對(duì)國(guó)內(nèi)現(xiàn)狀的了解和調(diào)查,大多數(shù)國(guó)內(nèi)的內(nèi)核應(yīng)用停留在這個(gè)層面,大多數(shù)內(nèi)核相關(guān)的工作也是在這個(gè)層面進(jìn)行。(2)熟練階段對(duì)內(nèi)核的一個(gè)或者幾個(gè)部分比較熟悉,針對(duì)熟悉部分,可以進(jìn)行深度的開發(fā)應(yīng)用。比如對(duì)設(shè)備驅(qū)動(dòng)相關(guān)的總線、設(shè)備、中斷比較熟悉,并且可以做深層次的開發(fā)。這一階段的特點(diǎn)是對(duì)內(nèi)核的理解還不夠全面,需要時(shí)間積累增加對(duì)內(nèi)核整體的把握。(3)高級(jí)階段對(duì)整個(gè)內(nèi)核的重要部分都進(jìn)行了比較深入的分析。這一階段的特點(diǎn)是全面性,即使要學(xué)習(xí)內(nèi)核某些新的重要特性,也能在短時(shí)間內(nèi)迅速掌握重點(diǎn)。(4)終極階段此階段是Linux內(nèi)核組維護(hù)人員所達(dá)到的水準(zhǔn),能做開創(chuàng)性的工作,具有重大的應(yīng)用價(jià)值。處于這個(gè)階段的主要是歐美的資深開發(fā)人員(或者說是內(nèi)核hacker),國(guó)內(nèi)達(dá)到這個(gè)水準(zhǔn)的技術(shù)人員非常少。1.6本章小結(jié)內(nèi)核基礎(chǔ)層是整個(gè)內(nèi)核學(xué)習(xí)的基礎(chǔ)?;A(chǔ)層的實(shí)現(xiàn)部分是比較復(fù)雜的,尤其是內(nèi)存管理基礎(chǔ)層和進(jìn)程調(diào)度基礎(chǔ)層。但是應(yīng)用這些基礎(chǔ)層并不復(fù)雜,它們的應(yīng)用接口API也比較穩(wěn)定,在各個(gè)內(nèi)核版本中沒有太大的變化,在內(nèi)核代碼中經(jīng)常會(huì)調(diào)用這些基礎(chǔ)層的接口API。讀者在閱讀內(nèi)核代碼的時(shí)候,可以回顧這部分的知識(shí),增強(qiáng)熟練運(yùn)用的能力。第2章文件系統(tǒng)本章從文件系統(tǒng)開始探索整個(gè)內(nèi)核應(yīng)用層。在本書的整個(gè)體系中,文件系統(tǒng)擔(dān)當(dāng)著最重要的作用,可以認(rèn)為,Linux內(nèi)核的應(yīng)用層就是以文件系統(tǒng)為核心而展開的。本書以文件系統(tǒng)作為整個(gè)內(nèi)核應(yīng)用層的核心,主要基于下列理由。?文件系統(tǒng)本身具有重大作用。國(guó)內(nèi)一些技術(shù)公司,已經(jīng)開始對(duì)文件系統(tǒng)的深度研究和應(yīng)用。這其中,既有通信公司,也有傳統(tǒng)IT公司和互聯(lián)網(wǎng)公司。當(dāng)前,分布式文件系統(tǒng)的廣泛應(yīng)用讓文件系統(tǒng)成為當(dāng)前內(nèi)核應(yīng)用的熱門。?文件系統(tǒng)在整個(gè)內(nèi)核架構(gòu)中具有基礎(chǔ)架構(gòu)性質(zhì)。字符設(shè)備、塊設(shè)備這些設(shè)備驅(qū)動(dòng)的概念都要依靠文件系統(tǒng)來實(shí)現(xiàn)。設(shè)備管理的基礎(chǔ)架構(gòu)也要依賴文件系統(tǒng)(sysfs)。而設(shè)備和驅(qū)動(dòng)是國(guó)內(nèi)當(dāng)前在內(nèi)核層面應(yīng)用最多的方面,也是國(guó)內(nèi)程序員在底層開發(fā)中應(yīng)用最多的方面。前言講到,書籍的作用只是帶領(lǐng)讀者入門,帶領(lǐng)入門的關(guān)鍵是提供一條學(xué)習(xí)分析的快捷路徑。根據(jù)筆者的經(jīng)驗(yàn),從架構(gòu)層面分析內(nèi)核,難點(diǎn)就是如何劃分代碼的層次。如何找到一個(gè)核心點(diǎn),然后從核心點(diǎn)擴(kuò)展,每一部分都自成單元,既具有普遍性,又能向外擴(kuò)展,是本文最大一個(gè)挑戰(zhàn)。如果沒有建立層次分明的架構(gòu)體系,內(nèi)核代碼就會(huì)顯得龐大混亂,難以梳理和學(xué)習(xí)。從文件系統(tǒng)入手,掌握基本概念和實(shí)現(xiàn)架構(gòu)后,可以從文件系統(tǒng)引出設(shè)備文件的概念,設(shè)備文件又可以引申到字符設(shè)備和塊設(shè)備,這樣就從文件系統(tǒng)過渡到設(shè)備管理。設(shè)備管理包含了設(shè)備驅(qū)動(dòng),設(shè)備驅(qū)動(dòng)要用到中斷,設(shè)備里面的塊設(shè)備又控制了通用塊層和I/O調(diào)度。而文件系統(tǒng)向外引申又和網(wǎng)絡(luò)的socket聯(lián)系。深入文件系統(tǒng)的代碼,可以了解到內(nèi)存的頁面管理。從文件系統(tǒng)出發(fā),層次推進(jìn)基本囊括了內(nèi)核應(yīng)用層的重要概念和架構(gòu)。這是作者總結(jié)的一種學(xué)習(xí)體系,希望通過這種體系串聯(lián)起內(nèi)核的知識(shí)點(diǎn)。2.1文件系統(tǒng)的基本概念在深入分析文件系統(tǒng)之前,有必要介紹文件系統(tǒng)的幾個(gè)基本概念,這將從架構(gòu)層次理解文件系統(tǒng)設(shè)計(jì)的目的,從而從全局層面理解內(nèi)核文件系統(tǒng)的代碼,大大減低分析代碼的難度和工作量。2.1.1什么是VFSLinux內(nèi)核通過虛擬文件系統(tǒng)(VirtualFileSystem,VFS)管理文件系統(tǒng)。VFS是Linux內(nèi)核文件系統(tǒng)的一個(gè)極其重要的基礎(chǔ)設(shè)施,VFS為所有的文件系統(tǒng)提供了統(tǒng)一的接口,對(duì)每個(gè)具體文件系統(tǒng)的訪問要通過VFS定義的接口來實(shí)現(xiàn)。同時(shí)VFS也是一個(gè)極其重要的架構(gòu),所有Linux的文件系統(tǒng)必須按照VFS定義的方式來實(shí)現(xiàn)。VFS本身只存在于內(nèi)存中,它需要將硬盤上的文件系統(tǒng)抽象到內(nèi)存中,這個(gè)工作是通過幾個(gè)重要的結(jié)構(gòu)實(shí)現(xiàn)的。VFS定義了幾個(gè)重要的結(jié)構(gòu):dentry、inode、super_block,通過這些結(jié)構(gòu)將一個(gè)真實(shí)的硬盤文件系統(tǒng)抽象到了內(nèi)存,從而通過管理dentry、inode這幾個(gè)對(duì)象就可以完成對(duì)文件系統(tǒng)的一些操作(當(dāng)然,在合適的時(shí)候,仍然需要將內(nèi)存的數(shù)據(jù)寫入到硬盤)。2.1.2超級(jí)塊super_block超級(jí)塊(super_block)代表了整個(gè)文件系統(tǒng)本身。通常,超級(jí)塊是對(duì)應(yīng)文件系統(tǒng)自身的控制塊結(jié)構(gòu)(可參考ext2文件系統(tǒng)的超級(jí)塊結(jié)構(gòu))。超級(jí)塊保存了文件系統(tǒng)設(shè)定的文件塊大小,超級(jí)塊的操作函數(shù),而文件系統(tǒng)內(nèi)所有的inode也都要鏈接到超級(jí)塊的鏈表頭。對(duì)于一個(gè)具體文件系統(tǒng)的控制塊可能還含有另外的信息,而通過超級(jí)塊對(duì)象,我們可以找到這些必要的信息。超級(jí)塊的內(nèi)容需要讀取具體文件系統(tǒng)在硬盤上的超級(jí)塊結(jié)構(gòu)獲得,所以超級(jí)塊是具體文件系統(tǒng)超級(jí)塊的內(nèi)存抽象。超級(jí)塊對(duì)象整個(gè)結(jié)構(gòu)很龐大復(fù)雜,作為學(xué)習(xí)的起步階段,沒必要探究每個(gè)成員的具體意義和使用目的(實(shí)際上,強(qiáng)記也很容易忘記)。代碼清單2-1是超級(jí)塊對(duì)象簡(jiǎn)化后的結(jié)構(gòu)定義。代碼清單2-1超級(jí)塊結(jié)構(gòu)簡(jiǎn)化定義structsuper_block{unsignedlongs_blocksize;unsignedchars_blocksize_bits;……/*省略超級(jí)塊的鏈表、設(shè)備號(hào)等代碼*/unsignedlonglongs_maxbytes;/*Maxfilesize*/structfile_system_type*s_type;structsuper_operations*s_op;unsignedlongs_magic;structdentry*s_root;structlist_heads_inodes;/*allinodes*/structlist_heads_dirty;/*dirtyinodes*/structblock_device*s_bdev;void*s_fs_info;/*Filesystemprivateinfo*/};從兩個(gè)方面了解超級(jí)塊結(jié)構(gòu)的作用。1)超級(jí)塊結(jié)構(gòu)給出了文件系統(tǒng)的全局信息。?s_blocksize指定了文件系統(tǒng)的塊大小。?s_maxbytes指定文件系統(tǒng)中最大文件的尺寸。?s_type是指向file_system_type結(jié)構(gòu)的指針。?s_magic是魔術(shù)數(shù)字,每個(gè)文件系統(tǒng)都有一個(gè)魔術(shù)數(shù)字。?s_root是指向文件系統(tǒng)根dentry的指針。超級(jí)塊對(duì)象還定義了一些鏈表頭,用來鏈接文件系統(tǒng)內(nèi)的重要成員。?s_inodes指向文件系統(tǒng)內(nèi)所有的inode,通過它可以遍歷inode對(duì)象。?s_dirty指向所有dirty的inode對(duì)象。?s_bdev指向文件系統(tǒng)存在的塊設(shè)備指針。在后面具體文件系統(tǒng)的例子中,可以看到這些成員是如何賦值和應(yīng)用的。2)超級(jí)塊結(jié)構(gòu)包含一些函數(shù)指針。super_operations提供了最重要的超級(jí)塊操作。例如super_operation的成員函數(shù)read_inode提供了讀取inode信息的功能。每個(gè)具體的文件系統(tǒng)一般都要提供這個(gè)函數(shù)來實(shí)現(xiàn)對(duì)inode信息的讀取,例如ext2文件系統(tǒng)提供的具體函數(shù)是ext2_read_inode。從這個(gè)例子,我們可以理解“VFS提供了架構(gòu),而具體文件系統(tǒng)必須按照VFS的架構(gòu)去實(shí)現(xiàn)”的含義。2.1.3目錄項(xiàng)dentry對(duì)于一個(gè)通常的文件系統(tǒng)來說,文件和目錄一般按樹狀結(jié)構(gòu)保存。直觀來看,目錄里保存著文件,而所有目錄一層層匯聚,最終到達(dá)根目錄。從這個(gè)樹狀結(jié)構(gòu),我們可以想象VFS應(yīng)該有數(shù)據(jù)結(jié)構(gòu)反映這種樹狀的結(jié)構(gòu)。實(shí)際上確實(shí)如此,目錄項(xiàng)(dentry)就是反映了文件系統(tǒng)的這種樹狀關(guān)系。在VFS里,目錄本身也是一個(gè)文件,只是有點(diǎn)特殊。每個(gè)文件都有一個(gè)dentry(可能不止一個(gè)),這個(gè)dentry鏈接到上級(jí)目錄的dentry。根目錄有一個(gè)dentry結(jié)構(gòu),而根目錄里的文件和目錄都鏈接到這個(gè)根dentry,二級(jí)目錄里的文件和目錄,同樣通過dentry鏈接到二級(jí)目錄。這樣一層層鏈接,就形成了一顆dentry樹。從樹頂可以遍歷整個(gè)文件系統(tǒng)的所有目錄和文件。為了加快對(duì)dentry的查找,內(nèi)核使用了hash表來緩存dentry,稱為dentrycache。dentrycache在后面的分析中經(jīng)常用到,因?yàn)閐entry的查找一般都先在dentrycache里進(jìn)行查找。dentry的結(jié)構(gòu)定義同樣很龐雜,和超級(jí)塊類似,我們當(dāng)前只分析最重要的部分。dentry結(jié)構(gòu)簡(jiǎn)化后的定義如代碼清單2-2所示。代碼清單2-2dentry結(jié)構(gòu)的簡(jiǎn)化定義structdentry{……/省略dentry鎖、標(biāo)志等代碼/structinode*d_inode;/*Wherethenamebelongsto-NULLisnegative*//**Thenextthreefieldsaretouchedby__d_lookup.Placethemhere*sotheyallfitinacacheline.*/structhlist_noded_hash;/*lookuphashlist*/structdentry*d_parent;/*parentdirectory*/structqstrd_name;/**d_childandd_rcucansharememory*/union{structlist_headd_child;/*childofparentlist*/structrcu_headd_rcu;}d_u;structlist_headd_subdirs;/*ourchildren*/structdentry_operations*d_op;structsuper_block*d_sb;/*Therootofthedentrytree*/intd_mounted;};對(duì)dentry結(jié)構(gòu)的成員解釋如下。?d_inode指向一個(gè)inode結(jié)構(gòu)。這個(gè)inode和dentry共同描述了一個(gè)普通文件或者目錄文件。?dentry結(jié)構(gòu)里有d_subdirs成員和d_child成員。d_subdirs是子項(xiàng)(子項(xiàng)可能是目錄,也可能是文件)的鏈表頭,所有的子項(xiàng)都要鏈接到這個(gè)鏈表。d_child是dentry自身的鏈表頭,需要鏈接到父dentry的d_subdirs成員。當(dāng)移動(dòng)文件的時(shí)候,需要把一個(gè)dentry結(jié)構(gòu)從舊的父dentry的鏈表上脫離,然后鏈接到新的父dentry的d_subdirs成員。這樣dentry結(jié)構(gòu)之間就構(gòu)成了一顆目錄樹?d_parent是指針,指向父dentry結(jié)構(gòu)。?d_hash是鏈接到dentrycache的hash鏈表。?d_name成員保存的是文件或者目錄的名字。打開一個(gè)文件的時(shí)候,根據(jù)這個(gè)成員和用戶輸入的名字比較來搜尋目標(biāo)文件。?d_mounted用來指示dentry是否是一個(gè)掛載點(diǎn)。如果是掛載點(diǎn),該成員不為零。2.1.4索引節(jié)點(diǎn)inodeinode代表一個(gè)文件。inode保存了文件的大小、創(chuàng)建時(shí)間、文件的塊大小等參數(shù),以及對(duì)文件的讀寫函數(shù)、文件的讀寫緩存等信息。一個(gè)真實(shí)的文件可以有多個(gè)dentry,因?yàn)橹赶蛭募穆窂娇梢杂卸鄠€(gè)(考慮文件的鏈接),而inode只有一個(gè)。根據(jù)上面的描述是否可以得出結(jié)論,即dentry和inode代表一個(gè)文件?事實(shí)基本如此,inode和dentry分別代表了文件通用的兩個(gè)部分,只不過對(duì)某些文件系統(tǒng)而言,inode提供的信息還不夠,還需要其他信息。這個(gè)將在后面具體的文件系統(tǒng)里面看到。inode的結(jié)構(gòu)定義如代碼清單2-3所示。因?yàn)閕node的定義非常龐大,在我們初次認(rèn)識(shí)inode結(jié)構(gòu)的時(shí)候,將inode結(jié)構(gòu)定義大大簡(jiǎn)化,以重點(diǎn)突出幾個(gè)結(jié)構(gòu)成員。代碼清單2-3inode的結(jié)構(gòu)定義structinode{structlist_headi_list;structlist_headi_sb_list;structlist_headi_dentry;unsignedlongi_ino;atomic_ti_count;loff_ti_size;unsignedinti_blkbits;structinode_operations*i_op;conststructfile_operations*i_fop;/*former->i_op->default_file_ops*/structaddress_space*i_mapping;structblock_device*i_bdev;……/*省略鎖等代碼*/};inode結(jié)構(gòu)非常復(fù)雜,同樣,我們只分析其中最重要的幾個(gè)成員,以簡(jiǎn)單理解inode最重要的作用。而其他的成員在后面的章節(jié)中繼續(xù)分析,逐漸豐富對(duì)inode的理解。?成員i_list、i_sb_list、i_dentry分別是三個(gè)鏈表頭。成員i_list用于鏈接描述inode當(dāng)前狀態(tài)的鏈表。成員i_sb_list用于鏈接到超級(jí)塊中的inode鏈表。當(dāng)創(chuàng)建一個(gè)新的inode的時(shí)候,成員i_list要鏈接到inode_in_use這個(gè)鏈表,表示inode處于使用狀態(tài),同時(shí)成員i_sb_list也要鏈接到文件系統(tǒng)超級(jí)塊的s_inodes鏈表頭。由于一個(gè)文件可以對(duì)應(yīng)多個(gè)dentry,這些dentry都要鏈接到成員i_dentry這個(gè)鏈表頭。?成員i_ino是inode的號(hào),而i_count是inode的引用計(jì)數(shù)。成員i_size是以字節(jié)為單位的文件長(zhǎng)度。?成員i_blkbits是文件塊的位數(shù)。?成員i_fop是一個(gè)structfile_operations類型的指針。文件的讀寫函數(shù)和異步io函數(shù)都在這個(gè)結(jié)構(gòu)中提供。每一個(gè)具體的文件系統(tǒng),基本都要提供各自的文件操作函數(shù)。?i_mapping是一個(gè)重要的成員。這個(gè)結(jié)構(gòu)目的是緩存文件的內(nèi)容,對(duì)文件的讀寫操作首先要在i_mapping包含的緩存里尋找文件的內(nèi)容。如果有緩存,對(duì)文件的讀就可以直接從緩存中獲得,而不用再去物理硬盤讀取,從而大大加速了文件的讀操作。寫操作也要首先訪問緩存,寫入到文件的緩存。然后等待合適的機(jī)會(huì),再?gòu)木彺鎸懭胗脖P。后面我們將分析文件的具體讀寫,在此處只需要理解基本的作用即可。?成員i_bdev是指向塊設(shè)備的指針。這個(gè)塊設(shè)備就是文件所在的文件系統(tǒng)所綁定的塊設(shè)備。2.1.5文件文件對(duì)象的作用是描述進(jìn)程和文件交互的關(guān)系。這里需要指出的是,硬盤上并不存在一個(gè)文件結(jié)構(gòu)。進(jìn)程打開一個(gè)文件,內(nèi)核就動(dòng)態(tài)創(chuàng)建一個(gè)文件對(duì)象。同一個(gè)文件,在不同的進(jìn)程中有不同的文件對(duì)象。文件的結(jié)構(gòu)定義如代碼清單2-4所示。代碼清單2-4文件的數(shù)據(jù)結(jié)構(gòu)structfile{structdentry*f_dentry;structvfsmount*f_vfsmnt;conststructfile_operations*f_op;……/*省略部分代碼*/loff_tf_pos;structfown_structf_owner;unsignedintf_uid,f_gid;structfile_ra_statef_ra;structaddress_space*f_mapping;};?f_dentry和f_vfsmnt分別指向文件對(duì)應(yīng)的dentry結(jié)構(gòu)和文件所屬于的文件系統(tǒng)的vfsmount對(duì)象。?成員f_pos表示進(jìn)程對(duì)文件操作的位置。例如對(duì)文件讀取前10字節(jié),f_pos就指示第11字節(jié)位置。?f_uid和f_gid分別表示文件的用戶ID和用戶組ID。?成員f_ra用于文件預(yù)讀的設(shè)置。在第10章將繼續(xù)詳細(xì)分析預(yù)讀的使用。?f_mapping指向一個(gè)address_space結(jié)構(gòu)。這個(gè)結(jié)構(gòu)封裝了文件的讀寫緩存頁面。2.2文件系統(tǒng)的架構(gòu)VFS是具體文件系統(tǒng)的抽象,而VFS又是依靠超級(jí)塊、inode、dentry以及文件這些結(jié)構(gòu)來發(fā)揮作用。所以文件系統(tǒng)的架構(gòu)就體現(xiàn)在這些結(jié)構(gòu)的使用方式中。2.2.1超級(jí)塊作用分析每個(gè)文件系統(tǒng)都有一個(gè)超級(jí)塊結(jié)構(gòu),每個(gè)超級(jí)塊都要鏈接到一個(gè)超級(jí)塊鏈表。而文件系統(tǒng)內(nèi)的每個(gè)文件在打開時(shí)都需要在內(nèi)存分配一個(gè)inode結(jié)構(gòu),這些inode結(jié)構(gòu)都要鏈接到超級(jí)塊。圖2-1展示了超級(jí)塊之間的關(guān)系以及超級(jí)塊和inode之間的鏈接關(guān)系。super_block1和super_block2是兩個(gè)文件系統(tǒng)的超級(jí)塊,它們被鏈接到super_blocks鏈表頭,后者使用的就是內(nèi)核基礎(chǔ)層介紹的雙向鏈表數(shù)據(jù)結(jié)構(gòu),順著super_blocks鏈表可以遍歷整個(gè)操作系統(tǒng)打開過的文件的inode結(jié)構(gòu)。圖2-1超級(jí)塊的結(jié)構(gòu)2.2.2dentry作用分析2.1.3節(jié)分析了VFS中dentry的數(shù)據(jù)結(jié)構(gòu)和作用,為了進(jìn)一步理解dentry,我們用圖2-2來解釋dentry的鏈接關(guān)系。圖2-2文件系統(tǒng)的目錄結(jié)構(gòu)如圖2-2所示,根目錄下有usr和home兩個(gè)目錄,usr目錄下有wj和nk兩個(gè)文件,home目錄下有個(gè)mnt目錄,這是另外一個(gè)文件系統(tǒng),掛載(mount)到當(dāng)前文件系統(tǒng)。在mnt目錄下有個(gè)cj文件。文件系統(tǒng)的dentry鏈表圖如圖2-3所示。這只是個(gè)示意圖,但是展示了幾個(gè)重要的概念。1)每個(gè)文件的dentry鏈接到父目錄的dentry,形成了文件系統(tǒng)的結(jié)構(gòu)樹。具體說,就是usr和home兩個(gè)dentry的d_child成員鏈接到根目錄dentry的d_subdirs成員。而wj和nk兩個(gè)dentry結(jié)構(gòu)鏈接到usr這個(gè)dentry。2)所有的dentry都指向一個(gè)dentry_hashtable。dentry_hashtable是個(gè)數(shù)組,它的數(shù)組成員是第1章介紹過的hash鏈表數(shù)據(jù)結(jié)構(gòu)。這里所說的dentry,指的是在內(nèi)存中的dentry。如果某個(gè)文件已經(jīng)被打開過,內(nèi)存中就應(yīng)該有該文件的dentry結(jié)構(gòu),并且該dentry被鏈接到dentry_hashtable數(shù)組的某個(gè)hash鏈表頭。后續(xù)再訪問該文件的時(shí)候,就可以直接從hash鏈表里面找到,避免了再次讀硬盤。這是dentry的cache概念。3)home目錄下的mnt目錄指向一個(gè)掛載的文件系統(tǒng)。如何判斷目錄不是一個(gè)普通的目錄,而是一個(gè)文件系統(tǒng)?這是dentry的d_mounted成員的功能。如果該成員不為0,代表該dentry是個(gè)掛載點(diǎn),有文件系統(tǒng)掛載,需要特殊處理。圖2-3dentry鏈表圖從圖2-3可以看到,掛載過來的文件系統(tǒng)本身也有一個(gè)dentry樹,也有自己的根目錄。兩個(gè)dentry樹之間并沒有鏈接關(guān)系。如何查找到掛載的文件系統(tǒng)哪?我們用圖2-4來解釋。圖2-4展示了一個(gè)新的數(shù)據(jù)結(jié)構(gòu)vfsmount。對(duì)這個(gè)數(shù)據(jù)結(jié)構(gòu)不做過多的探討,只需要知道每個(gè)文件系統(tǒng)都有這樣一個(gè)結(jié)構(gòu)。當(dāng)文件系統(tǒng)被掛載的時(shí)候,它的vfsmount結(jié)構(gòu)就被鏈接到內(nèi)核的一個(gè)全局鏈表—mount_hashtable數(shù)組鏈表。圖2-4掛載的鏈表圖mount_hashtable是個(gè)數(shù)組,它的每個(gè)成員都是一個(gè)hash鏈表。上文的例子有兩個(gè)vfsmount,cj文件所在文件系統(tǒng)的vfsmount被鏈接到mount_hashtable。這樣當(dāng)發(fā)現(xiàn)mnt目錄是個(gè)特殊的目錄時(shí),從mount_hashtable數(shù)組找到hash鏈表頭,再遍歷整個(gè)hash鏈表,就能找到cj文件所在文件系統(tǒng)的vfsmount,然后mnt目錄的dentry就被替換,置換為新文件系統(tǒng)的根目錄。具體過程參考打開文件的代碼分析。2.2.3inode作用分析系統(tǒng)內(nèi)核提供了一個(gè)hash鏈表數(shù)組inode_hashtable,所有的inode結(jié)構(gòu)都要鏈接到數(shù)組里面的某個(gè)hash鏈表。這種用法和前文介紹的hash鏈表數(shù)組dentry_hashtable的用法很類似,這里就不再分析了。在Linux系統(tǒng)里,字符設(shè)備和塊設(shè)備、普通文件和目錄、socket等都是一個(gè)文件,所以它們都有自己的inode結(jié)構(gòu)。為了辨別這些不同的類型,inode結(jié)構(gòu)的i_mode成員用不同的值代表不同的類型。如表2-1所示。表2-1i_mode代表的類型inode還有一個(gè)重要的作用是緩存文件的數(shù)據(jù)內(nèi)容。這是通過成員i_mapping實(shí)現(xiàn)的。i_mapping使用了數(shù)據(jù)結(jié)構(gòu)radix樹來保存文件的數(shù)據(jù)。這個(gè)數(shù)據(jù)結(jié)構(gòu)在1.2節(jié)已經(jīng)介紹過。在后面文件的讀寫過程分析中,還將繼續(xù)分析文件內(nèi)容讀寫的代碼。2.2.4文件作用分析文件對(duì)象代表進(jìn)程和具體文件交互的關(guān)系。內(nèi)核為每個(gè)打開的文件申請(qǐng)一個(gè)文件對(duì)象,同時(shí)返回文件的號(hào)碼。如圖2-5所示,每個(gè)進(jìn)程都指向一個(gè)文件描述符表。圖2-5進(jìn)程和文件描述符表這個(gè)表里面用數(shù)組保存了進(jìn)程中每個(gè)打開的文件。當(dāng)應(yīng)用進(jìn)程打開一個(gè)硬盤上的文件時(shí),內(nèi)核要為這個(gè)文件分配一個(gè)文件對(duì)象并保存文件指針到文件描述符表里面的數(shù)組。注意這里的“文件”指的是在硬盤上具體存在的文件,而“文件對(duì)象”是在內(nèi)存里存在的文件結(jié)構(gòu)。當(dāng)讀寫文件的時(shí)候,通過文件號(hào)就可以獲得文件的對(duì)象。這也就是讀寫文件必須先打開文件的原因,如果不執(zhí)行打開的過程,是不能完成文件讀寫的。2.3從代碼層次深入分析文件系統(tǒng)文件系統(tǒng)千頭萬緒,但對(duì)用戶來說,最重要的就是創(chuàng)建目錄、創(chuàng)建文件、打開文件和文件讀寫。對(duì)于通常的硬盤文件系統(tǒng)來說,這要涉及硬盤的讀寫和硬盤空間管理,而讀寫從文件系統(tǒng)層一直到通用塊設(shè)備層再到硬盤驅(qū)動(dòng),未免太過復(fù)雜。為了簡(jiǎn)化,我們給出一個(gè)最簡(jiǎn)單文件系統(tǒng),通過這個(gè)例子導(dǎo)入文件系統(tǒng)的基本概念。然后通過代碼分析,逐步深入內(nèi)核,了解一下博大精深的內(nèi)核架構(gòu)。提示本文假定讀者已經(jīng)了解模塊概念,以及如何編譯和安裝模塊。2.3.1一個(gè)最簡(jiǎn)單的文件系統(tǒng)aufs我們先寫一個(gè)最簡(jiǎn)單的文件系統(tǒng),這個(gè)文件系統(tǒng)直接創(chuàng)建在內(nèi)存中。它在內(nèi)存中創(chuàng)建了兩個(gè)目錄和幾個(gè)文件,用戶可以通過ls命令顯示目錄和文件,但是無法創(chuàng)建目錄和文件,也不能對(duì)文件進(jìn)行讀寫操作。這樣不涉及硬盤操作,大大簡(jiǎn)化了開始階段需要考慮的問題,這個(gè)例子如代碼清單2-5所示。代碼清單2-5最簡(jiǎn)單文件系統(tǒng)aufs源代碼#include<linux/module.h>#include<linux/fs.h>#include<linux/pagemap.h>#include<linux/mount.h>#include<linux/init.h>#include<linux/namei.h>#defineAUFS_MAGIC0x64668735staticstructvfsmount*aufs_mount;staticintaufs_mount_count;staticstructinode*aufs_get_inode(structsuper_block*sb,intmode,dev_tdev){structinode*inode=new_inode(sb);if(inode){inode->i_mode=mode;inode->i_uid=current->fsuid;inode->i_gid=current->fsgid;inode->i_blksize=PAGE_CACHE_SIZE;inode->i_blocks=0;inode->i_atime=inode->i_mtime=inode->i_ctime=CURRENT_TIME;switch(mode&S_IFMT){default:init_special_inode(inode,mode,dev);break;caseS_IFREG:printk("creatafile\n");break;caseS_IFDIR:inode->i_op=&simple_dir_inode_operations;inode->i_fop=&simple_dir_operations;printk("creatadirfile\n");inode->i_nlink++;break;}}returninode;}/*SMP-safe*/staticintaufs_mknod(structinode*dir,structdentry*dentry,intmode,dev_tdev){structinode*inode;interror=-EPERM;if(dentry->d_inode)return-EEXIST;inode=aufs_get_inode(dir->i_sb,mode,dev);if(inode){d_instantiate(dentry,inode);dget(dentry);error=0;}returnerror;}staticintaufs_mkdir(structinode*dir,structdentry*dentry,intmode){intres;res=aufs_mknod(dir,dentry,mode|S_IFDIR,0);if(!res)dir->i_nlink++;returnres;}staticintaufs_create(structinode*dir,structdentry*dentry,intmode){returnaufs_mknod(dir,dentry,mode|S_IFREG,0);}staticintaufs_fill_super(structsuper_block*sb,void*data,intsilent){staticstructtree_descrdebug_files[]={{""}};returnsimple_fill_super(sb,AUFS_MAGIC,debug_files);}staticstructsuper_block*aufs_get_sb(structfile_system_type*fs_type,intflags,constchar*dev_name,void*data){returnget_sb_single(fs_type,flags,data,aufs_fill_super);}staticstructfile_system_typeau_fs_type={.owner=THIS_MODULE,.name="aufs",.get_sb=aufs_get_sb,.kill_sb=kill_litter_super,};staticintaufs_create_by_name(constchar*name,mode_tmode,structdentry*parent,structdentry**dentry){interror=0;/*Iftheparentisnotspecified,wecreateitintheroot.*Weneedtherootdentrytodothis,whichisinthesuper*block.Apointertothatisinthestructvfsmountthatwe*havearound.*/if(!parent){if(aufs_mount&&aufs_mount->mnt_sb){parent=aufs_mount->mnt_sb->s_root;}}if(!parent){printk("Ah!cannotfindaparent!\n");return-EFAULT;}*dentry=NULL;mutex_lock(&parent->d_inode->i_mutex);*dentry=lookup_one_len(name,parent,strlen(name));if(!IS_ERR(dentry)){if((mode&S_IFMT)==S_IFDIR)error=aufs_mkdir(parent->d_inode,*dentry,mode);elseerror=aufs_create(parent->d_inode,*dentry,mode);}elseerror=PTR_ERR(dentry);mutex_unlock(&parent->d_inode->i_mutex);returnerror;}structdentry*aufs_create_file(constchar*name,mode_tmode,structdentry*parent,void*data,structfile_operations*fops){structdentry*dentry=NULL;interror;printk("aufs:creatingfile'%s'\n",name);error=aufs_create_by_name(name,mode,parent,&dentry);if(error){dentry=NULL;gotoexit;}if(dentry->d_inode){if(data)dentry->d_inode->u.generic_ip=data;if(fops)dentry->d_inode->i_fop=fops;}exit:returndentry;}structdentry*aufs_create_dir(constchar*name,structdentry*parent){returnaufs_create_file(name,S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO,parent,NULL,NULL);}staticint__initaufs_init(void){intretval;structdentry*pslot;retval=register_filesystem(&au_fs_type);if(!retval){aufs_mount=kern_mount(&au_fs_type);if(IS_ERR(aufs_mount)){printk(KERN_ERR"aufs:couldnotmount!\n");unregister_filesystem(&au_fs_type);returnretval;}}pslot=aufs_create_dir("womanstar",NULL);aufs_create_file("lbb",S_IFREG|S_IRUGO,pslot,NULL,NULL);aufs_create_file("fbb",S_IFREG|S_IRUGO,pslot,NULL,NULL);aufs_create_file("ljl",S_IFREG|S_IRUGO,pslot,NULL,NULL);pslot=aufs_create_dir("manstar",NULL);aufs_create_file("ldh",S_IFREG|S_IRUGO,pslot,NULL,NULL);aufs_create_file("lcw",S_IFREG|S_IRUGO,pslot,NULL,NULL);aufs_create_file("jw",S_IFREG|S_IRUGO,pslot,NULL,NULL);returnretval;}staticvoid__exitaufs_exit(void){simple_release_fs(&aufs_mount,&aufs_mount_count);unregister_filesystem(&au_fs_type);}module_init(aufs_init);module_exit(aufs_exit);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Thisisasimplemodule");MODULE_VERSION("Ver0.1");整個(gè)程序只有兩百多行,即使對(duì)文件系統(tǒng)一點(diǎn)不懂也能了解大概意思。這個(gè)例子不包括文件讀寫等內(nèi)容,目的只是說明文件系統(tǒng)內(nèi)dentry、inode、super_block等幾個(gè)重要概念。程序編譯后,我們通過insmod命令加載模塊,然后執(zhí)行如下操作。1)在根目錄下創(chuàng)建一個(gè)目錄:mkdirau2)掛載文件系統(tǒng):mount–taufsnone/au3)列出文件系統(tǒng)的內(nèi)容:ls看到了什么?可以發(fā)現(xiàn)"womanstar"和"manstar"兩個(gè)目錄。然后到womanstar目錄再執(zhí)行l(wèi)s,目錄下果然有l(wèi)bb、fbb、ljl三個(gè)文件。這就是我們?cè)诖a中希望做到的事情。2.3.2文件系統(tǒng)如何管理目錄和文件現(xiàn)在我們先看看這段程序究竟執(zhí)行了什么就創(chuàng)建了一個(gè)最簡(jiǎn)單文件系統(tǒng),然后再深入內(nèi)核分析究竟發(fā)生了什么。整個(gè)程序代碼可以分為三部分。1)首先是register_filesystem函數(shù),這個(gè)函數(shù)把a(bǔ)ufs文件系統(tǒng)登記到系統(tǒng);2)然后調(diào)用kern_mount函數(shù)為文件系統(tǒng)申請(qǐng)必備的數(shù)據(jù)結(jié)構(gòu);3)最后在aufs文件系統(tǒng)內(nèi)創(chuàng)建兩目錄,每個(gè)目錄下面創(chuàng)建三個(gè)文件。register_filesystem函數(shù)顧名思義,就是把一個(gè)特定的文件系統(tǒng)登記到內(nèi)核。這個(gè)函數(shù)的實(shí)現(xiàn)如代碼清單2-6所示。代碼清單2-6register_filesystem函數(shù)intregister_filesystem(structfile_system_type*fs){intres=0;structfile_system_type**p;if(!fs)return-EINVAL;if(fs->next)return-EBUSY;INIT_LIST_HEAD(&fs->fs_supers);write_lock(&file_systems_lock);p=find_filesystem(fs->name);if(*p)res=-EBUSY;else*p=fs;write_unlock(&file_systems_lock);returnres;}函數(shù)register_filesystem的參數(shù)是一個(gè)文件類型指針,函數(shù)執(zhí)行部分要在內(nèi)核尋找相同名字的文件系統(tǒng),如果不存在相同名字的文件系統(tǒng),就把a(bǔ)ufs加入到系統(tǒng)的文件系統(tǒng)鏈表。如果這個(gè)文件系統(tǒng)已經(jīng)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(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ǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年能源管理與企業(yè)節(jié)能策略
- 第2單元雙休必讀經(jīng)典書
- 2026年劇本殺運(yùn)營(yíng)公司質(zhì)量問題整改管理制度
- 2026年劇本殺運(yùn)營(yíng)公司員工跨部門培訓(xùn)管理制度
- 生成式人工智能在初中歷史課堂個(gè)性化教學(xué)中的應(yīng)用探討教學(xué)研究課題報(bào)告
- 高中生對(duì)基因編輯技術(shù)科學(xué)證據(jù)的批判性思維訓(xùn)練課題報(bào)告教學(xué)研究課題報(bào)告
- 護(hù)理部護(hù)理工作信息化建設(shè)匯報(bào)
- 健全消防安全制度
- 體育消費(fèi)券制度
- 會(huì)員管理制度
- DB3711∕T 129-2023 露天礦山生態(tài)修復(fù)驗(yàn)收規(guī)范
- 過年留人激勵(lì)方案
- 四川省德陽市第五中學(xué)2025-2026學(xué)年上學(xué)期八年級(jí)數(shù)學(xué)第一次月考試題(無答案)
- (英語)高一英語完形填空專題訓(xùn)練答案
- 公安副職競(jìng)聘考試題庫及答案
- 口腔診所勞務(wù)合同協(xié)議書
- 2025年度商鋪裝修工程總包與施工合同
- 門窗維修協(xié)議合同范本
- 子宮肌瘤課件超聲
- 2025年異丙醇行業(yè)當(dāng)前發(fā)展現(xiàn)狀及增長(zhǎng)策略研究報(bào)告
- 出租車頂燈設(shè)備管理辦法
評(píng)論
0/150
提交評(píng)論