- 论坛徽章:
- 0
|
翻译自——GNULinux应用程序编程(GNU/Linux Application Programming) by M.Tim Jones
第二章:GNU/Linux构架
总览
高层次构架
主要内核组件构架分解
介绍
GNU/Linux操作系统被组织分成几个层次。虽然理解内核中的内容不是应用程序开发的必要,但是知道操作系统是如何组织的是很重要的。在这一章中,我们将从一个高层次出发来察看GNU/Linux的构成,以我们的方式穿越所有层次。
高层次构架
让我们从高层次来看GNU/Linux构架。让我们在20,000步外看GNU/Linux操作系统的组成结构。在核心的是仲裁所有想获得底层硬件资源例如内存,CPU(通过调度程序)以及外围设备的使用的Linux内核。而Shell(有很多种)使得用户可以访问内核。Shell提供了命令解释器和载入用户应用程序的方法然后执行应用程序。最后我们将看到应用程序是GNU/Linux操作系统的主要组成部分。这些应用程序为操作系统带来了丰富多彩的功能,例如视窗系统,Web浏览器,电子邮件程序,语言解释器当然还有编程开发工具。
在内核中我们同样放置了各种硬件驱动程序使得访问外围设备(例如配置CPU)变得简单。访问外围设备比如串行接口,监视器适配器以及网络适配器的驱动程序可以在这里找到。
这是一种简单的观点,但是接下来我们将更深入地去察看去理解Linux内核的组成。
Linux内核构架
GNU/Linux操作系统是一种层次构架。Linux内核是单一的和具有层次性的但是没有太多的限制(不毗邻的层次才可以存在依赖关系)
注意:操作系统被分成两个部分的软件。在顶端的是用户空间(在这里我们可以看到应用程序和工具同时还有GNU C库)。在底部的是内核空间(在这里我们可以找到各种各样的内核组件)。这样的一种划分同时还代表着地址空间的不同-这也是非常值得我们去注意的。每一个在用户空间的进程都有一个不被他人所共享的独自享有的内存区域。内核在它所在的地址空间进行操作,但所有的内核元素共享同一地址空间。因此如果一个内核组件弄了一个糟糕的内存引用,整个内核将会崩溃(也被称为内核恐慌)。 最后,硬件在底层操作物理地址(在内核中被映射为虚拟地址)。
现在让我们看一看每一个Linux内核的元素来确认它们可以做什么还有对于作为应用程序开发者的我们来说它们为我们提供了什么样的功能。
注意:GNU/Linux根本上来说是一个单一模块的操作系统-内核是一个单一实体。这是和分开运行着小型内核和其它进程(在内核之外运行)提供着网络,文件系统,内存管理等功能的微核操作系统不同的地方。很多在今天还存在的微核操作系统包括CMU的Mach,Apple的Darwin,Minix,BeOS,Next,QNX/Neutrino等等。到底哪一种更好,还在被激烈讨论着,而微核构架展现了它良好的动态性和灵活性。实际上,GNU/Linux早已采用了一些类微核特征的功能比如可载入内核模块功能。
GNU系统程序库(glibc)
Glibc是一个可移植程序库,它实现了标准C库函数还包括超过一半以上的系统调用。一个应用程序通过链接GNU C程序库来获得对一般函数甚至Linux内核内部的访问。Glibc实现了一些在头文件中说明了的接口。例如,stdio.h头文件定义了很多标准输入输出函数(比如fopen和printf)而且还有所用方法步骤(stdin,stdout,stderr)都已经提供了的标准流。
当生成应用程序时,GNU编译器将会自动将象征符号和GNU系统库相确定(可能的话)-运行时动态链接共享库文件。
注意:在嵌入式系统开发中,对于标准C库的使用有可能是有问题的。GCC编译器允许使用nostdlib来屏蔽掉象征符号和标准C库函数之间的链接确定。这样就使得开发者可以重写标准C库中的函数。
当一个系统调用发起时,将产生一系列特别的动作来在用户空间(应用程序运行的地方)和内核空间(系统调用实现的地方)之间转换控制权。
系统调用接口
当一个应用程序调用一个函数比如fopen,它是在调用一个具有特权的在内核里实现的系统函数。标准C函数库(glibc)提供了一个钩(hook)从用户空间到内核中提供所需函数的地方。这是非常有必要知道的,让我们更进一步的深入进去。
一个典型的系统调用将会导致宏调用在用户空间中产生。系统调用所需要的参数被载入寄存器后,一个系统中断将会产生。这个中断引起控制从用户空间到内核空间中实际系统调用发生的地方(控制转移通过一个叫做sys_call_table发生的)。
一旦调用在内核中发生,返回用户空间是通过一个叫做_ret_from_sys_call的函数进行。寄存器则被适当地载入用户空间中的栈帧。
在大量参数被使用的情况下(比如指向存储单元的指针),一份数据的拷贝将会从用户空间中传递到内核空间。
注意:系统调用的源代码可以在./linux/kernel/sys.c中的内核源代码中找到。
内核组件
内核协调分配所有想获取系统资源的访问比如接口,CPU等等。它还加强了系统的安全防止了一个用户对另一个用户的干扰。内核由一系列重要组件组成,我们将就此在下面进行讨论。
init
init组件在启动Linux后就开始执行其功能。它通过了一个叫做start_kernel的函数提供了内核的入口点。这个函数具有构架依赖性,因为不同的处理器构架对init有不同的需求。init对所有传人内核的选项进行解析并采取相应的动作。
在硬件和内核组件完成初始化之后,init组件打开初始控制台(/dev/console)并开始init进程。它是GNU/Linux中的所有进程之母且没有父进程(其它进程都有父进程)。一旦init已经开启,在内核之外的系统初始化已经被正确的完成了。
注意:内核init组件可以在Linux中的/init里的Linux内核源代码中找到。
进程调度程序
Linux内核提供了一个具有优先权的的管理在系统上运行的进程的调度程序。这意味着调度程序允许一个进程在一个时间段内运行后,如果进程没有放弃CPU(通过一个系统调用或者调用一个等待资源的函数),那么调度程序将会暂时性的停止当前运行的程序而调度其它程序。
例如通过调整进程优先权或链接调度方式(比如FIFO先进先出,Round-Robin循环调度)调度程序是可以控制的。赋予给进程来让它们运行的时间段同样是可以调整的。进程调度所需要的时间(timeout)是基于一个叫做jiffies的变量,一个jiffy是一小段在init中计算的基于CPU速度的内核时间。
注意:调度程序的源代码(以及其它内核核心模块比如进程控制和内核模块支持)可以在Linux中的/kernel里的Linux内核源代码中找到。
内存管理
Linux中的内存管理程序是Linux内核中最重要的一部分之一。它提供了从物理内存到虚拟内存间的映射函数(反过来亦一样)还有对一个物理磁盘的内存分页(页面调度)和交换。由于Linux中的内存管理方面是依赖于处理器的,内存管理程序和构架依赖代码一起合作去访问机器的物理内存。
当内核在维护它的虚拟地址空间时,每一个在用户空间中的进程都有它自己的私有的唯一的虚拟地址空间。
内存管理程序同时还提供了一个使用最近最少使用则替换的策略的实现了一个需求式的内存分页系统的后台交换程序。
注意:内存管理程序组件可以在Linux中的/mm中的Linux内核源代码中找到。
用户空间内存管理的要点将会在本书的17章“共享内存编程”和18章“其它应用程序开发主题”中讨论。
虚拟文件系统
虚拟文件系统VFS是一个在Linux中的提供让上层软件能够区分不同文件系统的的抽象层。Linux提供了独特的文件系统,比如ext2,Minix,NFS以及Reiser。Linux并没有把这些文件系统都视为各自的单一的文件系统,相反它提供给文件系统一个可以在其中插入它们的共同函数(比如open,close,read,write,select等等)的层。因此如果我们需要打开一个在Reiser日志文件系统中的文件,我们可以使用相同的常用函数open就像我们在其它文件系统一样。
VFS还是设备驱动程序的接口故它可以仲裁协调数据是怎么写入媒介(磁盘)的。在这里的抽象同样十分重要,因为它是无关于硬盘(或其它媒介)的,它提出了一个公共的观点并因此使得新文件系统的开发简单化了。实际上,多文件系统可以同步表示(挂载)。
注意:虚拟文件系统组件在Linux中的/fs里的Linux源代码中可以找到。同时还可以找到一系列代表着各个文件系统的子文件夹,例如linux/fs/ext3提供了第三扩展文件系统的源代码。 Linux提供了各种不同的文件系统,每一种都提供了在不同场景下使用的特征。比如,xfs非常适合存放并操作大型文件(例如声音和视频),而Reiser则适合操作大量的小型文件。文件系统特点影响着性能,因此如若你需要特别的应用时慎重的选择文件系统是十分有意义的。文件输入输出的主题将会在本书第十章“在GNU/linux中的文件操作”中讨论。
网络接口
Linux网络接口提供了一个和VFS非常相似的构架。网络接口组件由三层将网络细节抽象到更高层次的层构成同时提供了一个不考虑底层协议和物理媒介的公共接口。
提供给网络协议和网络设备的公共接口使得协议和物理设备能够基于系统的实际配置进行信息交换。就像VFS,灵活性是一个设计的关键。
网络组件同时还提供了信息包调度程序服务来保证服务所需求的质量。
注意:网络接口组件可以在Linux中的/net里的Linux内核源代码中可找到。
使用BSD Socket API进行网络编程将在第十章讨论。
进程间通信(IPC)
IPC组件提供了标准系统VIPC工具,包括了旗语,消息队列以及共享内存。如同VFS和网络组件,IPC所有元素共享一个公共接口。
注意:IPC组件可在Linux的/ipc中的Linux内核源代码找到。
IPC在本书将会被详细地讨论。在16章“旗语同步”中将讨论旗语和旗语API,在15章“带消息队列的IPC”将讨论消息队列API,而17章将详细讨论共享内存API。
可载入模块
可载入内核模块是GNU/Linux的重要元素之一,因为它们提供了动态改变内核的方法。只带着动态载入的必须的模块由此内核的的足印可以很小。在新驱动程序之外内核模块组件同样可以用来扩展Linux内核以增加新功能。
Linux内核模块是带着为模块init和cleanup准备的函数被特别编译的。当被安装入内核中(使用insmod工具),必要的象征符号在内核地址空间于运行时被决定并链接到正在运行中的内核。使用rmmod工具模块同样可以从内核中清除出来(而列出使用lsmod工具)。
注意:可载入模块的内核代码可在Linux的/kernel中的Linux内核源代码里找到。
设备驱动程序
设备驱动程序组件提供大量的可用的设备驱动程序。实际上,几乎有将近一半的Linux内核源文件是关于设备驱动程序的。这并不让人惊奇,因为有那么多的硬件设备,但是它并没有详细告诉你有多少是Linux支持的。
注意:设备驱动程序的源代码可以在Linux/drivers中的Linux内核源代码里找到。
架构依赖代码
在内核堆栈里的最底层是架构依赖代码。假如各种硬件平台被Linux内核所支持,那么关于架构家族和处理器的源代码就可以在这里被找到。在架构家族中的是公共启动支持文件和其它的对于指定的处理器家族(例如,硬件接口比如:DMA,MMU内存接口,中断操作等等)来说是十分详细的要素。架构代码同时还提供了详细明确的流行主板商的主板代码。
硬件
假如一系列处理器和处理器家族得到支持虽然并非Linux内核的一部分,硬件元素还是很值得去讨论的。今天我们可以发现Linux内核运行在支持一个MMU(存储器管理单元)的单地址空间构架上。处理器家族包括Arm,PowerPC。Intel 0x86(包括AMD,Cyrix,VIA),MIPS,摩托罗拉68k,Sparc以及其它的。Linux内核还可以在微软的Xbox(奔腾3)上和世嘉的Dreamcast-DC上发现。内核源代码上提供了支持这些硬件元素的信息。
注意:在Linux/arch中的Linux的内核源代码你可以找到关于处理器和各种主板的源代码
总结
Linux内核是GNU/Linux的核心。它是单块集成的并通过定义一系列层来隔离它的主要元素。假如可以获得统一的接口,它这样的设计使得添加新的设备驱动程序或者协议变得简单。这一章让我们高屋建瓴地往下看它的构架,同时讨论了它的主要元素,还在合适的地方提供了Linux内核源代码的所在。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/6957/showart_349155.html |
|