Linux - 内核机制

1、Linux内核体系结构

首先讨论Linux内核的体系结构,包括内核的职能、内核的组织结构和模块、内核提供的服务以及进程管理等。

内核的职能

内核(也称为操作系统)有如下两个主要作用:

  • 与系统的硬件设备进行交互并对其加以控制。
  • 为应用程序提供运行环境。

某些操作系统允许应用程序直接访问硬件设备,但这种能力目前已经很少见。类UNIX操作系统向应用程序隐藏了所有的低层硬件细节。应用程序如果希望使用某个硬件资源,就必须向操作系统发出请求。操作系统对该请求进行评估,并在请求有效时代表应用程序与硬件设备进行交互。为了实施这种机制,操作系统依赖于硬件设备禁止与应用程序直接交互的能力。

内核组织结构与模块

与其他许多类UNIX操作系统相似,Linux是单内核(monolithic)结构。这意味着虽然Linux被划分成控制系统各种组件(例如内存管理和进程管理)的多个子系统,但所有的子系统都紧密集成在一起,从而构成整个内核。与之相反,微内核(microkernel)操作系统提供了最少量的功能集合,而所有其他的操作系统层次都在微内核之上以进程方式执行。由于各个层次之间存在着消息传递,微内核操作系统的效率通常较低,但这类操作系统非常便于扩展。

Linux内核可通过模块方式进行扩展。模块具有既提供了微内核的优点却又没有额外开销的内核特性。模块是一种可以在系统运行时链接到内核的对象。

内核服务

内核为在用户模式中运行的应用程序提供了一组与系统进行交互的接口。这些接口也称为系统调用,应用程序可以通过接口访问硬件和其他内核资源。系统调用不仅为应用程序提供了抽象化的硬件层次,还确保了系统的安全和稳定性。

大多数应用程序并不直接使用系统调用。相反,在编程时采用了应用程序接口(API)。需要注意的是,在API和系统调用之间不存在关联。API是作为库文件的组成部分提供给应用程序使用的,这些API一般通过一个或多个系统调用来实现。

/proc文件系统的外部性能视图

/proc文件系统为用户提供了关于内核内部数据结构的视图。可以利用它查看和修改内核的某些内部数据结构,从而改变内核的行为。/proc文件系统提供了一种通过微调系统资源来改善应用程序以及系统整体性能的简单方法。

/proc文件系统是一种由内核以动态创建方式生成数据的虚拟文件系统。它被组织成多种目录形式,其中每个目录都对应于特定子系统的可调选项。附录A详细阐述了使用/proc文件系统对系统进行微调的方法。

Linux的另一个要素是内存管理。

2、内存管理

Linux的内存管理问题包括地址空间、物理内存、内存映射、分页机制及交换机制。

地址空间

虚存的优点之一是每个进程都认为自己拥有所需的全部地址空间。虚存的大小可以是系统中物理内存大小的许多倍。系统中的每个进程都有自己的虚址空间,这些虚址空间相互之间完全独立。运行某个应用程序的进程不会影响到其他进程,应用程序之间也是相互保护的。虚址空间由操作系统映射至物理内存。从应用程序的角度来说,这个地址空间是一个线性的平面地址空间;但内核对用户虚址空间的处理则有很大的不同。

线性地址空间被划分为两部分:用户地址空间和内核地址空间。用户地址空间不会在每次发生上下文切换时都改变,而内核地址空间则始终保持不变。为用户空间和内核空间分配的空间容量主要取决于系统是32位还是64位的体系结构。例如,x86是32位的体系结构,它只支持4GB的地址空间,其中3GB为用户空间保留,1GB分配给内核地址空间。具体的划分大小由内核配置变量PAGE_OFFSET决定。

物理内存

为了支持多种体系结构,Linux使用与体系结构无关的方式来描述物理内存。

