Linux 内核深入理解 - 绪论

发布于:2024-04-28 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

多用户系统

进程

内核体系架构

文件系统概述

Base

硬链接和软链接

Unix文件类型

文件描述符与索引节点

文件操作的系统调用

Unix内核简述

进程的实现

可重入内核

进程地址空间

同步和临界区

信号与进程之间的通信

进程管理

内存管理

虚拟内存

随机访问存储器的使用

内核分配器

进程的虚拟地址空间处理

高速缓存


任何计算机系统都包含一个名为操作系统的基本程序集合!在这个集合里最重要的程序被称为内核。当操作系统启动的时候内核会被装进RAM当中。

操作系统说来说去就是两个主要目标:他充当底层的物理部件好用的抽象,给予上层服务一个好的平台

  • 与硬件部分进行交互,为包含在硬件平台上的所有底层可编程部件提供服务!

  • 为运行在计算机系统上层的应用程序即所谓的用户程序提供一个执行环境!

为了保障安全。我们的操作系统引入了一组概念,也就是用户模式和特权模式。我们会在之后的博客中有所涉及:简单的讲:一些涉及到底层硬件访问的操作需要在特权模式下进行,反之则会在用户模式下进行!程序的运行有时会在两者之间进行频繁的切换,从而更好地完成程序所提供的服务。

多用户系统

多用户系统就是一台能够并发的执行和独立的执行分别属于两个或者多个用户的若干应用程序的计算机。

并发意味着几个应用程序同时处于活动状态执行自己应用进程所需要执行的任务,而并不需要考虑其它应用程序在做什么!

多用户操作系统必须包含以下几个特点

  1. 核实用户身份的认证机制

  2. 防止有错误的用户程序妨碍其它应用程序在系统中运行的保护机制

  3. 防止有恶意的用户程序干涉或者窥视其他用户的活动的保护机制

  4. 限制分配给每个用户的资源数的记账机制

于是我们会把用户进行分为用户组在多用户系统中每个用户在机器上都会有自己的私用空间!典型的它需要一些磁盘空间来存储文件!以及接受私人邮件信息等!这一些都需要分层的特权来实现一定的保护机制!

所有的用户都用一个唯一的数字来进行标识,也就是用户标识符!通常一个计算机系统只能够由有限个人来使用。当其中的某个用户开始一个工作session的时候,操作系统会要求输入一个登录口令。如果用户输入无效则会拒绝登录,从而保障用户的隐私。

进程

所有的操作系统都有一个基本的抽象:也就是进程!

进程可以定义为一个程序执行时的一个实例或者一个运行程序所使用的上下文。在传统的操作系统中:一个进程在地址空间中执行一个独立的指令序列,地址空间是允许引用的内存地址集合。

在多用户系统中必须实施一种执行环境:在这样的环境里,几个进程可以并发的活动,并能竞争系统资源。允许进程并发活动的系统叫多道程序系统或多处理系统。

作为一个单处理器系统上,只有一个进程能够占用CPU。这也就意味着实际上只有一条执行流,那么他们是如何实现一种感官上的并发呢?

这需要操作系统的进程是抢占式的!也就是说操作系统需要记录每个进程所占有的CPU时间,并且周期性地激活调度程序。以保证感官上的并发!Unix操作系统是一个具有抢占式进程的多处理操作系统。换而言之,Unix操作系统是一个抢占式的多道处理操作系统!

内核体系架构

大部分Unix内核是单块结构的!也就是说它们属于宏内核操作系统!每一个内核层都被集成到整个内核程序中,并且代表当前进程是在内核态下运行!相反微内核操作系统只需要内核有一个很小的函数:即通常包括几个同步原语,一个简单的调度程序,和IPC通信机制。运行在微内核上的几个系统进程实现宏内核操作系统实现的功能:如内存分配,设备驱动,系统调用处理等。

