如何编译一个linux下的驱动模块

如何编译一个linux下的驱动模块,第1张

首先,我们要了解一下模块是如何别被构造的。模块的构造过程与用户空间

的应用程序的构造过程有显著不同;内核是一个大的、独立的程序

,

对于它的各

个部分如何组合在一起有详细的明确的要求。

Linux2.6

内核的构造过程也与以

前版本的内核构造过程不同;

新的构造系统用起来更加简单,

并且可产生更加正

确的结果

,

但是它看起来和先前的方法有很大不同。内核的构造系统非常复杂

,

我们所看到的只是它的一小部分。

如果读者想了解更深入的细节,

则应阅读在内

核源码中的

Document/kbuild

目录下的文件。

在构造内核模块之前,

有一些先决条件首先应该得到满足。

首先,

读者要保证你

有适合于你的内核版本的编译器、模块工具

,

以及其他必要工具。在内核文档目

录下的文件

Documentation/Changes

里列出了需要的工具版本;

在开始构造内

核前,

读者有必要查看该文件,

并确保已安装了正确的工具。

如果用错误的工具

版本来构造一个内核

(

及其模块

)

,可能导致许多奇怪的问题。另外也要注意

,

使

用太新版本的编译器偶尔可能也会导致问题。

一旦做好了上面的准备工作之后

,

其实给自己的模块创建一个

makefile

则非常

简单。实际上

,

对于本章前面展示的

" hello world"

例子

,

下面一行就够了

:

obj-m := hello.o

如果读者熟悉

make

但是对

Linux2.6

内核构造系统不熟悉的话

,

可能奇怪这个

makefile

如何工作。毕竟上面的这一行不是一个传统的

makefile

的样子。问

题的答案当然是内核构造系统处理了余下的工作。上面的赋值语句

(

它利用了由

GNU make

提供的扩展语法

)

说明有一个模块要从目标文件

hello.o

构造,而从

该目标文件构造的模块名称为

hello.ko.

如果我们想由两个源文件

(

比如

file1.c

file2.c )

构造出一个名称为

module.ko

的模块

,

则正确的

makefile

可如下编写

:

obj-m := module.o

module-objs := file1.o file2.o

为了让上面这种类型的

makefile

文件正常工作

,

必须在大的内核构造系统环境

中调用他们。假设读者的内核源码数位于

~/kernel-2.6

目录

,

用来建立你的模

块的

make

命令

(

在包含模块源代码和

makefile

的目录下键入

)

应该是

:

make -C ~/kernel-2.6 M=`pwd` modules

这个命令首先是改变目录到用

-C

选项指定的位置

(

即内核源代码目录

)

,其中保

存有内核的顶层

makefile

文件。这个

M=

选项使

makefile

在构造

modules

标前

,

返回到模块源码目录。

然后,

modules

目标指向

obj-m

变量中设定的模块,

在上面的例子里,我们将该变量设置成了

module.o

上面这样的

make

命令对于多个文件的编译显得不是很方便

,

于是内核开发者就

开发了一种

makefile

方式

,

这种方式使得内核树之外的模块构造变得更加容易。

代码清单

1.4

展示了

makefile

的编写方法:

代码清单

1.4 makefile

ifeq ($(KERNELRELEASE),)

KERNELDIR ?= /source/linux-2.6.13

PWD := $(shell pwd)

modules:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:

rm -rf *.o *~ core .depend .*. *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else

obj-m := hello.o

endif

我们再次看到了扩展的

GNU

make

语法在起作用。在一个典型的构造过程中,这

makefile

将被读取两次。当从命令行中调用这个

makefile ,

它注意到

KERNELRELEASE

变量尚未设置。我们可以注意到,已安装的模块目录中存在一

个符号连接,

它指向内核的构造树,

这样这个

makefile

就可以定位内核的源代

码目录。如果读者时间运行的内核并不是要构造的内核,则可以在命令行提供

KERNELDIR=

选项或者设置

KERNELDIR

环境变量

,

或者修改

makefile

