Linux内核模块Demo
1. 思路
要实现一个内核模块,要考虑以下几个方面:
- 头文件
- 模块入口
- 模块出口
- 模块加载时是否需要传参
- 模块许可协议声明,需要遵循GPL协议
- 模块作者声明,告诉别人发现bug时该联系谁
- 模块描述,简要叙述这个模块是干什么的
- 模块版本,版本号对于软件的迭代更新是非常重要的
2. 源代码
/*
** hello_module.c
** 该文件是linux内核模块的简单示例代码,
** 目的是展示一个Linux内核模块的基本构成。
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
static unsigned int param_int = 0; //模块参数,用于存放模块加载时传入的整型参数。
static unsigned int param_array[16] = {0}; //模块参数,用于存放模块加载时传入的数组参数。
static unsigned int input_array_len = 0; //与param_array结合使用,用于保存实际传入的参数长度。
MODULE_LICENSE("GPL v2"); //声明许可协议
MODULE_AUTHOR("Wanglehe"); //声明模块作者
MODULE_DESCRIPTION("This is a simple module test."); //模块说明
MODULE_VERSION("v1.0"); //模块版本说明
// 声明一个整型参数,输入值保存在param_int中。0644表明该参数root用户可写,其他用户只读。
module_param(param_int, uint, 0644);
// 声明一个数组型参数,输入值保存在param_array中,实际输入的数据长度保存在input_array_len中。
// 0644表明该参数root用户可写,其他用户只读。
module_param_array(param_array, uint, &input_array_len, 0644);
/*
** 模块入口函数。
** 该函数当模块加载时被调用。
** static关键字用于限定该函数的作用域。
** 需要提供int型返回值。
** __init关键字表明该函数被编译链接到init段。
** init段的在执行完成之后,其所占用的资源就会被释放。
*/
static int __init hello_init(void)
{
int i = 0;
//作为示例代码,此处指打印一些提示信息。
printk(KERN_ERR "%s %d %s: Hello module init.\n", __FILE__, \
__LINE__, __func__);
//打印参数的值,验证在有输入时,参数是否改变。
printk(KERN_ERR "Int Arg = %d\n", param_int);
for(i=0; i<input_array_len; i++)
{
printk(KERN_ERR "Array[%d]=%d\n", i, param_array[i]);
}
return 0;
}
/*
** 模块出口函数
** 该函数在模块卸载是被调用。
** static关键字限定了该函数的作用域。
** __exit关键字表明该函数被编译链接到exit段。
** 如果代码不是以模块的形式动态加载到内核,而是直接被编译到内核中,那么使用__exit
** 进行修饰的代码不会被编译到内核,因为永远不会被调用。
*/
static void __exit hello_exit(void)
{
//作为示例代码,此处指打印一些提示信息。
printk(KERN_ERR "%s %d %s: Hello module exit.\n", __FILE__, \
__LINE__, __func__);
return;
}
module_init(hello_init); //指定模块入口
module_exit(hello_exit); //指定模块出口
/**
** 模块操作命令:
** (1)模块加载:
** insmod hello_module.ko
** (2)模块带参数加载:
** insmod hello_module.ko param_int=100 param_array=1,2,3,4,5
** 传入参数时,参数名必须正确,否则会出错。
** 不同参数之间用空格隔开,数组参数的多个值之间用逗号隔开
** (3)获取模块信息:
** modinfo hello_module.ko
** (4)列出已经加载的模块:
** lsmod
** (5)卸载模块:
** rmmod hello_module
**/
3. Makefile
#!/bin/bash
# 告诉编译器要编译哪些源码
# 鉴于示例代码为hello_module.c,所以此处指定为hello_module.o
obj-m += hello_module.o
# 内核源码目录
KDIR := /work/itop/iTop4412_Kernel_3.0
# 获取当前目录
PWD ?= $(shell pwd)
# 编译模块
all:
make -C $(KDIR) M=$(PWD) modules
# 删除编译生成的文件
clean:
rm -rf *.o *.mod.c *.ko *.order *.symvers