linux驅動入門

來源:互聯網
上載者:User

 

用來防止使用者程式直接存取核心中關鍵性資料結構和硬體裝置是驅動程式的主要目的之一,所以,一個設計精良的驅動通常應該隱藏了硬體的複雜性和多變性。例如,一個程式寫資料到磁碟時,只需要開啟一個檔案並執行寫命令即可,而不必瞭解磁碟中的每個扇區的大小是512位元組或者是1024位元組,細節性的問題則交由驅動程式處理。此外,驅動程式還可以隱藏硬體的多變性(可能來自於不同廠家,甚至同一廠家不同型號)並給使用者提供一個統一的提供者。這也是Unix/Linux信條之一“一切皆檔案”的賴以存在的基礎。 1、可載入模組(loadable module)Linux採取了“宏核心”的結構,並附帶一個設計精良的介面以實現在系統運行時動態載入或移除驅動模組。這種富有彈性的設計,為終端使用者,甚至也為程式的開發過程帶來了極大的便利。基於此功能,在開發驅動的過程中,開發人員不用在每一次驅動程式的修改後重啟系統就能對其進行測試。當然,驅動程式也可以以靜態方式編譯進核心,而且,許多關鍵性的驅動也需要這樣編譯。比如,對於無盤工作站而言,由於系統啟動最初就需要通過網卡從其它共用檔案系統中載入所需的資源,此時必須將網卡驅動靜態編譯進核心,因為可載入模組都是系統啟動後才進行動態載入。通常使用啟動指令碼裝載動態驅動模組,當然,也可以使用相關命令在需要時再進行載入。此外,核心也可以在某個服務需要某個特殊模組時自動請求載入所需的模組。雖然前文中一直稱驅動程式為可載入模組,但核心模組並沒有確定的術語,硬體驅動(device drivers)、可載入核心模組(LKM,loadable kernel modules)、核心模組(kernel modules)、可載入模組(loadable modules)、驅動模組(driver modules)和模組(modules)等都常用來表示可動態載入進核心的硬體驅動,後文中則不加區別的使用它們。 2、硬體驅動結構儘管Linux/Unix驅動程式模組的開發一直處於不斷地演化中,但其基本結構並沒有太大改變。硬體裝置大體上可分為兩大類:字元裝置和塊裝置。
  • 字元裝置是以串列流式資料序列進行資料存取的裝置,字元裝置驅動負責實現這種行為;通常字元裝置驅動至少需要實現 open、 close、read和 write等系統調用。常見的字元裝置如控制台( /dev/console )和串口( /dev/ttyS0 )。
  • 塊裝置通常是可編址的,其資料存取也通常以固定大小的資料區塊進行,但資料區塊的存入位置則可能是隨機的。在大部分Unix系統中, 一個塊裝置傳送一個或多個長度經常是512位元組(或者其2次冪倍)的整塊資料,但Linux允許一次傳遞任意位元組的資料,其跟字元裝置的區別僅在於核心內部對資料的管理方式上和驅動程式的介面實現上有所不同。磁碟是常見的塊裝置。
3、一個驅動程式的例子因為Linux支援可載入式硬體驅動,所以很容易構建出一個關於簡易驅動架構來說明驅動程式的結構。下面就是這樣一個關於字元型裝置驅動程式的例子:

