编译并运行这个模块:
/ k$ w2 t- }, y1 ~ //需要root权限来运行
: v9 c3 y; N6 ^1 ~ make
# g/ C0 {, a& D insmod helloworld.ko
* ?/ q/ d# \! j( w) I/ t$ A1 g rmmod helloworld.ko
% Z( o. [2 N/ Q! P/ c. f5 } 尽管我们对它的一些细节还不够了解,它确实神奇的工作了,这个Hello World信息输出到了屏幕终端上(不是VT),www.britepic.org或者系统的Kenrel log里(/var/log/messages),你可以通过运行dmesg来看到这些信息。. F1 b7 C8 D0 c/ {: O: f9 R
驱动基础
( f2 y4 }0 p9 S) ] 我们通过分析上面的代码来了解一个驱动程序的基本概念。( h9 C0 {3 E$ a" ]) n T
头文件
% H7 r3 Z# ^& z8 k1 @% i3 T4 t 就像你写C程序需要包含C库的头文件那样,Linux内核编程也需要包含Kernel头文件,大多的Linux驱动程序需要包含下面三个头文件:& a# B# m% L0 g' O+ J8 b/ a
#include
/ W( I) a& j R$ v7 a #include 5 [8 G! l7 S: Q; E8 G! x6 Y
#include " B6 |" g* E+ D
init.h 定义了驱动的初始化和退出相关的函数,9 x+ K9 [ x3 {! Q0 n* ?
kernel.h 定义了经常用到的函数原型及宏定义+ O4 ~7 k. H' ?: C3 B6 z
module.h 定义了内核模块相关的函数、变量及宏。) n/ O3 y3 L/ f% N# D, E
初始化
5 i- F. Y, N- _. P) S 任何一个驱动都去需要提供一个初始化函数,当驱动加载到内核中时,这个初始化函数就会被自动执行,初始化的函数原型定义如下:
( \" }# k0 t/ H typedef int (*initcall_t)(void);
3 g0 E: q% T/ l) Q) A 驱动程序是通过module_init宏来声明初始化函数的:3 d# `5 L3 D9 ^7 V
static int __init hello_init(void)
1 { M' O9 l" N! H {; Q2 m$ P2 v7 i! S
printk(KERN_ALERT "Hello World!\n");8 m$ e J3 f, i ~2 W
return 0;7 |3 M9 k! ]' C0 q( Y' m3 j5 G; |
}
# G7 m% z3 Q, X5 T5 J7 Q* r module_init(hello_init);" v7 t; W, l a& A
__init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,这样当函数初始化完成后这个区域可以被清除掉以节约系统内存。Kenrel启动时看到的消息“Freeing unused kernel memory: xxxk freed”同它有关。
1 f. i: i: q) h( O; J* q1 \9 C 初始化函数是有返回值的,只有在初始化成功是才返回0,否则返回错误码(errno)。- ~$ y6 g7 `! c! F# P
卸载8 H1 S. C6 z. H; X0 G7 i
如果驱动程序编译成模块(动态加载)模式,那么它需要一个清理函数。当移除一个内核模块时这个函数被调用执行清理工作。清理函数的函数原型定义为:
u0 M7 U, X% @& @ typedef void (*exitcall_t)(void);' G5 ~0 j9 t9 S: A
驱动程序是通过module_exit宏来声明清理函数的:
8 ~6 j' w, S9 E# x9 { static void __exit hello_exit(void)/ \& j' Y$ x4 k2 F2 T/ z; h! w o+ P
{& Z9 j3 R8 A, i
printk(KERN_ALERT "Goodbye World!\n");; Q8 _2 C. B2 t7 K
}$ F! C, E9 O0 I0 x
module_exit(hello_exit);
' a F6 K z# x 同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作。显然,__init和__exit对动态加载的模块是无效的。
- T4 @3 S, q7 j* s+ u 版权信息. [" P' U- E: J: ~0 H
Linux内核是按照GPL发布的,同样Linux的驱动程序也要提供版权信息,否则当加载到内核中是系统会给出警告信息。Hello World例子中的版权信息是GPL。
+ F8 d$ Q, H% }0 Q! D- j MODULE_LICENSE("GPL");+ I7 L) M4 |+ v+ A8 {
后续4 G6 A0 M6 R0 U7 F2 T4 R+ |" b
这里你了解了一个驱动程序的基本框架,所有的驱动都会包含这些内容。这里我们没有对Linux 驱动程序的编译系统做详细的介绍,因为它相对C应用程序的编译有些复杂。Linux2.6内核采用Kbuild系统做编译,下一章你会了解到Kbuild的详细内容。 |