物理内存可以组织成内存体(bank)的结构,每个内存体与处理器的距离都是特定的。随着越来越多的机器采用非一致性内存访问(Nonuniform Memory Access,NUMA)技术,这种类型的内存布局已非常普遍。Linux VM以节点来表示这种排列方式。每个节点划分为许多称为管理区(zone)的内存块,它们代表了内存中的地址范围。有三种不同的管理区:ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。例如,x86具有以下内存管理区:

  • ZONE_DMA          内存地址的前16MB
  • ZONE_NORMAL      16MB~896MB
  • ZONE_HIGHMEM     896MB~内存结束地址

每个管理区都有各自的用途。从前的一些ISA设备对于可以在哪些地址上执行I/O操作具有限制条件,而ZONE_DMA可以消除这些限制。
ZONE_NORMAL用于所有的内核操作和分配。它对于系统性能是极为重要的。
ZONE_HIGHMEM是系统中其余的内存。需要注意的是,ZONE_HIGHMEM无法用于内核的分配和数据结构,只能用于保存用户数据。

内存映射

为了更好地理解内核内存的映射机制,下面以x86为例加以说明。前文提过,内核只有1GB的虚址空间可用,其他3GB保留给用户空间。内核将ZONE_DMA 和 ZONE_NORMAL中的物理内存直接映射到其地址空间。这意味着系统中最前面的896MB物理内存被映射到内核虚址空间,从而只剩下128MB的虚址空间。这128MB的虚址空间用于诸如vmalloc和kmap等操作。

如果物理内存容量较小(少于1GB),这种映射机制运作良好。然而,目前的所有服务器都支持数十千兆字节的内存。Intel公司在其Pentium处理器中引入了物理地址扩展(Physical Address Extension,PAE)机制,能够支持最多64GB物理内存。前述的内存映射机制使得如何处理高达数十千兆字节的物理内存成为x86 Linux的一个主要问题来源。Linux内核按照如下方式处理高端内存(896MB以上的所有内存):当Linux内核需要寻址高端内存中的某个页面时,通过kmap操作将该页面映射到一个小的虚址空间窗口中,在该页面上执行操作,然后解除对该页面的映射。64位体系结构的地址空间非常巨大,因此这类系统不存在这个问题。

分页机制

虚存可以有多种实现方式,其中最有效的方式是基于硬件的方案。虚址空间被划分成固定大小的内存块,称之为页面。虚存访问通过页表被转换成物理内存地址。为了支持各种体系结构和页面尺寸,Linux采用了三级分页机制。它提供了如下三种页表类型:

  • 页面全局目录(Page Global Directory,PGD)
  • 页面中层目录(Page Middle Directory,PMD)
  • 页表(Page Table,PTE)

地址转换提供了一种将进程的虚址空间与物理地址空间分离的方法。每个虚存页面都可以在主内存中标记为“存在”或“不存在”。如果进程访问某个不存在的虚存地址,硬件就会产生一个页面错误,并由内核对其进行处理。内核处理该错误时将该页面置于主内存中。在这个过程中,系统可能需要将现有的某个页面替换掉,以便为新页面提供空间。

替换策略是分页系统最关键的内容之一。Linux 2.6版本修补了以前Linux版本中关于各种页面选择和替换的问题。

交换机制

交换是当主存容量不足时将整个进程移入或移出辅助存储器的过程。由于上下文切换的开销非常大,许多现代的操作系统,包括Linux,都不采用这种方法,而是采用分页机制。在Linux中,交换是在页面层次而不是在进程层次上执行的。交换的主要优点是扩展了进程可用的地址空间。当内核需要释放内存以便为新页面提供空间时,可能需要丢弃一些较少使用的或未用的页面。某些页面因为未被磁盘备份而不容易释放,需要被复制到后备存储器(交换区)中,在必要时还要从后备存储器中读回。交换机制的主要缺点是速度慢。磁盘的读写速度通常都非常缓慢,因此应该尽量消除交换操作。

3、 进程管理

本节讨论Linux中的进程管理,包括进程、任务、内核线程、调度以及上下文切换。

进程、任务与内核线程

任务只是一种“需要完成的工作的一般性描述”,可以是一个轻权的线程,也可以是一个完整的进程。

