歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> 關於Unix >> Linux內核模塊開發(筆記)

Linux內核模塊開發(筆記)

日期:2017/3/6 14:26:08   编辑:關於Unix
個人筆記。。在不放過來都快找不到了。有空還得好好整理一下了。 調試方法 printk() 是用來調試內核最常用的一種技術,他打印的信息會輸出在 dmesg 中,所以調試前最好使用 dmesg -c 來清掉以前 dmesg 的信息。 使用的例子如下:

  個人筆記。。在不放過來都快找不到了。有空還得好好整理一下了。

  調試方法

  printk() 是用來調試內核最常用的一種技術,他打印的信息會輸出在 dmesg 中,所以調試前最好使用 dmesg -c 來清掉以前 dmesg 的信息。 使用的例子如下:

  printk(KERN_DEBUG "Here i am:%s:%d\n", FUNCTION, LINE);

  可以打印的級別可以看看 linux/kernel.h 中的定義。 strace 這個命令超級強大,可以顯示程序所有的系統調用,還可以顯示調用時使用的參數。 但這個時候不需要麻煩的配置就可以直接使用,但不能象 gdb 調試 c 程序一樣,所以內核為我們提供了一個 kdb ,可以支持動態修改變量,斷點設置,單步執行

  kernel oops messages

  這是內核開發時常會出現的一個錯誤信息。主要原因是由於 NULL 指針引用,和其它不正常的指針操作引起的。這時 oops 會顯示故障時的處理器信息, 模塊 CPU 寄存器內容,頁描述符表的位置之類的信息。

  內核模塊簡單介紹

  模塊是工作在內核空間的 模塊實際是目標文件(由函數和數據結構組成),不象普通程序有個鏈接的過程,不能獨立運行,只能在運行時鏈接到系統做為內核的一部分運行,從面擴展內核功能 內核模塊會占用內核空間的內存,所以會影響內存使用,它還會修改內核中的一些內容,所以容易造成系統掛掉。在內核中需要維護符號表。並且內核之間有依賴性。

  最簡單的內核模塊

  注:如果是 redhat 安裝的話,需要安裝 kernel-devel 才能寫內核模塊,如果是自己編譯內核,記的不要刪除源碼,不然沒法開發模塊。

#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> /* __init 的標記是內核模塊的入口,這個函數加載完後就會釋放內存空間 */ static int __init hello_init(void) { printk(KERN_INFO "Hello world"); /* 打印的信息會出現在 dmesg 中 釋放*/ return 0; /* 返回 0 是正常 */ } /* __exit 的標記是退出內核模塊,當這個模塊卸載時會執行 */ static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye world"); } /* 下面這二個是宏,初始化和消除函數時使用和上面的裝載卸載模塊沒關系。 */ module_init(hello_init); module_exit(hello_exit);

放個編譯上面模塊的 Makefile obj-m := hello.o all: make -C /lib/modules/$(shell uname -r )/build M=$(shell pwd) modules clean: make -C /lib/modules/$(shell uname -r )/build M=$(shell pwd) clea

  放個編譯上面模塊的 Makefile

obj-m := hello.o all: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean

  給 Makefile 放到上面 hello.c 的相同的目錄中(如果上面寫的模塊代碼叫 hello.c 的話)。然後使用 make 就能編譯了。

  insmod lsmod rmmod

  調用 insmod 時會給需要的模塊加載進內核,會給 ko 的文件以目標代碼加載。裝載時會調用 module_init 指定的函數。退出也調用相應的 module_exit.

  lsmod 可以顯示你寫的模塊,其實是讀 /proc/modules 。接下來我寫寫怎麼樣自己通過內核來建 proc 文件。

  模塊加載參數

  如果在模塊加載時,想指定參數,也提供了相應的頭文件

#include <linux/moduleparam.h> static int test; module_param(test, int, 0644);

  這樣以後,直接在內核模塊內使用 test 的變量就行了。

  模塊的信息

  在程序中可以為模塊加一些描述,發行版權聲明,和作者。

MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Test"); MODULE_AUTHOR("xxx");

  模塊的符號導出

  在 Perl 中,模塊是可以導出變量和方法到其它的模塊中的。在 Linux 內核中也有這樣的方法。

EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name);

  這二個可以導出指定的全局變量,也可以是方法。這個要加載 的頭文件,不要忘記了。

  其它的模塊要使用這個,直接使用 extern void name(void); 就可以使用了。這些導出的函數只能內核和內核模塊使用。不能用戶調用,可以由 /proc/kallsyms 來查看導出的變量和方法

  實例

  寫個內核模塊,通過 proc 可以見到一些信息,通過 proc 的讀和寫的功能。來實現設置和讀取信息。

  proc 介紹

  proc 是一個非常方便的用來動態的向 Linux 內核加入和禁用代碼的一個方法。

  proc/sys 中是用來配置內核的參數,可以通過 sysctl -w key=value

  象普通文件可以支持 open,read,write,close

例如 讀 cat /proc/cpuinfo 寫 echo fukei-name /proc/sys/kernel/hostname proc 的功能實現 proc 在 c 中是一個結構體來實現的,是 struct proc_dir_entry 。它可以給讀寫綁定到特

  例如

  讀

cat /proc/cpuinfo

  寫

echo fukei-name > /proc/sys/kernel/hostname

  proc 的功能實現

  proc 在 c 中是一個結構體來實現的,是 struct proc_dir_entry 。它可以給讀寫綁定到特定的函數上。然後通過別人對 proc 中文件的操作來觸發和回調相應的綁定的函數。

  read_proc 和 write_proc 是這個結構體的成員,也是一種結構體。函數就注冊在這個上面。有興趣的同學可以看看 include/linux/proc_fs.h 中的 read_proc_t 和 write_proc_t 的定義。

  實現起來也簡單。

struct proc_dir_entry *proc_entry = create_proc_entery(....); int my_read_proc() { } proc_entry->read_proc = my_read_proc();

  在這的 create_proc_entery 會返回一個 proc_dir_entry 的結構體的引用。失敗就是 NULL 。

  這樣,當用戶空間進行 read 的系統調用時,如使用 cat proc 中的內容時。內核會調用注冊到 read_proc 上的這個 my_read_proc 來實現的.

#include <linux/module.h> #include <linux/proc_fs.h> #include <linux/sched.h> #include <linux/mm.h> #define MODULE_NAME "Memory" int my_read_proc(char *page,char **start, off_t off,int count, int *eof,void *data) { struct task_struct *tsk = current; int len; len = sprintf( page, "This module info: task %s pid %d\n",tsk->comm, tsk->pid ); return len; } struct proc_dir_entry *proc_entry; int init_module(void) { proc_entry = create_proc_entry(MODULE_NAME, 0644, NULL); if (proc_entry==NULL){ remove_proc_entry(MODULE_NAME, NULL); } proc_entry->read_proc = my_read_proc; return 0; } void cleanup_module(void) { remove_proc_entry(MODULE_NAME, NULL); // 退出和出錯記的刪除 } MODULE_LICENSE("GPL");

Copyright © Linux教程網 All Rights Reserved