探本溯源深入领略Linux31内核绝美风光之系统启动篇一Word文档下载推荐.docx
- 文档编号:19979206
- 上传时间:2023-01-13
- 格式:DOCX
- 页数:10
- 大小:26.67KB
探本溯源深入领略Linux31内核绝美风光之系统启动篇一Word文档下载推荐.docx
《探本溯源深入领略Linux31内核绝美风光之系统启动篇一Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《探本溯源深入领略Linux31内核绝美风光之系统启动篇一Word文档下载推荐.docx(10页珍藏版)》请在冰豆网上搜索。
"
Set
version
information
on
all
module
symbols"
3.
depends
MODULES
4.
help
5.
Usually,
modules
have
to
be
recompiled
whenever
you
switch
a
new
6.
kernel.
...
这是一个简单的配置菜单条目,不过这也说明了所有的配置条目所应有的特征:
config"
引出一个新的配置选项,接下来的行定义了一系列的属性,这些属性的类型可以是配置选项,输入提示,依赖关系,帮助文档或是默认值,需要注意的是同一个配置菜单条目可以被定义多次,但每一次定义都仅有一个输入提示,并且类型定义不能冲突。
绝大多数的条目都定义了一个配置选项,而其他的条目为这些条目充当依赖关系。
鉴于Kconfig文件重要性,这里列出一些其常用语法:
——类型定义:
bool"
/"
tristate"
string"
hex"
int"
,表示用户配置该选项时的输入类型。
这里有两种最基本的类型:
tristate和string类型,其他类型则基于这两者。
类型定义允许给出输入提示,比如:
7.bool
Networking
support"
等价于
8.bool
9.prompt
两者都提示该配置菜单的输入类型为bool型,输入之前将有一条提示为“Networkingsupport”。
——默认值:
"
default"
<
expr>
["
if"
]
一个配置选项可以有任意数量的默认值。
如果多个默认值是可见的,那么当且仅当第一个定义的默认值处于活动状态,即如果用户不配置该选项那么该默认值将被选择。
默认值将不受所在菜单项的限制,这意味着默认值可以被定义在任何其他地方或被先前的定义所覆盖。
可以通过添加"
条件语句增加指定条件下存在的默认值。
——类型定义+默认值:
def_bool"
def_tristate"
这是类型定义和默认值的简写方式。
同样可以通过增加"
语句添加指定条件下的类型及默认值。
——依赖:
dependson"
这为本菜单项定义了一个依赖条件,如果有多个依赖,那么它们可以通过符号"
&
连接。
依赖应用于本菜单项从其定义处开始的所有其他属性。
例如:
10.bool
foo"
if
BAR
11.default
y
12.depends
13.bool
14.default
——帮助文档:
help"
或者"
---help---"
该属性定义了一个帮助文档,帮助文档的末尾取决于其缩进层次,换言之,比帮助文档开始的第一行有更小缩进的一行指示整个帮助文档结束。
与"
在作用上并无差别,只不过"
帮助开发者将配置选项从整个菜单项中清晰的分离出来。
以上是对Kbuild/Makefile/Kconfig的简要介绍,其实理解这三者的关键在于:
因为构建的软件是通过对源文件编译得到的,而Linux内核支持多种CPU架构,这使得整个内核包含成千上万个每个架构下各自所需的源文件,然而在形成内核映像的过程中只需要从源文件中抽取出一部分针对某一种具体体系结构的源文件,以及所有体系结构下都需要的源文件(例如内存管理/进程调度/网络等)进行编译即可,KbuildMakefile中的一部分实现的正是这样的功能,然而事实是Makefile中构建每个目标所需的源文件之间的相互依赖关系错综复杂,并且上层的Makefile还需要调用下层的Makefile,这还有可能导致目标覆盖,期间还可能需要scripts目录中的脚本文件辅助编译,还有Makefile中一大堆预定义的对于初学者来说格式奇怪的环境变量/自动化变量以及内建函数等等,最后链接顺序对于某些模块来讲具有严格的递进关系,否则还有可能导致硬件的损坏,可以看到,仅仅内核映像所需源文件的剖析过程就将耗费大量的精力,所以这一部分工作建议通过搜索引擎完成。
而与我们的源文件有关的Kconfig则应该适当了解,否则正如上文所讲,将对代码的具体剖析过程造成极大的困扰。
以Page_32_types.h头文件中定义的宏__PAGE_OFFSET中使用到的变量CONFIG_PAGE_OFFSET为例,遇到这类配置类型的变量首先在对应体系结构下的目录中找到Kconfig文件,注意我们的讲解总是针对x86_32架构的,因此该Kconfig文件在arch\x86中,之后的剖析过程中对配置类型的变量所在的目录将不在赘述,有关CONFIG_PAGE_OFFSET的菜单项如下所示:
PAGE_OFFSET
hex
default
0xB0000000
VMSPLIT_3G_OPT
0x80000000
VMSPLIT_2G
0x78000000
VMSPLIT_2G_OPT
0x40000000
VMSPLIT_1G
7.
0xC0000000
8.
X86_32
可以看到该菜单项是一个类型为hex,即以16进制格式显示的整型且不可视(non-visible)变量(因为没有任何输入提示,这表现为hex之后没有带双引号的字符串,也没有prompt输入提示符),如果预定义了表达式VMSPLIT_3G_OPT那么该值则为0xB0000000,后接三个默认值均为在指定条件下PAGE_OFFSET可取的值,最后一个没有任何条件的默认值为0xC0000000,即当我们使用默认配置时PAGE_OFFSET的取值即为0xC0000000,最后看到dependsonX86_32语句,说明该菜单项对X86_32配置选项具有依赖关系,但我们看到其后并未定义其他属性,因此直接忽略该依赖即可。
源文件中的PAGE_OFFSET的含义是内核在单个进程的线性地址空间中的偏移量,这表明了在默认情况下,Linux内核将占用x86架构下0~4G虚拟内存中最高1G的线性地址空间,同时也表明了在任意进程的页目录项中从0x300(即768)开始的项均对应的是内核地址空间。
以上是有关Kbuild/Makefile/Kconfig的简要介绍,想要更加详细的了解这一部分的内容可参考之前所列的资料。
可以看到与我们的源代码剖析联系最紧密的就是我们的Kconfig配置文件,因此能够大致理解Kconfig文件是阅读内核源码的最基本要求,另外在x86\configs目录下的i386_defconfig文件中也有对一部分CONFIG__XXXX标识的默认配置。
以上介绍的内容仅仅只是我们内核之旅开始的前奏,接下来就将正式进入我们的内核世界中。
加电启动
首先需要知道,内存(DRAM)是一类易失性存储设备,从微观上表现为,泄漏电流的各种因素会导致DRAM单元在10~100毫秒时间内失去电荷,因此存储系统必须周期性地通过读出然后写回来刷新存储器的每个位,这与SRAM不同,只要有电,SRAM中存储的信息就不需要刷新,SRAM比之DRAM的优点还有存取速度快,对光和电的噪音干扰不敏感,所有这些优点的代价是SRAM单元比DRAM单元使用更多的晶体管,更贵且功耗更大;
从宏观上来讲,其易失性则表现为一旦断电,那么内存中所存储的信息都将丢失。
因此我们的内核必须存储在一类永久性的介质中,即使断电也照样可以保存信息,在PC上这样的介质就是我们的硬盘,它是通过将信息转换为电信号,再将电信号转换为磁场去磁化材料,这样就将信息永久地保存下来,当然还可以是其他非易失性存储设备如U盘。
然而我们的程序在执行之前都必须先被载入内存,因为在CPU上执行的指令都是在内存中进行寻址的,继而这就要求存在某种外力,在开机启动时能够将内核“放入”内存并执行相应的初始化工作,其后将控制权转移给内核,向上对用户提供贴心的服务,向下则可以有效的管理硬件资源使得整个精彩纷呈的机器世界有效运转,而这种“外力”就是我们的基本输入/输出系统(BasicInput/OutputSystem,BIOS),之所以称其为BIOS是因为其包括几个中断驱动的低级过程。
所有操作系统在启动时都需要借助这些过程对计算机硬件设备进行初始化。
因为BIOS过程只能在实模式下运行,所以Linux一旦进入保护模式就不再使用BIOS,而是为计算机上的每个硬件设备提供各自的设备驱动程序。
在开始启动时,有一个特殊的硬件电路在CPU的一个引脚上产生一个RESET逻辑值,CPU在识别出RESET信号后将数据总线设为高阻抗状态,地址线强行设为1,并禁用中断。
在这之后就将处理器的一些寄存器设成固定的值,其中最重要的两个寄存器——CS段寄存器被置为0xf000,EIP指令指针寄存器为0x0000fff0,因为此时CR0寄存器中的PE位还未被置位,因此处于实模式状态下,对指令的地址计算是通过在16位的CS段寄存器内容最右边补齐一个0从而形成一个小段,再加上IP指令指针寄存器中的内容(EIP寄存器的低16位),用一个形象的公式即表示为:
CS*16+IP,最终得到地址值为0xffff0。
注意这个地址寻址的第一条指令存放在BIOS中,而又因为BIOS被固化在ROM中,并且对ROM中的指令进行寻址则需要使用32位地址(原因请参考PC存储器一文),联系到之前所介绍的,CPU将地址线强行设为1,最终形成的物理地址即为0xfffffff0。
形成该地址之后,CPU将其交给MCH(南桥芯片-相当于一个分配器,根据不同的地址映射分配给不同的设备去处理),MCH会决定这个地址要分配给ICH(北桥芯片-解码器),最终这个地址被解析到BIOS的ROM里面的地址。
而BIOS实际上大致执行以下4个操作:
1.对硬件执行一系列测试,用来检测现在有哪些设备以及这些设备是否正常运转,这个阶段被称为POST(Power-On-Self-Test,加电自检)。
2.初始化硬件设备,这个阶段保证所有的硬件设备操作不会引起IRQ(中断请求)线与I/O端口的冲突,最后显示系统中安装的所有PCI设备的一个列表。
3.搜索一个操作系统来启动。
这个过程可以根据用户设置的顺序依次进行访问系统中的软盘、硬盘以及CD-ROM的第一个扇区(即引导扇区),通常是在开机时按del键进入BIOS的设置界面,但也可能是其他键,视各个具体的PC而定。
4.按上述访问次序找到一个有效设备后,即将第一个扇区的内容拷贝到RAM中物理地址0x00007c00处,随后跳转到该地址开始执行刚刚加载的代码。
有关BIOS启动过程更详细的介绍可以参考BIOS启动过程——硬件检测及初始化浅析一文。
想要学习BIOS编程的可以参考这里。
引导装入过程(bootloader)
引导装入程序是由BIOS装载,且用来把操作系统的内核映像装载到RAM中所执行的一个程序,该可执行过程存放在硬盘的第0个磁道第0个扇区上,由于总共只需占用较少(512字节)的存储空间,故也可称之为引导记录,除此之外该引导记录还包括64字节的分区表以及2个字节标识有效引导记录结尾的标签(0x550xAA)。
但从Linux2.6开始不再执行这样的引导装入程序,这一点可以通过从header.S文件剖析得知(有关Linux下GAS格式的汇编语法可参考GAS学习系列):
1.BOOTSEG
=
0x07C0
/*
original
address
of
boot-sector
*/
2.SYSSEG
0x1000
historical
load
>
4
.code16
/*表示16位模式*/
.section
.bstext"
ax"
/*将以下代码放在.bstext节区,"
表示该节区可分配并且可执行*/
.global
bootsect_start
/*定义全局标号bootsect_start*/
8.bootsect_start:
9.
10.
#
Normalize
the
start
11.
ljmp
$BOOTSEG,
$start2
12.
13.start2:
14.
movw
%cs,
%ax
15.
%ax,
%ds
16.
%es
17.
%ss
18.
xorw
%sp,
%sp
19.
sti
在全局标号bootsect_start后执行一个长跳转指令ljmp$BOOTSEG,$start2,因为BOOTSEG的值为0x07C0,并且当前仍处于实模式下,因此该指令表示转移到段基址为0x7C00(seg*16),偏移为start2地址处,联系到开机启动时第一个扇区的内容被BIOS拷贝到物理地址0x00007C00处,所以该长跳转指令实际表示转移到本段start2标号处继续执行,可以看到在start2标号后的指令将cs段寄存器中的内容依次填充至ds/es/ss寄存器中,接着将sp堆栈指针寄存器内容清零并启用中断。
1.
cld
/*清除方向标志,使用在串传送指令中(在本例中为lodsb),表示在完成传送后将di寄存器自动增1*/
$bugger_off_msg,
%si
/*将bugger_off_msg的首地址移入si寄存器*/
5.msg_loop:
lodsb
/*将内存地址ds:
si存放的内容读入al寄存器中*/
andb
%al,
%al
/*测试al寄存器中的内容是否为0*/
jz
bs_die
/*若是则跳转到标号bs_die处继续执行*/
movb
$0xe,
%ah
$7,
%bx
int
$0x10
/*调用BIOS0x10号中断,该中断的作用是显示字符*/
jmp
msg_loop
可以发现上述代码段的作用即是调用BIOS中断例程在屏幕上显示标识为bugger_off_msg的字符串内容,有关BIOS中断内容可以参考这里。
bugger_off_msg的定义如下:
1.bugger_off_msg:
.ascii
Direct
booting
from
floppy
is
no
longer
supported.\r\n"
Please
use
boot
loader
program
instead.\r\n"
\n"
Remove
disk
and
press
any
key
reboot
.
.\r\n"
.byte
0
该字符串告知用户“已不再支持从软盘直接启动的方式,请代之以使用一个bootloader程序,移除磁盘并按任意键重启”。
接着显示完该字符后跳转到bs_die标号处继续执行,该代码段如下所示:
1.bs_die:
Allow
user
key,
then
/*将ax寄存器清零*/
$0x16
/*从键盘读入字符,即等待用户按键*/
$0x19
/*重新启动系统*/
0x19
should
never
return.
In
case
it
does
anyway,
invoke
BIOS
reset
code...
$0xf000,$0xfff0
/*长跳转到0xf
fff0处,即重新执行BIOS中的一系列初始化过程*/
以上代码等待用户按键之后即刻重启。
实际上虽然header.S文件中自带bootloader,但如果内核从硬盘启动该段代码无论如何都不会被执行,而内核的维护者也不再维护这段引导记录,关于这一点也可以从链接脚本文件的设置中看出。
1.OUTPUT_FORMAT("
elf32-i386"
)
2.OUTPUT_ARCH(i386)
3.ENTRY(_start)
/*指定入口标号*/
5.SECTIONS
6.{
0;
.bstext
:
{
*(.bstext)
}
.bsdata
*(.bsdata)
497;
.header
*(.header)
13.
.entrytext
*(.entrytext)
.inittext
*(.inittext)
.initdata
*(.initdata)
__end_init
.;
......
18.}
以上文件为arch\x86\boot目录下的setup.ld,可以发现链接脚本指定的入口为_start标号而非bootsect_start,这就好比在C/C++中指定main()函数为入口点。
事实上当Linux内核从_start标号处开始执行后便再也不会跳入bootsect_start标号执行。
在这里简单提一下链接脚本的语法:
在SECTIONS{}内设置最终生成的文件中各个节区的属性,.=0表示.bstext以及.bsdata节
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 溯源 深入 领略 Linux31 内核 风光 系统启动