线程是最轻权的任务实例。在内核中创建线程的成本可能很高,也可能较低,这取决于线程需要拥有的特征。最简单的情形是线程与其父线程共享所有资源,包括代码、数据以及许多内部数据结构,而仅在区分该线程与其他线程上有一点点差异。

Linux中的进程是一个“重权的”数据结构。如果有必要的话,多个线程可以在单个进程中运行(并共享该进程的一些资源)。在Linux中,进程只是一个拥有全部重权特征的线程。线程和进程由调度器以相同方式进行调度。

内核线程是始终在内核模式中运行且没有用户上下文的线程。内核线程通常针对某个特定功能而存在,很容易在内核中处理它。内核线程经常具有所期望的作用:能够像其他任何进程一样进行调度;当其他进程需要该功能发挥作用时,为这些进程提供实现该功能的目标线程(通过发送信号)。

调度与上下文切换

进程调度是确保每个进程能公平分享CPU的一门科学(有人称之为艺术)。对于“公平”的定义,人们总是存在着不同的看法,因为调度器往往根据并不明显的可见的信息来做出选择。

本书后面几章中将更深入地阐述进程调度。需要注意的是,许多Linux用户都认为,一个在所有时候大部分正确的调度器要比一个在大多数时候完全正确的调度器更为重要,即缓慢运行的进程要优于因过于精心选择调度策略或错误而停止运行的进程。Linux当前的调度器程序就遵循了这个原则。

当一个进程停止运行,被另一个进程替换时,称为上下文切换。通常,这个操作的开销是很高的,内核程序员和应用程序员总是试图尽量减少系统执行上下文切换的数量。进程可以因为等待某个事件或资源而主动停止运行,或者因为系统决定应将CPU分配给另一个进程而被动地放弃运行。对于第一种情况,如果没有其他进程等待执行,CPU实际上可能进入空闲状态。在第二种情况下,该进程或者被另一个等待进程所替换,或者分配到一个新的运行时间片或时间周期继续执行。

即使在某个进程正按照有序的方式调度和执行时,也可以被其他更高优先级的任务中断。例如,假若磁盘为磁盘读操作准备好了数据后,就向CPU发送信号,并期望CPU从磁盘上获取数据。内核必须及时地处理这个情况,否则就会降低磁盘的传输率。信号、中断和异常是不同的异步事件,但在许多方面却类似,并且即使CPU已处于忙状态,它们也都必须被迅速处理。

例如,准备好了数据的磁盘会导致一个中断。内核调用该特定设备的中断处理程序,中断当前运行的进程并使用其众多资源。当中断处理程序执行结束后,当前运行的进程恢复执行。这实际上是侵占当前运行进程的CPU时间,因为当前版本的内核只测量自从该进程进入CPU之后所经过的时间,却忽略了中断会耗用该进程的宝贵时间这一事实。

中断处理程序通常是非常快速和简洁的,因而能够快速处理和清除以便使后续数据能够进入。但有时一个中断可能需要处理的工作比在中断处理程序中所期望的短时间内完成的工作更多。中断也需要一个定义良好的环境来完成其工作(要记住,中断利用了某个随机进程的资源)。在这种情况下,要收集足够信息,将工作延迟提交至bottom half处理程序进行处理。bottom half处理程序会不时地被调度执行。尽管在Linux早期版本中普遍使用了bottom half机制,但当前的Linux版本中不鼓励使用这种机制。

4、进程间通信

为了允许进程间可以互相进行通信,Linux支持许多进程间通信(InterProcess Communication,IPC)机制。信号(signal)和管道(pipe)是两种基本机制,但Linux也支持系统V的IPC机制。

信号

信号用于将事件通知给一个或多个进程,是用户进程间一种原始的通信和同步方式。信号也可用于作业控制。

内核能够产生一组已定义的信号,系统中的其他进程如果拥有适当的权限,也可以产生信号。

