编译是在本地运行、交叉编译是在嵌入式板卡上运行, 这个两个编译过程的区别仅仅是Makefile的差异。
本文是在Vmware虚拟环境下,Ubuntu系统里进行的测试。
首先创建一个空的文件夹,创建hello.c文件,文件内容如下:
#include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { printk(KERN_ALERT "Hello, world\n"); return 0; } static void hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel world\n"); } module_init(hello_init); module_exit(hello_exit);在编写之前,先确认一下系统的内核源码。
$ uname -r 4.4.0-193-generic然后看一下系统内建的源码文件。
$ ls /usr/src/ linux-headers-4.4.0-186 linux-headers-4.4.0-190 linux-headers-4.4.0-186-generic linux-headers-4.4.0-190-generic linux-headers-4.4.0-189 linux-headers-4.4.0-193 linux-headers-4.4.0-189-generic linux-headers-4.4.0-193-generic注意编写Makefile时,采用统一的4.4.0-193-generic版本。 创建Makefile文件,跟hello.c在同一文件夹下,执行: $ vim Makefile
KERNELDIR=/lib/modules/4.4.0-193-generic/build PWD:=$(shell pwd) INSTALLDIR=/home/mgm/work/hello/install obj-m:= hello.o modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: cp hello.ko $(INSTALLDIR) clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions .PHONY: modules modules_install clean需要注意点:
文件名的第一个M必须大写。命令行都要以tab空格开头。KERNELDIR也可以用下面语句代替 KERNELDIR := /lib/modules/$(shell uname -r)/build 通过shell命令传递内核版本参数。PWD传递的是当前工作路径。-C ( K E R N E L D I R ) 指 明 跳 转 到 源 码 目 录 下 读 取 那 里 的 M a k e f i l e ; M = (KERNELDIR) 指明跳转到源码目录下读取那里的Makefile;M= (KERNELDIR)指明跳转到源码目录下读取那里的Makefile;M=(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。保存Makefile,退出后,执行make命令,没有问题的话会出现如下信息:
# make make -C /lib/modules/4.4.0-193-generic/build M=/home/work/hello modules make[1]: Entering directory '/usr/src/linux-headers-4.4.0-193-generic' Building modules, stage 2. MODPOST 1 modules make[1]: Leaving directory '/usr/src/linux-headers-4.4.0-193-generic'执行模块加载命令
然后执行卸载命令
这里实验是用的虚拟机,在仿真器中运行insmod和rmmod,是不会在屏幕上看到任何输出的。实际上它可能输出到系统日志文件中的,例如/var/log/message,打开message,定位到最后,就会看到输出的消息。
# cat /var/log/kern.log | tail -2 Oct 20 01:09:17 ubuntu kernel: [108198.793234] Hello world Oct 20 01:09:30 ubuntu kernel: [108212.185816] Goodbye, cruel world在进行交叉编译驱动模块编写之前,需要先确保嵌入式系统运行的linux源码在主机环境中是可以被正确交叉编译的。
在工作目录下创建一个空白文件夹,创建hello.c 源程序,可以复制上面例子中的代码。 然后在同目录下创建Makefile文件 执行 $vim Makefile 输入
ifneq ($(KERNELRELEASE),) obj-m:= hello.o else KDIR := /home/mgm/opt/freescale/imx6/linux-4.1.15 PWD := $(shell pwd) CROSS_COMPILE=arm-poky-linux-gnueabi- CC =$(CROSS_COMPILE)gcc all: make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules clean: rm *.o *.ko *.mod.c endif这一行KDIR := /home/mgm/opt/freescale/imx6/linux-4.1.15 需要换成自己的路径, 编译器CROSS_COMPILE=arm-poky-linux-gnueabi-也要根据自己的工具设置。
编译前可以测试一下编译器
$ arm-poky-linux-gnueabi-gcc -v正常的话会打印很多行编译器信息。
然后执行编译
$ make编译后的信息
$ make make -C /home/serana/imx6/linux-4.1.15 M=/home/serana/imx6/test ARCH=arm CROSS_COMPILE=arm-poky-linux-gnueabi- modules make[1]: Entering directory `/home/serana/imx6/linux-4.1.15' CC [M] /home/serana/imx6/test/hello.o Building modules, stage 2. MODPOST 1 modules CC /home/serana/imx6/test/hello.mod.o LD [M] /home/serana/imx6/test/hello.ko make[1]: Leaving directory `/home/serana/imx6/linux-4.1.15'把编译好的模块下载到目标板上后,执行
# insmod ./hello.koPASS !