linux内核包含了很多东西,她能支持很多的驱动,设备,所以linux内核会很大(通常一个压缩的linux内核包在70多M)。在嵌入式开发中,我们需要我们自己的内核小一点,以便能放到我们的设备上面跑,但又不失其内核的功能。所以,我们需要对linux内核进行裁剪,使其足够小。
[liuyong@centos6 kernel]$ ls
build.sh linux-3.0 linux-3.0.tar.bz2 patch
[liuyong@centos6 kernel]$ cd linux-3.0
[liuyong@centos6 linux-3.0]$ ls
arch CREDITS drivers include Kbuild lib mm README scripts tools
block crypto firmware init Kconfig MAINTAINERS modules.order REPORTING-BUGS security usr
COPYING Documentation fs ipc kernel Makefile net samples sound virt
[liuyong@centos6 linux-3.0]
使用如下的命令,可以配置实现内核的裁剪
[liuyong@centos6 linux-3.0]$
[liuyong@centos6 linux-3.0]$ export TERM=vt100
[liuyong@centos6 linux-3.0]$ make menuconfig
scripts/kconfig/mconf Kconfig
.config - Linux/x86_64 3.0.0 Kernel Configuration
----------------------------------------------------------------------------------------------------------------------------------
+------------------------------------------ Linux/x86_64 3.0.0 Kernel Configuration ------------------------------------------+
| Arrow keys navigate the menu. <Enter> selects submenus --->. Highlighted letters are hotkeys. Pressing <Y> includes, |
| <N> excludes, <M> modularizes features. Press <Esc><Esc> to exit, <?> for Help, </> for Search. Legend: [*] built-in |
| [ ] excluded <M> module < > module capable |
| |
| +-------------------------------------------------------------------------------------------------------------------------+ |
| | General setup ---> | |
| | [*] Enable loadable module support ---> | |
| | -*- Enable the block layer ---> | |
| | Processor type and features ---> | |
| | Power management and ACPI options ---> | |
| | Bus options (PCI etc.) ---> | |
| | Executable file formats / Emulations ---> | |
| | -*- Networking support ---> | |
| | Device Drivers ---> | |
| | Firmware Drivers ---> | |
| | File systems ---> | |
| | Kernel hacking ---> | |
| | Security options ---> | |
| | -*- Cryptographic API ---> | |
| | [*] Virtualization ---> | |
| | Library routines ---> | |
| | --- | |
| | Load an Alternate Configuration File | |
| | Save an Alternate Configuration File | |
| +---------------------------------------------------------------------------------------------------------------------+ |
|+------------------------------------------------------------------------------------------------------------- +|| + <Select> < Exit > < Help > +|
| +----------------------------------------------------------------------------------------------------------------------+|
linux内核是以模块化的形式存在的,或者说linux驱动是以模块化的形式存在的。在linux中,存在有两条链(做一个形象的比喻),一条是驱动链,一条是设备链。
当新的驱动作为一个模块添加进来之后,即进入驱动链,他需要告诉内核,我这个驱动,能够支持什么设备。如果有现存的设备在,就驱动这个设备。如果没有可以支持的设备,就等在那里。
当新的设备插入时候,即进入设备链。这个设备被系统检测到,他告诉内核,我需要什么样的驱动才能正常的工作,如果有现存的驱动可用,那么就驱动该设备,开始正常工作,如果没有这样的驱动,那就显示出错,罢工。
我们在用make menuconfig 配置内核的时候,实际上是选择将哪些东西编译进入内核(即我们的内核镜像文件如:Image.gz),哪些东西编译为模块。一般的:系统必须要使用的,固定的设备都要编译进内核中,而其他的,动态使用的设备都是当成模块编译的,方便我们在使用设备的时候,动态的加载和卸载。
拿我的第一个模块hurryliu_hello.ko来说
#include<linux/init.h>
#include<linux/module.h>
static int hello_init(void)
{
printk(KERN_INFO "my first module ,hello world \n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_INFO "hello world exit \n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("hurryliu<>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("My first module ");
MODULE_ALIAS("A sample module");
这个内核模块只包含内核加载函数,卸载函数,以及其他的一些声明等。我们编译的时候,会产生一个名为hurryliu_hello.ko的内核模块,使用insmod hurryliu_hello.ko 加载这个模块时,串口将打印“my first module ,hello world ”字样(注意:之所以从串口输出,是因为我们在配置内核的时候,指定默认的输出为串口,如果我们将默认输出配置为LCD显示屏,那么在LCD显示屏正常工作时,应该打印以上字样)使用rmmod hurryliu_hello卸载模块的时候,会打印“hello world exit ”字样。
也许有人会问,这里的输出函数为什么不是printf?
那是因为:内核模块中用于输出的函数式内核空间的printk,而非用户空间的printf。虽然功能相似,但是这两个函数有本质上的区别printk是直接操作硬件,是属于内核中的,而printf来自于系统库函数的调用。但是我们在将模块加载到内核或者从内核卸载的时候,是直接和内核打交道,所以不需要经过上层的系统调用。
这也就是为什么我们的linux内核会这么大?他需要自己解决函数调用等的问题,这些函数在内核调试的时候,是不能从用户态经系统调用的,所以,自己搞一些东西出来,自给自足啦。