`
gstarwd
  • 浏览: 1536762 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

linux内核模块编程

阅读更多

主题: linux内核模块的程序结构--模块加载函数(必须),模块卸载函数(必须),模块许可证声明(必须),模块参数(可选),模块导出符号(可选),模块作者的等信息声明(可选)

一个linux内核模块主要由以下几个部分组成。
1、模块加载函数"用module_init()来指定"(必须)
   当通过insmod和modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。
linux模块加载函数一般以 __init表示声明。典型声明如下::
static int __init  initialization_function(void)
{
       /*初始化代码*/
}
module_init(initialization_function);
    模块加载函数必须使用module_init(函数名)的形式被指定。它返回整型值,若初始化成功,应返回0,而初始化失败时,应返回错误编码。在linux内核中,错误编码是一个负值,在<linux/errno.h>中定义,包括-ENODEV、-ENOMEM之类的符号值。返回相应的错误编码是种非常好的习惯,只有这样,应用程序才能利用perror等方法把他们转换成有意义的错误信息字符串。
    在2.6内核中,可以使用“request_module(const char *fmt,...)函数”加载内核模块(注意:前面加载模块都是通过insmod和modprobe来实现的),驱动开发人员可以通过调用::
request_module(module_name);

request_module("char-major-%d-%d",MAJOR(dev),MINOR(dev));
来加载其他内核模块。
   在linux内核中,所有表示为__init的函数在连接的时候放在.init.text这个区段内,此外,所有的__init函数在段.initcall.init中还保存了一份函数指针,在初始化时,内核会通过这些指针调用这些__init函数,并在初始化完成后释放init区段(.init.text,.initcall.init等)。
////////////////////////////////////////////////////////////////////
2、模块卸载函数"用module_exit()来指定"(必须)
   当通过rmmod和modprobe -r命令卸载内核模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。
   linux内核模块于在函数一般以__exit表示说明,典型的模块卸载函数的形式如下::
static void __exit  cleanup_function(void)
{
         /*释放代码*/
}
module_exit(cleanup_function);
   模块卸载函数在模块卸载的时候执行,不返回任何值,必须以"module_exit(函数名)"的形式来指定。
通常来说,模块卸载函数要完成与模块加载函数相反的功能,如下::
1>若模块加载函数注册了XXX,则模块卸载函数应该注销XXX;
2>若模块记载函数的动态申请了内存,则模块函数应该释放该该内存。
3>若模块加载函数申请了硬件资源(中断,DMA通道、I/O端口和I/O内存等)的占用,则模块卸载函数应该释放这些硬件资源。
4>模块加载函数一般用来开启硬件,模块卸载函数一般要关闭硬件。

和__init一样,__exit也可以使用对应函数在运行完成后自动回收内存。实际上,__init和__exit都是宏,
分别定义为::
#define __init __attribute__((__section__(".init.text")))

#ifdef MODULE
#define __exit __attribute__((__section__(".exit.text")))
#else
#define __exit    \ __attribute__used____attribute((__section__(".exit.text")))
#endif
/////////////////////////////////////////////////////////////////////
3、模块许可证声明"MODULE_LICENSE("Daul BSD/GPL")"(必须)
   模块许可证(LICENSE)声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染(kernel tainted)的警告。
   在linux2.6内核中,可接受的LICENSE包括"GPL"、"GPL v2"、"GPL and additional rights"、"Dual BSD/GPL"、"Dual MPL/GPL"和"Proprietary"

大多数情况下,内核模块应遵循GPL兼容许可权。linux2.6内核模块中最常见的是以MODULE_LICENSE("Dual BSD/GPL")语句声明模块采用BSD/GPL双LICENSE.

//////////////////////////////////////////////////////////////////////
4、模块参数(可选)
“模块参数”是“模块被加载的时候可以被传递给模块的值”,它本身对应模块内部的“全部变量”。
   我们可以使用"module_param(参数名,参数类型,读/写权限)"为模块定义一个参数,例如::下列代码定义了一个整型参数和一个字符指针参数。
static char *book_name="深入浅出linux设备驱动";
static int num = 4000;
module_param(num,int,S_IRUGO);
module_param(book_name,charp,S_IRGUO);

在装载内核模块时,用户可以向内核模块传递参数,
形式为"sudo insmod/modprobe 模块名(例如linux.ko)   参数名=参数值",若果不传递,参数将使用模块内定义的默认值。

向内核模块传递参数时,参数的类型可以是byte(字节),short(短整型),ushort(无符号短整型),int,uint(无符号int),long,ulong(无符号long)、charp(字符指针)、bool或invbool(布尔的反),在模块被编译时会将module_param中声明的类型与变量定义的类型进行比较,判断是否一致。

   模块被加载后,在/sys/module目录下将出现以此模块名命名的目录。当"参数读/写权限"为0时,表示此“参数不存在sysfs文件系统下对应的文件节点”,如果此模块存在"参数读/写权限"不为0的命令行参数,在此模块的目录下将出现parameters目录,包含一系列“以参数名命名的文件节点”。同时,这些文件的权限就是通过传入module_param()的"参数读/写权限",而文件的内容为参数的值。

   除此之外,模块也可以拥有参数数组,形式为"module_param_array(数组名,数组类型,数组长,参数读/写权限)",在2.6.0~2.6.10版本,需将数组常变量名赋给"数组长",从2.6.10版本开始,需将数组长变量的指针赋给"数组长",当不需要保存实际输入的数组元数个数时,可以设置"数组长"为NULL。
  
   运行insmod和modprobe命令时,应使用逗号分割输入的数组元素。
例如::
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
   
static char *book_name="Dissecting Linux Device Driber";
static int num=4000;

static int __init book_init(void)
{
    printk(KERN_INFO"book name :%s\n",book_name);
    printk(KERN_INFO"book num :%s\n",num);
    return 0;
}

static void __exit book_exit(void)
{
    printk(KERN_INFO"Book module exit\n");
}

module_init(book_init);
module_exit(book_exit);

module_param(num,int,S_IRUGO);
module_param(book_name,charp,S_IRUGO);

MODULE_AUTHOR("chenbaihu");
MODULE_VERSION("v1.0");
MODULE_DESCRIPTION("A simple Module for testing module params");
编译该模块,Makefile为::
obj-m := module_param.o
kernel_path=/usr/src/kernels/2.6.29.6-217.2.16.fc11.i686.PAE 
//内核路径
all:
    make -C $(kernel_path)  M=$(PWD) modules
clean:
    make -C $(kernel_path)  M=$(PWD) clean
然后,运行make命令,进行编译.生成module_param.ko文件.
加载该模块.
第一种方案::
    “sudo insmod module_param.ko”命令时,运行结果为::
book name :Dissecting Linux Device Driber
book num :4000
第二种方案::
   “sudo insmod module_param.ko num=5000”命令时,运行结果为::
book name :Dissecting Linux Device Driber
book num :5000   //参数传入了。
进入/sys/module/module_param/下,输入tree命令::
.
|-- holders
|-- initstate
|-- notes
|-- parameters
|   |-- book_name     //模块参数文件
|   `-- num           //模块参数文件
|-- refcnt
|-- sections
|   `-- __param
|-- srcversion
`-- version
//////////////////////////////////////////////////////////////////////
5、模块导出符号(可选)
内核模块可以导出符号(symbol,对应与函数或变量),这样其他模块可以使用本模块中的变量和函数。

     linux2.6的"/proc/kallsyms"文件对应这内核符号表,它记录了符号以及符号符号所在的内存地址。
