450 likes | 643 Views
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 启动协议. 特殊的几个地址
E N D
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
计算机是如何启动的 • BIOS • 软盘启动 • 硬盘启动 • Grub • Lilo • 启动协议
特殊的几个地址 • BIOS:第一个扇区0x07c0 • 第一个扇区的内容是什么? • 观察setup.ld • 了解第一个扇区的内容
关键: 阅读: documentation/i386/boot.txt • 实模式 • 保护模式 • 分页模式 • 页表 • GDT表 • IDT表
I386内核从实模式开始运行 首先看一下什么是实模式 • 实模式是为了兼容早期的CPU而设置的 • i386系统总是始于实模式 • 实模式下 • 地址总线:20位 • 内存范围:0~1MB • 逻辑地址 = 段地址 + 段内偏移 • 段地址 = 段寄存器中的值*16 (或左移4位) • 段寄存器长度:16bit • 段寄存器有: • cs/ds/es/fs/gs
保护模式下, • 地址总线32位,访存范围为4GB • 原来的段寄存器现在被称作段选择子,与GDT表配合使用 • GDT表由gdtr指示其位置和长度 • 使用特殊的指令进行操作:sgdt/lgdt
Interrupt Descriptor Table descriptor Global Descriptor Table descriptor descriptor descriptor descriptor descriptor descriptor descriptor descriptor descriptor descriptor descriptor descriptor descriptor descriptor descriptor GDTR descriptor descriptor descriptor descriptor 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(512处) start_of_setup 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 BSS • Setup 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 startup_32 最后 i386_start_kernel 重点 start_kernel 重点
关于页表的初始化 • 观察default_entry后的代码,忽略PAE • 页目录:swapper_pg_dir:参见head_32.S • 第一个页表:pg0:参见vmlinux_32.lds
pg0 pg0 高地址 内核BSS段 swapper_pg_dir 内核数据段 内核代码段 低地址
初始化页目录和页表 物理地址 空间低端4M … 对应线性地址3GB 1024项 … 对应线性地址0 swapper_pg_dir pg0
关于GDT • 1)boot_gdt • 2)per_cpu__gdt_page
关于setup_idt • 在idt表中,填写ignore_int • 几个特殊的项: 输出如下信息: 输出如下信息:
关于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.