工作问题解决
1.init 测试设计 选项 -e
telinit -e
init 测试选项 man 手册 正式。
2.
8.2 Linux源代码的目录结构
8.2.1 Linux目录结构
Linux的源代码全部在一个目录下,这里有很多文件夹,包含不同功能的源代码:
├—init 内核初始化代码
├—kernel 内核核心部分:进程,定时,程序执行,信号,模块…
├—mm 内存处理
├—arch 平台相关代码
├—i386 IBM的PC体系结构
├—kernel 内核核心部分
├—mm 内存管理
├—math-emu 浮点单元软件仿真
├—lib 硬件相关工具函数
├—boot 引导程序
├—compressed 压缩内核处理
├—tools 生成压缩内核映像的程序
├—alpha 康柏的Alpha体系结构
├—s390 IBM的System/390体系结构
├—sparc Sun的SPARC体系结构
├—sparc64 Sun的Ultra-SPARC体系结构
├—mips SGI的MIPS体系结构
├— ppc Motorola-IBM的基于PowerPC的体系结构
├—m68k Motorola的基于MC680x0的体系结构
├—arm 基于ARM处理器的体系结构
├—fs 文件系统
├—proc /proc虚拟文件系统
├—devpts /dev/pts虚拟文件系统
├—ext2 Linux本地的Ext2文件系统
├—isofs ISO9660文件系统(CD-ROM)
├—nfs 网络文件系统(NFS)
├—nfsd 集成的网络文件系统服务器
├—fat 基于FAT的文件系统的通用代码
├—msdos 微软的MS-DOS文件系统
├—vfat 微软的Windows文件系统(VFAT)
├—nls 本地语言支持
├—ntfs 微软的Windows NT文件系统
├—smbfs 微软的Windows服务器消息块(SMB)文件系统
├—umsdos UMSDOS文件系统
├—minix MINIX文件系统
├—hpfs IBM的OS/2文件系统
├—sysv SystemV,SCO,Xenix,Coherent和Version7文件系统
├—ncpfs Novell的Netware核心协议(NCP0
├—ufs UnixBSD,SunOs,FreeBSD,NetBSD,OpenBSD和NeXTStep文件系统
├—affs Amiga的快速文件系统(FFS)
├—coda Coda网络文件系统
├—hfs 苹果的Macintosh文件系统
├—adfs Acorn磁盘填充文件系统
├—efs SGI IRIX的EFS文件系统
├—qnx4 QNX4 OS使用不的文件系统
├—romfs 只读小文件系统
├—autofs 目录自动装载程序的支持
├—lockd 远程文件锁定的支持
├—Net 网络代码
├—Ipc System V的进程间通信
├— Drivers 设备驱动程序
├—block 块设备驱动程序
├—paride 从并口访问IDE设备的支持
├—scsi SCSI设备驱动程序
├—char 字符设备驱动程序
├—joystick 游戏杆
├—ftape 磁带流设备
├—hfmodem 无线电设备
├—ip2 IntelliPort的多端口串行控制器
├—net 网卡设备
├—sound 音频卡设备
├—video 视频卡设备
├—cdrom 专用CD-ROM设备(除ATAPI和SCSI之外)
├—isd0n ISDN设备
├—apl000 富士的AP1000设备
├—macintosh 苹果的Macintosh设备
├—sgi SGI的设备
├—fc4 光纤设备
├—acorn Acorn的设备
├—misc 杂项设备
├—pnp 即插即用的支持
├—usb 通用串行总线(USB)的支持
├—pci PCI总线的支持
├—sbus Sun的SPARC SBus的支持
├—nubus 苹果的Macintosh Nubus的支持
├—zorro Amiga的Zorro总线的支持
├—dio 惠普的HP300 DIO总线的支持
├—tc Sun的TurboChannel支持(尚未完成)
├— Lib 通用内核函数
├— Include 头文件(.h)
├—linux 内核核心部分
├— lockd 远程文件加锁
├—nfsd 集成的网络文件服务器
├— sunrpc Sun的远程过程调用
├— byteorder 字节交换函数
├— modules 模块支持
├—asm-generic 平台无关低级头文件
├—asm-i386 IBM的PC体系结构
├—asm-alpha 康柏的Alpha体系结构
├—asm-mips SGI的MIPS体系结构
├—asm-m68k Motorola-IBM的基于PowerPC的体系结构
├—asm-ppc Motorola-IBM的PowerPC体系结构
├—asm-s390 IBM的System/390体系结构
├—asm-sparc Sun的SPARC体系结构
├—asm-sparc64 Sun的Ultra-SPARC体系结构
├—asm-arm 基于ARM处理器的体系结构
├—net 网络
├—scsi SCSI支持
├—video 视频卡支持
├—config 定义内核配置的宏所在的头文件
├— scripts 生成内核映像的外部程序
├— Documentation有关内核各个部分的通用解释和注释的文本文件
8.2.2 内核配置系统——config.in和Makefile
1,配置系统的基本结构
Linux内核的配置系统由三个部分组成,分别是:
Makefile:分布在 Linux 内核源代码中的 Makefile,定义 Linux 内核的编译规则;
配置文件(config.in):给用户提供配置选择的功能;
配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供基于字符界面,基于 Ncurses 图形界面以及基于 Xwindows 图形界面的用户配置界面,各自对应于 Make config,Make menuconfig 和 make xconfig).
这些配置工具都是使用脚本语言,如 Tcl/TK,Perl 编写的(也包含一些用 C 编写的代码).本文并不是对配置系统本身进行分析,而是介绍如何使用配置系统.所以,除非是配置系统的维护者,一般的内核开发者无须了解它们的原理,只需要知道如何编写 Makefile 和配置文件就可以.所以,在本文中,我们只对 Makefile 和配置文件进行讨论.另外,凡是涉及到与具体 CPU 体系结构相关的内容,我们都以 ARM 为例,这样不仅可以将讨论的问题明确化,而且对内容本身不产生影响.
2,Makefile
(1)Makefile 概述
Makefile 的作用是根据配置的情况,构造出需要编译的源文件列表,然后分别编译,并把目标代码链接到一起,最终形成 Linux 内核二进制文件.
由于 Linux 内核源代码是按照树形结构组织的,所以 Makefile 也被分布在目录树中.Linux 内核中的 Makefile 以及与 Makefile 直接相关的文件有:
Makefile:顶层 Makefile,是整个内核配置,编译的总体控制文件.
.config:内核配置文件,包含由用户选择的配置选项,用来存放内核配置后的结果(如 make config).
arch/*/Makefile:位于各种 CPU 体系目录下的 Makefile,如 arch/arm/Makefile,是针对特定平台的 Makefile.
各个子目录下的 Makefile:比如 drivers/Makefile,负责所在子目录下源代码的管理.
Rules.make:规则文件,被所有的 Makefile 使用.
用户通过 make config 配置后,产生了 .config.顶层 Makefile 读入 .config 中的配置选择.顶层 Makefile 有两个主要的任务:产生 vmlinux 文件和内核模块(module).为了达到此目的,顶层 Makefile 递归的进入到内核的各个子目录中,分别调用位于这些子目录中的 Makefile.至于到底进入哪些子目录,取决于内核的配置.在顶层 Makefile 中,有一句:include arch/$(ARCH)/Makefile,包含了特定 CPU 体系结构下的 Makefile,这个 Makefile 中包含了平台相关的信息.
位于各个子目录下的 Makefile 同样也根据 .config 给出的配置信息,构造出当前配置下需要的源文件列表,并在文件的最后有 include $(TOPDIR)/Rules.make.
Rules.make 文件起着非常重要的作用,它定义了所有 Makefile 共用的编译规则.比如,如果需要将本目录下所有的 c 程序编译成汇编代码,需要在 Makefile 中有以下的编译规则:
%.s: %.c
$(CC) $(CFLAGS) -S $< -o $@
有很多子目录下都有同样的要求,就需要在各自的 Makefile 中包含此编译规则,这会比较麻烦.而 Linux 内核中则把此类的编译规则统一放置到 Rules.make 中,并在各自的 Makefile 中包含进了 Rules.make(include Rules.make),这样就避免了在多个 Makefile 中重复同样的规则.对于上面的例子,在 Rules.make 中对应的规则为:
%.s: %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(*F)) $(CFLAGS_$@) -S $< -o $@
(2) Makefile 中的变量
顶层 Makefile 定义并向环境中输出了许多变量,为各个子目录下的 Makefile 传递一些信息.有些变量,比如 SUBDIRS,不仅在顶层 Makefile 中定义并且赋初值,而且在 arch/*/Makefile 还作了扩充.
常用的变量有以下几类:
A,版本信息
版本信息有:VERSION,PATCHLEVEL, SUBLEVEL, EXTRAVERSION,KERNELRELEASE.版本信息定义了当前内核的版本,比如 VERSION=2,PATCHLEVEL=4,SUBLEVEL=18,EXATAVERSION=-rmk7,它们共同构成内核的发行版本KERNELRELEASE:2.4.18-rmk7
B,CPU 体系结构:ARCH
在顶层 Makefile 的开头,用 ARCH 定义目标 CPU 的体系结构,比如 ARCH:=arm 等.许多子目录的 Makefile 中,要根据 ARCH 的定义选择编译源文件的列表.
C,路径信息:TOPDIR, SUBDIRS
TOPDIR 定义了 Linux 内核源代码所在的根目录.例如,各个子目录下的 Makefile 通过 $(TOPDIR)/Rules.make 就可以找到 Rules.make 的位置.
SUBDIRS 定义了一个目录列表,在编译内核或模块时,顶层 Makefile 就是根据 SUBDIRS 来决定进入哪些子目录.SUBDIRS 的值取决于内核的配置,在顶层 Makefile 中 SUBDIRS 赋值为 kernel drivers mm fs net ipc lib;根据内核的配置情况,在 arch/*/Makefile 中扩充了 SUBDIRS 的值,参见4)中的例子.
D,内核组成信息:HEAD, CORE_FILES, NETWORKS, DRIVERS, LIBS
Linux 内核文件 vmlinux 是由以下规则产生的:
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
–start-group \
$(CORE_FILES) \
$(DRIVERS) \
$(NETWORKS) \
$(LIBS) \
–end-group \
-o vmlinux
可以看出,vmlinux 是由 HEAD,main.o,version.o,CORE_FILES,DRIVERS,NETWORKS 和 LIBS 组成的.这些变量(如 HEAD)都是用来定义连接生成 vmlinux 的目标文件和库文件列表.其中,HEAD在arch/*/Makefile 中定义,用来确定被最先链接进 vmlinux 的文件列表.比如,对于 ARM 系列的 CPU,HEAD 定义为:
HEAD := arch/arm/kernel/head-$(PROCESSOR).o \
arch/arm/kernel/init_task.o
表明 head-$(PROCESSOR).o 和 init_task.o 需要最先被链接到 vmlinux 中.PROCESSOR 为 armv 或 armo,取决于目标 CPU. CORE_FILES,NETWORK,DRIVERS 和 LIBS 在顶层 Makefile 中定义,并且由 arch/*/Makefile 根据需要进行扩充. CORE_FILES 对应着内核的核心文件,有 kernel/kernel.o,mm/mm.o,fs/fs.o,ipc/ipc.o,可以看出,这些是组成内核最为重要的文件.同时,arch/arm/Makefile 对 CORE_FILES 进行了扩充:
# arch/arm/Makefile
# If we have a machine-specific directory, then include it in the build.
MACHDIR := arch/arm/mach-$(MACHINE)
ifeq ($(MACHDIR),$(wildcard $(MACHDIR)))
SUBDIRS += $(MACHDIR)
CORE_FILES := $(MACHDIR)/$(MACHINE).o $(CORE_FILES)
endif
HEAD := arch/arm/kernel/head-$(PROCESSOR).o \
arch/arm/kernel/init_task.o
SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe
CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
LIBS := arch/arm/lib/lib.a $(LIBS)
E,编译信息:CPP, CC, AS, LD, AR,CFLAGS,LINKFLAGS
在 Rules.make 中定义的是编译的通用规则,具体到特定的场合,需要明确给出编译环境,编译环境就是在以上的变量中定义的.针对交叉编译的要求,定义了 CROSS_COMPILE.比如:
CROSS_COMPILE = arm-linux-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
……
CROSS_COMPILE 定义了交叉编译器前缀 arm-linux-,表明所有的交叉编译工具都是以 arm-linux- 开头的,所以在各个交叉编译器工具之前,都加入了 $(CROSS_COMPILE),以组成一个完整的交叉编译工具文件名,比如 arm-linux-gcc.
CFLAGS 定义了传递给 C 编译器的参数.
LINKFLAGS 是链接生成 vmlinux 时,由链接器使用的参数.LINKFLAGS 在 arm/*/Makefile 中定义,比如:
# arch/arm/Makefile
LINKFLAGS :=-p -X -T arch/arm/vmlinux.lds
F,配置变量CONFIG_*
.config 文件中有许多的配置变量等式,用来说明用户配置的结果.例如 CONFIG_MODULES=y 表明用户选择了 Linux 内核的模块功能.
.config 被顶层 Makefile 包含后,就形成许多的配置变量,每个配置变量具有确定的值:y 表示本编译选项对应的内核代码被静态编译进 Linux 内核;m 表示本编译选项对应的内核代码被编译成模块;n 表示不选择此编译选项;如果根本就没有选择,那么配置变量的值为空.
(3)Rules.make 变量
前面讲过,Rules.make 是编译规则文件,所有的 Makefile 中都会包括 Rules.make.Rules.make 文件定义了许多变量,最为重要是那些编译,链接列表变量.
O_OBJS,L_OBJS,OX_OBJS,LX_OBJS:本目录下需要编译进 Linux 内核 vmlinux 的目标文件列表,其中 OX_OBJS 和 LX_OBJS 中的 “X” 表明目标文件使用了 EXPORT_SYMBOL 输出符号.
M_OBJS,MX_OBJS:本目录下需要被编译成可装载模块的目标文件列表.同样,MX_OBJS 中的 “X” 表明目标文件使用了 EXPORT_SYMBOL 输出符号.
O_TARGET,L_TARGET:每个子目录下都有一个 O_TARGET 或 L_TARGET,Rules.make 首先从源代码编译生成 O_OBJS 和 OX_OBJS 中所有的目标文件,然后使用 $(LD) -r 把它们链接成一个 O_TARGET 或 L_TARGET.O_TARGET 以 .o 结尾,而 L_TARGET 以 .a 结尾.
(4)子目录 Makefile
子目录 Makefile 用来控制本级目录以下源代码的编译规则.我们通过一个例子来讲解子目录 Makefile 的组成:
#
# Makefile for the linux kernel.
#
# All of the (potential) objects that export symbols.
# This list comes from \’grep -l EXPORT_SYMBOL *.[hc]\’.
export-objs := tc.o
# Object file lists.
obj-y :=
obj-m :=
obj-n :=
obj- :=
obj-$(CONFIG_TC) += tc.o
obj-$(CONFIG_ZS) += zs.o
obj-$(CONFIG_VT) += lk201.o lk201-map.o lk201-remap.o
# Files that are both resident and modular: remove from modular.
obj-m := $(filter-out $(obj-y), $(obj-m))
# Translate to Rules.make lists.
L_TARGET := tc.a
L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y)))
LX_OBJS := $(sort $(filter $(export-objs), $(obj-y)))
M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
include $(TOPDIR)/Rules.make
A,注释
对 Makefile 的说明和解释,由#开始.
B,编译目标定义
类似于 obj-$(CONFIG_TC) += tc.o 的语句是用来定义编译的目标,是子目录 Makefile 中最重要的部分.编译目标定义那些在本子目录下,需要编译到 Linux 内核中的目标文件列表.为了只在用户选择了此功能后才编译,所有的目标定义都融合了对配置变量的判断.
前面说过,每个配置变量取值范围是:y,n,m 和空,obj-$(CONFIG_TC) 分别对应着 obj-y,obj-n,obj-m,obj-.如果 CONFIG_TC 配置为 y,那么 tc.o 就进入了 obj-y 列表.obj-y 为包含到 Linux 内核 vmlinux 中的目标文件列表;obj-m 为编译成模块的目标文件列表;obj-n 和 obj- 中的文件列表被忽略.配置系统就根据这些列表的属性进行编译和链接.
export-objs 中的目标文件都使用了 EXPORT_SYMBOL() 定义了公共的符号,以便可装载模块使用.在 tc.c 文件的最后部分,有 “EXPORT_SYMBOL(search_tc_card);”,表明 tc.o 有符号输出.
这里需要指出的是,对于编译目标的定义,存在着两种格式,分别是老式定义和新式定义.老式定义就是前面 Rules.make 使用的那些变量,新式定义就是 obj-y,obj-m,obj-n 和 obj-.Linux 内核推荐使用新式定义,不过由于 Rules.make 不理解新式定义,需要在 Makefile 中的适配段将其转换成老式定义.
C,适配段
适配段的作用是将新式定义转换成老式定义.在上面的例子中,适配段就是将 obj-y 和 obj-m 转换成 Rules.make 能够理解的 L_TARGET,L_OBJS,LX_OBJS,M_OBJS,MX_OBJS.
L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) 定义了 L_OBJS 的生成方式:在 obj-y 的列表中过滤掉 export-objs(tc.o),然后排序并去除重复的文件名.这里使用到了 GNU Make 的一些特殊功能,具体的含义可参考 Make 的文档(info make).
D,include $(TOPDIR)/Rules.make
3,配置文件
(1)配置功能概述
除了 Makefile 的编写,另外一个重要的工作就是把新功能加入到 Linux 的配置选项中,提供此项功能的说明,让用户有机会选择此项功能.所有的这些都需要在 config.in 文件中用配置语言来编写配置脚本,
在 Linux 内核中,配置命令有多种方式:
配置命令 解释脚本
Make config, make oldconfig scripts/Configure
Make menuconfig scripts/Menuconfig
Make xconfig scripts/tkparse
以字符界面配置(make config)为例,顶层 Makefile 调用 scripts/Configure, 按照 arch/arm/config.in 来进行配置.命令执行完后产生文件 .config,其中保存着配置信息.下一次再做 make config 将产生新的 .config 文件,原 .config 被改名为 .config.old
(2)配置语言
A,顶层菜单
mainmenu_name /prompt/ /prompt/ 是用\’或”包围的字符串,\’与”的区别是\’…\’中可使用$引用变量的值.mainmenu_name 设置最高层菜单的名字,它只在 make xconfig 时才会显示.
B,询问语句
bool /prompt/ /symbol/
hex /prompt/ /symbol/ /word/
int /prompt/ /symbol/ /word/
string /prompt/ /symbol/ /word/
tristate /prompt/ /symbol/
询问语句首先显示一串提示符 /prompt/,等待用户输入,并把输入的结果赋给 /symbol/ 所代表的配置变量.不同的询问语句的区别在于它们接受的输入数据类型不同,比如 bool 接受布尔类型( y 或 n ),hex 接受 16 进制数据.有些询问语句还有第三个参数 /word/,用来给出缺省值.
C,定义语句
define_bool /symbol/ /word/
define_hex /symbol/ /word/
define_int /symbol/ /word/
define_string /symbol/ /word/
define_tristate /symbol/ /word/
不同于询问语句等待用户输入,定义语句显式的给配置变量 /symbol/ 赋值 /word/.
D,依赖语句
dep_bool /prompt/ /symbol/ /dep/ …
dep_mbool /prompt/ /symbol/ /dep/ …
dep_hex /prompt/ /symbol/ /word/ /dep/ …
dep_int /prompt/ /symbol/ /word/ /dep/ …
dep_string /prompt/ /symbol/ /word/ /dep/ …
dep_tristate /prompt/ /symbol/ /dep/ …
与询问语句类似,依赖语句也是定义新的配置变量.不同的是,配置变量/symbol/的取值范围将依赖于配置变量列表/dep/ ….这就意味着:被定义的配置变量所对应功能的取舍取决于依赖列表所对应功能的选择.以dep_bool为例,如果/dep/ …列表的所有配置变量都取值y,则显示/prompt/,用户可输入任意的值给配置变量/symbol/,但是只要有一个配置变量的取值为n,则/symbol/被强制成n.
不同依赖语句的区别在于它们由依赖条件所产生的取值范围不同.
E,选择语句
choice /prompt/ /word/ /word/
choice 语句首先给出一串选择列表,供用户选择其中一种.比如 Linux for ARM 支持多种基于 ARM core 的 CPU,Linux 使用 choice 语句提供一个 CPU 列表,供用户选择:
choice \’ARM system type\’ \
“Anakin CONFIG_ARCH_ANAKIN \
Archimedes/A5000 CONFIG_ARCH_ARCA5K \
Cirrus-CL-PS7500FE CONFIG_ARCH_CLPS7500 \
……
SA1100-based CONFIG_ARCH_SA1100 \
Shark CONFIG_ARCH_SHARK” RiscPC
Choice 首先显示 /prompt/,然后将 /word/ 分解成前后两个部分,前部分为对应选择的提示符,后部分是对应选择的配置变量.用户选择的配置变量为 y,其余的都为 n.
F,if语句
if [ /expr/ ] ; then
/statement/
…
fi
if [ /expr/ ] ; then
/statement/
…
else
/statement/
…
fi
if 语句对配置变量(或配置变量的组合)进行判断,并作出不同的处理.判断条件 /expr/ 可以是单个配置变量或字符串,也可以是带操作符的表达式.操作符有:=,!=,-o,-a 等.
G,菜单块(menu block)语句
mainmenu_option next_comment
comment \’…..\’
…
endmenu
引入新的菜单.在向内核增加新的功能后,需要相应的增加新的菜单,并在新菜单下给出此项功能的配置选项.Comment 后带的注释就是新菜单的名称.所有归属于此菜单的配置选项语句都写在 comment 和 endmenu 之间.
H,Source 语句
source /word/
/word/ 是文件名,source 的作用是调入新的文件.
(3)缺省配置
Linux 内核支持非常多的硬件平台,对于具体的硬件平台而言,有些配置就是必需的,有些配置就不是必需的.另外,新增加功能的正常运行往往也需要一定的先决条件,针对新功能,必须作相应的配置.因此,特定硬件平台能够正常运行对应着一个最小的基本配置,这就是缺省配置.
Linux 内核中针对每个 ARCH 都会有一个缺省配置.在向内核代码增加了新的功能后,如果新功能对于这个 ARCH 是必需的,就要修改此 ARCH 的缺省配置.修改方法如下(在 Linux 内核根目录下):
备份 .config 文件
cp arch/arm/deconfig .config
修改 .config
cp .config arch/arm/deconfig
恢复 .config
如果新增的功能适用于许多的 ARCH,只要针对具体的 ARCH,重复上面的步骤就可以了.
4,help file
大家都有这样的经验,在配置 Linux 内核时,遇到不懂含义的配置选项,可以查看它的帮助,从中可得到选择的建议.下面我们就看看如何给给一个配置选项增加帮助信息.
所有配置选项的帮助信息都在 Documentation/Configure.help 中,它的格式为:
给出本配置选项的名称, 对应配置变量, 对应配置帮助信息.在帮助信息中,首先简单描述此功能,其次说明选择了此功能后会有什么效果,不选择又有什么效果,最后,不要忘了写上”如果不清楚,选择 N(或者)Y”,给不知所措的用户以提示.
4,实例
对于一个开发者来说,将自己开发的内核代码加入到 Linux 内核中,需要有三个步骤.首先确定把自己开发代码放入到内核的位置;其次,把自己开发的功能增加到 Linux 内核的配置选项中,使用户能够选择此功能;最后,构建子目录 Makefile,根据用户的选择,将相应的代码编译到最终生成的 Linux 内核中去.下面,我们就通过一个简单的例子–test driver,结合前面学到的知识,来说明如何向 Linux 内核中增加新的功能.
(1)目录结构
test driver 放置在 drivers/test/ 目录下:
$cd drivers/test
$tree
.
|– Config.in
|– Makefile
|– cpu
| |– Makefile
| `– cpu.c
|– test.c
|– test_client.c
|– test_ioctl.c
|– test_proc.c
|– test_queue.c
`– test
|– Makefile
`– test.c
(2)配置文件
A,drivers/test/Config.in
#
# TEST driver configuration
#
mainmenu_option next_comment
comment \’TEST Driver\’
bool \’TEST support\’ CONFIG_TEST
if [ “$CONFIG_TEST” = “y” ]; then
tristate \’TEST user-space interface\’ CONFIG_TEST_USER
bool \’TEST CPU \’ CONFIG_TEST_CPU
fi
endmenu
由于 test driver 对于内核来说是新的功能,所以首先创建一个菜单 TEST Driver.然后,显示 “TEST support”,等待用户选择;接下来判断用户是否选择了 TEST Driver,如果是(CONFIG_TEST=y),则进一步显示子功能:用户接口与 CPU 功能支持;由于用户接口功能可以被编译成内核模块,所以这里的询问语句使用了 tristate(因为 tristate 的取值范围包括 y,n 和 m,m 就是对应着模块).
B,arch/arm/config.in
在文件的最后加入:source drivers/test/Config.in,将 TEST Driver 子功能的配置纳入到 Linux 内核的配置中.
(3)Makefile
A,drivers/test/Makefile
# drivers/test/Makefile
#
# Makefile for the TEST.
#
SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
ALL_SUB_DIRS := $(SUB_DIRS) cpu
L_TARGET := test.a
export-objs := test.o test_client.o
obj-$(CONFIG_TEST) += test.o test_queue.o test_client.o
obj-$(CONFIG_TEST_USER) += test_ioctl.o
obj-$(CONFIG_PROC_FS) += test_proc.o
subdir-$(CONFIG_TEST_CPU) += cpu
include $(TOPDIR)/Rules.make
clean:
for dir in $(ALL_SUB_DIRS); do make -C $$dir clean; done
rm -f *.[oa] .*.flags
drivers/test 目录下最终生成的目标文件是 test.a.在 test.c 和 test-client.c 中使用了 EXPORT_SYMBOL 输出符号,所以 test.o 和 test-client.o 位于 export-objs 列表中.然后,根据用户的选择(具体来说,就是配置变量的取值),构建各自对应的 obj-* 列表.由于 TEST Driver 中包一个子目录 cpu,当 CONFIG_TEST_CPU=y(即用户选择了此功能)时,需要将 cpu 目录加入到 subdir-y 列表中.
B,drivers/test/cpu/Makefile
# drivers/test/test/Makefile
#
# Makefile for the TEST CPU
#
SUB_DIRS :=
MOD_SUB_DIRS := $(SUB_DIRS)
ALL_SUB_DIRS := $(SUB_DIRS)
L_TARGET := test_cpu.a
obj-$(CONFIG_test_CPU) += cpu.o
include $(TOPDIR)/Rules.make
clean:
rm -f *.[oa] .*.flags
C,drivers/Makefile
……
subdir-$(CONFIG_TEST) += test
……
include $(TOPDIR)/Rules.make
在 drivers/Makefile 中加入 subdir-$(CONFIG_TEST)+= test,使得在用户选择 TEST Driver 功能后,内核编译时能够进入 test 目录.
D,Makefile
……
DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o
DRIVERS-$(CONFIG_TEST) += drivers/test/test.a
DRIVERS-$(CONFIG_TEST_CPU) += drivers/test/cpu/test_cpu.a
DRIVERS := $(DRIVERS-y)
……
在顶层 Makefile 中加入 DRIVERS-$(CONFIG_TEST) += drivers/test/test.a 和 DRIVERS-$(CONFIG_TEST_CPU) += drivers/test/cpu/test_cpu.a.如何用户选择了 TEST Driver,那么 CONFIG_TEST 和 CONFIG_TEST_CPU 都是 y,test.a 和 test_cpu.a 就都位于 DRIVERS-y 列表中,然后又被放置在 DRIVERS 列表中.在前面曾经提到过,Linux 内核文件 vmlinux 的组成中包括 DRIVERS,所以 test.a 和 test_cpu.a 最终可被链接到 vmlinux 中.
8.3 内核配置与生成
Linux是一种内核功能可裁减的操作系统,可通过专门的配置方法,选取合适的功能与设备,生成所需的可在嵌入式系统上运行的内核.
8.3.1 make menuconfig
在终端下进入嵌入式Linux源代码的目录,运行命令make menuconfig,会马上进入内核配置菜单,菜单里有如下一些选项,供配置时挑选:
1. Code maturity level options
代码成熟等级.此处只有一项:prompt for development and/or incomplete code/drivers,如果你要试验现在仍处于实验阶段的功能,比如khttpd,IPv6等,就必须把该项选择为Y了;否则可以把它选择为N.
2. Loadable module support
对模块的支持.这里面有三项:
Enable loadable module support:除非你准备把所有需要的内容都编译到内核里面,否则该项应该是必选的.
Set version information on all module symbols:可以不选它.
Kernel module loader:让内核在启动时有自己装入必需模块的能力,建议选上.
3. Processor type and features
CPU类型:
Processor family:根据开发板的情况选择CPU类型.
High Memory Support:大容量内存的支持.可以支持到4G,64G,一般可以不选.
Math emulation:协处理器仿真.协处理器是在386时代的硬件,现在早已不用了.
MTTR support:MTTR支持.可不选.
Symmetric multi-processing support:对称多处理支持.除非你富到有多个CPU,否则就不用选了.
4. General setup
这里是对最普通的一些属性进行设置.这部分内容非常多,下面介绍一下经常使用的一些选项:
Networking support:网络支持.必须,没有网卡也建议选上.
PCI support:PCI支持.如果使用了PCI的卡,当然必选.
PCI access mode:PCI存取模式.可供选择的有BIOS,Direct和Any.
Support for hot-pluggabel devices:热插拔设备支持.
PCMCIA/CardBus support:PCMCIA/CardBus支持.有PCMCIA就必选了.
System V IPC
BSD Process Accounting
Sysctl support:以上三项是有关进程处理/IPC调用的,主要就是System V和BSD两种风格.如果你不是使用BSD,就按照缺省吧.
Power Management support:电源管理支持.
Advanced Power Management BIOS support:高级电源管理BIOD支持.
5. Memory Technology Device(MTD)
MTD设备支持.可不选.
6. Parallel port support
并口支持.
7. Plug and Play configuration
即插即用支持.虽然Linux对即插即用目前支持的不如Windows好,但是还是选上吧,这样你可以拔下鼠标之类的体验一下Linux下即插即用的感觉.
8. Block devices
块设备支持.这个就得针对自己的情况来选了,简单说明一下吧:
Normal PC floppy disk support:普通PC软盘支持.这个应该必选.
XT hard disk support:
Compaq SMART2 support:
Mulex DAC960/DAC1100 PCI RAID Controller support:RAID镜像用的.
Loopback device support:
Network block device support:网络块设备支持.如果想访问网上邻居的东西,就选上.
Logical volume manager(LVM)support:逻辑卷管理支持.
Multiple devices driver support:多设备驱动支持.
RAM disk support:RAM盘支持.
9. Networking options
网络选项.这里配置的是网络协议.内容太多了,不一一介绍了,自己看吧,如果你对网络协议有所了解的话,应该可以看懂的.如果懒得看,使用缺省选项(肯定要选中TCP/IP networking哦)就可以了.让我们看看,TCP/IP,ATM,IPX,DECnet,Appletalk……支持的协议好多哦,IPv6也支持了,Qos and/or fair queueing(服务质量公平调度)也支持了,还有kHTTPd,不过这些都还在实验阶段.
10. Telephony Support
电话支持.这个是什么东东 让我查查帮助,原来是Linux下可以支持电话卡,这样你就可以在IP上使用普通的电话提供语音服务了.记住,电话卡可和modem没有任何关系.
11. ATA/IDE/MFM/RLL support
这个是有关各种接口的硬盘/光驱/磁带/软盘支持的,内容太多了,使用缺省的选项吧,如果你使用了比较特殊的设备,比如PCMCIA等,就到里面自己找相应的选项吧.
12. SCSI support
SCSI设备的支持.我没有SCSI的设备,所以根本就不用选,如果你用了SCSI的硬盘/光驱/磁带等设备,自己找好了.
13. IEEE 1394(FireWire)support
Fireware串行总线.
14. I2O device support
I2O接口适配器才能支持的,在智能Input/Output(I2O)体系接口中使用.
15. Network device support
网络设备支持.上面选好了协议,现在选设备.里面大概分了类,有ARCnet设备,Ethernet(10 or 100 Mbit),Ethernet(1000Mbit),Wireless LAN(non-hamradio),Token Ring device,Wan interfaces,PCMCIA network device support几大类..
16. Amateur Radio support
业余无线广播.
17. IrDA(infrared)support
红外支持.
18. ISDN subsystem
如果使用ISDN上网,这个就必不可少了.
19. Old CD-ROM drivers(not SCSI,not IDE)
非SCSI/IDE口的光驱.
20. Character devices
字符设备.内容很多,大类有:
I2C support:I2C是Philips极力推动的微控制应用中使用的低速串行总线协议.如果要选择下面的Video For Linux,该项必选.
Mice:鼠标.现在可以支持总线,串口,PS/2,C&T 82C710 mouse port,PC110 digitizer pad,自己根据需要选择.
Joysticks:手柄.
Watchdog Cards:虽然称为Cards,这个可以用纯软件来实现,当然也有硬件的.如果你把这个选中,那么就会在你的/dev下创建一个名为watchdog的文件,它可以记录你的系统的运行情况,一直到系统重新启动的1分钟左右.有了这个文件,你就可以恢复系统到重启前的状态.
Video For Linux:支持有关的音频/视频卡.
Ftape, the floppy tape device driver:
PCMCIA character device support:
21. File systems
文件系统.介绍以下几项:
Quota support:Quota可以限制每个用户可以使用的硬盘空间的上限,在多用户共同使用一台主机的情况中十分有效.
DOS FAT fs support:DOS FAT文件格式的支持,可以支持FAT16,FAT32.
ISO 9660 CD-ROM file system support:光盘使用的就是ISO 9660的文件格式.
NTFS file system support:ntfs是NT使用的文件格式.
/proc file system support:/proc文件系统是Linux提供给用户和系统进行交互的通道,建议选上,否则有些功能没法正确执行.
还有另外三个大类都规到这儿了:Network File Systems(网络文件系统),Partition Types(分区类型),Native Language Support(本地语言支持).值得一提的是Network File Systems里面的两种:NFS和SMB分别是Linux和Windows相互以网络邻居的形式访问对方所使用的文件系统,根据需要加以选择.
22. Console drivers
控制台驱动.一般使用VGA text console就可以了,标准的80*25的文本控制台.
23. Sound
声卡驱动.
24. USB supprot
USB支持.很多USB设备,比如鼠标,调制解调器,打印机,扫描仪等,在Linux都可以得到支持,根据需要自行选择.
25. Kernel hacking
配置了这个,即使在系统崩溃时,也可以进行一定的工作.普通用户是用不着这个功能的.
配置好以后保存.
8.3.2 生成内核
make menuconfig命令执行完以后,顺序执行下列命令:
#make dep
#make clean
#make zImage
#make bzImage
#make modules
#make modules_install
#depmod –a
第一个命令make dep实际上读取配置过程生成的配置文件,来创建对应于配置的依赖关系树,从而决定哪些需要编译而那些不需要;第二命令make clean完成删除前面步骤留下的文件,以避免出现一些错误;第三个命令make zImage和第四个命令make bzImage实现完全编译内核,二者生成的内核都是使用gzip压缩的,只要使用一个就够了,它们的区别在于使用make bzImage可以生成大一点的内核.建议大家使用make bzImage命令.
后面三个命令只有在你进行配置的过程中,在回答Enable loadable module support (CONFIG_MODULES)时选了”Yes”才是必要的,make modules和make modules_install分别生成相应的模块和把模块拷贝到需要的目录中.
8.3.3 在嵌入式开发板上运行Linux
内核生成成功以后,要将其刷写到嵌入式系统的Flash存储器中,这样嵌入式系统就可脱离PC独立运行Linux了.
嵌入式系统要脱离PC独立运行Linux往往还需要在开发板的Flash存储器中预先写入Bootloader.Bootloader是初始化硬件的一个程序,作用相当于PC的Bios,加电时它在嵌入式系统上首先运行,初始化硬件以后再将控制权交给Linux.制作bootloader的工具很多,如BLOB,Armboot等,制作出来的Bootloader写在嵌入式系统Flash存储器的起始位置,使嵌入式系统在加电时首先运行.除了初始化硬件之外,它通常建立一个串口控制台,使嵌入式系统能通过串口与PC通讯,可通过PC机上的Linux的minicom工具通过串口控制嵌入式系统.
Bootloader除了以上作用外,还需要有将Linux内核从Flash中copy到内存中并运行内核的功能,这样嵌入式系统才能不需要串口控制台输入命令来运行Linux.
8.4 内核源代码修改
嵌入式Linux在开发板上运行,需要针对硬件对源代码进行修改,对2.4版的Linux而言,需要修改的源程序集中在这么几个文件:
linux/arch/XXXXXXX/boot/compressed/head.S;
linux/arch/XXXXXXX/boot/Makefile;
linux/arch/XXXXXXX/config.in;
linux/arch/XXXXXXX/kernel/head-XXXX.S;
linux/arch/XXXXXXX/kernel/setup.c;
linux/arch/XXXXXXX/mach-XXXXXXX/arch.c;
linux/arch/XXXXXXX/mach-XXXXXXX/Makefile;
linux/drivers/,驱动程序;
linux/include/asm-XXXXXXX/,根据硬件情况,修改头文件;
以上这些文件根据不同硬件系统而不同,其中”XXXXXXX”表示某一种硬件,如”ARM”等.这些文件需要根据具体MPU和开发板进行修改.
以上这些文件中的.S文件含有汇编代码,这里的汇编语言根据MPU的种类不同而不同.config.in,Makefile文件用脚本语言修改,其他的.h,.c文件都属C语言程序..S文件都是Linux启动(boot)时所需要的,要参考硬件手册进行修改.
驱动程序的修改与添加是一件需要大量时间和精力的工作,这需要编写者,修改者熟悉Linux驱动程序.驱动程序的编写另有专家著作,这里不多说,有兴趣者参考其它专著.
对相应文件进行修改的过程无疑是Linux移植中最难的一步,这需要修改者对Linux非常熟悉,可以通过这一步来掌握Linux源代码的编写方法.
3.工作要大动干戈。
scsi。。。直接去掉 没用的。 不用”原来”
4.