?? (ldd) ch12-加載塊設備驅動程序(轉載).htm
字號:
<TD width="8%" height=4>
<DIV align=center><A href="mailto:joyfire@sina.com"><FONT
color=#ffffff>聯系</FONT></A></DIV></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<TABLE borderColor=#666666 cellPadding=2 width="90%" align=center border=2>
<TBODY>
<TR>
<TD bgColor=#000000>
<P align=center><A
href="http://211.71.69.201/joyfire/lsdp/index.htm"><FONT color=#ffffff
size=2>目錄頁</FONT></A> | <A
href="http://211.71.69.201/joyfire/lsdp/14.htm"><FONT color=#ffffff
size=2>上一頁</FONT></A> | <A
href="http://211.71.69.201/joyfire/lsdp/16.htm"><FONT color=#ffffff
size=2>下一頁</FONT></A></P>
<P align=center><FONT face=黑體 color=#ffffff size=6>(LDD)
Ch12-加載塊設備驅動程序(轉載) </FONT></P><SPAN
style="LINE-HEIGHT: 1; LETTER-SPACING: 0pt"><FONT color=#ffffff size=3>
<P>發信人: Altmayer (alt), 信區: GNULinux<BR>標 題: (LDD) Ch12-加載塊設備驅動程序(轉載)<BR>發信站: 飲水思源 (2001年12月13日08:57:39 星期四), 站內信件<BR> <BR>【 以下文字轉載自 <FONT
color=#00ff00>UNIXpost </FONT>討論區 】<BR>【 原文由<FONT
color=#00ff00> altmayer.bbs@bbs.nju.edu.cn,</FONT> 所發表 】<BR> <BR>【 以下文字轉載自 <FONT
color=#00ff00>altmayer </FONT>的信箱 】<BR> <BR>第十二章 加載塊設備驅動程序<BR> <BR> <BR> <BR>正如在第一章“Linux核心簡介”中“設備與模塊的分類”中所概述的一樣,Unix的設備<BR>驅動程序并不僅限于字符設備。本章就來介紹一下第二大類的設備驅動程序——塊設備<BR>驅動程序。所謂面向塊的設備是指數據傳輸是以塊為單位的(例如軟盤和硬盤),這里<BR>硬件的塊一般被稱作“扇區(Sector)”。而名詞“塊”常用來指軟件上的概念:驅動<BR>程序常常使用1KB大小的塊,即使扇區大小為512字節。<BR> <BR>在這一章,我們將來構造一個全特征的塊設備驅動程序sbull(Simple Block Utility<BR>for Loading Localities)。這個驅動程序與scull類似,也是使用計算機的內存作為硬<BR>件設備。換句話說,它是一個RAM-disk的驅動程序。sbull可以在任何Linux計算機上執<BR>行(不過我只在有限的幾個平臺上作過測試)。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>行(不過我只在有限的幾個平臺上作過測試)。<BR> <BR>注冊驅動程序<BR> <BR>和字符設備驅動程序類似,核心里的塊設備驅動程序也是由一個主設備號來標識。用來<BR>對其進行注冊和取消注冊的函數是:<BR> <BR>int register_blkdev(unsigned int major, const char*name, struct<BR>file_operations *fops)<BR> <BR>int unregister_blkdev(unsigned int major, const char*name);<BR> <BR>參數的含義與字符設備驅動程序一樣,對主設備號的動態賦值也類似。因此,一個sbull<BR>設備與scull一樣將自己注冊:<BR> <BR>result=register_blkdev(sbull_major,“sbull”,$sbull_fops);<BR> <BR>if(result<0){<BR> <BR> printk(KERN_WARNING“sbull:can’t get major %d\n”,sbull_major);<BR> <BR> return result;<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR> }<BR> <BR>if (sbull_major==0) sbull_major=result; /*dynamic*/<BR> <BR>major=sbull_major; /*Use “major”later on to save<BR>typing*/<BR> <BR>register_blkdev 的fops參數與我們在字符設備驅動程序中使用的類似,為read,write<BR>以及fsync的操作并不要求針對某個驅動程序。通用函數block_read, block_write及blo<BR>ck_fsync被用來代替任何針對某個驅動程序的函數。另外,check_media_change和reval<BR>idate對塊設備驅動程序也有意義,二者都在sbull_fops中定義。<BR> <BR>在sbull中使用的fops結構如下:<BR> <BR>(代碼236)<BR> <BR>通用的讀寫操作被用來獲得較高的性能。通過數據緩沖獲得加速,這在字符設備驅動程<BR>序重中是沒有的。塊設備驅動程序可以被緩沖是因為它們的數據服從于計算機的文件層<BR>次結構,任何應用程序都無法直接訪問,而字符設備驅動程序則不是這樣。<BR> <BR>不過,當緩沖的高速緩存不能滿足一個讀請求或當一個待處理的寫操作要刷新到物理磁<BR>盤上時,驅動程序必須被調用來進行真正的數據傳送。fops結構除了read和write外,并<BR></P></FONT><FONT
color=#ffffff size=3>
<P>盤上時,驅動程序必須被調用來進行真正的數據傳送。fops結構除了read和write外,并<BR>不帶有入口點,因此,必須要一個額外的結構blk_dev_struct來發出對實際數據傳送的<BR>請求。<BR> <BR>這個結構在<linux/blkdev.h>定義,它有幾個域,但只有第一個域需被驅動程序設置。<BR>下面是這個結構在核心2.0中的定義。<BR> <BR>(代碼237)<BR> <BR>當核心需要為sbull設備產生一個I/O操作時,它便調用函數blk_dev[sbull_major].requ<BR>est_fn。因此這個模塊的初始化函數須設置這個域使其指向它自己的請求函數。這個結<BR>構中的其它域只供核心函數或宏進行內部使用;你不必在你的代碼段中顯式地使用它們<BR>。<BR> <BR>一個塊設備驅動程序模塊與核心的關系見圖12-1。<BR> <BR>除了blk_dev還有幾個數組帶有塊設備驅動程序的信息。這些數組一般由主設備號(有時<BR>也用次設備號)進行索引。它們在drivers/block/ll_rw_block.c中被聲明和描述。<BR> <BR>int blk_size[][];<BR> <BR>這個數組由主設備號和次設備號索引。它以KB為單位描述了每個設備的大小。如果blk_s<BR>ize[major]是NULL,則不對這個設備的大小進行檢查(也就是說,核心可能要求數據傳<BR></P></FONT><FONT
color=#ffffff size=3>
<P>ize[major]是NULL,則不對這個設備的大小進行檢查(也就是說,核心可能要求數據傳<BR>送通過end_of_device)。<BR> <BR>int blksize_size[][];<BR> <BR>被每個設備所使用的塊的大小,以字節為單位。與上一個數組類似,這個二維數組也是<BR>由主設備號和次設備號索引。如果blksize_size[major]是一個空指針,那么便假設其塊<BR>大小為BLOCK_SIZE(目前是1KB)。塊大小必須是2的冪,因為核心使用移位操作將偏移<BR>量轉換為塊號。<BR> <BR>int hardsect_size[][];<BR> <BR>與其它的一樣,這個數據結構也是由主設備號和次設備號索引。硬件扇區的缺省大小為5<BR>12字節。直到包括2.0.X版本為止,可變扇區大小仍未真正支持,因為一些核心代碼仍舊<BR>假設扇區大小為半KB。不過很可能在2.2版本中會真正實現可變扇區大小。<BR> <BR>int read_ahead[];<BR> <BR>這個數組由主設備號索引,它定義了一個文件被順序讀取時,核心可以提前讀取多少扇<BR>區。在進程請求數據之前將其讀出可以改善系統的性能及總的吞吐率。慢速設備最好指<BR>定一個較大的提前讀的值,而一個快速設備則可以在較小的提前讀的值下工作的很好。<BR>這個提前讀的值越大緩沖高速緩存則需要越多的內存。每個主設備號有一個提前讀的值<BR>,它對所有次設備號有效。這個值可以通過驅動程序的ioctl方法來改變;硬盤驅動程序<BR></P></FONT><FONT
color=#ffffff size=3>
<P>,它對所有次設備號有效。這個值可以通過驅動程序的ioctl方法來改變;硬盤驅動程序<BR>一般設為8個扇區,對應著4KB。<BR> <BR>sbull設備允許在加載時設置這些值,它們作用于示例驅動程序的所有次設備號。在sbul<BR>l中變量名和它們的缺省值為:<BR> <BR>size=2048(KB)<BR> <BR>由sbull生成的每個ramdisk占兩兆字節,正如系統的缺省值。<BR> <BR>hardsect=512(B)<BR> <BR>sbull扇區大小是常用的半KB值。改變hardsect的值是不允許的。<BR> <BR>如前所述,其它的扇區大小并不被支持。如果你一定要改它,可以將sbull/sbull.c中的<BR>安全檢查去掉。不過請做好發生嚴重的內存崩潰的危險的準備。除非在你嘗試時,已經<BR>加上了對可變扇區大小的支持。<BR> <BR>rahead=2(扇區)<BR> <BR>因為ramdisk是一個快速設備,所以這個缺省提前讀的值比較小。<BR> <BR>sbull設備也允許你選擇一個設備個數進行安裝。devs是設備個數,缺省設為2,表明缺<BR></P></FONT><FONT
color=#ffffff size=3>
<P>sbull設備也允許你選擇一個設備個數進行安裝。devs是設備個數,缺省設為2,表明缺<BR>省內存使用量為4兆——2個大小為2MB的盤。<BR> <BR>sbull設備的init_module的實現如下(不含主設備號的注冊和錯誤恢復):<BR> <BR>(代碼239)<BR> <BR>相應的清除函數如下所示:<BR> <BR>(代碼240)<BR> <BR>這里,調用fsync_dev是必須的,用以清除核心保存在不同高速緩存中的對設備的所有引<BR>用。事實上,fsync_dev是運行在block--_fsync之后的引擎,它是塊設備的fsync“方法<BR>”。<BR> <BR>頭文件 blk.h<BR> <BR>由于塊設備驅動程序的絕大部分是設備無關的,核心的開發者通過把大部分相同的代碼放<BR>在一個頭文件<linux/blk.h>中,來試圖簡化驅動程序的代碼。因此,每個塊設備驅動程<BR>序都必須包含這個頭文件,在<linux/blk.h>中定義的最重要的函數是end_request,它<BR>被聲明為static(靜態)的。讓它成為靜態的,使得不同驅動程序可有一個正確定義的e<BR>nd_request,而不需要每個都寫自己的實現。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>在Linux1.2中,這個頭文件應該用<linux/../../drivers/block/blk.h>來包含。原因在<BR>于當時還不支持自定義的塊設備驅動程序,而這個頭文件最初位于drivers/block源碼目<BR>錄下。<BR> <BR>實際上,blk.h相當不尋常,比如它定義了幾個基于符號MAJOR_NR的符號,而MAJOR_NR必<BR>須由驅動程序在它包含這個頭文件之前聲明。這里,我們再次看到blk.h在設計時并沒有<BR>真正考慮自定義驅動程序。<BR> <BR>看看blk.h,你會發現幾個設備相關的符號是按照MAJOR_NR的值聲明的,也就是說MAJOR_<BR>NR應該提前知道。然而,如果主設備號是動態賦值的,驅動程序無法預知其值,因此也<BR>就不能正確定義MAJOR_NR。如果MAJOR_NR未定義,blk.h就不能設定一些在end_request<BR>中使用的宏。因此,為了讓自定義驅動程序從通用的end_request函數受益,從而避免重<BR>新實現它,驅動程序必須在包含blk.h之前定義MAJOR_NR和其它幾個符號。<BR> <BR>下面的列表描述了一些必須提前定義的<linux/blk.h>中的符號。列表結尾給出了sbull<BR>中使用的代碼。<BR> <BR>MAJOR_NR<BR> <BR>這個符號用來訪問一些數組,特別是blk-_dev和blksize-_size。自定義驅動程序(如sb<BR>ull)不能給這個符號賦一個常量值,可以將其定義(#define)為一個存有主設備號的<BR>變量。對sbull而言,它是sbull-_major。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>變量。對sbull而言,它是sbull-_major。<BR> <BR>DEVICE_NAME<BR> <BR> 被生成的設備名。這個字符串用來從end_request中打印錯誤信息。<BR> <BR>DEVICE_NR(kdev_t device)<BR> <BR>這個符號用來從kdev_t設備號中抽取物理設備的序號。這個宏的值可以是MINOR(device)<BR>或別的表達式。這要依據給設備或分區分配次設備號的常規方式而定。對同一個物理設<BR>備上的所有分區,這個宏應返回同一個設備號——也就是說,DEVICE_NR表達的是磁盤號<BR>,而不是分區號。這個符號被用來聲明CURRENT_DEV,它在request_fn中用來確定被一個<BR>傳送請求訪問的硬件設備的次設備號,可分區設備將在后面“可分區設備”一節中介紹<BR>。<BR> <BR>DEVICE_INTR<BR> <BR>這個符號用來聲明一個指向當前下半部處理程序的指針變量。宏SET_INTR(intr)和CLEAR<BR>_INTR用來給這個變量賦值。當設備可以發出具有不同含義的中斷時,使用多個處理程序<BR>是很方便的。這個主題將在后面“中斷驅動的塊設備驅動程序”一節中討論。<BR> <BR>TIMEOUT_VALUE<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>DEBICE_TIMEOUT<BR> <BR>TIMEOUT_VALUE以記數的方式表達超時,這個超時的值與老計時器之一(特別地指計時器<BR>號DEVICE_TIMEOUT)相關聯。一個驅動程序可以在數據傳送時間太長時,通過調用一個<BR>回調函數來檢測錯誤條件。不過,由于老計時器由一個預賦值的計時器靜態數組組成(<BR>見第六章“時間流”中“核心計時器”一節),一個自定義的驅動程序不能使用它們。<BR>我在sbull中對這兩個符號都未定義,而是用一個新的計時器實現超時。<BR> <BR>DEBICE_NO_RANDOM<BR> <BR>在缺省情況下,函數end_request對系統熵值(即所有隨機性的總量)有所貢獻,這被/d<BR>ev/random所使用。如果一個設備不能對隨機設備貢獻顯著的熵值,DEVICE_NO_RANDOM應<BR>被定義。/dev/random在第九章的“安裝中斷處理程序”中進行了介紹,SA_SAMPLE_RAND<BR>OM也在那兒做了解釋。<BR> <BR>DEVICE_OFF(kdev_t device)<BR> <BR>end_request函數在結束時調用這個宏。例如在軟盤驅動程序中,它調用一個函數,這個<BR>函數負責更新用來控制馬達停轉的一個計時器。如果設備沒有被關掉,那么串DEVICE_OF<BR>F可以被定義為空。sbull不使用DEVICE_OFF。<BR> <BR>DEVICE_ON(kdev_t device)<BR></P></FONT><FONT
color=#ffffff size=3>
<P>DEVICE_ON(kdev_t device)<BR> <BR>DEVICE_REQUEST<BR> <BR>這些函數實際上并未在Linux的頭文件中使用,所以驅動程序并不需要定義它們。大多數<BR>官方的Linux設備驅動程序聲明這些符號并在內部使用它們,但我在sbull里并沒有使用<BR>它。<BR> <BR>sbull驅動程序以如下的方式聲明這些符號:<BR> <BR>(代碼242)<BR> <BR>頭文件blk.h用上面列出的這些宏定義了一些可以由驅動程序使用的額外的宏,我將在后<BR>續章節里對之進行介紹。<BR> <BR>處理請求<BR> <BR>系統性能的方式排序。這些聯結表中的請求被傳遞個驅動程序的請求函數,由它對鏈接<BR>表中的每個請求執行如下的任務:<BR> <BR>l 檢查當前請求的有效性。這個工作由在blk.h中定義的宏INIT_REQUEST完成。<BR> <BR>l 進行實際的數據傳送。用變量CURRENT(實際上是個宏)可以獲得發出請求的一<BR></P></FONT><FONT
color=#ffffff size=3>
<P>l 進行實際的數據傳送。用變量CURRENT(實際上是個宏)可以獲得發出請求的一<BR>些細節。CURRENT是一個指向結構request的指針,我將在下節介紹這個結構的域。<BR> <BR>l 清除當前的請求。這個操作由靜態函數end_request完成,函數的代碼在blk.h<BR>中。驅動程序向這個函數傳遞一個參數,即成功時為1,失敗時為0。當end_request以參<BR>數0調用時,一個“I/O error”消息會被發給系統日志(通過printk)。<BR> <BR>l 循環回至開始,消化下一個請求。可以按照程序員的喜好使用一個goto或是一<BR>個for(;;),或者while(1)。<BR> <BR>實踐中,請求函數的代碼如下構造:<BR> <BR>(代碼243)<BR> <BR>盡管這段代碼除了打印消息外什么都沒有做,運行這個函數可以對數據傳送的基本設計<BR>有一個很好的了解。到此為止,代碼中唯一不清楚的地方是CURRENT的確切含義及它的域<BR>,這個我將在下一節介紹。<BR> <BR>我的第一個sbull實現只包含了所示的空代碼。我意在一個“不存在”的設備上構造一個<BR>文件系統,并使用它一會兒,只要數據仍在緩沖高速緩存中。在運行一個象這樣羅嗦的<BR>請求函數時,看看系統日志能幫助你理解緩沖高速緩存是如何工作的。<BR> <BR>在編譯時,定義符號SBULL_EMPTY_REQUEST,那么這個空且羅嗦的函數可以在sbull 中運<BR></P></FONT><FONT
color=#ffffff size=3>
<P>在編譯時,定義符號SBULL_EMPTY_REQUEST,那么這個空且羅嗦的函數可以在sbull 中運<BR>行。如果你想理解核心是如何處理不同塊大小的,你可以在insmod命令行上實驗blksize<BR>=。這個空的請求函數通過打印每個請求的細節揭示了內部核心的工作情況。你或許也可<BR>以試試hardsect=,但目前它被關閉了,因為比較危險。(見本章開始時的“注冊驅動程<BR>序”)。<BR> <BR>請求函數的代碼并不顯式地調用return(),因為當列表中的待處理請求耗盡時,INIT-_RE<BR>QUEST會替你完成這個工作。<BR> <BR>執行實際的數據傳送<BR> <BR>為了給sbull構造一個可以工作的數據傳送,讓我們先來看看核心是如何在結構request<BR>中描述一個請求的。這個結構在<linux/blkdev.h>中定義。通過訪問CURRENT的域,驅動<BR>程序可以得到所有為在緩沖高速緩存的物理塊設備之間傳送數據所需要的信息。<BR> <BR>CURRENT是用來訪問當前請求(即被首先服務的那個請求)。正如你可能猜到的,CURREN<BR>T是blk_dev[MAJOR_NR].current_request的縮短形式。<BR> <BR>下面這些當前請求的域包含了請求函數的有用信息:<BR> <BR>kdev_t rq_dev;<BR> <BR>請求所訪問的設備。有本驅動程序所管理的所以設備均被使用同一個請求函數。一個請<BR></P></FONT><FONT
color=#ffffff size=3>
<P>請求所訪問的設備。有本驅動程序所管理的所以設備均被使用同一個請求函數。一個請<BR>求函數處理所有的次設備號;rq_dev可以被用來取得被操作的次設備。盡管Linux1.2稱<BR>這個域為dev,你仍然可以通過宏CURRENT_DEV來訪問這個域。CURRENT_DEV在我們所討論<BR>的所有版本的核心中是可移植的。<BR> <BR>int cmd;<BR> <BR>這個域是READ或WRITE。<BR> <BR>unsigned long sector;<BR> <BR>請求指向的第一個扇區。<BR> <BR>unsigned long current_nr_sectors;<BR> <BR>unsigned long nr_sectors;<BR> <BR>當前請求的扇區數(大小)。驅動程序應該引用current_nr_sectors,而應該忽略nr_sec<BR>tors(列在這里只是為了完整)。請看下一節“集簇請求”以獲得更多的細節。<BR> <BR>char *buffer<BR> <BR>緩沖高速緩存中的域。如果cmd==READ,就是寫數據的位置;如果cmd==WRITE,就是讀數<BR></P></FONT><FONT
color=#ffffff size=3>
<P>緩沖高速緩存中的域。如果cmd==READ,就是寫數據的位置;如果cmd==WRITE,就是讀數<BR>據的位置。<BR> <BR>struct buffer_head *bh<BR> <BR>這個結構描述了這個請求列表中的第一個緩沖區。我們將在“集簇請求”中用到這個域<BR>。<BR> <BR>在這個結構中還有其它的一些域,但它們基本上是核心內部使用的,驅動程序并不期望<BR>使用它們。<BR> <BR>sbull中可工作的請求函數的實現如下所示。在下面的代碼中sbull-_devices與scull_de<BR>vice類似。我們在第三章字符設備驅動程序的“打開方法”中介紹過scull_devices。<BR> <BR>(代碼245)<BR> <BR>由于sbull只是個RAM盤,所以它的“數據傳送”簡化為一個memcpy調用。這個函數唯一<BR>“奇怪”的特征是條件語句中限制只能報告最多5個錯誤。這樣做的目的是為了防止系統<BR>日志被太多的信息搞亂,因為end-_request(0)在請求失敗時已打印了“I/O error”的<BR>消息。靜態計數器是限制消息報告的標準做法,在核心中被多次用到。<BR> <BR>集簇請求<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>上面請求函數中每次循環迭代都傳送幾個扇區——按照數據的使用,一般情況下,相當<BR>于一個塊的“數據”量。例如,交換一次執行PAGE_SIZE大小的數據,而在ext2文件系統<BR>中就是傳送1KB的塊。<BR> <BR>盡管在I/O中最方便的數據大小是一個塊,但如果把相鄰塊的讀或寫集簇起來,你會獲得<BR>很高的性能改善。在這個意義上,“相鄰”指的是在硬盤上塊的位置,而“連續”則指<BR>連續的內存區域。<BR> <BR>將相鄰塊集簇有兩個好處。首先,集簇加速了傳送(例如,軟盤驅動程序將相鄰的塊組<BR>合在一起,一次傳送一個磁道的數據)。另外,它還能通過避免分配冗余的request結構<BR>來節省核心中的內存。<BR> <BR>如果你愿意,也可以完全忽略集簇。上面給出的框架請求函數在沒有集簇的情況下可以<BR>完全正確地工作。不過,如果你想利用集簇,你需要更加仔細地研究struct_request的<BR>內部。<BR> <BR>不幸的是,我所知道的所有的核心(至少到2.1.51)都不能為自定義驅動程序進行集簇<BR>,而只對象SCSI和IDE這類內部驅動程序使用。如果你對核心的內部不感興趣,你可以跳<BR>過本節的其余部分。不過,集簇將來還可能在模塊中實現,它是通過減少相鄰扇區的請<BR>求延遲來提高數據傳送性能的一個有趣的途徑。<BR> <BR>在我描述驅動程序如何利用集簇請求之前,讓我們先來看看當一個請求被排隊時發生了<BR></P></FONT><FONT
color=#ffffff size=3>
<P>在我描述驅動程序如何利用集簇請求之前,讓我們先來看看當一個請求被排隊時發生了<BR>什么。<BR> <BR>當核心請求數據塊傳送時,它掃描目標設備的活動請求鏈表。當一個新塊在盤上與一個<BR>已經被請求的塊相鄰時,它就被集簇到第一個塊上。當前已存在的請求便被擴大了而不<BR>是增加一個新請求。<BR> <BR>不幸的是,磁盤上相鄰的兩個數據緩沖區在內存中并不一定相鄰。這個發現,外加上需<BR>要有效地管理緩沖高速緩存,導致創建一個buffer_head結構。一個buffer_head和一個<BR>數據緩沖相關聯。<BR> <BR>因此,一個“集簇”的請求,就是一個指向buffer_head的結構鏈表的request_struct結<BR>構。end_request函數負責這個問題,這就是為什么前面給出的請求函數可以獨立于集簇<BR>而工作。換句話說,end_request要么清除當前請求并準備為下一個服務,要么準備處理<BR>同一個請求中的下一個緩沖區。因此,集簇對不關心它的設備驅動程序是透明的,上面<BR>的sbull函數就是一個例子。<BR> <BR>一個驅動程序可能希望通過在它的request_fn函數中每次循環時處理整個緩沖區頭鏈表<BR>的辦法來從集簇中獲益。為了做到這一點,驅動程序應該指向CURRENT->current_nr_sec<BR>tors(這個域我在上面的sbull_request中已經用過)和CURRENT->nr_sectors,它包含<BR>了集簇在“當前”buffer_heads列表中的相鄰扇區的數目。<BR> <BR>當前緩沖區頭是CURRENT->bh,而數據塊是CURRENT->bh->b_data。后一個指針為了象sbu<BR></P></FONT><FONT
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -