內核編程入門,就以最爲簡單的hello.c爲例。
環境:Redhat 9.0,內核版本2.4.20-8。
雖然現在2.6.x的內核很早就就發佈了,但是畢竟很多公司還在使用2.4.x的內核。作爲新手,從2.4.x的內核入手是可行的。原因有如下幾條:
(1)2.4.x比較成熟。可能你遇到的絕大多數問題,網上都有解決方案。在這個過程中,你可以節省大量的時間,同時還可以對比網上的解決方案,加深認識,總結解決問題的方法,調整自己的學習方法和思路。
(2)事物的發展總不可能是一蹴而就的。瞭解發展的歷程,對深入理解問題有很大的好處。所以在2.4.x的內核的基礎上學習2.6.x的內核,就能夠體會到2.6.x的內核在哪些方面要出色,或者爲什麼要採取這種改進技術。相信理論清晰了,即時2.6.x的內核也會容易上手。
下面總結了第一個內核程序hello.c的學習過程。
(一)第一階段:儘量簡單
#define MODULE
#include <linux/module.h>
int init_module(void)
{
printk( "Hello World!/n ");
return 0;
}
void cleanup_module(void)
{
printk( "Goodbye!/n ");
}
執行,出現錯誤一:
[root@lqm drivers]# gcc -c hello.c
[root@lqm drivers]# insmod hello.o
hello.o: kernel-module version mismatch
hello.o was compiled for kernel version 2.4.20
while this kernel is version 2.4.20-8.
這是因爲內核源代碼版本和編譯器版本不一致造成的。
(1)編譯器版本/usr/include/linux/version.h
#define UTS_RELEASE "2.4.20 "
#define LINUX_VERSION_CODE 132116
#define KERNEL_VERSION(a,b,c) (((a) < < 16) + ((b) < < 8) + (c))
(2)內核源代碼版本/usr/src/linux-2.4.20-8/include/linux/version.h
/usr/src/linux-2.4.20-8/include/linux
[root@lqm linux]# cat version.h
#include <linux/rhconfig.h>
#if defined(__module__smp)
#define UTS_RELEASE "2.4.20-8smp "
#elif defined(__module__BOOT)
#define UTS_RELEASE "2.4.20-8BOOT "
#elif defined(__module__bigmem)
#define UTS_RELEASE "2.4.20-8bigmem "
#else
#define UTS_RELEASE "2.4.20-8 "
#endif
#define LINUX_VERSION_CODE 132116
#define KERNEL_VERSION(a,b,c) (((a) < < 16) + ((b) < < 8) + (c))
可以採取修改編譯器版本號與內核源代碼版本號一致的辦法來解決這個問題,即修改/usr/include/linux/version.h中 #define UTS_RELEASE "2.4.20 "
爲 #define UTS_RELEASE "2.4.20-8 "
執行,出現錯誤二:
[root@lqm drivers]# gcc -c hello.c
[root@lqm drivers]# insmod hello.o
Warning: loading hello.o will taint the kernel: no license
See http://www.tux.org/lkml/#export-tainted for information about tainted modules
Module hello loaded, with warnings
[root@lqm drivers]# tail -n 1 /var/log/messages
Jan 30 12:02:08 lqm kernel: Hello
也就是說出現了no license的警告。GNU的軟件需要有GPL,所以修改源代碼如下:
#define MODULE
#include <linux/module.h>
MODULE_LICENSE( "GPL ");
int init_module(void)
{
printk( "Hello World!/n ");
return 0;
}
void cleanup_module(void)
{
printk( "Goodbye!/n ");
}
這時沒有錯誤了。寫了一個腳本,測試流程自動化:
#!/bin/bash
gcc -c hello.c
sleep 1
insmod hello.o && echo -e "Instal module - hello.o/n "
sleep 1
tail -n 1 /var/log/messages
lsmod | grep "hello " && echo -e "Module hello has instaled/n "
rmmod hello && echo -e "Remove module - hello/n "
sleep 1
tail -n 1 /var/log/messages
lsmod | grep "hello " || echo "Module hello has removed "
執行結果如下:
[root@lqm hello]# ./run
Instal module - hello.o
Jan 30 13:31:29 lqm kernel: Hello World!
hello 748 0 (unused)
Module hello has instaled
Remove module - hello
Jan 30 13:31:30 lqm kernel: Goodbye!
Module hello has removed
(二)第二階段:完善,深入一點
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE( "GPL ");
static int init_module(void)
{
printk( "Hello, world!/n ");
return 0;
}
static void cleanup_module(void)
{
printk( "Goodbye!/n ");
}
寫Makefile文件如下:
# Kernel Programming
# Shandong University, Linqingmin
# The path of kernel source code
INCLUDEDIR = /usr/src/linux-2.4.20-8/include/
# Compiler
CC = gcc
# Options
CFLAGS = -D__KERNEL__ -DMODULE -O -Wall -I$(INCLUDEDIR)
# Target
OBJS = hello.o
all: $(OBJS)
$(OBJS): hello.c /usr/include/linux/version.h
$(CC) $(CFLAGS) -c $ <
install:
insmod $(OBJS)
uninstall:
rmmod hello
.PHONY: clean
clean:
rm -f *.o
寫Makefile時應該注意,不要用空格來代替 <TAB> 。否則會出現錯誤:missing separator. Stop.
修改執行腳本run:
#!/bin/bash
# The first step
make && make install && echo -e "Instal module - hello.o/n "
sleep 1
tail -n 1 /var/log/messages
lsmod | grep "hello " && echo -e "Module hello has instaled/n "
# The second step
make uninstall && echo -e "Remove module - hello/n "
sleep 1
tail -n 1 /var/log/messages
lsmod | grep "hello " || echo "Module hello has removed "
# The last step
make clean
執行結果如下:
[root@lqm hello]# ./run
gcc -D__KERNEL__ -DMODULE -O -Wall -I/usr/src/linux-2.4.20-8/include/ -c hello.c
hello.c:18: warning: `init_module ' defined but not used
hello.c:25: warning: `cleanup_module ' defined but not used
insmod hello.o
Instal module - hello.o
Jan 31 13:40:23 lqm kernel: Hello,
hello 728 0 (unused)
Module hello has instaled
rmmod hello
Remove module - hello
Jan 31 13:40:24 lqm kernel:
Module hello has removed
rm -f *.o
(三)第三階段:總結
1、一個內核模塊至少應該包括兩個函數:
(1)init_module:模塊插入內核時調用
(2)cleanup_module:模塊移除時調用
這個簡單的程序就是隻實現了這兩個函數,而且只做了打印信息的工作,沒有使用價值。典型情況下,init_module爲內核中的某些東西註冊一個句柄,相當於模塊初始化的工作。cleanup_module則是撤銷模塊前期的處理工作,使模塊得以安全卸載。
2、insmod實現動態加載模塊。在當前OS上,動態加載模塊以測試硬件等,避免了繁瑣的工作。但是,在這種情況下,會出現版本不匹配的情況。另外,要分清楚內核源代碼路徑和編譯器路徑的不同,知道在編譯時該指定那個路徑。第二階段開始出現過幾個錯誤都是因爲默認的路徑是編譯器路徑,而不是內核源代碼路徑。體會內核模塊化帶來的好處!
3、應用Make工具來管理項目。即使小,也要訓練。在2.4內核和2.6內核下,Makefile的編寫會有所不同。只是語法形式的不同,先深入掌握一種,另一種注意一下應該可以避免犯錯誤。
繼續努力!
2007/02/01補記
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE( "GPL ");
static int __init hello_init(void)
{
printk( " <1> Hello, world!/n ");
return 0;
}
static void __exit hello_exit(void)
{
printk( " <1> Goodbye!/n ");
}
module_init(hello_init);
module_exit(hello_exit);
# Kernel Programming
# Shandong University, Linqingmin
# The path of kernel source code
KERNELDIR = /usr/src/linux-2.4.20-8
# Compiler
CC = gcc
# Options
CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O -Wall
ifdef CONFIG_SMP
CFLAGS += -D__SMP__ -DSMP
endif
# OBJS
OBJS = hello.c
# Target
TARGET = hello.o
$(TARGET): hello.c
$(CC) $(CFLAGS) -c $ <
install:
insmod $(TARGET)
uninstall:
rmmod hello
.PHONY: clean
clean:
rm -f *.o
#!/bin/bash
# The first step
make && make install && echo -e "Instal module - hello.o/n "
sleep 1
lsmod | grep "hello " && echo -e "Module hello has instaled/n "
# The second step
make uninstall && echo -e "Remove module - hello/n "
sleep 1
lsmod | grep "hello " || echo "Module hello has removed "
# The last step
make clean
執行結果:
gcc -D__KERNEL__ -DMODULE -I/usr/src/linux-2.4.20-8/include -O -Wall -c hello.c
insmod hello.o
Hello,World!
Instal module - hello.o
hello 776 0 (unused)
Module hello has instaled
rmmod hello
Goodbye!
Remove module - hello
Module hello has removed
rm -f *.o
說明:
這個版本採取了顯式的初始化和清除函數,顯然無論是調試的便利還是編程風格,都比前幾個版本要好。這種機制具體描述如下:如果你將模塊初始化函數命名爲my_init(而不是init_module),將清除函數命名爲my_exit,則可以使用下面兩行來進行標記(通常在源文件的末尾)
module_init(my_init);
module_exit(my_exit);
注意必須包含頭文件 <linux/init.h> 。
第一個簡單的內核編程實驗:hello.c
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.