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

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

按照《linux设备驱动开发详解》一书中的步骤实现经典例子"hello,world!"的例子。

具体步骤如下:

=============================================

1.源码如下:

/*

* hello.c -- the example of printf "hello world!" in the screen of driver program

*/

#include <linux/init.h>

#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL")/* declare the license of the module ,it is necessary */

static int hello_init(void)

{

printk(KERN_ALERT "Hello World enter!\n")

return 0

}

static int hello_exit(void)

{

printk(KERN_ALERT "Hello world exit!\n")

}

module_init(hello_init)/* load the module */

module_exit(hello_exit)/* unload the module */

进入目录:

[root@Alex_linux /]#cd /work/jiakun_test/moduletest

[root@Alex_linux moduletest]# vi hello.c

然后拷入上面书上的源码。

2.编译代码:

1>.首先我在2.4内核的虚拟机上进行编译,编译过程如下:

[root@Alex_linux moduletest]#gcc -D__KERNEL__ -I /usr/src/linux -DMODULE -Wall -O2 -c -o hello.o hello.c

其中-I选项指定内河源码,也就是内核源码树路径。编译结果:

hello.c:1:22: net/sock.h: No such file or directory

hello.c: In function `hello_init':

hello.c:6: warning: implicit declaration of function `printk'

hello.c:6: `KERN_ALERT' undeclared (first use in this function)

hello.c:6: (Each undeclared identifier is reported only once

hello.c:6: for each function it appears in.)

hello.c:6: parse error before string constant

hello.c: In function `hello_exit':

hello.c:11: `KERN_ALERT' undeclared (first use in this function)

hello.c:11: parse error before string constant

hello.c: At top level:

hello.c:13: warning: type defaults to `int' in declaration of `module_init'

hello.c:13: warning: parameter names (without types) in function declaration

hello.c:13: warning: data definition has no type or storage class

hello.c:14: warning: type defaults to `int' in declaration of `module_exit'

hello.c:14: warning: parameter names (without types) in function declaration

hello.c:14: warning: data definition has no type or storage class

在网上查询有网友提示没有引入kernel.h

解决:vi hello.c

在第一行加入:#include <linux/kernel.h>

再次编译仍然报KERN_ALERT没有声明

修改编译条件-I,再次编译:

[root@Alex_linux moduletest]#gcc -D__KERNEL__ -I /usr/src/linux -DMODULE -Wall -O2 -c -o hello.o hello.c

[root@Alex_linux moduletest]#ls

hello.c hello.o Makefile

[root@Alex_linux moduletest]#

2>.接着我尝试在2.6内核的虚拟机上进行编译

编译过程如下:

[root@JiaKun moduletest]# ls

hello.c makefile

[root@JiaKun moduletest]# vi hello.c

[root@JiaKun moduletest]# make

make -C /mylinux/kernel/2.4.18-rmk7 M=/home/alex/test/moduletest modules

make: *** /mylinux/kernel/2.4.18-rmk7: No such file or directory. Stop.

make: *** [modules] Error 2

[root@JiaKun moduletest]# vi makefile

[root@JiaKun moduletest]# make

make -C /usr/src/kernels/2.6.18-53.el5-i686 M=/home/alex/test/moduletest modules

make[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686'

scripts/Makefile.build:17: /home/alex/test/moduletest/Makefile: No such file or directory

make[2]: *** No rule to make target `/home/alex/test/moduletest/Makefile'. Stop.

make[1]: *** [_module_/home/alex/test/moduletest] Error 2

make[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686'

make: *** [modules] Error 2

[root@JiaKun moduletest]# mv makefile Makefile

[root@JiaKun moduletest]# make

make -C /usr/src/kernels/2.6.18-53.el5-i686 M=/home/alex/test/moduletest modules

make[1]: Entering directory `/usr/src/kernels/2.6.18-53.el5-i686'

CC [M] /home/alex/test/moduletest/hello.o

Building modules, stage 2.

MODPOST

CC /home/alex/test/moduletest/hello.mod.o

LD [M] /home/alex/test/moduletest/hello.ko

make[1]: Leaving directory `/usr/src/kernels/2.6.18-53.el5-i686'

[root@JiaKun moduletest]# ls

hello.c hello.ko hello.mod.c hello.mod.o hello.o Makefile Module.symvers

一、手工加载测试

1、insmod

./key_test.ko

加载驱动模块到内核

2、cat

/proc/modules

|grep

key_test

查看key_test模块在内核中的地址,不加过滤器可以看到全部加载的模块。

3、lsmod

显示模块,这时可以看到所有的模块名字,后面跟的是主设备号和次设备号。

4、rmmod

key_test

把模块从内核里卸载。

二、动态加载

1、把key_test.c源代码放到内核源代码的/drives/char/下,因为这是属字符型驱动,放在这编译到zImage中。

2、这时我们make

menuconfig

编译内核是看不到key_test这个选项的。我们把这个选项写到菜单里面才行。在内核源代码的/drives/char/下有一个Kconfig文件,打开

(1)

vi

Kconfig

加几行到里面:

config

ConFig_key_test

bool

"key

test"

//前面那个bool换成tristate就是支持模块化编译

上面句是在make

menuconfig时会出现key

test这个选项在drive/char子菜单下,bool前面是TAB键

------help----------

这句是出现在菜单选项下面的

This

key

test

help.

这句是你的驱动的说明会出现在help里面

(2)在/drivers/char目录下的Makefile文件里加上一句:

obj-$(CONFIG_key_test)

+=

key_test.o

上面这句是让Make时把key_test编译到内核中.

(3)

make

menuconfig

把key_test选项选取上

(4)

make

zImage

生成zImage文件,重启动加载这个新编的内核。

3、lsmod就能看到key_test了,但是还不能用,没有接口,也就是/dev下面没有

4、mknod

/dev/key_test

c

121

0

这是创建设备到/dev下,使普通程序可以调用了,121是在源代码里定义的它的主设备号,0是次设备号。

5、cat

/dev/key_test

这是相当于open这个设备了,或者写一个程序直接调用open、write等函数。

fd=("/dev/key_test",ORW)

第一步:准备源代码

首先我们还是要来编写一个符合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


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

原文地址: http://outofmemory.cn/yw/6207559.html

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

发表评论

登录后才能评论

评论列表(0条)

保存