零,简单概括总结嵌入式上电启动顺序
·启动第一步:加载BIOS
当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了。
启动第二步:读取MBR
硬盘上第0磁道第一个扇区被称为MBR,也就是Master Boot Record,即主引导记录,它的大小是512字节,别看地方不大,可里面却存放了预启动信息、分区表信息。
系统找到BIOS所指定的硬盘的MBR后,就会将其复制到0x7c00地址所在的物理内存中。其实被复制到物理内存的内容就是Boot Loader,而具体到你的电脑,那就是lilo或者grub了。
启动第三步:Boot Loader
Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备。
Boot Loader有若干种,其中Grub、Lilo和spfdisk是常见的Loader。
我们以Grub为例来讲解吧,毕竟用lilo和spfdisk的人并不多。
系统读取内存中的grub配置信息(一般为menu.lst或grub.lst),并依照此配置信息来启动不同的操作系统。
启动第四步:加载内核
根据grub设定的内核映像所在路径,系统读取内存映像,并进行解压缩操作。此时,屏幕一般会输出“Uncompressing Linux”的提示。当解压缩内核完成后,屏幕输出“OK, booting the kernel”。
系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。至此,Linux内核已经建立起来了,基于Linux的程序应该可以正常运行了。
启动第五步:用户层init依据inittab文件来设定运行等级内核被加载后,第一个运行的程序便是/sbin/init,该文件会读取/etc/inittab文件,并依据此文件来进行初始化工作。其实/etc/inittab文件最主要的作用就是设定Linux的运行等级,其设定形式是“:id:5:initdefault:”,这就表明Linux需要运行在等级5上。
启动第六步:进程执行rc.sysinit
在设定了运行等级后,Linux系统执行的第一个用户层文件就是/etc/rc.d/rc.sysinit脚本程序,它做的工作非常多,包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等,可以到/etc/rc.d中查看一下rc.sysinit文件.
启动第七步:启动内核模块
具体是依据/etc/modules.conf文件或/etc/modules.d目录下的文件来装载内核模块。
启动第八步:执行不同运行级别的脚本程序
根据运行级别的不同,系统会运行rc0.d到rc6.d中的相应的脚本程序,来完成相应的初始化工作和启动相应的服务。
启动第九步:执行/etc/rc.d/rc.local打开了此文件,里面有一句话,
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don’t
# want to do the full Sys V style init stuff.
rc.local就是在一切初始化工作后,Linux留给用户进行个性化的地方。你可以把你想设置和启动的东西放到这里。
启动第十步:执行/bin/login程序,进入登录状态
此时,系统已经进入到了等待用户输入username和password的时候了,你已经可以用自己的帐号登入系统了。
一,明确嵌入式linux系统软件启动流程
->硬件上电
-> CPU自动运行Nand的u-boot(属于bootloader的一种) u-boot运行
-> u-boot会从"某个地方"将kernel内核加载到内存的某个地址,加载完以后,
并且uboot从内存的这个地址启动kernel内核,给内核传递启动参数
->内核一旦启动,uboot的生命周期结束
->内核开始玩命运行,运行到最后
-> 根据uboot传递的启动参数,去到"某个地方"去挂接(找)根文件系统rootfs
-> 一旦找到,内核至此把控制权交给根文件系统rootfs的第一号进程sbin/init
->第一号进程启动,创建一个子进程,子进程运行shell程序(bin/sh) 接下来就开始接收和处理用户输入的命令(ls...)
注意:如果内核和rootfs烧写Nand,笔记提及“某个地方“就是指Nand的某个分区
二,uboot相关
1,bootloader:
特点:
(1).启动代码,本质就是一个裸板程序
(2).bootloader统称,u-boot属于bootloader的一种类似PC的BIOS!
(3).下位机上电运行的第一个程序!
(4).烧写到闪存(Nand)的0地址开始
功能:
用来加载linux操作系统内核kernel到内存,并且从内存中启动linux内核,最后给内核传递启动参数,
告诉内核,你将来要挂接的根文件系统rootfs在哪里
2, 首先明确uboot的功能:
从某个地方加载内核到内存,并且从内存中启动内核,并且给内核传递的启动参数,告诉内核,根文件系统在哪里;
然后明确uboot的生命周期:
上电CPU自动开始运行 / 内核启动结束
3, uboot所做的工作:
观察make编译最后的链接过程,肯定指定了链接脚本的路径和程序运行的基地址!
通过链接脚本获取到运行的第一个文件:start.S
通过链接脚本获取到运行的第一个入口:_start
bootloader 是系统的一个引导程序,可以用于调试升级系统。可以是EBOOT \UBOOT\ SERIL BOOT等待其他一切方式。
Eboot 是使用网口的意思,UBOOT 是使用USB口的意思,SERIL BOOT 是串口,但是现在很多BSP时间上都是在EBOOT的基础上修改的,加上了usb部分或其他的功能,我们的bootloader虽然是名字是EBOOT 但实际上就没有网口的功能。
3.1,uboot的第一阶段——初始化硬件
start.S :汇编实现的阶段,此阶段通常称之第一阶段
(1).初始化CPU(Cache,MMU,关闭中断)
(2).初始化内存(内存控制器)
(3).初始化闪存(Nand控制器)
(4).初始化时钟(时钟控制器)时钟源:24MHz / APLL:1G/...
(5).关闭看门狗(看门狗控制器)
(6).初始化UART
(7).初始化LCD
注意:并不是所有的硬件都需要在uboot中进行初始化,哪些初始化,那些不初始化完全根据用户的实际需求,如路由器
3.2 正式进入uboot的C语言实现的阶段,此阶段通常称之为第二阶段
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
uboot继续运行,执行对应的C语言实现的代码:start_armboot:lib_arm/board.c
典型代码:
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
{
if ((*init_fnc_ptr)() != 0)
{
hang ();
}
} //此循环中做了各种init工作!
4,移植(修改)uboot的关注点:
在init_sequence它中有一个函数指针board_init此函数里面做了根开发板硬件相关的初始化代码将来可以将自己的硬件初始化添加在此函数中调用即可。注意:board_init函数将来要必看!
最后跑到一个死循环:
for (;;) { main_loop (); }
main_loop()
{
s = getenv("bootcmd");
//如果3秒以内没有键盘操作
run_command(s, 0);//执行bootcmd的命令
//如果有键盘操作,最后进入
for (;;)
{
readline(s);//读取用户输入的命令
run_commond(s, 0);//执行uboot的命令
}
}
总结:uboot的代码执行
A.先运行start.S 入口:_start(汇编实现)
B.再运行board.c 入口:start_armboot(C语言实现)
5,查看所有的uboot命令
help 命令名:查看命令的帮助
print //打印环境变量关键环境变量:
ipaddr:下位机的ip
serverip:上位机的ip以上两个ip需要在同一个网段
bootcmd:内核启动
bootargs:内核传参
setenv //修改环境变量
setenv bootcmd ...
saveenv //保存修改的环境变量
ping //检查网卡的物理连接
tftp //tftp文件下载
nand erase/write/read
reset 重启
boot 启动,本质就是执行bootcmd的命令
md:读取外设的数据并打印显示
mw. 向外设写入数据
三, linux操作系统内核(kernel):
1, 特点:
(1)linux系统的核心;
(2)操作系统内核由bootloader加载启动类似windows由BIOS加载引导。
(3)内核启动以后,运行到最后,根据bootloader传递过来的参数,内核根据此参数,去到 “某个地方”找根文件系统rootfs,
找到以后,执行根文件系统rootfs中的第一号进程init,最后init第一号进程创建一个子进程来运行sh程序(shell),
接收和处理用户输入的命令,例如ls,cd,mkdir等,至此linux系统运行完毕!
(4)烧写到Nand上
功能:7大子系统 进程管理子系统 —— 进程的创建,调度,抢占,销毁
内存管理子系统 —— 内存的分配,映射,销毁
网络协议栈 —— 除了物理层,其它层的软件
文件系统 —— EXT4/EXIT3/EXT2/FAT32/NTFS/CRAMFS/YAFFS2/UBIFS/RAMDISK
设备驱动 —— 硬件设备驱动程序
系统调用 —— open/close/read/write/brk/sbrk/mmap/fork/exit/...
平台相关代码 —— 这些代码能够让linux运行在X86,ARM/powerpc/mips/fpga/dsp这些主流的硬件上
2,设置内核的启动参数
重启开发板,进入uboot的命令行模式:
setenv bootcmd nand read 50008000 500000 500000 \; bootm 50008000
saveenv //保存环境变量bootcmd的值
说明:bootcmd:此环境变量的功能用于uboot起来以后,如果用户在3秒以内没有进行键盘操作,3秒以后uboot会自动执行bootcmd对应的命令:nand read 50008000 500000 500000 ; bootm 50008000
说明:将内核从Nand的5M开始,读5M到内存的 0x50008000地址,然后再利用bootm命令从内存的0x50008000启动内核,内核就开始玩命的运行了
总结:从Nand加载内核到内存,并且启动内核
setenv bootargs root=/dev/mtdblock3 init=/init console=ttySAC0,115200
saveenv
说明:bootargs:此环境变量的作用是用于给内核传递启动参数告诉内核,根文件系统rootfs在哪里设置的参数信息为:root=/dev/mtdblock3 init=/init console=ttySAC0,115200 (SAC0,115200不能有空格)
说明:root=/dev/mtdblock3:告诉内核,根文件系统rootfs在Nand的第四分区‘;
init=/init:一旦找到了根文件系统rootfs,启动的第一号进程在根文件系统rootfs的/init,运行此程序;
console=ttySAC0,115200:指定内核启动时打印输出信息,使用的串口为第一个串口ttySAC0,
指定的波特率为115200重启开发板,就不要操作键盘,3秒以后系统启动!等待着系统的起来!
3,Kconfig的使用语法
作用:仅仅生成一个选项,生成的选项给对应的Makefile使用,Makefile根据选项最终决定内核程序如何编译
例子:
cd /opt/kernel
vim drivers/char/Kconfig
config HELLOWORLD
tristate "hello, world"
help
this is a test
说明:
config关键字用来生成一个选项CONFIG_HELLOWORLD(注意别忘记CONFIG_)
此选项CONFIG_HELLOWORLD给将来编译内核程序的Makefile使用
4, tristate关键字,指示此选项的操作方式有三种:
(1).按Y选择为*,最终导致CONFIG_HELLOWORLD=y表示此选项对应的内核程序helloworld.c将会和zImage编译在一起("在一起")
(2).按M选择为M,最终导致CONFIG_HELLOWORLD=m表示此选项对应内核程序helloworld.c不会和zImage编译在一起,需要进行单独编译(分家)
(3).按N不选择,不选择也就不编译,CONFIG_HELLOWORLD=空
5,驱动内容对应硬件简介
System Type --->
//说明当前内核支持ARM架构,支持三星S5PV210处理器
ARM system type (Samsung S5PV210/S5PC110) --->
//指定内核启动的打印输出信息用哪个串口
(0) S3C UART to use for low-level messages
//选择某个开发板,一般选择参考板即可,让内核支持当前的开发板
Board selection (SMDKV210) --->
Boot options --->
//此选项的内容就是将来内核启动的参数,内核根据
此参数去挂接根文件系统rootfs
(console=ttySAC2,115200) Default kernel command string
//如果此选中选中(按Y选择为*),那么内核启动的参数是内核自己传递
就是上面的选项
//如果此选项没选([]里面为空),那么内核启动的参数用
uboot的bootargs
[ ]Always use the default kernel command string
Device Drivers --->
//Nand和Nor的驱动
//*:包含此驱动,空就是不支持此驱动
<*> Memory Technology Device (MTD) support --->
//网络设备驱动(有线和无线网卡)
[*] Network device support --->
//键盘,鼠标,触摸屏,游戏手柄,摇杆
Input device support --->
//I2C总线驱动支持
<*> I2C support --->
//SPI总线驱动支持
[*] SPI support --->
//一线式总线驱动支持
<*> Dallas's 1-wire support --->
//看门狗驱动支持
[*] Watchdog Timer Support --->
//摄像头驱动支持
<*> Multimedia support --->
//LCD显示屏驱动支持
Graphics support --->
//声卡驱动
<*> Sound card support --->
//USB相关驱动
[*] USB support --->
//SD卡驱动
<*> MMC/SD/SDIO card support --->
File systems --->
[*] Miscellaneous filesystems --->
//Nand常用yaffs2文件系统,可以读写
<*>YAFFS2 file system support
//Norflash常用jffs2文件系统,可以读写
<*>Journalling Flash File System v2 (JFFS2) support
//Nand和Nor都可以,此文件系统只能读不能写
<*>Compressed ROM file system support (cramfs)
[*] Network File Systems --->
//如果此选项不选,内核通过NFS网络挂接根文件系统就没门
[*]Root file system on NFS
四,根文件系统(rootfs):
特点:
(1).最后要执行的软件
(2).内核去挂接(找)根文件系统rootfs,根据bootloader传递的参数
明确:根文件系统rootfs仅仅是一个代名词而已里面包含了一堆的应用程序(ls),
一堆的动态库(libc.so)和静态库(libc.a),一堆的配置文件(/etc/init.d/tftpd-hpa)而已!
(3)烧写到Nand上