Linux 設備驅動開發實例

編譯和運行

驅動編譯要用到kernel的Makefile文件 — — 也就是源碼樹的編譯系統。所以,源碼須要被配置和編譯,以ubuntu自帶的源碼爲例:linux

編譯外部模塊(.ko)的編譯命令是:程序員

make -C <path_to_kernel_src> M=mak**eC<pathtokernelsrc>M=PWDubuntu

也就是進入到kernel目錄,利用kbuild系統來編譯驅動文件。obj-m 告訴編譯系統須要編譯成一個module(.ko),foo.o代表須要源文件是foo.c或者foo.S,若是驅動模塊包含多個文件(如: foo_main.c, foo_common.c),寫法以下:併發

kbuild將編譯$(foo-y)列出的全部文件,合併產生 foo.ko函數

在編譯期間,模塊的Makefile會被kbuild屢次讀取,所以建議使用$(KERNELRELEASE)來區分Makefile的使用階段,優化後的Makefile以下:oop

第一次運行make的時侯,$(KERNELRELEASE) 爲空,所以,Makefile的 'else' 內容首先被讀取,而後,執行 *‘make -C .....’*, 執行過程當中,會回讀Makefile文件,此次, 'ifneq' 條件知足,兩次走不一樣的路徑,編譯系統配置不一樣的變量參數。測試

若是,不使用 $(KERNELRELEASE) 區分的話,每次編譯系統都會設置全部的變量和規則,可能會與kernel的Makefile變量或者規則衝突,所以,建議在(KERNELRELEASE)爲空的狀況下,配置driver專用的變量和規則,除了使用(KERNELRELEASE)爲空的狀況下,配置drive**r專用的變量和規則,除了使用(KERNELRELEASE)外,kernel還提供了一些其它的作法, 更多的kernel 編譯系統信息,請參考kernel源碼下的 「Documentation/kbuild/」優化

驅動模塊運行相關命令ui

  • insmod foo.ko —— 加載driver 到kernel去運行。
  • rmmod foo —— 從kernel 移除driver.
  • lsmod —— 查看當前kernel 運行的模塊。

字符設備

字符設備驅動實際上就是實現一個文件接口,讓設備文件能夠像一個普通文件那樣來訪問,這樣應用程序就能夠使用libc庫的'文件IO API(open/write/read/close 系列函數)' 來訪問驅動程序,與驅動交換數據,所以,它的核心就是實現文件系統的接口 -- 文件操做。url

程序入口

宏內核與微內核的一個最大區別就是驅動程序的運行空間。微內核系統,驅動程序做爲一個應用程序,運行在用戶空間,它的入口就是應用程序的‘main’函數。 Linux做爲一個宏內核系統,它的驅動程序與內核是一體的,運行在內核空間,它的入口是 ‘module_init’,‘module_exit’則是對應的退出函數,它們必定是成對出現的。

foo_init 執行了最基本的字符設備操做:使用 cdev_add 添加一個 'cdev'到字符設備列表(實際上是一個map結構), 這樣就把foo這個字符設備託付給kernel進行管理了,當應用程序操做相應的設備文件時,kernel能調度到foo驅動程序。

foo_exit 必定要使用 cdev_del 從列表裏面刪除設備,否則,當kernel從列表裏面查找到 cdev時,返回的將是「過期」的指針,使用它來 callback相應操做時,就會出現空指針異常,致使kernel會掛掉。切記!foo_initfoo_exit 必定要成對使用,執行相反的操做。

bug 實例:

  1. foo_exit 不執行 cdev_del 函數。
  2. insmod foo.ko -- OK。
  3. 應用程序對設備文件讀寫 -- OK。
  4. rmmod foo -- OK。
  5. insmod foo.ko -- OK。
  6. 應用程序對設備文件讀寫 -- core dump。

rmmod foo’時,會調用 foo_exit,可是,程序員忘了執行 cdev_del 函數,致使 foo.cdev 的指針沒有被刪除而變成了一個空指針,它仍然在字符設備列表裏面。 當第二次插入foo.ko後, 讀寫該設備時,Kernel找的是舊的 foo.cdev 空指針,用它調用相應的文件操做時,就發生了空指針的 core dump 錯誤。

文件操做

setup_dev: 註冊當前的設備的文件操做函數,當應用程序操做設備文件時,調用到對應的驅動函數。與用戶空間交換數據,copy_from/to_user,這兩個函數返回0表示函數執行成功。

  • copy_from_user: 把用戶寫入的數據copy驅動數據buf保存起來。
  • copy_to_user: copy驅動數據buf到 用戶讀取數據的buf。

應用程序與字符驅動的交互流程

  1. 建立設備文件 -- sudo mknod /dev/foodev c 500 0
  2. 修改設備文件權限 -- sudo chmod 766 /dev/foodev
  3. 應用程序使用open函數打開設備文件。
  4. kernel根據文件類型(字符設備文件)找到字符設備列表,並根據設備號(Major, Minor),找到對應的設備驅動模塊。
  5. 調用設備驅動的open函數 foo_open 。
  6. 應用程序調用 read/write函數來讀寫設備文件。
  7. 驅動調用 foo_read/write並使用copy_from/to_user來交換數據。

常見問題

Q: 讀寫設備文件時,write或者 read函數返回0,不能讀寫數據 ? A: 這類設備文件讀寫失敗問題,頗有多是權限問題,確認下文件讀寫權限,其次是數據是否符合驅動的要求。

塊設備

塊設備指的是存儲設備,塊設備驅動就是存儲驅動如:HD,SSD。Linux 用 Block 子系統對它們進行管理,把應用層的IO讀寫請求,轉變爲Request ,傳給相應的會設備驅動。驅動流程比較簡單:

register_blkdev → alloc_disk → 處理request

Q: 文件系統與Block子系統的關係? A: Block子系統主要是提供最底層的數據讀寫,也就是raw io,文件系統使用它進行IO操做。

註冊

註冊塊設備(主設備號)

註冊設備(MAJOR,MINOR)

添加磁盤

這個磁盤會出如今 /dev 目錄下面, 本例是 /dev/frd0,用戶能夠對設備文件進行格式化,分區等磁盤相關的操做。如: ‘mkfs.ext2 /dev/frd0’, ‘mount /dev/frd0 /mnt’。

初始化請求隊列

處理設備請求

kernel 提供了一些宏來幫助遍歷請求列表。對請求的處理策略,就是Block驅動最核心最精華的部分,開發者得根據設備的物理特性來提升訪問效率,解決併發擁堵等問題。 *fr_queue_rq()* -- ‘**請求隊列’**處理函數,在初始化請求隊列時設置,Loop處理每一個請求:

*fr_transfer()* -- 物理設備讀寫數據,根據請求的上下文內容(context),進行底層數據傳輸,這裏就是最底層的IO通信了,驅動根據物理設備的接口協議來進行數據的讀寫。

塊設備驅動測試

執行上面命令後,frd_data_r 和 frd_data_w的內容應該是同樣的。

以上就是良許教程網爲各位朋友分享的Linux 設備驅動開發實例。 以上就是良許教程網爲各位朋友分享的Linux相關知識。

相關文章
相關標籤/搜索