本文共 13598 字,大约阅读时间需要 45 分钟。
摘要
我们在这里讨论的是对嵌入式linux系统的启动过程的输出信息的注释,通过我们的讨论,大家会对嵌入式linux启动过程中出现的、以前感觉熟悉的、但却又似是而非的东西有一个确切的了解,并且能了解到这些输出信息的来龙去脉。
嵌入式linux的启动信息是一个很值得我们去好好研究的东西,它能将一幅缩影图呈现在我们面前,来指导我们更加深入地理解linux内核。
关键字:linux,嵌入式,启动,bootloader
正文
作为一名嵌入系统开发者,你一定遇到过下面的情景:
在某论坛上看到一篇帖子,上面贴着嵌入式linux开发板启动时的有关信息,然后大家在帖子里讨论着这个启动过程中出现的问题,随机举例如下:
Linux version |
上面的这些输出信息,也可能包括你自己正在做的嵌入式linux开发板的输出信息,其中的每一行,每一个字的含义,你是否深究过,或者说大部分的含义你能确切地知道的?本人想在这里结合本人在实践中一些体会来和广大嵌入式linux的开发者一起读懂这些信息。
我们在这里将以一个真实的嵌入式linux系统的启动过程为例,来分析这些输出信息。启动信息的原始内容将用标记标出,以区别与注释。
嵌入式linux的启动主要分为两个阶段:
①
②
第一节:start_kernel
第二节:用户模式( user_mode )开始,start_kernel结束
第三节:加载linux内核完毕,转入cpu_idle进程
第一部分
Boot loader v0.12 NOTE: this boot loader is designed to boot kernels made with the 2.4.xx releases bootloader for XV Built at Nov 20 2005 10:12:35 |
Bootloader头信息,版本,编译时间等,这个因不同的bootloader的设计而有所不同,由此你能看出bootloader的版本信息,有很多使用的是通用的bootloader,如u-boot,redboot等。
Loaded to 0x90060000 |
将bootloader加载到内存ram中的0x90060000处,即将bootloader加载到内存的高端地址处。
Linux内核将被bootloader加载到0x90090000处。
Found boot configuration |
查找到了启动boot的配置信息
Booted from parallel flash |
从flash中启动代码,此处的flash为并行闪存。Flash的分类列举如下:
闪存分三类:并行,串行,不可擦除。
①并行Parallel flash
NOR Flash,Intel于1988年发明.随机读取的速度比较快,随机按字节写,每次可以传输8Bit。一般适合应用于数据/程序的存贮应用中.NOR还可以片内执行(execute-in-place)XIP.写入和擦除速度很低。
NAND Flash,1989年,东芝公司发明.是以块和页为单位来读写的,不能随机访问某个指定的点.因而相对来说读取速度较慢,而擦除和写入的速度则比较快,每次可以传输16Bit,一般适用在大容量的多媒体应用中,容量大。如:CF,SM.
②串行Serial Flash
③不可擦除Mask Rom Flash的特点是一次性录入数据,具有不可更改性,经常运用于游戏和需版权保护文件等的录入。其显著特点是成本低。
注意:任何flash器件的写入操作只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。NAND器件执行擦除操作是十分简单的,而NOR则要求在进行擦除前先要将目标块内所有的位都写为0。
从上面的信息,我们可以对flash类型特点有个比较明确的了解。
CPU clock rate: 200 MHz |
开发板上所使用的CPU的主频为200MHZ.
DRAM size is 128MB (128MB/0MB) |
动态内存ram大小为128M。这里我们列举一下内存的类型及工作原理。
根据内存的工作原理可以划分出两种内存:DRAM和SRAM
①DRAM表示动态随机存取存储器。这是一种以电荷形式进行存储的半导体存储器。DRAM中的每个存储单元由一个晶体管和一个电容器组成。数据存储在电容器中。电容器会由于漏电而导致电荷丢失,因而DRAM器件是不稳定的。为了将数据保存在存储器中,DRAM器件必须有规律地进行刷新。
②SRAM是静态的,因此只要供电它就会保持一个值。一般而言,SRAM
SRAM常常用于高速缓冲存储器,因为它有更高的速率;而DRAM常常用于PC中的主存储器,因为其拥有更高的密度。
在嵌入式系统中使用DRAM内存的设计比较广泛。
地址辅助说明:
先说明一下内存地址数字情况,主要是为了方便记忆。
可以访问的内存为4G。
0x40000000是1GB处;0x00040000是256K处,0x00020000是128K处,0x90000000是2GB多的地方。
1M->0x00100000,
2M->0x00200000,
8M->0x00800000
16M->0x01000000,
32M->0x02000000
256M->0x10000000
64K->0x00010000
4K->0x00001000
这个是个快速记忆的方法,你可以根据地址中1的位置和其后0的个数来快速知道换算后的地址是在多少兆的地方。比如,1的后面5个0,代表1M的大小,6个0,代表16M,以此类推。
ROMFS found at 0x46040000, Volume name = rom
romfs,只读文件系统所在的地址为:0x46040000 (flash映射后的第3分区)。
卷名为rom。
romfs和rootfs概念上有所区别。
flash在内存中的的起始地址为0x46000000,而ROMFS在flash分区上的起始位置为0x00040000,所以ROMFS在内存地址中的位置就为0x46040000。这个细节的部分可以参考flash分区时的地方,Creating 3 MTD partitions。
romfs中包括kernel和app应用,不包括bootloader和firmware信息头。romfs只读文件系统里的内容有很多种分类方法,我们可以将kernel和app同时放里面,作为根文件系统下的一个文件,也可以在flash上另外划分区域来分别存放。
VFS虚拟文件系统交换器
在linux系统中,目前已经开发出多种文件系统,那么如何让这些文件系统能共存在一个系统中呢,从linux 2.0开始,引入了虚拟文件系统管理器
Linux
①
②
③
VFS的确切叫法是Virtual Filesystem Switch虚拟文件系统交换器,这里的VFS中的“S”是指的switch,这个需要强调一下的,它很容易被混淆成“system”,如果理解成“system”将是不正确的,请多加注意。
VFS是具体文件系统filesystem的一个管理器。
VFS是Linux内核中的一个软件层,一种软件机制,它也提供了内核中的一个抽象功能,允许不同的文件系统共存,可以称它为
注意:rootfs并不是一个具体的文件系统类型,如jffs。它只是一个理论上的概念。在具体的嵌入系统实例中,可以将某种具体的文件系统设置为根文件系统rootfs,如我们可以设置romfs为根文件系统,也可以设置jffs为根文件系统。
这里的ROMFS只读文件系统只是一种具体的文件系统类型,也是在嵌入系统中经常使用到的类型。
看完了上面的内容,以后你对出现的类似“kernel Panic:VFS:Unable to mount root fs on 0:00”的含义应该已经了解了。其中“VFS:”就是虚拟文件系统管理器操作时的输出信息了。
File linux.bin.gz found |
linux kernel内核文件名,它是在只读文件系统romfs上的一个组成部分。
Unzipping image from 0x4639DE60 to 0x90090000, size = 1316021 |
将romfs中的linux kernel解压缩到0x90090000,之后会从这个内存地址启动内核。romfs为压缩格式文件,使用压缩的只读文件系统,是为了保持制作出来的整个系统所占用的flash空间减小。这个内核的大小为1.3M左右,这也是目前大多数嵌入系统所使用的方法。
Inptr = 0x00000014(20) Inflating.... |
释放,解压中。。。(变大,充气,
Outcnt = 0x0030e7c8(3205064) Final Inptr = 0x001414ad(1316013) Original CRC = 0xcbd73adb Computed CRC = 0xcbd73adb |
做释放后的CRC检查
Boot kernel at 0x90090000 with ROMFS at 0x46040000 |
kernel已经被从romfs中释放到内存地址0x90090000处,可以跳转到此处启动kernel了,这里是指定的kernel的起始地址
Press 'enter' to boot |
系统等待启动,后面将看到linux kernel的启动过程了。
第二部分
第一节:start_kernel
Linux的源代码可以从得到,或者你可以查看linux代码交叉引用网站:
在start_kernel中将调用到大量的init函数,来完成内核的各种初始化。如:
page_address_init();
sched_init();
page_alloc_init();
init_IRQ();
softirq_init();
console_init();
calibrate_delay();
vfs_caches_init(num_physpages);
rest_init();
具体内容可以参考[]
Linux version |
上面的代码输出信息,是跟踪linux代码分析后得到的,进入init目录下的main.c的start_kernel启动函数.
嵌入式linux使用的是linux内核版本为2.4.22
linux source code代码中start_kernel中输出的linux_banner信息。这个信息是每个linux kernel都会打印一下的信息,如果你没有把这句去掉的话。
Found bootloader memory map at 0x10000fc0.
bootloader经过内存映射后的地址为:0x10000fc0,
Processor: ARM pt110 revision 0 |
pT110是ARM微处理器arm核的一种,另一种为pT100。此处为显示ARM的类型。
On node 0 totalpages: 20480 zone(0): 20480 pages. zone(0): Set minimum memory threshold to 12288KB Warning: wrong zone alignment (0x90080000, 0x0000000c, 0x00001000) zone(1): 0 pages. zone(2): 0 pages. |
预留内存大小,在节点0上总共20页, zone(0)
Kernel command line: root=/dev/mtdblock3 |
Kernel
也就是说,内核会根据上面的kernel命令行,知道只读文件系统romfs将是根文件系统rootfs。
start_kernel(void)中输出的上面的这句信息。
这行命令是在linux内核启动过程中都会输出的一句。
Console: colour dummy device 80x30 |
代码中console_init()的输出信息,
serial_xx: setup_console @ 115 |
串口设置值为115200,此为波特率输出信息。对串口设置的信息做一个打印的动作,在调试时会非常有用。
Calibrating delay loop... 82.94 BogoMIPS |
Calibrate:校准,
Bogomips,是由linus Torvalds写的,
linux内核中有一个函数calibrate_delay(),它可以计算出cpu在一秒钟内执行了多少次一个极短的循环,计算出来的值经过处理后得到BogoMIPS值
你可以将计算机的bogomips与计算机处理器的bogomips进行比较。Torvalds称这个程序为BogoMips来暗示两台计算机间的性能度量是错误的,因为并非所有起作用因素都能被显示出来或被认可。尽管计算机基准中经常用到MIPS,但环境的变化容易导致度量的错误。Bogomips能测出一秒钟内某程序运行了多少次。
察看/proc/cpuinfo文件中的最后一行也能得到这个数值。
上面这个输出,在所有的linux系统启动中都会打印出来。
进入内存初始化
mem_init(void), [arch/i386/mm/init.c]
Memory: 80MB = 80MB total Memory: 76592KB available (1724K code, 2565K data, 72K init) |
当前内存使用情况,将列出总的内存大小,
进入虚拟文件系统VFS初始化
vfs_caches_init()
Dentry cache hash table entries: 16384 (order: 5, 131072 bytes) Inode cache hash table entries: 8192 (order: 4, 65536 bytes) Mount cache hash table entries: 512 (order: 0, 4096 bytes) Buffer cache hash table entries: 4096 (order: 2, 16384 bytes) Page-cache hash table entries: 32768 (order: 5, 131072 bytes) |
名词:
①
②
③
④
⑤
Dentry目录数据结构(目录入口缓存),提供了一个将路径名转化为特定的dentry的一个快的查找机制,Dentry只存在于RAM中;
i节点(inode)数据结构存放磁盘上的一个文件或目录的信息,i节点存在于磁盘驱动器上;存在于RAM中的i节点就是VFS的i节点,dentry所包含的指针指向的就是它;
buffer cache内存缓冲区,类似kupdated,用来在内存与磁盘间做缓冲处理;
Page Cache
在内存中建立各个缓冲hash表,为kernel对文件系统的访问做准备。
VFS(virtual filesystem switch)虚拟文件切换目录树有用到类似这样的结构表。
上面的输出信息,在一般的linux启动过程中都会看到。
POSIX conformance testing by UNIFIX |
conformance:顺应,
在2.6的内核中就将上面的这句输出给拿掉了。
第二节:用户模式( user_mode )开始,start_kernel结束
PCI: bus0: Fast back to back transfers disabled PCI: Configured XX as a PCI slave with 128MB PCI memory PCI: Each Region size is 16384KB PCI: Reserved memory from 0x10080000 to 0x15080000 for DMA and mapped to 0x12000000 |
设备的初始化
Linux NET4.0 for Linux 2.4 Based upon |
英国威尔士,斯旺西大学的NET3.039, TCP/IP
此信息,在linux启动过程中都会出现。
Initializing RT netlink socket |
对Socket的初始化,socket_init(),Netlink
此信息,在linux启动过程中都会出现。
Starting kswapd |
启动交换守护进程kswapd,进程IO操作例程kpiod
kswapd可以配合kpiod运行。进程有时候无事可做,当它运行时也不一定需要把其所有的代码和数据都放在内存中。这就意味着我们可以通过把运行中程序不用的内容切换到交换分区来更好的是利用内存。大约每隔1秒,kswapd醒来并检查内存情况。如果在硬盘的东西要读入内存,或者内存可用空间不足,kpiod就会被调用来做移入/移出操作。kswapd负责检查,kpiod负责移动。
Journalled Block Device driver loaded |
加载日志块设备驱动。
日志块设备是用来对文件系统进行日志记录的一个块设备。日志文件系统是在传统文件系统的基础上,加入文件系统更改的日志记录。
它的设计思想是:跟踪记录文件系统的变化,并将变化内容记录入日志。日志文件系统在磁盘分区中保存有日志记录,写操作首先是对记录文件进行操作,若整个写操作由于某种原因(如系统掉电)而 中断,系统重启时,会根据日志记录来恢复中断前的写操作。在日志文件系统中,所有的文件系统的变化都被记录到日志,每隔一定时间,文件系统会将更新后的元 数据及文件内容写入磁盘。在对元数据做任何改变以前,文件系统驱动程序会向日志中写入一个条目,这个条目描述了它将要做些什么,然后它修改元数据。
devfs: v1.12c devfs: boot_options: 0x1 |
Devfs模块的输出信息。
设备文件系统devfs,版本1.12c,
pty: 256 Unix98 ptys configured |
Pty模块的输出信息,与控制台操作有关的设置。
将通过
①
②
使用
linux-2.4.22\drivers\char\Pty.c,
loop: loaded (max 8 devices) |
加载返还块设备驱动,最多支持8个设备
8139too Fast Ethernet driver eth0: RealTek RTL8139 at 0x60112000, 00:10:0d:42:a0:03, IRQ 14 eth0: Identified 8139 chip type 'RTL-8100B/8139D' |
网卡驱动,基地址为:0x60112000,
RTL8139
从
SCSI subsystem driver Revision: 1.00 |
USB设备信息,USB会被当做SCSI来处理。
mumk_register_tasklet: (1) tasklet 0x905bf9c0 status @0x9025e974 |
软中断信息输出。Tasklet是在2.4中才出现,它是为了更好地利用多CPU。
Probing XX Flash Memory |
探测
Amd/Fujitsu Extended Query Table v1.3 at 0x0040
number of CFI chips: 1
AMD与富士通合资设立的Flash供货商Spansion。AMD因获利不佳,已经退出Flash市场,后续由Spansion合资公司经营.主要生产NOR类型的flash,特点是容量小,速度快。Spansion商标的flash,在我们开发中会经常看到。以后大家看到Spansion的芯片,就能了解到它和AMD还有富士通的来龙去脉了。
Common flash Interface (CFI)是指一个统一的flash访问接口,表示这种flash是这种接口类型的。
Using buffer write method |
使用flash写缓冲方式
flash提供了写BUFFER的命令来加快对flash上块的操作。对Flash擦除和写数据是很慢的。如果用写BUFFER的命令会快一点。据手册上说,会快20倍。Buffer Size
比如某flash有128个128K字节块。允许用户对任意块进行字节编程和写缓冲器字节编程操作,每字节编程时间为210μs;若采用写缓冲器字节编程方式,32字节编程共需218μs,每字节编程时间仅为6.8μs。芯片的块擦除时间为1s,允许在编程或块擦除操作的同时进行悬挂中断去进行读操作,待读操作完成后,写入悬挂恢复命令,再继续编程或块擦除。
Creating 3 MTD partitions on "XX mapped flash": 0x00000000-0x00020000 : "BootLoader" 0x00020000-0x00040000 : "Config" 0x00040000-0x01000000 : "Romfs" |
此处为重要信息部分,需要特别留意。
在内存中映射过的flash,创建三个MTD分区:
flash上的内容将被映射到内存中的对应地址
前128K为BootLoader--->0x00000000-0x00020000
接着的128K为系统配置信息Config存放的位置--->0x00020000-0x00040000
再后面的
上面的内容,大家可以根据前面的换算公式得到。
A>
B>
C>
转载地址:http://japci.baihongyu.com/