`
cloudtech
  • 浏览: 4594579 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

linux 内核模块编程简要总结

 
阅读更多

摘要

Linux内核模块编程的资料有些纷繁复杂,有的过于简单,有的过于庞杂,我试图用笔记的形式想读者展示怎样来进程Linux模块编程,力图做到简明扼要,这篇文章也是作为本人备忘的资料,所以有些地方过于简略是难免的。本来这篇文章的目的就是让用户知其然,至于所以然还是请参考相应的资料,其实最好的资料莫过于Linux Kernel Source。

适用范围:

  • Linux Kernel >= 2.6.0

Linux模块简介

首先这个module不同于microkernel的module,microkernel的module是一个个的daemon进程,工作于用户空间,Linux的module只是一个内核的目标代码,内核通过执行运行时的连接,来把它整合到kernel中去,所以说Linux的module机制并没有改变Linux内核为monolithic OS本质,其module也是工作于内核模式,享有内核的所有特权。

至于为什么要引入Linux Kernle Module(一下简称LKM),我想至少有一下几点:

  • 模块化编程的需要,降低开发和维护成本。
  • 增强系统的灵活性,使得修改一些内核功能而不必重新编译内核和重启系统。
  • 降低内核编程的复杂性,使入门门槛降低。

相关宏及头文件

LKM需要包含以下头文件:<linux/kernel.h> <linux/module.h>

需要定义以下宏:__KERNEL__, MODULE

一个简单的内核模块示例

/*file:   hello.c*/
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/module.h>
#include <linux/kernel.h>
static int __init hello_init(void)
{
printk(KERN_ALERT "Hello, my LKM./n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_ALERT "Bye, my LKM./n");
}
module_init(hello_init);
module_exit(hello_exit);

很简答吧,不是吗?这个LKM的功能其实也很简单,就是当通过insmod加载它的时候,他打印Hello, my LKM.通过rmmod卸载它的时候他打印bye, my LKM.一个最基本的内核模块一般都包含有两个函数,一个是初始化函数(比如说这里的hello_init),一个是卸载函数(hello_exit), 当然也可以没有任何函数,只是提供一些变量。但是初始化函数和卸载函数必须成对出现。并且init函数当操作成功时返回值大于等于零,当操作失败时,返回非零。宏module_init和module_exit用于注册初始化函数和卸载函数。

LKM的编译

一个示例的Makefile如下所示

obj-m:= hello.o 
KERNELBUILD:= /lib/modules/`uname -r`/build
default:
make -C $(KERNELBUILD) M=$(shell pwd) modules
clean:
rm -rf *.o .*.cmd *.ko *.mod.c .tmp_versions

如果这个目录下面还有其它模块,只需要在hello.o后面添加就行了。

obj-m:= hello.o mod.o

在模块所在目录执行make,等成功后就可以得到我们想要的模块(hello.ko)了。

如果一个模块存在许多源文件,比如:hello, 由hello1.c hello2.c共同连接而成,需要在Makefile中加入如下行

hello-objs:= hello1.o hello2.o

LKM的加载

Linux为用户提供了modutils,用来操纵模块。这个工具集主要包括:

insmod 安装模块
rmmod 删除模块
modprobe 比较高级的加载和删除模块,可以解决模块之间的依赖性
lsmod 列出已经加载的模块和其信息
modinfo 用于查询模块的相关信息,比如作者,版权...

试着用命令insmod hello.ko加载模块,rmmod删除模块,看看有什么事情发生了。你有可能看不见任何输出,难道是有错误发生?No,执行命令tail /var/log/message呵呵,是不是看到了?

Feb 19 00:07:35 gentux Hello, my LKM.
Feb 19 00:07:38 gentux Bye, my LKM.

模块其它信息

比较常用信息常常包括:作者、描述、版权等,为此LKM为我们提供了如下宏:

MODULE_AUTHOR("author");
MODULE_DESCRIPTION("the description");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("dev"); // 设备驱动程序所支持的设备。

比较常用的Free license有"GPL", "GPL v2", "GPL and additional rights", "Dual BSD/GPL", "Dual MPL/GPL"。

模块参数

用户空间的应用程序可以接受用户的参数,LKM也可以做到,只是方式有些不同而已。相关的宏有:

MODULE_PARM(var, type);
MODULE_PARM_DESC(var, "the description of the var");

模块参数的类型(即MODULE_PARM中的type)有一下几种:

  • b byte(unsigned char)
  • h short
  • i int
  • l long
  • s string(char*)

这些参数最好有默认值,如果有些必要参数用户没有设置可以通过在module_init指定的init函数返回负值来拒绝模块的加载。 LKM还支持数组类型的模块,如果在类型符号前加上数字n则表示最大程度为n的数组,用“-”隔开的数字分别代表最小和最大的数组长度。

示例:
MODULE_PARM(var, "4i"); // 最大长度为4的整形数组
MODULE_PARM(var, "2-6i"); // 最小长度为2,最大长度为6的整形数组

如何用insmod传入参数,其实man一下就可以了,不过现在的man有些过于简单,所以在此说明一下:

insmod variable=value[,value2...] ...

其中value可以用引号括起来,也可以不用。但是有一点“=”前后不能留有空格,并且value中也不能有空格。

模块符号的导出

和用户空间的应用程序不同的是,引入一个模块的目的常常是为了给内核提供一些routine,来完成特定的功能,很少有模块什么符号都不导出,为此Linux为用户提供了如下宏:

EXPORT_SYMBOL(var); // 输出symbol var
EXPORT_SYMBOL_GPL(var); // 输出的symbol版权为GPL

模块之间的依赖性

有的时候两个模块之间可能有依赖性,要加载的模块A,依赖于模块B,此时insmod是无能为力的,只能用modprobe来加载模块和其依赖的模块,否则只能手动一个个加载。

modprobe通过读取由depmod -a生成的/lib/modules/version/modules.dep来获得其所依赖的模块列表(也有可能是一个模块树),然后调用insmod来一个个按顺序加载。

命名空间的问题

  1. 对于不需要export的全局symbol最好用static进行修饰,限制其作用域为本文件,以防污染内核的命名空间。
  2. 对于由内核或其它模块export的一些symbol,最好用extern进行修饰,以示其不在本文件。
  3. 在可能用到errno变量的场合,因为内核没有export此symbol,只能有用户自行定义,比如:int errno;

一个较复杂的模块示例

/*file:   hello.c*/
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_AUTHOR("xiaosuo <xiaosuo@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("This module is a example.");
static int int_var = 0;
static const char *str_var = "default";
static int int_array[6];
MODULE_PARM(int_var, "i");
MODULE_PARM_DESC(int_var, "A integer variable");
MODULE_PARM(str_var, "s");
MODULE_PARM_DESC(str_var, "A string variable");
MODULE_PARM(int_array, "2-6i");
MODULE_PARM_DESC(int_array, "A integer array");
//上面这部分在我的系统是编译不过的,查一下资料,改成下面的语法就可以了
module_param(int_var,int,0);
static int __init hello_init(void)
{
int i;
printk(KERN_ALERT "Hello, my LKM./n");
printk(KERN_ALERT "int_var %d./n", int_var);
printk(KERN_ALERT "str_var %s./n", str_var);
for(i = 0; i < 6; i ++){
printk("int_array[%d] = %d/n", i, int_array[i]);
}
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_ALERT "Bye, my LKM./n");
}
module_init(hello_init);
module_exit(hello_exit);

参考资料

The Linux Kernel Module Programming Guide

Linux 内核编程指南(第三版)

Linux内核分析及编程

分享到:
评论

相关推荐

    Linux内核工作原理 word版本 强烈推荐

    动态模块一章描叙Linux核心是如何仅在需要时动态加载某些模块,比如文件系统。 处理器一章给出了目前Linux可以在其上运行的一些处理器的简要介绍。 资源一章则提供了有关Linux核心资源的有用信息。

    多通道串行通信设备的Linux驱动程序实现 (2007年)

    针对该通信设备,介绍其在Linux下采用内核模块编程方式,实现中断和DMA方式数据传送的方法。实验测得此传输方案的本地速率可达24.096 Mb/s,传输和响应速度较传统读写方式有了显著提高,数据可稳定传输在460 kb/s,...

    jack_projects:展示了我的编程技能的程序集以及过去几年中完成的项目。 README文件简要说明了每个文件是什么

    lift_kernel_module.c-该程序是Linux内核的内核模块。 它调用自定义系统调用来运行模拟电梯,该电梯在所需楼层上载人和下车。 fat32_file_system.c-此程序模拟FAT32文件系统。 它通过寻址文件映像的各个字节并对其...

    入门学习Linux常用必会60个命令实例详解doc/txt

    不过目前大多数较新的Linux发行版本(包括红旗 Linux、中软Linux、Mandrake Linux等)都可以自动挂装文件系统,但Red Hat Linux除外。 umount 1.作用 umount命令的作用是卸载一个文件系统,它的使用权限是超级...

    学ARM和学单片机一样简单12

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单15

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单4

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单3

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单9

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单2

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单11

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单7

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单5

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单13

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单6

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单14

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    学ARM和学单片机一样简单8

    (7)、LINUX内核的配置、编译 (8)、演示开发板与PC机通过NFS实现文件共享 10、块设备-USB主设备CH375驱动编程(122分钟)(第十讲) (1)、USB驱动框架简介 (2)、CH375-hw.h、Ch375-hcd.c程序讲解 (3)...

    彩虹UDA软件狗工具带硬复制工具

    提供了针对 Linux 内核 2.2 和 2.4 版本驱动程序,开发商可以使用 Linux 模块保护运行于 Linux 操作系统上的应用程序。 新增功能: 软件狗开发套件 3.0 版新增功能在软件狗开发套件 V3.0 中,新增了与原并口...

    ARM 实验指导书第一册UCOS-II.doc

    三、ZY21ARM13BC实验系统各功能模块介绍 14 第三章 嵌入式软件的基本使用 32 一、超级终端的配置 32 二、ADS1.2集成开发环境的使用 38 三、简易仿真器的配置 53 四、Flash Programmer软件安装 55 第四章 基础实验 57...

Global site tag (gtag.js) - Google Analytics