内核模块的编译
模块(module)是可以安装需求加载或卸载到系统内核中,扩展了内核的功能而不需要重启或重新编译内核。没有模块,我就要建立一个完整内核,而且直接添加入内核镜像中,这样就会显的非常臃肿。你可以通过运行lsmod或打开/proc/modules来查看加载到内核中的模块。那么如何编译内核模块,模块如何加载到内核当中呢?下面我们就写一个简单的程序看看这个过程。
我们回忆一下那段经典的"hello world“程序,它所实现的模块在2.4内核下是有效的。
***********************************************
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
int init_module(void)
{
printk(KERN_INFO "Hello, world!\n");
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Goodbye cruel world!\n");
}
************************************************
人们意想之中可能不会认为如此一段简单的代码会用得着做什么改变。但是,即使在2.6内核下它已无法工作了。如何解决这个问题呢?请看下文。
首先,程序的第一行 #define MODULE 已经不再必要了,因为内核构建系统(Kernel Build System)会自动完成它的定义。
下面再来看这段代码最大的问题,你定义了init_module和cleanup_module,这本来是<linux/init.h>中宏module_init和module_exit完成的工作。当然,在2.4内核下这样是可行的。但现在你必须摒弃这种方法。虽然忽略编译器的警告,老方法还是“行得通”,但是产生的模块将是无用的。
更新之后的"hello world"代码如下 。
************************************************
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world!\n");
r eturn 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world!\n");
}
module_init(hello_init);
module_exit(hello_exit);
*************************************************
编写Makefile,可以参考下面例子。
*************************************************
obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
rm –rf *.o *~core.depend .*.cmd *.ko *.mod.c.temp_versions
*************************************************
然后就是执行make命令生成hello.ko模块。
将模块加载到内核当中去利用insmod命令,可以利用lsmod命令查看是否加载进去。
最后执行dmesg命令在屏幕最下端显示结果。
其中dmesg命令详解如下:
功能说明:显示开机信息。
语 法:dmesg [-cn][-s <缓冲区大小>]
补充说明:kernel会将开机信息存储在ring buffer中。您若是开机时来不及查看信息,可利用dmesg来查看。开机信息亦保存在/var/log目录中,名称为dmesg的文件里。
参 数:
-c 显示信息後,清除ring buffer中的内容。
-s<缓冲区大小> 预设置为8196,刚好等于ring buffer的大小。
-n 设置记录信息的层级。
现在它可以正常工作了,在系统消息中亦可看到"Hello,world"。但是你发现,还会有一些不愉快地消息也伴随着出现其中 “hello: module license 'unspecified' taints kernel.“
内核污染(kernel tainting)经常指私有模块被加入在内核当中。但眼下这种情况却不同。什么问题呢?因为代码中缺少了许可证申明。
MODULE_LICENSE("Dual BSD/GPL");
MODULE_LICENSE并不是一个新的宏,他在2.4.10版本内核中已经被添加。但许多以前的代码都缺少许可证申明,那时是无所谓的。但现在如果你厌倦想看到"taints kernel"警告,加上它还是值得的。 许可证的申明对于你访问内核中那些基于GPL许可证的功能模块是必要的。以上我们代码中嵌入的是GPL许可证。
再次编译模块,结果就令人满意了。
应用程序编译
仅用来实时编译运行的应用程序:
编写Makefile文件
CC = /usr/local/arm/2.95.3/bin/arm-linux-gcc
LD = /usr/local/arm/2.95.3/bin/arm-linux-ld
EXEC = helloworld
OBJS = helloworld.o
CFLAGS +=
LDFLAGS +=
all: $(EXEC)
$(EXEC): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS$(LDLIBS_$@))
cp $(EXEC) /tftpboot/examples/
clean:
-rm -f $(EXEC) *.elf *.gdb *.o
./helloworld 来运行应用程序(运行前可能要改变权限)
将应用程序编译进内核,参考Uclinux 的设置:
编写自己的源程序代码:
uCLinux/Linux应用程序通常存放在OS_HOME/user目录下,我们在该目录下创建一个Hello目录,并创建Hello.c文件,输入以下代码,该程序即大家最常见的Hello world!。
Hello world应用程序
Hello.c
#include <stdio.h>
#include <stdlib.h>
void main()
{
printf(“Hello world!”);
}
--------------------------------------------------------------------------------------------
为Hello world编写Makefile文件。该文件存放在OS_HOME\user\hello\下
Makefile
--------------------------------------------------------------------------------------------
#########################################################
# uclinux project type.
#########################################################
EXEC = Hello
OBJS = Hello.o
CFLAGS += -I.
all: $(EXEC)
$(EXEC): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
romfs:
$(ROMFSINST) /bin/$(EXEC)
clean:
-rm -f $(EXEC) *.elf *.gdb *.o
-------------------------------------------------------------------------------------------
上面的代码我就不解释了,大家参考相关文件,注意Hello为可执行文件名,Hello.o为目标文件名,大家可以根据自己的工程来修改。
修改uCLinux/Linux的相关配置文件。
这里需要修改的文件有:OS_HOME/user/Makefile,OS_HOME/config/config.in两个文件,下面分别针对uCLinux和Linux讲解
1. uCLinux中修改OS_HOME/user/Makefile文件
打开OS_HOME/user/Makefile:
--------------------------------------------------------------------------------------------
#
# Makefile -- Build instructions for user level apps
#
.EXPORT_ALL_VARIABLES:
#
# Include architecture specific build rules.
#
ifndef ROOTDIR
ROOTDIR=..
endif
UCLINUX_BUILD_USER=1
include $(LINUX_CONFIG)
include $(CONFIG_CONFIG)
include $(ARCH_CONFIG)
-include $(MODULES_CONFIG)
VEND=$(ROOTDIR)/vendors
#
# must run the vendor build first
#
dir_y = $(VEND)/$(CONFIG_VENDOR)/$(CONFIG_PRODUCT)/.
dir_n =
dir_ =
dir_$(CONFIG_JFFS_FS) += mtd-utils
dir_$(CONFIG_JFFS2_FS) += mtd-utils
……(中间略去)
dir_$(CONFIG_USER_WLG3) += wlg3
dir_$(CONFIG_USER_TEST111) += test111
dir_$(CONFIG_USER_HELLO) += Hello
dir_y +=
all:
for i in $(dir_y) ; do make -C $$i || exit $? ; done
romfs:
for i in $(dir_y) ; do make -C $$i romfs || exit $? ; done
clean:
-for i in $(dir_y) $(dir_n) $(dir_) ; do \
[ ! -d $$i ] || make -C $$i clean; \
Done
添加代码,上面代码中灰底蓝字部分。该行代码中CONFIG_USER_HELLO定义一个符号,在其他文件中用到,+= Hello为应用程序的相对路径。
2. uCLinux中修改OS_HOME/config/config.in
打开OS_HOME/config/config.in文件
--------------------------------------------------------------------------------------------
##################################################################
#
# NOTE : it is fairly important that changes to this file consider their
# effect on other platforms and default setups. If you can, only
# add to the file, changing the name of a variable will break
# many configurations.
#
#################################################################
mainmenu_name "uClinux Application Configuration"
#################################################################
mainmenu_option next_comment
comment 'Core Applications'
bool 'init' CONFIG_USER_INIT_INIT
……(中间略去)
endmenu
#################################################################
mainmenu_option next_comment
comment 'Jian hua test project'
bool 'bulud ucLinux project' CONFIG_USER_UCLINUXPRO
endmenu
#################################################################
mainmenu_option next_comment
comment 'Build my project:Hello'
bool 'build project that named Hello' CONFIG_USER_HELLO
endmenu
#################################################################
--------------------------------------------------------------------------------------------
在该文件末尾添加代码如上中的灰底蓝字部分。comment 'Build my project:Hello' 用于添加一个名为“Build my project:Hello”的主菜单项,bool 'build project that named Hello' CONFIG_USER_HELLO用于在刚才建立的主菜单项下建立名为build project that named Hello的子菜单项,CONFIG_USER_HELLO必须和您添加在OS_HOME/user/Makefile文件中的符号相同。
这样所有的文件就修改好了
执行Make config 或make xconfig就可以重新配置uCLinux。