编译是在本地运行、交叉编译是在嵌入式板卡上运行,
这个两个编译过程的区别仅仅是Makefile的差异。
本文是在Vmware虚拟环境下,Ubuntu系统里进行的测试。
一、创建C文件
首先创建一个空的文件夹,创建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);
二、编写本地运行的Makefile
在编写之前,先确认一下系统的内核源码。
$ 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 ./hello.ko
然后执行卸载命令
rmmod hello.ko
这里实验是用的虚拟机,在仿真器中运行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
三、编写嵌入式系统运行的Makefile
在进行交叉编译驱动模块编写之前,需要先确保嵌入式系统运行的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.ko
PASS !