Linux内核动态模块开发入门

写在前面

  这篇文章旨在介绍Linux内核动态模块开发时的基本结构,以及如何编译开发的模块。




Linux内核模块开发

  我们知道Linux的内核是可以定制的,在编译之前我们可以通过make menuconfig对我们的内核进行配置。其中,在配置的过程中,有些模块我们可以选择直接编入内核,或者编成模块动态使用,或者不编译。
menuconfig.png
  如上图所示,当我们选择编译成模块的话,在menuconfig中的配置就可以选择为M,直接编入内核的话就可以选择*,不编译就选空。
  所以,当我们开发的内容的内容相对于原本的内核比较独立的时候,我们就可以使用模块开发,并且可以选择性的配置。



模块开发的结构

  从结构上来说,Linux内核一个模块的规范还是比较简单的:初始化处理和结束处理。我们可以看一段最为简单的模块代码。

/*===========================================================================
FILE:
   printk_test.c

DESCRIPTION:
   moddule printk test
==========================================================================*/

//---------------------------------------------------------------------------
// Include Files
//---------------------------------------------------------------------------

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static void __init TestInit( void )
{
    printk(KERN_INFO "This is test start.\n" );
}

static void __exit TestExit( void )
{
    printk(KERN_INFO "Test over.\n" );
}


module_init( TestInit );
module_exit( TestExit );

  在上面的一段代码中,只要掌握里面四个地方,就能做简单的内核模块的开发了。

__init和module_init(…)
  当一个模块加载的时候(insmod),最先执行的就是使用__init声明的这个函数。当然我在上面作为示例,只做了一些打印信息的操作。在实际开发中,通常在这个函数里面,我们可以做一些初始化的操作,比如参数检查、线程创建等等。初始化完成后,就可以执行我们所需要的具体操作了(执行线程主任务等)。
  另外,我们还需要告诉内核,加载这个模块的时候从这个__init函数里面开始执行。告诉内核的方法就是module_init(…)这个函数,使用方法很简单,仿照上面的那段代码就可以。


__exit和module_exit(…)
  相对应的,我们在卸载一个模块的时候(rmmod),启动的就是用__exit声明的函数。同样的,在这个函数里面我们就可以做一些结束处理,比如内存释放,文件关闭,线程关闭等等。
  当然,我们还是需要告诉内核,在卸载模块的时候调用这个结束函数——module_exit(…)。



内核模块编译

准备工作

  Linux的内核是开源的,所以我们可以从官网上下载它的源码包。当然,从它的官网下载可能会比较慢,这里推荐一个下载较快的上海交通大学的ftp服务器,根据需要选择内核的版本进行下载。
  内核下载地址
  我们把内核源码包拷贝到虚拟机,解压。(我在这里下载了一个3.10版本的内核)
  $ tar -zxvf linux-3.10.tar.gz &emsp;&emsp;
  解压完成后,我们就要把我们的这个测试模块加进内核的工程编译了。
  Linux内核的工程结构是比较复杂的,我们如果加一个对原本没什么影响的模块,那么就不需要修改原本的内核代码,只需要在工程中加入我们的模块就可以,在这里需要修改两个内容:KconfigMakefile

Kconfig
  这个文件的功能就是我们在最开始提到的配置文件了,我们通过修改和添加Kconfig文件,来使得我们能在make menuconfig时,对我们新加的文件进行配置。
  以这次的示例,我们加一个打印模块的测试。
  首先,在源码工程中的**/linux-3.10/driver/** 下面建立一个我们的文件夹目录printktest,把我们的代码printk_test.c拷贝到这个路径下。
  然后,我们在这个路径下面添加Kconfig文件:
  config PRINTK_TEST
    tristate "PRINT INFOMATION"
    ---help---
    JUST TEST
  在这个Kconfig文件中,第一行是对我们这个模块的命名,编译的时候,它会告诉编译器是否要编译这个模块,所以它同样会在之后的Makefile中体现。第二行有一个关键字tristate,这个单词是告诉make menuconfig它可以选择静态编译到内核里,或者动态编译为ko文件,再或者选择不编译。help后的文件就是对模块的解释说明了。

—————————————————————————————————

  写好了这个Kconfig文件,我们需要告诉原本的工程,把这个新加的模块包含进去。这个时候需要修改原本的Kconfig文件,把我们自己写的Kconfig文件包含进工程。是哪个文件呢,通常就是上级目录的Kconfig文件。在本例中,我们的模块路径在/driver/printktest里面,所以打开/driver/路径下面的Kconfig文件,添加一行。
  source "drivers/printktest/Kconfig"
  现在,我们可以再源码的根目录执行make menuconfig,看看它能否找到我们所添加的这一模块。
module_select.png
  我们可以很容易在Device Drivers下面找到我们添加的PRINT INFORMATION TEST,按下M将其配置成动态模块编译,保存退出。这个时候在内核源码的根目录下面会生成一个隐藏文件**.config**,我们在终端下使用vim编辑器可以看到,这个隐藏文件里面,可以找到我们刚刚所作的操作——有一个变量CONFIG_PRINTK_TEST,这个变量被设置成了M。这个变量就是我们Kconfig文件中声明的模块名,Linux对配置文件的命名规则就是再加一个CONFIG。而这个变量就会在接下来写Makefile中用到。


Makefile
  Makefile这个东西,想必大家都很熟悉它的作用了。上面是让我们的模块在make menuconfig的时候可配置,但到了真正的编译还是靠Makefile起的作用,所以我们也需要添加和修改Makefile。
  与Kconfig文件类似,首先还是要在我们模块的路径下加一个Makefile文件,因为本例比较简单就一个c文件,所以Makefile也比较简单,就一行:
  obj-$(CONFIG_PRINTK_TEST) += printk_test.o
  这个时候我们可以看到,在Makefile里面有一个很眼熟的东西,就是CONFIG_PRINTK_TEST。这个就是我们通过make menuconfig后生成.config文件里面的那个变量,我们选择静态编译、动态编译或者不编译,就是通过这个变量来实现的。

—————————————————————————————————

  同样的,我们还需要在上一级目录/driver的Makefile里面加一句话,把包含我们新加的路径:
  obj-$(CONFIG_PRINTK_TEST) += /printktest



编译

  当我们开发给不同的平台时,会用到交叉编译工具。对于交叉编译的情况,首先要安装配置交叉工具链,然后再对内核进行make menuconfig配置,配置结束后,再编译。对于交叉编译工具的配置这里暂时不做详解了。编译方法同我们编译其他工程一样,make或者make后跟你所需要的参数。




使用

  编译完成后,我们可以在我们新建的路径下找到后缀名为ko的驱动文件,当然这个驱动文件在交叉编译的build机上是无法运行的,需要在host上,也就是开发板上挂载。
  挂载时,到ko文件所在的路径运行:
  $ insmod printk_test.ko
  我们也可以查看当前已经挂载的驱动:
  $ lsmod
  卸载驱动时,不管在什么路径下,运行:
  $ rmmod printk_test.ko

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值