




已閱讀5頁,還剩2頁未讀, 繼續免費閱讀
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第10章 合理使用數據類型在進一步討論更深的主題之前,我們需要先停一停,快速地回顧一下可移植問題。Linux1.2版本和2.0版本之間的不同就在于額外的多平臺能力;結果是,大多數源代碼級的移植問題已經被排除了。這意味著一個規范的Linux驅動程序也應該是多平臺的。但是,與內核代碼相關的一個核心問題是,能夠同時存取各種長度已知的數據項(例如,文件系統數據類型或者設備卡上的寄存器)和利用不同處理器的能力(32位和64位的體系結構,也有可能是16位的)。當把x86的代碼移植到新的體系結構上時,核心開發者遇到的好幾個問題都和不正確的數據類型相關。堅持強數據類型以及編譯時使用-Wall -Wstrict-prototypes選項能夠防止大部分的臭蟲。內核使用的數據類型劃分為三種主要類型:象int這樣的標準C語言類型,象u32這樣的確定數據大小的類型和象pid_t這樣的接口特定類型。我們將看一下這三種類型在何時使用和如何使用。本章的最后一節將討論把驅動器代碼從x86移植到其它平臺上可能碰到的其它一些典型問題。如果你遵循我提供的這些準則,你的驅動程序甚至可能在那些你未能進行測試的平臺上編譯并運行。使用標準C類型大部分程序員習慣于自由的使用諸如int和long這樣的標準類型,而編寫設備驅動程序就必須細心地避免類型沖突和潛在的臭蟲。問題是,當你需要“2個字節填充單位(filler)”或“表示4個字節字符串的某個東西”時,你不能使用標準類型,因為通常的C數據類型在不同的體系結構上所占空間大小并不相同。例如,長整數和指針類型在Alpha上和x86上所占空間大小就不一樣,下面的屏幕快照表明了這一點:morgana% ./datasizesystem/machine: Linux i486sizeof(char) = 1sizeof(short) = 2sizeof(int) = 4sizeof(long) = 4sizeof(longlong) = 8sizeof(pointer) = 4wolf% ./datasizesystem/machine: Linux alphasizeof(char) = 1sizeof(short) = 2sizeof(int) = 4sizeof(long) = 8sizeof(longlong) = 8sizeof(pointer) = 8 sandra% ./datasizesystem/machine: Linux sparcsizeof(char) = 1sizeof(short) = 2sizeof(int) = 4sizeof(long) = 4sizeof(longlong) = 8sizeof(pointer) = 4 datasize程序是一個可以從在OReilly FTP站點的misc-progs目錄下獲得的小程序。在混合使用int和long類型時,你必須小心,有時有很好的理由這樣做,一種情形就是內存地址,一涉及到內核,內存地址就變得很特殊。雖然概念上地址是指針,但是通過使用整數類型,可以更好地實現內存管理;內核把物理內存看做一個巨大的數組,內存地址就是這個數組的索引。而且,一個指針很容易被取地址(deference),而使用整數表示內存地址可以防止它們被取地址,這正是人們所希望的(比使用指針更安全)。因而,內核中的地址屬于unsigned long類型,這是利用了指針和長整數類型大小總是相同這一事實,至少在所有Linux當前支持的平臺上是這樣的。我們等著看看將來把Linux移植到不符合這一規則的平臺上的時候,會發生些什么。分配確定的空間大小給數據項有時內核代碼需要指定大小的數據項,或者用來匹配二進制結構* 讀分區表時,執行二進制文件時或者解碼一個網絡包時,就會發生這種情況。或者用來在結構中插入填充字段對齊數據。為此目的,內核提供如下的數據類型,它們都在頭文件中聲明,這個文件又被頭文件所包含:u8; /* 無符號字節(8位) */u16; /* 無符號字(16 位) */u32; /* 無符號32位數值 */u64; /* 無符號64位數值 */這些數據類型只能被內核代碼所訪問(也即,在包含頭文件之前必須先定義_KERNEL_)。相應的有符號類型也是存在的,但一般不用;如果你需要使用它們的話,只要把名字中的u替換為s就可以了。如果用戶空間的程序需要使用這些類型,可以在這些名字前面添加2個下劃線:_u8和其它類型是獨立于_KERNEL_定義的。例如,如果一個驅動程序需要通過ioctl系統調用與一個運行在用戶空間內的程序交換二進制結構的話,頭文件必須將結構中的32位字段定義為_u32。重要的是要記住這些類型特定于Linux,使用它們就會防礙軟件向其他Unix變體的移植。但是,有些情況下也需要明確說明數據大小,而標準頭文件(在每個Unix系統上都能找到的)并未聲明較合適的數據類型。你也許注意到,有時內核也使用一般的數據類型,象unsigned int,用于那些大小與體系結構無關的項。這通常是為了向后兼容。當u32及其相關類型在1.1.67版本引入時開發者沒辦法把存在的數據類型改成新類型,因為當結構字段和賦予的值之間類型不匹配時,編譯器會發出警告+ 實際上,即使兩種類型僅是同一對象的不同名字,例如PC上的unsigned long和u32類型,編譯器也會發出類型不匹配的信號。Linus當初可沒預料到為自己使用而編寫的這個操作系統會發展成為多平臺的;因此,一些舊的結構的數據類型定義上不是很嚴格。接口特定的類型內核中最常使用的數據類型有它們自己的typedef聲明,這樣就防止了任何移植上的問題。例如,進程號(pid)通常使用pid_t,而不是int。使用pid_t屏蔽了任何實際數據類型之間可能的差別。我使用“接口特定”這種表述來指代特定數據項的編程接口。屬于指定“標準”類型的其它數據項也可以認為是接口特定的。比如,一個jiffy計數總是屬于unsigned long類型的,獨立于它的實際大小你喜歡那么頻繁地使用jiffy_t類型么?這里我關注的是接口特定類型的第一類,那些以_t結尾的類型。_t類型完整的列表在頭文件中,但是該列表幾乎沒什么用。當需要一個特定類型時,你可以在你要調用的函數原型或者使用的數據結構中找到它。只要你的驅動程序使用了需要這種“定制”類型的函數,又不遵循慣例的時候,編譯器都會發出一個警告;如果你打開-Wall編譯開關并且細心地去除了所有警告,你就可以自信你的代碼是可移植的了。_t數據項的主要問題是當你需要打印它們的時候,并不總是容易選擇正確的printk或者printf格式,并且你在一種體系結構上排除了的警告,在另一種體系結構上可能又會出現。例如,當size_t在一些平臺上是unsigned long,而在另外一些平臺上卻是unsigned int時,你怎么打印它呢?任何時候,當你需要打印一些特定接口的數據的時候,最行之有效的方法就是,把它強制轉換成最可能的類型(通常是long或unsigned long類型),然后把它用相應的格式打印出來。這種做法不會產生錯誤或者警告,因為格式和類型相符,而且你也不會丟失數據位,因為強制類型轉換要么是個空操作,要么是將該數據項向更大數據類型的擴展。實際上,通常我們并不會去打印我們討論的這些數據項,因此只有顯示調試信息時才會碰到這些問題。更經常的,除了把接口特定的類型作為參數傳遞給庫或內核函數以外,代碼僅僅只會對它們進行些儲存和比較。雖然大多數情形下,_t類型都是正確的解決方案,但有時候正確的類型也可能并不存在。這會發生在一些還沒被拋棄的舊接口上。在內核頭文件中我發現一處疑點,為 I/O函數聲明數據類型時不是很嚴格(參見第8章“硬件管理”中的“平臺相關性”一節)。這種不嚴格的類型定義主要是出于歷史上的原因,但在編寫代碼時卻會帶來問題。就我而言,我經常在把參數交換給out函數時遇上麻煩;而如果定義了port_t,編譯器將會指出這些錯誤。其它與移植有關的問題除了數據類型定義問題之外,如果想讓你編寫的驅動程序能在不同的Linux平臺間移植的話,還必須注意到其它一些軟件上的問題:時間間隔在處理時間間隔時,不能假定每秒一定有100個jiffy。雖然對當前的Linux-x86而言這是對的,但并不是所有Linux平臺都是以100HZ運行。如果你改變了HZ的數值,那么即使對x86,這種假設也是錯誤的,何況沒人知道未來的內核會發生些什么變化。使用jiffy計算時間間隔的時候,應該把時間轉換成以HZ為單位。例如,為了檢測半秒鐘的超時,可以把消逝的時間和HZ/2作比較。更常見的,與msec毫秒對應的jiffy的數目總是msec*HZ/1000。許多的網絡驅動程序在移植到Alpha上時都必須修正該細節;有些開始是為PC設計的驅動程序給超時明確定義了一個jiffy值,但是Alpha卻有著不同的HZ數值。頁大小使用內存時,要記住內存頁的大小為PAGE_SIZE字節,而不是4KB。假設頁大小就是4KB并硬編碼該數值是PC程序員常犯的錯誤Alpha頁大小是這的兩倍。相關的宏有PAGE_SIZE和PAGE_SHIFT。后者包含要得到一個地址所在頁的頁號時需要對該地址右移的位數。對當前的4KB和8KB的頁,這個數值通常是12或者13。這些宏在頭文件中定義。讓我們來看一種簡單的情況。如果驅動程序需要16KB空間來存放臨時數據,它不應當指定get_free_pages函數的參數order(“2”的冪)。需要一種可移植的解決辦法。此時,可以使用條件編譯#ifdef _alpha_,但這只適用于已知的平臺,而如果要支持別的平臺,它就不能奏效了。我建議使用下面的代碼:buf = get_free_pages(GFP_KERNEL, 14 - PAGE_SHIFT, 0 /*dma*/);或者,更好一些的代碼:int order = (14 - PAGE_SHIFT 0) ? 14 - PAGE_SHIFT : 0;buf = get_free_pages(GFP_KERNEL, order, 0 /*dma*/);兩種解決辦法都利用了16KB等于114這一常識。兩個數的商就是它們對數的差(的冪),而14和PAGE_SHIFT都是冪。第二種解決辦法就更好,因為它可以防止把一個負的order值傳遞給get_free_pages函數;order值時在編譯時就計算好的,沒有運行時的額外開銷,而且,上面給出的實現方法是不依賴于PAGE_SIZE來分配任何2的冪次大小的內存空間的安全方法。字節序要小心的是不要主觀假設字節序。雖然PC是按低字節優先的方式存儲多個字節(“小印地安,little endian”),但是大多數更高級的平臺是以另一種方式工作的(“大印地安,big endian”)。雖然好的程序不會依賴于字節序,但有時驅動程序需要創建占一個字節以上的整數,或者相反(一個字節以下)。此時,代碼中就應該將頭文件包含進來,并且檢測頭文件中是否定義了_BIG_ENDIAN或_LITTLE_ENDIAN。起始的下劃線在Linux-1.2之后版本的頭文件中卻去掉了,在頭文件后再包含scull示例程序中的頭文件sysdep.h就可以修正這個不兼容。當字節序相關問題與網絡傳輸有聯系的時候,就應當使用下面各種函數來進行16位和32位數值的轉換,這些函數也都是在頭文件中定義的:unsigned long ntohl(unsigned long);unsigned short ntohs(unsigned short);unsigned long htonl(unsigned long);unsigned short htons(unsigned short);在網絡程序員當中,這些函數是眾所周知的。它們得名于“Network TO Host Long”(從網絡到主機的long類型)或類似的短語。2.1.10版的內核增加了cpu-to-little-endian和cpu-to-big-endian兩種轉換,2.1.43版的內核在這方面又加以擴充。新增的一些實用函數將在第17章“近期發展”中的“轉換函數”一節中描述。數據對齊在編寫可移植代碼時最后一個值得考慮的問題是如何訪問未對齊數據例如,當一個4字節的數值被儲存在不是4字節的整數倍的地址中時,如何將它讀出來。PC的用戶常常訪問未對齊的數據項,但并不是所有體系結構都允許這樣做。舉個例子,在Alpha上,每當程序試圖傳送未對齊數據時,都會產生一個異常。如果你需要訪問未對齊數據,可以使用下面這些宏:#include get_unaligned(ptr);put_unaligned(val,ptr);這些宏是與類型無關的。對各種數據項,不管它是1字節,2字節,4字節還是8字節,這些宏都有效。在2.0版以前的內核并不提供這些宏,但在1.2版的內核在頭文件sysdep.h中對它們作了定義。一個通用準則就對顯式的常數值持懷疑態度。通常,使用預編譯的宏來使代碼參數化使代碼更通用。雖然我不能在此列出所有參數化的值,但你可以在頭文件中找到正確的提示。不幸的是,有些地方問題還沒有得到解決,例如對磁盤扇區數據的處理。出于歷史的原因,Linux只能處理.5KB的磁盤扇區。所幸的是,現存所有設備都滿足這個限制。目前正在逐漸地改進代碼以支持不同的扇區大小。但要找到代碼中所有在.5KB的假設下進行硬編碼的地方卻非常困難。扇區大小的問題將在第12章“加載塊設備驅動程序”中進一步描述。快速參考在本章引入了如下一些符號:#i
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 河北勞動關系職業學院《北京規劃研究》2023-2024學年第二學期期末試卷
- 齊魯醫藥學院《微機原理與嵌入式系統實驗》2023-2024學年第二學期期末試卷
- 濰坊工程職業學院《Java開發框架》2023-2024學年第二學期期末試卷
- 三亞學院《演講與主持》2023-2024學年第二學期期末試卷
- 大連工業大學藝術與信息工程學院《建筑概預算》2023-2024學年第二學期期末試卷
- 天津開發區職業技術學院《微電子器件基礎》2023-2024學年第二學期期末試卷
- 心理咨詢技能課件
- 內蒙古鴻德文理學院《酒店收益管理》2023-2024學年第二學期期末試卷
- 吉林交通職業技術學院《動植物檢驗檢疫》2023-2024學年第二學期期末試卷
- 廣西機電職業技術學院《電法勘探》2023-2024學年第二學期期末試卷
- 垃圾焚燒發電廠應急預案
- 動柱龍門合格證書
- 超星爾雅學習通《歷史的三峽:近代中國的思潮與政治(華東師范大學)》2025章節測試答案
- 腎內科疾病臨床路徑
- 船舶修造知識培訓課件
- 城市地下綜合管廊PPP項目運營維護方案
- 2025年醫療器械質量合規協議模板
- 智能設計方法 教學大綱
- 2025年廣東深圳市煙草專賣局公司招聘筆試參考題庫含答案解析
- 第四批四川省高校重點實驗室名單
- 福建省龍巖新羅區重點中學2025屆中考生物全真模擬試卷含解析
評論
0/150
提交評論