中设置

KERNELDIR

的那一行。在找到内核源码树

,

这个

makefile

会调用

default:

,

这个目标使用先前描述过的方法第二次运行

make

命令

(

注意,在这个

makefile

make

命令被参数化成

$(MAKE))

,以便运行内核构造系统。在第二

次读取

makefile

时,

它设置了

obj-m,

而内核的

makefile

负责真正构造模块。

这种构造模块的机制看起来很繁琐,可是,一旦我们习惯了使用这种机制

,

则会

欣赏内核构造系统带给我们的便利。需要注意的是

,

上面

makefile

并不完整,

一个真正的

makefile

应包含通常用来清除无用文件的目标

,

安装模块的目标等

等。一个完整的例子可以参考例子代码目录的

makefile

第一步:准备源代码

首先我们还是要来编写一个符合linux格式的模块文件,这样我们才能开始我们的模块编译。假设我们有一个源文件mymod.c。它的源码如下:

mymodules.c

1. #include <linux/module.h>/* 引入与模块相关的宏 */

2. #include <linux/init.h> /* 引入module_init() module_exit()函数 */

3. #include <linux/moduleparam.h>/* 引入module_param() */

4

5. MODULE_AUTHOR("Yu Qiang")

6. MODULE_LICENSE("GPL")

7

8. static int nbr = 10

9. module_param(nbr, int, S_IRUGO)

10.

11. static int __init yuer_init(void)

12.{

13.int i

14.for(i=0i<nbri++)

15.{

16.printk(KERN_ALERT "Hello, How are you. %d/n", i)

17.}

18.return 0

19.}

20.

21.static void __exit yuer_exit(void)

22.{

23.printk(KERN_ALERT"I come from yuer's module, I have been unlad./n")

24.}

25.

26. module_init(yuer_init)

27. module_exit(yuer_exit)

我们的源文件就准备的差不多了,这就是一个linux下的模块的基本结构。第9行是导出我们的符号变量nbr。这样在你加载这个模块的时候可以动态修改这个变量的值。稍后将演示。yuer_init()函数将在模块加载的时候运行,通过输出的结果可以看到我们的模块是否加载成功。

第二步:编写Makefile文件

首先还是来看看我们Makefile的源文件,然后我们再来解释;

Makefile

obj-m := modules.o #要生成的模块名

modules-objs:= mymod.o#生成这个模块名所需要的目标文件

KDIR := /lib/modules/`uname -r`/build

PWD := $(shell pwd)

default:

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

clean:

rm -rf *.o .* .cmd *.ko *.mod.c .tmp_versions

ARM平台

Makefile

obj-m += mymod.o

KDIR := /home/workspace2/kernel/linux-2.6.25#如果是用于arm平台,则内核路径为arm内核的路径

PWD = $(shell pwd)

all:

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

clean:

rm -rf *.o

在arm板上插入是

insmod mymod

如果出现以下错误

insmod: chdir(/lib/modules): No such file or directory

则运行

mkdir /lib/modules/2.6.25 (与arm内核版本相同)

并将mymod.ko文件复制到该目录下

cp mymod.ko /lib/modules/2.6.25

然后再执行 (insmod 只在/lib/modules/2.6.25目录下查找相关驱动模块)

insmod mymod

现在我来说明一下这个Makefile。请记住是大写的Makefile而不是小写的makefile;

obj-m :这个变量是指定你要声称哪些模块模块的格式为 obj-m := <模块名>.o

modules-objs :这个变量是说明声称模块modules需要的目标文件 格式要求 <模块名>-objs := <目标文件>

切记:模块的名字不能取与目标文件相同的名字。如在这里模块名不能取成 mymod;

KDIR :这是我们正在运行的 *** 作系统内核编译目录。也就是编译模块需要的环境

M= :指定我们源文件的位置

PWD :这是当前工作路径$(shell )是make的一个内置函数。用来执行shell命令。

第三步:编译模块

现在我们已经准备好了我们所需要的源文件和相应的Makefile。我们现在就可以编译了。在终端进入源文件目录输入make

