常用书目下载地址:http://www.cnblogs.com/pengdonglin137/p/3688029.html
第二章
1、进程上下文和中断上下文(Page20)
当应用程序执行系统调用,造成上下文的切换而进入内核时,内核会代表这个进程执行内核代码。你会经常听到,这种情况称为内核运行于进程上下文中。相反,处理IDE驱动器的中断处理程序(ISR)也是内核代码,但运行时并不代表任何特定的进程。这种情况通常被称为内核运行于中断上下文中。
2、独立处理器、配套芯片组和集成处理器 (Page26)
独立处理器是指那些专注于指令处理功能的处理器。与集成处理器相比,独立处理器需要额外的支持电路完成其基本操作。在大多数情况下,这意味着处理器周围需要配备一个芯片组或者一个定制的逻辑芯片,已实现一些增强功能,包括DRAM控制器、系统总线寻址配置以及外围设备(比如键盘控制器和串行端口)。独立处理器一般会提供最强的整体CPU性能。
单独处理器都需要连接支撑逻辑芯片才能访问外设,这些外设包括系统主内存(DRAM)、ROM或者闪存、系统总线(比如PCI)或者其他外设,比如键盘控制器、串行端口和IDE接口,诸如此类。执行逻辑芯片的的功能一般由配套的芯片组来完成,而这个芯片组很可能是专门为某个系列的处理器设计的。
虽然独立处理器的应用很广泛,包括一些高负载处理引擎,但是大多数的小型嵌入式系统都采用某种集成处理器或者片上系统(System On Chip,SOC)。
参考阅读:
http://pan.baidu.com/s/1eQGbs5c
http://www.cnblogs.com/pengdonglin137/p/3690410.html
3、查询本机的内核的发布信息
cat /proc/version
4、顶层源码目录
在本书中,会常常提到顶层源码目录。这时,我们指的是内核源码树的最高一层目录。顶层源码目录包含以下列出的子目录:
arch/ block/ crypto/ Documentation/
drivers/ firmware/ fs/ include/
init/ ipc/ kernel/ lib/
mm/ net/ samples/ scripts/
security/ sound/ usr/ virt/
5、编译内核是显示详细编译过程
如: make ARCH=arm CROSS_COMPILE=arm-linux- V=1 uImage
1: make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build
2: make V=2 [targets] 2 => give reason for rebuild of target
3: make O=dir [targets] Locate all output files in "dir", including .config
4: make C=1 [targets] Check all c source with $CHECK (sparse by default)
5: make C=2 [targets] Force check of all c source with $CHECK
6、清理
有几个make命令会删除配置文件而不会给出任何警告。最常见的就是make distclean(此外还有make mrproper)。这个make目标的设计初衷是上内核代码树回到原始的、未配置的状态。这包括删除源码树中所有的配置数据,当然也会删除原先的.config文件。
1: Cleaning targets:
2: clean - Remove most generated files but keep the config and
3: enough build support to build external modules
4: mrproper - Remove all generated files + config + various backup files
5: distclean - mrproper + remove editor backup and patch files
7、bzImage和zImage (Page61)
很多架构和机器类型都需要一个二进制镜像目标,而这个目标与具体使用的架构和引导加载程序有关。比较常见的这类目标是zImage。对于很多架构来说,这就是默认的二进制镜像目标,可以被加载到目标嵌入式系统中并运行。新手常犯的一个错误就是将bzImage指定为make的目标。然而,bzImage目标是针对x86/PC架构的。有一个常见的错误观点,认为bzImage是指经过压缩工具bzip2压缩过的镜像,其实不然,bzImage是指大(big)的zImage。
8、make menuconfig
在顶层目录执行make menuconfig ,然后再顶层Makefile中的:
459 %config: scripts_basic outputmakefile FORCE
460 $(Q)mkdir -p include/linux include/config
461 $(Q)$(MAKE) $(build)=scripts/kconfig $@
%config可以匹配*config,然后进入script/kconfig中执行 make menuconfig,
7 ifdef KBUILD_KCONFIG
8 Kconfig := $(KBUILD_KCONFIG)
9 else
0 Kconfig := arch/$(SRCARCH)/Kconfig
1 endif
2
3 xconfig: $(obj)/qconf
4 $< $(Kconfig)
5
6 gconfig: $(obj)/gconf
7 $< $(Kconfig)
8
9 menuconfig: $(obj)/mconf
0 $< $(Kconfig)
1
2 config: $(obj)/conf
3 $< $(Kconfig)
4
5 oldconfig: $(obj)/conf
6 $< -o $(Kconfig)
其中Kconfig是arch/arm/Kconfig,对于menuconfig,配置工具是mconf,他可以解析Kconfig文件。
9、嵌入式Linux发行版所包含的的组件 (Page73)
Linux内核;
引导加载程序,你需要将它移植到特定的硬件平台上,并做相应的配置;
适合于你所选架构的交叉编译器和相关的工具链;
文件系统,其中包含很多软件包——主要是二进制可执行文件和程序库,而且他们是针对本地硬件架构和处理器而编译的;
设备驱动程序,内核通过它们访问硬件板卡上的定制硬件;
开发环境,包括主机上的工具和软件;
Linux内核源码树,并且适合于特定的处理器和硬件板卡;
10、单体内核
Linux采用单体(monolithic)内核结构。也就是说,这个内核是由代码编译并静态链接生成的,是一个单一的可执行文件。然而,也可以编译一组源码文件,并通过增量链接的方式生成一个对象模块,他可以动态加载到运行的内核中。
内核的构建过程,不管采用哪种架构,构建时都会生成一些通用文件,其中之一就是名为vmlinux的ELF二进制文件(处于顶层内核源码目录中)。这个二进制文件就是单体内核(monolithic kernel)本身,我们也称它为内核主体。
下面是tq2440在生成内核源码顶层目录下的vmlinux时的链接命令:
arm-linux-ld -EL -p --no-undefined -X --build-id -o vmlinux \
\
-T arch/arm/kernel/vmlinux.lds \
\
arch/arm/kernel/head.o \
arch/arm/kernel/init_task.o \
\
init/built-in.o \
--start-group \
usr/built-in.o \
\
arch/arm/kernel/built-in.o \
arch/arm/mm/built-in.o \
arch/arm/common/built-in.o \
arch/arm/mach-s3c2410/built-in.o \
arch/arm/mach-s3c2400/built-in.o \
arch/arm/mach-s3c2412/built-in.o \
arch/arm/mach-s3c2440/built-in.o \
arch/arm/mach-s3c2442/built-in.o \
arch/arm/mach-s3c2443/built-in.o \
arch/arm/plat-s3c24xx/built-in.o \
arch/arm/plat-s3c/built-in.o \
arch/arm/nwfpe/built-in.o \
\
kernel/built-in.o \
mm/built-in.o \
fs/built-in.o \
ipc/built-in.o \
security/built-in.o \
crypto/built-in.o \
block/built-in.o \
arch/arm/lib/lib.a \
lib/lib.a \
arch/arm/lib/built-in.o \
lib/built-in.o \
drivers/built-in.o \
sound/built-in.o \
firmware/built-in.o \
net/built-in.o \
\
--end-group .tmp_kallsyms2.o
11、内核是如何合成的? (具体需要参见第五章的第一节P75)
以arm为例,其中vmlinusx 在内核源码顶层目录,Image在arch/arm/boot/下,piggy.gz在arch/arm/boot/compressed/下,bootable kernel image在arch/arm/boot/下。
这里主要说明如下俩个概念的不同:启动加载程序(Bootstrap Loader)和引导加载程序(Bootloader)
引导加载程序简单理解为uboot或者Bootloader或者第1阶段的加载程序,可以将启动加载程序看做是第2阶段的加载程序(简单理解为:zImage头部的几个文件,负责初始化设置一些硬件以及解压内核镜像的部分)。
不要将启动加载程序和引导加载程序混淆,很多架构都使用启动加载程序(第2阶段加载程序)将Linux内核镜像加载到内存中。有些启动加载程序会对内核镜像进行校验和检查,而大多数启动加载程序会解压并重新部署内核镜像。引导加载程序和启动加载程序之间的区别也很简单:但硬件单板加电时,引导加载程序获得其控制权,根本不依赖内核。相反,启动加载程序的主要作用是作为裸机引导加载程序和Linux内核之间的粘合剂。启动加载程序负责提供合适的上下文让内核运行于其中,并且执行必要的步骤以解压和重新部署内核二进制镜像。这类似于PC架构中的主加载程序和次加载程序的概念。
启动加载程序和内核镜像拼接在一起,用于加载。
(具体参见:Page79)
12、查看内核初始化细节 (page96)
initcall_debug是一个很有趣的内核命令行参数,它允许你观察启动过程中的函数调用。只需在启动内核是设置一下initcall_debug,就可以看到系统输出相关的诊断信息:
下面initcall最后的return x after XX usecs 表示函数的返回值以及函数调用的持续时间。
calling spi_init+0x0/0x84 @ 1
initcall spi_init+0x0/0x84 returned 0 after 2936 usecs
calling i2c_init+0x0/0x60 @ 1
initcall i2c_init+0x0/0x60 returned 0 after 4214 usecs
calling customize_machine+0x0/0x24 @ 1
S3C Power Management, Copyright 2004 Simtec Electronics
initcall customize_machine+0x0/0x24 returned 0 after 22922 usecs
这是个查看内核初始化细节的好办法,特别是可以了解内核调用各个子系统和模块的顺序。更有趣的是函数调用的持续时间。如果你关心系统启动时间,通过这种方法可以确定启动时间是在哪些地方被消耗的。
13、根文件系统
根文件系统指的是挂在于文件系统层次结构根部的文件系统,简单表示为/。
简单来说,根文件系统是内核挂载的第一个文件系统,挂载位置是文件系统层次结构的顶端。
Linux系统对于根文件系统有一些特殊需求。Linux要求根文件系统中包含应用程序和工具软件,通过它们来引导系统、初始化系统服务(比如网络和系统控制台)、加载设备驱动程序和挂在额外的文件系统。
14、根文件系统中的glibc和Linux动态加载器
在嵌入式系统的根文件系统中一般都会有这两个动态库:glibc(如libc-XXX.so)和Linux动态加载器(如ld-XXX.so)。其中glibc包含标准C程序库的函数,比如printf()和很多其他大多数的应用程序都依赖的常用函数。
Linux动态加载器负责将二进制程序加载到内存中,并且,如果应用程序引用了共享库中的函数,它还需要执行动态链接。
15、定制用户空间初始化程序
在init/main.c中:
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
在上面的语句中,如果execute_command非空,它会指向一个运行在用户空间的字符串,而这个字符串中包含了一个定制的、由用户提供的命令。开发人员在内核命令行中指定这个命令,并且他会由我们前面所研究的__setup宏进行设置。
在init/main.c中:
static int __init init_setup(char *str)
{
unsigned int i;
execute_command = str;
/*
* In case LILO is going to boot us with default command line,
* it prepends "auto" before the whole cmdline which makes
* the shell think it should execute a script with such name.
* So we ignore all arguments entered _before_ init=... [MJ]
*/
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("init=", init_setup);
在include/linux/init.h中:
#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata __aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id \
__used __section(.init.setup) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
下面是一个内核命令行的例子:
initcall_debug init=/sbin/myinit console=ttyS1,115200 root=/dev/hda1
含义:
内核显示所有的初始化函数调用,配置初始的控制台设备/dev/ttyS1,其数据速率为115Kbit/s,并执行一个定制的、名为myinit的用户空间初始化进程,这个程序位于根文件系统的/sbin目录中。它还指导内核从设备/dev/hda1挂载器根文件系统,这个设备是第一个IDE硬盘。
注意一下,一般来说,内核命令行中各个参数的先后次序无关紧要。
16、应用程序依赖关系
大多数应用程序有两类依赖关系:
1、动态链接的应用程序对程序库的依赖,这种应用程序中包含未解决的引用,这需要由程序库提供;
2、应用程序可能需要的外部配置文件或者数据文件
对于前者,可以使用工具来确定,这里介绍两种方法(XXX代表可执行程序):
1、ldd xxx
2、objdump –x xxx | grep NEEDED
对于后者,至少要对相关的应用程序有个基本了解。
17、rmmod 和 modprobe –r
如:modprobe –r ext3 可以用于删除模块,包括某个模块所依赖的模块,而rmmod不会删除一个模块所依赖的模块。
18、 /proc 、sysfs以及tmpfs
他们俩个都是伪文件系统(Pseudo File Systems)。
/proc 文件系统的名称源于他的最初设计目的:它是一个接口,内核通过它可以获取一个Linux系统上所有进程的信息。随着时间的推移,它也不断发展壮大,可以提供更多方面的信息,而不仅限于进程。很多用户空间的应用程序都依靠/proc文件系统中的内容来完成它的工作。例如mount命令,如果在执行时不带任何参数,会列出系统中当前所有已挂载文件系统的信息,而它是从/proc/mounts文件中获取这些信息的。如果不存在/proc文件系统,mount命令直接返回。除了mount之外,其他一些与/proc文件系统交互的实用程序还有free、pkill、pmap以及uptime。请参考procfs软件包获取更多信息。
挂载/proc文件系统: mount –t proc /proc /proc 或者 mount –t proc none /proc
sysfs是对具体的内核对象(比如物理设备)进行建模,并且提供一种将设备和设备驱动程序关联起来的方法。从sysfs中可以获取很多系统信息,很多实用工具都使用了这些信息,比如电源管理和热插拔能力,还有mtd-utils中的很多与Flash操作相关的工具。
tmpfs中的所有内容都是存储在内核的虚拟内存中的,断电或者重启后,这些内容都丢失了。tmpfs文件系统对于快速临时文件存储很有用。对于那些会使用很多小的临时文件的应用程序来说,这可以提高它们的性能。
挂载tmpfs文件系统: mount –t tmpfs /tmpfs /tmp 或者 mount –t tmpfs none /tmp
嵌入式系统中自动挂载这三种文件系统一般在/etc/fstab中设置:
1: # cat /etc/fstab
2: proc /proc proc defaults 0 0
3: sysfs /sys sysfs defaults 0 0
4: tmpfs /dev tmpfs defaults 0 0
5: tmpfs /tmp tmpfs defaults,size=120M 0 0
6: tmpfs /var/run tmpfs defaults 0 0
19、MTD概述
内存技术设备(Memory Technology Device,MTD)子系统的目的是让内核支持种类繁多的类似内存的设备,比如闪存芯片。市面上有很多不同种类的闪存芯片,对它们进行编程的方法也多种多样,主要原因是它们要支持很多特殊和高效的模式。MTD子系统采用了层次化架构,将底层设备的复杂性和(使用这些内存和闪存设备的)高层的数据组织及存储格式分隔开。
简单来说,MTD是一个设备驱动程序层,它提供了一套访问原始闪存设备的通用API接口。MTD支持很多种闪存设备。然而,MTD不是块设备。MTD与设备打交道时是以擦除块(erase block)为单位的,其大小不一,而块设备是以固定大小的块(称为扇区)位操作单位的。块设备有两种主要操作——读取扇区和写入扇区,而MTD有3种:读、写和擦除。MTD设备的写寿命是有限的,所以MTD会包含内部逻辑将写操作分布开来已延长设备的寿命,这被称为损耗均衡(wear leveling)。
与通常的想法相反,SD/MMC卡、CompactFlash卡、USB闪存盘以及其他一些类似的设备都不属于MTD设备。这些设备的内部都包含了闪存转换层,用于完成类似MTD的功能,比如块擦除和损耗均衡。因此,对于系统来说,它们看上去就像是传统的块设备,不需要经过MTD的特殊处理。
Linux中的大多数设备属于字符设备或者块设备中一种。而MTD既不是字符设备,也不是块设备。虽然一些转换机制可以使MTD看起来像字符设备或块设备,但是在Linux驱动架构中,MTD有其独特之处。这是因为MTD驱动程序必须完成一些闪存特有的操作,比如块擦除操作和损耗均衡,而传统的块设备驱动程序是没有类似操作的。
20、软实时和硬实时
软实时
大多数人都同意软实时意味着操作有时间限制。如果超过了时间限制后操作还没有完成的话,体验的质量就会下降,但不会带来致命后果。桌面工作站就是一个需要软实时的绝好的例子。编辑文档时,你期望按键之后立刻在屏幕上看到结果。在播放mp3文件时,你期望听到没有任何杂音、爆音或者中断的高品质音乐。
一般而言,普通人无法分辨出小于几十毫秒的延时。当然音乐家能够听出比这更短的延时,并且告诉你它们影响了音乐的质量。如果这些所谓的软实时事件错过了时限,结果可能不尽如人意,并导致体验的质量有所下降,但这并不是灾难性的。
硬实时
硬实时的特点是错过时限会造成严重结果。在一个硬实时系统中,如果错过了时限,后果往往是灾难性的。当然,“灾难”是相对而言的。但如果你的嵌入式设备正在控制喷气式飞机引擎的燃料流,而它没能及时的响应飞行员输入的命令或者操作特性的变化,致命后果就不可避免了。
注意,时限的持续时间并不是硬实时的特征。原子钟中处理每个嘀嗒的服务程序就是这样的例子。只要在下一个嘀嗒到来之前的持续1秒的时间窗口内处理完成,数据就依然有效。但如果错过了某个嘀嗒,全球定位系统就可能会产生几英尺或甚至几英里的误差!
考虑到这一点,我们借鉴了一组常用的软实时和硬实时的定义。对于软实时系统,如果错过的了时限,系统的计算值或者结果会不太理想。然而,对于硬实时系统,如果错过了某个时限,系统就是失败的,而且可能会造成灾难性的后果。