• Linux内核模块简介


    Linux内核模块简介

    1. 目录

    2. 概述

    3. 模块开发常用的指令

    4. 内核模块程序结构

    5. 模块使用计数

    6. 模块的编译

    7. 使用模块绕开GPL

    8. 总结

     

    二.概述                                                                                       

    Linux内核整体结构已经很庞大,包含了很多的组件,而对于我们工程师而言,有两种方法将需要的功能包含进内核当中。

             一:将所有的功能都编译进Linux内核。

             二:将需要的功能编译成模块,在需要的时候动态地添加。

    上述两种方式优缺点分析:

             第一种:

                       优点:不会有版本不兼容的问题,不需要进行严格的版本检查

                       缺点:生成的内核会很大;要在现有的内核中添加新的功能,则要编译整个内核

             第二种:

    优点:模块本身不编译进内核,从而控制了内核的大小;模块一旦被加载,将和其它的部分完全一样。

    缺点:可能会有内核与模块版本不兼容的问题,导致内核崩溃;会造成内存的利用率比较低。

    三.模块开发常用的指令                                                                

    在内核模块开发的过程中常用的有以下指令。

    1)  insmod: 将模块插入内核中,使用方法:#insmod XXX.ko

    2)  rmmod: 将模块从内核中删除,使用方法:#rmmod XXX.ko

    3)  lsmod: 列表显示所有的内核模块,可以和grep指令结合使用。使用方法:#lsmod | grep XXX

    4)  modprobe: modprobe可载入指定的个别模块,或是载入一组相依赖的模块。modprobe会根据depmod所产生的依赖关系,决定要载入哪些模块。若在载入过程中发生错误,在modprobe会卸载整组的模块。依赖关系是通过读取 /lib/modules/2.6.xx/modules.dep得到的。而该文件是通过depmod 所建立。

    5)  modinfo: 查看模块信息。使用方法:#modinfo XXX.ko

    6)  tree –a: 查看当前目录的整个树结构。使用方法:#tree -a

     

    四.内核模块程序结构                                                                   

    1)  模块加载函数(一般需要)

    在用insmod或modprobe命令加载模块时,该函数被执行。完成模块的初始化工作。

    Linux内核的模块加载函数一般用__init标识声明,模块加载函数必须以module_init(函数名)的形式被指定。该函数返回整型值,如果执行成功,则返回0,初始化失败时则返回错误编码,Linux内核当中的错误编码是负值,在<linux/errno.h>中定义。

    在Linux中,标识__init的函数在连接时放在.init.text这个区段,而且在.initcall.init中保留一份函数指针,初始化的时候内核会根据这些指针调用初始化函数,初始化结束后释放这些init区段(包括前两者)。

    代码清单:

    复制代码
     1 static int __init XXX_init(void)
     2 
     3 {
     4 
     5          return 0;
     6 }
     7 
     8  
     9 
    10 moudle_init(XXX_init);
    复制代码

    2)  模块卸载函数(一般需要)

    在用rmmod或modprobe命令卸载模块时,该函数被执行。完成与加载相反的工作。

    模块的卸载函数和模块加载函数实现相反的功能,主要包括

    1. 若模块加载函数注册了XXX,则模块卸载函数注销XXX
    2. 若模块加载函数动态分配了内存,则模块卸载函数释放这些内存
    3. 若模块加载函数申请了硬件资源,则模块卸载函数释放这些硬件资源
    4. 若模块加载函数开启了硬件资源,则模块卸载函数一定要关闭这些资源

    代码清单:

    复制代码
    1 static void __exit XXX_exit(void)
    2 
    3 {
    4 
    5 }
    6 
    7  
    8 
    9 moudle_exit(XXX_exit);
    复制代码

    3)  模块许可证声明(必须)

    如果不声明,则在模块加载时会收到内核被污染的警告,一般应遵循GPL协议。

    代码清单:

    1 MODULE_LICENSE("GPL");

    4)  模块参数(可选)

    模块在被加载时传递给模块的值,本身应该是模块内部的全局变量。

    示例程序book.c

    复制代码
     1 #include <linux/init.h>
     2 
     3 #include <linux/module.h>
     4 
     5  
     6 
     7 static char *bookName = "Good Book.";
     8 
     9 static int bookNumber = 100;
    10 
    11  
    12 
    13 static int __init book_init(void)
    14 
    15 {
    16 
    17          printk(KERN_INFO "Book name is %s
    ", bookName);
    18 
    19          printk(KERN_INFO "Book number is %d
    ", bookNumber);
    20 
    21          return 0;
    22 
    23 }
    24 
    25  
    26 
    27 static void __exit book_exit(void)
    28 
    29 {
    30 
    31          printk(KERN_INFO "Book module exit.
    ");
    32 
    33 }
    34 
    35  
    36 
    37 module_init(book_init);
    38 
    39 module_exit(book_exit);
    40 
    41 module_param(bookName, charp, S_IRUGO);
    42 
    43 module_param(bookNumber, int, S_IRUGO);
    44 
    45  
    46 
    47 MODULE_LICENSE("GPL");
    复制代码

    在向内核插入模块的时候可以用以下方式,并且可以在内核日志中看到模块加载以后变量已经有了值。

     

    5)  模块导出符号(可选)

    使用模块导出符号,方便其它模块依赖于该模块,并使用模块中的变量和函数等。

    在Linux2.6的内核中,/proc/kallsyms文件对应着符号表,它记录了符号和符号对应的内存地址。对于模块而言,使用下面的宏可以导出符号。

    1 EXPORT_SYMBOL(符号名);

    1 EXPORT_GPL_SYMBOL(符号名);

    6)  模块信息(可选)

    模块信息则是指模块的作者信息等。

    五.模块使用计数                                                                     

    Linux内核提供了MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT宏来管理模块使用计数。但是对于内核模块而言,一般不会自己管理使用计数。

    六.模块的编译                                                                         

    将下面的Makefile文件放在book.c同级的目录下,然后使用#make命令或者#make all命令编译即可生成book.ko模块文件。

    对应的Makefile:

    复制代码
     1 ifneq ($(KERNELRELEASE),)
     2 
     3 mymodule_objs := book.o
     4 
     5 obj-m := book.o
     6 
     7 else
     8 
     9 PWD := $(shell pwd)
    10 
    11 KVER ?= $(shell uname -r)
    12 
    13 KDIR := /usr/src/linux-headers-2.6.38-8-generic
    14 
    15  
    16 
    17 all:
    18 
    19          $(MAKE) -C $(KDIR) M=$(PWD)
    20 
    21 clean:
    22 
    23          rm -rf *.mod.c *.mod.o *.ko *.o *.tmp_versions *.order *symvers
    24 
    25 endif
    复制代码

    七.使用模块绕开GPL                                                                

    如果功能不编译成模块,则无法绕开GPL,编译成模块后公司发布产品则只需要发布模块,而不需要发布源码。为了Linux系统能够支持模块,需要做以下的工作:

    1. 内核编译时选择“可以加载模块”,嵌入式产品一般都不需要卸载模块,则可以不选择“可卸载模块”
    2. 将我们的ko文件放在文件系统中
    3. Linux系统实现了insmod、rmmod等工具
    4. 使用时可以用insmod手动加载模块,也可以修改/etc/init.d/rcS文件,从而在系统启动的时候就加载模块。

    八.总结                                                                                   

    本文主要介绍内核模块的概念和基本编程方法,内核模块主要由加载、卸载函数功能函数等一系列声明组成。它可以被传入参数,也可以导出符号,供其它的模块使用。

     
     
  • 相关阅读:
    fzuoj Problem 2177 ytaaa
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Capture the Flag
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Team Formation
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Beauty of Array
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Lunch Time
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Convert QWERTY to Dvorak
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest May Day Holiday
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Demacia of the Ancients
    zjuoj The 12th Zhejiang Provincial Collegiate Programming Contest Ace of Aces
    csuoj 1335: 高桥和低桥
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3449444.html
Copyright © 2020-2023  润新知