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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335