0%

Linux是一个可移植性非常好的操作系统,它广泛支持许多不同体系结构的计算机。可移植性是指代码从一种体系结构移植到另外一种体系结构上的方便程度。我们都知道Linux是可移植的,因为它已经能够在各种不同的体系结构上运行了。但这种可移植性不是凭空得来的—-需要在编写可移植代码时就为此付出努力并坚持不懈。现在,这种努力已经开始得到回报了,移植Linux到新的系统上就很容易完成。本章中我们将讨论如何编写可移植的代码—-编写内核代码和驱动程序时,必须时刻牢记这个问题。

阅读全文 »

调试工作艰难是内核级开发区别于用户级开发的一个显著特点。相比于用户级开发,内核调试的难度确实姚坚苦得多。更可怕的是,它带来的风险比用户级别更高,内核的一个错误往往立刻就能让系统崩溃。
驾驭内核调试的能力(当然,最终是为了能够成功地开发内核)很大程度上取决于经验和对整个操作系统的把握。调试内核的关键在于你对内核的深刻理解。然而我们必须找到可以开始着手的地方,所以,在这一章里我们从调试内核的一种可能步骤开始。

阅读全文 »

页高速缓存(cache)是Linux内核实现磁盘缓存。它主要用来减少对磁盘的I/O操作。具体的讲,是通过把磁盘中的数据缓存到物理内存中,把对磁盘的访问变为对物理内存的访问。这一章将讨论页高速缓存和页回写(将页高速缓存中的变更数据刷新回磁盘的操作)。
磁盘高速缓存之所以在任何现代操作系统中尤为重要源自两个因素:第一,访问磁盘的速度要远远低于(差好几个数量级)访问内存的速度—-ms和ns的差距,因此,从内存访问数据比磁盘访问速度更快,若从处理器的L1和L2高速缓存访问则更快。第二,数据一旦被访问,就很有可能在短期内再次被访问到。这种在短时期内集中访问同一片数据的原理称作临时局部原理。临时局部原理能保证:如果在第一次访问数据时缓存它,那就极有可能在短期内再次被高速缓冲命中(访问到高速缓冲中的数据)。正是由于内存访问要比磁盘访问快得多,再加上数据一次被访问后更可能再次被访问的特点,所以磁盘的内存缓存将给系统存储性能带来质的飞跃。

阅读全文 »

其实内核除了管理本身的内存外,还必须管理用户空间中进程的内存。我们称这个内存为进程地址空间,也就是系统中每个用户空间进程所看到的内存。Linux操作系统采用虚拟内存技术,因此,系统中的所有进程之间以虚拟方式共享内存。对一个进程而言,它好像都可以访问整个系统的所有物理内存。更重要的是,即使单独一个进程,它拥有的地址空间也可以远远大于系统物理内存。

阅读全文 »

系统中能够随机(不需要按顺序)访问固定大小数据片(chunks)的硬件设备称作块设备,这些固定大小的数据片就称作块。最常见的块设备是硬盘,除此之外,还有软盘驱动器、蓝光光驱和闪存等许多其他设备。注意,它们都是以安装文件系统的方式使用的—-这也是块设备一般的访问方式。
另一种基本的设备类型是字符设备。字符设备按照字符流的方式被有序访问,像串口和键盘就属于字符设备。如果一个硬件设备是以字符流的方式被访问的话,那就应该将它归于字符设备;反过来,如果一个设备是随机(无序)访问的,那么它就属于块设备。
对于这两种类型的设备,它们的区别在于是否可以随机访问数据—-换句话说,就是能否在访问设备时随意地从一个位置跳转到另一个位置。举个例子,键盘这种设备提供的就是一个数据流,当你输入“wolf”这个字符串时,键盘驱动程序会按照和输入完全相同的顺序返回这个由四个字符组成的数据流。如果让键盘驱动程序打乱顺序来读字符串,或读取其他字符,都是没有意义的。所以键盘就是一种典型的字符设备,它提供的就是用户从键盘输入的字符流。对键盘进行读操作会得到一个字符流,首先是“w”,然后是“o”,再是“l”,最后是“f”。当没人敲键盘时,字符流就是空的。硬盘设备的情况就不大一样了。硬盘设备的驱动可能要求读取磁盘上任意块的内容,然后又转去读取别的块的内容了,而被读取的块在磁盘上位置不一定要连续。所以说硬盘的数据可以被随机访问,而不是以流的方式被访问,因此它是一个块设备。
内核管理块设备要比管理字符设备细致得多,需要考虑的问题和完成的工作相对于字符设备来说要复杂的多。这是因为字符设备仅仅需要控制一个位置—-当前位置,而块设备访问的位置必须能够在介质的不同区间前后移动。所以事实上内核不必提供一个专门的子系统来管理字符设备,但是对于块设备的管理却必须有一个专门的提供服务的子系统。不仅仅是因为块设备的复杂性远远高于字符设备,更重要的原因是块设备对执行性能的要求很高;对硬盘每多一份利用都会对整个系统的性能带来提升,其效果要远远比键盘吞吐速度成倍的提高大得多。另外,我们将会看到,块设备的复杂性会为这种优化留下很大的施展空间。这一章的主题就是讨论内核如何对块设备和块设备的请求进行管理。该部分在内核中称作块I/O层。有趣的是,改写块I/O层正是2.5开发板内核的主要目标。本章涵盖了2.6内核中所有新的块I/O层。

