45
Linux 内内内内内内内 内内内内内内内内内内内内 内内内0551 3606864 [email protected] Spring 2009

Linux 内核源代码导读

  • Upload
    natane

  • View
    94

  • Download
    11

Embed Size (px)

DESCRIPTION

Linux 内核源代码导读. 中国科学技术大学计算机系 陈香兰( 0551 - 3606864 ) [email protected] 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

Page 1: Linux 内核源代码导读

Linux 内核源代码导读

中国科学技术大学计算机系陈香兰( 0551 - 3606864 )

[email protected] 2009

Page 2: Linux 内核源代码导读

回顾编译得到的 bzImage 的结构

Setup.bin+vmlinux.bin (具有自解压能力)head_32.o+misc.o+piggy.o

vmlinux

Head-y + init-y + main

Page 3: Linux 内核源代码导读

基于 I386 的 Linux 的启动

Page 4: Linux 内核源代码导读

计算机是如何启动的

BIOS

软盘启动硬盘启动

GrubLilo

启动协议

Page 5: Linux 内核源代码导读

特殊的几个地址

BIOS :第一个扇区 0x07c0

第一个扇区的内容是什么?观察 setup.ld

了解第一个扇区的内容

Page 6: Linux 内核源代码导读
Page 7: Linux 内核源代码导读

关键:

实模式保护模式

分页模式页表

GDT 表

IDT 表

阅读: documentation/i386/boot.txt

Page 8: Linux 内核源代码导读

I386内核从实模式开始运行

首先看一下什么是实模式

实模式是为了兼容早期的 CPU 而设置的 i386 系统总是始于实模式实模式下

地址总线: 20 位 内存范围: 0~1MB 逻辑地址 = 段地址 + 段内偏移

段地址 = 段寄存器中的值 *16 (或左移 4 位)段寄存器长度: 16bit

段寄存器有:cs/ds/es/fs/gs

Page 9: Linux 内核源代码导读

保护模式下,地址总线 32 位,访存范围为 4GB原来的段寄存器现在被称作段选择子,与 GDT 表配合使用GDT 表由 gdtr 指示其位置和长度使用特殊的指令进行操作: sgdt/lgdt

Page 10: Linux 内核源代码导读

图示

descriptordescriptordescriptordescriptordescriptordescriptordescriptor

descriptordescriptordescriptordescriptordescriptordescriptordescriptor

descriptordescriptordescriptordescriptordescriptordescriptor

Interrupt Descriptor Table

Global Descriptor Table

GDTR

IDTR

Page 11: Linux 内核源代码导读

一般装载 gdt 和 idt 之后,要重新装载段寄存器 cs 、 ds 、 es 、 fs 、 gs

cs 通常通过一条长跳转指令装载其他数据段寄存器直接设置

Page 12: Linux 内核源代码导读

控制寄存器( Control Registers)

CR0 CR1 CR2 CR3 CR4 (扩展相关,忽略)

与内存相关

Page 13: Linux 内核源代码导读

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

Page 14: Linux 内核源代码导读

CR1、 CR2、 CR3

CR1 :保留 CR2 :在缺页异常的时候,记录缺页地址

CR3 :记录页目录所在的物理地址和两个标记 (PCD & PWT)

Page 15: Linux 内核源代码导读

阅读 documentation/i386/boot.txt

对于 i386 平台,由于一些历史的原因,因此 Linux 的启动比较复杂

这个文档包含如下内容1 、 Linux/i386 的启动协议( 10 个 + )2 、内存布局图(大内核,小内核)3 、实模式下的内核头结构(即 setup header )以及各参数的解释

4 、内核的命令行( command line )

Page 16: Linux 内核源代码导读

5 、实模式代码的内存布局6 、启动配置示例7 、装载 Linux 的剩余部分8 、特殊的命令行参数9 、运行内核10 、高级启动回调函数

关于其中的一些内容,我们将在合适的时候说明

Page 17: Linux 内核源代码导读

加载 I386内核的内存布局图