模块可以使用如下宏导出符号到内核符号表::
    EXPORT_SYMBOL(符号名);
    EXPORT_SYMBOL_GPL(符合名);    //只是用于GPL许可权模块。
导出的符合将可以被其他模块使用,使用前声明以下既可以。

内核模块中的符号导出(例子)::
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("Daul BSD/GPL");

int add_integar(int a,int b)
{
    return a+b;
}
int sub_integer(int a,int b)
{
    return a-b;
}

EXPORT_SYMBOL(add_integar);    //导出函数
EXPORT_SYMBOL(sub_integer);    //导出函数
编译后,sudo insmod export_symbol.ko将该模块加入内核。
从"/proc/kallsyms"中可以找到add_integae\sub_integer相关信息。
使用"cat /proc/kallsyms|grep integar"命令,就可以看到下面的结果::
f99f8048 r __ksymtab_add_integar    [export_symbol]
f99f805c r __kstrtab_add_integar    [export_symbol]
f99f8000 T add_integar    [export_symbol]

6、模块作者等信息(可选)
MODULE_AUTOR("作者信息");
MODULE_DESCRIPTION("模块描述信息");
MODULE_VERSION("版本信息");
MODULE_ALIAS("别名信息");
MODULE_DEVICE_TABLE("设备表信息");
对于USB,PCI等设备驱动,通常会创建一个MODULE_DEVICE_TABLE,表示驱动所支持的设备列表。
分享到:
评论

