Linux设备驱动程序 第三版 读书笔记(一)
?Linux設備驅動程序 第三版 讀書筆記(一)
Bob Zhang
2017.08.25
編寫基本的Hello World模塊
#include <linux/init.h> #include <linux/module.h> // 聲明模塊的許可證書 MODULE_LICENSE("Dual BSD/GPL"); static __init hello_init(void) {// KERN_ALERT表示的是日志級別printk(KERN_ALERT "Hello, world\n"); return 0; } static __exit void hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel world\n"); } // 注冊模塊初始化函數,在模塊安裝到內核時會被調用 module_init(hello_init); // 注冊模塊的退出函數,在模塊從內核移除時會被調用 module_exit(hello_exit);
需要注意的地方是,內核代碼不支持浮點數。
模塊的編譯
1 obj-m += hello.o但是如果需要編譯的模塊由多個文件組成,則可以使用下面的代碼:
1 obj-m := module.o 2 module-objs := file1.o file2.o?模塊裝載
一般使用insmod對模塊進行裝載:
1 insmod hello.ko但是insmod不會對要裝載的模塊的依賴做檢查,如果模塊引用了內核中沒有的符號,則會報“unresolved symbol”的錯誤。如果要想檢查模塊的依賴再裝載,可以使用modprobe命令。
modprobe, 如同 insmod, 加載一個模塊到內核. 它的不同在于它會查看要加載的模塊, 看是否它引用了當前內核沒有定義的符號. 如果發現有, modprobe 在定義相關符號的當前模塊搜索路徑中尋找其他模塊. 當 modprobe 找到這些模塊( 要加載模塊需要的 ), 它也把它們加載到內核.如果你在這種情況下代替以使用 insmod , 命令會失敗, 在系統日志文件中留下一條 " unresolved symbols "消息.
模塊查看
當模塊裝載好之后,可以使用lsmod檢查模塊是否真的裝載到內核中了:
1 #查看所有加載到內核的模塊 2 lsmod 3 4 #查看指定的模塊,如hello.ko 5 lsmod | grep hellolsmod 程序生成一個內核中當前加載的模塊的列表. 一些其他信息, 例如使用了一個特定模塊的其他模塊, 也提供了. lsmod 通過讀取 /proc/modules 虛擬文件工作. 當前加載的模塊的信息也可在位于 /sys/module 的 sysfs 虛擬文件系統找到.
模塊卸載
1 #卸載hello.ko 2 rmmod hello.ko我們可以使用rmmod工具從內核中移除模塊。注意,如果內核認為模塊仍然在使用狀態(例如,某個程序正打開由該模塊導出的設備文件),或者內核被配置為禁止移除模塊,則無法移除該模塊。配置內核并使得內核在模塊忙的時候仍能“強制”移除模塊也是可能的。但是,如果讀者在某種情況下希望利用這種特性,則重新引導系統可能是更加合適的做法。?
內核符號表
Linux內核頭文件提供了一個方便的方法來管理符號對模塊外部可見性,從而減少了可能造成的名字空間污染(名字空間中的名稱可能會和內核其他地方定義的名稱發生沖突),并且適當隱藏信息。如果一個模塊需要向其他模塊導出符號,則應該使用下面的宏。
EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name);這兩個宏均用于將給定的符號導出到模塊外部。_GPL版本使得要導出的模塊只能被GPL許可證下的模塊使用。符號符號必須在模塊文件的全局部分導出,不能在函數中導出,這是因為上面這兩個宏江被擴展成為一個特殊的變量聲明,而該變量必須是全局的。該變量將在模塊許可執行文件的特殊部分(即一個“ELF段”)中保存,在裝載時,內核通過這個段來尋找模塊導出的變量。
其他宏定義
大部分內核代碼中都要包含相當數量的頭文件,以便獲得函數、數據類型和變量的定義。我們將在用到這些文件時向讀者介紹,但是有幾個頭文件是專門用于模塊的,因此必須出現在每個可裝載的模塊中。故而,所有的模塊代碼中都包含下面兩行代碼:
1 #include <linux/module.h> 2 #include <linux/init.h>module.h包含有可裝載模塊需要的大量符號和函數的定義。包含init.h的目的是指定初始化和清理函數,就像我們在hello模塊中看到的那樣。大部分的模塊還包括了“moduleparam.h”頭文件,這樣我們就可以在裝載模塊的時候向模塊傳遞參數。接下來介紹一些常用的宏。
盡管不是嚴格要求,但模塊應該制定代碼所使用的許可證。為此我們只需要包含MODULE_LICENSE行:
1 MODULE_LICENSE("GPL");內核能夠識別的許可證有“GPL”(任一版本的GNU(GNU's Not Unix)通用公共許可證)、“GPL v2”(GPL版本2)、“GPL and additional rights(GPL及附加權利)”、“Dual BSD/GPL(BSD/GPL雙重許可證)”、“Dual MPL/GPL(MPL/GPL雙重許可證)”以及“Proprietary(專有)”。如果一個模塊沒有顯示地標記為上述內核可識別的許可證,則會被假定是專有的,而內核裝載這種模塊就會被“污染”。
可在模塊中包含的其他描述性定義包括:
// 描述模塊作者 MODULE_AUTHOR(BobZhang<zhangbob@email.com>); // 簡短說明模塊用途 MODULE_DESCRIPTION("This is a hello world demo module."); // 代碼修訂號;有關版本字串的創建慣例,請參考<linux/module.h>中的注釋 MODULE_VERSION(version); // 模塊的別名 MODULE_ALIAS("hello_world"); // 告訴用戶空間模塊所支持的設備 MODULE_DEVICE_TABLE(device_table);上述MODULE_聲明可出現在源文件中源代碼函數以外的任何地方。但新近的內核編碼習慣是將這些聲明放在文件的最后。?
模塊參數
內核提供一種方式傳遞參數給模塊,那就是在運行insmod或modprobe命令時,將要傳遞給模塊的參數給出,而modeprobe還可以從他的配置文件(/etc/modprobe.conf)中讀取參數值。這兩個命令可在命令行接受幾種參數類型的賦值。為了演示這種功能,我們假定對前面的hello模塊做一些必要的增強。我們添加了兩個參數:一個是整數值,其名稱為howmany;另一個是字符串,名稱為whom。在裝載這個增強的模塊時,將相whom問候howmany次。這樣我們可用下面的命令來裝載該模塊:
1 insmod hello.ko howmany=10 whom="Bob"上面這條命令的效果會讓hello模塊打印10次“hello, Bob”。
更改后的hello模塊如下:
#include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h>static char *whom = "world"; static int howmany = 1; module_param(howmany, int, S_IRUGO); module_param(whom, charp, S_IRUGO);// 聲明模塊的許可證書 MODULE_LICENSE("Dual BSD/GPL"); static __int hello_init(void) { int count = 0;// KERN_ALERT表示的是日志級別for(; count < howmany; ++count)printk(KERN_ALERT "hello, %s\n", whom); return 0; } static __exit void hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel world\n"); } // 注冊模塊初始化函數,在模塊安裝到內核時會被調用 module_init(hello_init); // 注冊模塊的退出函數,在模塊從內核移除時會被調用 module_exit(hello_exit);內核支持的模塊參數類型如下:
| 類型 | 描述 |
| bool invbool | 布爾值(取true或false),關聯的變量應該是int型。invbool類型反轉其值,也就是說,true值變成false,而false變成true。 |
| charp | 字符指針值。內核會為用戶提供的字符串分配內存,并相應設置指針。 |
| int long short uint ulong ushort | 具有不同長度的基本整數類型。以u開頭的類型用于無符號值。 |
?
?
?
?
?
?
?
?
?
?
?
?
模塊裝載器也支持數組參數,賊提供數組值時用逗號劃分各數組成員。要聲明數組參數,需要使用下面的宏:
1 module_param_array(name, type, num, perm);其中,name是數組的名稱(也就是參數的名稱),type是數組原書的類型,num是一個證書變量,而perm是常見的訪問許可值。如果裝載時設置數組參數,則num會被設置為用戶提供的值的個數。模塊裝載器會拒絕接受超過數組大小的值。
module_param中的最后一個成員是訪問許可值,我們應使用<linux/stat.h>存在的定義。這個值用來控制水能夠訪問sysfs中對模塊參數的表述。如果perm被設置為0,就不會有對應的sysfs入口項;否則,模塊參數會在/sys/module中出現,并設置為給定的訪問許可。如果參數使用S_IRUGO,則任何人均可讀取該參數,但不能修改;S_IRUGO|S_IWUSR允許root用戶修改該參數。注意,如果一個參數通過sysfs而被修改,則如同模塊修改了這個參數的值一樣,但是內核不會以任何方式通知模塊。大多數情況下,我們不應該讓模塊參數是可寫的,除非我們打算檢測這種修改并作為相應的動作。
在用戶空間編寫驅動
可以在用戶空間編寫驅動,欲知詳情請google或者bing搜索。
當前進程
雖然內核模塊不像應用程序那樣順序執行,然而內核執行的大多數操作還是和某個特定的進程相關。內核代碼可以通過訪問全局項current來獲得當前進程。current在<asm.current.h>中定義,是一個指向struct task_struct的指針,而task_struct結構在<linux/sched.h>文件中定義。current指針指向當前正在運行的進程。在open、read等操作系統調用的執行過程中,當前進程指的是調用這些系統調用的進程。如果需要,內核代碼可以通過current獲得與當前進程相關的信息。
1 printk(KERNE_INFO "The process is \"%s\" [pid %i]\n", 2 current->comm, current->pid);存儲在current->comm成員中的命令是當前進程所執行的程序文件的基本名稱(base name),如果必要,會裁剪到15個字符以內。
?
未完待續~
一切偉大的思想和行動都有一個微不足道的開始。
Any great thoughts and actions has a small beginning.
轉載于:https://www.cnblogs.com/zhanghang-BadCoder/p/7427641.html
總結
以上是生活随笔為你收集整理的Linux设备驱动程序 第三版 读书笔记(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 正则重温(学习笔记)
- 下一篇: VS 替换 行