每晚十五分钟,通读一个技术点,水滴石穿,一切只为渴求更优秀的你!
————零声大学
Linux是一个庞大、高效而复杂的操作系统,尽管它的开发起始于LinusTorvalds一个人,但随着时间的推移,越来越多的人加入了Linux的开发和对它的不断健全。怎样从整体上掌握Linux内核的体系结构,对于Linux的开发者和剖析者都至关重要。
Linux内核在整个操作系统中的位置
Linux的内核不是孤立的,必须把它放到整个系统中去研究,如图1.1所示,显示了Linux内核在整个操作系统的位置。
Linux内核在整个操系统中的位置
由图可以看出,Linux操作系统由4个部份组成。
1.用户进程
用户应用程序是运行在Linux操作系统最高层的一个庞大的软件集合。当一个用户程序
在操作系统之上运行时,它成为操作系统中的一个进程。
2.系统调用插口
在应用程序中,可通过系统调用来调用操作系统内核中特定的过程,以实现特定的服务。
比如,在程序中安排一条创建进程的系统调用,则操作系统内核便会为之创建一个新进程。
系统调用本身也是由若干条指令构成的过程。但它与通常的过程不同,主要区别是:系
统调用是运行在内核态(或叫系统态),而通常过程是运行在用户态。在Linux中,系统调
用是内核代码的一部份。
3.Linux内核
这是本书要讨论的重点。内核是操作系统的灵魂,它负责管理c盘上的文件、内存,负
责启动并运行程序,负责从网路上接收和发送数据包等。简言之,内核实际是具象的资源操
作到具体硬件操作细节之间的插口。
4.硬件
这个子系统包括了Linux安装时须要的所有可能的化学设备。诸如,CPU、内存、硬盘、
网路硬件等。
前面的这些界定把整个Linux操作系统分为4个层次。把用户进程也列入操作系统的范
围内是由于用户进程的运行和操作系统密切相关,而系统调用插口可以说是操作系统内核的
扩展,硬件则是操作系统内核赖以生存的物质条件。这4个层次的依赖关系表现为:下层依
赖上层。
Linux内核的作用
从程序员的角度来讲,操作系统的内核提供了一个与计算机硬件等价的扩充或虚拟的计
算平台。它具象了许多硬件细节,程序可以以某种统一的方法进行数据处理,而程序员则可
以避免许多硬件细节。从另一个角度讲,普通用户则把操作系统看成是一个资源管理者,在
它的帮助下,用户可以以某种便于理解的形式组织自己的数据,完成自己的工作并和其他人
共享资源。
Linux以统一的形式支持多任务,而这些方法对用户进程是透明的,每一个进程运行起
来就似乎只有它一个进程在计算机上运行一样,独占显存和其他的硬件资源,而实际上,内
核在并发地运行几个进程,而且还能让几个进程公正合理地使用硬件资源,也能使各进程之
间互不干扰安全地运行。
Linux内核的具象结构
Linux内核由5个主要的子系统组成,如图所示。
Linux内核子系统及其之间的关系
(1)进程调度(SCHED)控制着进程对CPU的访问。当须要选择下一个进程运行时,由调度程序选择最值得运行的进程。可运行进程实际是仅等待CPU资源的进程,假如某个进程在等待其他资源,则该进程是不可运行进程。Linux使用了比较简单的基于优先级的进程调
度算法选择新的进程。
(2)显存管理(MM)允许多个进程安全地共享主显存区域。Linux的显存管理支持虚拟显存,即在计算机中运行的程序,其代码、数据和堆栈的总数可以超过实际显存的大小,操作系统只将当前使用的程序块保留在显存中,其余的程序块则保留在c盘上。必要时,操作系统负责在c盘和显存之间交换程序块。
显存管理从逻辑上可以分为硬件无关的部份和硬件相关的部份。硬件无关的部份提供了进程的映射和虚拟显存的对换;硬件相关的部份为显存管理硬件提供了虚拟插口。
(3)虚拟文件系统(VirtulFileSystem,VFS)隐藏了各类不同硬件的具体细节,为所有设备提供了统一的插口,VFS还支持多达数十种不同的文件系统,这也是Linux较有特色的一部份。
虚拟文件系统可分为逻辑文件系统和设备驱动程序。逻辑文件系统指Linux所支持的文件系统,如ext2,fat等,设备驱动程序指为每一种硬件控制器所编撰的设备驱动程序模块。
(4)网路插口(NET)提供了对各类网路标准合同的存取和各类网路硬件的支持。网路插口可分为网路合同和网路驱动程序两部份。网路合同部份负责实现每一种可能的网路传输合同,网路设备驱动程序负责与硬件设备进行通讯,每一种可能的硬件设备都有相应的设备驱动程序。
(5)进程间通讯(IPC)支持进程间各类通讯机制。从图1.2所示可以看出,处于中心位置的是进程调度,所有其他的子系统都依赖于它,由于每位子系统都须要挂起或恢复进程。通常情况下,当一个进程等待硬件操作完成时深入理解linux内核 第四版,它被挂起;当操作真正完成时,进程被恢复执行。比如,当一个进程通过网路发送一条消息时,网路插口须要挂起发送进程,直至硬件成功地完成消息的发送,当消息被发送出去之后,网路插口给进程返回一个代码,表示操作的成功或失败。其他子系统(显存管理,虚拟文件系统及进程间通讯)以相像的理由依赖于进程调度。
各个子系统之间的依赖关系如下。
•进程调度与显存管理之间的关系:这两个子系统相互依赖。在多道程序环境下,程序
要运行必须为之创建进程,而创建进程的第一件事,就是要将程序和数据放入显存。
•进程间通讯与显存管理的关系:进程间通讯子系统要依赖显存管理支持共享显存通讯
机制深入理解linux内核 第四版,这些机制准许两个进程不仅拥有自己的私有显存,还可存取共同的显存区域。
•虚拟文件系统与网路插口之间的关系:虚拟文件系统借助网路插口支持网路文件系统
(NFS),也借助显存管理支持RAMDISK设备。
•显存管理与虚拟文件系统之间的关系:显存管理借助虚拟文件系统支持交换,交换进
程(swapd)定期地由调度程序调度,这也是显存管理依赖于进程调度的惟一缘由。当一个进
程存取的显存映射被换出时,显存管理向文件系统发出恳求,同时,挂起当前正在运行的进
程。
不仅如上图所示的依赖关系以外,内核中的所有子系统还要依赖一些共同的资源,但在图中并没有显示下来。这种资源包括所有子系统都用到的过程,比如分配和释放显存空间的过程,复印警告或错误信息的过程,还有系统的调试解释器等。
Linux内核源代码
为了深入地了解Linux的实现机制,还必须阅读Linux的内核源代码,下边是对有关源代码的介绍。
多版本的内核源代码
对不同的内核版本,系统调用通常是相同的。新版本也许可以降低一个新的系统调用,但旧的系统调用将仍然不变,这对于保持向后兼容是十分必要的—一个新的内核版本不能打破常规的过程。在大多数情况下,设备文件将一直相同,而另一方面,版本之间的内部插口有所变化。
Linux内核源代码有一个简单的数字系统,任何质数内核(如2.0.30)是一个稳定的版本,而质数内核(如2.1.42)是正在发展中的内核。本书是基于稳定的2.4.16源代码的。发展中的内核总是有最新的特征,支持最新的设备,虽然它们还不稳定,其实不是你所想要的,但它们是发展最新而又稳定的内核的基础。
目前,较新而又稳定的内核版本是2.2.x和2.4.x,由于版本之间稍有差异,因而,假如你想让一个新驱动程序模块既支持2.2.x,也支持2.4.x,就须要依照内核版本对模块进行条件编译。
对内核源代码的更改是以补丁文件的方式发布的。patch实用程序拿来对内核源文件进行一系列的修订,比如,假如你有2.4.9内核源代码,而想移到2.4.16,你可以获得2.4.16的补丁文件,应用patch来修订2.4.9源文件。诸如:
$cd/usr/src/linux
$patch-p1<patch-2.4.16
Linux内核源代码的结构
Linux内核源代码坐落/usr/src/linux目录下,其结构分布如图所示,每一个目录或子目录可以看作一个模块,其目录之间的连线表示“子目录或子模块”的关系。下边是对每一个目录的简单描述。
include/目录包含了构建内核代码时所需的大部份包含文件,这个模块借助其他模块重建内核。
init/子目录包含了内核的初始化代码,这是内核开始工作的起点。
arch/子目录包含了所有硬件结构特定的内核代码,如图1.3所示,arch/子目录下有i386和alpha模块等。
drivers/目录包含了内核中所有的设备驱动程序,如块设备,scsi设备驱动程序等。
fs/目录包含了所有文件系统的代码,如:ext2,vfat模块的代码等。
net/目录包含了内核的连网代码。
mm/目录包含了所有的显存管理代码。
ipc/目录包含了进程间通讯的代码。
kernel/目录包含了主内核代码。
图显示了8个目录,即init、kernel、mm、ipc、drivers、fs、arch及net的包含文件都在“include/”目录下。在Linux内核中包含了drivers、fs、arch及net模块,这就促使Linux内核既不是一个层次式结构,也不是一个微内核结构,而是一个“整体式”结构。由于系统调用可以直接调用内核层,因而,该结构促使整个系统具有较高的性能,其缺点是内核更改上去比较困难,除非遵守严格的规则和编码标准。
在图中所示的模块结构,代表了一种工作分配单元。借助这些结构,我们期望LinusTorvalds能维护和提高内核的核心服务,即init/、kernel/、mm/及ipc/,其他的模块drivers、fs、arch及net也可以作为工作单元,比如,可以分配一组人对块文件系统进行维护和进一步地开发,而另一组人对scsi文件系统进行构建。图1.3所示类似于Linux的自愿者开发队伍一起工作来提高和扩充整个系统的框架。
Linux源代码的分布结构
从何处开始阅读源代码
像Linux内核这样庞大而复杂的程序看上去确实让人望而生畏,它像一个很大的球,没有起点和终点。在读源代码的过程中,你会碰到这样的情况,当读到内核的某一部分时又会涉及到其他更多的文件,当返回到原先的地方想继续往下读时,又忘了原先读的内容。在Internet上,好多人因此付出了很大的努力,制做出了源代码导航器,这为源代码阅读提供了挺好的条件,下载站点为。下边给出阅读源代码的一些线索。
1.系统的启动和初始化
在基于Intel的系统上,当loadlin.exe或LILO把内核放入到显存并把控制权传递给内核时,内核开始启动。关于这一部份,看arch/i386/kernel/head.S,head.S进行特定结构的设置,之后跳转到init/main.c的main()类库。
2.显存管理
显存管理的代码主要在/mm,但特定结构的代码在arch/*/mm。缺页中断处理的代码在mm/memory.c,而显存映射和页高速缓存器的代码在mm/filemap.c。缓冲器高速缓存是在mm/buffer.c中实现,而交换高速缓存是在mm/swap_state.c和mm/swapfile.c中实现。
3.内核
内核中,特定结构的代码在arch/*/kernel,调度程序在kernel/sched.c,fork的代码在kernel/fork.c,task_struct数据结构在include/linux/sched.h中。
4.PCI
PCI伪驱动程序在drivers/pci/pci.c,其定义在include/linux/pci.h。每一种结构都有一些特定的PCIBIOS代码,Intel的在arch/alpha/kernel/bios32.c。
5.进程间通讯
所有SystemVIPC对象权限都包含在ipc_perm数据结构中,这可以在include/linux/ipc.h中找到SystemV消息是在ipc/msg.c中实现,共享显存在ipc/shm.c中,讯号量在ipc/sem.c中,管线在ipc/pipe.c中实现。
6.中断处理
内核的中断处理代码是几乎所有的微处理器所特有的。中断处理代码在arch/i386/kernel/irq.c中linux防火墙设置,其定义在include/asm-i386/irq.h中。
7.设备驱动程序
Linux内核源代码的好多行是设备驱动程序。Linux设备驱动程序的所有源代码都保存在/driver,依据类型可进一步界定为:
/block
块设备驱动程序如ide(在ide.c)。假如想看包含文件系统的所有设备是怎样被初始化的,应该看drivers/block/genhd.c中的device_setup(),device_setup()除了初始化了硬碟,当一个网路安装nfs文件系统时,它也初始化网路。块设备包含了基于IDE和SCSI的设备。
/char
这是看字符设备(如tty,并口及键盘等)驱动程序的地方。
/cdrom
Linux的所有CDROM代码都在这儿,如在这里可以找到SoundblasterCDROM的驱动程序。
注意ideCD的驱动程序是ide-cd.c,置于drivers/block;SCSICD的驱动程序是scsi.c,置于drivers/scsi。
/pci
这是PCI伪驱动程序的源代码,在这儿可以看见PCI子系统是怎样被映射和初始化的。
/scsi
在这儿可以找到所有的SCSI代码及Linux所支持的scsi设备的所有设备驱动程序。
/net
在这儿可以找到网路设备驱动程序,如DECChip21040PCI以太网驱动程序在tulip.c中。
/sound
这是所有声卡驱动程序的所在地。
8.文件系统
EXT2文件系统的源代码全部在fs/ext2/目录下,而其数据结构的定义在include/linux/ext2_fs.h,ext2_fs_i.h及ext2_fs_sb.h中。虚拟文件系统的数据结构在include/linux/fs.h中描述,而代码是在fs/*中。缓冲区高速缓存与更新内核的守护进程的实现是在fs/buffer.c中。
9.网路
网路代码保存在/net中,大部份的include文件在include/net下,BSD套节口代码在net/socket.c中,IP第4版本的套节口代码在net/ipv4/af_inet.c。通常的合同支持代码(包括sk_buff处理类库)在net/core下,TCP/IP联网代码在net/ipv4下,网路设备驱动程序在/drivers/net下。
10.模块
内核模块的代码部份在内核中,部份在模块包中,后者全部在kernel/modules.c中,而数据结构和内核守护进程kerneld的信息分别在include/linux/module.h和include/linux/kerneld.h中。假如想看ELF目标文件的结构,它坐落include/linux/elf.h中。
Linux内核源代码剖析工具
但凡尝试做过内核剖析的人都晓得,Linux的内核组织结构似乎十分有条理,然而,它其实是众人合作的结果,在阅读代码的时侯要将各个部份结合上去,确实是件十分困难的事情。由于在内核中的代码层次结构肯定分多个层次,这么对一个函数的剖析常用linux系统,肯定会涉及到多个函数,而对每一个函数都可能有多层的调用,一层层出来,直接在代码文件中查找肯定会让你丧失耐心和兴趣的。最后,好多人只能望洋却步,或则只是找一本书来瞧瞧基本原理,却不敢去说自己真正看过内核源代码。任何一本原理书只能是你阅读源代码的导航器,绝不能取代源代码的阅读。
老话说:“工欲善其事,必先利其器”。面对Linux这样庞大的源代码,必须有相应工具的支持能够使剖析有效地进行下去。在此介绍两种源代码的剖析工具,希望能对感兴趣的读者有所帮助。
Linux超文本交叉代码检索工具
Linux超文本交叉代码检索工具LXR(LinuxCrossReference),是由德国波恩学院物理系ArneGeorgGleditsch和PerKristianGjermshus编撰的。这个工具实际上运行在Linux或则UNIX平台下,通过对源代码中的所有符号构建索引,因而可以便捷地检索任何一个符号,包括函数、外部变量、文件名、宏定义等。不仅仅是针对Linux源代码,对于C语言的其他小型的项目,都可以构建其LXR站点,便于开发者查询代码,以及后继开发者学习代码。
目前的LXR是专门为Linux下边的Apache服务器设计的,通过运行perl脚本,检索在安装时按照须要构建的源代码索引文件,将数据发送到网路顾客端的Web浏览器上。任何一种平台上的Web浏览器都可以访问,这就便捷了习惯在Windows平台下工作的用户。LXR的英文网站为,在中国Linux论坛上有其镜像。
读者假如想构建自己的LXR网站,则可直接通过,下载LXR的tarball方式的安装包。另外,由于LXR使用glimpse作为整个项目中文本的搜索工具,为此还须要下载glimpse,位置在,下载glimpse-4.12.6.bin.Linux-2.2.5-22-i686.tar.gz,也可以使用更新的版本下载之后根据说明进行安装和配置,就可以构建自己的LXR网站。假如你上网很便捷,就可以直接从网站查询所须要的各类源代码信息。
Windows平台下的源代码阅读工具(SourceInsight)
为了便捷地学习Linux源程序,我们不妨回到我们熟悉的Windows环境下。并且在Windows平台上,使用一些常见的集成开发环境,疗效也不是很理想,例如无法将所有的文件加进去,查找速率平缓,对于非Windows平台的函数不能彩色显示。在Windows平台下有一个强悍的源代码编辑器,它的卓越性能致使学习Linux内核源代码的难度大大增加,这便是SourceInsight3.0,它是一个Windows平台下的共享软件,可以从上下载30天试用版本。因为SourceInsight是一个Windows平台的应用软件,所以首先要通过相应手段把Linux系统上的程序源代码移到Windows平台下,这一点可以通过在linux平台中将/usr/src目录下的文件拷贝到Windows平台的分区上,或则从网上或光碟中直接拷贝文件到Windows平台的分区上。
这个软件的安装十分简单,双击安装文件名,之后按提示进行即可。安装完成后,就可启动该程序。这个软件使用上去也十分简单:先选择Project菜单下的new,新建一个工程,输入工程名,接着要求你把欲读的源代码加入(可以加入整个目录)后,该软件就剖析你所加的源代码。剖析完后,就可以进行阅读了。对于打开的阅读文件,假如想看某一变量的定义,先把光标定坐落该变量,之后单击工具条上的相应选项,该变量的定义就显示下来。对于函数的定义与实现也可以同样操作。
文章评论