zImage/Image 的内核加载器所使用的经典的内存布局( 1M=0x100000 )为

Page 18: Linux 内核源代码导读

Header.S分析

前 512 个字节的内容

Page 19: Linux 内核源代码导读
Page 20: Linux 内核源代码导读

关于msg_loop输出的字符串

??不支持软盘启动???

Page 21: Linux 内核源代码导读

Header.S分析

关于 512 字节的最后( setup header )在 setup.ld 中

在 Head.S 中

Page 22: Linux 内核源代码导读

Header.S分析

第二个扇区开始

接下来仍然是 setup header 参数部分,直到 start_of_setup

Page 23: Linux 内核源代码导读

start_of_setup设置堆栈检查 setup 中的标签清除 BSS 段调用 C 入口 main

start_of_setup

_start(512 处 )

main

Page 24: Linux 内核源代码导读

Main.c分析

main

go_to_protected_mode

=?

重点:

Page 25: Linux 内核源代码导读

关于 go_to_protected_mode

关键move_kernel_around

setup_idt

setup_gdt

protected_mode_jump :参见 pmjump.S

重点:

Page 26: Linux 内核源代码导读

关于 protected_mode_jump

关键进入保护模式

通过设置 cr0

进入 32 位代码通过一条手工设置的代码

最后进入 setup header 中指定的 code32_start

Page 27: Linux 内核源代码导读

关于 boot_params.hdr.code32_start

对 boot_params.hdr 的赋值之处: copy_boot_params

Hdr 的定义之处在 Head.S 中

Page 28: Linux 内核源代码导读

code32_start 在 setup header 的第二部分

此处对应于压缩映像的 head_32.S

Page 29: Linux 内核源代码导读

解压缩头中的 head_32.S

32 位代码

关键 1 :调用 decompress_kernel关键 2 :跳转到 vmlinux 的头 head_32.S

阅读此目录下的 vmlinux_32.lds了解入口处的代码

Page 30: Linux 内核源代码导读

关于解压缩 head_32.S中的 relocated相关

即“ .text” 部分

Clear BSSSetup the stack for the decompressor

Do the decompression, and jump to the new kernel..

Page 31: Linux 内核源代码导读

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

最后

重点

重点

Page 32: Linux 内核源代码导读

关于页表的初始化

观察 default_entry 后的代码,忽略 PAE页目录: swapper_pg_dir :参见 head_32.S

第一个页表: pg0 :参见 vmlinux_32.lds

Page 33: Linux 内核源代码导读
Page 34: Linux 内核源代码导读

swapper_pg_dir

Page 35: Linux 内核源代码导读

pg0

内核代码段

内核数据段

内核 BSS 段

低地址

高地址pg0

swapper_pg_dir

Page 36: Linux 内核源代码导读

初始化页目录和页表

对应线性地址 0

对应线性地址 3GB

1024 项

pg0swapper_pg_dir

B4M4

G3offset =

物理地址空间低端 4M

Page 37: Linux 内核源代码导读

打开分页机制

Page 38: Linux 内核源代码导读

关于 GDT

1 ) boot_gdt2 ) per_cpu__gdt_page

Page 39: Linux 内核源代码导读

关于堆栈 stack_start

Page 40: Linux 内核源代码导读

关于 setup_idt

在 idt 表中,填写 ignore_int

几个特殊的项:

输出如下信息:

输出如下信息:

Page 41: Linux 内核源代码导读

装载 GDT、 IDT,进入 3G地址空间

Page 42: Linux 内核源代码导读

关于 start_kernel

对 Linux 内核的各个部分进行初始化Start_kernel 属于手工初始化的第一个进程( 0号进程),该进程最后执行 cpu_idle ,成为 idle进程

系统创建的第一个进程( 1 号进程)

该进程最后找到一个 init 程序进行 Linux 运行环境的初始化

Page 43: Linux 内核源代码导读
Page 44: 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/中删去即可

Page 45: Linux 内核源代码导读

Thanks !The end.