View
95
Download
11
Category
Preview:
DESCRIPTION
Linux 内核源代码导读. 中国科学技术大学计算机系 陈香兰( 0551 - 3606864 ) xlanchen@ustc.edu.cn Spring 2009. 回顾编译得到的 bzImage 的结构 Setup.bin+vmlinux.bin (具有自解压能力). head_32.o+misc.o+piggy.o. vmlinux. Head-y + init-y + main. 基于 I386 的 Linux 的启动. 计算机是如何启动的. BIOS 软盘启动 硬盘启动 Grub Lilo 启动协议. 特殊的几个地址 - PowerPoint PPT Presentation
Citation preview
Linux 内核源代码导读
中国科学技术大学计算机系陈香兰( 0551 - 3606864 )
xlanchen@ustc.edu.cnSpring 2009
回顾编译得到的 bzImage 的结构
Setup.bin+vmlinux.bin (具有自解压能力)head_32.o+misc.o+piggy.o
vmlinux
Head-y + init-y + main
基于 I386 的 Linux 的启动
计算机是如何启动的
BIOS
软盘启动硬盘启动
GrubLilo
启动协议
特殊的几个地址
BIOS :第一个扇区 0x07c0
第一个扇区的内容是什么?观察 setup.ld
了解第一个扇区的内容
关键:
实模式保护模式
分页模式页表
GDT 表
IDT 表
阅读: documentation/i386/boot.txt
I386内核从实模式开始运行
首先看一下什么是实模式
实模式是为了兼容早期的 CPU 而设置的 i386 系统总是始于实模式实模式下
地址总线: 20 位 内存范围: 0~1MB 逻辑地址 = 段地址 + 段内偏移
段地址 = 段寄存器中的值 *16 (或左移 4 位)段寄存器长度: 16bit
段寄存器有:cs/ds/es/fs/gs
保护模式下,地址总线 32 位,访存范围为 4GB原来的段寄存器现在被称作段选择子,与 GDT 表配合使用GDT 表由 gdtr 指示其位置和长度使用特殊的指令进行操作: sgdt/lgdt
图示
descriptordescriptordescriptordescriptordescriptordescriptordescriptor
descriptordescriptordescriptordescriptordescriptordescriptordescriptor
descriptordescriptordescriptordescriptordescriptordescriptor
Interrupt Descriptor Table
Global Descriptor Table
GDTR
IDTR
一般装载 gdt 和 idt 之后,要重新装载段寄存器 cs 、 ds 、 es 、 fs 、 gs
cs 通常通过一条长跳转指令装载其他数据段寄存器直接设置
控制寄存器( Control Registers)
CR0 CR1 CR2 CR3 CR4 (扩展相关,忽略)
与内存相关
CR0
CR0, MSW register (Machine Status Word, 32-bit version) 包含系统控制位,用于控制操作模式和状态
Instruction: lmsw LINUX’ setup.S:
movw $1, %ax lmsw %ax jmp flush_instr // why? flush_instr:
To turn on the PE-bit (enables protected-mode),
PE-bit (Protection Enabled)0 CPU is in real-mode, 1 CPU is in protected-mode
CR1、 CR2、 CR3
CR1 :保留 CR2 :在缺页异常的时候,记录缺页地址
CR3 :记录页目录所在的物理地址和两个标记 (PCD & PWT)
阅读 documentation/i386/boot.txt
对于 i386 平台,由于一些历史的原因,因此 Linux 的启动比较复杂
这个文档包含如下内容1 、 Linux/i386 的启动协议( 10 个 + )2 、内存布局图(大内核,小内核)3 、实模式下的内核头结构(即 setup header )以及各参数的解释
4 、内核的命令行( command line )
5 、实模式代码的内存布局6 、启动配置示例7 、装载 Linux 的剩余部分8 、特殊的命令行参数9 、运行内核10 、高级启动回调函数
关于其中的一些内容,我们将在合适的时候说明
加载 I386内核的内存布局图
zImage/Image 的内核加载器所使用的经典的内存布局( 1M=0x100000 )为
Header.S分析
前 512 个字节的内容
关于msg_loop输出的字符串
??不支持软盘启动???
Header.S分析
关于 512 字节的最后( setup header )在 setup.ld 中
在 Head.S 中
…
Header.S分析
第二个扇区开始
接下来仍然是 setup header 参数部分,直到 start_of_setup
start_of_setup设置堆栈检查 setup 中的标签清除 BSS 段调用 C 入口 main
start_of_setup
_start(512 处 )
main
Main.c分析
main
go_to_protected_mode
=?
重点:
关于 go_to_protected_mode
关键move_kernel_around
setup_idt
setup_gdt
protected_mode_jump :参见 pmjump.S
重点:
关于 protected_mode_jump
关键进入保护模式
通过设置 cr0
进入 32 位代码通过一条手工设置的代码
最后进入 setup header 中指定的 code32_start
关于 boot_params.hdr.code32_start
对 boot_params.hdr 的赋值之处: copy_boot_params
Hdr 的定义之处在 Head.S 中
…
code32_start 在 setup header 的第二部分
此处对应于压缩映像的 head_32.S
解压缩头中的 head_32.S
32 位代码
关键 1 :调用 decompress_kernel关键 2 :跳转到 vmlinux 的头 head_32.S
阅读此目录下的 vmlinux_32.lds了解入口处的代码
关于解压缩 head_32.S中的 relocated相关
即“ .text” 部分
Clear BSSSetup the stack for the decompressor
Do the decompression, and jump to the new kernel..
Vmlinux中的 head
观察 vmlinux.lds 观察“ .text.head” 、“ .text” 、“ .data” 等
.text.head Set segments to known values. Clear BSS first so that there are no surprises... Copy bootup parameters out of the way. Initialize page tables. Enable paging Set up the stack pointer Initialize eflags. call setup_idt check if it is 486 or 386. call check_x87 装载 GDT 、 IDT ,进入 3G 地址空间 jmp i386_start_kernel
i386_start_kernel
start_kernel
startup_32
最后
重点
重点
关于页表的初始化
观察 default_entry 后的代码,忽略 PAE页目录: swapper_pg_dir :参见 head_32.S
第一个页表: pg0 :参见 vmlinux_32.lds
swapper_pg_dir
pg0
内核代码段
内核数据段
内核 BSS 段
低地址
高地址pg0
swapper_pg_dir
初始化页目录和页表
对应线性地址 0
对应线性地址 3GB
1024 项
pg0swapper_pg_dir
B4M4
G3offset =
物理地址空间低端 4M
…
…
打开分页机制
关于 GDT
1 ) boot_gdt2 ) per_cpu__gdt_page
关于堆栈 stack_start
关于 setup_idt
在 idt 表中,填写 ignore_int
几个特殊的项:
输出如下信息:
输出如下信息:
装载 GDT、 IDT,进入 3G地址空间
关于 start_kernel
对 Linux 内核的各个部分进行初始化Start_kernel 属于手工初始化的第一个进程( 0号进程),该进程最后执行 cpu_idle ,成为 idle进程
系统创建的第一个进程( 1 号进程)
该进程最后找到一个 init 程序进行 Linux 运行环境的初始化
upstart管理的 ubuntu启动过程:
1, 内核启动 init
2,init找到 /etc/event.d/rc-default 文件,确定默认运行级别 (X)
3,触发相应的 runlevel事件,开始运行 /etc/event.d/rcX
4,rcX 运行 /etc/init.d/rc ,传入参数 X
5,/etc/init.d/rc脚本进行一系列设置,最后运行相应的 /etc/rcX.d/ 中的脚本
6,/etc/rcX.d/ 中的脚本按事先设定的优先级依次启动,直至最后给出登录画面 ( 启动 X服务器和 GDM)
若不想启动某程序,只要把相应的符号链接从 /etc/rc2.d/中删去即可
Thanks !The end.
Recommended