阅读全文 »

虚拟文件系统(有时也称作虚拟文件交换,更常见的是简称VFS)作为内核子系统,为用户空间程序提供了文件和文件系统相关的接口。系统中所有文件系统不但依赖VFS共存,而且也依靠VFS系统协同工作。通过虚拟文件系统,程序可以利用标准的Unix系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作,如下图所示:
img not found

阅读全文 »

在内核里分配内存可不像在其他地方分配内存那么容易。造成这种局面的因素很多。从根本上讲,是因为内核本身不能像用户空间那样奢侈地使用内存。内核与用户空间不同,它不具备这种能力,它不支持简单便捷的内存分配方式。比如,内核一般不能睡眠。此外,处理内存分配错误对内核来说也绝非易事。正是由于这种限制,再加上内存分配机制不能太复杂,所以在内核中获取内存要比在用户空间复杂得多。不过,从程序开发者角度来看,也不是说内核的内存分配就困难得不得了,只是在用户空间中的内存分配不太一样而已。
本章讨论的是在内核之中获取内存的方法。在深入探究实际的分配接口之前,我们需要理解内核是如何管理内存的。

阅读全文 »

时间管理在内核中占有非常重要的地位。相对于事件驱动而言,内核中有大量的函数都是基于时间驱动的。其中有些函数是周期执行的,像对调度程序中的运行队列进行平衡调整或对屏幕进行刷新这样的函数,都需要定期执行,比如说,每秒执行100次;而另外一些函数,比如需要推后执行的磁盘I/O操作等,则需要等待一个相对时间后才运行—-比如说,内核会在500ms后再执行某个任务。除了上述两种函数需要内核提供时间外,内核还必须管理系统的运行时间以及当前日期和时间。
请注意相对时间和绝对时间之间的差别。如果某个事件在5s后被调度执行,那么系统所需要的不是绝对时间,而是相对时间;相反,如果要求管理当前日期和当前时间,则内核不但要计算流逝的时间而且还要计算绝对时间。所以这两种时间概念对内核时间管理来说都至关重要。
另外,还请注意周期性产生的事件与内核调度程序推迟到某个确定点执行的事件之间的差别。周期性产生的事件—-比如每10ms一次—-都是由系统定时器决定的。系统定时器是一种可编程硬件芯片,它能以固定频率产生中断。该中断就是所谓的定时器中断,它所对应中断处理程序负责更新系统时间,也负责执行需要周期性运行的任务。系统定时器和时钟中断处理程序是Linux系统内核管理机制中的中枢,本章将着重讨论它们。
本章关注的另外一个焦点是动态定时器—-一种用来推迟执行程序的工具。比如说,如果软驱马达在一定时间内都未活动,那么软盘驱动程序会使用动态定时器关闭软驱马达。内核可以动态创建或撤销动态定时器。本章将介绍动态定时器在内核中的实现,同时给出在内核代码中可供使用的定时器接口。

阅读全文 »

在使用共享内存的应用程序中,程序员必须特别留意保护共享资源,防止共享资源并发访问。内核也不例外。共享资源之所以要防止并发访问,是因为如果多个执行线程同时访问和操作数据,就有可能发生各线程之间相互覆盖共享数据的情况,造成被访问数据处于不一致的状态。并发访问共享数据是造成系统不稳定的一类隐患,而且这种错误一般难以跟踪和调试–所以首先要认识到这个问题的重要性。
要做到对共享资源的恰当保护往往很困难。多年之前,在Linux还未支持对称多处理器的时候,避免并发访问数据的方法相对来说比比较简单。在单一处理器的时候,只有在中断发生的时候,或在内核代码明确请求调度、执行另一个任务的时候,数据才有可能被并发访问。因此早期内核开发工作相比如今要简单许多!
但当年的太平日子一区不复返了,从2.0版开始,内核就开始支持对称多处理器了,而且从那以后对它的支持不断地加强和完善。支持多处理器意味着内核代码可以同时运行在两个或者更多的处理器上。因此如果不加保护运行在两个不同处理器上的内核代码完全可能在同一时间并发访问共享数据。随着2.6版本内核的出现,Linux内核已经发展成抢占式内核,这意味着调度程序可以在任何时刻抢占正在运行的内核代码,重新调度其他的进程执行。现在,内核代码中有不少部分都能够同步执行,而它们都必须妥善的保护起来。
本章我们将提纲挈领式地讨论操作系统内核中的并发和同步问题。下一章我们将详细介绍Linux内核为解决同步问题和防止产生竞争条件而提供的机制及接口。

阅读全文 »