Linux内核模块的编译和静态库的使用

1520阅读 0评论2015-07-05 renyuan000
分类:LINUX


1. linux2.6内核Makefile简单语法与应用

1.1 概述

2.6的Makefile的写法和应用相对于2.4有了一些变化,可能对于很多人来说,因为找不到相关的文档,都是模仿内核中已有的文件来写自己的Makefile。其实,在内核的Documentation/kbuild(这个目录下的文档值得深究,对了解内核编译有很大的用处)目录下面,有对内核Makefile语法的详细说明。在这里就2.6内核中Makefile最常见的简单应用情况做一个翻译和归纳介绍。


   2.6内核的Makefile分为5个组成部分:
   1. 最顶层的Makefile
   2. 内核的.config配置文件
   3. 在arch/$(ARCH) 目录下的体系结构相关的Makefile
   4. 在scripts/ 目录下的 Makefile.* 文件,是一些Makefile的通用规则
   5. 各级目录下的大概约500个kbuild Makefile文件


   顶层的Makefile文件读取.config文件的内容,并总体上负责build内核和模块。Arch Makefile则提供补充体系结构相关的信息。 Scripts目录下的Makefile文件包含了所有用来根据kbuild Makefile 构建内核所需的定义和规则。


1.2 Kbuild Makefile


 
对于Makefiles的不同组成部分,有一些不同的语法规则。针对的对象也不同,对于大部分内核模块或设备驱动的
开发者和使用者来说,最常接触到的就是各层目录下基于kbuild架构的kbuild Makefile文件。Kbuild Makefile的语法结构非常简单,核心内容主要包括:
 
1.2.1 目标定义
目标定义就是用来定义哪些内容要做为模块编译,哪些要编译链接进内核。
例如

 
  1. obj-y += foo.o

表示要由foo.c或者foo.s文件编译得到foo.o并链接进内核,而obj-m则表示该文件要作为模块编译。 除了y,m以
外的obj-x形式的目标都不会被编译。而更常见的做法是根据.config文件的CONFIG_ 变量来决定文件的编译方式,如:
 
  1. obj-$(CONFIG_ISDN) += isdn.o
  2. obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
除了obj-形式的目标以外,还有lib-y library库,hostprogs-y 主机程序等目标,但是基本都应用在特定的目录
和场合下。
 
1.2.2 多文件模块的定义
最简单的kbuild Makefile如上一节一句话的形式就够了,如果一个模块由多个源文件组成,那么稍微复杂一些,采用模块名加 -objs后缀或者 -y后缀的形式来定义模块的组成文件。如以下例子:
 
  1. obj-$(CONFIG_EXT2_FS) += ext2.o
  2. ext2-y := balloc.o bitmap.o
  3. ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
模块的名字为ext2,由balloc.o和bitmap.o两个目标文件最终链接生成ext2.o 直至ext2.ko文件,是否包括
xattr.o取决于内核配置文件的配置情况。如果CONFIG_EXT2_FS的值是y也没有关系,在此过程中生成的 ext2.o将
被链接进built-in.o最终链接进内核。这里需要注意的一点是,kbuild Makefile所在的目录中不应该再包含和模
块名相同的源文件如ext2.c/ext2.s。(这一点非常重要,也即含有module_init和module_exit的模块也必须在ext2-y中指定,而obj-$(CONFIG_EXT2_FS)中指定的只是一个最终生成的.ko的名字,并且去掉了.o后缀)或者写成如-objs的形式:

 
  1. obj-$(CONFIG_ISDN) += isdn.o
  2. isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
 
1.2.3 目录层次的迭代
如下例:
 
  1. obj-$(CONFIG_EXT2_FS) += ext2/
 
如果CONFIG_EXT2_FS 的值为y或m,kbuild将会将ext2目录列入向下迭代的目标中,但是其作用也仅限于此,具体
ext2目录下的文件是要作为模块编译还是链入内核,还是有ext2目录下的Makefile文件的内容来决定的。
 
1.2.4 模块的编译
编译模块的时候,你可以将模块放在代码树中,用Make modules的方式来编译你的模块,你也可以将模块相关文
件目录放在代码树以外的位置,用如下命令来编译模块:

 
  1. make -C path/to/kernel/src M=$PWD modules

