Linux 内核源代码漫游

本文介绍了Linux内核的启动过程,包括系统引导、内核解压缩、保护模式转换及内核初始化等关键步骤,并阐述了进程的创建与结束机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux 内核源代码漫游 创建时间:2001-10-11 21时13 分
第 1 页 共 9 页
Linux内核源代码漫游
Alessandro Rubini 著, rubini@pop.systemy.it
赵炯 译, gohigh@sh163.net ( www.plinux.org)
本章试图以顺序的方式来解释Linux 源代码,以帮助读者对源代码的体系结构以及很多
相关的unix 特性的实现有一个很好的理解。目标是帮助对Linux 不甚了解的有经验的C 程
序员对整个Linux 的设计有所了解。这也就是为什么内核漫游的入点选择为内核本身的启始
点:系统引导(启动)。
这份材料需要对C 语言以及对Unix的概念和PC机的结构有很好的了解,然而本章中并
没有出现任何的C 代码,而是直接参考(指向)实际的代码的。有关内核设计的最佳篇幅是
在本手册的其它章节中,而本章仍趋向于是一个非正式的概述。
本章中所参阅的任何文件的路径名都是指主源代码目录树,通常是/usr/src/linux。
这里所给出的大多数信息都是取之于Linux 发行版1.0 的源代码。虽然如此,有
时也会提供对后期版本的参考。这篇漫游中开头有 图标的任何小节都是强调1.0 版本
后对内核的新的改动。如果没有这样的小节存在,则表示直到版本1.0.9-1.1.76,没有作
过改动。
有时候本章中会有象这样的小节,这是指向正确的代码以对刚讨论过的主题取得
更多信息的指示符。当然,这里是指源代码。
引导(启动)系统
当PC 的电源打开后,80x86 结构的CPU 将自动进入实模式,并从地址0xFFFF0 开始自
动执行程序代码,这个地址通常是ROM-BIOS 中的地址。PC 机的BIOS 将执行某些系统的检
测,在物理地址0 处开始初始化中断向量。此后,它将可启动设备的第一个扇区读入内存地
址0x7C00 处,并跳转到这个地方。启动设备通常是软驱或是硬盘。这里的叙述是非常简单
的,但这已经足够理解内核初始化的工作过程了。
Linux 的最最前面部分是用8086 汇编语言编写的(boot/bootsect.S),它将由BIOS 读
入到内存0x7C00 处,当它被执行时就会把自己移到绝对地址0x90000 处,并将启动设备
(boot/setup.S)的下2kB字节的代码读入内存0x90200 处,而内核的其它部分则被读入到地
址0x10000 处。在系统加载期间将显示信息"Loading..."。然后控制权将传递给
boot/Setup.S 中的代码,这是另一个实模式汇编语言程序。
启动部分识别主机的某些特性以及vga 卡的类型。如果需要,它会要求用户为控制台选
择显示模式。然后将整个系统从地址0x10000 移至0x1000 处,进入保护模式并跳转至系统
的余下部分(在0x1000 处)。
下一步是内核的解压缩。0x1000 处的代码来自于zBoot/head.S,它初始化寄存器并调
用decompress_kernel(),它们依次是由zBoot/inflate.c、zBoot/unzip.c和zBoot/misc.c
组成。被解压的数据存放到了地址0x10000 处(1兆),这也是为什么Linux 不能运行于少于
2 兆内存的主要原因。[在1 兆内存中解压内核的工作已经完成,见 Memory Savers--ED]
将内核封装在一个gzip 文件中的工作是由zBoot 目录中的Makefile 以及工具完
成的。它们是值得一看的有趣的文件。
内核发行版1.1.75 将boot 和zBoot 目录下移到了arch/i386/boot 中了,这个改
动意味着对不同的体系结构允许真正的内核建造,不过我将仍然只讲解有关i386 的信息。
解压过的代码是从地址0x10100 处开始执行的[这里我可能忘记了具体的物理地址了,
Linux 内核源代码漫游 创建时间:2001-10-11 21时13 分
第 2 页 共 9 页
因为我对相应的代码不是很熟],在那里,所有32 比特的设置启动被完成: IDT、GDT 以及
LDT 被加载,处理器和协处理器也已确认,分页工作也设置好了;最终调用start_kernel
子程序。上述操作的源代码是在boot/head.S 中的,这可能是整个内核中最有诀窍的代码了。
注意如果在前述任何一步中出了错,计算机就会死锁。在操作系统还没有完全运转之前
是处理不了出错的。
start_kernel()是位于init/main.c 中的,并且没有任何返回结果。从现在起的任何代
码都是用C 语言编制的,除了中断管理和系统调用的入/出代码(当然,还有大多数的宏都
嵌入了汇编代码)。
让轮子转动起来
在处理了所有错综复杂的问题之后,start_kernel()初始化了内核的所有部分,尤其是:
• 设置内存边界和调用paging_init();
• 初始化中断、IRQ 通道和调度;
• 分析(解析)命令行;
• 如果需要,就分配一个数据缓冲区(profiling buffer)以及其它一些小部分;
• 校正延迟循环(计算“BogoMips”数);
• 检查中断16是否能与协处理器工作。
最后,为了生成初始进程,内核准备好了移至move_to_user_mode(),它的代码也是在
同一个源代码文件中的。然后,所谓的空闲任务,进程号0 就进入无限的空闲循环中运行。
接着初始进程(init process)尝试着运行/etc/init、/bin/init 或者/sbin/init。
如果它们没有一个运行成功的,就会去执行代码“/bin/sh /etc/rc”并且在第一个终
端上生成一个根命令解释程序(root shell)。这段代码回溯至Linux 0.01,当时操作系
统只有一个内核,并且没有登录进程。
在从一个标准的地方(让我们假定我们有)用exec()执行了init 初始化程序之后,内
核就对程序的执行没有了直接的控制。从现在起它的规则是提供对系统调用的处理,以及为
异步事件服务(比如硬件中断等)。多任务的环境已经建立,从现在起是init程序通过fork()
派生出的系统进程和登录进程来管理多用户的访问了。
由于内核是负责提供服务的,这个漫游文章将通过观察这些服务(“系统调用”)以及
通过提供基本数据结构的原理和代码的组织结构继续讨论下去。
内核是如何看见一个进程的
从内核的观点来看,一个进程只是进程表中的一个条目而已。
而进程表以及各个内存管理表和缓冲存储器则是系统中最为重要的数据结构。进程表中
的各个单项是task_struct结构,是定义在include/linux/sched.h中的非常大的数据结构。
在task_struct 中保留着从低层到高层的信息,范围从某些硬件寄存器的拷贝到进程工作目
录的inode 信息。
进程表既是一个数组和双链表,也是一个树结构。它的物理实现是一个静态的指针数组,
它的长度是定义在include/linux/tasks.h 中的常量NR_TASKS,并且每个结构都位于一个
保留内存页中。这个列表结构是通过指针next_task和pre_task 构成的,而树结构则是非
常复杂的并且我们在此将不加以讨论。你可能希望改动NR_TASKS的默认值128,但你要保
证所有源文件中相关的适当文件都要被重新编译过。
在启动引导过程结束后,内核将总是代表某个进程而工作,并且全局变量current ---
一个指向某个task_struct条目的指针 --- 被用于记录正在运行的进程。current仅能通
Linux 内核源代码漫游 创建时间:2001-10-11 21时13 分
第 3 页 共 9 页
过在kernel/sched.c 中的调度程序来改变。然而,由于所有的进程都必须访问它,所以使
用了宏for_each_task。当系统负荷很轻时,它要比数组的顺序扫描快得多。
进程总是运行于“用户模式”或“内核模式”。用户程序的主体是运行于用户模式而其
中的系统调用则运行于内核模式中。在这两种执行模式中进程所用的堆栈是不一样的 -- 常
规的堆栈段用于用户模式,而一个固定大小的堆栈(一页,由该进程所有)则用于内核模式。
内核堆栈页是从不交换出去的,因为每当一个系统调用进入时它就必须存在着。
内核中的系统调用(system calls)是作为C 语言函数存在的,它们的‘正规’名称是
以‘sys_’开头的。例如一个名为burnout 的系统调用将调用内核函数sys_burnout()。
系统调用机制在本手册的第三章中进行了讨论。观看在include/linux/sched.h
中的for_each_task和SET_LINKS 能够帮助理解进程表中的列表和树结构。
创建和结束进程
unix 系统是通过fork()系统调用创建一个进程的,而进程的终止是通过exit()或收到
一个信号来完成的。它们的Linux实现位于kernel/fork.c和kernel/exit.c 中。 派生出
一个进程是很容易的,所以fork.c程序很短并易于理解。它的主要任务是为新的进程填写
数据结构。除了填写各个字段以外,相关的步骤有:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值