自:http://blog.sina.com.cn/s/blog_5ae509900100bfft.html
装载驱动:
#include <linux/init.h>
定义的宏
__init
__initdata //模块始初化的函数或数据标记,标记为初始化的项目会在初始化结束后丢弃;
__exit
__exitdata //模块清除阶 段的函数或数据标记,标记为清除化项目会在内核示被配置为可卸载模块的情况下被丢弃。
//内核将以上相应的目标对象放置在可执行文件的特殊ELF段中而让这些标记起做
内核源码中的宏定义:
#define
__init __section(.init.text)
__cold
#define
__initdata __section(.init.data)
#define
__exit __section(.exit.text)
__exitused __cold
#define
__exitdata __section(.exit.data)
//用来指定函数或数据所属区段,内核模块装载时从指定区段读取。如没有在指区段没有找到相应入口点,则装载失败。
定义的宏
module_init
( __init func ); //模块装载入口
module_exit
( __exit func ); //模块卸载出口 如果未定义,则表明该驱动无法卸载
内核源码中的宏定义:
#define
module_init(initfn) \
static
inline initcall_t __inittest(void) \
{
return initfn; } \
int
init_module(void) __attribute__((alias(#initfn)));
#define
module_exit (exitfn) \
static
inline exitcall_t __exittest(void) \
{
return exitfn; } \
void
cleanup_module(void) __attribute__((alias(#exitfn)));
说明:__attribute__是gcc专有的,用来说明函数的属性
weak 和 alias 分别是两个属性。weak 使得 main 这个符号在目标文件中作为 weak symbol 而不是 global symbol。用 nm 命令查看编译
dummy.c 生成的目标文件可用看到 main 是一个 weaksymbol,它前面的标记是 W。
而 alias 则使cleanup_module是#exitfn 的一个别名,#exitfn 和
cleanup_module 必须在同一个编译单元中定义,否则会编译出错。
#include <linux/module.h> //设备驱动必须包含的头文件,包含可装载模块需要的大量符号和函数的定义
常用宏:
MODULE_LICENSE
( "Daul BSD/GPL" );//为模块指定代码所使用的许可证,有许多许可证类型。这里就不列举了。
//如果模块没有显式地标记内核可识别的许可证,则会被假定为专有的。内核加载这种模块就会抱怨内核被“污染”。
MODULE_AUTHOR
( AUTOHR ); //模述模块作者
MODULE_DESCRIPTION
( description );//对模块的描述信息
MODULE_VERSION
( version ); //指定模块版本
MODULE_DEVICE_TABLE
( table_information );//模块设备表信息
MODULE_ALIAS
( alternate_information ); //指模块的相关文档信息
EXPORT_SYMBOL
( symbol_name );
EXPORT_SYMBOL_GPL
( symbol_name );
//当需要模块层叠技术时(如:msdos文件系统依赖于由fat模块导出的符号),应该了解导出内核符号的用法。
//EXPORT_SYMBOL_GPL使得要导出的模块只能被GPL许可证下的模块使用。符号必须在模块文件的全局部分导出,不能在函数中导出。
//因为上面这两上宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。该变量将在模块可执行文件的特殊部分(ELF段)保存。
//装载时,内核通过这个段来寻找模块导出的变量。
//更多宏定义请查看该头文件注释
#include <linux/moduleparam.h>
常用宏:
module_param
( variable, type, perm );
//用来创建模块参数的宏,用户可在装载模块时调整这些参数的值。
//type类型:
// bool
charp(字符指针) int invbool(反转逻辑) long short ushort uint ulong.
module_param_array
( name, type, num, perm );
//以上perm的值在
<linux/stat.h>中定义
//如果perm被设置为0,就不会有对应的sysfs入口点,否则模块参数会在/sys/module中出现。
//S_IRUGO,任一用户均可以读取:记忆方法:S=status(状态)IR=ReadOnly(只读)
UGO=user,group,other(用户,用户组,其它)
//S_IWUSE,只有root可以修改该参数
#include <linux/kernel.h>
常用函数:
static
inline int __cold printk(const char *s, ...);//printk函数原型定义,用法跟标准C库printf类似。
//定义此函数的原因是:内核模块装载后在内核空间执行,不能依赖C库函数。printk不到持浮点数(float类型)。
#include <linux/errno.h>
//在linux内核中,错误编译是定义在该头文件中的负整数。如下是部分定义:
#define
ERESTARTSYS 512
#define
ERESTARTNOINTR 513
#define
ERESTARTNOHAND 514
#define
ENOIOCTLCMD 515
#define
ERESTART_RESTARTBLOCK 516
#define
EBADHANDLE 521
#define
ENOTSYNC 522
#define
EBADCOOKIE 523
#define
ENOTSUPP 524
#define
ETOOSMALL 525
#define
ESERVERFAULT 526
#define
EBADTYPE 527
#define
EJUKEBOX 528
#define
EIOCBQUEUED 529
#define
EIOCBRETRY 530
内核装裁与卸载演示代码,尽可能将上面的演示代码使用起来:
//在测试代码之前得重新编译内核,构建自己的代码树:
<section_one.c>
#include
<linux/init.h>
#include
<linux/module.h>
#include
<linux/kernel.h>
#include
<linux/moduleparam.h>
#include
<linux/stat.h>
MODULE_LICENSE
( "Daul BSD/GPL" );
static
char* p_param = "hello word !";
static
int number = 1;
static
int section_begin ( void )
{
printk
( KERN_INFO "section_one begin,param:%s,number:%d", p_param, number );
return
0;
}
static
void section_end ( void )
{
printk
( KERN_ALERT "section_one end !" );
}
module_init
( section_begin );
module_exit
( section_end );
module_param
( p_param, charp, S_IRUGO );
module_param
( number, int, S_IRUGO );
EXPORT_SYMBOL_GPL
( section_begin );
EXPORT_SYMBOL_GPL
( section_end );
MODULE_AUTHOR
( "Leehom" );
MODULE_DESCRIPTION
( "A simple demo !" );
MODULE_VERSION
( "Version 1.0" );
<Makefile>
ifneq
($(KERNELRELEASE),)
obj-m:=section_one.o
else
KERN_DIR:=/usr/src/linux
CURRENT_DIR:=$(shell
pwd)
default:
$(MAKE)
-C $(KERN_DIR) M=$(CURRENT_DIR)
endif
编译代码:
[leehom@leehom
section_one]$ make
make
-C /usr/src/linux M=/home/leehom/Public/section_one
make[1]:
Entering directory `/usr/src/linux-2.6.26'
CC
[M] /home/leehom/Public/section_one/section_one.o
Building
modules, stage 2.
MODPOST
1 modules
CC /home/leehom/Public/section_one/section_one.mod.o
LD
[M] /home/leehom/Public/section_one/section_one.ko
make[1]:
Leaving directory `/usr/src/linux-2.6.26'
查看编译结果,内核模块文件section_one.ko已经构建好了。
[leehom@leehom
section_one]$ ls
built-in.o Module.markers section_one.c section_one.mod.c
Makefile modules.order section_one.c~ section_one.mod.o
Makefile~ Module.symvers section_one.ko section_one.o
加载驱动模块:
[leehom@leehom
section_one]$ sudo insmod section_one.ko
查看加载结果:
[leehom@leehom
section_one]$ dmesg | tail -n 1
section_one
begin,param:hello word !,number:1
查看模块信息:
[leehom@leehom
section_one]$ modinfo section_one.ko
filename: section_one.ko
version: Version
1.0
description: A
simple demo !
author: Leehom
license: Daul
BSD/GPL
srcversion: 1E48889243E5036D4DD16AA
depends:
vermagic: 2.6.26
SMP mod_unload 686 4KSTACKS
parm: p_param:charp
parm: number:int
卸载模块驱动并查看结果:
[leehom@leehom
section_one]$ sudo rmmod section_one.ko
[leehom@leehom
section_one]$ dmesg | tail -n 1
section_one
begin,param:hello word !,number:1<1>section_one end !
加载模块驱动并传递参数:
[leehom@leehom
section_one]$ sudo insmod section_one.ko p_param='test' number=2
[leehom@leehom
section_one]$ dmesg | tail -n 1
section_one
begin,param:hello word !,number:1<1>section_one end !<6>section_one begin,param:test,number:2
查看导出的符号:
[leehom@leehom
/]$ sudo cat -n /proc/kallsyms | grep section_one
[sudo]
password for leehom:
32525 00000000
a section_one.c [section_one]
32526 d097d000
t section_end [section_one]
32527 d097d010
t section_begin [section_one]