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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何编译一个linux下的驱动模块相关的知识,希望对你有一定的参考价值。

  这是一个简单而完整的实例,对于理解Linux下的驱动模块是非常有帮助的。

  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

3.执行代码,加载驱动模块:
  2.4内核加载模块:
insmod ./hello.o
但是此时并没有输出printk打印的信息。但是可以在/var/log/messages 中看到打印的信息,这是由于KERN_ALERT优先级不够高。这里
需要修改为:KERN_EMERG。再次编译,加载模块即可以看到结果
2.6内核加载模块:
[root@JiaKun moduletest]# insmod hello.ko
[root@JiaKun moduletest]#
Message from syslogd@ at Sat Jul 26 19:52:44 2008 ...
JiaKun kernel: Hello, world
  有的朋友可能会出现insmod命令找不到的错误,这可能有下面几个原因:
<1> 你的系统没有安装module-init-tools工具,关于此问题,只需安装即可,但是一般装完系统是有这个命令的。
<2> 环境变量没有添加导致不能使用该命令。使用echo $PATH即可查看PATH环境变量,发现没有/sbin这个路径,所以你当然不能使用insmod这个命令了。解决的方法很简单,只需在命令行输入:
PATH = "$PATH:/sbin"即可添加。(insmod在/sbin这个目录下,你可以使用whereis insmod查看)。
<3> insmod这个命令需要在root权限下才能使用。
加载完成后你可以输入lsmod查看hello这个模块哦。

4.卸载驱动模块:rmmod hello.
  加载模块后就可在屏幕上看到如下信息:Hello world enter.
卸载时就可在屏幕上看到如下信息:hello world exit.
  [root@JiaKun moduletest]# rmmod hello.ko
[root@JiaKun moduletest]#
Message from syslogd@ at Sat Jul 26 19:52:58 2008 ...
JiaKun kernel: Goodbye, cruel world

另外,如果有多个文件,则按下列方式编写Makefile文件(file1.c、file2.c):
obj -m := modulename.o
module-objs := file1.o file2.o
参考技术A 直接写Makefile 文件(假设要编译的文件为hello.c)

1 ifneq ($(KERNElRELEASE),)

2 obj-m := hello.o

3 else

4 obj-m := hello.o

5 KERNELDIR ?= /lib/modules/$(shell uname -r)/build

6 PWD := $(shell pwd)

7

8 default:

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

10 endif

第一个ifeq ($(KERNELRELEASE),)目前,并无用处,它的由来是指在Linux源码根目录下的Makefile编译内核时,KERNELRELEASE宏会被定义,那么如果是从源码根目录开始的make则会将myhello.o模块编译进内核。

KERNELDIR ?= /usr/src/$(shell uname -r) ,这句是对KERNELDIR进行赋值,这个变量是后面用到的指代内核源码目录用的。

PWD := $(shell pwd),这句是对PWD变量进行赋值,作用是将$(shell pwd)的返回结果既求得当前目录的路径赋值给PWD,这个变量在后面指代要编译的驱动程序所在的位置。

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

这句是Makefile的规则:这里的$(MAKE)就相当于make,-C 选项的作用是指将当前工作目录转移到所指定的位置。“M=”选项的作用是,当用户需要以某个内核为基础编译一个外部模块的话,需要在make modules 命令中加入“M=dir”,程序会自动到所指定的dir目录中查找模块源码,将其编译,生成ko文件。
参考技术B 首先,我们要了解一下模块是如何别被构造的。模块的构造过程与用户空间 的应用程序的构造过程有显著不同;内核是一个大的、独立的程序 , 对于它的各 个部分如何组合在一起有详细的明确的要求。 Linux2.6 内核的构造过程也与以 前版本的内核构造.

linux如何加载驱动

   linux操作系统下,加载驱动的方式有二:

    静态加载驱动;

    动态加载驱动;

   作为前者,静态加载驱动是通过将驱动程序编译到内核而进行的一系列配置操作;对于后者而言则是向内核注册设备信息,从而在kernel启动后,再通过insmod指令,关联好主、次设备号,从而以模块的形式进行加载的;

   二者各有优点,所以应用的场合也是不一样的;

参考技术A 在此Linux驱动开发采用网络的方式,介绍两种驱动开发的方法:

一.驱动编译到内核

1.先选择一个放置驱动代码的位置,例:drivers/char/xxx.c

2.在drivers/char/Kconfig文件添加以下内容:

menu " xxx support"

config xxx

tristate "xxx support"

---help---

xxx use gpio as ir input .

If you want xxx support, you should say Y here and also to the

specific driver for your bus adapter(s) below.

This xxx support can be built as a module.

endmenu

3.编写Makefile,位置与上相同(drivers/char/),内容如下:

obj-$(CONFIG_xxx) += xxx.o //黑色xxx与上面Kconfig中的xxx相同,而红色xxx应与

驱动xxx.c的命名相同

4.修改上一级Makefile和Kconfig //根据自己驱动的位置做相应的修改,参考上级的Mak_

efile和Kconfig的内容

至此,修改完毕,此时你可以使用make menuconfig,发现已有 xxx support ---> 选项,进入后选择M,再make modules,到char目录下,已生成xxx.ko。

最后,再进入make menuconfig ,选择 xxx support,编译进内核.

二. 驱动作成模块加载

一般在开发时采用这种方式比较方便,无需每次都要通过tftp下载内核,nfs去挂载根文件系统,我们只需要把驱动作为模块,在本地虚拟机中完成操作。

1>.模块制作
a.驱动编译
把 xxx.c文件放入drivers/char子目录下,修改drivers/char/Makefile
obj-m += xxx.o
然后,
make modules,生成模块都 drivers/char/xxx.ko
再把 xxx.ko 放到单板根文件系统的/lib/modules/2.6.14.1(自己的内核版本)/目录下
b.驱动测试
编写测试代码及Makefile文件,make后,把生成文件放在单板跟文件系统/usr/bin/目录下
2>.调试
在根文件系统中建立设备文件:
# mknod /dev/buttons c 232 0
加载模块
# insmod s3c24xx_buttons

卸载模块
# rmmod s3c24xx_buttons

了解更多开源相关,去LUPA社区看看吧。
参考技术B

    insmod

    modprobe

参考技术C insmod

以上是关于如何编译一个linux下的驱动模块的主要内容,如果未能解决你的问题,请参考以下文章

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

驱动编译进内核和编译模块的区别

linux 怎样插入一个驱动模块

Debian下编译安装驱动模块

linux设备驱动的模块编译

linux驱动开发-模块驱动