事实证明微内核操作系统的效率比较低,因为它需要花费大量的时间进行进程之间的通信。不过微内核操作系统比单核快内核有一定的理论优势,因为它强迫系统程序员采用模块化的方法来构建程序。所以Linux充分吸收了微内核操作系统的优点:提供了一个模块的机制!模块是这样的一个目标文件:它上面的代码可以在运行时链接到内核,或者从内核中解除链接。这种目标代码通常是由一组函数组成,从而来实现文件系统,驱动程序,或其他内核上层功能。

使用模块的主要优点有:

  • 首先它保证了一种模块化的方法

  • 其次它实现了一种平台无关性

  • 接着它可以节省内存使用,当我们不再需要他的时候可以动态的进行解除,同样的在我们需要它的时候可以动态的进行加载

  • 最后他无性能损失!

文件系统概述

Base

我们这样定义文件:

Unix文件是一个以字节序列组成的信息载体!

内核并不负责解释自己文件的内容!

文件或目录名由除/和空字符之外的任意ASCII字符序列组成!大多数文件系统对文件名的长度都会有所限制。(比如说我们的常见的Ext2是255,你不可以把文件名搞得太长!)

这个可以查看自己的ulimit值

与树的根相对应的目录被称为根目录,按照惯例它的名字是/

在同一目录中的文件名并不能相同,而在不同目录中的文件名则可以相同(因为可以通过连接不同的目录文件名从而唯一的标识这个文件)

Unix的每个进程都有一个工作目录(pwd,想你的shell怎么区分你当前在文件系统海洋的何处!)

当标识文件名时引用符号...:它们分别标识当前工作目录,和父目录。如果当前工作目录是根目录,那么这两个目录就是完全一致的!

硬链接和软链接

包含在文件目录的文件名就是一个文件的硬链接,或者简称链接。

在同一目录或不同的目录中同一个文件可以有好几个链接!因此对应几个文件名。

他有两方面的限制:首先他不允许用户给目录创建硬链接,因为这可能会把目录树变为环形图,从而就不可能通过名字来定位一个文件!

其次只有在同一文件系统中的文件才能创建链接!这带来比较大的限制!因为现在操作系统可能包含了多种文件系统!这些文件系统位于不同的磁盘和根目录或分区,用户也许无法知道它们的物理划分!

为了克服这些限制则引入了软链接,或者是符号链接。符号链接是短文件这些文件包含了另一个文件的任意路径名。路径名可以指向位于任意一个操作系统文件系统的任意文件或目录,甚至可以指向一个根本不存在的文件!

Unix文件类型

Unix文件类型可以是以下列的一种:

  • 普通文件

  • 目录

  • 符号链接

  • 面向块的设备文件

  • 面向字符的设备文件

  • 管道

  • 命名管道

  • 套接字

前三种文件类型是所有Unix文件系统的基本类型

文件描述符与索引节点

Unix对文件的内容和描述文件的信息给出了清楚的区分!除了设备文件和特殊文件系统外,每个文件都由字符序列组成!文件内容不包含任何克控制信息!如文件长度或者文件结束符。

我们使用索引节点在内核中表示文件,从而代表一大块文件数据进行管理!

文件也有访问权限和文件模式:文件的潜在用户有三种

  1. 作为文件所有者的

  2. 用户同组用户但是不包括所有者

  3. 所有剩下的用户

文件有三种访问类型:读,写与执行!

文件操作的系统调用

有open, read, write, close等,这里我们暂时不加讨论!(可以参考Linux系统编程手册学习!)

Unix内核简述

我们下面重点来讨论Unix内核,首先我们要说的是进程/内核模式:

如前所述CPU既可以运行在用户态,也可以运行在内核态。当一个程序在用户态下执行的时候,它并不能直接访问内核的数据结构和程序,然而当应用程序在内核态下运行时则不再会有这些限制。当应用程序有所请求内核服务时,内核才会把这个进程流陷入内核态。当完成任务时把进程送回用户态。进程是动态的实体,在系统内通常只有有限的生存期。创建,撤销,同步现有进程的任务都要委托给内核中的一组例程来完成!

