Linux 内核源代码导读
description
Transcript of Linux 内核源代码导读
![Page 2: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/2.jpg)
回顾编译得到的 bzImage 的结构
Setup.bin+vmlinux.bin (具有自解压能力)head_32.o+misc.o+piggy.o
vmlinux
Head-y + init-y + main
![Page 3: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/3.jpg)
基于 I386 的 Linux 的启动
![Page 4: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/4.jpg)
计算机是如何启动的
BIOS
软盘启动硬盘启动
GrubLilo
启动协议
![Page 5: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/5.jpg)
特殊的几个地址
BIOS :第一个扇区 0x07c0
第一个扇区的内容是什么?观察 setup.ld
了解第一个扇区的内容
![Page 6: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/6.jpg)
![Page 7: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/7.jpg)
关键:
实模式保护模式
分页模式页表
GDT 表
IDT 表
阅读: documentation/i386/boot.txt
![Page 8: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/8.jpg)
I386内核从实模式开始运行
首先看一下什么是实模式
实模式是为了兼容早期的 CPU 而设置的 i386 系统总是始于实模式实模式下
地址总线: 20 位 内存范围: 0~1MB 逻辑地址 = 段地址 + 段内偏移
段地址 = 段寄存器中的值 *16 (或左移 4 位)段寄存器长度: 16bit
段寄存器有:cs/ds/es/fs/gs
![Page 9: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/9.jpg)
保护模式下,地址总线 32 位,访存范围为 4GB原来的段寄存器现在被称作段选择子,与 GDT 表配合使用GDT 表由 gdtr 指示其位置和长度使用特殊的指令进行操作: sgdt/lgdt
![Page 10: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/10.jpg)
图示
descriptordescriptordescriptordescriptordescriptordescriptordescriptor
descriptordescriptordescriptordescriptordescriptordescriptordescriptor
descriptordescriptordescriptordescriptordescriptordescriptor
Interrupt Descriptor Table
Global Descriptor Table
GDTR
IDTR
![Page 11: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/11.jpg)
一般装载 gdt 和 idt 之后,要重新装载段寄存器 cs 、 ds 、 es 、 fs 、 gs
cs 通常通过一条长跳转指令装载其他数据段寄存器直接设置
![Page 12: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/12.jpg)
控制寄存器( Control Registers)
CR0 CR1 CR2 CR3 CR4 (扩展相关,忽略)
与内存相关
![Page 13: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/13.jpg)
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 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/14.jpg)
CR1、 CR2、 CR3
CR1 :保留 CR2 :在缺页异常的时候,记录缺页地址
CR3 :记录页目录所在的物理地址和两个标记 (PCD & PWT)
![Page 15: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/15.jpg)
阅读 documentation/i386/boot.txt
对于 i386 平台,由于一些历史的原因,因此 Linux 的启动比较复杂
这个文档包含如下内容1 、 Linux/i386 的启动协议( 10 个 + )2 、内存布局图(大内核,小内核)3 、实模式下的内核头结构(即 setup header )以及各参数的解释
4 、内核的命令行( command line )
![Page 16: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/16.jpg)
5 、实模式代码的内存布局6 、启动配置示例7 、装载 Linux 的剩余部分8 、特殊的命令行参数9 、运行内核10 、高级启动回调函数
关于其中的一些内容,我们将在合适的时候说明
![Page 17: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/17.jpg)
加载 I386内核的内存布局图
zImage/Image 的内核加载器所使用的经典的内存布局( 1M=0x100000 )为
![Page 18: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/18.jpg)
Header.S分析
前 512 个字节的内容
![Page 19: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/19.jpg)
![Page 20: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/20.jpg)
关于msg_loop输出的字符串
??不支持软盘启动???
![Page 21: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/21.jpg)
Header.S分析
关于 512 字节的最后( setup header )在 setup.ld 中
在 Head.S 中
…
![Page 22: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/22.jpg)
Header.S分析
第二个扇区开始
接下来仍然是 setup header 参数部分,直到 start_of_setup
![Page 23: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/23.jpg)
start_of_setup设置堆栈检查 setup 中的标签清除 BSS 段调用 C 入口 main
start_of_setup
_start(512 处 )
main
![Page 24: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/24.jpg)
Main.c分析
main
go_to_protected_mode
=?
重点:
![Page 25: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/25.jpg)
关于 go_to_protected_mode
关键move_kernel_around
setup_idt
setup_gdt
protected_mode_jump :参见 pmjump.S
重点:
![Page 26: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/26.jpg)
关于 protected_mode_jump
关键进入保护模式
通过设置 cr0
进入 32 位代码通过一条手工设置的代码
最后进入 setup header 中指定的 code32_start
![Page 27: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/27.jpg)
关于 boot_params.hdr.code32_start
对 boot_params.hdr 的赋值之处: copy_boot_params
Hdr 的定义之处在 Head.S 中
…
![Page 28: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/28.jpg)
code32_start 在 setup header 的第二部分
此处对应于压缩映像的 head_32.S
![Page 29: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/29.jpg)
解压缩头中的 head_32.S
32 位代码
关键 1 :调用 decompress_kernel关键 2 :跳转到 vmlinux 的头 head_32.S
阅读此目录下的 vmlinux_32.lds了解入口处的代码
![Page 30: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/30.jpg)
关于解压缩 head_32.S中的 relocated相关
即“ .text” 部分
Clear BSSSetup the stack for the decompressor
Do the decompression, and jump to the new kernel..
![Page 31: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/31.jpg)
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 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/32.jpg)
关于页表的初始化
观察 default_entry 后的代码,忽略 PAE页目录: swapper_pg_dir :参见 head_32.S
第一个页表: pg0 :参见 vmlinux_32.lds
![Page 33: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/33.jpg)
![Page 34: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/34.jpg)
swapper_pg_dir
![Page 35: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/35.jpg)
pg0
内核代码段
内核数据段
内核 BSS 段
低地址
高地址pg0
swapper_pg_dir
![Page 36: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/36.jpg)
初始化页目录和页表
对应线性地址 0
对应线性地址 3GB
1024 项
pg0swapper_pg_dir
B4M4
G3offset =
物理地址空间低端 4M
…
…
![Page 37: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/37.jpg)
打开分页机制
![Page 38: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/38.jpg)
关于 GDT
1 ) boot_gdt2 ) per_cpu__gdt_page
![Page 39: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/39.jpg)
关于堆栈 stack_start
![Page 40: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/40.jpg)
关于 setup_idt
在 idt 表中,填写 ignore_int
几个特殊的项:
输出如下信息:
输出如下信息:
![Page 41: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/41.jpg)
装载 GDT、 IDT,进入 3G地址空间
![Page 42: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/42.jpg)
关于 start_kernel
对 Linux 内核的各个部分进行初始化Start_kernel 属于手工初始化的第一个进程( 0号进程),该进程最后执行 cpu_idle ,成为 idle进程
系统创建的第一个进程( 1 号进程)
该进程最后找到一个 init 程序进行 Linux 运行环境的初始化
![Page 43: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/43.jpg)
![Page 44: Linux 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/44.jpg)
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 内核源代码导读](https://reader035.fdocuments.net/reader035/viewer/2022081418/56814541550346895db20ad2/html5/thumbnails/45.jpg)
Thanks !The end.