Linux内核全注释阅读 Linux内核体系结构1

Linux内核全注释阅读 Linux内核体系结构1

从linux内核代码开始,逐步阅读linux内核的主要模块,了解linux内核的基本架构,了解linux内核的主要功能,了解linux内核的模块化设计思想是这个系列的主要内容,也顺带可以当作复习操作系统。 书中是从liunx 0.11版本进行学习的,根目录如下图

alt text

这章概要地描述了 Linux 操作系统的体系结构、内核源代码文件放置的组织结构以及每个文件大致功能。还介绍了 Linux 对物理内存的使用分配方式、内核的几种堆栈及其使用方式和虚拟线性地址的使用分配。最后开始注释内核程序包中 Linux/日录下的所看到的第一个文件,也即内核代码的总体Makefile 文件的内容。该文件是所有内核源程序的编译管理配置文件,供编译管理工具软件 make 使用。 大体来说,一个操作系统主要由四个主要部分构成,分别是用户程序,操作系统服务,操作系统内核以及硬件系统

alt text

Linux 内核的主要用途就是为了与计算机硬件进行交互,实现对硬件部件的编程控制和接口操作,调度对硬件资源的访问,并为计算机上的用户程序提供一个高级的执行环境和对硬件的虚拟接口。

Linux内核模式

操作系统内核的结构模式主要分为:整体的单内核模式和层次性的微内核模式 单内核将操作系统的主要功能(如进程管理、内存管理、文件系统、设备驱动等)集中在一个大的内核空间中,所有模块运行在内核态,共享同一个地址空间。由于所有模块都在同一个地址空间中运行,模块之间的通信通过直接函数调用实现,效率较高。由于所有功能都集成在一个内核中,单内核的设计通常较为紧凑,模块之间的耦合度较高。单内核通常以单个静态二进制文件的形式存储在磁盘上,启动时整个内核被加载到内存中。 微内核将操作系统的核心功能(如基本的内存管理、进程间通信、同步原语、中断管理等)放在内核中,而其他功能(如文件系统、设备驱动、网络协议栈等)作为独立的服务运行在用户空间。每个服务都是一个独立的进程,运行在各自的地址空间中,彼此之间通过进程间通信(IPC)进行交互。微内核“最小化内核”,只将最核心、最必要的功能放入内核,其他功能尽可能移到用户空间。 Liunx是使用单内核模式的 在单内核模式的系统中,操作系统所提供服务的流程为:应用主程序使用指定的参数值执行系统调 用指令(int x80),使 CPU 从用户态(User Mode)切换到核心态(Kernel Model),

用户态不能直接使用系统资源,也不能改变 CPU 的工作状态,并且只能访问这个用户程序自己的存储空间。 核心态可以访问整个系统的资源,可以改变 CPU 的工作状态,并且可以访问所有用户程序的存储空间。 这有关CPU指令集权限是CPU 实现软件指挥硬件执行的媒介,具体来说每一条汇编语句都对应了一条 CPU 指令;CPU的操作权限如果可以直接进行I/O操作一类权限高的就是核心态,反之就是用户态。 这也比较好理解,因为可能应用程序或者用户随随便便就把电脑整蓝屏了,这并不好玩

然后操作系统根据具体的参数值调用特定的系统调用服务程序,而这些服务程序则根据需要再底层的一些支持函数以完成特定的功能。在完成了应用程序所要求的服务后,操作系统又从核心态切换回用户态,返回到应用程序中继 续执行后面的指令。因此概要地讲,单内核模式的内核也可粗略地分为三个层次:调用服务的主程序层、执行系统调用的服务层和支持系统调用的底层函数。

alt text

Linux 内核系统体系结构

Linux 内核主要由 5 个模块构成,它们分别是:进程调度模块、内存管理模块、文件系统模块、进 程间通信模块和网络接口模块。 进程调度模块用来负责控制进程对 CPU 资源的使用。所采取的调度策略是各进程能够公平合理地访问 CPU,同时保证内核能及时地执行硬件操作。 内存管理模块用于确保所有进程能够安全地共享机器主内存区,同时,内存管理模块还支持虚拟内存管理方式,使得 Linux 支持进程使用比实际内存空间更多的内存容量。并可以利用文件系统把暂时不用的内存数据块会被交换到外部存储设备上去,当需要时再交换回来。 文件系统模块用于支持对外部设备的驱动和存储。 虚拟文件系统模块通过向所有的外部存储设备提供一个通用的文件接口,隐藏了各种硬件设备的不同细节。从而提供并支持与其它操作系统兼容的多种文件系统格式。 进程间通信模块子系统用于支持多种进程间的信息交换方式。网络接口模块提供对多种网络通信标准的访问并支持许多网络硬件。

alt text

所有的模块都与进程调度模块存在依赖关系。因为它们都需要依靠进程调度程序来挂起(暂停)或重新运行它们的进程。通常,一个模块会在等待硬件操作期间被挂起,而在操作完成后才可继续运行。

EXP:当一个进程试图将一数据块写到软盘上去时,软盘驱动程序就可能在启动软盘旋转期间将该进程置为挂起等待状态,而在软盘进入到正常转速后再使得该进程能继续运行,避免资源浪费。另外 3 个模块也是由于类似的原因而与进程调度模块存在依赖关系

这里可以关联到操作系统中学到的进程以及避免死锁的概念,进程调度就是为了避免死锁而提出的一种策略。进程有三种基本状态:运行,阻塞,就绪。运行状态进程已获得CPU,其程序正在执行的状态。阻塞状态正在执行的进程由于发生某事件(如IO请求、申请缓冲区失败,资源不足等)暂时无法继续执行的状态。就绪状态进程已经分配除CPU以外的所有必要资源,只要在获得CPU就可立刻执行。