-C指定代码树的位置,M=$PWD 或 M=`PWD` 告诉kbuild回到当前目录来执行build操作。对于交叉编译环境为:
 
  1. $(MAKE) ARCH=arm CROSS_COMPILE=arm-linux- -C $(KERNELDIR) M=$(PWD) modules
注意这里是以arm为例。

1.2.5 模块的安装
当你需要将模块安装到非默认位置的时候,你可以用INSTALL_MOD_PATH 指定一个前缀,如:

 
  1. make INSTALL_MOD_PATH=/foo modules_install

模块将被安装到 /foo/lib/modules目录下。
 

2. 内核静态库的使用
 

在驱动程序里面编译链接内核静态库需要从三个方面注意:
1. 首先保证你正确生成了内核静态库文件,而非应用程序库文件;
2. 其次是除去静态库之外的驱动文件的正确性,包括内核库函数的调用和头文件的申明;
3. 正确修改驱动的Makefile;
这几个方面都正确之后,就能正确地将内核库编译进指定的驱动中去。
 

2.1编译内核静态库文件

 
下面给出一个最简单的详细例子给大家参考,毕竟有代码参考才是大家快速编程的王道。
模块代码目录结构如下:
 

driver.c

 

Makefile

 

lib

myalib.c

myalib.c

Makefile

内核静态库包含一个myalib.c和myalib.h文件,代码如下:

  1. //myalib.c
  2. void test(void)
  3. {
  4.     printk("A lib test.\n");
  5. }

 

  1. //myalib.h
  2. void test(void);

 

  1. # This is the Makefile for parse.
  2. RM = rm -f

  3. CCFLAGS = -c
  4. ARFLAG = -rc

  5. CC = gcc
  6. AR = ar

  7. lib_OBJECTS = myalib.o
  8. lib_SOURCE = myalib.c

  9. LIB = libtest.a

  10. libtest.a:$(lib_OBJECTS)
  11.     $(AR) $(ARFLAG) -o $@ $^

  12. $(lib_OBJECTS):$(lib_SOURCE)
  13.     $(CC) $(CCFLAGS) -o $@ $^

  14. clean:
  15.     $(RM) $(lib_OBJECTS)
  16.     $(RM) $(LIB)

在输入终端下执行”make”命令即可生成正确的内核库libtest.a文件

 

2.2 链接内核静态库文件

 

驱动代码包括一个driver.c文件。

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include "./lib/myalib.h" //如果这里不加lib,那么需要在Makefile中添加EXTRA_CFLAGS += -I$(绝对路径)

  4. MODULE_LICENSE("Dual BSD/GPL");

  5. int init_module(void)
  6. {
  7.     printk("into init_module.\n");
  8.     test();
  9.     return 0;
  10. }

  11. void cleanup_module(void)
  12. {
  13.     printk("into cleanup_module.\n");
  14. test();
  15. }

 

可以看到,在驱动初始化中调用了库文件中的test()函数,并且包含其头文件。

 

 

2.3 Makefile的修改


修改驱动Makefile文件,加入内核库文件的编译链接。
这个地方的修改比较简单,只需内核库文件当做.o文件一样去进行编译链接即可。

 

  1. # Makefile
  2. KERNELRELEASE=
  3. ifneq ($(KERNELRELEASE),)
  4.     obj-m := drv.o
  5.     drv-objs := driver.o lib/libtest.a
  6. else
  7.     KDIR := /lib/modules/$(shell uname -r)/build
  8.     PWD := $(shell pwd)
  9. $(OBJDIR)/%.o: $(SRCDIR)/%.c
  10.     $(CC) -o $@ $(CFLAGS) -c $<
  11. default:
  12.     $(MAKE) -C $(KDIR) M=$(PWD) modules
  13. clean:
  14.     rm -f *.o *.ko *.o.cmd *.mod.c *.symvers
  15. endif

注意添加的lib/libtest.a


OK,最后终端下执行”make”命令即可成功地将内核库文件和目标驱动文件链接到一起。然后就可以装载驱动进行验证了。

最后需要说明一下,以上只是个最简单的演示版代码,在这个基础上,大家可以根据自己的需要丰富内核库和驱动的内容,从而达到真正的项目用途的目的。

上一篇:QUIC和TCP
下一篇:Linux内核探索之路——关于书