分类: 嵌入式
2009-07-13 09:12:05
1.将嵌入式开发包的内核的源代码安装在/usr/src下,文件名为:linux-
# tar jxvf linux-
解压后生成一个新的目录/usr/src/linux-
注:该目录会因内核版本的不同而不同,各位动手实践的朋友只需知道自己的源代码所在的具体位置即可。
2.配置及编译内核。
进入/usr/src/ linux-source-
一般情况下,需要先用命令诸如"make menuconfig", "make xconfig"或者"make oldcofig"对内核进行配置,这几个都是对内核进行配置的命令,只是它们运行的环境不一样,执行一下这几个命令中的任何一个即可对内核进行配置:
Loadable module support (可加载模块选项)
其选项如下:
[*] Enable loadable module support
这个选项可以让你的内核支持模块,模块是什么呢?模块是一小段代码,编译后可在系统内核运行时动态的加入内核,从而为内核增加一些特性或是对某种硬件进行支持。一般一些不常用到的驱动或特性可以编译为模块以减少内核的体积。在运行时可以使用modprobe命令来加载它到内核中去(在不需要时还可以移除它)。
一些特性是否编译为模块的原则是,不常使用的,特别是在系统启动时不需要的驱动可以将其编译为模块,如果是一些在系统启动时就要用到的驱动比如说文件系统,系统总线的支持就不要编为模块了,否在无法启动系统。(当然还有一些变通的方法,我以后会提到)
[ *] Module unloading
这个选项可以让你卸载不再使用的模块,如果不选的话你将不能卸载任何模块(有些模块一旦加载就不能卸载,不管是否选择了这个选项)。不选择这个选项会让你的内核体积减小一点
[ ] Module versioning support (EXPERIMENTAL)
这个选项将让你可以使用其它版本内核中编译的模块,不过并不可靠,所以一般我们不选择它
[*] Automatic kernel module loading
一般情况下,如果我们的内核在某些任务中要使用一些被编译为模块的驱动或特性时,我们要先使用modprobe命令来加载它,内核才能使用。不过,如果你选择了这个选项,在内核需要一些模块时它可以自动调用modprobe命令来加载需要的模块,这是个很棒的特性,当然要选Y喽:)
Kernel Feautre -> Preemptible Kernel
[*]
注:该项为内核抢占式调度设置, 必须保证在构建运行的内核与编译环境的内核时都选上,一般PC机运行的内核已经选上了,否则在insmod时将出现如下错误:
# insmod st7565p_driver26.ko
Using st7565p_driver26.ko
st7565p_driver26: version magic '
gcc-3.4'
insmod: cannot insert `st7565p_driver26.ko': Invalid module format (-1): Exec format error
在内核源码的目录下执行:
# make
# make bzImage
其中,第一个make也可以不执行,直接make bzImage。这个过程可能要持续一个小时左右,因此是对整个内核重新编译了。执行结束后,可以看到在当前目录下生成了一个新的文件: vmlinux, 其属性为-rwxr-xr-x。
然后执行:
# touch * // 时间或时区设置,源代码的时间戳比本机的时间更新,否则产生:make[2]: 警告:检测到时钟错误。您的创建可能是不完整的。
# make modules 对内核的所有模块进行编译
# make modules_install 对内核的所有模块进行和安装
执行结束之后,会在/lib/modules下生成新的目录/lib/modules/
目录“/usr/src/
可以重启一下系统。
3.编写模块文件及Makefile
也以LDD3上的hello.c为例:
#include
#include
#include
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk("hello,world.\n");
return 0;
}
static void hello_exit(void)
{
printk("Good bye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
Makefile文件的内容为:
obj-m := hello.o
#KERNELDIR:=/lib/modules/$(shell uname -r)/build
KERNELDIR:=/lib/modules/
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
# make -C $(KERNELDIR) M=$(PWD) clean
上面的Makefile是这样确定内核源码树所在的目录的: 我们先到/lib/modules目录, 会看到一些以内核版本为名的目录, 目录中有一个build文件, 它是一个符号连接, 指向内核源码树.
上面的例子中只讨论了所有的代码在一个文件中的情况. 若代码分布在多个源文件中, 比如file1.c, file2.c, 生成hello.ko. 应该这样写Makefile:
obj-m := hello.o
hello-objs := file1.o file2.o
注意, 虽然我们的目的是生成.ko文件, 但在Makefile中写为.o!
hello.c和Makefile文件应该位于同一个目录下,可以放在/home下。
4.:编译和装载模块
在文件所处的目录下,执行:
# make
然后查看该目录下有哪些文件生成:可见,已经生成模块文件hello.ko.
然后,下载该文件到开发板,就可以加载该模块:
运行命令:
# insmod hello.ko
应该可以看到返回的信息:Hello, world
然后再运行命令:
# rmmod hello
应该可以看到返回的信息:Goodbye!
以上讲的是在在源码树之外编译模块,那么在源码树中呢?
在源码树中编译模块
官方内核模块的源代码都是按模块(驱动)类型组织的, 我们到内核源码树的drivers目录可以看到char, usb, block之类的子目录. 那么我们在内核源码树中添加文件时, 最好也遵循这些分类. 分类的规则自己灵活把握.
下面以前面的"hello, world"这个简单的模块为例, 来看看如何在内核源码树中编译模块.
1, 不新建子目录
(1) 先在内核源码树中的drivers目录编辑一个c源程序, 名为hello.c.
(2) 修改drivers目录的Makefile文件, 添加: obj-m += hello.o
(3) 重新编译内核(回到源码树根目录, 运行 $ make).
这样, 在drivers目录多出了这样几个文件: hello.mod.c, hello.mod.o, hello.o, hello.ko. hello.ko就是编译出来的模块了.
2, 新建子目录
如果源文件比较多, 可以在drivers目录中新建子目录. 还是以hello, world为例:
(1) 在内核源码树的drivers目录中新建一个hello子目录, 并将hello.c放在hello目录中.
(2) 修改drivers目录的Makefile文件, 添加: obj-m += hello/
(3) 在hello目录中新建一个Makefile文件, 内容为: obj-m += hello.o
(4) 重新编译内核(回到源码树根目录, 运行 $ make).
这样, 新生成的模块文件就位于hello目录中.
---------------------------
你 好,我最近刚接触linux驱动移植,试了好长时间才把“hello world”移成功,但一直有个疑问:在make时到底是调用的哪个编译器?gcc还是arm-linux-gcc?我尝试通过在Makefile中加 “CC = arm-linux-gcc”,但在最后查看内核信息时还是显示所用编译器为gcc!
---------------------------