荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: georgehill (清风浮云 人生), 信区: Linux
标  题: Linux内核源码指南 (转寄)
发信站: BBS 荔园晨风站 (Wed Oct 18 18:25:38 2000), 站内信件

【 以下文字转载自 georgehill 的信箱 】
【 原文由 georgehill.bbs@smth.org 所发表 】
发信人: lion (我要offer!), 信区: Linux
标  题: Linux内核源码指南 (转寄)
发信站: BBS 水木清华站 (Wed Oct 18 16:52:54 2000)


找到一些好文,未敢独享,本着Internet精神,翻译出来大家同乐.
本译文的发布遵守GPL规范.
原文出处:Kernel Hackers Grouphttp://khg.redhat.com/HyperNews/get/khg.html]
从零开始
PC机加电后,80x86的处理器把自己设置为实模式。并跳转到0xFFFF0,开始执行代码,
正常情况下,那里正是ROM-BIOS的入口地址。PC的BIOS将进行一系列的系统检测并初始
化从物理地址0开始的中断向量表。然后启动设备的第一个扇区的数据将被装载到0x7C0
0处,并开始执行。启动设备通常是软驱或者硬盘。这个过程听上去不怎么复杂,确实不
复杂,这就是我们进一步理解内核初始化工作之前必需的所有知识。
Linux内核最开始的部分(boot/bootsect.S)是用8086汇编语言编写的,运行的时候,它
把自己装载到绝对物理地址0x90000处,并继续从启动设备读入2K数据,装载到0x90200
处,内核剩下的部分装载到0x10000处。在系统进行上述装载工作期间,屏幕会显示"Lo
ading...".装载完成后,系统把控制权交给另一段用汇编语言编写的程序--boot/Setup
.S。
setup阶段将识别一些系统特征和显示卡类型。需要的话,还会提示用户选择控制台的显
示模式。然后它会把整个系统从0x10000处移动到0x1000处,并进入保护模式,转入0x1
000执行剩下的代码。
下一步是内核的解压缩。0x1000处的代码来自zBoot/head.S,它将初始化寄存器并调用
decompress_kernel()过程,该过程将依次执行zBoot/inflate.c, zBoot/unzip.c 和 z
Boot/misc.c。解压缩后的数据将从0x100000(1M处)开始存放,这就是为什么Linux不
可能在小于2M内存的系统上运行的原因。[已经可以在1M内存的系统中使用不压缩内核;
参见 Memory Savers--ED]
把内核压缩到一个GZIP文件中的工作,是由Makefile指定zBoot目录中的工具程序完成的
,都是一些看上去很古怪的文件。
从1.1.75版内核开始,boot and zBoot目录被下移到了arch/i386/boot。这个改变意味
着我们可以同时生成多种指令系统的内核。但我们仍只考虑i386的情况。
解压缩之后的代码将从0x1010000开始运行,所有的32bit相关的设置将在这里完成:装
载IDT,GDT和LDT,识别处理器和协处理器,设置内存页。然后执行start_kernel程序。
[Maybe I"ve lost track of physical addresses, here, asI don"t know very well
 gas source code]以上操作的源代码是boot/head.S。这可能是整个内核代码中最有灵
感的部分。
要说明的是,以上的任何一个步骤出现错误,系统都会锁起。操作系统在没有完整启动
之前无法进行错误处理。
函数start_kernel()是在init/main.c中定义的,这个函数永远不会返回。从现在开始的
一切代码就都是C语言编写的了,当然要除去中断管理和enter/leave系统调用。(其实,
 大多数的宏都嵌入了汇编语言)
对付完那些狡猾的问题之后,start_kernel()初始化内核的所有部分,特别是:·设置
内存边界并调用paging_init()·初始化陷阱,IRQ通道和进程调度。
·解析命令行参数.
·如果需要,分配一个profiling缓冲。
·初始化所有的设备驱动程序和磁盘缓冲,以及其他一些次要设备。
·测量延时循环周期 (计算 CPU的"BogoMips"值)。
·测试中断16是否分配给协处理器。
最后,内核做好move_to_user_mode()(转入用户模式--译注)的准备,以便fork出init进
程(出自同一个源文件)。接着0号进程启动,也就是所谓的摽战虜,一个无限的空循
环。内核将依次试图执行/etc/init、/bin/init、/sbin/init,以便启动init进程。
如果三个都没有成功,将执行?bin/sh /etc/rc敚⒃诘谝桓鲋斩松蟜ork一个root she
ll界面。这部分代码可以上搠至Linux 0.01,那时候的Linux还只是一个单独的内核,没
有login进程可用。
从上述标准位置exec()(一个系统函数,用于启动一个子进程并取代父进程--译注)过
init进程之后,内核对于程序流不再具有直接的控制权,从现在开始,内核的职责是提
供系统调用以及维护非同步的事件(比如硬件中断)。现在多任务已经设置完毕,从现
在开始是由init依靠fork()和login进程管理多用户访问。
鉴于内核提供的服务内容,我们的简介将围绕这些服务进行,也会论及一些总体思想、
底层的数据结构以及代码组织。.

