Linux設備驅動:一,簡介

內核 角色劃分:

  • 進程管理:內核創建和銷燬進程;調度器控制進程如何共享CPU。
  • 內存管理:內核爲每一個進程都在有限的可用資源上建立一個虛擬地址空間。
  • 文件系統:結構化的文件系統。
  • 設備控制:設備驅動。
  • 網絡:所有的路由和地址解析問題都在內核中實現。
每個模塊可以動態鏈接到運行中的內核中,通過 insmod 程序,以及 rmmod程序。



3 類 驅動
  • 字符設備
  • 塊設備
  • 網絡接口

鏈接一個模塊到內核:


用戶空間 內核空間

模塊在內核空間中運行,應用程序在用戶空間運行。

應用程序存在於虛擬內存中, 有一個非常大的堆棧區.

內核, 相反, 有一個非常小的堆棧;

常常, 當你查看內核 API 時, 你會遇到以雙下劃線(__)開始的函數名. 這樣標誌的函數名通常是一個低層的接口組件, 應當小心使用. 本質上講, 雙下劃線告訴程序員:" 如果你調用這個函數, 確信你知道你在做什麼."

內核代碼不能做浮點算術.

modprobe工具:
如同insmod,但它會查看要加載的模塊,看它是否引用了當前內核沒有定義的符號。

使用 堆疊 來劃分模塊成不同層,這有助於通過簡化每一層來縮短開發時間。


輸出符號給其他模塊使用:
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);

用戶空間驅動的好處
  • 完整的 C 庫可以連接. 驅動可以進行許多奇怪的任務, 不用依靠外面的程序(實現使用策略的工具程序, 常常隨着驅動自身發佈).
  • 程序員可以在驅動代碼上運行常用的調試器, 而不必走調試一個運行中的內核的彎路.
  • 如果一個用戶空間驅動掛起了, 你可簡單地殺掉它. 驅動的問題不可能掛起整個系統, 除非被控制的硬件真的瘋掉了.
  • 用戶內存是可交換的, 不象內核內存. 一個不常使用的卻有很大一個驅動的設備不會佔據別的程序可以用到的 RAM, 除了在它實際在用時.
  • 一個精心設計的驅動程序仍然可以, 如同內核空間驅動, 允許對設備的並行存取.
  • 如果你必須編寫一個封閉源碼的驅動, 用戶空間的選項使你容易避免不明朗的許可的情況和改變的內核接口帶來的問題.
用戶空間驅動的缺點
  • 中斷在用戶空間無法用. 在某些平臺上有對這個限制的解決方法, 例如在 IA32 體系上的 vm86 系統調用.
  • 只可能通過內存映射 /dev/mem 來使用 DMA, 而且只有特權用戶可以這樣做.
  • 存取 I/O 端口只能在調用 ioperm 或者 iopl 之後. 此外, 不是所有的平臺支持這些系統調用, 而存取/dev/port可能太慢而無效率. 這些系統調用和設備文件都要求特權用戶.
  • 響應時間慢, 因爲需要上下文切換在客戶和硬件之間傳遞信息或動作.
  • 更不好的是, 如果驅動已被交換到硬盤, 響應時間會長到不可接受. 使用 mlock 系統調用可能會有幫助, 但是常常的你將需要鎖住許多內存頁, 因爲一個用戶空間程序依賴大量的庫代碼. mlock, 也, 限制在授權用戶上.
  • 最重要的設備不能在用戶空間處理, 包括但不限於, 網絡接口和塊設備.

insmod
modprobe
rmmod

用戶空間工具, 加載模塊到運行中的內核以及去除它們.


#include <linux/init.h>
module_init(init_function);
module_exit(cleanup_function);
指定模塊的初始化和清理函數的宏定義.


__init
__initdata
__exit
__exitdata
函數( __init 和 __exit )和數據 (__initdata 和 __exitdata)的標記, 只用在模塊初始化或者清理時間. 爲初始化所標識的項可能會在初始化完成後丟棄; 退出的項可能被丟棄如果內核沒有配置模塊卸載. 這些標記通過使相關的目標在可執行文件的特定的 ELF 節裏被替換來工作.

#include <linux/sched.h>
最重要的頭文件中的一個. 這個文件包含很多驅動使用的內核 API 的定義, 包括睡眠函數和許多變量聲明.

struct task_struct *current;
當前進程.
current->pid
current->comm
進程 ID 和 當前進程的命令名.

obj-m
一個 makefile 符號, 內核建立系統用來決定當前目錄下的哪個模塊應當被建立.


/sys/module
/proc/modules
/sys/module 是一個 sysfs 目錄層次, 包含當前加載模塊的信息. /proc/moudles是舊式的, 那種信息的單個文件版本. 其中的條目包含了模塊名, 每個模塊佔用的內存數量, 以及使用計數. 另外的字串追加到每行的末尾來指定標誌, 對這個模塊當前是活動的.

vermagic.o
來自內核源碼目錄的目標文件, 描述一個模塊爲之建立的環境.


#include <linux/module.h>
必需的頭文件. 它必須在一個模塊源碼中包含.

#include <linux/version.h>
頭文件, 包含在建立的內核版本信息.

LINUX_VERSION_CODE
整型宏定義, 對 #ifdef 版本依賴有用.


EXPORT_SYMBOL (symbol);
EXPORT_SYMBOL_GPL (symbol);
宏定義, 用來輸出一個符號給內核. 第 2 種形式輸出沒有版本信息, 第 3 種限制輸出給 GPL 許可的模塊.


MODULE_AUTHOR(author);
MODULE_DESCRIPTION(description);
MODULE_VERSION(version_string);
MODULE_DEVICE_TABLE(table_info);
MODULE_ALIAS(alternate_name);
放置文檔在目標文件的模塊中.


module_init(init_function);
module_exit(exit_function);
宏定義, 聲明一個模塊的初始化和清理函數.


#include <linux/moduleparam.h>
module_param(variable, type, perm);
宏定義, 創建模塊參數, 可以被用戶在模塊加載時調整( 或者在啓動時間, 對於內嵌代碼). 類型可以是 bool, charp, int, invbool, short, ushort, uint, ulong,或者 intarray.


#include <linux/kernel.h>
int printk(const char * fmt, ...);
內核代碼的 printf 類似物.




發佈了98 篇原創文章 · 獲贊 74 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章