内核本身不是一个进程,而是进程的管理者。除用户进程之外:Unix系统还包括几个所谓的内核进程的特权进程。它们具有以下特点:

  • 它们以内核态运行在内核地址空间

  • 他们不与用户直接交互,因此不需要终端设备!

  • 他们通常在系统启动时创建然后一直处于活跃状态直到系统关闭

进程的实现

为了让内核管理进程所有的进程都需要用一个进程描述符进行抽象。当内核暂停一个进程的时候,就会把几个相关处理器寄存器的内容保存在进程描述符里,包括:

  • 程序计数器和栈指针寄存器

  • 通用寄存器

  • 浮点寄存器

  • 包括CPU状态信息的处理器控制器

  • 用来跟踪进程对RAM访问的内存管理寄存器

当内核决定恢复一个进程的时候,他用进程描述符中合适的字段来装载CPU寄存器。因为程序计数器中所存的值指向下一条将要执行的指令,所以进程恐怕停止的地方恢复执行!

可重入内核

所有的Unix内核都是可重入的!

这意味着若干个进程可以同时在内核态下执行,当然在单处理器系统上只有一个进程在真正的执行!但是有许多进程可能在等待CPU或者某一个IO操作完成时在内核台下被阻塞!

提供可重入的一种方式就是编写函数,以便这些函数只能更改局部变量,而不更改全局数据结构。这样的函数叫做可重入函数!

如果一个硬件中断发生,可重入内核可以挂起正在执行的进程,即使这个进程处于内核态

在最简单的情况下CPU从第一条指令到最后一条指令顺序的执行内核控制路径。也就是表示内核处理系统调用异常或中断所执行的指令序列

然而当下述事情发生之一,CPU交错执行内核控制路径:

  • 运行在用户态下的进程调用了一个系统调用,而相应的内核控制路径。正是这个请求没有办法立即得到满足,然后内核控制路径调用调度程序选择一个新的进程。进行调度完成后,进程切换。发生第一个内核控制路径还没有完成,而CPU又重新执行其他的内核控制路径。在这种情况下,两条控制路径代表两个不同的进程。

  • 在执行当执行一个内核控制路径时,CPU检测到了一个异常:比如说访问了一个不在RAM中的页,那么第一个控制路径将会被挂起,而CPU开始执行合适的过程。比如说在这个例子中我们则是给那进程分配一个新页,并从磁盘中读取它的内容。当这个过程结束后第一个控制路径可以恢复执行,在这种情况下两个控制路径代表同一个进程在执行

  • 当CPU在运行一个启用了中断的内核控制路径时,一个硬件中断发生。一个控制路径还没执行完,CPU马上开始执行另一个内核控制路径来处理这个中断。当这个中断处理程序终止时,第一个内核控制路径恢复。在这个情况下两条内核控制路径运行是同一进程的可执行上下文。所花费的系统CPU时间都算给了这个进程。然而中断处理程序无需代表这个进程运行

  • 在支持抢占式调度的内核中,CPU正在运行。但是被一个更加高级的进程加入就绪队列。中断发生,调度开始。第一个内核控制路径并没有执行完。CPU代表高优先级进程又开始了另一个内核控制路径,只有把内核编译成支持抢占式调度后才有可能会出现这种情况!(你放心,咱们就是这个hhh)

进程地址空间

每个进程运行在它的私有地址空间!在用户态下运行的进程涉及到私有栈,数据区和代码区。

当在内核态运行时,进程访问内核的数据区,代码区。但是使用的是另外的私有栈。尽管看起来每个进程都在访问他们自己的私有栈,但是为了更好的进程间通信,有时进程之间也会共享部分地址空间!

Linux支持映射内存(mmap)系统调用,该系统调用将允许存放在块设备上的文件或信息映射到进程的部分地址空间。这为正常的读写传送数据方式提供了另一种选择

同步和临界区

实现可重入的内核需要利用同步机制,如果内核控制路径对某个内核数据结构进行操作被挂起时,那么其他内核控制路径就不应该对这个数据结构进行操作,否则会破坏一致性状态!