运行结果:

make[1]: Entering directory `/usr/src/linux-headers-2.6.24-24-generic'

CC [M] /home/yuqiang/桌面/mymodule/mymodules.o

LD [M] /home/yuqiang/桌面/mymodule/modules.o

Building modules, stage 2.

MODPOST 1 modules

CC /home/yuqiang/桌面/mymodule/modules.mod.o

LD [M] /home/yuqiang/桌面/mymodule/modules.ko

make[1]: Leaving directory `/usr/src/linux-headers-2.6.24-24-generic'

第四步:加载/卸载我们的模块

从上面的编译中我可以看到。已经有一个modules.ko生成了。这就是我们的模块了。现在我们就可以来加载了。

首先在终端输入:sudo insmod modules.ko

现在我们来看看我们的模块加载成功没有呢?

在终端输入:dmesg | tail -12这是查看内核输出信息的意思。tail -12 显示最后12条;

显示结果如下:

[17945.024417] sd 9:0:0:0: Attached scsi generic sg2 type 0

[18046.790019] usb 5-8: USB disconnect, address 9

[19934.224812] Hello, How are you. 0

[19934.224817] Hello, How are you. 1

[19934.224818] Hello, How are you. 2

[19934.224820] Hello, How are you. 3

[19934.224821] Hello, How are you. 4

[19934.224822] Hello, How are you. 5

[19934.224824] Hello, How are you. 6

[19934.224825] Hello, How are you. 7

[19934.224826] Hello, How are you. 8

[19934.224828] Hello, How are you. 9

看到了吧。我们的模块的初始化函数yuer_init()已经成功运行了。说明我们的模块已经加载成功;

现在我们再来卸载模块试试看。

在终端输入:sudo rmmod modules

在终端输入:dmesg | tail -3

[19934.224826] Hello, How are you. 8

[19934.224828] Hello, How are you. 9

[20412.046932] I come from yuer's module, I have been unlad.

可以从打印的信息中看到,我们的模块的退出函数已经被执行了。说明我们的模块已经被成功的卸载了。到目前位置我们就已经算是对模块的编译到编译运行算是有了一个整体上的认识了。对于以后深入的学习还是应该有点帮助的。下面我们将在看看于模块相关的一些简单的 *** 作。

第五步:加载模块时传递参数

在终端输入:sudo insmod module_name.ko nbr=4

在终端输入:dmesg | tail -6

显示结果如下:

[20800.655694] Hello, How are you. 9

[21318.675593] I come from onefile module, I have been unlad.

[21334.425373] Hello, How are you. 0

[21334.425378] Hello, How are you. 1

[21334.425380] Hello, How are you. 2

[21334.425381] Hello, How are you. 3

这样我们就可以看到在模块加载的时候动态设置了我们的一个变量。初始化函数中的循环只执行了4次。

可能你会问我怎么知道一个模块可以设置那些变量呢。当然,你可以先不设变量加载一次。然后可以在终端输入ls /sys/module/<modules_name>/parameters/来查看。在这里我们是这样输入的

在终端输入:ls /sys/moedle/modules/parameters/

显示结果:

nbr

如果我们的模块加载成功了。最后我们还可以通过modinfo来查看我们的模块信息。如下

在终端输入:sudo modinfo modules.ko

显示结果:

filename: modules.ko

license:GPL

author: Yu Qiang

srcversion: 20E9C3C4E02D130E6E92533

depends:

vermagic: 2.6.24-24-generic SMP mod_unload 586

parm: nbr:int

以装载和卸载模块为例:

1、首先输入代码

#include <linux/init.h>

#include <linux/module.h>

2、然后输入下方的代码:

static int my_init(void)

{

                    return  0

}

static void my_exit(void)

3、然后在输入下方的代码:

{

                    return

}

module_init(my_init)

module_exit(my_exit)这样就完成了。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/8546360.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-17
下一篇2023-04-17

发表评论

登录后才能评论

评论列表(0条)

    保存