• 内核模块入门之深入浅出


    出学内核模块,略做总结。希望对广大菜鸟有所帮助。

    为了不浪费大牛们的时间,在开头先列出文章中将要讲到的几个知识点,都了解的可以飘过哈!

    一、内核模块代码的特点

    二、内核模块的Makefile 的编写

    三、内核模块的安装、卸载

    四、模块的可选信息

    五、内核模块导出

    六、内核加载常见的问题

    一、内核模块代码的特点

       何谓内核模块?为什么需要内核模块?

       你当然可以不需要内核模块,只需要将代码编入内核中即可。但这又导致了内核将越来越庞大。使用内核模块还有一个特点,那就是“即插即用”,可以在系统运行是加载和卸载,大大方便了模块的使用和开发。

    那么,内核模块在代码编写上有啥特点呢?

     

    复制代码
    (1)必须有一下函数:

    //一个规范,并非一定要如下格式定义函数名:
    int hello_init() //入口
    void hello_exit() //出口
    
    

    (2)模块尾部必须如下声明:

    moudule_init(hello_init) //申明模块入口函数
    moudule_exit(hello_exit) //模块出口
    
    
    (3)没有main函数。


    (4)使用printk打印,而不是printf
    复制代码

    一个简单而又经典的hello world例子:

    复制代码
    #include <linux/moudle.h>
    #include <linux/init.h>

    static int __init hello_init()
    {
    printk("Hello World! ");
    return 0;
    }

    static void __exit hello_exit()
    {
    printk("<7>hello <0>exit ");
    }

    moudule_init(hello_init);
    moudule_exit(hello_exit);
    复制代码

    怎么样?看完代码你是不是会大喊一声:尼玛,怎么会这么简单?尼玛,他就是那么简单啊。

    二、内核模块的Makefile 的编写

    了解了内核模块的基本格式后,开始来编写makefile。

    以下是一个最简单的内核模块的makefile格式:

    复制代码
    #ifneq:不等于空  (第一次为空,走else,第二次才走下面)
    ifneq ($(KERNELRELEASE),)

    #内核模块名字
    obj-m := hello.o
    hello-objs := main.o add.o

    else

    #内核源码的路径
    KDIR := /lib/modules/2.6.18-53.el5/build

    all:

    #
    # -C : 表示进入$(KDIR)目录后使用目录中的makefile进行编译
    # M=$(PWD): 表示当前目录,modules:目标模块
    make -C $(KDIR) M=$(PWD) modules

    clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.symvers


    endif
    复制代码

    按照这个模块,我们只要对“内核模块名”和“内核源码的路径”两项根据你的实际情况进行修改即可。

    尼玛,怎么又是那么简单?不是忽悠广大网友吧?

    这里LZ以RP保证,正的就是这么简单。想学点难的?别急,好戏还在后头呢。

    三、内核模块的安装、卸载

    编译完模块后,我们当然需要来安装、卸载,使用如下命令:

    加载:insmod (insmod hello.ko)
    卸载:rmmod (rmmod hello)
    查看:lsmod
    加载:modprobe (modprobe hello)

    modprobe 与 insmod 都是加载一个模块到内核中,区别在于:
    modprobe会根据文件modules.dep查看要加载的模块是否依赖于其他模块,如果是,modprobe会首先安装依赖的模块加载到内核中。

    四、模块的可选信息

    1.你或许会在内核模块中看到以下代码:

    MOUDLE_LICENSE("GPL");                //指定这个模块遵守的协议
    MOUDLE_AUTHOR("Pearry Zhou"); //模块作者
    MOUDLE_DESCRIPTION("XX Module"); //模块描述
    MOUDLE_VERSION("V1.0"); //模块版本
    MOUDLE_ALIAS("a simple module"); //模块别名

    2.模块参数

    我们知道int main(int argc, char** argv), argc表示命令行输入的参数个数,argv中保存你输入的参数。

    那么内核模块中可以通过命令行输入参数么?当然可以。

    通过宏moudle_param指定模块参数,模块参数用于在加载模块时传递参数给模块。

    moudle_param(name,type,perm)
    这样你就定义了一个 变量名为name,类型为type,访问权限为perm的变量。

    前两个就不用解释了吧?

    第三个,perm常见的值有:
    S_IRUGO : 任何用户都对/sys/module中出现的该参数有读权限
    S_IWUSR : 允许root用户修改/sys/module中出现的该参数

    例如:

    int a = 3;
    char *st;
    module_param(a,int,S_IRUGO);
    module_param(st,charp,S_IRUGO);

    (注:charp为字符串指针)

    模块参数的使用:

    insmod xx.ko a=4    //例子中的参数a被修改为4.你也可以在函数中把a打印出来查看结果

    是不是很神奇?赶紧动手尝试下吧。邓爷爷教导我们:实践是检验真理的唯一标准。这句话TMD就是说给我们程序员听的啊,有没有发现,很多时候写完代码发现很多低级错误都是因为自己“想当然”的这么认为而没有去实践过导致的?(比如 int *p; ... p +=2; p真的是p的地址+2么?赶紧膜拜邓爷爷吧!没有邓爷爷)

    五、内核模块导出

    1.为什么要有“内核模块导出”这个概念?

    当你的模块中调用了其他模块的接口时,即使你已经加载了所依赖的模块,
    在加载你的模块的时候还是会出现加载失败(提示:insmod:-1 Unknow symbl in module),
    这个时候,就必须用到内核模块导出。


    2.如何导出?

    假设一个模块中存在函数:
    int add_int(int a,int b);
    int sub_int(int a,int b);

    则使用如下宏导出:

    EXPORT_SYMBOL(add_int);
    EXPORT_SYMBOL(sub_int);

    导出之后,其他模块即可使用extern引用(当然所依赖模块必须已经加载)。

    类似的导出宏还有:EXPORT_SYMBOL_GPL //只能用于GPL许可证的模块。

    六、内核加载常见的问题

    1.版本不匹配

    (1)出现如下错误表示版本不匹配
    insmod hello.ko
    disagrees about version ofsymbol struct_module
    insmod:error inserting 'hello.ko':-1 Invalid module format


    (2)解决方法

    I.使用modprobe --force-modversion命令强行插入

    II.查看内核版本命令:uname -r
    确保内核模块版本与当前内核版本相同,更换内核版本等手段

    因为在编译内核模块的时候需要在makefile中指定内核源代码,
    所以只要指定的内核源码与使用的当前内核版本相同即可,可在代码的makefile最前面看到版本号:

    VERSION = 3
    PATCHLEVEL = 2
    SUBLEVEL = 6

    即内核版本为3.2.6

    总之:代码中makefile中的版本号,要与uname -r显示出来的版本号相同。

  • 相关阅读:
    【EOSS】Windows 环境编译 Apollo 代码
    【配置与安装】CentOS7 升级gcc版本
    【Linux系统】Linux 环境变量 PATH 的增添和删除
    【C++百科】std(标准库)与 stl(标准模板库)的关系
    【C++语法】C ++ 函数后面加 throw() 的作用
    【gtest/gmock】gtest:Google C++ Testing Framework
    【Linux文件系统】inode(索引节点)和 block(块)
    【Linux命令】ls -l显示的内容中total到底是什么?
    【vi】Linux vi/vim 简介
    【DOS命令】color
  • 原文地址:https://www.cnblogs.com/spinsoft/p/3288317.html
Copyright © 2020-2023  润新知