轉自:http://hi.baidu.com/zkheartboy/blog/item/6331d5cec964f60093457e0d.html
一個LINUX 核心模組編程的手記, 未寫完不斷更新中…
一 相關命令
0 查看系統裝載了哪些 核心模組
lsmod modulename
1 載入核心模組
insmod modulename
2 卸載核心模組
rmmod modulename
3 建立裝置檔案
mknod filename dev_type major_number minor_number
說明: 參數 filename 建立的裝置檔案名稱
dev_type 裝置檔案的類型,
c character device (字元裝置)
b block device (塊裝置)
兩種檔案類型的主要區別, block device 有 buffer
major_number 主要號碼 用於標記那個驅動可以使用此裝置(每一個驅動只有唯一的 )
minor_number 鏡像號碼 the driver to distinguish between the various hardware it controls(不能準確翻譯)
———————————————
例:
ls -l /dev/hda[1-3]
brw-rw—- 1 root disk 3, 1 Jul 5 2000 /dev/hda1
brw-rw—- 1 root disk 3, 2 Jul 5 2000 /dev/hda2
brw-rw—- 1 root disk 3, 3 Jul 5 2000 /dev/hda3
b 表示裝置類型
3 表示 major number
1,2,3 表示 minor number
———————————————-
4 查看程式執行過程( 一般用於 檢查程式用了哪些系統調用)
strace ./app
5 自動載入模組(調試好了再用, 要不系統容易掛)
depmod -a (檢查核心 依賴關係是否正確) 產生 /lib/modules/version/modules.dep 檔案
modprobe 在 insmod之前掛載 (核心模組也有依賴性~~) 可以編輯 /etc/modules.conf 讓系統啟動時載入 重要的,比如驅動
modprobe -a msdos (載入 於msdos有依賴關係的所有模組)
二 相關檔案
1 /proc/modules.
存放載入核心的資訊。 lsmod 就是讀的這個檔案
2/etc/modules.conf
讓系統啟動時載入 重要的,比如驅動
3/lib/modules/version/modules.dep
核心依賴關係 ( 猜的, 沒看)
4/var/log/message
我是用turbo 10 案頭版本 作的測試 所以 printk() 函數 輸出時不能顯示在我的kde的終端上
這個問題曾經鬱悶 我 20 分鐘, 後來 情急之下 查看 message檔案 才發現都printk輸出資訊都記錄在 這裡了, 我也夠笨的:(
當知道怎麼建立使用 "字元裝置檔案" 之後 就能寫自己的 printx函數了。也知道為什麼不能顯示在 kede的終端上了 :)
5 /proc/devices
已經使用的裝置檔案 裡面資料的結構是 major number device name
三 使用者空間 VS 核心空間 | 系統調用 (有趣)
核心可以訪問所有的資源,使用者程式也是如此, 只不過,要藉助驅動來訪問。
核心要管理所有的資源,他是使用者程式於硬體的一個橋樑。一個使用者程式運行過程當中, 他把通過系統調用,把資料傳送給核心空間, 然後核心處理這個請求, 之後又把結果返回給使用者空間。核心可以捕獲到所有的這些資訊, 你可以替換到原來的系統調用, 你可一
讓open打不開檔案, 你可以讓mkdir建立不了目錄!!
三 該死的MakeFile!!!
一開始看的所有相關的文章,幾乎都是基於2.4版本的,照著2。4版本的makefile寫,但是我的核心是2.6的, 所以我百試不爽,編譯完了不能載入,鬱悶了好久,差一點沒換掉核心,後來找到了2。6 的手冊才知道問題所在。
2.6 版本核心模組的MakeFile 編譯成.ko檔案
————————————————————
ifneq ($(KERNELRELEASE),)
obj-m:= test.o #模組名稱
else
KDIR:= /lib/modules/$(shell uname -r)/build #核心路徑
PWD:= $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
endif
————————————————————
2.4 版本核心模組的MakeFile 編譯成.o檔案
————————————————————
ARGET =test
WARN =-W -Wall -Wstrict-prototypes -Wmissing-prototypes
INCLUDE=-isystem /lib/modules/2.6.0-1/build/include #核心路徑
CFLAG =-O2 -DMODULE -D__KERNEL__ ${WARN} ${INCLUDE}
CC =gcc
${TARGET}.o: ${TARGET}.c
${CC} ${CFLAG} -c ${TARGET}.c
.PHONY: clean
clean:
rm -rf ${TARGET}.o
————————————————————
四 核心模組程式結構
————————————————-
/*
* hello-1.c - 簡單的hello the world 程式摘自手冊
*/
#include <linux/module.h>/* 所有的核心模組都需要的 */
#include <linux/kernel.h>/* Needed for KERN_ALERT */
//初始化程式,載入模組時執行
int init_module(void)
{
printk("<1>Hello world 1.n"); //本函數輸出資訊, 相當於使用者空間的printf
/*
* 返回非0值,表示出錯,核心不能被載入
*/
return 0;
}
//清除函數,卸載模組時執行
void cleanup_module(void)
{
printk(KERN_ALERT "Goodbye world 1.n");
}
——————————————————
2.6 版本有更多的書寫方法,比如加入載入參數, 加入核心說明等等,可以參考相關文檔
http://www.tldp.org/LDP/lkmpg/2.6/html/index.html
五 應用字元裝置檔案
5.1 資料結構
1 file_operations 在<linux/fs.h> 中定義
說明: 這個結構體中包含對裝置操作的函數指標, 可以通過他的配置和實現操作函數來完成對裝置的操作
把你不使用的入口 設為NULL。
—————————————————————————————-
struct file_operations {
struct module *owner;
loff_t(*llseek) (struct file *, loff_t, int);
ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t,loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int,unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t(*readv) (struct file *, const struct iovec *, unsigned long,loff_t *);
ssize_t(*writev) (struct file *, const struct iovec *, unsigned long,loff_t *);
ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t,void __user *);
ssize_t(*sendpage) (struct file *, struct page *, int, size_t,loff_t *, int);
unsigned long (*get_unmapped_area) (struct file *, unsigned long,unsigned long,
unsigned long,unsigned long);
};
—————————————————————————————-
靠,這傢伙也太難記了,我曾經3次看到這,就去論壇灌水了,不想往下看了。 今兒耐著性子把他看完,才發現自己是個笨蛋!
在新的驅動程式中,你可能會看到一更簡單,更友好的給file_operations 賦值的方式,看下面的這個結構,手冊上說這玩意是
gcc extension gcc 擴充 (GNU c), 是這個意思吧, 反正我理解的就是,gcc在編譯它的時候 先把下面這個玩意翻譯正標準的c,然後再編譯。 哀,可憐的 c標準!不過話又說回來,這東西除了linux別的也沒什麼用處。
——————————————
struct file_operations fops = {
read: device_read, //對應 相應的函數
write: device_write,
open: device_open,
release: device_release
};
——————————————
手冊上說 下面的這個是 C99 特性寫法!(這玩意BSD 也用不了吧? 為什麼還要這麼亂!) 注意:低版本的 gcc
可能會不正常編譯地!因為它超出了GNU C 標準
(http://www-900.ibm.com/developerWorks/cn/linux/l-c99/index.shtml C99
標準參考)
——————————————
struct file_operations fops = {
.read = device_read, //對應 相應的函數
.write = device_write,
.open = device_open,
.release = device_release
};
——————————————
俺選擇 的是第一種寫法!
2 *file* 結構 在 <linux/fs.h> 中定義
說明:
* 它不同於FILE (glibc 中定義的)
* 它是核心級的結構體
* 它表示抽象的 'file'
* 它不是磁碟上的那個 file
* 驅動用結構體來給這個東西賦值,****** 完善理解 *******
* 指向它的指標類型 為 filp , 也可以直接寫成 struct file file
5.2 相關函數
1 註冊裝置函數
<linux/fs.h>
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
還記得之前寫的建立裝置檔案的命令吧,
參數說明: major: 它就是 major number 對應唯一的驅動,記住!
name: 它就是 filename 可以在 /proc/devices 裡面找到它(叫device name 更好點)
fops: file_operations 結構體, 這個可以理解為操作裝置的 核心入口,可以用上面說的兩種方法去填充它
函數返回: 1 major 為 0: 動態分配的 major number 值
2 major 為 指定值:
3 major 為特殊數值:
特殊說明: 因為一個驅動只能有一 個 major值,所以有以下三種建立方法
1 先建立裝置檔案,你可以分配一個固定的major值
2 動態分配 major值, 讀取 /proc/devices 檔案 找到major值, 用指令碼建立(在使用者空間建立裝置檔案)
3 動態分配 major值,使用系統調用函數,直接建立裝置檔案 (在核心空間執行)
第 1 種方法不可取, 因為你不可預知 是否將來要 用到這個major number
第 2 種方法,需要在使用者空間建立
第 3 種方法最好,可以動態建立,銷毀裝置檔案,不過麻煩
2 取消註冊裝置函數
關於:counter