結構體file_operations
在標頭檔案linux/fs.h
中定義,用來儲存驅動核心模組提供的對裝置進行各種操作的函式的指標。該結構體的每個域都對應著驅動核心模組用來處理某個被請求的事務的函式的位址。
舉個例子,每個字元裝置需要定義乙個用來讀取裝置資料的函式。結構體file_operations
中儲存著核心模組中執行這項操作的函式的位址。一下是該結構體在核心2.6.5
中看起來的樣子:
struct file_operations ;
重的項就為null。
gcc還有乙個方便使用這種結構體的擴充套件。你會在較現代的驅動核心模組中見到。新的使用這種結構體的方式如下:
struct file_operations fops = ;
同樣也有c99
語法的使用該結構體的方法,並且它比gnu
擴充套件更受推薦。我使用的版本為2.95
為了方便那些想移植你的**的人,你最好使用這種語法。它將提高**的相容性:
struct file_operations fops = ;
這種語法很清晰,你也必須清楚的意識到沒有顯示宣告的結構體成員都被gcc
初始化為null。
指向結構體struct file_operations
的指標通常命名為fops。
關於file
結構體
每乙個裝置檔案都代表著核心中的乙個file
結構體。該結構體在標頭檔案linux/fs.h
定義。注意,
file
結構體是核心空間的結構體,
這意味著它不會在使用者程式的**中出現。它絕對不是在glibc
中定義的file
。file
自己也從不在核心空間的函式中出現。它的名字確實挺讓人迷惑的。它代表著乙個抽象的開啟的檔案,但不是那種在磁碟上用結構體inode
表示的檔案。
指向結構體struct file
的指標通常命名為filp
。你同樣可以看到struct file file
的表達方式,但不要被它**。
去看看結構體file
的定義。大部分的函式入口,像結構體struct dentry
沒有被裝置驅動模組使用,你大可忽略它們。這是因為裝置驅動模組並不自己直接填充結構體file
:它們只是使用在別處建立的結構體file
中的資料。
註冊乙個裝置
如同先前討論的,字元裝置通常通過在路徑/dev
下的裝置檔案進行訪問。主裝置號告訴你哪些驅動模組是用來操縱哪些硬體裝置的。從裝置號是驅動模組自己使用來區別它操縱的不同裝置,當此驅動模組操縱不只乙個裝置時。
將核心驅動模組加載入核心意味著要向核心註冊自己。這個工作是和驅動模組獲得主裝置號時初始化一同進行的。你可以使用標頭檔案linux/fs.h
中的函式register_chrdev
來實現。
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
其中unsigned int major
是你申請的主裝置號,const char *name
是將要在檔案/proc/devices
中顯示的名稱,struct file_operations *fops
是指向你的驅動模組的file_operations
表的指標。負的返回值意味著註冊失敗。注意註冊並不需要提供從裝置號。核心本身並不在意從裝置號。
現在的問題是你如何申請到乙個沒有被使用的主裝置號?最簡單的方法是檢視檔案documentation/devices.txt
從中挑選乙個沒有被使用的。這不是一勞永逸的方法因為你無法得知該主裝置號在將來會被占用。最終的方法是讓核心為你動態分配乙個。
如果你向函式
register_chrdev
傳遞為0
的主裝置號,那麼返回的就是動態分配的主裝置號。***就是既然你無法得知主裝置號,你就無法預先建立乙個裝置檔案。有多種解決方法:
第一種方法是新註冊的驅動模組會輸出自己新分配到的主裝置號,所以我們可以手工建立需要的裝置檔案。第二種是利用檔案/proc/devices
新註冊的驅動模組的入口,要麼手工建立裝置檔案,要麼編乙個指令碼去自動讀取該檔案並且生成裝置檔案。第三種是在我們的模組中,當註冊成功時,使用mknod
系統呼叫建立裝置檔案並且在驅動模組呼叫函式cleanup_module
前,呼叫rm
刪除該裝置檔案。
登出乙個裝置
即使是root
也不能允許隨意解除安裝核心模組。當乙個程序已經開啟乙個裝置檔案時我們解除安裝了該裝置檔案使用的核心模組,我們此時再對該檔案的訪問將會導致對已解除安裝的核心模組**記憶體區的訪問。幸運的話我們最多獲得乙個討厭的錯誤警告。如果此時已經在該記憶體區載入了另乙個模組,倒霉的你將會在核心中跳轉執行意料外的**。結果是無法預料的,而且多半是不那麼令人愉快的。
平常,當你不允許某項操作時,你會得到該操作返回的錯誤值(一般為負值)。但對於無返回值的函式cleanup_module
這是不可能的。然而,卻有乙個計數器跟蹤著有多少程序正在使用該模組。你可以通過檢視檔案/proc/modules
的第三列來獲取這些資訊。如果該值非零,則解除安裝就會失敗。你不需要在你模組中的函式cleanup_module
中檢查該計數器,因為該項檢查由標頭檔案linux/module.c
中定義的系統呼叫sys_delete_module
完成。你也不應該直接對該計數器進行操作。你應該使用在檔案linux/modules.h
定義的巨集來增加,減小和讀取該計數器:
try_module_get(this_module): increment the use count.
try_module_put(this_module): decrement the use count.
保持該計數器時刻精確是非常重要的;如果你丟失了正確的計數,你將無法解除安裝模組,那就只有重啟了。不過這種情況在今後編寫核心模組時也是無法避免的。
chardev.c
下面的**示範了乙個叫做chardev
的字元裝置。你可以用cat
輸出該裝置檔案的內容(或用別的程式開啟它)時,驅動模組會將該裝置檔案被讀取的次數顯示。目前對裝置檔案的寫操作還不被支援(像echo "hi" > /dev/hello
),但會捕捉這些操作並且告訴使用者該操作不被支援。不要擔心我們對讀入緩衝區的資料做了什麼;我們什麼都沒做。我們只是讀入資料並輸出我們已經接收到的資料的資訊。
為多個版本的核心編寫核心模組
系統呼叫,也就是核心提供給程序的介面,基本上是保持不變的。也許會添入新的系統呼叫,但那些已有的不會被改動。這對於向下相容是非常重要的。在多數情況下,裝置檔案是保持不變的。但核心的內部在不同版本之間還是會有區別的。
linux
核心分為穩定版本(版本號中間為偶數)和試驗版本(版本號中間為奇數)。試驗版本中可以試驗各種各樣的新而酷的主意,有些會被證實是乙個錯誤,有些在下一版中會被完善。總之,你不能依賴這些版本中的介面(這也是我不在本文件中支援它們的原因,它們更新的太快了)
。在穩定版本中,我們可以期望介面保持一致,除了那些修改**中錯誤的版本。
如果你要支援多版本的核心,你需要編寫為不同核心編譯的**樹。可以通過比較巨集linux_version_code
和巨集kernel_version
在版本號為a.b.c
的核心中,該巨集的值應該為2^16×a+2^8×b+c
在上乙個版本中該文件還保留了詳細的如何向後相容老核心的介紹,現在我們決定打破這個傳統。對為老核心編寫驅動感興趣的讀者應該參考對應版本的lkmpg
,也就是說,2.4.x
版本的lkmpg
對應2.4.x
的核心,2.6.x
版本的lkmpg
對應2.6.x
的核心。
file operation結構體詳解
2012 4 26 每乙個裝置檔案都代表著核心中的乙個file結構體。該結構體在標頭檔案linux fs.h定義。注意,file結構體是核心空間的結構體,這意味著它不會在使用者程式的 中出現。它絕對不是在glibc中定義的file。file自己也從不在核心空間的函式中出現。它的名字確實挺讓人迷惑的。...
關於程式關於世界
首先,在學了1年多的軟體設計的基礎上,問下自己 程式是什麼?業務需求是什麼?程式有什麼用?什麼是演算法?什麼是資料庫?或許每個人的理解不同,會給出不同的答案。那麼自己的理解 程式是乙個讓計算機工作的流程,在程式寫好之後,計算機就會按照,程式設計師定義好流程在執行。其實很多時候,乙個程式的好壞,在於乙...
關於血液關於軟體
1 自然沉降法 將血袋垂直吊掛於4 2 冰箱內,使紅細胞自然下沉1 3d,或將血袋呈70 80 角立於冰箱,需用時,用一次性分漿器分出血漿,制得濃縮紅細胞。2 洗滌法 一般用生理鹽水反覆洗滌3 6次。經洗滌的紅細胞,除白細胞和血小板減少外,血漿蛋白也極少,紅細胞中殘存的血漿蛋白含量約為原總蛋白的1 ...