- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章深入解析Linux内核及其相关架构的依赖关系由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
linux kernel 成功的两个原因:
灵活的架构设计使得大量的志愿开发者能够很容易加入到开发过程中,
每个子系统(尤其是那些需要改进的)都具备良好的可扩展性.
正是这两个原因使得linux kernel可以不断进化和改进.
1、linux内核在整个计算机系统中的位置 #FormatImgID_0# 。
分层结构的原则:
the dependencies between subsystems are from the top down: layers pictured near the top depend on lower layers, but subsystems nearer the bottom do not depend on higher layers. 。
这种子系统之间的依赖性只能是从上到下,也就是图中顶部的子系统依赖底部的子系统,反之则不行.
2、内核的作用 。
虚拟化(抽象),将计算机硬件抽象为一台虚拟机,供用户进程process使用;进程运行时完全不需要知道硬件是如何工作的,只要调用 linux kernel 提供的虚拟接口virtual interface即可.
多任务处理,实际上是多个任务在并行使用计算机硬件资源,内核的任务是仲裁对资源的使用,制造每个进程都以为自己是独占系统的错觉.
ps:进程上下文切换就是要换掉程序状态字、换掉页表基地址寄存器的内容、换掉 current 指向的 task_struct 实例、换掉 pc ——>也就换掉了进程打开的文件(通过 task_struct 的 files 可以找到)、换掉了进程内存的执行空间(通过 task_struct 的 mem 可以找到),
3、linux内核的整体架构 。
中心系统是进程调度器process scheduler,sched:所有其余的子系统都依赖于进程调度器,因为其余子系统都需要阻塞和恢复进程。当一个进程需要等待一个硬件动作完成时,相应子系统会阻塞这个进程;当这个硬件动作完成时,子系统会将这个进程恢复:这个阻塞和恢复动作都要依赖于进程调度器完成.
上图中的每一个依赖箭头都有原因:
进程调度器依赖内存管理器memory manager:进程恢复执行时,需要依靠内存管理器分配供它运行的内存.
ipc 子系统依赖于内存管理器:共享内存机制是进程间通信的一种方法,运行两个进程利用同一块共享的内存空间进行信息传递.
vfs 依赖于网络接口network interface:支持 nfs 网络文件系统,
vfs 依赖于内存管理器:支持 ramdisk 设备 。
内存管理器依赖于 vfs,因为要支持交换swapping,可以将暂时不运行的进程换出到磁盘上的交换分区swap,进入挂起状态.
4、高度模块化设计的系统,利于分工合作.
只有极少数的程序员需要横跨多个模块开展工作,这种情况确实会发生,仅发生在当前系统需要依赖另一个子系统时,
硬件设备驱动hardware device drivers、文件系统模块logical filesystem modules、网络设备驱动network device drivers和网络协议模块network protocol modules这四个模块的可扩展性最高.
5、系统中的数据结构 。
任务列表task list 。
进程调度器针对每个进程维护一个数据结构 task_struct;所有的进程用链表管理,形成 task list;进程调度器还维护一个 current 指针指向当前正在占用 cpu 的进程.
内存映射memory map 。
内存管理器存储每个进程的虚拟地址到物理地址的映射;并且也提供了如何换出特定的页,或者是如何进行缺页处理。这些信息存放在数据结构 mm_struct 中。每个进程都有一个 mm_struct 结构,在进程的 task_struct 结构中有一个指针 mm 指向次进程的 mm_struct 结构.
在 mm_struct 中有一个指针 pgd,指向该进程的页目录表(即存放页目录首地址)——>当该进程被调度时,此指针被换成物理地址,写入控制寄存器 cr3(x86体系结构下的页基址寄存器) 。
i-nodes 。
vfs 通过 inodes 节点表示磁盘上的文件镜像,inodes 用于记录文件的物理属性。每个进程都有一个 files_struct 结构,用于表示该进程打开的文件,在 task_struct 中有个 files 指针。使用 inodes 节点可以实现文件共享。文件共享有两种方式:(1)通过同一个系统打开文件 file 指向同一个 inodes 节点,这种情况发生于父子进程间;(2)通过不同系统打开文件指向同一个 inode 节点,举例有硬链接;或者是两个不相关的指针打开同一个文件.
数据连接data connection 。
内核中所有的数据结构的根都在进程调度器维护的任务列表链表中。系统中每个进程的的数据结构 task_struct 中有一个指针 mm 指向它的内存映射信息;也有一个指针 files 指向它打开的文件(用户打开文件表);还有一个指针指向该进程打开的网络套接字.
6、子系统架构 。
1. 进程调度器process scheduler 架构 。
(1)目标 。
进程调度器是 linux kernel 中最重要的子系统。系统通过它来控制对 cpu 的访问——不仅仅是用户进程对 cpu 的访问,也包括其余子系统对 cpu 的访问.
(2)模块 。
调度策略模块scheduling policy module:决定哪个进程获得对 cpu 的访问权;调度策略应该让所有进程尽可能公平得共享 cpu.
体系结构相关模块architecture-specific module设计一组统一的抽象接口来屏蔽特定体系接口芯片的硬件细节。这个模块与 cpu 交互以阻塞和恢复进程。这些操作包括获取每个进程需要保存的寄存器和状态信息、执行汇编代码来完成阻塞或者恢复操作.
体系结构无关模块architecture-independent module与调度策略模块交互将决定下一个执行的进程,然后调用体系结构相关的代码去恢复那个进程的执行。不仅如此,这个模块还会调用内存管理器的接口来确保被阻塞的进程的内存映射信息被正确得保存起来.
系统调用接口模块system call interface允许用户进程访问 linux kernel 明确暴露给用户进程的资源。通过一组定义合适的基本上不变的接口(posix 标准),将用户应用程序和 linux 内核解耦,使得用户进程不会受到内核变化的影响.
(3)数据表示 。
调度器维护一个数据结构——task list,其中的元素时每个活动的进程 task_struct 实例;这个数据结构不仅仅包含用来阻塞和恢复进程的信息,也包含额外的计数和状态信息。这个数据结构在整个 kernel 层都可以公共访问.
(4)依赖关系、数据流、控制流 。
正如前面提到过的,调度器需要调用内存管理器提供的功能,去为需要恢复执行的进程选择合适的物理地址,正因为如此,所以进程调度器子系统依赖于内存管理子系统。当其他内核子系统需要等待硬件请求完成时,它们都依赖于进程调度子系统进行进程的阻塞和恢复。这种依赖性通过函数调用和访问共享的 task list 数据结构来体现。所有的内核子系统都要读或者写代表当前正在运行进程的数据结构,因此形成了贯穿整个系统的双向数据流.
除了内核层的数据流和控制流,os 服务层还给用户进程提供注册定时器的接口。这形成了由调度器对用户进程的控制流。通常唤醒睡眠进程的用例不在正常的控制流范围,因为用户进程无法预知何时被唤醒。最后,调度器与 cpu 交互来阻塞和恢复进程,这又形成它们之间的数据流和控制流——cpu 负责打断当前正在运行的进程,并允许内核调度其他的进程运行.
2. 内存管理器memory manager 架构 。
(1)目标 。
内存管理模块负责控制进程如何访问物理内存资源。通过硬件内存管理系统(mmu)管理进程虚拟内存和机器物理内存之间的映射。每一个进程都有自己独立的虚拟内存空间,所以两个进程可能有相同的虚拟地址,但是它们实际上在不同的物理内存区域运行。mmu 提供内存保护,让两个进程的物理内存空间不互相干扰。内存管理模块还支持交换——将暂时不用的内存页换出到磁盘上的交换分区,这种技术让进程的虚拟地址空间大于物理内存的大小。虚拟地址空间的大小由机器字长决定.
(2)模块 。
architecture specific module提供访问物理内存的虚拟接口,
架构无关模块architecture independent module负责每个进程的地址映射以及虚拟内存交换。当发生缺页错误时,由该模块负责决定哪个内存页应该被换出内存——因为这个内存页换出选择算法几乎不需要改动,所以这里没有建立一个独立的策略模块.
系统调用接口system call interface为用户进程提供严格的访问接口(malloc 和 free;mmap 和 ummap)。这个模块允许用进程分配和释放内存、执行内存映射文件操作.
(3)数据表示 。
内存管理存放每个进程的虚拟内存到物理内存的映射信息。这种映射信息存放在 mm_struct 结构实例中,这个实例的指针又存放在每个进程的 task_struct 中。除了存放映射信息,数据块中还应该存放关于内存管理器如何获取和存储页的信息。例如:可执行代码能够将可执行镜像作为备份存储;但是动态申请的数据则必须备份到系统页中。(这个没看懂,请高手解惑?) 。
最后,内存管理模块还应该存放访问和技术信息,以保证系统的安全.
(4)依赖关系、数据流和控制流 。
内存管理器控制物理内存,当页面失败 page fault 发生时,接受硬件的通知(缺页中断)—— 这意味着在内存管理模块和内存管理硬件之间存在双向的数据流和控制流。内存管理也依赖文件系统来支持交换和内存映射 i/o——这种需求意味着内存管理器需要调用对文件系统提供的函数接口procedure calls,往磁盘中存放内存页和从磁盘中取内存页。因为文件系统请求非常慢,所以在等待内存页被换入之前,内存管理器要让进程需要进入休眠——这种需求让内存管理器调用进程调度器的接口。由于每个进程的内存映射存放在进程调度器的数据结构中,所以在内存管理器和进程调度器之间也有双向的数据流和控制流。用户进程可以建立新的进程地址空间,并且能够感知缺页错误——这里需要来自内存管理器的控制流。一般来说没有用户进程到内存管理器的数据流,但是用户进程却可以通过 select 系统调用,从内存管理器获取一些信息.
3. 虚拟文件系统virtual file system 架构 。
(1)目标 。
虚拟文件系统为存储在硬件设备上数据提供统一的访问接口。可以兼容不同的文件系统(ext2,ext4,ntf等等)。计算机中几乎所有的硬件设备都被表示为一个通用的设备驱动接口。逻辑文件系统促进与其他操作系统标准的兼容性,并且允许开发者以不同的策略实现文件系统。虚拟文件系统更进一步,允许系统管理员在任何设备上挂载任何逻辑文件系统。虚拟文件系统封装物理设备和逻辑文件系统的细节,并且允许用户进程使用统一的接口访问文件.
除了传统的文件系统目标,vfs 也负责装载新的可执行文件。这个任务由逻辑文件系统模块完成,使得 linux 可以支持多种可执行文件.
(2)模块 。
device driver module 。
设备独立接口模块device independent interface:提供所有设备的同一视图 。
逻辑文件系统logical file system:针对每种支持的文件系统 。
系统独立接口system independent interface提供硬件资源和逻辑文件系统都无关的接口,这个模块通过块设备节点或者字符设备节点提供所有的资源.
系统调用模块system call interface提供用户进程对文件系统的统一控制访问。虚拟文件系统为用户进程屏蔽了所有特殊的特性.
(3)数据表示 。
所有文件使用 inode 表示。每个 inode 都记录一个文件在硬件设备上的位置信息。不仅如此,inode 还存放着指向逻辑文件系统模块和设备驱动的的函数指针,这些指针能够执行具体的读写操作。通过按照这种形式(就是面向对象中的虚函数的思想)存放函数指针,具体的逻辑文件系统和设备驱动可以向内核注册自己而不需要内核依赖具体的模块特性.
(4)依赖关系、数据流和控制流 。
一个特殊的设备驱动是 ramdisk,这个设备在主存中开辟一片区域,并把它当成持久性存储设备使用。这个设备驱动使用内存管理模块完成任务,所以在 vfs 与对内存管理模块存在依赖关系(图中的依赖关系反了,应该是 vfs 依赖于内存管理模块)、数据流和控制流.
逻辑文件系统支持网络文件系统。这个文件系统像访问本地文件一样,从另一台机器上访问文件。为了实现这个功能,一种逻辑文件系统通过网络子系统完成它的任务——这引入了 vfs 对网络子系统的一个依赖关系以及它们之间的控制流和数据流.
正如前面提到的,内存管理器使用 vfs 完成内存交换功能和内存映射 i/o。另外,当 vfs 等待硬件请求完成时,vfs 需要使用进程调度器阻塞进程;当请求完成时,vfs 需要通过进程调度器唤醒进程。最后,系统调用接口允许用户进程调用来存取数据。不像前面的子系统,vfs 没有提供给用户注册不明确调用的机制,所以没有从vfs到用户进程的控制流.
4. 网络接口network interface 架构 。
(1)目标 。
网络子系统让 linux 系统能够通过网络与其他系统相连。这个子系统支持很多硬件设备,也支持很多网络协议。网络子系统将硬件和协议的实现细节都屏蔽掉,并抽象出简单易用的接口供用户进程和其他子系统使用——用户进程和其余子系统不需要知道硬件设备和协议的细节.
(2)模块 。
网络设备驱动模块network device drivers 。
设备独立接口模块device independent interface module提供所有硬件设备的一致访问接口,使得高层子系统不需要知道硬件的细节信息.
网络协议模块network protocol modules负责实现每一个网络传输协议,例如:tcp,udp,ip,http,arp等等~ 。
协议无关模块protocol independent interface提供独立于具体协议和具体硬件设备的一致性接口。这使得其余内核子系统无需依赖特定的协议或者设备就能访问网络.
系统调用接口模块system calls interface规定了用户进程可以访问的网络编程api 。
(3)数据表示 。
每个网络对象都被表示为一个套接字socket。套接字与进程关联的方法和 inode 节点相同。通过两个 task_struct 指向同一个套接字,套接字可以被多个进程共享.
(4)数据流,控制流和依赖关系 。
当网络子系统需要等待硬件请求完成时,它需要通过进程调度系统将进程阻塞和唤醒——这形成了网络子系统和进程调度子系统之间的控制流和数据流。不仅如此,虚拟文件系统通过网络子系统实现网络文件系统(nfs)——这形成了 vfs 和网络子系统指甲的数据流和控制流.
7、结论 。
1、linux 内核是整个 linux 系统中的一层。内核从概念上由五个主要的子系统构成:进程调度器模块、内存管理模块、虚拟文件系统、网络接口模块和进程间通信模块。这些模块之间通过函数调用和共享数据结构进行数据交互.
2、linux 内核架构促进了他的成功,这种架构使得大量的志愿开发人员可以合适得分工合作,并且使得各个特定的模块便于扩展.
可扩展性一:linux 架构通过一项数据抽象技术使得这些子系统成为可扩展的——每个具体的硬件设备驱动都实现为单独的模块,该模块支持内核提供的统一的接口。通过这种方式,个人开发者只需要和其他内核开发者做最少的交互,就可以为 linux 内核添加新的设备驱动.
可扩展性二:linux 内核支持多种不同的体系结构。在每个子系统中,都将体系结构相关的代码分割出来,形成单独的模块。通过这种方法,一些厂家在推出他们自己的芯片时,他们的内核开发小组只需要重新实现内核中机器相关的代码,就可以讲内核移植到新的芯片上运行.
最后此篇关于深入解析Linux内核及其相关架构的依赖关系的文章就讲到这里了,如果你想了解更多关于深入解析Linux内核及其相关架构的依赖关系的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
Linux 管道可以缓冲多少数据?这是可配置的吗? 如果管道的两端在同一个进程中,但线程不同,这会有什么不同吗? 请注意:这个“同一个进程,两个线程”的问题是理论上的边栏,真正的问题是关于缓冲的。 最
我找到了here [最后一页] 一种有趣的通过 Linux 启动 Linux 的方法。不幸的是,它只是被提及,我在网上找不到任何有用的链接。那么有人听说过一种避免引导加载程序而使用 Linux 的方法
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我试图了解 ld-linux.so 如何在 Linux 上解析对版本化符号的引用。我有以下文件: 测试.c: void f(); int main() { f(); } a.c 和 b.c:
与 RetroPie 的工作原理类似,我可以使用 Linux 应用程序作为我的桌面环境吗?我实际上并不需要像实际桌面和安装应用程序这样的东西。我只需要一种干净简单的方法来在 RaspberryPi 上
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 10 年前。 Improve thi
有什么方法可以覆盖现有的源代码,我应该用 PyQt、PyGTK、Java 等从头开始构建吗? 最佳答案 如果您指的是软件本身而不是它所连接的存储库,那么自定义应用程序的方法就是 fork 项目。据我所
我的情况是:我在一个磁盘上安装了两个 linux。我将第一个安装在/dev/sda1 中,然后在/dev/sda2 中安装第二个然后我运行第一个系统,我写了一个脚本来在第一个系统运行时更新它。
我在 i2c-0 总线上使用地址为 0x3f 的系统监视器设备。该设备在设备树中配置有 pmbus 驱动程序。 问题是,加载 linux 内核时,这个“Sysmon”设备没有供电。因此,当我在总线 0
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 11 年前。 Improve thi
我正试图在 linux 模块中分配一大块内存,而 kalloc 做不到。 我知道唯一的方法是使用 alloc_bootmem(unsigned long size) 但我只能从 linux 内核而不是
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
我有 .sh 文件来运行应用程序。在该文件中,我想动态设置服务器名称,而不是每次都配置。 我尝试了以下方法,它在 CentOS 中运行良好。 nohup /voip/java/jdk1.8.0_71/
我是在 Linux 上开发嵌入式 C++ 程序的新手。我有我的 Debian 操作系统,我在其中开发和编译了我的 C++ 项目(一个简单的控制台进程)。 我想将我的应用程序放到另一个 Debian 操
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 4 年前。 Improve this ques
我使用4.19.78版本的稳定内核,我想找到带有企鹅二进制数据的C数组。系统启动时显示。我需要在哪里搜索该内容? 我在 include/linux/linux_logo.h 文件中只找到了一些 Log
我知道可以使用 gdb 的服务器模式远程调试代码,我知道可以调试针对另一种架构交叉编译的代码,但是是否可以更进一步,从远程调试 Linux 应用程序OS X 使用 gdbserver? 最佳答案 当然
是否有任何可能的方法来运行在另一个 Linux 上编译的二进制文件?我知道当然最简单的是在另一台机器上重建它,但假设我们唯一能得到的是一个二进制文件,那么这可能与否? (我知道这可能并不容易,但我只是
我是一名优秀的程序员,十分优秀!