|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
9 G# w& \$ L: h5 p8 c5 p
+ G+ E: S* R, Y4 }. suclinux表示micro-control linux.即“微控制器领域中的Linux系统”,是Lineo公司的主打产品,同时也是开放源码的嵌入式Linux的典范之作。uCLinux主要是针对目标处理器没有存储管理单元MMU(Memory Management Unit)的嵌入式系统而设计的。它已经被成功地移植到了很多平台上。由于没有MMU,其多任务的实现需要一定技巧。
$ [7 s; _& w+ z8 d% K; s+ `5 \/ m! d- k1 |1 g3 `
& X+ Y& X0 [% KuClinux启动过程: C5 g2 D+ }' U1 \
# `" L2 z; ~6 [1 g. N5 W
uCinux的启动主要经历三个阶段。首先,必须完成CPU和存储器的硬件初始化,在系统RAM中建立程序堆栈和数据段,建立程序的运行时的环境。初始化完成之后,uClinux内核就取得了CPU的控制权,开始操作系统自身的初始化,这包括建立RAM中断矢量表、加载设备驱动程序、内存管理模块等等。这一切完成后,uClinux启动一个最初的init线程,进入到第三阶段,这时内核已经正常运行,外围模块也都就绪,开始执行一些脚本文件(如/etc/rc脚本文件)。
/ |3 e% {. F( v3 G0 x7 [" B5 `; ]
! }3 ?. J, b, B5 |, a一.kernel代码段之前的系统初始化
4 x. Z2 ]) ]1 W6 m6 t
5 m" n# u4 F R( w# X' e6 v# |: G8 W6 U1. uClinux-dist/linux-2.4.x/arch/ARMnommu/boot/compressed/head.S) I: H, t" ^6 t" P- h( T5 g6 C+ m
9 @2 r% Y' a- R+ i6 C
开发板从上电开始,最开始执行的程序放在uClinux-dist/linux-2.4.x/arch/armnommu/boot/compressed/head.S中。* P2 z) c( [) q1 }' t8 Q; D
( B1 w) j' b& G3 i
(1) 切换模式,关闭中断。 (line 96 )2 c: \/ p; ?! Y7 B. v/ T4 H A
9 T$ P; e" w; j G( i9 H/ l
(2) 首先程序要先给SYSCFG,EXTDBWTH,ROMCON0等一系列系统控制寄存器赋值,此时flash地址在 0X0,DRAM地址在0X1000000.(line 141 )
# q9 W- d- m& ?6 s+ B3 d/ g; N- d
) F4 s5 U |* {$ G(3) 点亮I/O口的指示灯。 (line 152 )
) I: |, N; E) T1 C+ F" \1 @6 g1 Z2 s
(4) 把在flash上的image复制到DRAM上。(line 161 )/ `5 r$ ~8 ^$ O
3 B# [; B1 y; Y1 X) t3 H
(5) 执行remap,把flash地址映射为0X1000000,DRAM地址映射为0.(line 172 )
' y$ v& A- ^' B, v+ v; W
* ~2 I5 Z: b+ |! A0 f+ n5 V(6) 打开cache和write buffer.(line 196 ); q. E6 t5 R( Q( @$ [+ C
" Q6 n/ C6 @- |# y(7) 设置好64K堆栈。(line 204 )
4 n0 k K7 s8 e
: E3 L' G6 {" d |/ s(8) 跳转到decompress_kernel函数(line 217 ),此处的跳转为带返回的跳转,以便于执行完此函数跳转回来。
; K+ j5 {3 ]( b$ a( Q# N0 A5 I+ s0 S3 q# c* ?& H7 Q- K
2. uClinux-dist/linux-2.4.x/arch/armnommu/boot/compressed/misc.c
1 w* H# ~- p6 G9 ?5 [% _- _$ f( [' f# A* p
此时的函数decompress_kernel是用C语言写的,line 297 。
. \% H& R, Z1 X7 X5 L# @/ I$ |1 b; B6 ]4 R, }, ^
(1) makecrc();进行crc校验。: H) d% R6 y) O( {
) b- F1 J8 j, ^/ m7 F" [
(2) puts(“Uncompressing Linux.。.”); 输出linux起动后的第一句话。
/ b/ [5 ?- K9 O7 I" J5 X9 ?4 }, O) Q+ E: }- B$ r! G3 }% W$ Q; j3 C
(3) gunzip();解压缩kernel.
: ?. B% V2 |! Q: p0 \9 r) ~
5 D. {% Q! L! X3 y# Q(4) puts(“ done, booTIng the kernel./n”);& M! p+ d. `* h6 v: u
6 o0 y# Q. i( ~4 s/ ?. S
3. uClinux-dist/linux-2.4.x/arch/armnommu/boot/compressed/head.S
0 ?, f. C; A) P. W0 F5 \$ Y) |$ P- g* ^
执行完decompress_kernel函数后,kernel又跳转回head.S中,因为此时我们还要检验解压缩之后的kernel起始地址是否紧接着kernel image,如果是,beq call_kernel(line 220),执行解压后的kernel.3 _0 w: J; M8 v4 ?: `1 n' y
5 Z7 V, a v# ?- J6 O, w
如果解压缩之后的kernel起始地址不是紧接着kernel image,执行relocate(line 236),将其拷贝到紧接着kernel image的地方,然后跳转,执行解压后的kernel.7 L8 [* |( d( c' a) }
2 w# w5 J) j: A; b& j- _
二.kernel执行
: U9 c& Z# W) w" e H1 C1 i: o8 W: j4 w/ v8 p6 \
1.uClinux-dist/linux-2.4.x/init/main.c中的start_kernel() (line 352)1 Q3 T! b# H2 J) ?2 t
7 [! i$ C: O/ _, P& ~4 ]+ S9 r系统启动过程到此,转入体系结构无关的通用C代码中,start_kernel() 中调用了一系列初始化函数,以完成kernel本身的设置。这些动作有的是公共的,有的则是需要配置的才会执行的。# I! j* S) }8 ? C, Z
0 B& N7 R$ \5 F0 P. ~(1) 输出Linux版本信息(printk(linux_banner)): R M. J6 u, e7 }/ l$ ~% H
2 P+ |5 I4 J, i: Q* h H
(2) 设置与体系结构相关的环境(setup_arch())
5 i6 k) O0 f3 j5 t5 Y1 L: x( r O2 w4 Z& t* F( e+ O: g; V
(3) parse_opTIons(command_line);解析command_line,将其转化为环境变量。 d' Q% A3 Y' U! [, y
) c7 k6 l+ ? S5 Y
(4) 初始化系统IRQ(init_IRQ())
* @ l- c) \; ^) r% k- W; {+ E+ F% D: U
(5) 核心进程调度器初始化(sched_init())
5 l* \6 e# D' A4 g! U! `* f
5 R7 r7 X/ I4 g5 b8 ~2 J4 K(6) 软中段初始化sofTIrq_init();) Z8 L+ P6 u, i p+ h* i, |$ [/ _
+ V6 Q* R& Q" I( k/ M
(7) 时间、定时器初始化(包括估测主频、初始化定时器中断等,TIme_init())
/ L0 @0 C9 z* P$ _# ~# C4 i4 ?: n0 ]. T8 R: {- i
(8) 控制台初始化console_init();: M1 s/ y" {6 E- P5 J
" e& V; |5 g# `* W(9) 核心CACHE初始化kmem_cache_init();
3 E& M$ d, N+ j1 a* P8 i, O/ ]3 D# Z
(10)延迟校准calibrate_delay();' i) \. n" \* u `1 ^* K
; f2 v7 n1 U D( B: D+ a$ `6 L' z(11)内存初始化(设置内存上下界和页表项初始值,mem_init())& X( E4 m l% T# N3 x0 D, j ]
4 s( z8 O9 ^: i2 {" Q; X
(12)文件,目录,块设备读写缓冲区初始化1 s/ _, m4 @# u" F5 n; h
7 D/ J* M6 \$ S8 N, \
(13)检查体系结构漏洞(check_bugs())7 J2 s" I6 K. ~) A+ R4 C
1 |1 C$ u/ I9 u(14)启动init过程(创建第一个核心线程,调用init()函数,原执行序列调用cpu_idle() 等待调度,init())
" o5 I' ^6 M) g( O) K3 {$ }
$ i0 j0 G: X+ Y. X! e( `9 G8 R至此start_kernel()结束,基本的核心环境已经建立起来了。
( k/ z: ]" s# L1 j: N0 R [8 M s8 I; ~3 Q
2.uClinux-dist/linux-2.4.x/init/main.c中的init() (line 548)
2 _6 g9 }1 @' t! r8 s* C# @& D: C: E
: O) \. D2 F: p' x( q, a现在我们进入内核引导第二部分,init()函数作为核心线程,首先锁定内核(仅对SMP机器有效,我们为空函数),然后调用 do_basic_setup() (line 551)完成外设及其驱动程序的加载初始化。
: ~% r e; I- H2 F. R q% b S* M4 v' _
过程如下:. E+ h/ }5 Q. A" B) d' ?4 P
2 C& G# q& ^% |* a* 网络初始化(初始化网络数据结构,包括sk_init()、skb_init()和proto_init()三部分,在proto_init()中,将调用protocols结构中包含的所有协议的初始化过程,sock_init())) F# {4 ^- w6 H1 x0 _
8 g$ s& ~! `$ h8 I
* 创建事件管理核心线程(start_context_thread()函数,这是系统创建的第二个内核线程,名叫“keventd”。其代码context_thread()也在kernel/context.c中,)( y: C- T: f- M' w( k
) h4 G: I+ D$ B8 u9 \启动任何使用__initcall标识的函数(方便核心开发者添加启动函数,此时由do_initcalls()函数启动)。
1 i: g2 x* ^# q+ |9 |3 s' d! C+ W& S
此时系统开始加载外部设备的初始化程序,如:在linux-2.4.x/driver/block/genhd.c中的device_init()函数,在genhd.c中由__initcall(device_init)标识在此时调用,device_init()函数是所有外部设备初始化的总入口,包括了块设备的初始化blk_dev_init,网络设备的初始化net_dev_init()和atmdev_init()等。
' V) X+ x/ \0 s" d" n4 D; S& p% i& O7 o
至此do_basic_setup()函数返回init(),在释放启动内存段(free_initmem())并给内核解锁以后,init()打开/dev/console设备,重定向stdin、stdout和stderr到控制台,最后,搜索文件系统中的init程序(或者由init=命令行参数指定的程序),并使用 execve()系统调用加载执行init程序。(line 576) 。
- O" D2 j$ b4 d- l1 `
/ P& I# Q' i: [) uinit()函数到此结束,内核的引导部分也到此结束了,
' }( J( Y! H2 |
3 P% Z0 u& Z' x6 y- U& f& S3. uClinux-dist/linux-2.4.x/init/main.c中的execve(“/etc/init”,argv_init,envp_init); (line 579)
( A" e) g- S l' E8 x1 R+ f1 g6 j; N% V* o6 A* ^
init进程是系统所有进程的起点,内核在完成核内引导以后,即在本线程(进程)空间内加载init程序,它的进程号是1。3 |2 `- I7 W. e
4 R. i1 ?" ~, F5 a) {init程序需要读取/vendors/SAMSUNG/4510B/inittab文件作为其行为指针,然后执行。1 a! Y! y0 u2 z9 B% R
' B8 L+ R4 t$ q' I5 j$ F9 d
. w& x( ]! E: d5 V& ~
|
|