如何同步内核控制路径呢最彻底的办法就是使用非抢占式的内核(Weird huh?),其次就是禁止中断,再就是使用信号量自旋锁等内核机制来防止竞争条件!

在我们使用防止竞争条件的内核上锁机制时,需要避免死锁情况!在这里不予详细讨论!

信号与进程之间的通信

Unix信号提供了一种把系统事件报告给进程的一种机制。

有两种系统事件:

  • 异步通告

  • 同步错误或异常

如果进程并没有指定如何处理信号时,内核会按照信号的编号进行默认操作。有可能有以下五种默认操作:

终止进程

将执行上下文和进程地址空间的内容写入一个文件,并且终止进程

忽略信号

挂起进程

如果进程曾被暂停,则恢复它

进程管理

Unix在进程和它正在执行的程序之间做出了清晰的划分!fork和_exit这两个系统调用分别用来创建一个进程和终止,与exec类系统调用则是装入一个全新的程序!以及还有僵死进程。如果父进程丢失了跟踪子进程的情况,那么这个子进程就认为僵尸进程。内核会检查子进程是否终止。引入僵死进程的特殊状态是为了表示终止的进程。很多内核也实现了waitpid系统调用,让父进程可以显示的等待一个特殊的子进程。对于那些已经成为僵尸进程的进程,他们将会被一个以init的特殊系统进程收养进行清除!

内存管理

虚拟内存

所有新进的Unix系统都提供了一种有用的抽象:叫做虚拟内存!

它作为一种逻辑层处于应用程序的内在请求与硬件内存单元管理单元之间。虚拟内存有很多用途与优点,它可以让

  • 若干进程并发执行

  • 应用程序所需内存大于可用物理内存时也可以运行

  • 程序集有部分代码装入内存时进程可以执行

  • 允许每个进程访问可用物理内存的子集进程

  • 可以共享库数据或程序或函数等一个单独内存映像

  • 程序是可定位的!也就是说我们可以把程序放在物理内存中的任何地方

  • 程序员可以编写与机器无关的代码!因为他们根本不需要关心物理内存的组织结构,也就是说他把物理内存进行了一层抽象

虚拟内存子系统的主要成分是虚拟地址空间进程所用的一组内存地址。不同于物理内存地址!

随机访问存储器的使用

随机访问存储器的使用分为两个部分:

一部分被专门用来存放内核映像,另一部分则由虚拟内存系统来进行处理:

  • 用来满足内核对缓冲区描述服务及其它动态内核数据结构的请求

  • 满足进程对一般性内存区的请求即对文件内存映射的请求

  • 借助于高速缓存从磁盘或者其他缓冲设备获得较好的性能内存

内核分配器

它是一个子系统,试图满足系统中所有部分对内存的请求!其中一些请求可能来自内核其他子系统。他们需要一些内核使用的内存,还有一些请求则是来自用户程序的系统调用,以用来增加用户程序进程的地址空间!

一个好的内核内存分配器需要具有以下特点:

它必须快,实际上这是最重要的属性!因为它为所有的内核子系统所调用

必须把内存的浪费减到最少

必须努力减轻内存的碎片问题

必须能与其他内存管理子系统进行合作,以便借用和释放页框

现在已经提出了好几种内核内存分配器进程!这个可以查询其他资料!

进程的虚拟地址空间处理

进程的虚拟地址空间包括了进程可以引用的所有虚拟内存地址,内核通常用一组内存区描述符描述进程!虚拟地址空间内核分配给进程的虚拟地址空间主要有以下这几个部分:

  • 组成程序的可执行代码

  • 程序的初始化和未初始化的数据

  • 初始程序栈

  • 所需共享库的可执行代码和数据

高速缓存

物理内存的一大优势就是用来磁盘和其它块设备的高速缓存!因为磁盘访问非常的慢,这与访问内存相比实在太长!因此磁盘通常是影响系统性能的一大瓶颈所在,最早的Unix系统中早就已经实现了一个策略就是对推迟写磁盘的时间,我们将在后续的实现中看看Linux是如何做到的!


网站公告

今日签到

点亮在社区的每一天
去签到