相关推荐

    linux 内核模块编程指导

    Linux 内核模块编程指导 Linux 内核模块编程是 Linux 操作系统内核开发的重要组成部分,本指南提供了详细的 Linux 内核开发指导,对于 Linux 内核开发者和入门者都是非常有价值的参考资料。 Linux 内核模块编程的...

    linux内核模块编程-----源码

    "Linux内核模块编程"是针对这种技术的学习与实践,旨在理解如何编写和管理这些模块。在本案例中,我们关注的焦点是如何编写一个内核模块来实现文件的读取操作。 首先,让我们讨论内核模块的基本结构。一个简单的...

    LINUX内核模块编程

    Linux内核模块编程是Linux系统开发中的一个重要环节,它允许开发者在不重新编译整个操作系统内核的情况下,添加、修改或删除内核功能。这种方式极大地提高了系统的灵活性和可扩展性。下面将详细介绍Linux内核模块的...

    LINUX内核模块编程.pdf

    LINUX内核模块编程.pdf 粗略的讲解

    linux 内核模块编程

    Linux内核模块编程是Linux系统开发中的一个重要领域,它允许开发者在不重新编译整个内核的情况下添加或修改内核功能。《Linux内核模块编程指南》是一部针对初学者的优秀教程,它详细介绍了如何编写、加载和卸载内核...

    Linux内核模块编程指南(经典)

    ### Linux内核模块编程指南(经典):知识点详解 #### 一、内核模块简介与基础知识 **1. 内核模块概念** - **定义**:内核模块是可加载到Linux内核中的独立程序片段,它允许在系统运行时动态地添加、删除或更改...

    Linux内核模块编程

    ### Linux内核模块编程 #### 一、内核模块基础 在Linux系统中,内核模块是可加载到内核中的程序片段,用于扩展或增强内核的功能而无需重启整个系统。内核模块的设计思想是使得操作系统能够动态地加载和卸载功能...

    linux内核模块编程.pdf(标记版)

    ### Linux内核模块编程知识点概览 #### 一、引言 **内核模块简介** - **定义**:内核模块是一种特殊的软件组件,可以动态加载到Linux内核中,用于扩展内核的功能而不需重启整个系统。这些模块通常用于添加硬件支持...

    很经典的linux内核模块编程指南The+Linux+Kernel+Module+Programming+Guide--2.2 && 2.6 && 2.4

    《Linux内核模块编程指南》是一本非常经典的教程,涵盖了从2.2到2.6版本的Linux内核模块开发知识。这本书对于那些想要深入理解Linux内核并编写内核模块的开发者来说,是不可或缺的参考资料。以下是该书涉及的一些...

    Linux内核模块编程指南 The Linux Kernel Module Programming Guide

    《Linux内核模块编程指南》是一本针对Linux系统内核模块开发的专业教程,旨在帮助程序员深入理解内核机制并能够编写自己的内核模块。这本书详细介绍了如何利用C语言与Linux内核进行交互,以实现对硬件设备的驱动、...

    Linux编程--Linux内核模块编程指南

    Linux内核模块编程指南 致谢 前言 第1章 Hello, World 145 1.1 内核模块的Makefiles文件 146 1.2 多重文件内核模块 147 第2章 字符设备文件 149 第3章 /proc文件系统 158 第4章 把/proc用于输入 162 第5章 把设备...

    Linux内核模块编程Report1

    【Linux内核模块编程】 Linux内核模块编程是Linux系统中一种灵活的扩展机制,允许开发者在不重新编译整个内核的情况下添加或修改功能。报告1主要涵盖了以下几个知识点: 1. **内核模块相关指令**: - **insmod**:...

    linux内核编程.pdf

    ### Linux内核模块编程基础 内核模块(Kernel Module)是Linux系统中一种能够加载到内核中运行的代码,可以动态添加到内核或从中卸载。内核模块的概念使得Linux内核可以灵活扩展,无需重启系统即可添加或移除功能。...

    linux内核模块编程(标记版)

    ### Linux内核模块编程概述 #### 什么是内核模块? 内核模块是可加载到Linux内核中的独立软件组件,它们允许系统管理员和开发者在不重新编译整个内核的情况下扩展或修改内核功能。模块可以动态加载和卸载,这使得...

    Linux内核模块编程指南

    ### Linux内核模块编程指南:深入理解与实践 #### 核心知识点概览: 1. **内核模块基本概念**:内核模块是Linux内核的动态加载组件,允许开发者扩展内核功能而不需重新编译整个内核。 2. **Hello World模块**:首...

Global site tag (gtag.js) - Google Analytics