Linux内核模块

来源:互联网 发布:淘宝对电子商务的意义 编辑:程序博客网 时间:2024/06/05 05:23

一、 Linux内核模块概念

内核模块是Linux内核向外部提供的一个接口,其全称为动态可加载内核模块(Loadable Kernel Module,LKM),简称为模块。Linux内核之所以提供模块机制,是因为它本身是一个单内核(monolithic kernel)。单内核的最大优点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其它内核上层的功能。

总之,模块是一个为内核(从某种意义上来说,内核也是一个模块)或其他内核模块提供使用功能的代码块。



二、 Linux内核模块的优缺点

内核模块的动态装载性具有如下优点:
  • 将内核映象的尺寸保持在最小,并具有最大的灵活性;
  • 便于检验新的内核代码,而不需重新编译内核并重新引导。

但是,内核模块的引入也带来了如下问题:
  • 对系统性能和内存利用有负面影响;
  • 装入的内核模块和其他内核部分一样,具有相同的访问权限,因此,差的内核模块会导致系统崩溃;
  • 为了使内核模块访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改这些符号表;
  • 有些模块要求利用其他模块的功能,因此,内核要维护模块之间的依赖性。
  • 内核必须能够在卸载模块时通知模块,并且要释放分配给模块的内存和中断等资源;
  • 内核版本和模块版本的不兼容,也可能导致系统崩溃,因此,严格的版本检查是必需的。


尽管内核模块的引入同时也带来不少问题,但是模块机制确实是扩充内核功能一种行之有效的方法,也是在内核
级进行编程的有效途径


三、 Linux内核模块的基本框架

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

static int __init Init(void)
{
//代码块
return 0;
}


static void __exit Exit(void)
{
//代码块
}


module_init(Init);
module_exit(Exit);
MODULE_LICENSE("GPL");


说明:
1. #include <linux/init.h>包含module_init(x)、module_exit(x)等宏定义
2. #include <linux/kernel.h>包含printk等内核封装函数

这些头文件是一个模块最基本的头文件,当我们写一个模块的时候需要加这几个头文件,这几个头文件中有我们模块中最基本的一些符号的定义,即结构体、宏等的定义。


3. module_init(x)修饰Init(),告诉内核加载函数
4. module_exit(x)修饰Exit(),告诉内核卸载函数
5. MODULE_LICENSE("GPL")为遵循GPL规范,内核对驱动的管理十分严格,对应不遵循GPL规范的驱动,内核不愿意非GPL的模块加入内核。一个非GPL模块加入内核的时候,内核会认为内核被污染了而报错。
另外,Linux内核识别的许可证有“GPL”——任意版本的GNU通用公共许可证、“GPL v2”——GPL第二版、
“GPL and additional rignts”——GPL及附加权利、“DualBSD/GPL”——BSD/GPL双许可证和“Proprietary”
——专有等。

6. 内核模块编程特点
<1>不能使用C库和C标准头文件
<2>必须使用GNU规范
<3>没有内存保护机制
<4>不能处理浮点运算
<5>注意同步和并发的问题
<6>注意可移植性



四、 内核模块实例(基于:linux-2.6.35.7, s5pv210)

1. Linux内核模块下一个简单的驱动程序编写:module_demo.c

#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>//加载函数static int __init Demo_Init(void){printk("Demo init\n");return 0;}//卸载函数static void __exit Demo_Exit(void){printk("Demo exit\n");}module_init(Demo_Init);//程序入口module_exit(Demo_Exit);//程序出口MODULE_LICENSE("GPL");//遵循GPL规范


2. Linux内核模块下驱动程序编译
对驱动程序的编译需具备如下要求:
1. 操作系统版本:内核源码的版本要和驱动安装的目标内核版本一致。
2. 处理器的版本:内核源码要针对具体的硬件平台配置过(如arm、x86平台的不同版本)
3. 工具链:内核源码要编译过。



编写Makefile编译:

KERNEL_DIR=/home/gec/linux-2.6.35.7-gec-v3.0-gt110//内核源码所在目录PWD=$(shell pwd)obj-m += module_demo.o modules:$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modulesclean:$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules clean


3. Linux内核模块下驱动程序调试
几个常用命令:
lsmod:查看当前已加载的模块
insmod:加载模块,内核会执行模块加载函数
rmmod:卸载模块,内核会执行模块卸载函数
modinfo:查看模块信息,需要模块信息文件(modules.dep)的支持
modprobe:加载模块,内核会执行模块加载函数

insmod与modprobe的区别:
1. modprobe需要模块信息文件(modules.dep)的支持
2. modprobe会检查模块的依赖,自动加载依赖的模块

模块的依赖:一个模块使用了另一个模块的变量或函数,这个模块就依赖于另一个模块。其次,如果一个模块中的变量或者函数
希望被被的模块使用,需要将对应的变量或者函数导出。导出方法:
1. EXPORT_SYMBOL:导出的内容所有模块都能使用
2. EXPORT_SYMBOL_GPL:导出的内容只有遵循GPL协议的模块才能使用



五、 Linux驱动程序设计与应用程序设计的区别

1. 应用程序有入口(main函数),但是没有出口。驱动程序有入口也有出口。
2. 应用程序编程的时候可以使用标准的C库,即可以使用libc提供的函数。驱动程序不能使用标准的C库,只能使用内核提供的函数。
3. 驱动程序稳定的、精简、可移植性
4. 应用程序和驱动程序的编译过程不同,驱动程序编译的时候,需要内核源码。




0 0
原创粉丝点击