内核怎样看待进程

从内核的角度看,一个进程只是进程表的一个条目而已。
而进程表,是系统中最重要的一个数据结构,另外的重要数据结构还有内存管理表和缓
冲存储器。进程表中的单位是一个叫task_struct 的庞大结构,在include/linux/sche
d.h中定义。task_struct中既包括底层信息,又包括高层信息--广泛地汇集了从硬件寄
存器的拷贝,到进程工作目录的inode号等等重要信息。
进程表既是个数组又是个双向链表,还是一个树。它的物理实现是一个由指针组成的静
态数组,指针的长度等于一个叫做NR_TASKS的常量,这个常量在include/linux/tasks.
h中给与了定义,数组元素所指向的每个structure都位于一个为之保留的内存页中。链
表结构由next_task和prev_task两个指针完成,但树的结构实现相当复杂,这里就不叙
述了。如果你想改变NR_TASKS的值(该值缺省为128),你一定要确认已经修正了所有相
关的从属文件,并重新编译。
系统引导完成之后,内核总是面向一个进程的全局变量状态和该task_struct的指针等能
够记录进程运行状态的方面进行工作。全局变量状态只能由scheduler(进程调度程序-
-译注)改变,参看源码kernel/sched.c。无论如何,所有的进程在任何时候都应该被内
核看到,由一个叫for_each_task的宏来实现。在系统轻载时,它要比顺序扫描数组更快

一个进程,永远是要么运行于"用户态模式",要么运行于"内核态模式"。以一个用户程
序为例,它的主体运行于用户态,而系统调用运行于内核态。堆栈的使用在两种不同的
模式下是不同的,常规的栈段供进程在用户态使用,而一个固定深度的栈(内核栈,长
一页,由进程专有)供进程在内核态使用。内核栈所在的页永远不会被换出,因为它必
须随时准备在进入系统调用是被使用。
内核中的系统调用以C语言函数的形式存在,他们的"官方"名称要在函数名称前加上"sy
s_"前缀。比如:调用burnout系统调用时,要写作"sys_burnout()"。
[更多信息]系统调用的机制将在本指南的第三章阐述。看一看include/linux/sched.h
中for_each_task 和 SET_LINKS的定义有助于理解关于进程表中的链表和树。

创建和毁灭进程

UNIX系统通过fork()系统调用创建进程,进程的终结通过调用exit()或者通过收到相应
信号来完成。Linux对它们的实现是在kernel/fork.c和kernel/exit.c中。
fork()比较简单,frok.c很简短而且很容易懂。它的主要任务是为新进程填写进程表的
数据结构。Relevant steps, apartfrom filling fields, are:
Getting a free page to hold the task_struct Finding an
empty process slot (find_empty_process()) getting
another free page for the kernel_stack_page copying the
father"s LDT to the child duplicating mmap information of the father
申请一个空闲页用来保存task_struct。找到一个空的进程槽(find_empty_process())
并再申请一个空闲页作为内核栈,把父进程的LDT拷贝给子进程。为父进程制作mmap的副
本。sys_fork() 也会管理文件描述和inode.
[新消息]1.0的内核提供某些简化的线程支持,而且fork()系统调用作出了有关的暗示。
内核线程支持的开发正在主流内核开发之外进行。
从进程中退出需要点技巧,因为子进程退出的话,父进程必须被告知。此外,一个进程
可以被其他进程kill()掉(这些是UNIX的特点)。因此,文件exit.c是sys_kill()的所
在,and thevarious flavours of sys_wait(), in addition to sys_exit().
exit.c中的代码我们不在这里阐述-那可不是件有趣的事情。它处理很多保证系统处于稳
定状态的细节问题。POSIX标准非常强调的信号也是必须要处理的内容。

--

※ 来源:·BBS 水木清华站 smth.org·[FROM: 211.69.205.152]
--
※ 转载:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.1.115]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店