在linux裝置驅動中,裝置號設乙個很重要的概念和變數。不論是主裝置號,還是次裝置號,在裝置驅動中都佔據了很重要的地位。那麼他在kernel中是如何操作的?這個資料結構都是通過那些函式可以很容易的在我們寫linux裝置驅動模組時被我們所使用呢?在include/linux/type.h檔案中我們能看到乙個關於dev_t的定義如下:
...typedef __u32 __kernel_dev_t;
typedef __kernel_fd_set fd_set;
typedef __kernel_dev_t dev_t;
...
從這個定義中我們能看到dev_t是乙個無符號的32位的整型。
首先我們需要說明的是,在linux中主次裝置號是放置在乙個無符號的32位的整型中,那麼這32位整型對於主次裝置號如何分配呢?
從源**中我們可以看到,主裝置號佔據12個位,次裝置好佔據20位。這在一定的時期內,主次裝置號是完全可以滿足系統需要的。
同時在include/linux/kdev_t.h檔案中我們能發現很多函式或者巨集定義的操作都是針對dev_t的。
具體可以看到我們經常用到的major(dev)、minor(dev)、mkdev(ma,mi)。
下面我們就具體分析下這三個我們經常用到的巨集定義:
#define minorbits 20
#define minormask ((1u << minorbits) - 1)
#define major(dev) ((unsigned int) ((dev) >> minorbits))
從這個巨集定義中我們可以看到其把無符號的32位的整型做位操作運算:右移20位。
在c語言中如果是右移,那麼左邊補0,這樣在這32位的整型中通過這個操作就只保留了原先第19位到31位的有效值,而這也正是我們所需要的。
下面我們看下minor這個巨集定義:
#define minor(dev) ((unsigned int) ((dev) & minormask))
要明白這個巨集定義的具體是多少,我們需要首先明白巨集定義minormask是什麼?
我們從前面的巨集定義中,我們看到:
#define minormask ((1u << minorbits) - 1)
minormask 是1u也就是1左移位20個位元組,二進位制的話就是10000000000000000000,也就是1後面帶20個0。
然後在減1呢,就成了二進位制11111111111111111111,也就是20個1,十六進製制的話是0xfffff。
好現在我們知道minormask是20個1,也就是十六進製制0xfffff,那麼我們在與dev_t做乙個位的與運算,就把32位中的前12為置0,保留其後面的20位,也正是我們想要的表是裝置次裝置號的後20個位元組。
好下面我們看下如果我們知道了主裝置號、次裝置號,我們如何生成乙個dev_t的資料結構。
巨集定義:
#define mkdev(ma,mi) (((ma) << minorbits) | (mi))
明白了前面我們所說的,其實這個就比較簡單了,把主裝置號左移20位,然後與上次裝置號,就是我們所需要的dev_t的資料結構。
那麼我們前面所說的關於dev_t的操作是新的2.6.x系列中的,在之前的2.4.x系列中,由於對裝置號的總共就16個位元組,也就是乙個短整型,那麼乙個系統中所能擁有的裝置號就是及其有限的了。
我們看下在老版本中的核心中他們的表示:
#define major(dev) ((dev)>>8)
#define minor(dev) ((dev) & 0xff)
#define mkdev(ma,mi) ((ma)<<8 | (mi))
從中我們可以看出,他是以8為為分界線,高8位為主裝置號,低8位為次裝置號,那麼乙個8位所能表示的最多也即是255個數值,那麼當我們系統中如果擁有的裝置大於這個數值的時候,在老版本的核心中就沒有辦法處理了。
在核心實現中還實現了兩個列印的函式,其實也是巨集定義:
#define print_dev_t(buffer, dev) \
sprintf((buffer), "%u:%u\n", major(dev), minor(dev))
#define format_dev_t(buffer, dev) \
()
從**中我們可以看出。
第一就是把裝置的主裝置號和次裝置號以字串的形式存放到buffer中,在使用這個巨集定義的時候需要注意的是:
buffer需要提前開闢空間,而且還需要是夠用的空間。
第二所實現的功能和第乙個很類似。這兒我們就不具體說明,請參考第乙個巨集定義的實現。
在這個檔案中還有很多的函式,這些函式的主要功能就是和老版本的核心**相容而產生的,比如:
static inline int old_valid_dev(dev_t dev)
此函式是判斷乙個dev_t是否可以轉換成舊制的dev_t。
static inline u16 old_encode_dev(dev_t dev)
把32位的裝置號轉換成16位的舊制的裝置號。
其中主要操作為:首先把主裝置號左移8位,為次裝置好空出8位的位置,然後與上次裝置號。
在使用這個函式的時候需要注意的就是需要首先判斷下32位的裝置號是否可以有效的轉換成16位的裝置號。
static inline dev_t old_decode_dev(u16 val)
上面函式的反操作。
主裝置號右移8位,然後與上255,即8個1。也就是取此變數的低8位,
次裝置號與上255,也是取此變數的低8位即可。
static inline u32 new_encode_dev(dev_t dev)
static inline dev_t new_decode_dev(u32 dev)
次函式比較簡單,再次就不多說了,請參考前面的實現。
linux裝置號之操作
在linux裝置驅動中,裝置號設乙個很重要的概念和變數。不論是主裝置號,還是次裝置號,在裝置驅動中都佔據了很重要的地位。那麼他在kernel中是如何操作的?這個資料結構都是通過那些函式可以很容易的在我們寫linux裝置驅動模組時被我們所使用呢?在include linux type.h檔案中我們能看...
19 linux裝置號之操作
在linux裝置驅動中,裝置號設乙個很重要的概念和變數。不論是主裝置號,還是次裝置號,在裝置驅動中都佔據了很重要的地位。那麼他在kernel中是如何操作的?這個資料結構都是通過那些函式可以很容易的在我們寫linux裝置驅動模組時被我們所使用呢?在include linux type.h檔案中我們能看...
Linux之裝置操作
linux系統的基本思路就是 一切都是檔案,無論是裡面儲存的資料還是外接裝置都是檔案。因此操作每乙個裝置都會有對應的檔案描述符號,我們可以像操作檔案一樣操作它們,但注意與操作檔案還是有區別的。直接操作裝置的函式 return int open in char path,in int flag ret...