嵌入式linux启动信息完全注释

2019-07-13 05:39发布

摘要 我们在这里讨论的是对嵌入式 linux 系统的启动过程的输出信息的注释,通过我们的讨论,大家会对嵌入式 linux 启动过程中出现的、以前感觉熟悉的、但却又似是而非的东西有一个确切的了解,并且能了解到这些输出信息的来龙去脉。 嵌入式 linux 的启动信息是一个很值得我们去好好研究的东西,它能将一幅缩影图呈现在我们面前,来指导我们更加深入地理解 linux 内核。   关键字 linux ,嵌入式,启动, bootloader 正文 作为一名嵌入系统开发者,你一定遇到过下面的情景: 在某论坛上看到一篇帖子,上面贴着嵌入式 linux 开发板启动时的有关信息,然后大家在帖子里讨论着这个启动过程中出现的问题,随机举例如下: Linux version 2.4.20-uc0 (root@Local) (gcc version 2.95.3
20010315 (release)(ColdFire patches - 20010318 from http://f
(uClinux XIP and shared lib patches from http://www.snapgear.com/)) #20
6
1
8 00:58:31 CST 2003
Processor: Samsung S3C4510B revision 6
Architecture: SNDS100
On node 0 totalpages: 4096
zone(0): 0 pages.
zone(1): 4096 pages.
zone(2): 0 pages.
Kernel command line: root=/dev/rom0
Calibrating delay loop... 49.76 BogoMIPS
Memory: 16MB = 16MB total
Memory: 14348KB available (1615K code, 156K data, 40K init)
Dentry cache hash table entries: 2048 (order: 2, 16384 bytes)
Inode cache hash table entries: 1024 (order: 1,
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes)
Page-cache hash table entries: 4096 (order: 2, 16384 bytes)
POSIX conformance testing by UNIFIX
Linux NET4.0 for Linux 2.4
Based upon Swansea University Computer Society NET3.039
Initializing RT netlink socket
Starting kswapd
Samsung S3C4510 Serial driver version 0.9 (2001-12-27) with no serial options en
abled
ttyS00 at 0x3ffd000 (irq = 5) is a S3C4510B
ttyS01 at 0x3ffe000 (irq = 7) is a S3C451
Blkmem copyright 1998,1999 D. Jeff Dionne
Blkmem copyright 1998 Kenneth Albanowski
Blkmem 1 disk images:
0: BE558-1A5D57 [VIRTUAL BE558-1A5D57] (RO)
RAMDISK driver initialized: 16 RAM disks of 1024K size 1024 blocksize
Samsung S3C4510 Ethernet driver version 0.1 (2002-02-20)
eth0: 00:40:95:36:35:34
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP: Hash tables configured (established 1024 bind 1024)
VFS: Mounted root (romfs
Freeing init memory: 40K
上面的这些输出信息,也可能包括你自己正在做的嵌入式 linux 开发板的输出信息,其中的每一行,每一个字的含义,你是否深究过,或者说大部分的含义你能确切地知道的?本人想在这里结合本人在实践中一些体会来和广大嵌入式 linux 的开发者一起读懂这些信息。 我们在这里将以一个真实的嵌入式 linux 系统的启动过程为例,来分析这些输出信息。启动信息的原始内容将用标记标出,以区别与注释。   嵌入式 linux 的启动主要分为两个阶段:      第一部分 bootloader 启动阶段      第二部分 linux 内核初始化和启动阶段 第一节: start_kernel 第二节:用户模式 ( user_mode ) 开始, start_kernel 结束 第三节:加载 linux 内核完毕,转入 cpu_idle 进程   第一部分 : bootloader 启动 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 是以字节进行传输的,每次可以传输 1-2Bit. 如: MMC,SD,MS 卡.串行闪存器件体积小,引脚也少,成本相对也更低廉。  ③不可擦除 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 DRAM 要快,这是因为 SRAM 没有刷新周期。每个 SRAM 存储单元由 6 个晶体管组成,而 DRAM 存储单元由一个晶体管和一个电容器组成。相比而言, DRAM SRAM 每个存储单元的成本要高。照此推理,可以断定在给定的固定区域内 DRAM 的密度比 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 43f291aa 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 开始,引入了虚拟文件系统管理器 VFS 的概念。 Linux 下的文件系统主要可分为三大块:      一是上层的文件系统的系统调用,      二是虚拟文件系统交换器 VFS(Virtual Filesystem Switch)      三是挂载到 VFS 中的各实际文件系统,例如 ext2 jffs 等。 VFS 的确切叫法是 Virtual Filesystem Switch 虚拟文件系统交换器,这里的 VFS 中的“ S ”是指的 switch ,这个需要强调一下的,它很容易被混淆成“ system ”,如果理解成“ system ”将是不正确的,请多加注意。 VFS 是具体文件系统 filesystem 的一个管理器。 VFS Linux 内核中的一个软件层, 一种软件机制, 它也提供了内核中的一个抽象功能,允许不同的文件系统共存,可以称它为 Linux 的文件系统管理者, 与它相关的数据结构只存在于物理内存当中。所以在每次系统初始化期间, Linux 都首先要在内存当中构造一棵 VFS 的目录树。 VFS 中的各目录其主要用途是用来提供实际文件系统的挂载点。而 rootfs 将是这个目录树的根结点的( root ),即 "/" 目录, VFS 的结构就是从这个 rootfs 开始的。有了 VFS ,那么对文件的操作将使用统一的接口,将来通过文件系统调用对 VFS 发起的文件操作等指令将被 rootfs 文件系统中相应的函数接口所接管。   注意: 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 的启动过程了。   第二部分 : linux 内核初始化以及启动 第一节: start_kernel Linux 的源代码可以从 www.kernel.org 得到,或者你可以查看 linux 代码交叉引用网站: http://lxr.linux.no/ 进行在线的代码查看,这是一个很好的工具网站。 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(); 具体内容可以参考 [http://lxr.linux.no/source/init/main.c] Linux version 2.4.22-uc0 (root@local) (gcc version 2.95.3 20010315 (release)) #33 .?1.. 20 12:09:106 上面的代码输出信息,是跟踪 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, 按上面的地址换算方法, 1 后面有 7 0 ,那么虚拟地址 256M 左右处。 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) 设置最小内存为 12MB, zone(1) zone(2) 0 页。警告:对齐不正确 Kernel command line: root=/dev/mtdblock3 Kernel 启动命令设为: /dev/mtdblock3 (在后面的说明中会看到 mtdblock3 是指的 flash 上的 romfs 分区。),用来指定根文件系统所在的位置, kernel 会将块设备 mtdblock3 当作文件系统来处理。 也就是说,内核会根据上面的 kernel 命令行,知道只读文件系统 romfs 将是根文件系统 rootfs start_kernel(void) 中输出的上面的这句信息。 这行命令是在 linux 内核启动过程中都会输出的一句。 Console: colour dummy device 80x30 代码中 console_init() 的输出信息 , 显示控制台属性:一般使用 VGA text console ,标准是 80 X 25 行列的文本控制台,这里是对属性进行了设置。 serial_xx: setup_console @ 115 串口设置值为 115200 ,此为波特率输出信息。对串口设置的信息做一个打印的动作,在调试时会非常有用。 Calibrating delay loop... 82.94 BogoMIPS Calibrate: 校准 , 进入时延校准循环。检查 CPU MIPS( 每秒百万条指令 ) Bogo Bogus( ) 的意思。这里是对 CPU 进行一个实时测试,来得到一个大体的 MIPS 数值 Bogomips, 是由 linus Torvalds 写的 , Linux 操作系统中衡量计算机处理器运行速度的一种尺度。提供这种度量的程序被称为 BogoMips ,当启动计算机时, BogoMips 能显示系统选项是否处于最佳性能。 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) 当前内存使用情况,将列出总的内存大小 , 及分配给内核的内存大小 : 包括代码部分,数据部分,初始化部分 , 总共刚好 4M 。请留意此处的内核的内存大小的各个值。   进入虚拟文件系统 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 :目录数据结构      Inode i 节点      Mount cache :文件系统加载缓冲      buffer cache :内存缓冲区      Page Cache :页缓冲区 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: 顺应 , 一致。即 POSIX 适应性检测。 UNIFIX 是一家德国的技术公司, Linux 原本要基于 POSIX.1 , 但是 POSIX 不是免费的 , 而且 POSIX.1 证书相当昂贵 . 这使得 Linux 基于 POSIX 开发相当困难 . Unifix 公司 (Braunschweig, 德国 ) 开发了一个获得了 FIPS 151-2 证书的 Linux 系统 . 这种技术用于 Unifix 的发行版 Unifix Linux 2.0 Lasermoon Linux-FT 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 设备的初始化 init()--->do_basic_init()--->pci_init() ,初始化 PCI ,检测系统的 PCI 设备。 Linux NET4.0 for Linux 2.4 Based upon Swansea University Computer Society NET3.039 英国威尔士,斯旺西大学的 NET3.039, TCP/IP 协议栈 此信息,在 linux 启动过程中都会出现。 Initializing RT netlink socket Socket 的初始化, socket_init() Netlink 一种路由器管理协议 (linux-2.4.22/net/core/Rtnetlink.c Routing netlink socket interface: protocol independent part 其中 RT route 路由的意思。这句输出是在 create 产生 rtnetlink socket 套接字时的一个调试输出。 ) 此信息,在 linux 启动过程中都会出现。 Starting kswapd 启动交换守护进程 kswapd ,进程 IO 操作例程 kpiod kswapd 可以配合 kpiod 运行。进程有时候无事可做,当它运行时也不一定需要把其所有的代码和数据都放在内存中。这就意味着我们可以通过把运行中程序不用的内容切换到交换分区来更好的是利用内存。大约每隔 1 秒, kswapd 醒来并检查内存情况。如果在硬盘的东西要读入内存,或者内存可用空间不足, kpiod 就会被调用来做移入 / 移出操作。 kswapd 负责检查, kpiod 负责移动。 Journalled Block Device driver loaded 加载日志块设备驱动。 日志块设备是用来对文件系统进行日志记录的一个块设备。日志文件系统是在传统文件系统的基础上,加入文件系统更改的日志记录。 它的设计思想是:跟踪记录文件系统的变化,并将变化内容记录入日志。日志文件系统在磁盘分区中保存有日志记录,写操作首先是对记录文件进行操作,若整个写操作由于某种原因 ( 如系统掉电 ) 而 中断,系统重启时,会根据日志记录来恢复中断前的写操作。在日志文件系统中,所有的文件系统的变化都被记录到日志,每隔一定时间,文件系统会将更新后的元 数据及文件内容写入磁盘。在对元数据做任何改变以前,文件系统驱动程序会向日志中写入一个条目,这个条目描述了它将要做些什么,然后它修改元数据。 devfs: v1.12c (20020818) Richard Gooch (rgooch@atnf.csiro.au) devfs: boot_options: 0x1 Devfs 模块的输出信息。 设备文件系统 devfs ,版本 1.12c pty: 256 Unix98 ptys configured Pty 模块的输出信息,与控制台操作有关的设置。 将通过 devpts 文件系统使用 Unix98 PTYs ,( Pseudo-ttys (telnet etc) device 是伪 ttys 设备的缩写。               TTY(/dev/tty) TeleTYpe 的一个老缩写,为用户输入提供不同控制台的设备驱动程序。它的名字来源于实际挂接到 UNIX 系统的、被称为电传打字机 (teletype) 的终端。在 Linux 下,这些文件提供对虚拟控制台的支持,可以通过按< Alt-F1 >到< Alt-F6 >键来访问这些虚拟控制台。这些虚拟控制台提供独立的、同时进行的本地登录对话过程               ttys(/dev/ttys) 是计算机终端的串行接口。 /dev/ttyS0 对应 MS-DOS 下的 COM1
使用 make dev 脚本 MAKEDEV 来建立 pty 文件。这样系统内核就支持 Unix98 风格的 pty 了。在进行 Telnet 登录时将要用到 /dev/p