linux 内核源代码导读

Post on 12-Jan-2016

95 Views

Category:

Documents

11 Downloads

Preview:

Click to see full reader

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

TRANSCRIPT

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.

top related