Linux 内核深入理解 - 绪论

目录

多用户系统

进程

内核体系架构

文件系统概述

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是如何做到的!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/4320.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

漏洞端到端管理小总结

漏洞端到端管理最佳实践涵盖了从漏洞的发现、分析、修复到监控的整个过程,确保组织能够及时发现并应对安全威胁。以下是一些建议的最佳实践: 发现与评估: 资产识别与分类:对组织的所有网络资产进行彻底清查,包括但不限…

redission原理笔记

加锁成功的线程,将UUID和线程id和key绑定, 加锁成功后,内部有一个看门狗机制,每隔十秒看下当前线程是否还持有锁,延长生存时间。 没有获取锁的就一直自旋等待,直到超时。 如果redis是主从同步的&#xff0…

MySQL__深度分页问题

文章目录 😊 作者:Lion J 💖 主页: https://blog.csdn.net/weixin_69252724 🎉 主题: MySQL__深度分页问题) ⏱️ 创作时间:2024年04月27日 ———————————————— …

自动驾驶新书“五一”节马上上市了

我和杨子江教授合写的《自动驾驶系统开发》终于在清华大学出版社三校稿之后即将在五一节后出版。 清华大学汽车学院的李克强教授和工程院院士撰写了序言。 该书得到了唯一华人图灵奖获得者姚期智院士、西安交大管晓宏教授和科学院院士以及杨强教授和院士等的推荐,…

不使用加减运算符实现整数加和减

文章目录 进位 进位 加粗 最近想出了不使用运算符实现加与减 首先按位与找出的是需不需要进位 按位与是两边同时为1,则为1,那么如果两边同时为1的话,是不是就该进位?所以我们用按位与来判断是否需要进位 然后再按位异或找出不同的位数 按位异或是两边不相等,也就是1 和 0的时…

[每周一更]-(第94期):认识英伟达显卡

英伟达显卡:引领图形计算的领先者,显卡也常称为GPU(图形处理器 Graphics processing unit),是一种专门在个人电脑、工作站、游戏机和一些移动设备(如平板电脑、智能手机等)上执行绘图运算工作的…

CVPR2022 ACmix 注意力模块 | On the Integration of Self-Attention and Convolution

论文名称:《On the Integration of Self-Attention and Convolution》 论文地址:2111.14556 (arxiv.org) 卷积和自注意力是两种强大的表示学习技术,通常被认为是两种截然不同的并列方法。在本文中,我们展示了它们之间存在一种强烈…

排序试题解析(二)

8.4.3 01.在以下排序算法中,每次从未排序的记录中选取最小关键字的记录,加入已排序记录的 末尾,该排序算法是( A ). A.简单选择排序 B.冒泡排序 C.堆排序 D.直接插入排序 02.简单选择排序算法的比较次数和移动次数分别为( C )。…

微信小程序手写文件解决日期少一天且格式无法切割问题

编译环境 微信开发者工具 问题 在小程序中无法实现对日期的切割,并且可能会出现日期少一天的问题,这个问题可以由后端进行解决,也可以前端,这里用了前端新建一个wxs转换文件进行解决。 比如数据库中的数据是2024-03-02… 但是返…

js动态设置css主题(Style-setProperty)

hex颜色转RGB hex2Rgb(str) {str str.replace("#", "");const hxs str.match(/../g);for (let index 0; index < 3; index) hxs[index] parseInt(hxs[index], 16);return hxs; } RGB转HXS rgb2hex(r,g,b){const hexs [r.toString(16), g.toString…

UE5蓝图 函数勾选线程安全的意义,我在动画蓝图状态机中调用了函数(gpt答复分享)

在Unreal Engine中&#xff0c;蓝图函数的“线程安全”选项通常用于确定该函数是否可以安全地在多线程环境下调用。线程安全意味着函数在执行时不会导致数据竞争&#xff0c;状态错误&#xff0c;或其他并发问题。如果一个函数是线程安全的&#xff0c;它就可以在不同的线程中同…

【小沐学Java】VSCode搭建Java开发环境

文章目录 1、简介2、安装VSCode2.1 简介2.2 安装 3、安装Java SDK3.1 简介3.2 安装3.3 配置 4、安装插件Java Extension Pack4.1 简介4.2 安装4.3 配置 结语 1、简介 2、安装VSCode 2.1 简介 Visual Studio Code 是一个轻量级但功能强大的源代码编辑器&#xff0c;可在桌面上…

如何使用小浪助手快速下载学浪中的视频?

今天给大家准备好了一个工具&#xff0c;小浪助手&#xff0c;它可以帮你们快速下载学浪中的视频 小浪助手我已经打包好了&#xff0c;有需要自己取一下 学浪下载工具链接&#xff1a;https://pan.baidu.com/s/1_Sg-EGGXKc4bMW-NPqUqvg?pwd1234 提取码&#xff1a;1234 --…

江苏宿迁服务器的优势有哪些?

江苏宿迁服务器是一款性能强大、稳定可靠的服务器&#xff0c;能够应用在各种应用场景当中&#xff0c;比如云计算、大数据分析等&#xff0c;接下来就让我们来了解一下江苏服务器的优势都有哪些吧&#xff01; 江苏宿迁服务器采用了优秀的散热技术&#xff0c;并且配置了多种安…

opencv动态识别人脸

import cv2 import os import numpy as npdef take_faces():while True:key input(请输入文件夹的名字&#xff0c;姓名拼音的缩写&#xff0c;如果输入Q&#xff0c;程序退出!)if key Q:break# 在faces_dynamic下面创建子文件夹os.makedirs(./faces_dymamic/%s % (key), exi…

【语音识别】搭建本地的语音转文字系统:FunASR(离线不联网即可使用)

参考自&#xff1a; 参考配置&#xff1a;FunASR/runtime/docs/SDK_advanced_guide_offline_zh.md at main alibaba-damo-academy/FunASR (github.com)参考配置&#xff1a;FunASR/runtime/quick_start_zh.md at 861147c7308b91068ffa02724fdf74ee623a909e alibaba-damo-aca…

电脑教程1

一、介绍几个桌面上面的软件 1、火绒&#xff1a;主要用于电脑的安全防护和广告拦截 1.1 广告拦截 1.打开火绒软件点击安全工具 点击弹窗拦截 点击截图拦截 拦截具体的小广告 2、向日葵远程控制&#xff1a;可以通过这个软件进行远程协助 可以自己去了解下 这个软件不要…

模块四:一维前缀和模板——DP34 【模板】前缀和

文章目录 题目描述算法原理解法一&#xff1a;暴力解法&#xff08;时间复杂度为O(n*q))解法二&#xff1a;前缀和(时间复杂度为O(n)O(q))细节问题 代码实现CJava 题目描述 题目链接&#xff1a;DP34 【模板】前缀和 根据描述第一句可得数组长度应设为n 1 算法原理 解法一…

编写一个函数fun,它的功能是:实现两个字符串的连接(不使用库函数strcat),即把p2所指的字符串连接到p1所指的字符串后。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 编写…

个人学习-前端相关(2):ECMAScript 6-箭头函数、rest、spread

ES6的箭头函数 ES6允许使用箭头函数&#xff0c;语法类似java中的lambda表达式 let fun1 function(){} //普通的函数声明 let fun2 ()>{} //箭头函数声明 let fun3 (x) >{return x1} let fun4 x >{return x1} //参数列表中有且只有一个参数&#xff0c;()可…