Linux 内核模块简介

Linux提供了这样一种机制,这种机制被称为模块。模块具有这样的特点:

        模块本身不被编译入内核映像,从而控制了内核的大小。

        模块一旦被加载,它就和内核中的其它部分完全一样。

一、模块的组成

       一个Linux内核模块主要由如下几个部分组成:

       (1)模块加载函数

       Linux 内核模块加载函数一般以__init标识声明。

       内核模块加载函数:

       static int __init initialization_function(void)

       {

            /*初始化代码*/

       }

       module_init(initialization_function);

       模块加载函数必需以“module_init(函数名)”的形式被指定。它返回整型值,若初始化成功应返回0.而在初始化失败时,应该返回错误编码。在Linux内核里错误编码应该是一个负值,在<linux/errno.h>中定义,包含-ENODEV,

-ENOMEM之类的符号值。

       (2)模块卸载函数

       Linux内核模块卸载函数一般以__exit标识声明,典型的模块的卸载函数的形式如下所示:

       static void __exit cleanup_function(void)

       {

               /*释放代码*/

       }

       module_exit(cleanup_function);

       模块卸载函数在模块卸载的时候执行,不返回任何值,必须以"module_exit(函数名)"的形式来指定。

       (3)模块许可证声明

       MODULE_LICENSE("GPL");

       (4)模块参数

       我们可以用“module_param(参数名, 参数类型, 参数读/写权限)”为模块定义一个参数。例如下列代码定义了一个整型参数和一个字符指针参数:

       static char *book_name = "dissectting Linux Device Driver";

       static int num = 4000;

       module_param(num, int, S_IRUGO);

       module_param(book_name, charp, S_IRUGO);

       实例:带参数的内核模块

/*start*/
#include <linux/init.h>
#include <linux/module.h>


MODULE_LICENSE("GPL");

static char *book_name = "dissectting Linux Device Driver";
static int num = 4000;

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

static void __exit book_exit(void)
{
    printk(KERN_ALERT " Book module eixt\n");
}

module_init(book_init);
module_exit(book_exit);
module_param(num, int, S_IRUGO);
module_param(book_name, charp, S_IRUGO);
/*end*/

编译和运行:

模块加载:

insmod book.ko book_name=‘GoodBook‘ num=250

Message from syslogd@localhost at Mar 26 19:52:24 ...

 kernel: book name: GoodBook

Message from syslogd@localhost at Mar 26 19:52:24 ...

 kernel: book num: 250

 

模块卸载:

rmmod book

Message from syslogd@localhost at Mar 26 19:54:14 ...

 kernel: Book module eixt


       (5)模块导出符号

       Linux2.6的“/proc/kallsyms” 文件对应着内核符号表,它记录了符号及符号所在的内存地址。

       模块可以使用如下宏导出符号到内核符号表:

       EXPORT_SYMBOL(符号名);

       EXPORT_SYMBOL_GPL(符号名);

导出的符号可以被其它模块使用,使用前声明一下即可。EXPORT_SYMBOL_GPL()只适用于包含GPL许可权的模块。

       实例:内核模块中的符号导出

       

/*start*/
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

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

int sub_integar(int a, int b)
{
    return a - b;
}

EXPORT_SYMBOL(add_integar);
EXPORT_SYMBOL(sub_integar);
/*end*/

       insmod symbol.ko

       cat /proc/callsyms | grep integar

f7e42038 r __ksymtab_sub_integar[symbol]

f7e42050 r __kstrtab_sub_integar[symbol]

f7e42048 r __kcrctab_sub_integar[symbol]

f7e42040 r __ksymtab_add_integar[symbol]

f7e4205c r __kstrtab_add_integar[symbol]

f7e4204c r __kcrctab_add_integar[symbol]

f7e42000 T add_integar[symbol]

f7e42010 T sub_integar[symbol]

rmmod symbol

        (6)模块作者等信息声明

        在linux内核模块中,我们可以用 MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分别声明模块的作者、描述、版本、设备表和别名。

        对于USB、PCI等设备驱动,通常会创建一个MODULE_DEVICE_TABLE,表明该驱动模块所支持的设备:

        static struct usb_device_id skel_table[] = 

        {

              USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID),

              { }/*结束*/

        };


二、模块的编译

一个简单的Makefile:

       

/*$$*/
ifneq ($(KERNELRELEASE),)

obj-m := hello.o

else

KDIR := /lib/modules/2.6.32-358.el6.i686/build

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.ko.unsigned *.order *.symvers
endif
/*$$*/










Linux 内核模块简介,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。