將Linux 移植到新的體系結構時,開發者遇到的若干問題都與不正確的數據類型有關。堅持使用嚴格的數據類型和使用 -Wall -Wstrict-prototypes 進行編譯可能避免大部分的 bug。
-Wall 顯示所有的警告 -Wstrict-prototypes 嚴格的檢測原型,如果不一致,則出現警告內核數據使用的數據類型主要分為3個類型: 標准C語言類型、確定大小的類型和特定內核對象的類型。
當需要“一個2字節填充符”或“用一個4字節字串來代表某個東西”,就不能使用標准C語言類型,因為在不同的體系結構,C 語言的數據類型所占的空間大小不同。而且有的構架,內核空間和用戶空間的C數據類型所占空間大小也可能不同。
內核中的地址是unsigned long類型,指針大小和long類型相同。 盡管概念上地址是指針,但使用一個無符號整型可以更好地實現內存管理; 內核把物理內存看成一個巨型數組, 內存地址就是該數組的索引。我們可以方便地對指針取值,但直接處理內存地址時,我們幾乎從不會以這種方式對他取值。使用一個整數類型避免了這種取值,因此避免了bug。所以,利用至少在 Linux 目前支持的所有平台上,指針和長整型始終是相同大小的這一事實,內核中內存地址常常是unsigned long。 C99 標准定義了 intptr_t 和 uintptr_t 類型,它們是能夠保存指針值的整型變量。但沒在 2.6 內核中幾乎沒使用。內核:當需要知道你定義的數據的大小時,可以使用內核提供的下列數據類型:
內核中最常用的數據類型由它們自己的 typedef 聲明,阻止了任何移植性問題。
“接口特定(interface-specific)”由某個庫定義的一種數據類型, 以便為了某個特定的數據結構提供接口。
注意:
當處理時間間隔時,不要假定每秒的jiffies個數,不是每個 Linux 平台都以固定的速度運行。當計算時間間隔時,要使用 HZ ( 每秒的定時器中斷數 ) 來標定你的時間。
Hz:Linux核心每隔固定周期會發出timer interrupt (IRQ 0),HZ是用來定義每一秒有幾次timer interrupts。舉例來說,HZ為1000,代表每秒有1000次timer interrupts。 Tick:Tick是HZ的倒數,意即timer interrupt每發生一次中斷的時間。如HZ為250時,tick為4毫秒(millisecond)。 Jiffies:Jiffies為Linux核心變數(unsigned long),它被用來記錄系統自開機以來,已經過了多少tick。每發生一次timer interrupt,Jiffies變數會被加一。當使用內存時,記住一個內存頁是 PAGE_SIZE 字節, 不是 4KB。相關的宏定義是 PAGE_SIZE 和 PAGE_SHIFT(包含將一個地址移位來獲得它的頁號的位數)。如果用戶空間程序需要這些信息,可以使用 getpagesize 庫函數。
若一個驅動需要 16 KB 來暫存數據,一個可移植得解決方法是 get_order:
不要假設字節序。 代碼應該編寫成不依賴所操作數據的字節序的方式。
頭文件定義:
但是還有一個更好的方法:Linux 內核有一套宏定義來處理處理器字節序和特定字節序之間的轉換。例如:
u32 cpu_to_le32 (u32);
u32 le32_to_cpu (u32);
/*這些宏定義將一個CPU使用的值轉換成一個無符號的32位小頭數值,無論 CPU 是大端還是小端,也不管是不是32 位處理器。在沒有轉換工作需要做時,返回未修改的值。*/
編寫可移植代碼而值得考慮的最後一個問題是如何訪問未對齊的數據。存取不對齊的數據應當使用下列宏:
#include <asm/unaligned.h>
get_unaligned(ptr);
put_unaligned(val, ptr);
這些宏是無類型的,並對各總數據項,不管是 1、2、4或 8 個字節,他們都有效,並且在所有內核版本中都有定義。
關於對齊的另一個問題是數據結構的跨平台移植性。同樣的數據結構在不同的平台上可能被不同地編譯。為了編寫可以跨體系移植的數據結構,應當始終強制數據項的自然對齊。
自然對齊(natural alignment)指的是:數據項大小的整數倍的地址上存儲數據項。 應當使用填充符避免強制自���對齊時編譯器移動數據結構的字段,在數據結構中留下空洞。
為了目標處理器的良好性能,編譯器可能悄悄地插入填充符到結構中,來保證每個成員是對齊的。若定義一個和設備要求的結構體相匹配結構,自動填充符會破壞這個意圖。解決這個問題的方法是告訴編譯器這個結構必須是"緊湊的", 不能增加填充符。例如下列的定義:
Linux Kernel 的詳細介紹:請點這裡
Linux Kernel 的下載地址:請點這裡