进程调度子系统需要使用内存管理器来调整一特定进程所使用的物理内存空间。进程间通信子系统则需要依靠内存管理器来支持共享内存通信机制。这种通信机制允许两个进程访问内存的同一个区域以进行进程间信息的交换。虚拟文件系统也会使用网络接口来支持网络文件系统(NFS),同样也能使用内存管理子系统来提供内存虚拟盘(ramdisk)设备。而内存管理子系统也会使用文件系统来支持内存数据块的交换操作。

alt text

中断机制

linux中 中断机制分为硬件中断以及软件中断(异常)每个中断是由 0-255之间的一个数字来标识。 在使用 80X86 组成的 PC 机中,采用了两片 8259A 可编程中断控制芯片。每片可以管理 8 个中断源。通过多片的级联方式,能构成最多管理 64 个中断向量的系统。在 PC/AT 系列兼容机中,使用了两片 8259A芯片,共可管理 15 级中断向量。其级连示意图见图 2-5 所示。其中从芯片的 INT 引脚连接到主芯片的IR2 引脚上。主 8259A 芯片的端口基地址是 0x20,从芯片是 0xA0。 在总线控制器控制下,8259A 芯片可以处于编程状态和操作状态。编程状态是 CPU 使用 IN 或 OUT 指令对 8259A 芯片进行初始化编程的状态。一旦完成了初始化编程,芯片即进入操作状态,此时芯片即可随时响应外部设备提出的中断请求(IRQ0 – IRQ15)。通过中断判优选择,芯片将选中当前最高优先级的中断请求作为中断服务对象,并通过 CPU 引脚 INT 通知 CPU 外中断请求的到来,CPU 响应后,芯片从数据总线 D7-D0 将编程设定的当前服务对象的中断号送出,CPU 由此获取对应的中断向量值,并执行中断服务程序。 对于中断 int0–int31(0x00–0x1f),每个中断的功能由 Intel 公司固定设定或保留用, 属于软件中断,但 Intel 公司称之为异常。因为这些中断是在 CPU 执行指令时探测到异常情况而引起的。通常还可分为故障(Fault)和陷阱(traps)两类。中断 int32–int255 (0x20–0xff)可以由用户自己设定。在 Linux 系统中,则将 int32–int47(0x20–0x2f)对应于 8259A 中断控制芯片发出的硬件中断请求信号IRQ0-IRQ15,并把程序编程发出的系统调用(system_call)中断设置为 int128(0x80)。

系统定时

在 Linux 0.11 内核中,定时器中断的处理机制是操作系统实现多任务调度和时间管理的关键部分。以下是对其工作原理的总结: 定时器中断的初始化 Intel 8253 定时芯片 被设置为每隔 10 毫秒 发出一个时钟中断信号(IRQ0),这个时间间隔称为 1 个系统滴答(tick)。 每次时钟中断发生时,系统会调用 时钟中断处理程序(timer_interrupt)。

时钟中断处理程序(timer_interrupt)

jiffies 变量:

jiffies 是一个全局变量,用于记录自系统启动以来经过的时钟滴答数。 每次时钟中断发生时,jiffies 的值会加 1。

获取当前特权级(CPL):

从被中断程序的段选择符中获取当前特权级(CPL),用于判断中断发生时进程的运行状态(内核态或用户态)。

调用 do_timer() 函数:

将 CPL 作为参数传递给 do_timer() 函数,进一步处理时间统计和调度。

do_timer() 函数的功能

时间统计:

根据 CPL 的值,更新当前进程的运行时间统计: 如果 CPL = 0(内核态),则将进程的内核态运行时间 stime 加 1。 如果 CPL > 0(用户态),则将进程的用户态运行时间 utime 加 1。

定时器处理:

如果系统中注册了定时器,则遍历定时器链表,对每个定时器的时间值进行递减。 如果某个定时器的时间值递减到 0,则调用该定时器的处理函数。 进程时间片处理: 将当前进程的时间片减 1。 如果时间片仍未用完(时间片 > 0),则直接退出 do_timer(),继续运行当前进程。 如果时间片已用完(时间片 = 0),则根据当前进程的特权级进行进一步处理。

进程调度

用户态进程:

如果被中断的进程运行在用户态(CPL > 0),则调用 调度程序 schedule(),切换到其他进程运行。

内核态进程:

如果被中断的进程运行在内核态(CPL = 0),则 do_timer() 直接退出,继续运行当前进程。这是因为内核态进程通常涉及关键操作,不能被随意抢占。

定时器中断 是 Linux 0.11 内核实现多任务调度和时间管理的核心机制。 每次时钟中断发生时,系统会更新全局时间变量 jiffies,并根据当前进程的特权级统计其运行时间。 如果当前进程的时间片用完,且运行在用户态,则会触发进程调度,切换到其他进程运行。 内核态进程不会被抢占,以确保内核代码的稳定性和安全性。 因此这样的处理方式决定了 Linux 系统在内核态运行时不会被调度程序切换。内核态程序是不可抢占的,但当处于用户态程序中运行时则是可以被抢占的。 在我的理解就是每隔10毫秒,系统都会向CPU发出一次时钟中断,然后CPU调用时钟中断处理程序,处理系统时间的统计和调度,判断是否有中断或者是否需要改变进程。




Enjoy Reading This Article?

Here are some more articles you might like to read next: