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