进程可以忽略所生成的大多数信号,但有两个例外:SIGSTOP和SIGKILL。SIGSTOP信号导致进程终止执行,而SIGKILL信号导致进程退出并被忽略掉。除了SIGSTOP和SIGKILL信号之外,进程可以决定如何处理其他各种信号。例如,进程可以阻塞信号,也可以自已处理信号或允许内核处理信号。如果由内核处理信号的话,那么内核执行该信号的默认动作。Linux保存了关于每个进程如何处理各种可能信号的信息。

信号并不是在生成之后就会立即传递给进程,而是当该进程恢复运行时才被传递。每当进程退出一个系统调用时,如果存在着任何未被阻塞的信号,这些信号都会被传递。

Linux与POSIX标准兼容,因此进程可以指定当调用特定的信号处理例程时需要阻塞哪些信号。

管道

管道是未结构化的先进先出(first-in first-out,FIFO)单向数据流。写入者向管道的一端添加数据,读取者从管道的另一端获取数据,而已读取的数据会从管道中删除。管道提供了简单的流控制机制。

例如,以下命令将ls命令(列出目录中的文件)的输出结果通过管道连至less命令(标记文件的页码)的标准输入中:

$ ls | less

Linux还支持命名管道(named pipe)。与管道不同,命名管道不是临时对象,而是文件系统中可以通过mkfifo命令创建的实体。

系统V的IPC机制

Linux支持的3种进程间通信机制,最初出现在UNIX系统V(1983年)中。这3种机制是消息队列、信号量(semaphore)和共享内存。这些机制都共享通用的认证方法。进程只需通过系统调用将唯一的访问标识符传递给内核,就可以访问这些资源。对这些系统V IPC对象的访问要通过访问许可检查,这非常类似检查文件访问的方式。系统V IPC对象的访问权限由该对象的创建者通过系统调用进行设置。

1. 消息队列

消息队列允许一个或多个进程向其中写入消息,然后这些消息将由一个或多个读出进程读取。消息队列在功能上等同于管道,但与管道相比,消息队列更为通用,并有多方面的优势。消息队列通过消息而不是未格式化的字节流形式来传递数据,从而更易于处理数据;消息可以与类型相关联,因此接收者可以在处理非紧急消息之前检查紧急消息;当多个进程共享同一个消息队列时,类型域也可用于指定具体接收者。

2. 信号量

信号量是支持原子操作set和test的一些对象,用于实现各种同步协议。信号量最适于被描述成控制多个进程访问共享资源的计数器。信号量经常作为一种锁定机制,当一个进程对特定资源执行操作时,防止另一个进程访问该资源。

如果某个进程进入一个关键区域时更改了信号量的取值,但该进程由于崩溃或被中止而无法正常离开该关键区域,那么信号量会发生死锁(deadlocking)问题。Linux通过维护信号量数组的位移列表来避免这个问题。其思想是当应用这些位移量后,信号量将返回到在实施进程的一组信号量操作之前所处的状态。

3. 共享内存

共享内存允许一个或多个进程通过这些进程的虚址空间中的公共内存进行通信。对共享内存区域的访问要通过密钥和访问权限检查加以控制。当内存被共享时,并不会检查进程使用该内存的方式。每个希望共享该内存的进程必须通过系统调用挂接到该虚存上。进程可以自己选择共享内存在其虚址空间中的位置,或者让Linux选择一块足够大的空闲区域。进程第一次访问共享虚存中的某个页面时,会发生页面错误。Linux改正这个页面错误时,会分配一个物理页面并为之创建一个页表项。之后,其他进程对该页面的访问会将页面添加到虚址空间中。

当进程不再希望共享某块虚存时,可以卸离这些虚存。如果还有其他进程仍在使用这块内存,那么卸离操作仅会影响当前进程。当共享该内存的最后一个进程与之相卸离时,会释放这块共享内存的当前物理内存页面。

如果共享虚存没有被锁定到物理内存中,会导致进一步的复杂性。在这种情况下,可以在高端内存使用期间将共享内存的页面替换到系统的交换磁盘上。


参考:《Linux服务器性能调整》
作者: [美] SanderaK.John
出版社: 清华大学出版社

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值