/* Example Minimal Character Device Driver */
#include
<linux/module.h>
static int __init hello_init(void)
{
printk("Hello Device Driver World!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk("Goodbye, Cruel World!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_AUTHOR("Marion");
MODULE_DESCRIPTION("An example of device driver!");
MODULE_LICENSE("GPL");
/* End the hello*/

上面這個例子雖然短小,但卻充分具備了讓核心載入或卸載以及初始化或退出的程式結構。不同於標準的二進位可執行程式,裝置驅動是一種特殊的二進位模組,它不能簡單的通過shell執行。2.6系列核心的模組通常是核心對象 (kernel object)格式,這些模組在完成編譯後通常以.ko為尾碼。建立核心對象步驟和編譯選項非常複雜,本文僅描述其大體過程,以協助讀者簡單瞭解核心對象的構建步驟。 4、模組構建基礎驅動程式的編譯必須針對於特定的核心進行。儘管在另一個不同的核心上編譯的驅動模組也可以運行,但如果不確定此模組是否依賴於編譯時間的核心的某些特性時,將會給後來運行過程來帶來很大風險。因此,最穩妥的辦法還是基於某核心自身的代碼樹(Source tree)構建核心對象,這也可以保證在開發人員改變了核心的配置後,驅動也會在新配置的核心的基礎上進行重建。如果需要在不同的核心上構建核心對象,則必須確保驅動程式構建時的配置所使用的編譯選項、引用核心標頭檔的位置和核心配置選項在驅動實際啟動並執行核心上做了同樣的配置。為了基於前文中的例子構建驅動程式,大致要經過以下幾個步驟:(1)在核心源碼目錄中的.../driver/char目錄中建立一個名為examples的子目錄;
(2)在核心設定檔中添加一個功能表項目,以允許在編譯核心時可以選擇編譯examples(編譯進核心或編譯成模組);
(3)在.../driver/char/Makefile檔案中添加一個子目錄項examples以對應於前述第二步驟的功能表項目;
(4)為.../driver/char/examples目錄建立一個makefile檔案,在其中為前述第二個步驟中建立的功能表項目添加hello.o模組對象以進行編譯;(5)添加驅動程式源碼;下面詳細描述前面的幾個步驟:首先,在核心源碼目錄的.../driver/char目錄中建立examples子目錄,而後再建立兩個檔案:一個是前面例子中的驅動源碼,一個是為其建立的makefile檔案。makefile檔案非常簡單,其內容只有如下一行即可:
obj-$(CONFIG_EXAMPLES) += hello.o添加功能表項目至核心組態工具的過程可能稍有些繞。首先需要在.../driver/char/Kconfig檔案中添加一個"config"項以啟用前文中的examples配置項。添加位置是menu "Character devices"一行的後面,新增內容如下:config EXAMPLES
tristate "Enable Examples"
default m
---help---
   Enable compilation option for driver examples而後回到核心目錄中,運行核心編譯命令make gconfig(需要xwindow的支援)後,可以Device Drivers->Character drivers找到我們添加的“Enable Examples”項,預設為“-”(通過default項指定),即編譯為核心模組。如果指定為“對號”則表示以靜態方式編譯進核心;如果為空白,則表示不編譯此項。如所示:接下來還需要在.../drivers/char/Makefile檔案中添加一個選項,以指示核心編譯器在我們選擇了 CONFIG_EXAMPLES時會到examples子目錄中編譯hello1模組。這需要在 “obj-$(CONFIG_IPMI_HANDLER)      += ipmi/”一行附近添加如下行:obj-$(CONFIG_EXAMPLES)          += examples/至此,此樣本驅動構建基礎結構已經完成,而且會在核心編譯過程中自動選擇此項進行編譯了。在執行完前面的"make gconfig"命令後,此時再執行如下命令即可完成驅動模組的編譯。# make modules

CHK     include/linux/version.h
CHK     include/linux/utsrelease.h
CC [M] drivers/char/examples/hello.o
Building modules, stage 2.
MODPOST
CC      drivers/char/examples/hello.mod.o
LD [M] drivers/char/examples/hello.ko如果您的編譯過程顯示有如上資訊,則表示核心模組編譯完成。接下來就可以使用如下命令安裝剛剛編譯完成的核心模組:
# make modules_install使用此種方式安裝時,安裝過程會重新安裝所有已編譯的核心模組,包括此前編譯的其它模組,而這並非是必須的。在一個通過標準方式安裝的Linux系統上,核心模組通常位於/lib/modules/<kernel-version>/…之中,其中的<kernel-version>即當前系統運行中的核心版本號碼,並且此目錄的結構組織方式跟核心原始碼樹的結構是類似的。通常使用“make modules_install”命令安裝的模組就位於此目錄中。因此,在單獨安裝某個或某些核心模組時,可以通過在此目錄中建立跟編譯時間核心源碼樹中一樣的核心模組驅動相關的目錄,並把編譯完成的*.ko檔案複製到建立的對應目錄中來實現。5、載入/卸載核心模組安裝完成後,便可以手動載入或卸載這些模組了,這可以使用modprobe實現。我們首先去載入hello模組。# modprobe hello
# tail -1 /var/log/messages
Sep 14 22:06:23 localhost kernel: Hello Device Driver World!此模組在載入時會調用模組初始化函數,程式中使用module_init()宏(macro)來指定的模組初始化函數,如 module_init(hello_init)。在此模組中,初始化函數僅用來列印一行資訊至系統日誌,資訊內容是在hello_init()中定義好的。在實際驅動程式編寫中,初始化函數常用來執行資源分配及硬體裝置初始化。接下來可以使用lsmod命令以格式化列表的形式顯示系統中載入的所有模組。如果其中有hello模組出現則表示前面的載入是成功的。例如:# lsmod
Module                  Size Used by
hello                   5632 0
ipv6                  274208 18
autofs4                25092 2
i2c_core               25344 1 i2c_piix4
…………其中Used by一列表示當前模組正在被使用的資訊,以及依賴於當前模組的其它模組。如最後一行表示i2c_piix4模組依賴於i2c_core模組。核心模組的卸載可以通過使用modprobe的-r選項來實現。# modprobe -r hello
# tail -1 /var/log/messages
Sep 14 22:14:33 localhost kernel: Goodbye, Cruel World!hello模組退出時會調用exit例行函數,這使用module_exit()宏來實現。其工作方式類似前面的載入過程。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.