MIT 6.S081 操作系统工程笔记(五)

P4:Lecture 5 RISC-V Calling Convention and Stack Frames中文版 - MCATIN-麦可汀留学 - BV1rS4y1n7y1

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_0.png

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_1.png

也许人们听到我在说什么,或者你很厉害,所以我只想从类似于上周的开始,我们问你,嗯,Util实验室,我想先问一下思科实验室的情况,因为那是周四到期的,所以如果有人有特别有趣的东西,他们发现了实验室。

或者他们发现的讨厌的虫子,或者只是一个愚蠢的错误,他们做了任何你想分享的关于系统实验室的东西,就会有,那太好了,我不知何故设法从fork中删除了部分,你必须在面具上复制,所以我有工作。

然后我换了别的东西,然后我测试了它,你的音频就像剪掉,我想这一切都是对不起,你听到了吗,我听到了,哦好吧,嗯是的,我想是的,基本上我删除了面具的副本,然后我的叉子坏了,然后我就像我那样做了。

它怎么不起作用,所以花很多时间弄清楚我做错了什么,我意识到我刚刚删除了那行,所以说,有人听不到路卡说话吗?是在我这边吗,我想是在你那边,是啊,是啊,我听得很清楚,这一定是我这边,对不起,也许那样会更好。

你现在能听到我吗完美,好的,伟大,我担心我只是在说话,否,否,没有似乎在我这边,太厉害了,其他人有什么想分享的关于你的事吗?或者Sycall,有什么特别有趣的,还是实验室绝对可怕。

你觉得我们应该把它从课堂上删掉,再也不要这样做了,我有点东西,显然,事情的顺序很重要,这是一个一般性的陈述,所以我试着确定系统调用函数中的大量计算,在确定我是否真的需要进行跟踪之前。

所以所有的系统调用都得到了正确的跟踪,除了跟踪调用,我真的被这件事打扰了,直到我想是大卫指出,你知道你应该做计算,你先请,你应该追踪,她就像啊,那是,我很高兴你能听清,是啊,是啊,秩序很重要,嗯。

特别是页表,你会发现我认为那里的秩序也很重要,在整个课堂上,注意事情的顺序是很重要的,最好不要覆盖页表中的内容,我也有一些我实际上仍然困惑的事情,嗯,虽然我在内核代码中有很多调试print语句。

然后我运行了跟踪程序,所有的位都设置好了,所以基本上跟踪所有的系统调用,我想我在内核中看到了很多打印语句的跟踪,然后因为然后我假设我的指纹,我的printfs试图读写控制台,但是要写到控制台。

但后来我想知道,为什么我们,我想我只是糊涂了,为什么我们能够在内核中使用printf,是啊,是啊,我不是,我最近没有看printf代码,但我想有个文件,呃,如果您愿意,您可以查看printf c。

弄清楚那里发生了什么,所以我认为printf函数应该在那里实现,至少我们的版本,我不知道,也许是其中一个,其他核心员工中的一个可能知道发生了什么,内核中的printf不应该像跟踪中那样显示。

因为内核中的printf不调用任何系统调用,是啊,是啊,我也是这么想的,也许那时,也许追踪不是从那里,是啊,是啊,我不认为它来自内核中的printf,除非有人对,呃,思科,我想我们可以,呃。

从今天的讲座开始,我将谈谈,转换两个组件和处理器的过程,今天这更像是一个实用的讲座,或者至少这是目的,所以这里的目标是让你们都熟悉,嗯,风险五,处理器,风险五的汇编语言和调用约定,这在。

对于页表来说并不重要,但肯定是为了本周晚些时候分配的陷阱实验室,这对于调试和实现事情是必不可少的,因为你将与陷阱框架非常亲密地工作,还有堆栈之类的东西,这就是今天的目标,我的第一个目标是。

这可能是六年级的一点回顾,双O四或任何其他计算机体系结构主题课程,您可能在过去参加过,但我只想简单地回顾一下,有点像C语言,我们是如何组装的,也许还有一点关于处理器的,所以在整个过程中,然后,当然啦。

在整个讲座中,请随意打断,呃,你有什么问题吗,所以你知道我们在C中有我们正常的主要函数,你知道他们会做一些事情,也许他们打印了什么,然后他们退出,这一切看起来都很好,但正如你们中的任何一个人所知道的。

从六个双O四处理器,我其实不懂C语言,相反,他们明白我们所说的集会,或者更具体地说,他们理解程序集的二进制编码,所以这里,我在SCI五板上圈出了一张实际的风险五处理器的图片,当我们说处理器是风险五时。

这意味着它理解风险五指令集,所以每个处理器都有一个相关的ISA或指令,这是对处理器有意义的指令集,所以每条指令都有一个相关的二进制编码或操作码,当处理器运行时,它看到一个特定的编码,它知道该怎么做。

所以你知道这块板上的处理器碰巧了解风险五组装,这就是C代码被编译的目的,所以让C代码在处理器上实际运行的一般过程,我们从C开始,它被编译为程序集,有一些联系,类似的事情发生在步骤之间,但我们不是编译器。

所以上课,然后程序集将被转换为二进制,所以这是,你看到如此,如果你曾经注意过你的实验室目录里有什么,你跑完马木之后,你会看到一堆DOO文件躺在周围,这些是处理器理解的实际目标文件,你也看过的ASM文件。

嗯,你真的没有写任何,但是如果你从系统调用中回忆起来,uses pl被编译为一个名为use s和so dot s文件的文件,我们的汇编语言,所以你已经看到了你肯定看到了一些风险五组装,如果你拿了双四。

我相信你,您还看到了大量的汇编语言和一般的汇编语言外观,结构远不如C,所以你会看到一行又一行的说明,你知道你会看到一些简单的东西,比如添加购物中心,等等,等等,等等,这没有,没有,嗯,良好的控制流。

没有循环,有一些功能,但不是你可能记得的从C中知道的那种意义上的,你知道吗,我们看到标签与真正的函数定义和程序集相反,所以它是,这是一种低得多的语言,还有很多其他语言也被编译成程序集,所以嗯。

同样的过程也适用于像c+um这样的事情,你知道的,任何,任何已编译的语言都将在基础上转到相同的汇编语言,所以这就是,这是让我们的计算机,实际上理解我们正在编写的C代码。

但你会注意到我们提到的是风险五集会,在整个课程中,处理器是风险五,这是因为它很重要,因为有许多不同种类的组装,所以你自己不太可能使用风险五,就像你不会在上面运行Linux一样,而是。

大多数现代计算机将在所谓的x86上运行,或者你有时会看到x86664,这是一个不同的ISA,这是一个不同的指令集,它看起来很像风险五,但这是你在个人电脑上看到的,经常,所以如果你用情报。

所以英特尔CPU实现x86,我相信MD也是,这是两者之间相对重要的区别,它们不像最初看起来那么相似,这归结为风险五就是我们所说的风险,风险五的风险部分是指减少的指令集,然后呃。

x86664是所谓的CC或复杂指令集,这里有几个关键的区别,1只是存在于x86664中的指令数,事实上,写作的一大动机,嗯,为了冒险,五是我们在英特尔手册中实际有多少指令,所以作为参考,有整整三本书。

包括ISA和一些统计数据,我想有,呃,新指令的添加速度为,因为加了x86664,它是,呃,首次出版于七十年代,所以我相信有像北方,在x86664和风险五,另一方面。

风险五的程序集可以很好地包含在两个文档中,所以有点偏离了这一点,我们不希望你们在这门课上记住,每一个风险五指示,但如果你有兴趣,或者你会发现自己对一个特定的指令是什么或做什么感到困惑,如果你去课程网站。

我们在参考选项卡下的风险五,我们为您提供到特权和非特权指令集的链接。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_3.png

所以这有点,给你一大堆关于ISA的信息的文档。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_5.png

但你会注意到,像这样有240页,这是135页,所以它比x86指令集小得多,这就是风险五的好处之一,所以我们有风险五,我们有,呃,减少指令,不仅说明更简单,所以我的意思是,也就是说。

在x86664中有许多指令,我指的是像at、malt、sub之类的东西,嗯,以x86六十四为单位,有很多指令可以做不止一件事,所以他们表演一些复杂的,一组操作,然后给你结果,风险五不是这样的。

风险五指令的范围往往较小,所以他们拿着,你知道的,运行每条指令的周期更短,那么也许x86664是,这只是设计师们选择承担的一个权衡,嗯,什么都没有,或者你知道,没有规范的理由说明精简的指令集比。

你知道的,复杂的指令集,它们各有各的用途,嗯等等,等等,风险五的另一个很酷的地方,与x86相反的是,这是开源的,所以它是市场上唯一的开源指令集之一,这意味着任何人都可以为风险五开发一个板。

它来自加州大学伯克利分校的一个研究项目,这就是风险五开始的地方,从那以后,它得到了许多公司的支持,你可以在网上找到名单,但你知道,有大量的大公司对支持开放指令集感兴趣。

实际上我觉得最近SCI五号发布了一个公告,他们是风险五处理器的主要板制造商,他们将发布一个个人电脑板,表面上你应该知道,为个人电脑设计的运行Linux的风险五处理器,我想这是在最近一两周发布的。

关于它的公告,嗯,所以如果你,如果你好奇的话,如果你发现自己,你知道绝对你知道,5。在我成功之后,我想使用第五险,希望到那时会有一个可用的处理器,你可以在自己的电脑上运行Linux。

但即使在你的日常生活中,您很可能使用减少的指令集,即使你没有意识到,所以手臂组件,所以这是一个R M,这也是一个简化的指令集,骁龙系列处理器,所以如果你有一部安卓手机,运行缩减指令集。

即使你在使用iOS,iOS,我想我忘了它叫什么名字了,但是苹果有一些版本的ARM,他们也在自己运行在iPad上的处理器中实现,iPhone和大多数移动苹果设备,所以你知道减少的指令集到处都有。

如果你在现实世界中寻找风险五,你知道在你的Q MU之外,你可以在集成设备中找到,所以它是存在的,它不像,它当然不像X8664这样无处不在,但它是它是一个是,我是的,我想苹果就像你在聊天中说的那样。

我想苹果正在移动max到arm,我相信也是这样,在过去的几年里,肯定有一个减少指令集的推动,嗯给定,考虑到ISA的情报有多大,英特尔的ISA之所以这么大,是因为它们,非常关心向后兼容性,所以如果你写。

你知道的,现代英特尔处理器可以运行与英特尔代码相同的指令,你知道的,三四十年前,写在,所以他们不会真的反对任何指示,这样它们就能保持向后的兼容性风险五更现代。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_7.png

所以这不是一个担心,风险五也是如此,如果我们回到那些手册,风险五的独特之处在于它是分开的,我们有所谓的基整数指令集,所有这些都有五个处理器的风险,如果我们有第五个,所以加布里埃尔在聊天中问。

如果我们有一万五千条指示,几乎不可能有效地将它们输送到,为什么我们需要这么多,我想我是说我们需要这么多的向后兼容的原因,由你来决定,你知道你是否认为这非常重要,但其中许多指示。

我想他们中的很多人也被SIMD指令吃掉了,这是他们自己的一类特殊的东西,嗯,你不会,我从没见过英特尔的汇编代码,使用15000条指令中的全部用法,但这主要来自于对向后的需要,相容性与辛迪,但就像我说的。

风险五,呃,所谓的基整数指令集,它包含所有正常的加法和乘法,然后处理器可以选择实现许多其他模块,你可以在这里看到,在你的屏幕上可能无法阅读,但例如,如果你想要一个处理器,您知道。

它支持单精度浮点的标准扩展,然后你可以包括F模块,这使得风险五更容易支持向后兼容性,因为如果你能说你知道这些模块是做什么的,我包括和支持,编译器可以选择,然后对编译器可以说,好的。

你知道这个处理器告诉我它支持这些模块,所以我只能用这些模块编译这个代码,好的,比贝克说,似乎使用x86而不是风险5的唯一好处是,处理器是您可以获得的性能,然而。

这种性能带来了巨大的成本、复杂性和潜在的安全性,我的问题是,为什么我们仍然使用x86而不是像风险5这样的,嗯,最重要的答案是,世界已经运行在x86,因为我不知道为什么风险五也很现代。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_9.png

所以世界作为一个整体几乎是在x86上运行的,所以如果你突然开始将你的处理器转换为风险五,你冒着,你知道在一堆重要的事情上失去支持,英特尔也在他们的处理器中做了一些有趣的事情,就像安全方面一样。

有飞地和英特尔处理器,有些事情,嗯,他们近年来一直在做的事情试图给你额外的安全,然后呃,英特尔确实实现的一些指令,它们是超特异性的,对于某些计算来说是非常有效的,所以他们有这么多的指示,经常你知道。

对一种情况的完美指导,这种情况可能比风险五中存在的情况更有效,但更实际的答案是风险五是相对较新的,我们只是不,你知道的,没有人真正为个人电脑制造处理器,我认为科幻的公告是最近才发布的。

他们是第一批这样做的人,所以在实际层面上,无法运行英特尔的所有软件设计是我最好的答案,所以我们现在谈了一点关于组装的问题,所以我只是想看看一些实际的汇编代码。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_11.png

这是下面的C代码,这是一个简单的函数,有一个累加器,我们从零到n循环,我们把所有的数字从零到,然后返回该值,在最简单的层面上,你知道吗,这是最简单的装配,你可以不用编译那个程序。

如果你真的进入自己的电脑,你写了C代码,并试着编译它,你最终会得到一些看起来很不一样的东西,这是真的,有各种各样的原因,我们还没有,其中一些我们会讨论的,其中一些是特定于编译器的。

所以现代编译器做了大量的优化,嗯用它,当他们把你的C编译成程序集时,因此您的组装说明可能看起来不同,例如,当您在GDB中调试时,你可能会遇到一些东西告诉你,它的一些变量已经被优化出来了。

这意味着编译器决定它不需要那个变量,因此,这将有效地从程序中消失,但是是的,这是最直截了当的,我们在移动,呃,从零到t零的值,我们设定一个零到零,然后我们只是把t 0中的东西加到一个零上。

对于循环的每一次迭代,直到t零达到零,这就是这篇文章中所发生的一切,阿米尔,举起你的手,我在想什么点部分文本和点全局做全局,意味着您可以从其他文件中包含此内容,所以如果我们真的跳进去,让我们看看f点h。

这是你将要,如果你还没有,你会变得很熟悉,这基本上包括内核中您可能想要使用的所有函数,然后呃,在这里,你知道我们可以在我的档案里看到,我已经包括了这些函数的定义,所以点全局。

确保这些函数可以从其他地方调用,点文本只是说这是代码,所以如果你从图三回忆起来。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_13.png

书中有四个,所以如果我们去看书,我们进入页表,进程地址空间,所以在这张图中,这是一样的。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_15.png

这是文本,所以它只是意味着代码,回答你的问题,谢谢。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_17.png

所以如果我们想跑,嗯,假设我们也有一些集会,如果您发现自己对内核的样子感兴趣,我们可以进去,编译之后,您可以在文件内核内核ASM中查看,这是XV内核的完整组件,左边的每一个数字,这里有一个标签。

告诉你内存中的位置,本指令中的这将是,会很有用的,所以这里是,这是实际的,呃,使用实际的程序集代码,您可以看到函数的标签,以及在哪里宣布,所以这是一个非常非常有用的,当我们调试代码时。

希望我能在一秒钟内展示这一点,但现在我们将跳回第一个函数,求和二,我们将看看如何在GDB内部检查它,嗯,所以第一步是,我这里有两个窗户,ASM和S文件有什么区别,不能百分之百确定他们都在组装。

我想这个点,点ASM文件,包括一堆额外的注释,这些注释不包括在点中,所以通常当你编译你的C代码到点S,你最终会得到一些不包括所有这些行号的东西,诸如此类的事情,如果你想知道我们是怎么得到ASM文件的。

我想make文件会告诉你获得它的确切步骤,所以如果我们在航站楼里,我们有我们的两个,呃,两个窗口,所以首先要做的是,当然啦,让Q穆开始运行,所以我们可以在GDB模式下启动它,所以现在我们被冻结在这里。

然后我们可以启动GDB,卡舍教授上周展示了什么,我想有些人对此很兴奋,如果键入TUI启用,你有这个漂亮的窗户,它现在是空的,但会很有用,当您调试时,这样我们就可以设置一个断点,我应该注意到。

所有这些代码都生活在内核中,这些都不在用户空间中,这样我们就没有那些烦人的问题了,设置断点,这样我就可以在函数中设置一个断点,然后继续运行这个函数,而现在,呃,所以您在tui中看到的第一个窗口是源窗口。

是呀,正如大卫所说,内核ASM左边的那些数字真的很有用,当你调试东西的时候,你得到一个地址,它会告诉你,所以你现在可以看到,即使在这里我们也可以看到程序,所以PC,这里在gdb中是程序计数器。

所以我们可以看到这个地址,八零,零,依此类推,等等,如果我们进入内核ASM,我们搜索那个特定的地址,我们可以看到是井,它会出现两次,因为它是一个函数调用,但如果我们看这里,这是那个地址。

它是函数的和的顶部,所以如果你看到任何时候你看到,其中一个,所有的内核地址看起来都像零x 8,零零零,嗯,一些数字,那些,呃,那些地址,您可以直接跳转到内核ASM并找到确切的装配线,问题发生的地方。

然后可以相应地设置断点,但就目前而言,呃,TUI中的顶部窗口是源,如果我们想具体地看看组件,我们可以在GB ASM中进行布局,这将给我们所有的组装说明,我们也可以看看寄存器,如果我们键入布局reg。

我们会得到装配和寄存器,如果你发现自己,你知道想滚动东西,现在我们有三个窗口,我们需要指定哪一个是聚焦的,所以如果我想查看所有的寄存器,我要集中注意力,现在我的重点是注册窗口。

所以如果我移动箭头键或滚动,它现在会开始滚动那个窗口,我们可以把注意力集中在组装窗口上,一旦我们到了这里,我们可以看到所有的东西,所以让我们看看,我们可以在注册表寄存器窗口中看到。

我们可以看到t 0包含这个值,我们可以看到一个零包含这个值,当我们走过装配时,我们可以看着,好的,t,0的值是,是五个,它很好地突出了,改变的寄存器,我们可以记住,如果我们,我们得到最近执行的指令。

这样我们就可以通过,我们从0到0,现在我们可以看着自己经历这个循环,把值相加,一次又一次,这就像一个完整的,你知道玩具功能,然后我们可以继续,和,呃,你知道,过我们的生活,如果你好奇,嗯。

您设置了什么样的断点,或者你忘记了你在做什么,如果你输入,信息,断点或断点,您可以看到在代码中设置的所有断点,你甚至可以看到,好的,这个断点已经被击中过一次,你会得到很多有用的信息。

如果你不想有注册窗口,但你确实想看看寄存器,信息或信息寄存器,或i,或众多的GDB缩短中的任何一个,也会弹出注册窗口,所以有什么,关于GDB有什么问题吗,简单的,我知道已经,嗯。

广场上有很多关于这件事的帖子,所以现在是个好时机,呃,有些直截了当,我们会,我将展示更多DB的用法,你用什么命令打开多个窗口,所以我用T mux,所以我可以从头开始展示,如果我们去这里。

我开一个新的航站楼,所以这里只有一个空白的终端,如果您键入T MUX,这在雅典娜上可以买到,嗯,我马上回答下一个问题,所以现在我在团队包厢里,你可以从底部的绿色条看出,如果你想这样。

在Tebow团队中有几种方法可以做多个窗口,呃,如果你是这样,您可以键入Control BC,我知道有时候如果你习惯使用Emacs,那就很有意义了,但是不使用Emacs的正常人,先打控制键,然后打D键。

然后自己打C键,之后会给你第二个窗口,然后您可以使用控件B和p在两者之间导航,控制b然后n进入上一个和下一个,是的,大卫刚发了一张短信,这是很有用的,如果你想把窗户劈开,我想是Ctrl b。

然后百分号会把他们分开,然后唱一个双控B,然后双引号,我们会把它们水平地分开,所以这就是我们如何得到它们,如果我们在这种状态下,我们可以使用控件B和O在窗口之间跳转。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_19.png

所以这就是我得到多个窗口的原因,是啊,是啊,我不在乎。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_21.png

然后艾哈迈德问,为什么再次显示程序集地址,而不是c行数字啊,所以因为函数,所以说,如果我们回到组装函数,这完全是在装配中实现的,在C中一点也不,所以没有任何相关的C行号。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_23.png

嗯,如果我们设置断点。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_25.png

所以如果您键入delete,您将删除所有断点,所以我清除了旧的,如果我现在在演示一中设置一个断点,这是一个c断点,现在继续运行,如果我在这里键入布局拆分,我去拿C和总成,或者如果我只是想要C源。

我可以做布局源,我只想看看,所以那是呃,就是这么回事,这只是一个,这是一个怪癖,因为它没有相关的C代码,所以我们看不到种子行号,关于GDB团队盒子的任何其他问题,嗯,诸如此类的事情,所以布局分裂。

是用来打开源和程序集的额外窗口的,等等,对是的,是呀,所以布局,如果你做布局拆分,这将得到你的源代码和程序集布局源代码将得到你只是源代码ASM,我们会让你组装,寄存器是他们自己的东西,在哪里。

如果键入布局reg,我想是的,会调出收银机,但不幸的是,我不知道有什么办法可以弄到大会的登记簿,和C代码同时,在使用布局拆分的信息reg之外,我有个问题,所以当我们设定断点的时候,呃加一条线,对嗯。

它显示类似的地址,在本例中,断点2在零x 8处,三五四,任何指令可能有多个,任何类似的C代码行都可能有多个指令,所以哪一个做它的,呃展示我展示第一个,然后呃,这是给你的,是给要求启动TUI的人的。

我想是TUI使能不能启用UI,对不起,耶又来了,这里有上百份备忘单,对于GDB和团队来说也是如此,所以如果你发现自己迷失了,GDB甚至有自己的内置,它自己的东西叫做Apropos。

所以如果你找合适的tui,它实际上会向您显示所有的命令,所以耶,呃,这可能很有用,但也可能有点势不可挡,如果你做得恰到好处,破折号b,它会给你更多的信息,我觉得,我不记得了,我自己不经常用。

但如果你真的找到了,或者如果您忘记了在GDB中键入命令的确切方法,你不想谷歌它,当然会的,呃经常能找到你要找的东西,除了一大堆你不想要的东西,是啊,是啊,所以这是很有用的,gdb也有很好的文档记录。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_27.png

所以嗯,是啊,是啊,如果你发现自己迷失了,你知道,谷歌是你的朋友,所以现在我们,我们已经结束了,组装和风险五之类的事情,我想再深入一点细节,嗯,你在接下来的实验室里真正需要知道的。

这也是对文档的一点回顾,你们都有,当然作为勤奋的学生,通读一遍,为这次讲座做准备,所以你知道,这个桌子,当然是呃,你们都非常熟悉,从四个中的两个和你所读到的,这是寄存器表,寄存器很少,你知道的。

CPU或处理器上的位置,预定义的,它可以用来存储值,这很重要,因为装配操作,如果我们从汇编代码中记得,程序集不对内存进行操作,它在寄存器上运行,所以当我们添加,当我们做减法的时候,我们在寄存器上操作。

因此,您通常会看到的编写程序集的模式是,我们会有一些负载,所以我们将向寄存器加载一些值,该值可以来自内存,也可以来自另一个,呃,寄存器和嗯,这里我指的是一般的负载,不是加载指令,嗯,然后我们就做手术。

所以我们要对寄存器进行一些操作,然后如果我们关心返回地址之外的操作结果,我们就会,嗯,到将存储该寄存器的某个位置,到某个地方,是在内存中还是在另一个寄存器中,所以那是那是,呃,通常事情的工作方式。

和寄存器是执行任何类型计算的绝对最快的方法,呃,或访问任何值,这就是为什么,嗯,使用它们很重要,也是为什么我们更喜欢使用寄存器,使用内存,所以如果你还记得我们调用函数的时候。

所以你可以看到这里记录了零到七,一般来说,当我们谈论寄存器时,我们将以他们的名字来称呼他们,嗯,不仅不那么令人困惑,这只是一个标准,这也是您编写汇编代码的方式,你知道这些实际数字并不重要。

唯一重要的情况是风险五指令的压缩版本,如果你想知道更多,嗯,请随时阅读,基本思想是风险五,嗯正常的指令是64位,但也有一个压缩版本,指令是16位的,我们使用更少的寄存器,在这种情况下。

我们使用的寄存器是8到15,这些是我们可用的寄存器,所以我想有人有一个问题,为什么这是一个寄存器x9,为什么它和其他所有的寄存器都分开了,我猜这就是为什么,嗯,我们和其他所有的人分开了。

因为它在压缩指令模式下可用,而不是两点到十一点,所以这就是我的想法,那是用来压缩的,但在注册之外,他们的名字将被引用,所以零到七用于函数参数,但是如果我们有一个函数。

它需要比这些寄存器给我们的八个参数更多的参数,我们确实需要使用记忆,但这是一个事实的说明,我们不想使用记忆,当我们可以使用寄存器时,我们只我们只使用记忆,如果我们必须,还有这个,呃,这里的这一栏。

保存器列,这也是极其重要的,当我们讨论寄存器时,呼叫者与被呼叫者已保存,然后嗯,条款是我经常混淆他们,呼叫者和同事,它们只相差一个字母,我找到的记住他们的最简单的方法,就是,都保存了下来,我的意思是。

调用方保存的寄存器可以被函数覆盖,假设我有一个调用函数B的函数A,函数A正在使用任何寄存器,调用者保存的调用函数b在被调用时可以覆盖,我想一个很好的例子是回信地址。

因为您知道您可以看到返回地址是呼叫者保存的,这很重要,因为每个函数都需要使用返回地址,所以当a调用b时,B能够覆盖返回地址中的值是很重要的,这就是为什么它的呼叫者被保存而被呼叫者被保存。

寄存器只是我们使用的约定,嗯,因此,帧指针很重要,然后在函数调用中保留这些,所以基本上呼叫者保存的任何寄存器,进行调用的函数需要担心这些寄存器,如果他们的调用保存了正在调用的函数。

需要担心保留这些寄存器中的值,你知道,我经常把两者混淆,我发现自己又回到了这张桌子上,提醒自己他们的所作所为,所以你知道你,如果你还记得阅读,所有这些寄存器都有64位宽。

所以他们有64个地方我们可以放东西,然后嗯,根据调用约定,各种数据类型被制作成适合这64位的数据类型,所以如果我们有一个三,二位整数,取决于它是如何,它的标志是否延伸。

你知道我们要么在那个整数的前面加0,要么加1,为了使它成为64位放入这些寄存器,所以在我们继续之前,呃,有人对登记簿或其他东西有疑问吗,那种性质的事情,我有个问题,你能有一个,就可以,在1中放入返回值。

是呀,这是个好问题,所以我想理论上你可以,我们说零到一的原因,如果函数返回一个long long,一百二十八位,所以如果你从阅读中记得,如果函数参数为100,不仅仅是一个指针词。

所以当我们提到把一个字的大小,我们说六十四位,所以如果我们有两倍于指针字大小的东西,我们可以把它放在寄存器对中,因此,同样的约定也适用于回信地址,在哪里,如果我们有一个指针字的两倍大的东西。

我们可以把它放在一个零和一个一里,用它作为回信地址,我想你会遇到麻烦的,如果你只试着把一些东西放在一个里面,有道理,谢谢,我来自,呃,为什么寄存器不像连续的,为什么0和1和抱歉是分开的,否,那是一个。

那是个坏例子,这就是为什么1和2是分开的,为什么A在中间,就像,这有什么意义吗,是啊,是啊,所以呃,我之前简短地提到过这一点,但是有一个压缩的,这是猜测,我不知道,我不太确定。

但是有一个风险五说明的压缩版本,它是十六位大小的,相对于六十四,然后嗯,你会用它来尝试,然后呃,使代码占用更少的内存空间,当你使用那些16位指令时,您只能访问8到15的寄存器。

所以我认为S 1和S 2到11是分开的,因为他们想说清楚,这是一个在压缩指令模式下可供您使用的,而2到11岁不是,而原因,你知道的,我不知道他们为什么选x8到15,但我怀疑他们只是看了一堆代码。

我们就像,哦,这些是最常用的寄存器,任何其他问题,我有个问题,除了帧指针,堆栈指针,和所有,我不知道为什么我们需要更多的呼叫保存寄存器,但我们确实有很多,是啊,是啊,s 1的s 2 11的s。

那些只是为了,我相信在那里是为了自由,供编译器或程序员使用,所以也许在某些情况下你想,你想保证有东西还在附近,在函数调用之后,编译器可以选择使用s 1到11来做到这一点,嗯。

我手头没有一个具体的例子来说明这在哪里有用,但是呃,我敢肯定它出现在有一个被叫者,保存价值很重要,但这些基本上是,你知道的,程序员或编译器的选择,这是一到十一,我应该注意这些浮点寄存器。

它们是用于浮点算术的,据我所知,你在这个班上看不到他们,所以你真的不需要担心他们,好的,所以我们开始讨论函数调用,呃,因此,我想让我们讨论堆栈,所以这是,我们说的是堆栈,堆栈,如果你以前见过。

堆栈之所以重要,是因为它,它使我们的功能井然有序,它是允许和使功能工作的原因,它这个,是什么使返工,这也是我们经常发现自己保存注册表的地方,诸如此类的事情,在这里,我只给出了一个非常简单的,堆栈的布局。

所以这里的每一个盒子都是我们指的,由函数调用生成的,每次我们调用函数,那个函数为自己制造,它自己的堆栈帧,它在哪里使用自己,它通过在堆栈指针周围移动来做到这一点,这是堆栈指针,这是呃。

记住堆栈是非常重要的,我们从高处开始,我们向下生长,两个低地址,所以堆栈向下生长,总是,呃,所以,您将看到堆栈指针的算术是,呃,通常是通过减法来完成的,当我们想在程序集中制作一个新的堆栈框架时。

所以堆栈向下生长,和函数的堆栈帧包含,我保存了寄存器,局部变量,嗯也喜欢,我喜欢,我是说,如果你,如果参数寄存器用完,其他参数将显示在堆栈上,所以不是在,堆栈帧的大小并不都相同,即使它们在这个图中。

不是这样的,不同的函数有不同数量的局部变量,不同的安全寄存器等,等等,因此堆栈帧将是不同的大小,但你绝对可以指望的两件事,重要的是,呃,回信地址总是在第一件事上,和帧指针,上一帧帧指针。

也会出现在堆栈上的一个可预测的位置,所以说,嗯,这里的两个重要寄存器是sp,你知道,正如我们所讨论的,它用于这是堆栈的底部,或者一般,你知道的,它是堆栈的位置,然后fp是我们也很重要的寄存器。

这指向电流的顶部,框架,这很重要,因为这意味着回信地址,并且上一个fp将始终位于当前帧指针的固定位置,所以这意味着如果我想找到我的回信地址,或者我想找到上一帧,我总是可以通过查看当前帧指针来获得这些值。

我们存储前一个帧指针的原因是允许我们跳回,所以一旦这个函数返回,我们可以把这个移到fp,突然间,fp又会从指向这个,此堆栈帧到现在指向此堆栈帧,因此,我们使用帧指针来操作堆栈帧,并确保我们总是指向与。

事情就是这样,呃,事情是怎么做的,然后呃,这堆的这些碎片,需要通过程序集创建,所以一切,你读到的调用约定文档中的一切,这一切都由编译器有效地强制执行,因此编译器遵循调用约定,它是生成堆栈帧的原因。

它生成程序集代码,使我们的堆栈框架看起来正确,经常在函数的顶部,你会看到什么叫,然后就会有函数的主体,然后会有一个结尾出现在那之后,所以这有点像一种空气,组装函数通常看起来像。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_29.png

我们会看看的,嗯,所以这里我有另一个函数,然后一些所以你你会,你会注意到,通常有些管子没有这些东西,如果这是一个适当的功能,它应该,这很好用,因为它很简单,所以它在零上做所有的计算。

所以事情是这样的事情是好的,我们也不是,这是一个,它是叶函数,所以你会看到如果你看到叶函数这个术语,这是一个不调用另一个函数的函数,功能的特殊之处在于他们不需要担心,保存自己的回信地址还是真的保存。

任何调用者保存的寄存器,他们会,他们不会再进行函数调用了,所以他们不必像一些人那样小心,然后加倍,另一方面不是叶函数,所以你可以看到它在这里叫,嗯和二,所以它确实需要包括开场白,所以我们在这里看到。

我们从堆栈指针中减去16,所以我们在堆栈上腾出空间,我们在存储某物的寄信人地址,堆栈上的双倍,然后我们打电话给两个,之后,这个函数所做的就是,它调用一些2,然后将结果加倍,将返回的结果加倍,大约2。

在这里你可以看到结尾,所以我们将返回地址加载回我们的A并删除我们的堆栈框架,然后跳出函数。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_31.png

所以我们可以运行它以确保它能像我们期望的那样。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_33.png

到这里来,可以跑,呃,所以如果我们运行演示二或演示一,2。我们得到的总数是十五,我会告诉你演示二,只是呃,只需调用求和和双倍,事实上,你知道它很高兴地把总数翻了一番,大约两个回来。

所以我有一个问题要问任何人,如果我们,呃,删除这个序言,如果我们把它作为我们的函数会发生什么,一些双倍,有人能预测会发生什么吗,我是说,有一件事是,有些人然后翻倍,不知道它应该返回的回信地址。

所以召唤一些人,这将覆盖返回地址为一些然后加倍,所以在他们中的一些人的最后,它不会恢复到原来的颜色,是啊,是啊,没错,嗯,我们可以看到这就是发生的事情,呃再次,所以如果我们从这里出去,我们从这里出去。

我们现在重新编译,用我们破碎的功能,我们可以看看到底会发生什么,这样我们就可以为双倍的东西设置断点,设置为UI并让其继续,所以如果我们现在运行演示二,好的,我们在一些,然后翻倍再翻倍。

这只是一个程序集函数,所以我们要在组装中看看它,所以我们要做布局,布局注册表,因为这很重要,在这种情况下,寄存器中有什么,你会看到GDB知道一大堆额外的信息,所以我们可以看到,我们可以看到RA目前。

所以返回地址当前指向演示加18,所以它有效地指向函数,演示二,现在我们可以跑了,嗯,我们可以一步一步地完成我们的功能,看看会发生什么,所以我们叫了两个人,我们立即可以看到回信地址被两个。

到现在指向某双加四,这是有道理的,你知道,这就是我们所期望的,如果我们回到我们的代码,我们叫一些两个,所以有些人应该回到,呃到这里,现在我们可以跨过,你知道,所以上,然后我们回到,错误的,好的。

所以我们现在就在这里,当和,然后双倍返回,正如阿米尔指出的那样,嗯,它的回归,它还没有恢复自己的回信地址,相反,它的回信地址仍然是大约两个人使用的地址,所以我们会进入一个无限循环,我们一直这样做。

一遍又一遍,我们永远不会因此而终止,所以这是一个,我觉得,这很好地说明了为什么,跟踪我们的呼叫者和被呼叫者保存的寄存器是很重要的,和,希望,这也表明您可以使用某种GDB来调试,诸如此类的问题。

所以让我们恢复我们所拥有的,我们将进入其他演示来讨论,嗯,来说明一下,呃,有人问,为什么我们要减去16,那就是放置空间来获得我们的堆栈框架,所以这是从堆栈指针中减去16,所以它在向下移动。

基本上在记忆中,向下移动,这样我们现在就有空间放我们自己的堆栈框架,然后我们可以把东西粘在那里,在这一点堆栈,我们不想覆盖,堆栈指针上有什么,为什么不呢,我只是嗯,移动,你需要做16个因为指令是64个。

是啊,是啊,我想我想我们不会,其实不需要做16,但这通常是你会看到的,你一般会看到,我不认为是的,你你不能,你不能做四个,因为呃,你需要八个,所以四个不起作用,但我想你可以做指令大小和寄存器的大小。

对呀,所以寄存器是64个,是啊,是啊。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_35.png

然后公约,为什么你通常看到16是因为如果我们回到我们的文档,通常我们有返回地址和帧指针。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_37.png

呃,但我们不在这里做,因为我们被,呃,对我们的装配不太小心,所以正常情况下,如果我们如果我们看看,嗯,我敢肯定,如果我们看看内核,我们会看到,是啊,是啊,所以如果我们在内核中看到,它也是,你知道十六岁。

这通常是您在编译器中看到的,好的,所以现在我们可以回到这个,修复了我们的功能,现在我们可以看看一些C代码,所有的权利,所以这里我们有演示四,基本上就是,主函数的副本,就像对主要功能的模仿。

所以我们有我们的角,它是一个字符串数组,我们有虚拟主,它接受许多参数和参数字符串,给我们打印出来,所以就这样,那是,这就是这里发生的一切,这很简单,呃,如果我们在主线上设置一个断点,像这样跳过来,好的。

我们让这种情况继续下去,我们开始,我们跑下四个,好的,所以我们现在已经击中了,嗯,虚拟主,有几件重要的事情要记住,您可以使用GDP来处理堆栈帧,所以如果我们再输入i,那是信息,如果我们输入iframe。

我们可以看到一大堆呃,有关当前堆栈帧的有用信息,所以我们可以看到我们在堆栈级别,零,0级意味着它在调用堆栈中,我们可以去,框架在这个地址,程序计数器是可以的,这一切都很好,我们还有一个保存程序计数器。

如果我们再一次,如果我们使用这个地址,我们跳入内核ASM,我们搜索那个地址,我们确实可以看到那个地址在演示表单的内部,这正是我们希望程序返回的地方,它是由这个地址的一个框架调用的,源语言C,你知道吗。

很高兴知道,然后我们有参数列表,也从这个地址开始,当然啦,在本例中,我们的大部分参数都在寄存器中,然后我们甚至可以看到角是什么,我们是说,rc是3,rv是这个地址,如果我们想更深入地研究事物。

我们可以看像介绍弧这样的东西,它将告诉我们关于函数参数的信息,我们可以查看,但更重要的是我们可以打字,如果我们输入回溯或BT也有效,我们得到了所有,调用堆栈中的所有堆栈帧,这样你就可以看到。

你知道这里会发生一些问题,这时我们调用了一个系统调用,然后我们进入用户跟踪功能,然后到系统调用函数,然后到CIS演示,然后到演示四,最后到虚拟主,如果我们想调查这些堆栈帧中的任何一个。

更深入的我们可以做框架,然后这边的数字是多少,假设我想看看我的堆栈框架是什么,当六个电话被调用时,我可以进入第三帧,所以现在在GDB内部,我在看那堆框架,所以如果我输入信息框,我得到了这个。

这里我们得到了更多,更多的信息,我们有一堆保存的寄存器,我们有一些局部变量,我们对这个函数没有任何参数,我们可以看到程序在哪里计数器,在那里我们应该跳回各种各样的事情,所以如果你在调试东西。

这是超级超级有用的,事实上它很有用,我们让您实现自己的版本,不是很远,正如GDB告诉你的那样,但是我们,嗯,下一个实验室的练习之一,将实现您自己的回溯帮助器功能,用于调试实验室内部的东西,以便回溯跟踪。

呃挺有用的,然后如果我们输入0帧或者我们回到我们应该在的地方,如果我们想调查,你可能注意到这不是很有帮助,你知道的,RV是作为字符串数组的字符串,所以我们只要找个裁判,我们只是得到一个地址。

它实际上储存在哪里,如果我们想看看里面有什么,有几种方法可以做到这一点,最简单的,呃,如果我们输入打印,P代表打印,然后如果我们,呃,取消引用地址,所以我们去看看地址,我们可以做到这一点,你知道。

正如你所料,我们得到数组的第一个元素,因为当它试图打印字符串时,它就会消失,你知道的,就像C说的那样,它会进入,直到它击中空字符,所以我们得到了foo,它是数组中的第一个元素,如果我们想得到更多的东西。

我们可以把长度,所以如果我们这样做了,然后一个数字,它会上升到某个指数,这样我们就可以看到这里的两根弦,然后嗯,你又知道了,GDB相当聪明,这样我们甚至可以,呃,使用RC打印出整个参数数组。

所以所有这些信息都对你可用,你知道,然而,你想得到它,所以GDB是一个超级强大的工具,为什么GDB或抱歉,为什么编译器有时会优化出RG,C和RGB以前发生在我身上,是啊,是啊。

这只是意味着编译器找到了一种更有效的方法,很可能它只是去掉了变量,它在做所有的操作,嗯直接,你知道的,通过寄存器,它可能在零上做所有的操作,比如说,它可能只是在返回地址上做所有的计算。

看到这样的事情很常见,如果你,嗯,如果它是一个变量,你知道百分之百的必要,呃,如果你,你知道我们不喜欢,我们不给你对编译器的控制权,但如果你每天都发现,您可以尝试将编译器的优化标志设置为零,嗯。

但即使这样,您也知道编译器也可以,我们会做它的,我们会做一些优化,比贝克,你的手举起来了,是啊,是啊,嗯,除了一美元或二美元外,地址是什么?你知道,就像在吃饭或酒吧之前,呃,美元这个,是啊,是啊。

地址是什么?是谁可能是的地址吗,但是RV指向了其他地址,那些不应该是一样的吗,所以我们的房车在堆栈上,所以你知道,如果你看看这些地址,我们可以看到它们在内核中,呃,就在八点零的时候,零,零,嗯。

这是有道理的,因为我们他们被宣布为静态正确,所以在演示中,如果我走到这里,它们在这里申报,所以它们基本上就在内核的某个地方,他们都是汽车明星,弧,Arcs或RV是一系列汽车明星。

所以我想象每个元素都指向它自己的字符串,对,所以它是一个指针数组,尝试和喜欢,对此我也有一个问题,呃,数组的美元三版有,如果我看这些地址,第一个后缀是三十八,第二个后缀是四十,第三个是四十八。

它是不均匀的,即使三个参数中每个参数的长度是三个,那么为什么它们不一样呢,我不是百分之百确定,我的猜测是,这是一个试图将事情,嗯对,所以为了让它们保持合理的对齐,它的十六进制是均匀的。

正如人们指出的那样,对呀,是啊,是啊,是啊,有道理,哦,哦,是呀,我现在看到了,是啊,好的谢谢,所以你有时会看到直的东西放在哪里,奇怪地,在那里可能有两个东西在奇怪的偏移,那只是因为不是所有的事情都是。

呃,自然大小相同,好的,任何其他问题,呃,我们可以跳过第五个演示,我们从GDB获得的另一个有用的函数是,而不仅仅是破发点,呃,也有观察点,我们还可以设置条件断点,我只是简单地过一遍,我们运行演示六。

所以我们可以在这个函数里面设置,我们可以观看,这样我们就可以设置一个观察点,我想在这里你会看到,是啊,是啊,所以在这里你会看到什么,呃,卢卡说的是,我们看到和已经被优化出来了。

可能是因为它只是被放在零什么的,我们甚至可以看装配这个权利,这样我们就可以把它,事实上,我们可以看到这一切只是,呃,这一切都只是在堆栈上完成的,在我看来,哦不,这一切都是在,s zero零一三。

所以在这里你可以看到,编译器甚至决定使用质量保存的寄存器,这样我们就可以像看I一样设置东西,我们还没有申报,因为我们不在里面,在for循环中,所以如果我们现在通过C指令,如果我们为当地人做信息。

我们看到我们得到我,我们甚至可以看,嗯,一些手表点,所以现在,任何时候金额变化,我们实际上会得到通知,这样我们就可以继续,然后我们在这里删除,因为有些已经被移除了,所以我们不能,我们实际上不能看,呃。

我们甚至可以在两个上做断点之类的事情,所以这是一个条件断点,如果好的话,让我们看看我目前是什么,所以我目前是一个,让我们说,我想我的循环坏了,每当我五岁的时候,如果我想专门调试那个案例,我可以设置断点。

等于五,所以现在我们有了这个断点,如果我们继续下去,我们会有两个,我们可以看到它实际上打印了前几个值,所以我们现在只休息两个,如果我如果我们到达断点条件,所以那是呃,这可以再来一次,这可能很有用。

如果您想调试代码的特定边缘情况,观察点可能很有用,如果,例如,你认为有些事情不应该改变,但你怀疑它是,或者你认为每次你改变某个变量时都出了问题,所以那是那是,呃,那是它的一种用法,然后呃。

今天我想谈的最后一件事是结构,结构非常重要,他们会上来,嗯。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_39.png

经常在实验室里,我也会谈谈,结构在内存中布局,所以一个结构基本上是嗯,它是一个连续的、连续的内存区域,所以如果我们有一些结构,我们有一个领域,场二,第三场,当我们做那个结构的时候,这些字段将彼此对齐。

在记忆中,你可以把它想象成一个数组,但如果有一个,f2和f3都可以是不同的类型。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_41.png

我们可以把它们作为函数传递,通常他们是通过的,将它们作为参数传递给函数,通常它们是通过引用传递的,所以这里我做了一个结构人,有两个整数参数,我给他们传递一个人作为打印人的参数,然后呃,打印出一些信息。

所以如果我们跳入GDB,让我们逃离我们所有的断点和观察点,现在我们可以在人身上设置一个断点,这是你的,运行第七个演示,我们可以看到,现在我们在这里,所以如果我们键入iframe。

我们可以看到我们有一个论点p,我们确实可以,如果我们打印p,如果我们能判断出这个地址是一个结构人,如果我们取消引用,我们可以,GDB会告诉我们p到底是什么样子的,它的ID是1215是2 2。

只是为了展示事情是如何安排的,我们可以把这个地址,我们在这个地址做X,如果我们看,我相信,所以如果我们再来一次,所以我们可以用它来调试我们的结构,在代码中,我们可以这样看看结构是怎么回事。

所以GDB是一个超级强大的工具,不仅仅是为了你知道,单步执行代码,还可以检查代码中各种潜在的问题,查看参数和堆栈框架之类的东西,希望这将在下一个实验室有用,当您必须处理堆栈框架和汇编代码时。

这就是我今天主要想讲的内容,所以呃,最后七分钟的讲座,如果你们有任何其他问题,我都会敞开心扉,我有一个无关紧要的问题,嗯,谁在谁管理,编译器的创建,从like到get从c到各种指令集体系结构。

指令集体系结构的创建者,是第三方吗,或者类似的东西,我相信它不是指令集的创造者,所以通常是第三方,所以你会看到的两个大的C编译器是GCC,由,我认为GNU基金会,我躺着或lvm,这是它自己的东西。

所以我想你知道你可以找到,我相信Lovm甚至是开源的,这样你就可以找到,嗯,你可以,你可以找到专门的代码,这样做的,当一个新的指令集发布时,例如调用约定文件的风险五,以及所有这些指示文件,我仔细地想她。

但是,我想,就像高水平的合作,在编译器设计器和指令集设计器之间,嗯,但是是的,简短的回答是,i,我相信是第三者,谁可能做这件事,在制作指令的人的大量合作下设置风险五,风险五可能是个例外。

因为它来自一个研究项目,他们可能也为它自己编写了编译器,我不认为英特尔有,比如说,在gdcc或llvm中输入,好的,嗯,呃,谢谢你在那种情况下倾听,我想我们可以,你可以在这里结束讲座,还有五分钟。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/8869c25b6b4d641c47e873667332405a_43.png

P5:Lecture 6 - Isolation & System Call Entry_Exit 中文版 - MCATIN-麦可汀留学 - BV1rS4y1n7y1

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/2560cc93e558d8e9bdb7ab02e0210a2a_0.png

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/2560cc93e558d8e9bdb7ab02e0210a2a_1.png

我想先开始,能不能呃,有人能听到我吗,是呀,好,非常感谢,今天一切都好,我想谈谈运行和用户代码之间的过渡,在用户代码中运行程序并在内核中执行,这就是每当程序进行系统调用时必须发生的转换。

或者如果它经历了故障,像页面错误或除以零,或者如果一个设备,um决定中断,需要由内核设备驱动程序提供服务,嗯,有很多精心设计和重要的细节,这些从用户到内核的陷阱是如何发生的,细节对于执行隔离非常重要。

安全性和性能,有很多程序转换到内核,由于系统调用或页面错误,陷阱机构尽可能流线型是非常重要的,所有的权利,所以开始的情况很熟悉,或者我们有我们的用户程序,我将以外壳为例,它在用户空间中运行,我们有内核。

shell想要做的是对内核进行系统调用,我将使用write并使用这个shell写它的提示符,启动xpsix后发生的第一次写入,并使用shell尝试写入,以正确的系统调用为例。

所以我们需要弄清楚如何真正让执行转移,在shell中和具有用户权限的用户空间中运行,以主管权限在内核中运行,硬件的状态是,这将是非常重要的,因为我们所做的很多事情都是在改变硬件状态。

从适合运行用户代码的状态到适合运行内核代码的状态,我们关心的状态,现在最大的状态可能是三个两个用户寄存器,当你记得这个的时候,从星期一的讨论中,我们有所有的用户寄存器,像零和一这样的东西。

风险五有很多这三个–总共有两个,我们可以期望用户代码正确地使用所有这些,如果它使用所有这些,它将获得最高的性能,它们中的许多都有特殊的用途,其中一些我们会看到一个特别有趣的是堆栈指针。

实际上是这三个通用寄存器中的一个,好的,我们有这些寄存器,包括堆栈指针,在硬件中有一个单独的程序计数器寄存器,有当前模式,主管或用户,当然是用户模式,当我们在壳里执行任务时,然后有一堆寄存器。

控制CPU工作方式的特殊寄存器,有一个SAT P寄存器,它包含一个指向页表的指针,结果会是,还有其他几个对这次讨论非常重要,有一个STVAC,也就是处理内核中陷阱的指令。

我们会看到另一个叫Scratch的,这也是非常重要的,这是系统调用时正在运行的机器的状态,我们实际上需要改变很多这种状态,或者对国家做事情,作为进入内核的一部分,设置我们自己在内核中运行普通的C函数。

当然是在陷阱的瞬间,CPU的所有状态都设置为运行用户代码,不是不是内核代码,所以需要发生的事情是对,我要说什么,一个是我们需要保存所有三个两个寄存器,因为我们希望透明地恢复用户代码。

尤其是如果有设备中断,用户代码不期望的,我们希望能够让内核为中断服务,然后在没有注意到任何差异的情况下恢复用户代码,这意味着这三个,两个寄存器不能被内核干扰,因为内核需要使用寄存器。

它得先把它们都救在某个地方,这些需要保存,程序计数器也需要保存在某个地方,因为我们需要,你知道,它是一个用户,它几乎就像一个用户注册,所以我们需要继续执行它停止的用户程序,我们需要把模式切换到主管模式。

因为我们需要使用内核中的各种特权,这是一个页表指针当前指向用户页表,它只包含,嗯,只是用户程序需要的映射,加上一两个我们将看到的,但是用户页表不包含大部分内核数据的映射,因此,在运行大多数内核代码之前。

我们需要切换页表,我们需要将堆栈指针切换为指向堆栈,在内核的某个地方,因为我们需要将调用C函数堆叠在,最后我们需要跳入一次,我们设置了所有这些,交换了所有这些,所有这些资源都适合在内核中使用。

我们需要跳转到内核C代码,一旦我们在C代码中,嗯,生活要多得多,一切如常,我们只是在这个内核中运行一个C程序,嗯,所以今天至少我们会,我们稍后会讨论内核在C代码中的作用。

但今天的讨论实际上是如何从用户空间进入内核,我们可以在内核中运行C代码,有几个高层次的目标限制了我们的设计选项,对于这一个是为了安全和隔离,我们真的不想让用户代码干扰。

以一种可能损害安全性的方式进行用户内核转换,所以这意味着陷阱中涉及的硬件和内核机制,真的不能指望用户空间的任何东西,你知道我们不能对这些寄存器做任何假设,它们可能只是充满了可怕的恶意价值观,所以基本上。

xpsix表带处理程序甚至没有真正查看这些寄存器,它只是把他们救了出来,好的,所以我们要小心在这个陷阱机制中保持隔离,针对故意恶意用户代码,另一件重要的事情是,我们希望对用户代码透明。

我们希望能够抓住这个陷阱,在内核中做我们的业务,并在没有用户代码的情况下恢复用户代码,曾经注意到任何有趣的事情发生了,这只是为了更容易地编写用户代码,值得注意的事情,嗯,我们关心隔离和安全。

今天我们要谈谈,涉及进入内核的安全性方面,但当然,称为实现的系统,你知道,内核中实际正确的实现也,内核中的一切都必须小心,还必须写得仔细和安全,所以你知道,即使向内核的转换是完全安全的。

内核的其余部分都必须安全地编写,并注意,用户代码可能试图欺骗它,我想说的特别重要的一点是模式标志控制,此模式是用户或主管,当然,当我们处于用户空间时,它是用户,模式标志设置为监督员。

当我们在内核中执行时,但重要的是要确切地知道我们获得了什么特权,通过将模式从用户改为主管,事实证明,这些额外的掠夺是相当严格的,这就是你在主管模式下可以做的,在用户模式下不能做的不是,也许和,特权。

你可能会这么想,这是主管模式,就像它控制的东西一样,它做的一件事是你现在可以读写控制寄存器,也就是说,如果你处于主管模式,您可以读写SAT P页表指针,这个ST VEC指针,它控制陷阱在内核中的位置。

嗯,在此暂存器中的陷阱期间保存程序计数器的寄存器,加上其他几个,所以主管不能读写这些登记簿,用户代码无法,监管代码唯一能做的就是它可以使用p,嗯,有PTE U旗设置,我不知道你记不记得。

但是在每个页表条目中有一个PU标志要设置或不说,嗯,一面旗帜上有这个对不起,对不起,你可以用p,呃,没有设置PTE U标志的,如果设置了PTE U标志,这意味着用户代码可以使用页表项。

如果页表项中没有设置此标志,意思是只有主管模式才能使用,你知道我们一会儿就会知道这很重要,但是嗯,这些真的是主管模式唯一能做的事情,它不能做其他特别的事情,比如说,嗯,主管模式代码不能只读写任意地址。

嗯,说物理地址,强制执行页表的无主管模式,就像任何其他代码一样,如果页面中没有虚拟地址,它不是在SAT P指向的当前页表中吗,或者如果它设置了PTE U位标志,这意味着它是用户PPE。

那么主管模式代码不能使用该地址,所以我们甚至处于监管模式,被限制在任何虚拟地址,在当前页表中设置了虚拟地址,就是这样,所以我们只被允许做这些事情,所以你知道,我们将看到这是对陷阱代码所能做的重要限制。

当我们进入内核时,什么都不能做,我要预览一下,我将把这堂课的大部分时间花在gdb上,实际上通过执行进入内核的陷阱条目来跟踪,作为回报,会有很多细节,呃,也许能帮你,嗯,预见未来。

我们将追踪shell调用,那个,从外壳的角度来看,只是作为shell一部分的C函数调用,嗯,事实上,赖特,让这个,uh可能通过发出e call指令来发出系统调用,切换到内核和主管模式,它立即去哪里。

这是在内核中执行的第一条指令,在主管模式下是在一个用汇编程序编写的名为user back的函数中,这是蹦床的一部分,在蹦床的内核源代码中,所以执行的第一个代码,这个用户反向汇编程序函数,接下来。

那个汇编器函数跳转到C代码中,特别是trap c中称为user trap的函数,现在我们把它变成C代码,所以事情就容易理解多了,用户陷阱看到,哦,我们正在执行一个系统调用。

它调用一个名为syscall的函数,它在表中查找系统调用号,并在内核中调用特定的函数,实现系统调用的,对我们来说,这将是正确的,做这个生意,碰巧,你知道的,安排写入的任何字节出现在控制台上,当它完成时。

它返回到这个系统调用函数,然后打开系统调用函数,以便返回用户空间,因为我们想在Z呼叫后继续,有一堆东西,我们将看到为了返回用户空间,必须发生这种情况,所以有一个单独的函数叫做user,捕鼠。

它是用c写的,它在C陷阱里,这一部分返回到用户空间,用C代码做很方便,有一些最后的事情真的只能在汇编代码中完成,代码是用汇编程序编写的,它又出现在蹦床页面中,在一个名为user r的函数中,到最后。

一些必须发生的最后组装事情,最后,此汇编函数发出返回用户空间的机器指令,并在E调用后恢复执行,有人想回答吗,问一个关于,我即将切换到用GDB看东西,关于那种高级图片有什么问题吗,我会很抱歉的,我不能。

让我看看RVM是什么模式,在mlc函数中运行的c函数,里面的一切都是内核的一部分,它运行在主管模式下,我有一个,好的,有人问为什么这些函数是这样命名的,命名有点灾难,明年我决心让他们更加理性。

我想命名问题可能更喜欢寄存器的名称,在上一个董事会,除此之外,有五个人选这些名字,嗯,有人问,vm lc函数不直接访问物理内存吗?这是千真万确的,他们被允许那样做的原因是什么,虽然。

内核仔细地在页表中设置直接映射,页表里有很多很多的PDS,这导致每当内核试图读取或写入物理地址时,它实际上是一个虚拟地址,由内核页表翻译,物理地址等于它发出的虚拟地址,所以在内核里超级方便。

一旦您使用内核页表,因为内核有所有这些直接映射,但在我们设置好电流之前,直到陷阱机制切换到内核页表,这些映射都不可用,直到内核陷阱代码切换到内核页表,我们仍然使用用户页表,它没有这些方便的物理地址映射。

嗯,我能问个问题吗?拜托了,嗯所以,嗯,我不知道这可能和刚才说的不太相关,但是嗯。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/2560cc93e558d8e9bdb7ab02e0210a2a_3.png

读写系统调用权利,就像那些很贵,与仅仅是内存存储相比,因为你必须切换模式,来回,当你打开一个文件时,是否可以只喜欢而不是什么时候,而不是取回用系统调用的文件描述符,调用以获取页表映射。

然后你会写信给某个地址,刚刚映射到设备的,你可以设置限制,这样程序只能写给DEA,喜欢文件描述符,它被允许,允许通过虚拟页表映射,而不是跳转到内核再返回,是啊,是啊,这是一个很好的观察,事实上。

许多操作系统都提供了这一点,所谓内存映射文件访问,其中您确实映射了与文件内容相对应的页面,进入,进入您的虚拟,进入用户虚拟地址空间,所以你可以直接通过记忆读写它们,事实上。

您将在mmap实验室中实现它的一个版本,几周后,而且确实,就像你暗示的那样,对许多程序来说,这比调用媒体要快得多,我要切换到GDB世界,在这一点上,每个人都应该,查看我的屏幕共享。

我们将看到一个XV系统调用右边,shell在其初始提示符的右边,通过系统,您可以在h c中看到启动此操作的用户代码,我在展示这里只是在进行正确的系统调用,用这个美元符号提示,让我点燃。

当用户代码时实际会发生什么,当贝壳叫对的时候,对的只是一个,它是一个库函数,是,连接到外壳里的,你可以在udot s中看到它的来源,所以是这几个说明,下面是shell实际调用的正确函数的实现。

这是一个很短的函数,它所做的就是加载到一个7 a数字cis正确,或者只是象征性地定义为十六岁,告诉内核,我想运行第十六个系统调用,这恰好是对的,然后这个小函数使用e call指令。

这实际上是将代码送入内核的原因,内核做这个事情,然后当内核最终完成时,它返回到用户空间执行指令,在E电话之后,也就是返回到外壳,从右边的库函数返回到shell中,为了显示系统调用,我想做的是。

这其中的一部分,我先在e调用指令上加一个断点,我们需要知道它的地址,当然啦,但我们可以通过查看s h a m来发现这一点,xpsix编译过程产生的,和一个X H ASM,有一个带有,外壳的说明。

所以我要在e调用指令上加一个断点,地址是6号,非常好,我实际上要开始X6运行,我希望系统能破壳而出,就在执行调用之前好的,非常好,嗯,我们现在,你可以从GDB看到,我们即将,我正要执行那个e呼叫。

让我们检查一下,我们在我们认为我们在的地方,让我们打印,我们可以打印程序计数器,这是一个零xd6,正是我们所要求的,嗯,我们也可以打印所有三个两个寄存器,其中一些值,我们不知道的一切。

我们不在乎他们是什么,而是一个零,1和2是shell正确传递的三个参数,所以这些参数是文件描述符,零中有两个,我是指针,到shell要写入的字符缓冲区,以及它想用2写的字符数,嗯,我们可以说服自己。

我们正在寻找,呃,我们认为我们正在查看的代码实际上是通过打印出来的,shell要写入的缓冲区中的字节,事实上,它是一个美元符号和一个空间,所以我们在系统调用中,我们希望注意到一件事。

程序计数器和堆栈指针都位于低地址,地址接近于零,这加强了我们的信念,我们仍在执行,在用户和用户地址空间中,所有的地址都很小,一旦我们进入内核,你看,地址实际上是内存中加载得更高的内核。

系统调用的点切换了很多状态,必须改变的最重要的状态之一,在它被切换之前,我们必须接受的是当前的页表,当然我们可以看看SAT P,但我们得到的只是物理内存中的地址,页表实际上并没有告诉我们映射是什么。

页表是什么样子的,嗯幸运的是,QMU中有一种方法可以要求它打印当前页表,如果我打印控件A C,我进入Q MU监视器或控制台,如果我输入信息M,它会打印出完整的页表,这是一个很小的页表,它只包含六个映射。

当然是外壳的页表,shell是一个相当小的程序,这六个映射按照shell指令的顺序,外壳的数据,无效的页面,访问堆栈,保护页,以防shell试图使用太多的堆栈空间,我们可以看到它是无效的。

因为它没有在这里设置U旗,在此属性或标志列中,这些都是PTE下划线标志,rw和x只是控制,是否可以读取、写入或执行页面,下一个专栏是你,这就是PTE U旗是否设置。

用户代码只能获取设置了U标志的PD条目,我不知道下一栏是什么,我不得不承认,下一列是一个页面表条目是否曾经被使用过,及d是否曾发出或书面发出,此地址,好的,所以我们有一个很小的页面,和最后两个页表项。

顺便说一句,在巨大的虚拟地址上,接近非常接近虚拟地址空间顶部的,这就是你在书中读到的这两个,我们将听到更多关于陷阱框架页面的信息,现在蹦床页面,正如你所看到的,他们都没有设置U位。

所以用户代码无法获得这两者中的任何一个,现在不能使用这两个地址中的任何一个,但是一旦我们结束了我们的主管模式,我们可以得到这两页,所有的权利,当他们得知这个页表是,内核中没有任何映射。

你知道没有物理地址映射,没有内核数据或内核指令的映射,或者其他任何东西,除了最后两个,现在是最后两页,这是一个几乎完全专用于用户执行的页表,并且对于执行内核并不直接特别有用,什么是,什么是。

属性上的属性,在顶部列出的页表上,我相信这意味着页表曾经,此页表项曾被代码访问过,那就是它是否曾经发布过引用这个页表项的地址,D是程序是否写过,他们通过这个页表条目做了一个商店。

这些是硬件为了操作系统的方便而维护的位,和比XP更复杂的操作系统,如果物理内存不足,六个可能需要驱逐页面,他们可能需要将一些页内存写入磁盘,嗯,并使页表项无效,以释放物理内存,和许多策略。

你可以想象一个操作系统用来选择哪些页面存在,我们会咨询,一个位,看看这个页表条目是否曾经被使用过,如果没有使用过或最近没有使用过,那么这是驱逐到磁盘的一个很好的候选,它告诉内核,哦。

这一页实际上已经写好了,呃,因为它是从磁盘读取的,xpsix实际上并没有使用这两种方法中的任何一种,其他问题,让我提醒一下我们在哪里,我要打印出右边的内容,我们是对的,中的库函数,呃,在shell中。

程序计数器指向e调用指令,我们即将执行e调用指令,我们还在用户空间,但我们不会太久,嗯,嘣,我执行了e呼叫指令,好的,所以第一个问题是,打完电话后我们在哪里,我们可以看看程序计数器。

我们现在看到的是一个非常低的数字,德六现在是一个非常高的数字,事实上,我们看看程序Counas,虚拟地址,就像所有指示使用的地址一样,我们可以在页表中查看,其实,让我们检查一下页表,以确保。

我再去问Q MU要我的信息,这是相同的页表,所以那里什么都没变,我们将在那里查找新的当前程序计数器,程序计数器在右边,在这个蹦床页的开头,那是很高的地图,在用户内存中,我们正在执行。

我们可以看到那里的说明,我要用,这是说明书,内核开始时在监管模式下执行的第一条指令,在陷阱的开始,通过GDB中的一些怪异,我们实际上已经执行了本页开头的第一条指令,我们即将执行第二条指令。

我们可以看看寄存器,我不知道你是否记得这些寄存器值,但这里什么都没变,这些是用户程序所拥有的完全相同的寄存器内容,所以这些都充满了他们中的许多人或所有人的用户价值,为所有人,我们知道。

它们是唯一存在这些价值的位置,所以我们要非常小心,在这一点上,我们实际上不能使用任何寄存器,不首先将这些寄存器保存在某个地方,这样我们就可以恢复它们,因为如果内核在这一点上使用这些寄存器中的任何一个。

它会覆盖,无论用户价值是什么,然后如果我们试图恢复用户程序,我们将无法用正确的值设置它的寄存器,用户程序会做一些完全错误的事情,问题是的,你能回到你之前的说明面板吗,我想知道C R W,CSR RW。

好的,我们过几分钟再谈这个,但你的问题的答案是指令交换为零,与特殊划痕寄存器的内容,所以在,是啊,是啊,好的,这是非常重要的,基本上回答了这个问题,内核陷阱代码怎么能做任何事情。

如果它不能使用任何寄存器,答案,那个问题是出口吗,真的必须执行这个CSR RW零划痕指令,同时在划痕中保存一个零,碰巧把Scratch加载到零,所以现在内核可以在这个指令之后用0来表示它想要的任何东西。

好的,谢谢,好的,所以我们现在的地址是3 fff f f 0 0 um,最后一页是蹦床页,我们目前正在蹦床页面中执行,其中包含内核陷阱处理代码的第一条指令,Ecall不切换页表,那是打电话很重要的一点。

这意味着,这些最初的指令必须出现在每个用户页表中,因为Z call不会切换页表,我们需要执行内核的第一位,在用户页表的某个地方,是这个蹦床页面,内核仔细地将其映射到每个用户页表中。

这给了内核一个在陷阱开始时执行的位置,当我们还在使用用户页表时,控制的方式,嗯是通过ST VEC寄存器,这是另一个特权寄存器,只能由可写的人读取,由主管,嗯,和内核在进入用户空间之前。

设置tvec以指向内核希望陷阱去的地方,所以你可以看到,内核以前已经将这个ST甲板设置为这个三个FFF,f f f零零零地址,也就是蹦床页的开头,正是这个ST VEC寄存器,它的内容是为什么。

在E电话之后,我们结束了,最后我想提醒你,即使蹦床和陷阱框架页映射到用户页表中,用户地址空间,用户代码无法编写它们,因为P是给他们的,没有PTE U旗,所以它们不受用户代码的影响。

这就是为什么这个把戏是,为什么这个把戏是安全的,我一直在告诉你,假设我们处于主管模式,4。我不知道有什么办法可以直接查出这台机器的运行状态,嗯,但我确实观察到程序计数器当前正在页面中执行。

没有PU旗的蹦床页面,这只能在没有撞车的情况下发生,如果我们是主管模式,所以我从没有撞车推断,和程序计数器的值,我们必须处于监督模式,嗯,我们是怎么到这里的,当然是通过呼叫,真的只是先改变三件事。

从用户到主管的相等呼叫更改模式,第二次,他调用保存程序计数器,在SEC注册,所以我们可以看到打印程序寄存器程序计数器的效果,它肯定不再是用户程序计数器,尽管所有其他寄存器都是,这是从SDC复制的值。

我们也可以打印,呃,被拯救的人,这是主管例外,程序计数器就是它的意思,但这是所有保存用户程序程序计数器的地方,这有熟悉的价值,六,它是e调用指令在用户空间中的地址。

所以我们至少通过E调用保存了一个寄存器,E调用做的最后一件事,它做的第三件事是跳转到stc指向的指令,所以现在需要发生的,他为我们做了一点工作,但事实证明我们离,准备在内核中实际执行普通的C代码。

现在要做的是我们需要保存三个两个用户注册的内容,这样我们以后就可以恢复它们了,当我们现在要恢复用户代码时,我们需要切换到内核页表,因为目前我们使用的是用户页表,我们需要创建一个堆栈或找到一个堆栈。

并将堆栈指针寄存器设置为指向内核堆栈,所以我们可以运行C代码,这需要一个堆栈,然后我们需要跳转到内核中C代码中的某个合理的位置,现在作为一个旁白,你知道的,他的电话对我们没有任何帮助,嗯,你知道的。

但你可以有硬件可以定义e调用来做更多的事情,为我们提供更多这样的步骤,而不是把它们留给软件,我们将看到,在软件中做它们并不特别简单,所以你应该问问自己,为什么E电话不行,更多从用户空间进入内核的工作。

为什么它不保存他们的用户注册,或切换页表查找器以指向内核页表,或者自动将堆栈指针设置为指向内核堆栈,或者直接跳转到内核C代码天哪,而不是要经历所有这些复杂的汇编代码。

实际上已经有机器在硬件上完成了所有这些事情,在系统调用期间,风险五对他们中的任何一个都不起作用,风险五真的,他们采取的态度是,E做了所需的绝对最低限度,它可能做到的事情,把其他一切都留给软件。

原因是因为风险五,设计者希望允许软件有最大的灵活性,操作系统,程序员设计程序操作系统,不管他们喜欢什么,所以你可以想象,xpsix真的没有使用这种自由,但其他操作系统也这样做。

一些软件可以做的事情的例子,因为召唤是如此简单,嗯,也许一些操作系统可以在不切换页表的情况下执行一些系统调用,切换页面页表是昂贵的,如果您调用强制您这样做,嗯,这排除了非常精简的实现的可能性。

对于一些不切换页表的系统调用,嗯,一些操作系统将用户和内核虚拟地址映射到单个页表中,并对用户和内核使用相同的页表,因此,甚至不必切换页表,在用户和内核之间转换时,对他们来说,如果你调用切换页表。

那将是一种浪费,并使事情变慢,也许在某些情况下系统调用,比如说,有些寄存器不必保存,哪些是必须拯救的,这取决于软件,语言和编译器,但通过节省不到三个两个寄存器,可能会节省大量时间。

所以你不想让妮可强迫你,您不一定希望等于强制您保存所有寄存器,最后,对于一些简单的系统调用,可能根本不需要堆栈,对于非常关心性能的操作系统来说,这很好,不会对您强制任何特定的堆栈策略,再说一遍。

有许多聪明的硬件和软件方案非常精简,高性能系统调用和陷阱,因为这些东西的性能非常重要,人们很担心它,回到XP6和风险五,嗯,我们需要做的第一件事是保存一些寄存器,2。我们对五险一金几乎无能为力。

没有几个寄存器,不能使用寄存器,那么,在其他机器上保存用户注册表时,我们有什么选择呢?我们也许可以写出32个寄存器的内容,在物理记忆中方便的地方,我们实际上不能在风险五上做到这一点。

因为监管代码不允许直接访问物理内存,我们只能使用页表中的内容,页表里没有多少,xpsix做不到的另一种可能是简单地设置sat,p到内核页表右,然后我们可以使用所有的内核映射。

也许是为了帮我们保存用户注册,那是合法的,主管当然可以改变那个节拍,然而,在陷阱处理程序中的这一点上,也就是开始,我们甚至不知道内核页表的地址,以及在p处执行更改的指令。

要求加载到SAP中的地址来自寄存器,所以我们甚至执行更改页表的指令,我们需要一些备用的寄存器,以便将新的页表地址放入这些寄存器中,这样我们就可以执行set p修改指令,嗯好吧。

所以我们真的需要保存用户寄存器,解决办法有两部分,关于XV6是如何在风险上做到这一点的,五一是解决方案的一部分,每个用户地址都有xp6吗,每个用户页表映射,这个陷阱框架页,每个进程都是自己的陷阱框架页。

每一页的制作实际上都包含了一些有趣的,一堆不同种类的数据,但在这一点上,它包含的最重要的数据是三个两个插槽,内存中用于保存三个两个寄存器的空插槽,所以现在陷阱处理代码中的好消息是我们有一个映射。

我们保证有一个由内核设置的映射,之前在用户页表中,它指向一个为我们准备的地方,保存此进程,用户注册,它就在这里,是三个,它总是三个f的f f的f e零零虚拟地址,如果你想看看陷阱框架里到底有什么,它是。

XV六在那里的定义是,和结构压裂,就在这里,这样你就可以看到,每个槽里应该放什么,有三个两个插槽,你知道的,名叫拉斯布,gp,随便啦,这些是保存寄存器的地方,开头还有这五件事。

我们会看到它很快就会派上用场,这些是上校以前放置在陷阱框架中这些插槽中的值,就像,比如说,陷阱框架中的第一个插槽包含一个指向内核页表的指针,这将是我们的价值,我们很快就会,陷阱处理代码将加载到SAP中。

好的,所以如何说寄存器的一半答案,内核已经方便地映射了这个陷阱帧,和每个用户页表,另一个是这个指令,我们之前提到的这个划痕寄存器,所以有风险五提供的特殊的刮刮乐登记册,为了确切的目的,我们要用它来。

内核,当它之前进入用户空间时,在那里放一个指针到陷阱框架,基本上就是点,把这个地址输入SAT P,只是为了方便陷阱处理代码,更重要的是,尽管风险五中有指示,它将允许我们交换任何寄存器,那是刮刮乐。

它将保存寄存器,并将s Scratch值加载到我们指定的任何寄存器中,大家可以看到,如果我看蹦床代码,我们就在蹦床代码的开头,它做的第一件事是这个CSR RW,指令,这是这个窗口中的源,在这个窗口中。

我们实际上可以看到GDB在内核中看到了什么,我们实际上刚刚执行了这个交换指令,它将零与Scratch交换,以查看它做了什么,让我们打印一个零一个零,现在这三个f f e,它是一个指针。

它是陷阱帧的虚拟地址,以前是在S Scratch,但我们只是交换了一下,然后我们可以打印出Scratch中的内容,它是2,这是A寄存器的旧值,当然,一个零包含了正确函数的第一个参数,只需文件描述符二。

你知道贝壳通过什么,所以我们节省了零,我们有一个指向陷阱框架的指针,结果发现,现在我们已经在保存寄存器的路上了,事实上,这就是蹦床代码中接下来的三十多个指令所做的,它只是系统地使用这些SD指令。

这些是保存64位存储说明,将每个寄存器存储到陷阱帧中的不同偏移量,交换后现在包含的要召回的零,包含指向陷阱帧的指针,它包含此页的虚拟地址,我们只是在陷阱帧中以不同的偏移量存储每个寄存器,嗯。

所有这些商店都有点无聊,所以我想我要跳过他们,嗯,让我在更远的地方设置一个断点,回答问题,是呀,陷阱框架的地址怎么会出现在Scratch上,当我们把它换成零的时候,好的,在内核之前。

在它之前过渡到用户空间之前,设s划痕等于f的3 f f f e零,零零,陷阱帧的虚拟地址,所以当我们在shell中的用户空间执行时,划痕有这个指向陷阱框架的指针,它跳到蹦床的开始。

蹦床的第一个指令是CSR RW指令,它交换了一个零和划痕,所以现在划痕的旧价值,即陷阱框架的点现在在e零,这回答了你的问题吗?我想我在想我在哪里,c,在进程分配期间是否会发生这种情况,就像你说的哪里。

划痕寄存器实时,这口井在哪里,划痕区域存在于CPU上,它是CPU中的一个特殊寄存器,内核设置它,有点复杂,它的实际设置位置,或者我现在在右边给你看的,下面是内核x的代码。

内核在返回用户空间时执行的最后两条指令,发生的事情是,它在内核恢复所有用户寄存器后做的最后一件事,而且是,你知道的,准备返回用户空间,它实际上做了另一个这样的交换,内核被设置为等于陷阱帧的零。

而s Scratch仍然为保存的用户保留零,所以内核做这个交换,最终划痕有一个指针,那个陷阱架,和一个零,使保存的用户为零,然后返回到用户空间,所以你可能会想,一个零怎么会有,陷阱框架的地址。

这个问题的答案是,我们现在在C陷阱中寻找,最后一个C函数在离开用户空间时运行,c函数做的最后一件事是在这里调用这个函数,不管这个FN是什么,它传递的参数是陷阱框架和用户页表,在C代码中,当你调用函数时。

第一个参数为零,这基本上就是为什么一个零持有指向陷阱帧的指针,这个函数,它的值设置在蹦床页面中,快结束的时候,那个代码,我给你看了,这个代码是一个很好的答案,嗯对不起,我对此也很困惑,我不确定。

当你开始,当您开始您的过程时,它是它是好的和运行,然后在某个时候它不等于,我想在一些或某事,然后你什么时候调用这个返回函数,因为它应该在调用之前调用,但他没有从你那里回来,以前没有回来,我不知道。

我不明白你说的用户陷阱红色是什么,好的,嗯,也许这个问题的一个答案是内核总是,或者机器在内核中启动,所以当机器启动时,它随时都在内核中,进入用户空间的唯一途径,你知道,第一次或从系统调用返回时。

事实上是为了执行这个红色指令,这一行指令是风险的方式,精五定义从主管模式过渡到用户模式,因此,在任何用户代码执行之前,内核执行代码,我给你看这些,这两个编辑器缓冲区设置了各种各样的东西。

就像S刮和ST背,好的,我明白了,谢谢。不客气,我有个问题,我不确定我们是谈到这个还是时间,好的,我错过了,但是当我们在汇编代码中调用e调用指令时,呃,什么触发蹦床代码开始,是嗯的开关吗。

来自主管的CPU模式,用户主管很抱歉,是别的事吗,所以我们正在执行的代码,是这个电话吗?shell在用户空间执行它,E Call做了几件事,E调用指令将模式设置为主管,e调用指令保存程序计数器和epc。

E调用指令将程序计数器设置为,呃,称为ST VAC的控制寄存器,So和TC,是内核在进入用户空间之前设置的许多东西之一,嗯,所以这里回来了,SVEC只是内核–将其设置为蹦床页面的开头,就是这个地址。

这样当E调用发生时,E调用只是将tvec复制到程序计数器并继续,但是现在程序计数器正在蹦床页面中执行这个地址,这澄清了它,谢谢。我也有个问题,为什么保存在陷阱帧中的一些寄存器不应该,嗯。

也是用户程序可以访问的寄存器,呃,为什么,内存中的新区域而不使用程序,嗯好吧,这里可能有两个问题,一个是为什么我们要保存寄存器,内核必须保存寄存器的原因,内核即将运行C代码,它覆盖了这些寄存器。

如果我们想正确地恢复用户代码,我们需要恢复它,它的寄存器具有它们的原始值,你知道当E打电话的时候,让我们执行它,所以我们必须保存陷阱框架中的所有寄存器,这样以后。

我们可以在恢复用户代码之前恢复它们的所有值,也许你问题的另一半是,为什么它们被保存在陷阱帧中,而不是用户堆栈中,答案是我们不确定,用户程序甚至有一个堆栈权,当然有些编程语言没有堆栈。

堆栈指针并不指向任何特定的东西,或者可能为值为零,也有编程语言,他们有一堆,但它就像一种格式,那是,你知道,一些内核不理解的奇怪格式,也许,因为编程语言以堆中的小块为单位分配堆栈。

编程语言运行时理解如何,你知道的,使用这些小内存块作为堆栈,但是内核不知道,所以我们,如果我们想运行任意的用户程序,用许多不同的语言写的,内核不能对用户内存的哪些部分做出任何假设,它被允许存在或有效。

或者可以读可以写,所以,内核在保存和恢复寄存器时必须是自包含的,这就是为什么内核将这些东西保存在自己的内存中,在陷阱帧中而不是在用户内存中,好的,有道理,还有什么,好的,我们还在,嗯,不幸的是。

我们将其称为用户VAC和蹦床代码,我们才刚刚开始执行,我想我刚刚在这个代码中设置了一个断点,在保存了所有寄存器之后,我想我说了一个,此指令的断点,所以我们会继续执行,跳过所有用户寄存器的所有这些保存。

捕鼠器框架,好的,现在我们正在执行这个加载指令,此加载指令加载到堆栈指针寄存器中,加载的是,该值是由零指向的内存块中的第八个插槽,我们记得一个零点,这个陷阱帧到这个虚拟地址,倒数第二页。

以及陷阱页的格式,但是呃,陷阱架,这是陷阱的形式,我们很方便地用字节为单位的偏移量标记了每个字段,这意味着从陷阱帧的第八个字节开始加载,意味着我们正在加载内核堆栈指针。

内核为进入用户空间而设置的东西之一,它将陷阱帧中的这个槽设置为等于这个进程,呃,内核堆栈,这条指令的作用是,初始化堆栈指针以指向此进程的顶部,是内核堆栈,这是您运行用户代码所需做的一部分,所有的权利。

那么剩下的,或者在几个说明中得到这个蹦床代码的结尾,我们加载了堆栈指针,所以也许我可以为你打印堆栈指针,让我们看看,是啊,是啊,这是,所有的权利,嗯,这是果仁,这个过程是内核堆栈,它在高内存中。

因为xv6,治疗内核性,特别是这样您就可以在每个内核堆栈下放置一个保护页,好的,所以这个加载到TP,原来,因为在风险5中没有直接的方法来弄清楚什么核心,您正在运行的多个核心中的哪一个。

xpsix实际上在tp寄存器中保留了称为heart id的核心数,这在内核的许多地方都有使用,比如说,它是内核代码机制的一部分,找出当前在该核心上运行的进程,好吧,如果我们执行这个,希望,呃TP。

我们正在运行核心零,这是有道理的,因为我把qmu配置为只给xvsix一个内核,或者我们确实在核心零上运行,接下来的事情,呃,正在加载的地址,嗯,实际上负载到T零,我们要执行的第一个C函数。

也就是用户陷阱,所以我们在一些指令中使用它来跳跃,用户陷阱,c函数把这个负荷量代入t,呃正在加载呃的地址,内核页表,这样我们就可以打印,所以我们要切换页表,原来呃。

第一个中的这个东西实际上不是内核页表的地址,这是你需要输入SAT P的东西,它是A具有内核页表的地址,但移动了一些额外的标志位,但是一旦CSR W指令执行,我们将把页表从用户页表切换到内核页表。

让我强调一下,通过查看当前页表,现在,我们仍然在用这个非常小的用户页表执行,我要去运行步骤执行负载到卫星p,我们将再次查看内核页面,现在我们在一个完全不同的页表中,对呀,这是内核页表。

你可以看到所有这些不同的内存和设备控制寄存器区域,内核在自己的,呃,大内核页面,我也是,所以我们成功地切换了页表,现在到内核页表,所以我们在这一点上状态很好,因为我们有一堆,嗯,我们有内核页表。

我们可以读写内核数据,我们真的准备好了,嗯,在内核中执行C代码,一个谜题,不过,为什么我们不只是撞车,毕竟,我们在记忆中的某个地方执行,程序计数器保存虚拟地址,如果我们切换页表,那为什么不呢?

只是因为一些随机的垃圾,或者根本没有要映射到脚下的新页表中,我们执行死刑的地方,我们不会在这里坠毁,实际执行指令,他猜到发生了什么,我想是因为,哦对不起,嗯,因为我们还在蹦床代码中。

蹦床映射在同一个虚拟地址,在用户和内核中都是绝对正确的,所以我不知道你是否记得这里的地图,在用户页表中,但是内核页表末尾的这个映射,与,作为用户页表末尾的蹦床映射,所有其他的映射都是不同的。

但是这个映射是一样的,这是保存我们正在执行的指令的映射,因为当我们切换页表时,它没有改变,我们实际上继续执行,我们仍然在执行相同的指令序列,而不是崩溃,这就是蹦床页面的特别之处,它是。

它将用户页表映射到内核页表中,它被称为蹦床页的原因,是因为你在从用户空间到内核空间的路上反弹,随着下一个指令,这个JR,我们即将跳出蹦床进入内核C代码,看看我们要去哪里,JR就像跳到一个子程序。

这艘潜艇是由寄存器T 0指向的,打印T零,看看我们要去哪里,好的,这些是我们将要跳转到的说明,它们在称为用户陷阱的函数的开头,user trap只是在trap c中定义的一个c函数,这里是用户陷阱。

现在我们大概,我们即将从汇编代码跳转到用户陷阱,使用带有内核页表的堆栈,呃,让我实际执行指令,现在我要打开,打开C代码和GDB的显示,现在我们正在执行C代码,好的,所以现在我们在一个更理智的世界里。

我们只是在执行,C代码应该更容易理解一点,还有,你知道的,我们仍然会花一些时间阅读和编写各种有趣的控制寄存器,但希望环境没有陷阱框架中那么神秘,任何功能,任何,在这一点上有什么问题吗,嗯,我有个问题。

我们为什么不和GDB一起看看,嗯,平等是什么,我可能错过了,但我想我们直接跳进了蹦床,开关只是将模式位更改为主管和,equals将程序计数器寄存器设置为等于,无论发生什么。

无论内核碰巧存储在ST VAC内核中,都存储了这个值,我刚打印了三个FFF零零零,也就是蹦床页的开头,内核在进入用户空间之前将此数字存储在TVAC中,所以电话去哪里。

这就是它说程序计数器是tvec的地方,也就是蹦床页的开头,这回答了你的问题,是呀,我想是的,好的,所以现在我们在,呃,当用户陷阱,输入用户陷阱,其实,就像蹦床页一样,然后嗯,就像蹦床页面一样。

对于许多不同种类的陷阱,有系统调用,也有例外,比如除以零或使用未映射的虚拟地址,有一个设备中断,他们都来到这里,所以用户陷阱可以保存和恢复一些硬件状态,但它也需要看看陷阱原因的状态,想出怎么处理它。

所以我们将看到这两件事,因为我们执行,用户陷阱,让我,在用户陷阱中运行东西,让我们看看啊,它在这里做的第一件事是改变stback寄存器,事实证明,XVSix手柄陷阱的方式不同。

取决于它们是来自用户空间还是来自内核,我们只讨论了如果用户空间出现陷阱会发生什么,对于来自内核的陷阱,有一个完全不同的事件序列,在内核中发生的,因为,毕竟,比如说,内核已经在使用内核页表,你知道的。

如果在内核中出现陷阱,您已经有了内核页表,有一大堆事情不一定要发生,如果陷阱发生在内核中,所以在深入内核代码之前,我们更改SD VAC以指向此内核,vac,它是内核陷阱句柄而不是用户陷阱句柄,我们需要。

由于种种原因,我们需要弄清楚我们在运行什么过程,我们通过调用我的proc函数来做到这一点,你记得的心ID,我们把tp,这就是我的产品如何计算出当前正在运行的进程,我们需要保存保存的用户程序计数器。

它还坐在那里,因为,当我们在内核中时可能发生的事情之一,我们可能会切换到另一个过程,另一个进程可能进入该进程用户空间,另一个进程可能会进行系统调用,导致SCPC被覆盖。

我们必须将EPC保存在与此过程相关的一些内存中,这样它就不会被覆盖,它得到了,你知道的,我们用这个陷阱框架来保存EPC,还有很多其他的东西,无论如何,这就是当前代码行的作用,接下来发生的事情。

我们需要弄清楚,我们为什么来这里,原因,风险五的原因登记册有不同的号码,取决于这个陷阱发生的原因,某种异常对系统调用对设备中断值8表示我们在这里,我们中了一个陷阱,因为一个系统调用。

希望这真的叫我们八个,它确实有八个,因为我们在这里,因为一个系统调用,所以我们要执行这个if语句,第一件事就是,如果其他进程终止了此进程,我们不想继续,但我们的外壳不是这样,嗯,原来风险五。

存储在SEC中的程序计数器,是导致陷阱的指令的地址,但当我们继续,我们想在E呼叫后的下一个指令时继续,因此,我们为需要返回的系统调用编写了这段代码,我们向保存的用户程序计数器添加四个。

这样我们就可以继续下一个指令了,不要只是执行调用,好的下一个,事实证明,XV允许中断,当它处理系统调用时,只是为了更快地服务中断,有些系统调用需要很多时间,嗯,中断总是被风险五陷阱硬件关闭。

所以我们必须在这一点上明确地打开它们,接下来发生的事情是我们调用这个syscall函数,系统调用的工作,我就进去,定义在只调用c这里我们是,它所做的是,它查找系统调用号码,在页面顶部的系统调用大表中。

如果您还记得shell写函数,请将寄存器7设置为系统调用号码,也就是十六代表权利,所以Syall所做的是,它找回了在陷阱中被拯救的一个7,因蹦床代码而成名,它用它来索引到这个指针表。

到实现每个系统调用的函数,钓这个数字,我现在要打印NUM,那确实是同一天,十六岁了,贝壳最初放在那里的16个,系统调用代码索引到该syscalls表中,我们可以找出什么函数来。

它通过现在踏入它而脱离了称为表的系统,我们是对的还是对的,所以说,在这个文件中,C这个右边是正确系统调用的内核实现,我不打算谈这个,这就像相当复杂,从现在开始会发生什么,以及本讲座制度的实施要求。

我只对进入和退出内核感兴趣,所以我要,实际实现系统调用一件事,需要注意的一件有趣的事情是,系统调用需要找到它们的参数,所以你知道,记住要写一两个论点,一个缓冲区指针和另外两个。

被称为代码的系统获取它们的方式只是查看这些,嗯,看着陷阱框架,就像我们可以在陷阱框里寻找7,我们可以找一个零,这是第一个论点,我们看一个,那是缓冲点,在那里我们可以看到一个二,这是第二个论点,的数量。

嗯,要写入的字节,好的,所以系统调用完成了它的工作,然后是赖特,终于回来了,嗯,我们要看看会发生什么,在这里分配这个任务的原因,我们在陷阱帧中赋值为零的原因,这个系统调用都有一个返回值,喜欢写返回。

写入的字节数,关于风险五的约定和C代码是返回值放置在,通过调用的任何函数注册一个零,因此模拟返回值,我们只需将返回值粘贴在陷阱帧中的一个零中,当我们回到用户空间时,我们会看到这个。

陷阱帧中的一个零槽被恢复回实际的一个零寄存器,shell将零值视为,作为右边的返回值,如果我们只是执行这个并打印专业陷阱帧零,我们会看到它有第二个值,这意味着权利,返回值为2,说它实际上写了两个字节。

就像指示的那样,好的,在这一点上,我们又回到了,用户陷阱中的陷阱C,就在调用系统调用之后,所以现在有了这个P o,我们正在检查,如果恐惧,如果进程已被终止,因为您不想继续执行,如果它已经被杀死了。

但我们的外壳当然没有被杀死,跳过这个,然后嗯,用户陷阱调用单独的函数,用户陷阱红色,为了设置我之前说过的所有东西,每当我在进入用户空间之前说好的时候,内核确实很糟糕,废话,废话,嗯,它的用户陷阱红色。

负责设置所有这些东西,所以我们可以看看它做的所有不同的事情,它,嗯,它转过来,中断,它们在系统调用期间被打开,他们现在关机了,因为我们要改变ST VEC,而当我们在内核中的时候,它指向内核陷阱处理程序。

所以我们关掉中断,因为一旦我们将tvector点更改为用户陷阱处理程序,我们仍在内核中执行,如果发生中断,然后它将转到用户陷阱处理程序,即使我们在内核中执行,出于各种详细的原因,嗯。

这会导致内核出现故障,所以我们关掉中断,它们是从时间之间停止的,从下一行开始,在那里我们设置了ST VEC指向蹦床,一直通过返回用户空间的最终红色指令,原来蹦床转弯结束时的红色指令,打断了回来。

所以当我们在用户代码中执行时,中断是打开的,尽管我们刚刚把它们关掉了,接下来的几行,填满我们在货舱前看到的那些陷阱框架槽,方便蹦床代码的各种值,所以说,这里的代码隐藏了一个指向内核页表的指针。

在陷阱框架中,它隐藏了一个指向此进程内核堆栈的指针,它藏在陷阱框架里,um指向用户陷阱函数的指针,这就是蹦床代码在最后跳转到的,这样蹦床代码就可以恢复相同的值,因为用户代码可能干扰了它,所有的权利。

问题是的,嗯,我们为什么不保存电脑在蹦床,它本来可以,是啊,是啊,是啊,是啊,我的意思是SCPC可能是蹦床代码,碰巧没有保存它,连同其他寄存器,我们完全可以修改xpsix来保存它。

我是说它实际上被保存的地方,你可能还记得,只是碰巧把它保存在用户陷阱和C代码中,而不是蹦床代码中的装配代码,我不认为这,我想不出这样或那样做的好理由,用户寄存器确实必须保存在程序集代码中。

因为任何C代码都有权为所有人,我们知道,编译器生成代码,修改任何,用户注册,因此,在输入C之前,将这些用户寄存器保存在汇编代码中是非常重要的,但我们本可以早一点或晚一点被拯救的EPC,好的。

所以我们陷入了用户陷阱,红色准备陷阱,我们准备了陷阱框架,里面有所有这些需要的值,下次有一个从用户空间到内核的过渡下一个陷阱,嗯,我们必须在状态控制寄存器中设置一些东西,嗯,原来这个SPP位,状态控制。

是否控制红色返回的模式,通过清理它,我们只是说好,你看,下次我们执行S红色,我们想进入用户模式而不是主管模式,这个馅饼位控制,在我们执行红色后是否会启用中断,你知道,在我们进入用户空间之后。

我们确实希望他们能够,所以设置SPE位,然后我们将把这个新的修改后的状态写入实际的硬件中,国家登记册,嗯,原来Srat是做什么的,你知道的,这是正确的,我们将在蹦床代码的末尾执行。

红色的作用是将程序计数器设置为SEC寄存器,所以我们现在要设置epc寄存器,使保存的用户程序计数器,那个,如果你还记得我们最近在陷阱框架中保存的,这就是这里发生的事情,嗯,你还记得陷阱框架,呃。

包含指向内核页表的指针,因为蹦床需要切换到它,嗯,我们需要制作你需要写到ATP的特殊位模式,这里已经完成了,嗯,我很抱歉,我们现在正在准备一个指向用户页表的指针,我们需要在进入用户空间的路上切换到它。

我们实际上要在汇编代码中进行切换,因为它必须发生在蹦床上,因为只有蹦床中的代码映射在用户和内核空间中,所以说,我们只能真正切换页表,当我们在蹦床上表演的时候,但我们不是在蹦床上执行。

但我们仍然只是在一个普通的C函数中,所以我们准备这个指针,我们将把它作为第二个参数传递给程序集代码,在一个,这条线的作用是计算我们要跳转到的地址,在蹦床代码的末尾,事实证明,我们想要去的地方是用户阅读。

用户写入将带我们回到用户空间的指令地址,这个小公式,地址和虚拟地址,与该用户重新功能相对应的蹦床,好的,所以我们在函数中计算该用户的地址,然后这个烂摊子,下一行调用使用这个fn变量作为函数指针。

并跳转到这个函数,其中有两个参数,分别为零和一,我们现在可以去,在蹦床代码中,有,首先,我们切换到用户页表,所以这意味着实际执行这个,我们可以看到页表的变化只是为了咯咯笑,我们仍然使用巨大的内核页表。

我将重新运行用户陷阱,直到我们到达它跳到蹦床上的地步,所有的权利,我们在蹦床里,这些是即将开始执行蹦床代码的指令,就在这里,我们现在可以再次,让我们打印页表,它仍然是内核页表,这里要发生的第一件事。

虽然,蹦床代码,将把指向用户页表的that指针加载到sat p寄存器中,所以我们切换页表,如果我现在键入信息地图,我们现在有一个小得多的用户页表,但幸运的是仍然有蹦床页面地图。

这样我们就不会在下一个指令上崩溃,接下来发生的事情是这样的,嗯,我刚才向你们展示了这个代码序列所做的倒数第二件事,在通往用户空间的道路上是交换的零划痕,因此,我们需要将保存的用户设置为零。

所以当我们这样做的时候,交换一个零,最终将使保存的用户为零,原来零是指向陷阱帧的指针,因为C代码传递了,作为第一个论点,一个零的十二分之一是保存一个零的地址,在轨道框架中,我们要加载它,然后进入T 0。

然后把它加载到Scratch中,现在,在这一点上,我们仍然运行内核的东西,和所有的寄存器,接下来的三个两个指令,尽管从陷阱框架中加载出来,加载的零点,从陷阱帧中保存的所有用户寄存器,实际登记册。

所以我们真的很接近能够,到我们可以运行用户代码的地步,我将跳过所有这些负荷,在我们之前有什么问题吗,呃接近用户空间,我有一个快速的问题是陷阱框架中的值是零,现在我们所做的系统调用的返回值,是啊,是啊。

是啊,是啊,继电器零点就在陷阱框架下保持这个,但是我们打完井之后,好的,用户当前保存的位置啊,是呀,我称之为用户保存了一个零,但实际上系统调用返回的东西,用返回值覆盖它,我们希望shell为C和零。

所以它的当前位置是零,我们用两个覆盖了,因为返回值是s Scratch,所以我要指出S Scratch,并希望它等于两个,等于2,这回答了你的问题吗,是呀,我想是的,好的,我将跳过所有这些负荷。

将保存的用户值从陷阱帧还原到寄存器中,我现在为什么不把寄存器打印出来呢?我不知道这些看起来是否眼熟,但它们碰巧是同一组用户寄存器,我们在这个练习开始时就看到了,比如说,其中一个堆栈指针保存这个小值。

适用于内存不足的用户堆栈,一个是我们传递来写的缓冲区指针,二是字节数为零,但是是异常没有保存用户值,因为它仍然有指向我们陷阱框架的指针,嗯,但让我们看看,我们即将执行,我们即将执行。

这个CSR我们的W是零擦伤吗,就在蹦床的尽头,就在返回用户空间之前,它会交换一个零和划痕,刮擦确实有,有两个,这将是返回值,零有这个内核指针,它指向,指向陷阱框架。

但是在执行csr rw指令交换它们之后,希望我们会看到一个零保持这个返回值为2,Scratch持有指向陷阱框架的指针,这是内存中倒数第二页,这个值将保持不变,直到用户程序做了另一个陷阱,在这一点上。

我们之前谈到的陷阱处理代码,我们将能够在陷阱框架上使用作为抓取数据,所有的权利,我们还在内核里,但这是我们在内核中的最后一条指令,当我执行,这是真的,它就会切换到用户模式,嗯,在我这么做之前。

让我们来看看,它会切换到用户模式,它会复制秒到电脑上,因为我们仍然,我们还在用PC执行,那是在蹦床里,所以房地产将切换到用户模式,将SCPC复制到PC,然后继续执行,所以我要去运行红色繁荣。

现在我们又回到了,回到地址零x DEA,这是一个低地址,可能是用户内存,这是回首在S H ASM和地址XX药品管理局,确实是右末尾返回函数的地址,零是应该转动的返回值,所以我们又回到了用户空间。

我们可以回到壳里,从正确的系统调用,从进行系统调用的正确库函数,好的,有什么问题吗?呃,对不起,你能再说一遍吗?在s期间中断会发生什么,中断发生了什么,你说我们要把它们关掉。

但后来有别的东西让他们回到了老鼠的身边,什么启用中断,所以这是遗憾,我们在内核中以主管模式执行的最后一条指令,我刚才忘了说,但是除了设置程序计数器,等于SCPC并切换到用户模式,红色将重新启用。

所以我的意思是,你知道的,那是,你知道的,可能运行很长时间的用户程序,如果能接受磁盘中断就好了,什么的,哇哦,用户程序正在运行,我明白了,谢谢。其他问题,好的呃,结束系统调用,有点像函数调用。

就像函数调用一样,但是用户内核转换比函数调用复杂得多,有很多复杂性,由于隔离要求,内核就是不能信任用户空间中的任何东西,以及拥有简单快速的硬件机制的愿望,所以实际上XPSix并不太关心性能,但总的来说。

操作系统设计人员和CPU设计人员,非常感兴趣的是,在这种速度下,你可以做到陷阱的效率,嗯,xp六,是不是,你知道,以一种特殊的方式做所有这些事情,当然还有其他方法,几个问题,几个设计问题。

你可以考虑的替代方案,嗯一个是,你能想出办法让硬件或软件方面重新设计吗,十五六,重新设计风险五,使整个序列更简单,或者你能想办法让整个序列更快,和另一组问题要留在你的脑海里,嗯。

恶意程序是否会滥用这些机制,所有的权利,我要说的就是这些,我很乐意回答问题,对不起,我还有一个问题,我看到有一个嗯U,我注册了还是的,我想登记身份,但我们不用它,我们只使用i e。

并在用户空间中将其设置为false,为什么我们不能用UI UI天哪,答案会是,我不知道,呃,我们是SP,我们设置的是S我们设置的是S P,我是,我们最终可能会设置,我对UIE一无所知,我来猜猜。

让我看看,我猜在这个代码中实际发生了什么,我们在哪里,好的,所以我们在用户陷阱中,对吧,我们正在返回或将SPI E设置为S状态,我相信S红色指令会复制这个S P I E,这是以前的,的名字。

这是主管以前的中断启用,我怀疑SRT复制了咬进,呃,进入用户模式中中断的任何控件,这可能是这种UI E和S状态,你猜得怎么样?我明白了。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/2560cc93e558d8e9bdb7ab02e0210a2a_5.png

P6:Lecture 7 - Q&A for Labs 中文版Beta - MCATIN-麦可汀留学 - BV1rS4y1n7y1

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_0.png

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_1.png

好的,下午好,无论你在哪里,呃,大家能听到我快速检查声音以确保,是的,你很好,好的,好,谢谢。嗯,所以今天,呃,我们要谈谈,呃,我真的没有具体的议程,但基本上我们的计划是试着回答你可能有的问题。

上一个实验室或以前的实验室,所以我要采取的方法,呃,今天我将介绍员工的解决方案,嗯,尤其是我自己的解决方案,嗯,你知道,讨论他们,希望当我们通过您的员工解决方案时。

如果我不回答某个特定的问题或稍等一会儿,你可以跳进来,因为我把你问的所有问题,至少在今天上午十一点之前在底部的课堂讲稿中,我们会试着经历这一切,我将从页表应用程序开始。

因为大多数问题都是关于页表实验室的部分原因是六月,这是最难笑的,嗯,所以在深入技术之前,也许有几个评论,呃,这其中的一部分,呃,让我们来谈谈,呃,页表实验室一般,你知道,正如你可能观察到的那样,你知道。

实际上有几行代码,就解决方案而言,但不幸的是,你知道,难以揭穿,你知道的,有,一个原因是你知道当盒子出现的时候,你知道结果是非常极端的,就像在最坏的情况下,可能你们中的一些人观察到,在最坏的情况下。

通勤站,或XP六站,再也没有输出被打印了,就是这样,你知道这是你的工作来弄清楚,实际上哪里出了问题,呃,在最好的情况下,你知道你会有一种内核恐慌,但恐慌,你知道也许只是一个起点。

或者用来追踪问题的实际来源,你知道恐慌本身,可能跟别的事有关,所以内核中的不变量会被打破,但你知道早些时候在外面的某个地方,可能是在设置页表的时候,你知道你做错了什么最终导致了这种恐慌。

所以你必须追踪发生了什么,所以更少的代码行,难以调试的问题,当时的环境很恶劣,呃到桶里,呃,如果在内核调试中这样做,或,你知道内核编程,你知道的,使用编程环境或调试环境是,呃,更多是不可原谅的。

因为罗伯特在第一节课中提到,这是基本上做内核编程的困难部分之一,就是这样,你知道的,只是为了让你感觉好些,你知道的,只是对你来说很难,你知道这对你的员工来说很难,在帮助你方面,实际上,你知道的。

当我们做这些实验室的时候,然后我们做类似的问题,你知道我们可能在追踪虫子方面有更多的经验,但你知道他们只是他们确实需要时间,呃通过帮助你,你知道,它往往很难,我也是,因为有一些小的代码片段,有一个细节。

可能出了什么问题,他们正在想办法,实际上那个级别的细节是什么,呃,没那么容易,当然啦,你知道我们以前见过的一些问题,我们认识到,你知道的,比如说,我们自己没有犯同样的错误,但其他的,你知道的。

你发现了各种不同的方法来打破内核,我们以前从未见过的,所以这只是心,我认为这是一个,你知道吗,根据我们的经验,对于虚拟内存来说,这在历史上总是很难的,呃,所以每次第一个虚拟内存实验室。

要么在八一要么在它的前身,六点八到八点,必须是你知道的情况,这往往是所有实验室中最难的一圈,你知道有很多原因,一个,你知道恶劣的环境,你知道,呃,虫子太壮观了。

部分原因是你知道你还没有那么多内核编程经验,所以这是第一圈,所有这些东西聚集在一起,呃,今年我们试图让实验室更简单,呃,事实上,这是上周的一个新实验室,我们没有洛杉矶。

希望能让这个介绍或向虚拟内存的过渡变得更容易,在某些方面,我认为人们是成功的,在其他方面,你知道这仍然很难,所以我们真的不知道,也许真正引入虚拟内存编程的最简单的方法是圈数,下周实验室。

懒惰实验室实际上是第一个虚拟内存实验室,我怀疑今年你会发现,并不比去年学生们发现的容易得多,因为当你现在有更多的背景,也许这是一个很好的库存点,这是我在跳入之前想说的几个高水平的观点,技术越好。

细节越多,所以如果你有任何问题,是提问的好时机,快点,呃,你们知道你们中的一些人要求这个,呃,非常快速的回顾,你所知道的,在哪里,呃,你知道吗,这种设置是,嗯,所以基本上设置是页表,我们有物理记忆。

我在右边这里画,我们知道物理记忆,呃部分是关于设备的,他们生活在,你知道在这头牛上面,你知道零零和许多,更多的零,然后这里基本上是DRAM芯片,我们知道Q U实际上把内核,内核文本和数据,你知道对。

你知道,在这里,都在零以上,x,zero,zero,zero,所以这是内核所在的地方,在某种程度上,也许让我把它擦除一点,以使用内核,你知道,基本上是字面上的意思,你知道和关系有什么关系。

或者通常是说明,以及您所看到的汇编指令的二进制版本,还有一些数据结构位于这些,好的,所以这是它的记忆部分,然后在另一边我们有执行指令的CPU,所以CPU有一堆内部状态,嗯,还有一些寄存器。

你知道什么X零或R零什么的,废话废话,你知道也许程序计数器,你知道x 6开始,您知道已证明的计数器包含此值,哦,xzero,和,这就是你知道CPU知道的方式,基本上你知道,看看那个地址。

你知道第一条指令,它查看第一条指令,解码指令,并更新任何需要更新的CPU状态,你知道页表添加了某种组件,呃,你知道地址,你知道CPU,其他或指示是指示的一部分,就像你知道的,跳转到某个特定地址。

你知道吗,这些地址通常或最常见,或者会有虚拟地址,那些虚拟的衣服去了水果,你知道有些东西叫做mu,mu翻译成物理地址,嗯,这允许我们索引到,o,内存的一部分或内存的结束部分,控制此翻译如何发生。

你知道有一个ATP寄存器,其中包含当前正在运行的页表的页表的根,如果没有,那基本上就没有翻译了,虚拟地址直接是物理地址,所以当处理器真正启动时,SATP没有价值,所以当程序计数器是牛h x时,你知道吗。

基本上物理地址也总是调节,基本上这个道具,CPU,你知道吗,获取指令,你知道从那个特定的位置,一旦你知道,一旦SATP实际上包含一个非零值,然后呃,你知道的,比如说,也许在这里的某个地方。

就像我们会把它们画出来一样,使图片进一步扩展,我们知道内核实际上最大有128兆字节,所以基本上这都是自由内存,从这里到那里有空闲内存,空闲内存被放在calc中的列表中,就像你以前看到的那样。

所以这里的某个地方也是根页表,因为你知道一个内核页表,所以也许这里有一页,这就是根,一旦我们加载了那个值,您知道该值的物理地址的地址进入SAP地址并寄存器,这么说吧,也许这个地址是O x 7,你知道的。

f废话,我知道下面的一些事情,在两千万兆字节上,或者在内存的空闲空间将在SATP中加载该地址,在这一点上,你知道的,处理器或mmu将使用根页表来实际进行翻译,你知道从维吉尔到物理地址,你可以想到这个。

你知道这整件事,有点像一个单一的盒子,你知道集成的,你知道得到任何关于这张高水平照片的问题之前,让他们继续前进,所以说,记住这里的关键点之一,付费状态本身也生活在记忆中,好的,好的,所以让我们呃。

跳转到页表应用程序的第一部分,就是,呃,第一部分你必须打印,呃的页表,初始化程序和嗯,你知道吗,基本上用这个图3 4来解释它,数字三四就在这里,在这张图中这是图三四显示了用户地址空间。

让我们看一下用户地址空间,在我们详细讨论之前,所以我们在底部有文本,这是程序从地址0开始的指令,上面的说明是,你知道的,数据全局变量,呃住在那里,呃,然后有一个我称之为守卫页的东西,我们来谈谈。

然后是用户程序的堆栈,所以内核有自己的堆栈,呃,但是通常的或多个堆栈,用户程序有自己的堆栈,上面的基本上是所谓的堆内存,你知道自由记忆,你知道我们可以,你知道得到更多的记忆,使用s break。

所以我们休息吧,基本上指向用户地址空间的顶部,我们想成长,我们称之为S中断系统,呼唤成长,长出顶部长出底部部分,你知道到堆里,我们也知道在顶部,从上一节课有两页,两个特别页面,蹦床和潮流框架。

陷阱火车页和蹦床页,你知道,包含这些指令,基本上是在内核中转换和转换,轨道框架是,呃,是呃,在这个地方,你知道,呃,存储一些状态,当我们跳入内核时,因为我们需要内核,一旦它使用寄存器。

我们不能使用寄存器,因为用户空间程序仍在使用它们,我不打算多谈,陷阱架和蹦床,但你在那里,嗯好吧,所以下面的这张照片,呃,你知道实际上是打印出来的,当我读它从init,就像,这是执行的第一次成功呼叫。

对,就像我们知道的在代码中,做一些系统,为其中的程序调用exec,就在出口的尽头,我们打印出页表,所以有几件事,我们知道很多我们可以观察到的关于这个特定页表的事情,所以首先。

你知道我们除了打印出物理地址和物理地址,我也在打印旗帜,你知道你可以在这里看到,旗帜是一体的,它基本上说这个翻译,或者这个,呃,中间页这里差不多,那是一个包裹在页面之间的,它们基本上是对应的。

你知道到第二级页面,然后第二个对应于水平,对不起,我只是把箭头拧错了,所以这就是为什么我停顿了一秒钟,好的,所以上面,这是顶层页表,这种指向这个特定的页表,然后你知道这个指向那个特定的页表。

或者这就是我们所指的地址,有点像说,这是特定页面的基地址,如你所知,每一页只有4。96字节,就是页面大小除以64位,你会得到五到十二个条目,好的,所以您看到的是init地址空间的底部。

基本上所有这些都有免费页面,只有空闲页面,我们可以从免费页面上看到一些东西,我们知道所以底部的页面,基本上对应于虚拟地址零,呃这是在实际地址,你知道八七六,呃四零,你知道我们从之前的照片中知道。

内核可用于分配页的空闲内存,然后你看到的最后一件事,我们看到旗帜,我们向你展示旗帜有一个F,因此这意味着读取位是有效的数据集,W数据集的读取数据集,执行数据集,你有一套,因此此页可以包含数据和文本。

这些权限允许您了解用户程序,呃执行指令,从它到写入内存,并从用户空间执行,好的,所以也许最有趣的问题是第一页是怎么回事,你知道作为一个暗示,你知道我们看到第一页,呃只设置了f而不设置了u位,是卡片页吗。

是啊,是啊,这是守卫页,对呀,所以保护页,呃,被映射,因为它有一个观点,但它没有U位设置,所以任何试图,因此,如果用户程序从堆栈中运行,所以堆栈从上到下增长,所以如果它真的有更多,你知道的。

它的堆栈足够大,可以容纳96个完全满了,如果那样的话,用户程序,你知道的,把东西推到堆栈上,堆在地上,它会成长为保护页,因为U位没有设置,呃,我们会得到一个页面错误或对内核的跟踪,mu不能翻译为地址。

呃无法解决,无法将保护页上的任何地址转换为物理地址,因为遇到了没有单位基本上禁止翻译,好的,什么在下面,第二页,是的,那是堆栈页,再一次是四个的堆栈页,四,四千九十六字节。

你会看到这在权限方面是完全详细的,它已经,你知道的,一切,如果我们想的话,我们能把它弄得更紧吗,是啊,是啊,我想你可以禁用可执行位,是啊,是啊,您可能可以执行x位,这将禁止堆栈上有任何程序代码。

所以如果你喜欢动态生成代码并把它放在堆栈上,你知道你不能执行它,你知道这可能是件好事,所以我们本可以更紧密一点,好吧嗯,这基本上是页表的底部,所以现在让我们看看剩下的条目,所以基本上只剩下两个条目。

也许旗帜部分是最能说明问题的部分,呃,所以七个,呃,你知道,意味着你知道,改为,写,那个权利,那么我们认为这是什么,也许我也会做另一个,所以B是,我想是一个零,零一,所以大概是x和有效的。

那么我们怎么看,五十一是,蹦床和陷阱页面,是啊,是啊,所以电车很清楚,那是蹦床,呃,并可能找到武器绝对是蹦床正确,因为它设置了x位,所以我们必须被处死,或者我们允许从那里执行指令。

所以它想让我蹦床沿着右边,你知道到那一页,所以这可能是跟踪页面,因为那是我们用来恢复和保存的,注册它,好的,其中一个,可能是,主,最重要的是要注意的是没有你咬,那么这意味着什么呢,就是说。

用户程序实际上不能执行实际在5-10的指令,在蹦床页面,无法读取该页的权限,所以只有内核可以从那里执行指令,它只能读或写,所以基本上内核会在,同时仍然使用用户页表,这就是重点,是啊,是啊,就像我们一样。

你知道的,罗伯特在上一节课中解释说,这就像是从用户到内核的过渡,在我们跳到,在我们加载到TP寄存器中的内核页表之前,我们需要一点点,你知道的,内核需要一点内存来完成他的工作,好的。

所以在这张照片的其他几个有趣的地方,嗯,所以我们都在所有这些地址,对呀,八七,六,三个,零,八,六,两个,四,七一,这些都是页面或内存,在内核的那个范围内,不是的记忆,你知道这基本上是三个,嗯。

这些地址是,呃,毗连,现在他们不必是不,他们不必是,他们是不正确的,你知道,看看这个,你知道七个,六,四,零零,如果我们配置,那么下一个地址应该是八个,七,六,你知道六个,五零,对呀,而且它是。

所以页表没有什么很酷的地方,即使虚拟地址空间可能是连续的,物理地址空间或与之配套的物理页,连续的虚拟地址不一定是连续的,因此,这给了内核很大的灵活性,你知道的,分配款,和,呃,三页。

对这部分有什么问题吗,一个我有个问题,是啊,是啊,嗯,你能解释一下SVR K吗,嗯还有,如果我们要在以后的讲座中讨论它,也许我们可以只喜欢一个小的,是啊,是啊,实际上我们,让我来,我星期三再谈。

这实际上将是懒惰实验室的主题,所以让我推迟这个问题,到周三,呃,那就不够清楚了,请再问一次,听起来不错,谢谢。所以有一个问题,是啊,是啊,我记得书上说,蹦床和陷阱架在地址空间的顶部,是啊,是啊,绝对的。

但在这里它停在,就像第一个,根页表,指数是2,5,5不是5,1,是啊,是啊,精彩的问题,非常好,我很高兴你问了,我在问答中看到了,我正打算谈谈这件事,但我当然忘了,是啊,是啊,这是怎么回事。

你知道为什么2 5 5不是5 11,你知道我们,我们总是说蹦床住在地址空间的顶部,嗯,地址空间的顶部,正确的是,你知道有一点实际上是顶层目录的入口5-11,只有两五五,任何人,有什么想法吗。

为什么会这样,我们说过我们要用的一点,我们实际上没有使用,因为符号扩展问题,因为这只会让事情变得更容易,我们也不需要那种记忆,是啊,是啊,所以呃,所以这正是正确的答案,所以这基本上是愚蠢的技术,嗯。

所以虚拟地址原则上是,呃,我想是三十九位,当我们实际上在XP六中,只用其中的三个八个,结果,你知道的,对我们来说最大va的顶部基本上是255个条目,我们不使用三九位的原因是没有什么特别的,呃。

除此之外的好理由,基本上如果你有第n位的设置,然后剩下的,呃,64位地址中的位必须是一次,所以我们只是不想处理这个问题,如果我们设置了三个九个位,我们还得把四四十放在第一位,第四十二至四十三,等。

直到六十四,所以这就是期望,这有道理吗,那是一个非常,非常好的观察,面团,对不起,我也有一个问题,为什么文本和数据在同一页上,啊,很好的问题,也是,呃,那看起来很愚蠢,对呀,我是说,你知道。

为什么不把它们放在单独的页面上呢?这样您就可以设置权限位,呃更仔细,主要原因呃,我们这样做不是为了简单,这只会让执行变得更加复杂,我们想要尽可能简单准确的。

所以一个真正的操作系统你知道在同一个页面中没有数据和文本吗,事实上,我们必须指定,如果查看make文件中的加载程序标志,你会看到它有桌子一个选项,力实际上是数据和文本,在连续的页面中。

而不是在单独的页面中,还有什么问题吗,我有一个关于我们使用的比特数的后续问题,嗯,你说我们只用三个八位,硬件仍然为我们提供了三个九位,但是我们正在设计我们的操作系统,这样我们就使用了38,是啊,是啊。

所以我们基本上如果机器有更多,呃,公羊比二到三八,我们就不能用那个公羊了,现在我们已经在运行,我们假设的内存基本上比2-2-3-8少得多,所以对我们来说没什么大不了的,但真正的操作系统会做得更好。

所以纯粹是为了简单的复杂性,想让它像你一样容易,通过阅读可能的几行代码,是啊,是啊,有道理,好的,所以现在,呃,让我们呃,切换到,呃,第二部分,嗯,所以让我们提出,你知道,一幅画,你可能看了很多,呃。

内核着装空间,右边左边是虚拟服装空间右边是物理内存,呃,你知道的,这是你的i o设备,然后从这里开始就是,你知道的,DRAM,你知道,基本上遇到了实际上一二,两个,8兆字节,为了我们。

因为我们只是假设不超过250比120,八兆内存,所以这部分物理内存是自由内存,从现在开始,内核,哎呀,我有点画错了,但要小心一点,呃,所以今年我们基本上有,你知道,内核文本和数据,然后你知道。

你擦的这段记忆,你知道,基本上是内核分配器拥有的内存,从那里,我们为用户程序分配内存,我们为页表分配内存,等,内核从那里分配一切,直到内存不足,当它用完的时候,它达到了128MB。

然后它开始返回错误或系统调用,很好啊,所以让我,呃,把我的呃,第一部分,在某种意义上正确地完成了这项任务,作业的第二部分是,使用或复制内核页表运行,以便每个进程都有自己的,呃,内核页表,嗯。

这基本上就是这里的任务,所以让我跳到代码中,让我说几件事,呃,关于它的更一般的事情,呃,也许第一个问题,真的,你的头就像,你知道,在某些方面,你知道做一些琐碎的事情,我们已经有了犯罪页表。

我们只需要把它做完,你知道,每个特定的过程都有一个副本,嗯,你可能会说好,你知道这有多难,呃,事实证明我认为这对几个地区来说有点难,一些好的,一些不太好的,呃,硬化和接缝。

你知道一个原因是你知道XV6代码,呃,它是专门为一个内核页表设计的,你知道你在KVM中看到了这一点,在里面,所以这让它有点,你知道的,概括,你知道这实际上是一点点工作。

因为你实际上必须修改XT Six代码,嗯,KVM在里面,正如你所看到的,这并不是故事的全部,为内核构建页表,你知道鳄鱼里面也有东西,它实际上将映射添加到当前页表。

然后Vero磁盘中甚至有一些东西实际上可以交互,你知道的,内核页表,所以基本上内核中没有一个单独的地方,Alexis实际上在哪里构建了内核页面状态,那么你知道这有点复杂的第三个原因。

是因为你还得处理清理工作,呃,所以实际上创建这些副本的方面,但是每当用户进程退出时,我们也要清理一下,和那些正在使用的页表,因为我们想把它们送回自由内存池,这样我们以后就可以用了。

这样我们就可以继续运行进程,那么,这让事情变得有点复杂,因为你知道,在实际释放内核页表时,我们必须小心一点,或内核页表的副本,但我们当然不想,你知道的,实际仍在使用的空闲内存。

其他页表仍在使用重复表条目,所以我们得小心点,然后呢,呃,你知道的,基本上是,呃,很容易在页表中划出一小块区域,当您复制这些页表时,你知道如果你得到一个小东西,呃,你知道,从视觉上看。

你会得到一本坚硬的书,这里的一个问题是,正如我所说,稍微早一点就是,呃,硬bug出现了,很久以后,您构建了内核页表,或者构建内核页表的副本,一切看起来都很好,你看在ATP中加载它。

然后甚至可能内核运行一段时间,然后它就慌了,事实证明你知道,恐慌的原因是因为你知道,你在页表上犯了一些小错误,长,很久以前,所以这就是它使,呃,使内核编程变得困难,你知道,基本上你知道,这些很难装箱的。

基本上只是追踪很耗时,因为这个bug,否,它发生了,呃,这实际上不是bug的真正成本,但是虫子,你知道当你设置页表时,真正的成本会更早,所以有两种方法可以解决这个问题,现在到这一圈,这有点,呃。

两种解决方案,事实上,你们中的一些人可能会使用它们的混合物,只有一个,你知道的,我称之为复制方法,复制方法基本上是将副本复制到内核页表,所以每次你有一个新的内核页表,为页表分配页,你把它们填满等等。

等等,有一种方法,第二种方法,基本上是分享,呃,你知道的,内核页表,在这种情况下,你所做的是,而不是试图制作内核页表的一个漂亮的干净副本,您共享所有基本上将未修改的条目,你从作业中知道。

基本上以上的任何东西,对公共地址的声明实际上将保持不变或未修改,你知道在第三部分没有什么要加载的,所以你知道基本上所有零上的条目可能都是相同的,所以如果你愿意,你可以分享这些条目,嗯。

所以这两种方法都是,我觉得很好,不太清楚,哪个比较好,呃,我采取这种方法的解决方案,我真的没有充分的理由这样做,除了可能,呃,你知道的,部分洛杉矶,我不想想得太多,内核页表中有什么。

所以我想所有的东西都会保持相同的氢,把它们复制过来,或者你知道你复制了POVER,然后我就不用想太多了,内核地址空间的那一部分实际上是什么,你知道泄漏到短代码,但你知道我不确定它实际上比,比如说。

复制解决方案,但重要的是要认识到,基本上有两种不同的方法来解决这个特殊的问题,在任何一种情况下,你知道无论你用什么方法,你知道有一个实施策略,你知道我使用的实施策略,对于几乎任何内核程序来说。

都是在小步中完成所有事情,所以我脑子里可能有一个大致的计划,关于我是如何,我想做的所有改变,但一旦我开始做这些改变,我确实喜欢一两个,然后先确保那些工作,然后你知道,继续走。

我做的另一件事主要是作为一种策略,您保留现有代码,不要真的修改它,当然不是最初我只是添加代码并切换到这个新代码,然后小婴儿的脚步,我这么做的原因是这样我就可以很容易地比较所有的新代码。

我总是有一个有效的旧解决方案,我可以回滚到,呃,所以万一发生一些奇怪的虫子,然后我可以回去也许一步,然后再试一次,弄清楚实际上,我的推理是错误的,但基本上你知道,婴儿步,什么。

部分原因是这些街区很难找到。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_3.png

好的,呃,我来换一下,呃,到我的代码,所以也许从VM点CD开始,呃,所以这个,这是现有的KVM在里面,实际上每个人都能看到代码吗,好的,嗯和嗯,我们被要求的正确作业是复制它,我这样做的方式是。

我看看在哪儿,这是我的UVM创建,那只是无聊的部分,这基本上是在创造,呃,分配到顶层页目录,您将显示L两页目录,然后呃,这是我的KVM,它中的KVM的等价物,呃,所以我在这里得到页面目录的顶部,呃。

在本站,然后基本上我从内核页表中复制前五个和前十一个条目,KVM已经在其中设置了,所以这给了我大部分内核页表,然后我只需要绘制所有活的设备,在零条目中,因为零条目是我们以后要修改的条目。

或者我们将用户页面映射到底部,嗯,有几个设备住在入口的零点,还有那些,你知道设备需要添加,你知道到这个糟糕的过程页表,内核页表,所以也许我回到了之前的这张照片。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_5.png

所以让我再拉出内核页表,是啊,是啊,也许这张照片和其他照片一样好,所以如果你考虑一下我的解决方案,呃,我基本上这些条目我只是与现有的内核页表共享,所以我不必分配任何,l 1或l 2或l 0水平。

它们已经存在了,我唯一做的就是照本宣科地复制PTD条目,所以只有底部部分或底部入口,这是我需要真正重建或明确复制的部分,而不是复制p,这涵盖了,你知道的,底部1GB,地址空间的权利。

1996年的一页报道,所以这个,你知道吗,你知道的这个条目,从5到12,这是两兆字节,这是一个千兆字节,基本上这些条目中我只需要填写一个,这有道理吗。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_7.png

所以回到我的代码,基本上这就是我在KVM中所做的一切,创建,在KVM 3的情况下,呃也许不是也许更干净的解决方案,但我充分利用了我不必做任何事情的知识,从前八级目录中的条目一到五和十一。

我唯一能做的条目,的内核的底部条目,指向一个L一个条目的顶级目录,在那棵树上,我只需要通过一个永久保有权,l 2或l 0项,最后你知道免费的DL One然后是内核页表。

这就是我的KVM 3和我的KVM创建,这样我就可以为每个进程创建一个内核页表,然后呃,三一是我们做完的时候,所以一个快速的问题,是啊,是啊,你能再解释一下吗?只使用1到5 12而不是0的理由,是呀。

嗯好吧,所以呃,也许最容易做的事情,其实,让我回到这里的图片。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_9.png

呃,所以我们有我们的核心着装空间,正确,呃,你知道它是0 x 8 0 0 0,废话废话,呃,x zero在顶层页面目录中属于什么条目,零条目否,不是进入权的零,那么我们知道什么,所以像这样的零条目。

这个条目涵盖了多少钱,顶层页面的零条目的底部条目有多少地址空间,目录封面,我觉得克林特,呃,它在零到零x2之间是自由的,零零零,是呀,好的,所以也许你正在经历,我认为你的方向完全正确。

所以底部入口盖是1GB,对,我们知道就像,也许我可以,我们的底部条目是1GB,那个,在那千兆字节里是牌匾里被清洗过的,对呀,你的艺术和Verdio光盘,我相信嗯。

然后当前基数实际上已经坐在一个更高的条目中,对,我们可以计算它,如果我们想,我们可以拿公牛一个零,校正x移位,呃十二移动九移动九,我想我确实记得在我的头顶上,它是什么,但试着弄清楚,如果你想,其实。

也许我们可以试试。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_11.png

在课堂上做这件事总是很冒险的。但如此分贝92,所以让我们去,所以我们可以打印,所以移位是12,这给了我们呃,抵消了,这么好,那么我们就把那家伙转移,我想你可以说这个九,现在,这是下一个条目,还有一个。

所以这个条目二,有道理吗,所以我们回到我们这里的照片。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_13.png

呃,基本上当前的基础是顶级页面目录中的条目,对所以和呃,我们从作业的第三部分知道,基本上我们真的不用担心上面的任何事情,点击,你知道所有的东西,呃实际上落在了入口零,这将是真正的请求是的,谢谢。好的。

所以现在我们,我们唯一能做的就是,你知道吗,再次检查。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_15.png

我们接到电话的地方,呃,功能障碍,你知道,这将是在分配过程中,所以说,你是,新进程在哪里初始化,你知道,我相信你们都是这样想的,你知道你进入,你分配,我们在proc结构中声明字段。

基本上它的结果实际上是我们在内核页表中粘贴的,好的,然后我们唯一要担心的是,当然我们需要用页表,所以我们需要看看时间表,基本上,作业或多或少地告诉你该做什么,这是在切换到该用户进程之前。

您需要切换到内核页表,这基本上意味着加载,你知道磁盘进程,ATP中的内核页表,然后当你做完,呃,切换并运行其他处理,您运行完另一个进程回来,你要回去运行调度程序,您必须切换回主内核页表,内核页表。

因为这是调度器实际使用的,为什么我们需要做这个切换,为什么这很重要,任何人,因此它选择正确的内核页表,因为当您转到页表条目时,它选择了正确的。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_17.png

它选择正确的页表来选择它们。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_19.png

是啊,是啊,所以好吧,所以当我们停下来的时候,好的,让我换个方式问这个问题,这个内核页表怪胎是什么时候,当用户进程这样做时,如果我们不换,这意味着呃,我们可以使用正在释放的进程的页表,所以页表被释放了。

所以我们不想依赖于过程,我们现在不跑,是啊,是啊,完全正确,我们永远无法释放当前正在运行的项目的页表,所以我们得有个计划,你知道,在它们不再运行的时候,实际上要做三个过程。

你知道重量系统调用是我们的出路,基本上你知道,等待呼叫查找,如果有孩子,呃不是活着,你知道那是可以清理的,然后它把它们清理干净,但这意味着基本上,当你知道,父进程,事情在过程中,你要绝对确保。

您知道加载在p寄存器中的页表,呃不是,你知道其中一个,不是那个过程,你知道的,页表当前实际上是空闲的,现在,可能根本没有进程在运行,所以调度器基本上有自己的页表,这样你就知道。

所有的过程实际上都可以被清理,这有道理吗,对不起,我有个问题,嗯,您是在映射CL吗,进入新进程内核页表,是啊,是啊,我做为什么,嗯,因为我想作业上说,我只需要用户处理,不会比清理过的大,就是它,哦不。

我是说地图,牌匾和清洗过的,所以,但我想作业告诉我们,所以说,最低的是什么,我想,点击是最低的,基本上,标志告诉我们用户进程不会大于点击,地址,好的,只是为了让它变得容易,你知道的,我们本可以做的,呃。

我们想让它尽可能容易,为你清除,如果你想,你本可以做得更好,但你知道这是最简单的事情,你必须修改保险丝才能工作,进一步问题,呃,我有个问题,你能复制零到五十二吗,然后每次你切换,所以你还在用。

像全局根页表,除了只复制第一个根页表,每次你切换一个过程,您将该用户的地址复制到,是啊,是啊,原则是可能的吗,我想你可以做那样的事,所以基本上不是做,当您分配一个进程并释放它时,你可以动态地做。

在调度器切换期间,呃,这可能会更昂贵因为这意味着每次你在两个进程之间切换,您可能必须复制内核页表的部分内容,所以这可能是,呃,在表演方面,如果不是理想的事情,呃,作业上什么也没说,呃,如果这样做。

您可能会在用户测试中超时,我只是想知道,因为我试过这种方法,这是一次糟糕的经历,我在想我是不是路过,我可以想象,但我认为原则上是可能的,你知道吗,您可以正确分配一个新的页表,并每次切换。

当你切换出去的时候读它,嗯,我不认为这是最简单的,但你知道,原则,有可能,我想也许对六个做一些其他的修改,但是你,任何其他问题,你必须做的唯一另一个改变是你的用户陷阱红色。

您必须确保实际上使用进程内核页表运行,好的,好的,所以让我们把它们切换到,呃,就像没有火花,所以我们切换回实际上。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_21.png

好的,所以呃,事实上,这可能不是一张有帮助的照片,让我看看,我有点好,所以第三部分,呃,所以基本上我们的计划是我们像以前一样有我们的内核页表,你知道我们在某个地方有点击入口,我们要做的是用。

您知道下面的所有内容,单击以实际存储用户,页表,我们将把用户页表映射到所有用户,内核中的用户程序,底部的页表,呃,这就是目标,当然,第一个要问的问题,你知道为什么要这么做吗,嗯,你知道,有没有,你知道。

它的任何损坏,然后呃,你知道,我想可能有一个简单的方法。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_23.png

呃,看看为什么它可能很有趣,就是比较副本INS,中的新副本和旧副本,所以你,如果你知道的话,副本中发生的事情,从用户空间到内核地址空间的数据,呃,但你知道,如果内核没有映射用户地址空间。

基本上内核必须在正确的时候完成这个页面,因为当物理地址空间,它们并不毗连,基本上内核做什么并复制到,它基本上将虚拟用户地址转换为物理地址,因为内核已经用标识映射映射了所有物理内存。

物理礼服也是有效的内核虚拟礼服,然后它基本上移动页面的任何部分,需要复制的物理页面,它实际上复制了它,因此,如果示例的数据结构花费,你知道两个页面边界就像,比如说,这是你在实验室使用的信息结构。

也许它跨越了页表边界,它就会像,也许你知道,第一个物理页的一些字节,然后来自第二个物理页的一些字节,有道理吗,这就是当前复制所做的,目标基本上是让他们进入一个新的副本,内核程序不必担心。

物理布局基本上脱离了用户地址空间,然后在这个新的副本里,你知道,我们看到,我们基本上什么都不做,只需将用户地址直接复制到内核中,我们再也不用叫走路了,因为我们可以依靠,我们正确地设置了页表。

现在页表硬件将为我们做步行,所以这就是目标,因此,它使内核程序员的生活变得更容易一点,这种方法还有其他好处吗?是不是也更有性能,因为硬件会走路,而不是软件,是啊,是啊,嗯。

我认为他们的性能影响的一种方式,当然,呃,一种思考的方法是在复制正确,如果内核数据结构,或者我们从用户空间复制到内核空间的数据很大,我们必须这么做,你知道的,一次页,在每一页中,我们都要调用这个函数。

游走蝰蛇,然后做内部行走,所以,呃,这实际上可能是合理的,昂贵的,昂贵的,有什么例子,内核从用户空间复制大量数据的示例,哪个系统调用可能从用户空间复制大量数据,对呀,是啊,是啊,对呀,对呀,你写。

您可以给出任意数量的任意缓冲区,任意大小的,你知道,内核墙可能要复制这个,你知道变成,比如说,文件系统,或者进管子里,所以这可能是一个合理的,昂贵的,另一件事,还有什么好处,如果用户,呃。

我们在这次任务中没有探索这一点,但我们可以有另一个优势,所以如果你想想,你知道其中一些,这里的代码在它,你知道,从用户空间中取出一个结构,它将整个结构复制到内核空间,如果用户空间映射到内核页表。

我们一定要这么做吗,比如说,如果我们必须更新结构的一个字段,所以如果数据结构只是映射到内核地址空间,然后我们就可以像读和写一样,你知道的,对特定数据结构的存储指令,我们可以只更新一个字段。

不像内核现在所做的,基本上,它将结构从内核空间复制到用户空间,然后可能用复制退出,所以如果我们将用户空间映射到用户程序到内核地址空间,我们可以比现在更自由地操纵它,就动机而言,这有意义吗。

为什么许多内核实际上有这种特殊的结构,它们在那里映射。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_25.png

用户的底部,或者在哪里将用户编程映射到内核地址空间的底部,好的,所以呃,所以说,让我们看看,查看我的代码。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_27.png

所以你可以理解实际上,我这么做了,基本上有一个函数是键正确的,你知道吗,假设我们在某个有效的地方建造,用户页表,然后我们只需要映射用户页表中的条目,到内核页表中,或进程内核页表,函数k。

您有地图用户正是这样做的,嗯,而且很无聊,但我提出了几个有趣的观点来指出和工作,你知道这是同一种接口,uvm,Mac什么的,或者基本上,如果你知道你从旧的尺寸到新的尺寸页面,呃,你会发现,呃UKE。

你知道PTE的指针,对于用户页表中的特定虚拟地址,对呀,所以如果我们看看,如果你看看我们不久前的照片。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_29.png

你知道的,看这幅画,基本上这将返回什么,正确的是,我们将遍历用户页表,我们将找到,比如说,映射的PTE,你知道那个特定的虚拟地址,我们得到一个基本上指向页表中该项目的指针。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_31.png

这就是我们走的路,所以如果呃事情被映射,如果它是零不是零,那一定意味着你知道它在那里,所以这只是一次检查,下一件事,映射就在那里,嗯,我们检查这个有效,就像有调试帮助一样,在某种意义上。

你知道它应该是有效的,然后我在内核页表中做同样的事情,我查了那个虚拟地址,也在进程内核页表中,但我把这次叫做与一个同行,以便在必要时实际分配中间页,然后一旦我得到了指向内核PT的指针。

我只是把用户PT复制到内核P中,所以可能你们很多人都叫地图页,但我只是直接将PTE复制到内核中,pte,当然你知道我得禁用一些部分,我们一会儿再谈,呃,但是呃,它的本质是,我只想复制内核页面,笔架。

所以这意味着你知道,比如说,用户程序的所有物理内存都在内核空间和用户空间之间共享,好的,这有道理吗,嗯好吧,所以就切换一些位而言,呃,我有一点,呃超过必要,但我关掉了执行位,切换正确的位。

这个复制只需要阅读它,从不写信给它或复制出去,那是吗,然后呃,你得把U位调高,嗯,这是一种风险,五个具体的事情,如果您在内核模式下运行,U位设置在ptentry中,内核实际上无法访问那个特定的页面,嗯。

事实上,你可以,这几乎是一个选择,你也可以对风险五硬件进行编程,但基本上是内核模式,忽略U位,但是XP6不能做到这一点,所以你必须切换U位,关于你有什么问题吗,因为你们中相当多的人被问到这件事。

这个做完了吗,只是为了确保内核不会对用户内存造成任何损害,是啊,是啊,那么为什么会这样呢,你知道的,所以问题就像,这是出于调试原因吗,还是有某种孤立的理由这样做,我想这主要是为了调试区域。

因为内核是完全控制的,对呀,它就会,它可以根据自己的意愿禁用分页,所以不是说,你知道吗,用户空间受到内核的保护,我认为它主要是为了帮助内核开发人员,所以说,比如说,在十六箱未经修改。

xv六永远不应该取消引用或使用页面句点,嗯,嗯,所以你知道,基本上这只是帮助,你知道吗,如果你碰巧那样做,无论如何,你会立即得到一个页面错误,你知道,或者内核恐慌,它会帮助内核。

调试器或开发人员来调试内核,这有道理吗,是啊,是啊,谢谢。我有一个后续问题,我认为关于用户的部分是有道理的,但是写入和执行位呢,是啊,是啊,所以执行必须在,内核对这个页面做什么,它只能从它的右边到达。

唯一的东西,唯一的指令,基本上从这个页面获取数据,这个人是把指令和副本搬进来的吗,所以只有你知道它执行加载指令,加载值,你知道从那个特定的页面,并且页面应该只包含数据,所以内核没有理由写到那个页面。

所以保守地说,我禁用了正确的位,该页面应该有当前的不应该执行的结构,所以我也可以再次执行bit,这主要是,我想出于调试的原因,呃不是,你知道的,隔离带,好的,所以现在我们唯一需要做的就是。

基本上有一堆地方这个代码,调用此函数的位置,当你看到这些地方时,它被称为,去了解发生了什么,或者应该如何使用,我想可能是你知道的有趣的一个,你们中的一些人问的是叉子,我们要谈谈,他们两个,第一把叉子。

所以这里是呃,对fork的调用或对fork中的k kvm映射用户的调用,它需要结束,所以主要有趣的问题,我想你们很多人问的是,为什么必须是新进程内核页表,为什么它必须从新处理器复制,呃,页表而不是从。

可能是当前进程页表,因为无论如何,这是一项相同的权利,前进,基本上只是复制页表的用户部分,那么为什么我们不能使用当前的页表来做到这一点呢,为什么实际上你必须使用新的,您知道页面中孩子的用户部分。

孩子的用户页表,为什么会这样,移动你注意到你得到了什么bug,你不会得到一个重新映射错误,您可能无法重新映射错误,呃好吧,事情也许也是这样,但又一次,我想的bug不是重新映射的bug。

你们中没有人再尝试过,你的个人经历是什么,电话里任何尝试过这个的人,记住由此产生的体验是什么,我想结果是我和我没有肿块,但就像,我认为最终的体验是在用户测试的某个地方,特别是。

你应该测试做大量的分叉和退出,你会有麻烦的,你在区域中得到一个错误,出问题的是,呃,如果从父进程复制,如果父进程在,孩子处理我们,然后父进程页表将完全,呃,就像我们之前看到的那样清理干净了。

但是子进程仍然有指向父进程的指针,页表,所以基本上你会,基本上是这个孩子过程中的页面,页表已经处理过,它们仍然在使用,所以导致了各种各样的不良行为,因为冻结页面上的内核实际上到处都在写,出于调试原因。

所以现在基本上你有无效的,你知道的,在内核页表中,这有道理吗,好的,再来一个,呃,要看的是Exec点C,呃,在我的解决方案中,都很直接,几乎没有修改弹出,我们唯一做的药是,你当然知道第一件事,你知道的。

第一部分,然后这里是第三部分,它基本上是将新的用户页表映射到内核页表并退出,基本上,它所做的基本上是建立一个新的用户地址空间,你知道,有一行将新的用户地址空间复制到内核页表中,就是这样,有什么问题吗?

所以我希望,同时,呃,我已经回答了你的许多问题,呃,呃,所以在里面,呃,我们可以看看哪些,还有几个问题我还没有谈到,所以也许我们可以看看那些,或者除非你马上有问题,我们不是也修改了生长过程吗,是呀。

是啊,是啊,再做几个必要的改变,呃,你知道,在S中断或群岩中,有一个,我在这里加了一条线,非常相似的风格,大概也有类似的变化,我想这可能是它实际上或通常在它,当然可能会有变化。

因为您必须将init代码中的一个页面映射到内核页表中,为了那个过程,因为第一道工序比较特殊,好的,所以呃,我将在这里讨论一些问题,我把它们放在屏幕上,所以你希望能看到他们。

所以我不必觉得把它们完全读出来,我想第一个问题是一个实际上出现的问题,关于PTU的一点,呃,旗帜,呃,我想我们已经讨论过了,我们还掩盖了他没有恶意的事实,但主要是出于调试的原因,类似的问题。

如果你处于主管模式,你就不能做点什么吗,因为你可以切换ATP,你可以,答案是肯定的,一切皆有可能,所以真的,你知道的,U位不是关于用户程序受到内核的保护。

它实际上是作为一个标志来帮助内核开发人员构建内核,另一种具体的风险五个问题,这就是为什么IN干涉,为什么寄存器有点奇怪的顺序,呃,原因是,我想我们刚才提到过,在上一节课,基本上。

有一种叫做风险五压缩指令集的东西,它有一组很少的寄存器,然后呃,所以你必须,呃,对指令进行更紧凑的编码,这种奇怪的顺序反映了他们基本上是两个的事实,有压缩版本,这是一组寄存器,然后是未压缩的版本。

这就是我们正在使用的,它有一套完整的寄存器,和压缩,比如说,有1和0,作为零和作为一,但不是11岁的2岁,所以这就是主要原因,嗯,我想它经历了大部分这些问题,但如果你看到一个我应该讨论的问题。

我不知道你知道,请打断我,或者如果你问我还没有讨论的问题,呃,请问这些问题基本上是按你提交的顺序,所以这些是,好的,这里可能有,呃,所以这是我对操作系统的问题,使用一篇名为页表的文章,必须设置页表页。

以便层次结构的较低部分至少是部分和共享的,然后呃,你知道的,你显然在我的解决方案上,正确,我分享一到五个条目,十一,所以这是一个标准的把戏,很多操作系统都这样做,我想我们就这个问题谈了一点。

你转换是很重要的,你知道吗,到这个主内核页表,因为调度器可能再也没有用户处理器可以运行了,和时间表,当然啦,还需要一个页表,因此,它与主内核页表一起运行,我们在聊天中有一个问题,是啊,是啊,去吧。

什么是让你拉起聊天,坚持住,我能读懂,是啊,是啊,是啊,是啊,我拿到了,所以我想,你知道的,好的,所以问题是风险说明是怎么说的,分离的那一部分是,防止,允许用户程序使内核跳转到任意代码的bug。

在用户空间中,呃,是呀,呃,这是一个很好的观点,嗯,所以你知道,你可以在这里辩论,这是隔离属性还是内核调试属性,显然,内核不应该跳转到地址和用户地址空间的任何部分,你知道的,我认为这是一个工具。

帮助内核调试器抓住这些钱,出于某种原因,这就是为什么在XPSix中,我们还禁用或设置视图位,并且不允许内核引用任何页面,保护你知道我们,你知道的,如果您确实引用了内核实际上会出错的每个用户地址。

我想我把滑冰者盖住了,呃指向呃,所以这里有一个问题,管道是如何在xpsix中实现的,和true更改页表应用程序中实现的页表会影响此实现,所以管道基本上是内核中的缓冲内存,然后呃,对呀。

当你对着烟斗写字时,基本上叫,复制进去,并复制进去,或将字节从用户空间复制到管道中,在某些方面,你知道这个页表实验室的整个部分,基本上简化了代码,这样你就不用走很多路了,您就从用户空间进入管道。

这是一个答案问题,我想很多人问你喜欢,为什么UVM三号和自由行者最初对叶子感到恐慌,原因是你知道我们在那里制造了恐慌,因为它断了,呃,这表明XP6中的一个变体将被打破。

这是未修改的x x t 6的不变量,呃,在这种特殊情况下,呃,那不是真的,所以你必须基本上摆脱恐慌,意识到在那里恐慌并不重要,你在那里不要惊慌,我想我们谈了很多,比如为什么VM复制新是。

你知道为什么新的VM副本是一个好的,好的,这里还有一个问题,嗯,假设这可能是一个有趣的问题,一种更多的设计问题。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_33.png

有一张照片是有帮助的,我们查看内核地址空间,在那里我们限制它,看看我能不能找到我的熊猫,我们限制用户地址,用户程序可以更大,我们说基本上用户程序可以增长到干净的地址没有进一步。

假设我们真的想一直长到这里,我们怎么能这么做,什么什么,我们应该如何改变设计来允许,我们怎么重新映射这些东西,呃,像克林特和BC,你是,是啊,是啊,你会把它映射到哪里,所以如果你想释放这部分地址空间。

你能把它映射到哪里,就像当前基础之前的自定义一样,在物理停止之前和当前基础之后,是啊,是啊,或者这可能是更好的后鱼汤,这里有大量的自由空间,类地址空间未使用,像这样从这里到那里基本上是用来物理记忆的。

我们基本上可以在这里建立映射,对呀,比如我们可以把UART放在这里,就像你是零,我们可以在这一页上面放一个,基本上建立一个映射,映射到那个特定的物理地址,然后这将释放这个映射,我们可以把它作为用户空间。

如此相似,如果我们能做到这一点,你是零或Verio磁盘,你知道布莱克和克林特和真正的内核这样做,这有道理吗。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_35.png

没那么成问题吗,虽然,因为我们想要与原始内核页表相同的映射。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_37.png

嗯,我们必须在每个内核页表中都这样做,正确,如果我们有副本,我们得把它映射出来,哦,在那个特定的位置,我不确定那是一个,我不确定这是否能回答你的问题,但我不认为这是个问题,我明白了所以你,你做那个映射。

也在原作中,是啊,是啊,是啊,是啊,我们也必须在最初的一个中这样做,如果老上校一致认为你是零,那就更容易了,坐在上面,现在很多人这样问,为什么我们要将内核堆栈映射到高处。

这样做的原因是什么这样我们就可以在其中绘制这些,呃,内核堆栈很高,如果你现在做了复制方法,您实际上必须修改其中的一个进程来实际复制该映射,为什么要把它设置在虚拟地址空间的高处。

是因为风险5中的堆栈是向下增长的吗,所以你把它放在地址空间的高处,让它有向下生长的空间,你知道多少才是正确的方向,它要向下生长多少阶段,那不重要,因为只有一页,对呀,是啊,是啊,只有一页。

那么如果您增长超过一页会发生什么,你正确地跑进车库,保护页没有映射,所以内核会惊慌,这比实际上你写它的数据结构要好,所以它很高的原因是,呃。

因为我们在下面放了一个保护页它实际上有任何与车库相关的物理记忆,否,否,没有与保护页相关联的物理内存对吧,所以这就是把它放高的很酷的事情之一,我们可以在下面放一个保护页,实际上不消耗任何物理内存。

这有道理吗,哦,所以基本上康奈尔的东西页面将是物理的某个地方,但卡片页不会完全,再次,你可以用虚拟内存做的很酷的事情之一,是否有可能超调保护页,是啊,是啊,我是说,这是个好问题,你知道吗,什么,比如说。

如果你知道你分配了警卫,呃,堆栈上的缓冲区实际上是,你知道,比守卫页大得多,更正并进入下一个内核堆栈页面,呃,是的,是的,你会有一个严重的错误,不管怎样,你很可能会遇到这种情况。

因为您可能会使用缓冲区的第一个条目,然后你会得到页面错误,但你可能会倒霉,所以这个,这不是防弹内核调试技术,但事实证明它非常有效,大问题。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_39.png

还有问题吗,我会回到,我们还有几分钟,然后实际上我们还有一分钟。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_41.png

我有个问题,所以一旦我们有了进程的内核页表,这是否意味着在陷阱代码中我们不需要切换页表,我认为最大的问题是,一个好的设计问题,答案是肯定的,你不需要它的权利,因为,内核的主要原因,或者在蹦床代码中。

我们这么麻烦是因为我们必须复制用户,我们必须从内核页表切换到用户页表,和用户页表或页表,没有映射内核的其余部分,不利的等待,我不知道,我不确定那是不是真的,因为康奈尔需要在片场设置用户黑色用户标志。

是啊,是啊,是啊,是啊,好的,所以我想说几点,所以说,原则上,呃,你可以简化,呃趋势,我这么说吧,可以简化进出,如果您有一个映射的页表,用户和内核在单个页表中,因为这样你就不用换了。

你必须对6个进行更多的修改才能做到这一点,呃,但原则上你可以,事实上,呃,Linux,直到最近才使用,你知道的,这种策略,内核和用户代码位于单个页表中,呃,并依靠,基本上是U位,你知道要确保。

用户程序实际上不能修改任何内核页面,在这种情况下,进出代码稍微简单一点,因为当您进入或释放内核时,您不必切换页表,呃,发生在,比如说,熔毁攻击,如果你可能有这种侧通道攻击,以应对实际侧信道攻击,呃。

线性开关,或者有其他运行模式,跑步有两种模式,一种叫做KPTI模式,在那种模式下,基本上,这基本上反映了XP6,你知不知道,为内核提供单独的页表,为用户空间提供单独的页表,等一下等一下。

所以我还是不明白为什么那是有效的,假设用户进程和内核使用相同的页表,如果用户内存必须设置用户位,康奈尔将无法访问该用户内存,对呀,啊,好的,上,所以这样就可以了,在英特尔处理器上,这不是规则,所以说。

比如说,在处理器中,如果你的出价设定好了,内核仍然可以写入和读取该页面,哦,那只是风险五,啊,甚至在风险五,你可以改变它,在S状态编辑器中有一点,您可以将类型设置为一些位,如果你设置它,然后呢。

基本上在内核模式下,忽略视图位,好的,所以你是说这里面只有一点,CU处理器让我晚了四个小时,是呀,那件事我很抱歉,我应该意识到没有,否,否,没关系,它是,当你报道这件事的时候,这是一个有趣的认识,呃。

它让我想起了夏天我实际上有的一个虫子,正是那个虫子,我忘了这件事,所以我很抱歉,不不不,没关系,它是,呃,很有趣,确定弄清楚,但我,我在某个时候,我只是喜欢好吧,喜欢任何一个,我疯了。

或者完全出了什么问题,所以我要去睡觉,提交这篇广场帖子,希望当我醒来时,会有人接的,幸运的是,情况就是这样,所以我可以继续,当我当我做的时候,好的,所以我想我们有点拖延时间了,呃,但希望这是有帮助的。

并重申,你知道的,有虚拟内存的东西,我认为这很重要,因为我们将再进行三圈涉及虚拟内存的比赛,我希望在这一次之后,你知道这些会比这次经历更容易,我能问个问题吗?是啊,是啊,所以我要留下来。

所以每个人都想留下来问更多的问题,请随意这样做,如果你需要去下一节课,请你走吧,在所有的实验室里都有很多提到,就像,哦,我们这可能在未来的实验室里有用,但我们从来没有真正使用过我们编写的任何代码。

有什么计划吗?我们应该从以前的实验室导入代码吗,或者像我不想,因为如果它有虫子怎么办,我不想,你知道吗,影响下一个实验室,是啊,是啊,所以我们不这么做的一个原因,你知道为什么构建实验室不互相构建吗。

即使他们可以避免基本上依赖,就像你在早期的实验室里有一个虫子,用户测试或我们的任何测试都没有暴露,但会暴露在新的实验室里,然后你知道是痛苦的,嗯我提到的原因,那就是基本上真正发生的事情是。

我们将在页表上做更多的事情,所以说,比如说,你知道的这个实验室,在以后的实验室中简化和复制,呃,改为s break,它们基本上与您对内核所做的更改正交,这一圈,但您知道,您一直在考虑内核页表。

如此多的用户页面表将真正帮助您,对你有帮助的是,因此,调试由于不正确的页表而导致的这类问题的经验,好的,我只想确定一下,我错过了,因为我没有从一号实验室复制我的X拱形代码什么的。

决定购买实验室不相互依赖,好的,谢谢,星期三见,星期三,我有一个关于映射映射的问题的后续,呃,使用页表应用程序中的修改,嗯,那么需要蹦床吗,在用户页表中,呃好吧,所以我还没想清楚,所以如果你把核仁。

假设内核出口,假设我们有一个关节,一张单页表,这是你看到的正确的一个,我们跳出内核,呃通过乌尔,我们的目标是,我们仍然在内核模式下运行,所以这个页面当然可以访问,在内核中更正,保护页,我肯定你不会。

卡片页,我在说什么,蹦床页面,有没有,捕鼠器架,我们不是真的,我们无论如何都可以接触到,对,因为我们运行的是内核模式,我们知道它在PROC结构中的位置,或者我们可以得到它,所以我想我们可以退出。

当我们回来的时候,然后用户代码就可以运行了,你知道我们,当然U位有点,Ubid只设置在用户空间中的页面上,所以那很好,我也是,所以当我们回到课程中,你知道我们还在用这个页表,该页表已经映射了所有内核。

我们可以,你知道的,复制你知道的寄存器,我们需要直接保存到proc结构中,而不是必须经历,呃,有一个单独的页面,其中包含proc结构,我相信改变会很简单,好的,我明白了,谢谢。但我当然可能错了,哦耶。

我是。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/0e0a9164246e62f84280252dde192371_43.png

P7:Lecture 8 Page Faults 中文版 - MCATIN-麦可汀留学 - BV1rS4y1n7y1

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/1b44dfaa9ed2f5cd06fa161e0ad1f661_0.png

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/1b44dfaa9ed2f5cd06fa161e0ad1f661_1.png

好的,快速声音检查,大家能听到我吗,是的,好的,好,呃,反正,我们走吧,让我们得到,我们出发吧,嗯,你知道,下午好还是晚上好还是早上好,呃,随便啦,晚安,无论你在哪里,呃,所以今天的课,呃,将是关于。

页面保存,特别是我们要,计划是,封面,或实施,呃,你知道吗,实现一个数字,嗯,我们要看的特征是,呃,你,懒惰分配,这是下一个实验室的主题,是啊,是啊,我们会得到需求,裁剪和右叉,几乎你知道。

在一系列操作系统中实际上实现了所有这些特性,如果你看看Linux内部,你知道你会看到所有这些功能实际上都是在X6中实现的,就目前而言,都没有实施,事实上,你知道页面折叠在XT 6中的作用。

如果在用户阶段发生页面错误,基础基本上扼杀了这个过程,但没什么有趣的,所以在这节课中,我们将再次探索,您可以在页面错误处理程序中做哪些有趣的事情来实际实现这三个特性,所以这个讲座少了一点,呃。

遍历代码并理解现有代码,多一点设计水平,呃,在,从某种意义上说,我们甚至没有代码可看,呃,另一件要支持提到的事情,懒惰分配是下一批的主题,希望它今天能推出来,嗯,复制和写叉子。

这将是其中一个实验室的主题,Mmap将是后续实验室的另一个主题,所以这是要去,你知道的,这是操作系统中有趣的部分之一,我们将在这上面花相当多的时间,在实验室里,现在,你知道的,在深入细节之前。

后退一步可能会有所帮助,所以你可以考虑虚拟内存有,一个是隔离,在某种意义上如此孤立,呃,虚拟内存允许操作系统给每个应用程序自己的地址空间,因此,在一个应用程序中不可能弄脏,你知道的。

或意外或恶意修改其他应用程序的地址库,它还提供了用户和内核地址空间之间的隔离,我们已经讨论了很多,正如你在猪桌实验室看到的,但这是另一种观点,或者不是…的好处,你知道的,虚拟内存。

你知道我之前提到过几次,它提供了一个级别的交互,你知道的,处理器或指令,你们都可以使用虚拟地址,但是内核可以定义从虚拟地址到物理地址的映射,这允许各种有趣的,呃,有趣的功能,就像我将要在,在这次讲座中。

一秒钟,所以内核控制了从虚拟地址空间到物理地址空间的特殊映射,到目前为止,最重要的是地图绘制相当无聊,事实上,在内核中,它主要是直接映射,我们用x6做了一些有趣的事情,外部处理此映射。

我们看到的一个是蹦床页面,哎哟内核将一个页面映射到多个地址空间,我们看到的另一个有趣的例子是要堆叠的Garth页面,在内核中和内核空间中,在用户空间和内核空间,但如果你想一想,你知道到目前为止。

你知道的映射是相对静态的,你知道如果我们设置一次,你知道的,可能每个用户,我们一开始设置的内核页表映射,对于在fork上一次的进程,内核对这个映射没有做任何其他事情。

页面错误给我们的实际上是使这个映射动态,所以我用页面错误,是啊,是啊,我们可以改变,或者内核可以更改映射,如此动态地飞行,这是一个极其,极其,呃,强大的机制,所以如果你可以组合页表和页码false。

你有一个巨大的,内核具有巨大的灵活性,然后灵活性就下来了,因为你可以在飞行中改变这个方向,所以,我们要做的是,基本上看看这种动态重新映射的各种用法,或者动态地改变页表,给我们,你知道的,有趣的特征。

我们首先要考虑的是,你知道需要什么信息,你知道的,在这里,一个陷阱发生了,因此内核将只响应此页面错误,你知道你所知道的,它需要能够做出反应,你知道很明显,你知道我们想要虚拟礼服,带有默认内容的虚拟地址。

或者页面错误的代价,别让我说得好像我们想说的是,你知道你大概看到了一些恐慌,呃,调用您的页面错误洛杉矶,你知道内核实际上可以访问它们,事实上,当页面错误发生时,它会打印出来,然后呃,你知道它碰巧在圣。

因此,当用户应用程序导致页面错误时,页面错误基本上调用了罗伯特讨论过的相同的轨道机械,在上一堂课里,几乎一模一样,但是在页面错误的情况下,它还会将默认地址输入ST阀门寄存器。

所以这是我们可能想知道的一件事,我们可能想知道的第二件事是默认的类型,呃,因为它可能是在,呃,呃,我们可能想对术语做出不同的反应,呃,由于加载指令造成的页面错误,或由于存储指令而导致的页面错误。

或因跳跃指令而发生的碰撞,嗯和嗯,所以其实,你知道的,如果你看看文档中的风险五,风险是这样的,五个,呃文件在这里,在原因登记册上我们在陷阱讲座中被提及,实际与页面错误有关的原因,所以例子。

如果你看第13个是加载页面错误,第15个是存储页面错误,数字12在指令页错误,所以这些是呃,在S原因登记册中,我们得到这些信息,所以有三种不同的类型,你知道吗,阅读,写,指令和只是回去。

你知道一个的成本,你知道这实际上是由AL指令造成的,实际上内核传输是第八个,对呀,这就是我们在陷阱讲座中看到的,我们在陷阱课上花了很多时间思考,但基本上所有其他页面错误或异常。

我使用相同的你知道的机制从用户空间传输,到内核传输到内核空间,一次在内核空间,在页面错误的情况下,您知道STD值寄存器设置为原因寄存器集,那么我们可能想知道的第三件事,是指令还是寻址指令的虚拟地址。

导致页面错误的,你知道吗,有人还记得你从陷阱讲座中知道的地方吗,指令所在的地方,其中涉及,有人吗,阿成,是啊,是啊,准确地说,有一个登记簿,c更正主管异常程序计数器,就在它所在的地方,保存在哪里。

作为陷阱处理代码的一部分,在陷阱框架中,是啊,是啊,它最终进入陷阱框架正确,有一个陷阱架,epc实际上有异常程序计数器,所以如果你考虑一下硬件机制,x36的作用是什么,呃,我们有免费的信息。

对我们来说可能非常有价值,当我们得到真正的报酬,即地址,违约的成本违约类型,和异常程序计数器,如,它发生在用户空间的什么地方,我们之所以关心异常程序计数器更正是因为,当我们可能想修复的时候,你知道的。

在处理程序中,我们将修复页表,然后我们基本上可以重新启动相同的指令,你知道,希望在修复页面错误或修复页表后,2。这条指令完全可以正常运行,所以我们能成为一份简历是很重要的,实际上我们的成本违约的指令。

所以现在我想看看,你知道的,也是风险五实际上给我们的基本机制和基本信息,呃,我想看看一个基本上,浏览,你知道的,将帮助我们的功能,你知道的,实际上理解我们如何才能,呃。

使用巡逻处理程序修复页表并做有趣的事情,所以我首先想看的是分配和一个特殊的,你知道的,Let’休息,所以s break是系统调用,x t 6提供了,呃允许应用程序基本上增长,把它的堆积起来。

所以当应用程序,呃开始,呃,你知道吗,Let’休息,呃点在这里,你知道在堆的底部,你知道在堆栈的顶部,事实上,你知道这是同一个地方,你知道P大小,基本上你知道点,所以当休息被称为,呃,比如说。

叫一二三四五,您知道要分配的页数,作为大系统调用,它基本上颠簸起来,你知道这个边界,在那里做点什么,所以当S刹车真的发生时,这意味着什么,或者系统调用被调用,内核会分配一些物理内存。

将其映射到用户应用程序的地址空间,然后基本上从系统调用返回,嗯,你知道过了一段时间,只是,应用程序可以增长,你知道物理记忆,或者在它需要的内存中,呃,或者它可能想通过多次呼叫来吃东西。

应用程序将在其地址阶段减少或缩小,用负数打电话,但我想把重点放在我们正在成长的案例上,呃,地址空间实际上是六个,就像它一样,休息是热切的,或者做我们将要称之为,渴望,分配款,即一旦,就像休息一样。

内核将立即分配应用程序所请求的物理内存,现在事实证明,在实践中,应用程序实际上很难预测,呃,他们需要多少内存,往往问得太多,所以他们要求的比他们真正需要的多得多,然后呃,经常会。

这意味着基本上你知道地址阶段会增长相当多,即使使用应用程序实际上从未使用过的内存,你可能会想,哦,那太蠢了,这怎么可能发生得很好呢,你知道你在想,如果您编写一个应用程序,以及典型的应用程序。

也许它读取了一些输入,或者你知道它有一个矩阵,为一些计算引入,通常应用程序编写者会为最坏的情况制定计划,你知道的,你知道的,为应用程序可能永远不需要的最大矩阵分配内存,但在普通情况下。

应用程序可以用更小的,你知道输入,或者一个小得多的矩阵,所以这很常见,实际上对于应用程序程序员来说,也许你知道你在想你自己写的申请,实际上已经结束了,问,并实际拥有它们的用途,呃。

我们希望原则上问题不大,但你知道,实际使用,呃,虚拟内存和页错误处理程序,我们实际上完全可以,呃,以一种聪明的方式对此做出回应,然后基本的想法很简单,休息一下,我们基本上什么都不做。

我们唯一需要知道的是,记住的是,当然,我们确实增加了地址空间,所以唯一真正的,我们要做的实际上是,你知道的,P尺寸,你知道不管号码是什么,呃,现在把一边放在一个新尺寸的,加上你知道n。

其中n是分配的内存量,然后你就知道了,但我们不分配内核,不会在特定的时间点分配任何物理内存,它没有归零,绝对没有什么,然后在某个时候应用程序将使用或可能使用正确的内存。

如果它实际上是它真正需要的内存片段之一,呃,这将导致页面错误,对,因为我们还没有将内存映射到页面映射中,所以如果我们取消虚拟连衣裙,你知道上面,你知道这个尺寸,但低于p加n,呃,我们喜欢发生的是你知道。

内核将分配一个页面并重新启动指令,所以如果我们有页面错误,我们看到虚拟地址在一个比PIA大的,呃,它是蓝色的,对不起,这是一个低调的尺寸,对于P尺寸,那么我们就知道这一定是一个虚拟地址,我想在堆栈上面。

你知道这是一个地址,实际上是从堆中出来的,但我们为此,内核还没有分配任何物理内存,因此对此页面错误的响应,你知道的,可能是合理的直截了当,你知道的,在页面错误句柄本身中,我们可以分配一个页面。

使用K alec分配一个页面零到页面,将页面映射到页表中,所以更新页面订书钉,然后基本上重新启动指令,所以说,比如说,如果是加载指令或存储指令写入或试图读取,你知道从那没有分配,你知道的。

进程现在实际上拥有的一块内存,在我们映射到这个,你知道的,物理页面,重启指令应该能正常工作,我在这里,去吧,所以我想知道在我们进行重新分配的情况下,当一个进程消耗如此多的内存时。

它实际上耗尽了物理内存资源,如果我们不做热切分配,我们就偷懒,应用程序在什么时候会知道没有物理内存,啊,这是个好问题,啊,你想要,基本上你知道它几乎浏览了那里的应用程序,关于记忆有一种无限物理的幻觉。

你知道在某个时候,你当然知道,你知道可能会用到很多,你知道基本上没问题,所以它可能会使用所有的物理内存,所以如果它再碰到一页,在那个特定的时间点上没有物理记忆,那么你知道内核可以采取几个操作。

我稍后会讨论更复杂的,呃,你要在懒惰实验室做的是,你知道如果记忆,呃,返回错误,或者实际上你在特定的情况下扼杀了这个过程,因为你没有记忆,所以内核无能为力,在这一点上,你知道你返回或停止这个过程。

那是你要在懒惰实验室里做的一件事,我们将在这节课后面看到,你可以比那更老练,我认为这通常会提出一个话题,如果我们有一个运行在操作系统上的进程集合,有有限的物理内存。

有限的物理内存必须以某种方式在应用程序之间共享,所以我会在10-20分钟后再多说一点,作为聊天中聊天的问题,为什么条件是虚拟地址,虚拟正义是不要从零开始,好的,因为这里有一个关于这张特殊支票的问题。

我们这里有我们的数据,我们有我们的文本和用户进程,基本上我们撞到了,你知道,去做更大的事情,我们把它抬高了,生长,我们已经在这里分配了内存,所以这个内存还没有被物理分配。

所以这个检查只是检查地址是否低于p大小,它实际上是一个有效的,你知道的,用户地址空间中的地址,如果我们在P以上,大概是程序错误,以及试图取消引用的程序或用户应用程序,一个实际上没有的记忆。

希望这能回答这个问题,是啊,是啊,谢谢。好的,好,所以嗯,来感受一下,你知道这实际上意味着什么,而这种懒惰的分配,那可能是一个,唯一的编程,我们今天要做的两件事是,呃,让我们试着勾勒出或看看,其实。

它在代码中的样子,嗯和你看到的两个,这将是令人惊讶的。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/1b44dfaa9ed2f5cd06fa161e0ad1f661_3.png

呃放松,呃和呃,此外,呃,这可能是一个很大的帮助,呃,为了懒惰的实验室,呃,所以希望这能帮助你直截了当地走下去,它也让我们看到一对夫妇,陷阱,我有个问题实际上是关于聊天中的一个问题,什么。

为什么我们实际上需要杀死应用程序,操作系统就不能像叙述一样返回吗,你没有记忆了,试着做点别的,我们把这个问题推迟到以后再说吧,然后呃,在页面错误中,我们要扼杀这个过程。

但你知道我们在像素实验室可以更复杂,真正的内核更复杂,好的,呃,虽然最后他们还是会杀了我们,你知道,如果没有,没有更多的记忆,得到什么,你知道基本上别无选择,好的,所以我们要做的第一件事。

我们要修改一下,所以你记得CIS进程是,你知道的,实际增长应用程序分配内存的地址空间,诸如此类的事情,我们只是不打算这么做,我们只需要设置p大小,你知道,呃,p尺寸加n,所以让我们假设我们只是在成长。

暂时不用担心缩小,这就是我们要做的,因此,这将虚拟服装空间增加了n,这就是我们要做的一切,让我们看看我是否能不犯编程错误,我确实犯了一个编程错误,呃,我想我这里没有产品密钥,所以我所有的过程,好,是啊。

是啊,所以程序只是运行,呃,或者至少重新启动,我想如果我们真的喜欢,回声高,呃,我们会得到一个页面错误,我们得到有偿故障的原因是因为,呃,米歇尔,谁要叉子,你知道回声,然后你知道孩子会执行,回声。

shell实际上分配内存,所以贝壳叫s break,事情看起来不太好,但你知道,看起来有点有趣,你知道呃,这里的信息,所以这是我们的,它打印出S COS寄存器,呃,当s成本中的值,这是十五,呃。

有人记得十五是多少吗,你知道从那张桌子上,我刚才给你看了一点,实际使用写入或存储页错误的,呃,我们看到这个过程是免费的,那可能是贝壳,我们实际上看到异常程序计数器,是一二四,我们看到给定的地址。

我们错误的虚拟地址,也就是四千零八年,所以让我们看看,你知道我们可以看看外壳的组装,让文件对我们很好,足够好让我们实际生成,我们可以看看地址12a 4,你知道我们确实看到了,你知道有一个商店说明书对吧。

商店说明书,看起来你知道这就是我们的缺点,所以让我们向后滚动一点,看看这里的这个组件,你知道我们看到,呃实际上,这是malloc实现的一部分,所以这似乎是完全合理的正确,下面是malloc实现。

毫不奇怪,我们大概用s break来获得一些内存来实现你,用你的malloc,然后呃,我们基本上是在初始化一个免费的列表,使用我们刚刚从内核获得的内存,这一行12a 4大概写的是,你知道的。

我想这是对的,一些东西,但我们写的是实际上还没有分配的记忆,我们可以看到内存可能没有分配的另一个原因是,我想外壳实际上有四页的文本和数据,我们基本上坐在第四页上方,在第五页,事实上。

我们坐在它上面八个字节,这有点道理,呃,我们再看一下说明书,我们是十二八十四,你看这里基本上可能是一个将军,保持4000和8是额外的偏移量,你知道我们实际上是在引用,所以这是默认的,现在我们喜欢做的是。

你知道,做一些稍微复杂一点的事情,那么我们目前正在做的,所以让我们去生产,呃陷阱,让我们看看,然后呃,看用户陷阱对吧,因为这发生在用户陷阱中,呃,新陷阱是我们,呃,罗伯特讨论过的,呃,一周前,呃。

你知道它经历了不同的原因和行动形式,所以我们在这里,呃,这条线是S班是8,你知道这就是我们要处理系统调用的重点,然后有一条线检查它是否是任何,呃,设备爆发并在设备内部处理并爆发,如果这些都没有发生。

然后基本上我们得到了这个陷阱和被杀死的过程,基本上我们需要做的是,你知道我们需要在这里添加一些代码,你知道检查另一个案例,对吗,基本上,我想我们要看的情况是,如果R的原因是因果关系,就像。

I r s子句是十五,我们想做点别的,这有道理吗,那么我们在这里想做什么,进攻的计划是什么,你知道的,对于这几行代码,我想检查p大小是否大于虚拟地址,嗯,哦,是这样的,然后做一些,uv,乌姆马洛克。

我想这是我们能做到的一个方法,所以为了演示,我要抄近路,大概在实验室本身,你需要做更多的工作,但基本上这里是,我想经常,我们需要的第二个代码,所以让我们看看,呃,我是你的,你的陷阱,有点融入其中。

然后我们可以看看,您知道,只是用于调试的打印语句,基本上我要在这个处理程序中做的是,我要分配物理页面,嗯,如果没有身体节奏,意思是我们没有记忆了,我们现在要停止这个过程,嗯,如果有物理页面。

你知道我们会把页面归零,然后我们只需将页面映射到用户地址空间中的适当地址,特别是我们把它映射到四舍五入的虚拟地址上,对呀,所以错误的地址是四个,这里是五号和八号,所以这是八个字节,你知道第五页。

我们想把物理页面映射到物理页面的底部,虚拟页面所以四千,所以四舍五入到四千,然后我们将四个文件映射到这个物理页面中,当然,我们必须设置通常的权限位,你知道U位或读写,这有道理吗,我想我可以摆脱这条线。

所以让我们看看,让我们来试试,我想我犯了一些错误,我想在其他方面,你没有在底部有一个开口支架,哦耶,是啊,是啊,如果没有,我确实有,我还需要一个,哦耶,哦,对不起,我是说就在这里,你没有开口支架,对呀。

就像,希望能对优秀的,回声如此之高,呃,现在,我们当然会乐观,我们希望这能奏效,我告诉你这行不通,但我们确实有两个页面错误,对呀,我们有一页写满了四千八,你知道。

显然我们处理它们是因为我们有另一个页面错误,所以我们唯一的问题是我们只剩下,是不是有一种紫外线来抱怨,作为抱怨,我们正试图取消一些页面的映射,实际上没有映射,那会是什么呢。

为什么你会认为即使是我们也会有这种恐慌,任何人,这里取消映射的内存是什么,很有可能,被懒洋洋地分配而没有实际分配的,是啊,是啊,准确地说,你知道吗,懒洋洋分配的内存,但实际上你知道还没用过,对呀,因此。

没有物理页面用于特定的惰性内存,所以在这种情况下,当PT为零时,你知道没有地图,然而,这不是真正的恐慌,你知道吗,这实际上是我们所期望的,你知道这可能会发生,事实上,对于那个页面,我们不必做任何事情。

对呀,我们可以继续,你知道的,转到下一页,这有道理吗,所以我们就这么做吧,现在让我们做回声高和繁荣,我们有两个页面错误,但很好,所以我们基本上,呃,呃,你知道,有一个非常基本的最小懒惰分配方案,工作。

对此有什么问题吗,对不起,我真的没听懂,为什么你可以继续,呃,3。你能再解释一下吗?是的嗯,所以bug表明我们有,我们正试图释放页面,实际上没有映射,你知道这怎么可能发生。

可能发生这种情况的唯一原因是S的刹车向上移动了P大小,但从未使用过应用程序,在那段记忆中从未使用过,所以它实际上还没有映射,因为它实际上没有分配对,因为我们懒洋洋地分配,我们只为这些页面分配物理内存。

当我们需要的时候,如果我们不需要它,那就没有映射了,所以这是完全合理的,实际上会有一个案例,其中没有虚拟地址的映射,因为它实际上还没有分配,在这种情况下,我们什么都不做,你知道我们不能释放页面正确。

但是没有免费的页面,所以最好的办法是继续翻到下一页,好的,那是有道理的,谢谢。是啊,是啊,我们不知道继续,但基本上,你知道,只是继续前进,然后我们实际上会释放页面,那就是谢谢你,这就是为什么它继续。

有道理,另一个问题,是啊,是啊,在地图上的UVM中,我以为恐慌是,那是有原因的,所以一个更正确的,更合理的实现是有两个版本,我们会用一个不惊慌的,是啊,是啊,好的,为什么UVM,那里的恐慌。

基本上有一个不变量曾经是真的,对于未经修改的XP六右,实际上未经修改的X6永远不应该有一个案例,其中有未映射的用户内存,所以笔举起来,我们现在改变了XT 6的设计,所以我们必须,这个不变量就是不好。

不再是真的,所以我们必须消除恐慌,你知道,呃,因为任何婚姻都不是真的,合法地,不要太多,呃我明白了,谢谢,希望这会有很大帮助,我们还有下一圈,事实上,这就像是下一圈实验室的三个组成部分之一。

所以这是你必须做的第一件事之一,希望这能为你节省一些时间,也许你弥补了,你知道的,你在页面错误实验室经历的所有痛苦,但显然是不够的对吧,所以就像事情是什么,你知道的,做出了这些改变。

但更多的东西可能还是坏了,现在提到了一个收音机,我其实没做检查,虚拟地址是否在下面,P尺寸正确,那个,我们可能应该做任何其他可能被打破的事情,要增长的字节数,s中断的进程是int而不是无符号int。

所以负数可以用,是呀,然后负数可以用,这意味着缩小地址空间,所以我们缩小地址空间,我们也要小心一点,所以原来有一大堆像往常一样,呃,在操作系统中,有一大堆不同的案例。

就在我们将要查看这个特定的页表条目的地方,对于所有这些不同的情况,我们实际上可能需要稍微修改x36,这就是实验室的本质,你知道的,做得足够好,基本上你可以通过用户测试,通常测试会,你知道的,应力。

你需要处理的一大堆其他案件,到目前为止还有什么问题吗?

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/1b44dfaa9ed2f5cd06fa161e0ad1f661_5.png

好的,让我,呃,那样的话,我想谈谈其他一些用法,或者当你有页面错误和页表时,你可以做的很酷的事情,你动态地更新它们,另一个几乎微不足道的,呃,但你知道常用的是所谓的零场,呃,原来在呃,操作系统。

你知道有很多空页,嗯嗯,例如,如果你看看呃,用户空间中的地址空间,呃,是啊,是啊,其实六不是真的不先进,呃,但如果你看二进制文件的布局,我逃出来了,你知道有一些短信,有所谓的数据段。

通常也有所谓的BSS段,所以当编译器生成二进制文件时,你知道基本上填满了这三个部分,课文是,你知道的,你知道的说明,数据基本上是全局变量,实际上有一个不是零的值,所以一个初始化的,呃,数据。

VS基本上是一个描述,说得很好,有一大堆变量,它基本上列出了它们的大小,它们也是零,它们基本上没有被列出的原因,你知道吗,或者内存不在文件中是因为这节省了很多,你知道的,比如说。

如果你用C声明一个大矩阵,文件顶部是一个全局变量,它自动被称为琐碎的零,为什么要在文件中分配所有空间,就像注意,基本上你知道对于这个特定的变量,你知道内容应该归零。

然后基本上在正常操作系统上的Exec上,关于执行,你知道的,我们来看看这些免费的,分段,这是xpsix在发送数据时通常会做的事情,但他们是BSS的,你知道它会分配,你知道一个内存来保存BSS。

基本上在里面贴上零,你知道的,分配地址空间,把数据带进去,已经六点了,然后你知道,基本上,呃,你知道相当于BDS,你知道所有的全局变量,基本上为零,可能有很多,很多页,所有这些页面基本上都必须数为零。

这是虚拟地址空间,所以你知道要做的诀窍是说,就像,哇哦,我有这么多页需要零页,我要在物理记忆中做什么,这是虚拟地址空间,这是物理地址,你知道记忆,呃,我真正要做的是,我要分配一个零页,然后用零填充它。

基本上把所有其他页面映射到那个页面,你知道拯救我自己,你知道很多,你知道很多物理记忆,至少在启动时,当然,我的地图必须是有限的照顾,显性照料,我们不能推销,你知道我们不能允许它的权利。

因为每个人都依赖于它实际上保持为零的事实,所以我们就把它映射出来,只读,然后在某个时候,当应用程序开始写入,你知道的,基本上是从,有一个装载机和一个商店,到实际上是BSS一部分的页面之一。

所以因为我们想得到任何,商店,里面有一两个可变的内容,我们会得到一页,故障,那么我们应该在页面上做什么,故障,在这种特殊情况下,去吧,有人知道吗,我们应该在页面中做什么,这里的错误。

我想我们应该翻开新的一页,然后呃,对呀,在那里为零,并重新运行指令,是啊,是啊,正是这样,让我们假设我的画是这样的,实际上商店指令发生在顶部的那个,我们真正想做的是,基本上是分配一个新的物理页面。

记忆中的一页,你知道吗,Calc在里面放0,因为你知道这就是我们所期待的,然后我们可以改变顶部的映射,为了这个特殊的,因为对这个家伙来说,让我假设这个这个,我们可以改变这个映射,你知道被阅读。

写并指向新页面,然后你知道,基本上是这样复制的,什么是更新,然后重新启动结构,就是这样,为什么这是一个有效的,为什么我们认为这实际上是一个很好的优化,为什么操作系统要这么做,任何人,你不需要它,哦耶。

去吧,您不需要使用用户要求的那么多内存,所以最好在它,当你需要的时候,是啊,是啊,实际上相似,喜欢懒惰的分配,基本上是程序说分配了一个巨大的,你知道数组,你知道的,为了最糟糕的输入,它是一个全局数组。

一切都必须是零,但也许唯一的部分是被使用的,第二个好处是什么,第二个好处是你在执行中做的工作更少,所以程序可以启动得更快,得到更好的交互性能,因为您真的不必分配内存,你不是真的零记忆,您只需分配零一页。

剩下的就是你喜欢,你只是在映射页表,所以你只需要写P,有道理的,但不是,呃,更新,或者对,嗯,所以他们会变慢,因为每次页面折叠都会发生,是的,是的,所以你知道绝对有一个很好的观点,呃。

所以我们基本上把一些费用推迟到以后,就在我们做页面错误的时候,你知道,部分原因是我们希望也许不是所有的页面都被使用了,但就像,比如说,如果这一页是九十六页,四千九十六字节,呃,你知道吗。

基本上我们要把每四千人中的一页墙,九十六个零,在那里做一些模仿,但这是一个很好的观点,你知道吗,当然我们做了油漆,我们有,我们加上了桃子断层的成本,一个桃子要多少钱,我们应该如何看待这与存储指令相媲美。

或者贵得多,更贵,对呀,是啊,是啊,为什么好,商店会需要,打通公羊需要一些时间,但错误将不得不去内核,是啊,是啊,所以其实,有多少商店说明书,甚至在罗伯特上周给你看的陷阱处理代码中。

或者实际上你目前正在做的,在活板锁里,嗯,至少有一百个,是啊,是啊,至少100个字符以保存或存储寄存器,所以有相当多,所以两者都有,从用户空间传输到内核空间的开销,就像所有正在执行的指令一样,呃。

保存和还原状态,所以页面错误肯定不是免费的,所以之前问的问题是一个很好的问题,所以让我们呃,查看更多的优化,呃,一个人可以做,这些都是合理的无聊,或者可能是合理的直截了当,我们希望能得到更多。

几个更令人兴奋的,看看我想做的下一个是什么,下一个是在许多实现的操作系统中非常常见的一个,事实上,这也将是其中一个实验室的主题之一,所以让我们做下一个,那就是复制,有时也叫牛,叉子,你知道观察,呃是。

你知道很简单,事实上,我们知道我们在课堂上做过几次这样的观察,但是当,假设你知道,我们一秒钟前被枪杀的炮弹,查看shell运行,它处理一个命令,我实际上做了一个叉子来创造一个孩子,所以叉子。

你知道基本上得到了外壳的副本,所以我们有一个父母,我们有个孩子,然后几乎它做的第一件事的孩子是一个高管,也许执行几个指令,然后它实际上执行,你知道的,例如运行Echo,我们现在知道,呃。

应该在最后一圈拍,您知道fork是shell地址空间的完全副本,然后退出第一件事,它基本上做到了,你知道的,把它扔掉,然后用地址空间替换它,你知道的,包含回声,所以看起来有点浪费,就像,你知道的。

一年了,假设我们有父母的虚拟服装空间,然后我们有了孩子,我们实际上在做什么,这是物理记忆,在正常情况下,在XT 6或在未修改的XP 6中,你知道有一堆像,有四页纸,我们看到贝壳有一个,两个,三个,四。

你知道当我们开始的时候,当叉子跑的时候,你基本上得到了这四页的副本,一个,两个,三个,四,并复制所有内容,你知道从那些书页上,父母对孩子,你知道,然后呢,一旦退出发生,基本上,我们要释放这些页面。

并分配新的页面以实际包含Echo的内容,很好的优化,一个实际上对这个特定序列非常有效的是你知道,如果我们,如果父级有这四个映射,你知道,在它的地址空间里,从这里的零开始,第一个去那里,不管你知道什么。

取而代之的是,你知道,当我们创建子地址空间时,而不是创造,复制和分配新的物理内存,我们可以做的就是共享父级实际上已经分配的物理页面,所以我们可以设置PDS,你认识哪个孩子,到与父页中相同的物理页。

我们会再次小心一点,因为如果孩子想修改其中一个页面,更新不应对父级可见,因为我们知道我们希望父母和孩子之间有强烈的隔离,所以我们需要更小心一点,所以要小心一点,我们能做的就是映射这些页面。

在子级和父级中只读,那么我们当然会在某个时候出现页面错误,因为父级要运行,孩子要跑的地方,我也许你知道孩子或父母会做一个商店指示,可能会将数据保存到某个全局变量或某个地方,在这一点上克服。

这将跨越页面错误,因为你知道我们写的不仅仅是,所以我们得到一个页面错误,那么我们需要做好什么呢,我们需要复制这一页,所以让我们假设实际上是子级执行存储指令,所以我们分配一个新页面。

我们把你知道的页面上有错误的内容复制到这个新页面上,我们将该页映射到子,这次我们可以把它映射成读写,因为现在只有私人页面,你知道在孩子的地址空间中可见,事实上,有效的页面将被折叠在。

我们现在也可以读到父母,所以我们复制这一页,把它映射出来,并重新启动指令,当你重新启动指令时意味着你知道用户读取,你知道就像,我们在上周的讲座中回到了用户空间,这有意义吗,对这个有什么问题吗?对不起。

当你说我们绘制了父母的地图,um虚拟地址到,我们也读写呃,我们怎么找出来,和孩子的一样吗,是啊,是啊,因为,子空间的着装空间是父地址空间的副本,对呀,所以如果我们落到特定的虚拟衣服上。

因为地址空间是相等的,你知道这是同一个虚拟地址,在父母地址空间和孩子的地址空间中,有道理,谢谢。就像一些没有父母的过程,也许就像第一个发射的,它是否使用它的页面,它设置了吗,只需读取权限。

还是以读写开始,我们可以在福克斯做一个,它修改了,好的,由你决定,呃,其实这也是一圈,你要实现,复制的,就在懒惰的实验室之后,你知道你有一些自由,你知道简单的事情就是把它映射到只读的地图上。

您将得到一个页面错误,然后你会做任何你通常也会做的版权,所以你可以在两种情况下使用相同的机制,没有理由真正专门化或为第一个过程做一些单独的事情,我也有个问题,嗯,因为我们喜欢复制整张桌子,对不起。

很多时候整页都在,嗯,做任何类似的内存硬件实现,就像一个特定的指令,因为它们基本上是内存硬件通常只有一些数据线,读我或储存这段记忆,但我们有像,哦,将a页复制到b页,是啊,是啊,有一个x86,比如说。

有复制游骑兵的硬件说明,记忆,你知道,达到五岁,不会,但当然,你知道,在一个非常高性能的实现中,所有这些重写都将被流水线化,你知道,希望以内存带宽的速度运行,请注意,实际上,呃,你知道的。

原则上我们可能很幸运,我们正在正确地保存加载、存储或副本,因为在这张特殊的照片中,我只复制了一张,在未经修改的情况下,我们会复制所有四页,所以希望,你知道的,这就像严格来说更好,在内存消耗和性能方面。

你知道的,叉子会更快,我有个小问题,当页面错误发生时,我们看到它就像,它本质上就像我们试图写到一个只读地址,嗯,仁怎么说啊,像这样,这是一种在右叉上复制的情况,与它试图写入的内存被标记为已读的情况相反。

只对一些人来说,像合法的理由,除在右叉上复制外,它只是一种不变量,如果呃,如果是用户内存,然后它将被映射为读写,除非是端口复制的结果,是啊,是啊,对呀,所以它是一个需要在内核中维护的不变量。

内核必须以某种方式识别这是一个复制和写入页面,如果你愿意,所以我很高兴你问了这个问题,因为事实证明你知道风险五硬件,几乎所有的页表硬件实际上都支持这一点,我们之前没有提到这一点,但这是我们的。

你知道我们通常,你知道吗,无论两级,呃,或多级页表,这是我们的私人,如果你看PTE,你知道吗,我说过,你知道从零到七,但不是关于这两个部分,rw,它们是为主管软件保留的,所以你知道的主管软件。

这是内核可以自由地使用这些位,所以一个人可以做的一件事是说,决定那一点,A基本上意味着这是一个复制和写入页或复制和正确的错误,所以当内核程序知道这些页表用于复制和写入时,你可以在这些特定的页面上,啊。

你知道的片段,设置一点,呃,抄写,这样当页面折叠发生时,我们看到副本和右位被设置好了,然后我们就去做这个,否则我们就做别的事情,比如说,也许这是一个懒惰的分配,这有道理吗,是啊,是啊,谢谢。事实上在呃。

在实验室里,你知道的,你要做的一件事可能就是用这一点,你知道的,在PG中设置副本和写入位,在复印和写作实验室里还会出现一个环,目前在XT六中有一些,基本上是一个物理页面,或多或少只属于一个进程。

除了蹦床页面,这是蹦床页面,我们从来没有免费的,所以这并不是一个特别大的问题,但对于这些页面,你知道的,现在我们有多个进程或多个着装空间指向同一个物理地址,所以,比如说,如果父级立即退出,我们得小心点。

小心点,正确,因为如果父页面退出,我们可以立即释放该页面吗,可能不是因为可能有多个子进程,是啊,是啊,可能有一个子正确使用该页,所以如果我们如果内核释放那个页面,那你知道我们会有麻烦的。

因为基本上如果你看K3,k free实际上在三页纸上写了各种各样的,然后孩子带着那个页面运行,你知道各种奇怪的事情都会发生,那么现在释放的规则应该是什么,如果你没有孩子是免费的,我想是的。

也许更好的方法,或者更笼统的说法是说,我们真正需要做的是,我们需要参考计数,每个物理页面,当我们释放页面时,我们把裁判数减少一个,如果引用计数为零,然后实际上我们可以释放页面,所以。

您必须在复制和写入实验室中引入一些额外的数据结构或元信息,去做那个裁判,我们可以把这个存放在哪里,因为这个,如果我们要参考每一页,那可能会很多,是啊,是啊,我们对记忆的每一页都很好,是啊,是啊。

你可以少一点,但我们在,对于简单的实验室,我们要这么做,对于每一个身体上的,所以每四千九十六个字节,我们需要包含一个引用计数,我们能把它写在另外两个免费的部分上吗,好,好的,是的,是的,我不是读不懂。

但是呃,你知道的,如果你是多次那就太糟糕了,就在三四次之后,你不能不做,你不能再做优化了,但是是的,你知道这里也有一些自由,嗯,您真的需要用这一点来指定,如果是右边的副本,因为内核也可以,是啊,是啊。

是啊,是啊,你可以,您可以维护一些其他元信息,连同过程地址天说,基本上很好,这个和那个之间的虚拟地址是文本地址,所以我们有一个页面错误,你知道这一定是版权什么的,事实上,后来的一个实验室,你知道你。

您肯定会扩展X6为该区域维护的元信息,当你开始实施这些生活时,这里有一点自由,对此有什么进一步的问题吗,好的,呃,那我们去下一个,呃,这就是所谓的需求分页,另一个很受欢迎的,大多数操作系统都实现了它。

所以呃,你知道的,它基本上回到C出口,所以当前在未修改的x x x x x实际上加载文本段,和文件中的数据段,并将其映射到,和我们对懒惰和零填充的观察基本相同,我们能做的是,为什么要急切地做对。

为什么不等一会儿,看看应用程序是否真的需要,你知道那些特定的指令,你知道双星可能很大,把它都装进去,你知道的,从磁盘,它往往是昂贵的操作,也许数据段比它真正需要的要比典型的用例大得多,呃。

要求我们不必这么做,所以不是,呃,关于执行,而不是实际上,你知道的,我们分配虚拟地址空间,你知道我们会为文本和数据分配地址库,呃,文件里就是这样,呃,但在P中,我们将根本不绘制它们的地图。

你知道我们只是要保持,你知道的,在其中一页中,你知道我们只是不设置有效位,所以铃声为零,当然还有,你知道的,当我们拿不到第一页的时候,故障,如果我们在EXEC中这样做。

假设我们修改x 2 6来做到这一点,我们的第一次大故障什么时候发生?为用户地址运行的第一条指令是什么,用户程序,用户程序从哪里开始,它是在加载你的初始代码吗,是啊,是啊,是啊,是啊,好的。

我们只是把那一块留在正确的地方,这就是修改出口的全部意义,而不是在其中调用UVM,在哪里,所以你知道的大多数,当我们实际上,如果你记得因为后悔或类似的事情,你知道应用程序开始的地方正好是地址零。

所以我的照片有点,你知道他在这里领导,但这里是短信你的零,它上升到某个数字,基本上第一个指令,不管坐在这里的是什么,这是我们要做的第一个指令,实际上那个地址,这是第一条指令。

我们会在右边得到一个页面错误,因为我们还没有装,那么在页面上要做什么,故障,嗯,我们要在页面错误中做什么,它基本上会知道这是,呃,其中一个是什么,呃,呃,点播页面,我们必须记得早些时候的某个地方。

你知道,这对应于一些,呃,这对应于某个文件,这就是我们在像素处理程序中要做的是读取,你知道那个街区,并重新启动instru,然后我们就出发了,所以我们要得到,你知道吗,在最坏的情况下。

如果用户程序使用其所有文本,使用所有这些数据,然后我们会得到一个页面错误的每一页,你知道在里面,呃,这个程序,但你知道我们很幸运,你知道并没有使用它的所有数据段,或者不使用所有这些文本段。

那你知道我们可能会节省一些记忆,我们不应该做精确的,你知道吗,表演,你知道快得多,所以更有互动性,节目一开始就轰隆作响,它跑得很刺耳,在执行中几乎不用做任何工作,这使得这个优化有意义吗,好的,因此。

需求分页有一种轻微的扩展,这是事件分页的第二部分,第二部分主分页本原理,这里有一点问题我们还没有真正讨论过,可能是这样,你知道的,也许我们实际上正在阅读的文件。

或者文本和数据段甚至比物理内存中的实际内容还要大,或者多个应用程序从需求分页开始,也许他们的一些二进制文件,基本上比实际的财政记忆要大,所以你知道的典型的事情,如果你下去。

这个需求分页实际上是在内存不足的情况下,所以如果口径归零,如果内存不足,所以说,比如说,你知道你的需求页面,你得到一个页面,需要从文件系统分页的某些页上的错误,但你没有更多的免费页面了,你得做点什么。

所以典型的,这又回来了,你知道之前的一个问题,例子,懒惰也是如此,或者呃,您知道如果内存不足该怎么办,所以如果内存不足,你有一个很明显的选择就是驱逐一个投手,你可以举例而不是你知道,你可以举个例子。

驱逐页面并写入,你知道回文件,例如,如果是被修改的数据页,你知道你可以写回来,你知道到文件系统,嗯,然后一旦你选择了页面,然后你有一个新的免费页面,你可以把,呃,用你用新的,你知道只有三页。

让你知道你的错,然后基本上重新启动指令,再次重新启动指令有点复杂,因为整个机器基本上要启动并转移回用户空间,等等,所以这是一个,你知道的,典型的操作系统会这样做,当然,关键问题是驱逐哪个页面,选哪个。

那么一些候选人是什么,你知道的,什么,你做什么,选择要驱逐的页面的合理策略是什么,最近使用最少,是啊,是啊,所以这是最常用的策略,最近使用最少,这是通常被扔掉的页面,有一些曲折,如果你必须选择页面。

那么小的优化,您可以在脏页面和非脏页面之间进行选择,所以肮脏的页面是一个有商店的页面,非脏页面是存储页面,基本上只读过,但没有写你更愿意先给哪一个定罪,因为你必须在某个时候写脏话,无论如何,是啊,是啊。

呃,所以所以再说一遍,实际上再检查一下,嗯,我说脏话,因为在某个时候,30页需要写在记忆中,是啊,是啊,那倒是真的,呃,但也许现在你得写两遍,你知道一旦你写出来,可能后来又修改了,所以实际上通常,但是。

哦,我明白了,好的,操作系统正好相反,他们选择一个实际上没有写或不脏的页面,因为你什么都不用做,你可以重复使用它,你可以把市场,如果它出现在第一页的PT中,你们那里的市场是无效的,然后你就完蛋了。

然后可以在另一个页表中重用该页,所以倾向于拿走没有,你知道不脏的,首先,我能要求澄清一下这30页吗,所以说,我知道就像在缓存里,当我们有记忆的时候,然后我们说,好的,一条线是脏的。

因为它还没有被写进记忆中,但是内存中的一页呢,它对应于什么,怎么脏了,它必须写回哪里,只存在于记忆中对吧,是不是整个事情就像它在其他任何地方都不存在一样,真的所以什么时候能脏,是啊,是啊,好的,所以说。

比如说,如果是需求页文件页,其实,我们一会儿再谈这个,就像在下一个,呃,也许预取有点太多了,但是如果你有内存映射文件,将文件映射到,呃,内存,然后对其进行存储,那你就会弄脏那一页,好的,所以这只适用于。

就像一个页面实际上响应的不仅仅是一些内存,也可以是文件或其他东西,好的,好的,那是有道理的,好的,所以只是为了,呃,你知道,就为了多做一个,与此相关的另外两点,如果你看这个,又是宠物。

所以我们看到了这个爱尔兰W位,你会注意到实际上有一个位七,这是肮脏的一点,所以当寻呼硬件,当硬件写入时,你知道,在一页上,看着肮脏的部分,操作系统以后可以看到你啊,这个页面可以很容易地看到这个页面。

同样也有一个,呃,有一点,呃,代表过剩,所以每当一页被写或写,读的或写的,x的位将被设置,为什么知道这一点很有用,这在什么方面能很好地帮助内核,那些还没有被访问的,你可以,你可以驱逐右,是啊,是啊。

就像一种说法,如果你想实现这些最近使用的,和,你发现一个页面基本上在一段时间内没有被访问过,你知道它实际上最近没有被使用过,所以它实际上是驱逐的候选人,而设置了访问位的页面并不是真正的驱逐候选人。

所以位通常使用,或者实际上通常用于实际实现这个LRU策略,好的,但你必须重置吗,嗯,被访问的位每隔一段时间就不被访问,或者是的,这正是典型应用程序要做的,如果他们不这么做,也许为了所有的记忆。

他们扫过记忆,所以你知道,有一些著名的算法,被称为时钟算法,这是做这件事的一种方式,对不起,你为什么要设置它,你为什么要把它重置好,如果您想知道页面最近是否被使用,你需要,你定期做出决定。

然后说可能每一百毫秒或每隔几秒钟,你知道你清除了轴位,如果它在接下来的几毫秒内被访问,你知道它在过去的一百毫秒内被使用,和没有设置访问位的页面,在过去的一百毫秒内没有使用,这样你就可以保存计数器。

比如它们是如何被故意使用的,什么是真正的,基本上是垫脚石,能够实现复杂的Alleryour实现,好的,嗯,我想再谈一个,只是为了做,呃,然后这是最后一个,实际上你也将在,呃,其中一个实验室。

也就是内存映射文件,我们的想法是,你知道的,这里我们有我们的地址空间,我们真正想做的是,基本上能够将整个文件或部分文件加载到地址空间,这样我们就可以操纵,你知道的,使用加载和存储指令的文件内容,嗯,呃。

而不是呃,改为,你知道,不然你可以写,嗯,并能够支持这一点,你知道,使用大多数现代操作系统的典型操作系统可以提供称为nmap的系统调用,基本上,MF获取虚拟地址或选择和虚拟地址长度,不打算谈论。

然后是打开文件的文件描述符和偏移量,基本上这上面写的是,你应该,你知道的,地图,你知道他们遵循描述符,如果这是地址,您知道从偏移量开始的虚拟地址文件描述符,你知道在F文件中,你知道吗,把它映射到地址上。

在虚拟地址VA并这样做,我们有一些保护,喜欢读,写,等等,所以让我们说这是一个读-写,你知道内核,呃,赢了,所以内核实现和映射的方式是,你知道的,如果它不急切地,呃,这就像大多数助理不急切地做一样。

你知道我们基本上复制,呃,读取从偏移铺设开始的所有字节,从偏移量开始进入内存,设置,呃,p指向大块所在的物理内存,然后基本上从那时起,那个呃,应用程序可以使用分布,使用,加载和存储实际修改文件的指令。

然后也许当他们,呃,在那里我们都做完了,在那个调用中通常有一个对应的,这个长度允许应用程序说“好的”,这个特殊的文件我看完了,在地图的点上,我们需要把脏块写回来,我们可以很容易地找出哪些块是脏的。

因为他们必须在PTE中做一点设置,当然,在任何内存中,复杂的内存实现,呃,这一切都完成了,拉诺,你只是不立即映射文件,你只要在网站的某个地方做一个记录,说好,你知道这个P。

你知道它真的属于这个特定的文件描述符,所以网站上保留了一些信息,它通常在称为VMA或虚拟内存区域的结构中调用它,例如,对于这个文件f,它将是一个vm a,在一个dma中,我们记录,你知道的,文件描述符。

偏移量,等,我们应该生活在记忆的实际内容中,所以当我们得到一页,对于位于此的特定地址的错误,你知道,我们可以离开,内核可以从磁盘上读取并将其带入内存,在回答早期的问题时,就像。

这个肮脏的部分很重要的原因之一,因为在取消映射时,您必须写回脏块,这有意义吗,好问题,是啊,是啊,也许这是一个更普遍的问题,但这可能是一个问题吗,说,多工序,这些是在辅助存储上映射相同文件的内存。

然后像同步问题,是啊,是啊,问得好问得好,我如此,一般单位的语义是什么,例如,如果使用以下方法对同一文件读取或写入多个进程,会发生什么,我们知道写系统调用,是啊,是啊,是啊,是啊,这就像不对。

正确的理由会以某种顺序出现,或者权利会以某种顺序出现,所以如果两个进程要写入同一个块的文件,你知道要么第一个进程写Go要么第二个进程写两个中的一个,所以这里基本上是一样的,你知道的。

我们真的不必保证什么,如果您想做一个更复杂的UNIX操作系统支持文件行走,可以锁定文件的地方,然后您可以正确地同步,但默认情况下,在这个级别上没有同步或同步,那是有道理的,对不起,什么是长度。

什么是旗帜,哦,旗帜,呃,令是,你知道吗,要映射千个数字字节的区域,呃,戳被读取,写X标志,你知道是,你会看到当你做地图的时候,呃,这与该区域是否私人或共享有关,如果不共享。

那么您就知道它可以在多个进程之间共享,对此有什么进一步的问题吗,如果其他进程修改磁盘上的文件,这意味着这不会在这里反映出来,对呀,这是正确的,呃,除非我认为如果它不被分享,那么你应该反映这些变化,对呀。

但他们会使用相同的文件描述符,对啊,我不太清楚,你知道,和映射的语义,当事情被分享的时候,那里到底发生了什么,好的,我想是的,我想在惯用的情况下,是啊,是啊,和共享的,他们必须反映出来。

但是如果一个进程实际上打开相同的文件名完全分开设置,我想可能是不同步,即使是共享的,那太好了,好的,所以呃,这是在文件系统实验室之后,你实际上做内存映射文件,那将是我们最后一次,你知道吗。

虚拟记忆的笑声,除非你最后决定做更多的虚拟内存,呃,特征或练习,呃,无论你想做什么,呃,所以基本上结束这堂课的主要答案是,嗯那个,你知道的,总结的排序,你知道我们在过去非常详细地循环了一下,准确地说。

你知道页表是怎么工作的,你已经做了几圈和一圈页表,我们已经看了很多像,陷阱是如何工作的,而且你知道,页面错误,事实证明,如果你把两者结合起来,你知道的,你会得到,你可以实现非常强大和优雅,虚拟内存特性。

你知道,看了一整张单子,主要集中在那些在未来的失效中实际将要实现的,但你知道这只是你所知道的一个子集或样本,中实现的一些操作系统,典型的操作系统实现了今天实际讨论的所有,如果你看看Linux和所有的。

还有更多有趣的其他技巧,但希望这能给你一个很好的感觉,你知道的,虚拟内存的力量,一旦您可以在页错误处理程序中动态地更改页表,我想正好在两点五,所以也许有一个很好的点停止,但如果你有任何问题,你知道。

随便问他们,如果没有,你知道,我们应该好运地完成陷阱圈,我希望它不会像,呃,或者没有页表实验室那么难,对不起,我对这个问题的看法,当您在,在上一张幻灯片中,当您映射整个文件时,或者像你把它放进记忆中。

结果它比长度还长,因此,如果文件不适合虚拟地址空间,哦,我想这就像长度是我们想要的文件的多少,是啊,是啊,你知道的,所以长度就像我们想绘制的地图,从偏移量开始的文件描述符中的10个字节,哦,好的,好的。

我明白了,所以说,如果文件较长,那我们就不会把所有的都记在记忆中,是啊,是啊,是啊,是啊,我们可以随时检查,好的,我明白了,谢谢。谢谢。我有个问题,我有个问题,嗯,打洞的页面,所以我们在下面谈了一下。

就像传呼的人第二部分,但这是一般的程序吗,我们会在这些技术中使用,如果我们发现我们超出了物理边界,是啊,是啊,就像常见的魔法机制一样,像这样,甚至在分配中,我们跑在我,再也没有可用内存可以分配给页面了。

你知道,我们支持需求分页,或者我们通常驱逐的任何东西,你知道的,一些页面,你知道,通常使用LRU的一种方式来思考它,在稳定的状态下,操作系统基本上运行,呃,所有内存随时在使用,您想使用旧内存。

所以当我们开始新的事情时,你知道我们得腾出点空间。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/1b44dfaa9ed2f5cd06fa161e0ad1f661_7.png

P8:Lecture 9 - Interrupts 中文版 - MCATIN-麦可汀留学 - BV1rS4y1n7y1

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/ffef2399f630dec609e3b2538259bb70_0.png

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/ffef2399f630dec609e3b2538259bb70_1.png

已经签到的人的好奇心,你是如何体验陷阱圈的,如果你已经开始在懒惰分配实验室,怎么样了,如果你听到它的作用比桃子桌应用程序和安德鲁更痛苦,你想说点什么吗,我以为那个邋遢鬼没事。

这只是确保我真的正确地使用蹦床页面,有点烦人,是啊,但当我发现,然后就像这样 很好很好很好,是啊,实验室的全部目的是让你真正暴露在进入和退出的陷阱处理中,啊,阿米尔怎么样,你的经历怎么样,陷阱实验室。

我觉得陷阱实验室不错,比页表实验室更容易管理,我注意到有机会在实施的某些方面变得聪明,很好,懒惰的实验室 目前调试得很好,但两者都比 kt要好,依我看,我们会听到,我肯定你很高兴听到这个消息。

我也很高兴得到它,嗯,是啊,这两个先前的实验室比第一页表好多了,嗯,我想最新实验室最难的部分,懒惰的是,嗯,读和写,所有这些东西,但不是,没那么糟,依我看,好,与页表应用程序相比。

它会为你做凯瑟琳或懒惰实验室的陷阱圈,你在吗?凯瑟琳,我想凯瑟琳周你好,我做得很好,对不起,我不知道你在跟哪个凯瑟琳说话,我想只有20个人,所以你可能不太想,还有一个,我做得很好,好好,所以这个。

这种生活比蜜桃桌实验室少痛苦,或者是的,绝对不那么痛苦,下周,是啊,是啊,就是那个,在页表后面的那个,什么陷阱,那肯定要简单得多,是啊,好的很好,我们很高兴得到它,我只是想摆脱任何正式的舞会,见。

你知道吗,沥青钉比我们想象的木头更硬,所以就像,你知道这些更好,嗯好吧,呃,为什么我们要去,欢迎收看六八一的下节课,无论你在哪里,嗯,所以今天的主题是关于中断,在开始讨论中断之前。

我想分享一点我上周想说的一件事,我没来得及完成,你也许会觉得,嗯。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/ffef2399f630dec609e3b2538259bb70_3.png

当你看到,如果你能把我右边的屏幕,欧洲,所以在这里,呃,你知道吗,你看我的屏幕在左下角,我会让你通过对话机登录雅典娜,有几件事我想讲一下真正的锁操作系统是如何使用内存的,因为我们上周谈了很多。

尤其是在演讲的最后有很多关于,你知道吗,翻页,寻找自由内存,所有这些东西,所以这是雅典娜的机器之一,呃,有一件事需要注意,如果你看一下记忆线,它实际上告诉你运行的程序叫做 top,可能你们很多人都用过。

你看一台机器里有多少机器记忆,如你所见,这台机器里有相当多的内存,嗯,如果你仔细看一下,其实大部分都是,呃,就像,呃,你知道很大一部分内存不被应用程序使用,但它实际上是由缓冲区缓存使用的。

所以这在操作系统中很常见,你不会真的想把你的物理记忆,无所事事,无所事事,我们不妨用它来做一些有用的事情,所以在这种情况下,你知道它的大部分实际上是用来缓冲盖的,一点内存自由,不多啊,你知道吗。

光与机器中的总内存的比例,所以这是一个很常见的情况,像大多数操作系统一样,我们基本上在任何特定的时间瞬间 几乎没有内存空闲,所以这意味着什么,例如,如果应用程序或内核需要一些内存,我们得扔掉一些东西。

你知道吗,也许它有足够的几页,只有几页纸,但在某些时候它需要大量的自由记忆,它会要求驱逐者,一些东西,从应用程序或从缓冲区缓存,所以你知道有一种,所以重点是,基本上就是你的线性内核来分配内存。

一般不会有便宜的手术,因为你知道记忆不是,没有太多的内存可用,另一件有趣的事情是,我把顶部的输出按照内存的大小排序,所以你看每一个过程的每一行,你能看到这么大的地址空间,然后。

下一行实际上告诉你实际使用了多少内存,就像物理记忆的数量,你知道衣服的比例实际上是如何在物理记忆中的,如你所见,其 实,你知道它通常比地址空间要小,所以我们说的那些技巧。

这里使用的是酷酷的想法和虚拟内存的最后一句话,仅此请求分页共享列就是共享页面,你知道从共享库,一般来说,驻留内存比虚拟地址空间要小得多,可能还有其他一些本能,需要注意的事情,就像看着这台机器。

看起来很低,你知道吗,即使他们不到20岁,不到102岁,登录,嗯,你就会看到,有很多流程,你知道九百五十年大致没有表示,这就像机器已经运行了多久,两到四天九天,你知道你实际上是固定的,上校可能没那么久。

曾经,嗯嗯,好的,所以这就是,你知道任何关于这个的问题,您知道主要的一点是,大多数正在使用的内存和常驻内存通常比虚拟地址空间小得多,你可以在自己身上运行这个,你想看到你一个额外的手表,有问题吗?你看。

一些,呃,八分之六的学生正在登录,如果你环顾四周,你看到一堆 q在运行,好的,呃,没有问题,让我回到今天的话题。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/ffef2399f630dec609e3b2538259bb70_5.png

中间箍,嗯,你知道最基本的,你知道吗,基本思路简单明了,或者我们想要实现的是,在某些情况下,硬件需要注意,注意了,所以网络,因特网网卡的种类和中断,通常在键盘上键入一个键,你知道键盘会产生并打断。

你知道司机要做的是缓冲器 必须保存它的工作,不管它现在在做什么,是谁在现场做的,你知道吗,进程中断,然后继续或恢复到他正在做的工作中,嗯,你知道吗,基本上,储蓄和恢复是非常,非常类似于,你知道吗。

以前已经见过的机制,即系统调用,和陷阱,就像佩奇·福赫,不使用相同的机制,所以从这个意义上说,你知道吗,中断没有什么不同,与陷阱相比,或,你知道吗,系统调用,但有几件事让他们有点不同。

为什么我们会花上一堂关于它们的课,所以自由基本上认为不制造干扰,它与系统调用和异常略有不同,一,呃,我们要找一个更好的词,我要打电话给,它们是异步的,我的意思是这个叫做中断的系统,你知道吗。

当硬件发生故障时,然后中断处理程序运行,中断处理程序实际上可能与 cpu上当前运行的进程无关,所以不像,你知道吗,例子,当你进行系统调用时,或者当系统调用发生时,然后你跳入内核。

然后我们在调用进程的上下文中运行,打断不是这样的,它们可能与,呃,你知道吗,您知道在cpu中运行的进程实际上可能与处理程序无关,甚至可能没有花费它,有一个比我们目前看到的更多的并发性,事实上,嗯。

你知道吗,这里是讨论并发性的起点,我们会在周三的课上详细讨论,基本的观察结果是 基本上 cpu和产生它的设备是并行工作的,你认识那个有网卡的,你知道只是做它的事情,你知道吗,或许你在网络上回想。

然后在某一点上产生一个中断,一旦 cpu同时运行,也在做自己的事情,说吧,所以我们有真正的并行性,你知道吗,在设备和 cpu之间进行,你知道我们必须管理这种平行性,我们马上就会看到,最后,呃,你知道吗。

我们将主要集中在外部设备上,如网卡和uart,必须事先设定好,基本上每个设备都有自己的编程手册,就像风险五有说明书一样,你知道哪些登记,做什么,每个设备实际上都有类似的类型手册,与,你知道吗。

描述什么寄存器,它有你可以遵循的操作,设备的作用,你知道吗,对读写控制寄存器的响应,不幸的是,通常,设备的手册不如风险五手册清楚,这使得它是一个复杂的编程,更复杂的是,所以我在这节课上要做的其实很简单。

呃,如果你想谈谈正面是如何出现的,当你知道子弹印在前面,如果你输入"是",你知道这些角色,呃,变成红色 然后显示在控制台上,和,事实上,所以基本上剩下的关于这三个角色的演讲,嗯,呃,但你们所有人。

实现这一目标所需的机制,嗯,有什么问题吗?再深一点,好的,嗯,所以让我们,所以第一个问题,也许应该问的是中断是从哪里来的,所以我这次演讲的重点是外部干扰,你知道不是轮胎中断或软件中断,你知道外部干扰。

这里有来自板子上的设备,这是我们的科幻板,金,你或多或少地模仿,你知道有些小改动,我们看到实际上,你知道你从这块板子上环顾四周,有很多不同的设备可以连接到这块板上,你知道我们的网络连接器。

实际上这里有一个微型 usb在那边,有一个微型 sd卡,你知道有你知道重置按钮,所以有各种各样的线路 必须从这些设备上运行,知道进入 cpu,这堂课的大部分内容基本上是理解,你知道会发生什么。

当设备产生中断时,以及如何正确地,从设备上得到的信息,我相信像这样放下两个大头针,在这个扩展连接器的年份,我想其中一个是你是被传送的,另一个是美国艺术,你知道吗,零,收到为什么。

我认为它实际上与双方都有联系,以及通过这个延长线,我认为你是芯片本身,你知道吗,可能在那边的旅行中,好的,好的,所以基本上黑板上有很多线,呃,你知道吗,我们再放大一点 CPU的细节,你知道这里有个计划。

你知道吗,用于电路板或处理器的 sci fi文档,我们使用的风险5处理器,我们之前看过这个,呃,你知道我们在这里看到,基本上这里是右边的设备,这里你是零,你知道我们知道基本上你是零的内存映射。

物理地址空间的一些战争,就像所有的 dm,你知道吗,坐在这边,我们知道 dram位于 x 0以上,零和吹过六零,所以啊,是不同的设备,基本上有大量的存储指令,你知道吗,对那些,到物理地址,你知道吗。

我们可以在纽约证明这一点,并更详细地研究这一点,所有的设备基本上都能通过处理器,真正发生的是这个平台级的互联网中断控制器,这就是所谓的牌匾,简而言之,是那种设法打断进来的人,你知道吗,从外部设备。

所以如果你再放大一点,呃,这是一个图表,呃,在我们的芯片设备中,所以在这里我们看到,你知道吗,五条三条中断线 从不同的设备进来,可能是那些拥有地下线的设备,他们进入咔嗒声,然后咔嗒声和路线间奏,例如。

你知道吗,视情况而定,呃,这是我们的特别课程,他们挡了我们的路,我们经营它,我们在一个免费的课程中运行它,嗯嗯,基本上你知道斑块可以被编程,因此。

单击将把中断路由到这些核心中的一个或第一个可以接受中断的核心,你知道有一点灵活性,如果在这一点上 法庭上没有人可以接受打断,例如,它们禁用了中断,因为他们正在处理另一个中断,单击将保持中断工具。

你知道穿过衬衫是可以接受打断的,所以看一些内部状态,你知道要记住这一点,如果你稍微看一下文件,你知道你知道真实的事情发生的方式,是拔毛,指示有中断挂起,呃,其中一个课程,你知道基本上声称它,嗯。

所以这将告诉斑块不给任何其他核心,一旦核心完成,它会说就像,好的,我受够了这种特别的打断,你知道点击将为限流器,那些它可以在岩石中忘记这一点,有什么问题吗?有一种内在的风险,五中断结构,是啊。

所以当每颗心脏都有斑块的时候,该剧有没有保障公平的执行机制,嗯,通过内核来编程,以它想要的任何方式,你知道吗,盘子不是真的,呃,它只是传递的表面,中断,写它们。

但它需要内核程序来播放 应该传递中断的命令,etc,事实上,你知道吗,有优先级的中断,呃,内核可以决定哪一个中断比另一个中断更重要,有很大的灵活性,关于硬件还有什么问题吗,好的,嗯好吧。

所以这就是它的硬件方面,呃,让我们在更高的层次上讨论一下软件方面,嗯所以呃,通常,管理他的设备的代码被称为驱动程序,是啊,你也知道,基本上它只是代码,呃,你知道吗,最典型的路线和我们将看到的内核。

C码和 x或6,你知道所有的驱动器都在内核里面,嗯,在代码中,你知道基本上管理,或者驱动程序都在内核内部,所以我们得到了,你知道吗,我们今天要看的是,你知道就像你的瓦里是 uart芯片的驱动程序。

如果你看看它的结构,守则的内部结构,你知道大多数司机都有一个结构,是啊,我要画这个,不是地址阶段,或不打算使用地址空间,但通常当人们谈论司机时,有一个底部的部分作为顶部的部分,嗯。

以及中断处理程序的底层各方,所以当中断处理程序收到一个中断时,你知道 cpu启用了中断的处理器,中断就会开火,你知道处理器会,你知道吗,我们已经看到了第二条规则。

实际上被称为中断处理程序的设备 基本上调用这里的代码,在 justin中运行的中断处理程序不会在任何特定进程的上下文中运行,你知道只是你知道过程,只要处理中断,如果你愿意,最上面的部分。

驱动程序是用户处理的地方,或者你知道内核的其余部分,例如,在控制台情况下,您工作的正确镜像,真的有合适的界面,更高级别的代码实际上会调用,所以在典型的笼子里 实际上是,有一些问题,经常在司机身上。

你知道顶级代码,你知道在棍子里打电话,您知道进入这个队列和中断处理程序的字符,要么,你知道把女孩或依靠发送或接收,但如果是接收,可能会打断,什么也会粘,你知道其实字符使用。

我们会看到这个借口基本上是用来把顶部和底部分开的,允许设备与 CPU上的其他代码并行运行,中断处理人员,通常对网络商贩有一些限制,因为这些都是在任何上下文中运行的,你知道任何治疗的过程,你可以的。

你知道吗,调用复制或复制,因为它会,您知道当前的页表实际上可能并不反映应该将哪个字符复制到哪个产品的页表,所以司机的上半身,一般情况下,与通常级别进程的交互,可能会打电话复制一份。

它质疑这种高水平的图片,我们会更详细地研究这个问题,但这是最主要的,组织典型司机,当你意识到你知道,操作系统中有很多驱动程序,事实上,这是很常见的,你知道驱动程序代码的总和比核心内核本身更大或更大。

主要是因为你需要驾驶的每一个设备,你需要一个司机,大多数电脑都有很多设备,好的,我们来谈谈编程设备,所以典型的编程是使用映射的内存来完成的,I,O,就像一群人看到的,呃,在险五还是在科创板。

这些设备显示在物理地址空间的特定地址,这是由设备经理或电路板制造商决定的,它们是操作系统,你需要知道那些奇怪的设备在物理存储空间中的位置,然后他们用普通的加载和存储指令来编程,嗯到,呃,那些地址啊。

但基本上这些装载存储指令所做的是,嗯,它们读取或写入 以读取或写入控制寄存器,关于装置的,所以不是读写记忆,这些线性存储指令通常有副作用,设备的成本,做某事的设备,嗯,你必须查看设备的文档。

为了弄清设备的功能,你知道有时候这些文档很清楚 有时候这些文档不太清楚,你应该给你一点样品,打破嗯,我想在右边给你看两件事,离开屏幕,你看到内存映射,呃,物理内存映射空间,你知道为了科幻板。

你看地址是什么,哪里有特别的东西,MA,它们出现在物理地址空间,不应该用地图这个词,它们实际上出现在物理地址空间中,例如,清洁这个的c,你知道在0 x 2 0 0 0啊,我们做了。

让我们看看他们在哪里 有另一个,呃,牌匾,也在那里的某个地方,好的,这是在奥克斯的电影,那是平台,站台室和上行控制器,嗯是的,我们看到,实际上你在这个特定的地址是零,呃,结果是在我们用的 Q穆上。

用户在不同的位置有 u r x,因为我们用维库,他不会真的模仿科幻板,但一些与科幻板非常接近的东西,好的,所以这就是记忆地图,呃,或者是记忆地图 或者是衣服,你知道对设备,嗯然后呃,在左边。

只是一份文件,呃,UART,所以呃,这是1655,六五五零,这实际上是你的芯片,坐在那里,量子模拟,最后,我们用来与键盘和控制台进行交互,你知道这是一个合理的简单芯片,那里的设备里没有太多事情。

即使这样,他们也有点,你知道吗,复杂的地方在这张桌子上 这里显示了登记册,芯片有控制器寄存器,所以,例如,你知道控制寄存器零,在执行加载指令时归零,它持有它将持有数据,如果你恢复了指令,当收银员康复时。

基本上数据会被复制到,你知道吗,在外面的电线上,对,你是,这是一个,呃,基本上是一种允许你通过串行线路发送位的设备,你知道气味线是一条线 接收线是另一条线,基本上你要咬一口,你也知道。

基本上它们是多路复用的 或在这一行上序列化的,送到对岸,芯片的另一侧有一个按钮,基本上把大背组装成一个字节和其他一些你可以在这个设备上控制的东西,你可以在一定程度上控制设备的波特率,你也知道。

对我们来说 可能最重要的是登记册,A,登记册一,你知道你在这里说了什么,哪个是支持中断的寄存器,所以我们可以把它编程到一个可以产生中断的原因,所以特别是向下滚动这个文档,你会看到所有的卷轴向下一点。

你知道文件实际上更详细,基本上描述每个寄存器中的每个位的含义,例如,您在in中注册了一个变量,我在登记,谁赞成,你知道基本上这是线状态,中断和传输 保持寄存器,中断,所以这就是比特的意思。

你知道它一直在继续,有更多的文件,它告诉你寄存器越详细,以及如何在轮询模式或中断模式下使用它,我们以后再谈,你知道怎么做,或者你必须编程和提高,启用寄存器,等等,所以它是真实文件的简短版本。

你知道真正的文档,芯片制造商有更多的东西,更详细,我们能够谈论这个项目是如何,你可以看到这是最简单的设备之一,即使是那份文件,你知道吗,这是相当多的,对不起,我在想你的权利。

所以如果你写了一些东西给注册的传输持有,然后你再写,就在那之后嗯 它,它确保像以前的数字一样不会被覆盖,对,哦耶,是的,是的,是和不是,其实是你的一部分,这只是我们要关注的犹他州,基本上是加载指令。

加载一些值或存储指令,在寄存器中加载一些值,然后嗯,你是芯片走了,它将把那个字节发送到串行线上,正确,当它完成的时候,它会产生一个中断,对内核说,好的,我受够那个字节了,现在你可以给我下一个字节。

所以内核和设备都遵循一个协议,以确保实际上所有的事情都能解决,这种特殊的艺术,你知道我们用的是,有一个公正的,我想1655 55 58,实际上内部有一个5,我认为它可以缓冲一堆角色,就像我想的16岁。

但你还是得玩这个游戏,就像你不能在房间里放超过16个字符,直到设备告诉你,好吧,我见过一个角色,这有意义吗,好的,是啊,谢谢,所以我们不要再谈论这个了,在我们看更多细节之前,呃,你知道吗,呃。

你知道案例研究,你知道我们,我要去通过,呃,在某种程度上解释了设备和中断是如何工作的,有没有美元,LLS,你知道会发生什么,你知道吗,你到底是怎么打印的,所以基本上我们之前讨论过的设备,是啊。

基本上把角色,在这种情况下,美元进入纽约,实际上在我们刚才看的登记簿上,然后 uart生成,打断你以前,我们安排事情的方式,生成和中断啊,当设备,当角色被发送,在另一边。

所以基本上在 q和 ku中设置东西的方式是在 ent线上,在中心线的另一边,在另一个约克郡,这实际上是通过控制台连接到虚拟的,你知道另一边的虚拟控制台。

你知道在 ls中发生的事情 是因为我们需要使用输入,所以键盘实际上是连接到接收线的,实现了线条和键盘,你知道你在键盘上敲了一个键,键盘,虚拟键盘,在这种情况下,你知道你的筹码在那边,你知道会连载。

你知道那个角色 我把它寄给了外星人,在另一边,呃,另一边,你知道吗,把你知道的部分,你知道在一起又成了一个字节,然后产生一个中断,迭代和中断来告诉处理器,喂。

在中断句柄中的键盘上有一个可用的字符 中断句柄,从 uart到达字节,我们马上就会看到,所以有两件事我们想更详细地了解,实际上它们是如何工作的,好的,有什么问题吗?好的,所以嗯,风险五有一堆支持。

中断支持,是的,我们将看到更多细节,因为他们可以使用,但要知道有哪些,有一个安全主管中断启动注册,这有点,你知道一点,外部中断,但是像 uart这样的设备软件中断,我就不说了。

但是有可能从一个 cpu从一个风险五个核心发送一个中断到另一个风险五个核心和轮胎中断,我真的不打算谈论任何一个,你知道吗,软件中断或计时器中断的主管,我们要把重点放在外部干扰上。

然后是我们以前见过的另一个登记册,它被称为主管状态登记册,它有一点要禁用和启用间位,在这个特定的核心上,所以每个核心都有,你知道吗,这些这些登记册,嗯,除了这三个位元之外。

当你想有外部干扰或购物干扰你的时间,有一点可以控制你接收到的中断信号,所以你可以从不摆脱中断到有中断或相反的方式,但只要改变状态寄存器中的一个位,然后有一个单独的登记册,叫做主管,你的附加登记册。

基本上你可以利用这个过程,呃,你知道一个中断会很完美地发生,看一小口,看看到底是什么打断了,此外在登记的车辆中甚至会出现中断 我们之前应该见过几次,当然还有 SDG维克寄存器 基本上保存着这个程序。

也被寻址到处理器一旦切换到陷阱或陷阱,当发生中断时,会发生系统调用或页面错误,因为基本上这三种情况,同样的机制,我不打算谈太多,It"因果关系,因为我们知道,呃,之前的细节,它的运作方式基本上和。

当系统调用页面错误或其他异常时,好的,所以让我们看看,你知道吗,有点感觉,呃,这些东西是如何初始化的,你知道吗,所以我们甚至不讨论驱动程序实际上是如何工作的,但看看 x6是如何编程的,这些登记册。

所以我们要进入这个位置,你知道吗,在聊天中回答两个问题,这应该可以确保,好的,好的,好的,在聊天中有点落后,嗯,让我们看一下 x6程序。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/ffef2399f630dec609e3b2538259bb70_7.png

这些登记册,所以让我们调出一些代码,所以让我摆脱,是。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/ffef2399f630dec609e3b2538259bb70_9.png

我们其实知道,我真的不需要吃那个,所以我不会跑,用 d表示穿过事物,所以我只想看看相关的特定函数,嗯,所以可能第一件事是在开始点c,所以当机器靴子处理你的靴子时,开始函数叫做,它以 m模式运行。

你也知道,禁用分页,所以马上,因为后来,内核已经设置了页表,我们在这里看到 基本上代表,所有中断和监工模式的例外,嗯,然后是程序,主管允许中断,使登记册能够,你知道吗,接受痛苦的打断。

定时器中断和外部中断,我,然后在很大程度上,一个计时器打断了 居然碰巧来找他 都是在 m模式下处理的啊,代码中的"m"实际上是对注册时间进行编程的,因此我们的计时器中断产生的时间,我想下周再谈这个问题。

所以我要去主坚果,I,所以看看外部设备是如何处理的,所以我们的第一个外部设备就像,在那里我们打印到,你知道我们可以,我们可以看到这一切都在我们身上啊,所以这是它的控制,您可以初始化您的锁。

你知道我们其实一点也不在乎,至少不是为了这堂课,它实际上是把你的艺术,你知道你在里面是对的,这里,它实际上设置了新的芯片,或者为你的芯片配置,所以这就是兰迪的用途,你知道吗,一开始实际上禁用中断。

你知道有,只是有一个序列,当你编程的时候 你必须经历,现在你可以设定债券利率,然后你设置6位 或者你知道7位,你知道七八个字节,或修理或不修理,嗯嗯,你重置了你房子的内部 fifo,你知道吗。

把里面可能有的东西都清理出来,然后我们收到它,我们启用中断 以便在中断中传输,我在这里,我觉得你举手,是啊,我想知道波特率是多少,衬里运行,我知道了谢谢,嗯好吧,原来你已经在里面了,嗯,现在在这一点上。

你知道原则上计算机上的 uart可以产生中断,但当然我们实际上还没有被编程为中断,使这些东西能够通过项目本身来实现风险,你知道我们还没有设定舔这里的程序,所以在这一点上还没有真正发生什么,接下来。

如果我们要回到主圆点,我们会看到一点向下滚动,我们会看到这部电影实际上是一个初始化的,所以让我们来看看,基本上是轻弹 init代码,如果你看看刚才我给你看的这张桌子,使用物理内存或内存布局。

你知道李在某个特定的地方,正确的位置在内核菜单布局中,哪个骗子,零零零砖,你真的复制了那份文件,在某种程度上 我们把东西写在盘子上 基本上就是你知道的 拿着你为牌匾着装的号码,嗯。

种姓到一个三二指针整数,因为舔舐寄存器是32位的,然后你知道基本上给它写一个,所以这个类基本上是一个要写到寄存器上的类,你就是吉拉库,基本上它的作用就是,它允许来自 uart的中断请求,记住点击。

你知道基本上路线,呃打断了,所以一个过渡可能会从左边进来,从那张照片到电影,这个程序就像点击接受这些中断,类似地,它实际上通过接受来自iO磁盘的撕裂来编程点击,我是,根本不打算谈论,好的,然后我们回去。

你知道吗,到主点 c和,就在点击之后,机器只是在舔心脏,然后基本上每个核心,具体地说,所以唯一一个核心是第一个核心 实际上初始化了这个方面,所以基本上我们在这些设备上接受中断。

然后每个核心都必须单独说 我也对这些设备感兴趣,例如,每个核心实际上调用这些函数,所以每个核心都说我有兴趣打断你的艺术,基本上你是零,i或 q基本上是唯一的数字,你知道对于艺术,我对你的艺术很感兴趣。

我想应该是10号,我们基本上忽略了优先级,所以我们把它设为零,你知道必须向小集团表明 它实际上对接受间断剂感兴趣,现在你知道我们,你知道牌匾,所以我们基本上要对设备进行编程 以产生中断。

我们已经设定了 点击,你知道传递干扰,你知道对个人 CPU来说,但 CPU本身实际上还没有接受内部的,因为我们还没有在老鼠的财产中设置出价,所以让我们回到主点c,你也知道。

不管是什么意思 他都会做一些动作,但最后它实际上调用了调度程序,所以让我们看看 proc dot c 然后进入调度程序,我们在这里看到的是 当我们现在,基本上整台机器是排序处理器已经设置在调度时间表。

运行的中立进程,但之前啊,你知道做任何事情实际上使中断,所以我们来看看风险五,你会看到这基本上是一个 c函数 基本上允许中断,如果你不惊讶,你知道基本上它唯一能做的,它在状态寄存器中设置中断启用位。

所以在这个特定的时间点,就在这个时候,如果有一个中断在集团悬而未决,你就知道这个核心会被打断,稍后我们会看到,但在这一点上,中断被启用,所以这是基本的设置,有什么问题吗?哦,对不起,哦。

哪个核心会被打断,所以每个季度都运行他的摄像头循环,正确,无论核心需要什么,否则所有的核心都会中断,但假设只有,你知道吗,一个核心首先到达那里,就像核心一样,如果核心1在这一点上设置了它的中断启用位。

它可能会收到一个中断,一会儿见,中断时会发生什么,阿美说 谢谢,好的嗯好的,所以我想做的是,呃,看这个,如何印刷美元,你知道 shell的提示,所以我们要回到过去一点,现在我们想再看一遍。

所以这是第一个真正成为你的内部代码执行人员的过程,所以这基本上是第一个运行的过程,它的作用之一就是,它制造了一个代表控制台的设备,因此,在这里创建设备时使用make not操作并返回一个文件描述符。

你知道文件描述符零,因为它是第一个打开的文件描述符,然后采用它,你知道10个出局,然后标准错误,再做一次,所以这基本上把文件描述符设置为零,一二对应控制台,现在,所以你知道,从外壳上锻造出来的。

所以外壳打开了,你知道吗,开始用一个,如果我得到了,有一二三之类的 零一二开,你知道指着控制台,然后shell在空间中打印字符dollar,将脚本归档为2,所以即使一切都过去了,新的心,你知道所有的。

你知道把这一切都告诉应用程序程序员,就像贝壳一样 给了写贝壳的人,只是看起来像一个普通的文件来影响外壳本身,因为它写到文件脚本2,不知道另一边到底坐着什么,对我们在实验室里看到。

你可以用它做各种很酷的事情,但是这里有一个例子,基本上unix中的设备被表示为一个文件,这和其他任何事情都没有什么不同,好的,让我们来看看这个打印是如何工作的。

在小型 c库的 x6用户应用程序中有一个 printf,你很清楚你见过这种垃圾圈,你知道一种本能本身起作用,但在和基本上调用写系统 调用,在我们的情况下,写系统调用将是,你知道吗,文件描述符2。

她会拿着角色美元,当我们通过存储美元的内存位置的地址到要写入的文件描述符时,我们要求,你知道吗,写一个字,所以基本上每一个由shell编写的字符,当它导致系统调用时。

所以现在我们可以去看看实际发生了什么,所以我们,你知道吗,你以前见过系统调用,但是保存文件,他有权利,这是书写系统,称之为抓住他的论点,文件描述符,地址中写入的字节数,你知道美元已经被归档了。

我们快速看一下文件,好的,我们就快穿上我们想说的东西了,你知道在这里它的文件权利,它寻找阀门的类型,它是一个管道或者叫做管道函数,如果这是一个你知道可能做不到的设备。

然后它会为那个特定的设备调用正确的函数,所以我们知道实际上是要调用控制台中的写函数,所以让我们看看会发生什么,这是控制台,曲线图行走,你知道吗,我们现在不关心这个,呃,如果复制了一个字符。

在课程中你是 prc,所以这就是对你的呼唤,所以你们可以一起考虑,控制台是司机,我们在寻找司机的顶端,现在做一些专栏 进入视图艺术,实际上打印字符,所以你来了,让我们看看这会有点有趣,嗯。

基本上内部的uart保持一个缓冲器,所以让我们回到上面一点,其实传输是有缓冲的,缓冲区大小是32个字符,有一个指针,读指针中的写指针,它们被用来把这个字符放入一个圆形缓冲区,我会更详细地讲一点。

但这是给制片人的指示,也是消费者的指针,在这种情况下,你知道外壳,如果你愿意作为制片人,它实际上检查了,它做的第一件事就是检查,圆形缓冲区是否已满,所以用圆形参考工具进行检查的方法。

所以它从原始状态开始写指针和读指针都是零校正的,对缓冲区的索引为零,如果他们是平等的,你知道他们是,他们是,他们是,呃,缓冲器是空的,但是如果你在写指针上加一个,它和接收指针一样,例如,如果你知道你去。

你填一个零,一二三四五六七八,一九三一年,接下来你就要,你知道你加了一个 如果 然后,它仍然等于接收索引或读取索引,你知道缓冲器已经满了,所以没有意义,你知道在那个时候你什么都写不出来。

因为很显然你还很忙,你知道工作是你扔的,试图发送之前的31个字符,或指代人物,所以在这种情况下 巴克法则,贝壳,基本上我们把睡眠放在网站上,内核会运行一些其他的程序,直到架子被唤醒。

它实际上已经准备好发送,或者您正准备发送更多数据,当然,在这种情况下,情况不是这样的,对吧,因为就像刚开始印的几个字,所以缓冲器里有空间,所以基本上驱动程序会把字符放入缓冲区。

更新正确的指针以转到下一个插槽,然后叫你开始,基本上你的费用是没有别的,踢着设备说 嘿,去帮我做点事,嗯嗯,然后我们呃,它首先检查,如果设备还在忙,发送当前的字符,我最近,如果呃,如果设备忙。

你知道我们只是回去,然后我们就睡觉,可能欢迎睡觉,否则我们会从缓冲区中读取字符 并将其放入之前看到的传输寄存器中,所以传输寄存器就像这样,让我们看一下,你知道我们你写信登记,你知道石膏。

你知道什么类型的扳手,把值写进里面,你知道发射器的寄存器是零,正如我们以前从文档中看到的,好的,所以呃,所以基本上你知道我从哪里开始,你应该开始踢设备 然后说嘿,我有个登记簿,我有东西要给你送走。

基本上就是这样,基本上,如果你愿意的话,你知道系统上说,你知道贝壳叫将做,你知道一旦它真的踢到了设备,它们就会回到用户空间,贝壳可以继续做任何他想做的事。

接下来 shell可能会做的事情就是调用 read system来读取键盘上的输入,但这样外壳就会回到用户空间,使用我们以前见过的标准机制,就像用户一样,然后就走了,做了它的事情,与此同时。

这个设备还没有得到一个真正的踢腿来发送一些咬痕,嗯,所以,嗯,所以在某个时候,你知道我们会被打断,希望你知道 因为我们对硬件进行了编程 以产生中断,让我们回过头来谈谈这个问题,当中断发生时会发生什么。

好的,所以嗯所以,硬件是怎么看的,在这种情况下,当它中断时,风险五就会发生,它真的发生了,所以我们在状态寄存器中设置了这个中断激活位,所以这个过程可以被打断,你知道吗,假设键盘,你知道提高中断线。

你知道吗,穿过牌匾,你知道牌匾,你知道将中断路由到一个特定的核心,你知道那个核心有一点设置,所以你可以,它把主管设置在一个标签位,所以如果出价定了,发生了以下事情,这看起来很像我们以前见过的,呃。

但硬件做的第一件事是清楚的,这就停了,你知道如果再有人打扰你,所以我们可以先处理这些中断,如果我们想要更多的干扰,你知道我们必须重新启用那个部分,但你知道这只是停止了进一步的干扰,基本上是中断中断中断。

嗯,然后将 pc设置为当前 pc的异常程序计数器,你知道吗,例如,如果我们一起被打断,假设 shell将返回到用户空间,看起来很高兴地在用户空间中运行,中断出现了,因为一个角色讨厌。

然后程序面板剃须是任何程序计数器在用户空间,它救了那个啊,所以如果当前的模式,所以在这种情况下,在我们的例子中,假设我说的是用户模式的过程和监督模式,然后它设置模式,你知道对主管。

在基本上设置程序计数器,不管自卫队的价值是什么,好吧 那就要么呃,用户陷阱或uvec或内核ve,并取决于是否发生了中断,你知道我们是在你的内核空间 还是在用户模式下,但在我们的例子中。

可能 shell返回到用户空间的地方,你知道效果会把用户的效果地址,基本上硬件会根据位于 sdf的指令恢复内核,我们还记得上一节课的内容,基本上就是sdf,然后它就会启动,调用用户陷阱,好的。

所以最终基本上会陷入用户陷阱,这就是我们开始讲述这个故事的地方,因为我真的不想再谈论太多关于储蓄和恢复的事情了,因为我们在上一节课上已经详细解释过了,已经在这个最后的陷阱实验室里玩过了,所以你知道。

当你清楚地知道发生了什么,有问题吗?好的,所以嗯,我们又回到用户陷阱了,你知道吗,你在实验室里详细研究过的功能,和,呃,你知道吗,当你处理这件事的时候,一个系统调用的案例,我们要做的是。

基本上我们要做的就是看看这个案子,哪个是设备中断,对,所以我们要,装载机有点低,在文件下面,所以这是遮阳板中断,它主要看寄存器的成本 看这是否是外部中断,如果是内部干扰,它调用一个函数来单击声明。

基本上声明那个特定的清理邻居,所以让我们回到舔,再看索赔,基本上可以归结为,这个特殊的 cpu会告诉鼠疫,喂,我声称,你知道吗,如果 cpu 0或 cpu 1,声称是中断了,暗淡的回报打断了。

它得到了实际输入的中断的智商,所以在这种情况下,这将是一个中断的艺术和IRQ 10,所以点击平面基本上返回10,嗯,在这个代码里,我们看,你说嘿,是10号中断吗,十号中断,一定是被打断了。

我们基本上称之为 uart中断函数,你是一个中断函数,嗯,跑动,基本上把角色弄下来,你知道艺术和它得到的方式 你的性格已经有了,基本上看在第一个注册的接收登记册,把角色弄出来,这是一个整数。

然后它打电话给科索沃 中断做剩下的工作,好的,其实很抱歉,我只是做了错事,我们在讨论传输,呵呵,所以如果右边有一个角色,你知道我们会叫它 cas,但是在阅读方面没有正确的字符。

因为你知道我们没有读过任何键盘,什么都没做,我们只是在传送一个角色,所以这实际上会返回减去一个失败,然后基本上在循环中唯一的事情,它叫做,呃,从一开始就会发送任何可能在缓冲区中的其他字符。

子弹可能卡在里面了,可能里面还有另一个角色,因为美元之后是空间和正确的系统调用,可能,你知道写这个空间可能是在设备发送时并发发生的,所以当我们打断,你知道传输,中断完成,我们走吧,你知道。

它会在缓冲器里找到,太空角色的另一个角色,我们会把那个太空人送走,好的,有道理吗?我有一个很高的问题,其实,所以我知道你的建议很有用,如果我想用你的键盘,就像键盘上的字符,穿过 uart。

我们编写的内核代码,但是我不太明白你的意思,当外壳在打印时,像键盘这样的角色没有与,否,是的,但实际上这里有两个设备,一个是键盘,一个是显示器或控制台,你了解金的方式,你与控制台的互动是由约克。

把一个角色送到控制台,控制台的工作基本上是在显示器上画出那个字符,原来如此,好的,谢谢,可能需要一段时间,所以你才知道,你有点被驱使,好的,所以现在有几件事,你知道吗,我想退一步想想更高的层次。

我们继续,所以我们应该遍历代码,我们对这片土地有一种感觉,就像空气,浏览详情,退一步思考一下,你知道这一切意味着什么,尤其有趣的是 中断并发性,你就会考虑打断,透视并发性。

这实际上是使中断变得棘手或难以编程的原因之一,所以首先,呃,你知道吗,就像你之前提到的,装置,你知道,你是,在我们的情况下,设备和 cpu并行运行,一个例子是我们的你知道的场景。

我们刚刚谈到你已经送走了,你知道这个角色,你知道控制台,当这一切发生的时候,这种虐待正在进行,在这种情况下,不能回到外壳,shell可以让另一个系统尝试写一个空格字符,这一切都是平行发生的。

所以我们需要稍微管理一下这种并行性,我们已经看到了一点,但我要再详细谈一下,这里显示的并行性类型通常被称为生产者和消费者的并行性,对,我是说井里的那个,我待会再跟你说,所以我们会更详细地讨论这个问题。

第二是中断,停止当前运行的程序,或者当前程序正在运行,所以在shell的情况下,可能正在执行提取,就像你知道的,二百一十二和轰隆中断进来,在这一点上立即停止了,这很好,你知道吗,对用户来说。

空间代码并不是什么特别大的事情,因为再一次,当我们从中断台回来的时候,我们恢复所有的国家,你知道在用户级程序之外,它将继续,你知道在,你知道吗,地址被打断了,所以我们在那里看到了陷阱和页面。

所以你知道这实际上是合理的,但是有一点棘手 当内核本身被打断时,所以你在内核模式下运行内核会被打断,这意味着,呃,我们会更详细地讨论 即使是内核 也不会按照顺序直接执行,你知道吗。

如果你看到一条内核指令 后面跟着另一条内核指令,在这两个指令和中断之间可能会发生,取决于冬季是否启用中断和一些代码,中间有中断是不好的,在这种情况下,你知道吗,内核实际上可能必须禁用或启用中断。

使代码序列基本上是原子的,所以打断一下,启用和禁用,我马上就说,在更多细节上,后来被推迟了,出现的并发性问题是,对我来说,让我换个说法,司机的顶部,在驱动程序的底部可以平行运行,例如,在我们的例子中。

嗯,shell将再次被称为 write系统调用,你必须在它印上美元后加上空白,当我们要让它回到驾驶员的顶层时,它可能想添加,可能还想加上,你知道队列的空白处,实际上这辆车,但同时在另一个 cpu上。

它实际上可能需要中断,你知道,对于无人机来说,基本上也是平行运行的,这几乎是相同的代码,查看相同的队列,所以基本上中断处理程序可以平行地运行在设备驱动程序的下半部分,同时在不同的 CPU上并行运行。

在与司机上半部分的视野中,所以我们得稍微控制一下,你知道我们要做的是,那就是,使用锁,因为这里有一个共享的数据结构,即缓冲器,我们要确保你正确地知道数据缓冲区,你知道。

要确保每次只有一个cpu基本上操纵那个特定的缓冲区,我将使用锁,锁将成为周三课程的主题,我就不多说了,但它将在周三展示一个重要的时间,我想关注的实际上是一个制片人,消费者,呃,呃,呃,并行性。

这是一个典型的表现在司机身上的东西,很常见的现象是,所以生产者消费者,我觉得这很简单,所以你所看到的,你知道在驱动程序中基本上有一个缓冲器,在我们的案例中 我认为有三个两个条目,所以从0到3 1。

有两个指针,有一个读指针和写指针,所以你用了正确的指针,读取指针,如果他们是平等的,你知道缓冲器是空的,所以当你知道贝壳,你知道我们写文章,你知道把 c,你知道基本上把角色像美元放在这里,然后向上撞。

你知道下一个条目的正确指针,所以这就是制作人的部分,制片人可以继续,你知道加满油,你知道所有这些角色,直到你知道它是你,直到它击中,你知道真正的重点,直到您知道下一个增量是否会击中 read指针。

我们知道这个圆圈缓冲器是满的,在这一点上,你知道制片人冲了上来,我们刚才在代码中看到了这一点,用户基本上被称为睡眠,然后我们把这个外壳放到网站上 然后开始运行另一个进程,所以中断处理程序。

你就像你不在里面一样,或者进入,在这种情况下是消费者基本上每次,呃,呃,你什么时候开始,基本上,当你是一个中间会看,你知道读指针,如果读指针在写指针后面,所以正确的指针,假设空间在这里,现在是两个。

我知道如何画出空间,但这里有一个空间,所以写指针现在可能在这里,所以在宽容意味着,既然你知道你看到了啊,你知道我支持正确的命令,这就意味着,因为我得送下一个,所以你可以把它看作是,你知道吗,指针。

读取指针,追逐写指针,这就是发生的事情,现在可能是这样的情况,在某一点上,uart与写指针保持一致,对吗?当它们相等时,它知道缓冲区是空的,没有什么可做的,这有意义吗,对不起,所以这个数据结构是一样的。

好的,所以让我回到代码,然后呃,所以高级别图片,让我们回到uart,我刚才说的数据结构就是这个缓冲区,两个指针在 read指针中的位置是正确的,是这两个指数吗,这些指数,好的,内存中的数据结构。

只有一只公羊,所以所有的课程都可能与这个数据结构并行地交互,所以我们需要锁,好的,原来如此,我的问题是睡眠,是啊,它怎么知道,让贝壳睡觉,不会喜欢上面写的东西,只有这个的地址,是啊,让我好。

基本上你知道,休眠颜色将在休眠上运行的当前进程置于休眠状态,我们将在下周或下周讨论这个问题,在大量的细节上,它穿过等待的东西,在这种情况下,这就是衣服,你知道吗,基本上它有一个频道,身份证还是像波。

交流水,实际上睡在上面,在这个例子中,t是uart tx r的地址,启动函数,它想要,缓冲区里又有空间了,它会醒来,好的,我们会打给相应的电话,伴随着节奏醒来 表明任何睡觉的过程,这个地址应该被唤醒。

以及如何准确地实现这一点,我们稍后会看到一些东西,所以这是有道理的,所以是的,这两个电话一起打,睡觉醒来,有时被称为条件同步,还有其他问题吗?好的,很好嗯,让我看看,我看看,嗯啊。

所以基本上就有了印刷和女儿的整个故事,正如你所看到的,这实际上涉及到,有很多东西,呃,一起把钱放进控制台,类似的事情基本上发生在阅读端,所以在某种程度上 壳牌已经打印了门和空白。

然后我们将从键盘上调用读取这个读取输入,好的 现在我们想看看当这种情况发生时 会发生什么,所以这基本上是在最后开始的,再次归档,所以现在有一个读取系统调用,呃,如果是,你知道从控制台阅读,会导致,呃。

它实际上会,当然文件有点抱歉,不对,如果这个设备在本例中是控制台,然后我们调用该设备的 read方法,这是在控制台圆点 c,所以我们要去控制台,里德正在被调用,嗯,基本上它的结构和这个一样,你是。

你知道吗,上面的缓冲器上有个圆圈,在这里,它是一个输入缓冲区,里面有两个八个字符,然后基本上是同样的游戏,你知道这是一个生产者和消费者的相似之处,但在这种情况下,外壳是消费者的权利。

你知道它实际上是在阅读,你知道缓冲区里的人物,键盘是制作人,它将字符放入缓冲区,所以你知道让我们回到科萨尔,如果没有什么,如果读取指针,在正确的索引中读取索引的位置是相同的,你知道吗,也许缓冲器是空的。

它又回去睡觉了,所以在把美元印在符号里之后 贝壳就会进入休眠状态,或者内核会让外壳进入休眠状态,直到一个角色进来,所以在某一点上,假设你知道一个用户输入了l,你知道 ls在键盘上的第一个字符。

你知道我被派去,你知道在板子上的 uart芯片会被穿过来舔,你知道展示,否则核心就会被打断,我们会去聋人中心,聋子的内心,你就会打断,然后我们调用刚才看到的函数,当它回到海底,在那里我得到了喜欢。

这是我自私的 四秒钟,所以在这种情况下 你知道它会写,不会从你的设备中得到一个字符 正确的是我们会得到字符 l,我们会调用控制台中断函数,利用角色地狱,所以我们可以回到控制台,和控制台在,因此。

你知道得到的字符,所以现在我们有了一个,呃,如果是控制,P,在流程列表中打印一个列表,实现控制 h和控制 u做退格和控制任何,但在所有其他情况下,它基本上是做什么的。

它通过将字符打印到 les all来将其回传给用户,然后它基本上把字符插入缓冲区,所以呃然后醒来,你知道等待缓冲的过程,如果有一个过程在等待,如果它击中了新的葡萄酒,还有一个过程在等待。

会唤醒等待的过程,然后等待的过程会显示,你知道字符出了缓冲区,再一次,所以在这里我们再次看到这种解耦使用消费者和生产者之间的缓冲区,所以消费者的供给可以平行运行,每个人都有自己的速度,如果一个跑得很快。

你知道吗,要么缓冲区会在你的全部,然后我们会阻止,等另一个人抓到你的店,有什么问题吗?好的 再来一次,我想再谈一件事,嗯,那就是,嗯,你知道吗,我主要是在开玩笑,我说的是一种中断进化。

就像过去几十年发生的事情,嗯,所以,呃,对于面试来说是相对于处理器的合理快速,尤其是在,曾经很快,所以随着时间的推移,最初的单位被开发出来,你知道吗,在这种情况下 基本上意味着,就像硬件很简单。

因为它会,如果真的不需要对西尔斯做任何确定的事情,我想我们可以打断处理器,你知道这个项目会在工作中做到这一点,但他们得到的心本身可能是合理的,直截了当的,现在中断比处理器慢,我是说你可以。

你可以看到的权利,因为中断处理程序,呃,我需要保存或搜索,它让你接受中断,所以有一对夫妇,你知道吗,你知道吗,有一个,预算很少,只是用来实际服务中断的指令,所以如果设备产生中断高速,你知道吗。

处理器将很难跟上,所以你看看今天的设备,基本上硬件或设备,还有很多工作要做,所以基本上,在产生中断之前,设备本身需要做大量的工作,所以,呃,为了避免打断这个过程太多,现在,如果你真的有一个高性能的设备。

比如说你有一个千兆字节,千兆位,即使在,你也知道这张卡经常用,你就知道你实际上可以,或者甚至自身也能产生,你知道吗,从最低限度,就像64个字节,呃,也许你算算,你大概可以,基本上你产生的是一个点。

所以这基本上意味着,你知道吗,一次中断,如果你做对了,呃,每微秒,事实上,与比呃更多的人生活在一起,呃,它比呃稍微多一点,所以你基本上知道一种思考方法,如果有一微秒的时间。

它基本上完成了我们在内核中的操作,实际上是150万,比一微秒的预算还少,所以你可以计算出多少指令,不会有那么多,所以呃,在这种情况下,你知道,你知道吗,你需要一种不同的植物,比如你怎么处理,你知道吗。

解决这个问题的方法就是为这些快速的设备做一些,这叫做投票,而不是撒谎 打断,你知道 cpu也可以做的 就是不断读取控制寄存器,看看是否有一个字节,就像你知道的,你已经注册了,但你只要继续看我的登记表。

看看那里是否有一个权利,呃,基本上你知道这个生物的本质是 CPU旋转,它在设备上旋转,你知道,直到设备有了输入的测试数据,你知道这个废弃的CPU循环是正确的,因为。

你知道当我们一遍又一遍地检查寄存器的时候,看看它们是否真的是任何数据,你知道我们不会用这些周期来运行另一个应用程序,你知道之前你看到的是,你知道如果那里什么都没有,基本上内核推动外壳进入休眠状态。

以便另一个应用程序可以运行,但我们真的要,你现在就知道了,嗯,现在对于一个缓慢的设备,你显然想,你知道不能永远旋转,直到设备最终开始工作,我们真的很喜欢基本上切换掉外壳的末端。

这样我们就可以运行其他的东西,但是如果这个装置非常快,然后中断器的开销很高,你知道我们最好还是浪费,你知道基本上把设备,因为很快我们就会成功,所以如果我给你带路,GPO装置,你慢吞吞的,但你知道。

但如果设备很快,它节省了,保存,储蓄,或储存,或者保存整个条目,入境和出境费用,所以,例如,超,高性能网卡,你很清楚,你知道吗,呃,你知道的更多,最老练的司机是,基本上它们会动态地切换。

在轮询和间位之间切换,所以,好的,嗯,有问题吗?我的时间预算快到了,你剩下的问题,好的很好,那周三见 基本上周三见,我们将继续这个故事 并发性,我们要展示和平行,我们将看到一些实际管理并行性的机制。

以确保共享数据结构正确地获得数据,星期三见,哦,对不起,我只是我只是想找到我的问题在哪里,因为我失去了它,我的问题是在,嗯,所以我看到了,你在里面只是被召唤一次,所以只有一个缓冲器,一起分享课程。

嗯好吧,你也是唯一一个设备,对其他人来说,你知道通过通信,你知道在所有课程之间共享的一个 uart设备,可能是多个进程在不同的核心上运行,所有尝试打印到控制台,好的,原来如此,哦,是啊,是啊,有道理。

有道理,嗯,就像只有一个查询会做的东西与uart的时间,是啊,好吧,你知道这有点复杂,呃,但让我们看起来像这样,假设有很多人在写,作为一个实验,正确,和,我们看到的是,实际上让我,嗯。

所以在这里你会看到,你首先看到的是它实际上获得了一把锁,所以他们有多个核心试图把一个角色放进缓冲区,其中一个会拿到锁,其他人都不能走路,所以有一个人,你知道有一个 CPU实际上得到了成功锁定所需的锁。

是能够,你知道吗,如果可能的话,否则就去睡觉,你也知道,如果完成了,你知道吗,打开锁,接下来,你知道吗,核心可以进入并获得进入其工作的锁,所以基本上,这个锁将序列化对 uart的并发访问,有道理,是啊。

谢谢,我们会在星期三,我们将详细讨论锁,谢谢,你们也听到了,我有个问题,是啊,所以嗯,据我所知,我们为什么需要里面的锁,被打断的,因为我只有一个控制台,它们可能有多个核心,对,你的意思是,这种运气是。

我想是的,因为通常我们不想把东西锁在里面,中断,对,是啊,是啊,是啊,是啊,你通过电子邮件问了这个问题,啊耶,是啊,是的,问题是前面提到过,下半部分或中断处理程序可以与上半部分并行运行。

所以一个核心可以做你是普奇,另一个核心可能是你或运行 uart,好的,我们要确保它们不会纠缠在一起,基本上确保了它们被序列化,好的,这是有道理的,但是呃,这确实意味着 有时,就像所有的课程一样。

可能是在等待他们中的一个处理这件事,是啊,是啊,因为中断必须等待,然后就没有别的事情可以安排了,嗯好吧,我去开房,还有一些其他的过程,可能是跑步,不太可能,没有僵局,你知道吗,好的,有陷入僵局的风险。

但这不是这个,我们以后再谈,星期三,嗯嗯,但你大概知道这意味着什么,就像,如果缓冲池里有几个叫杜鲁基的家伙,然后一些点这个中断,他们实际上会释放锁,例如,让我们回到你的电脑上,实际上正确的是。

他们会叫睡眠,而睡眠实际上在我们的争论中带走了步行者,回头见,为什么呢 但在睡觉之前 实际上会让这个过程肯定进入睡眠状态 并释放锁,原来如此,所以基本上你称之为带着锁睡觉,意思是好的。

你睡觉的时候把锁打开,所以别人不会,好的,有意思,是啊,当你从梦中醒来,你只要重新获得它,好的,你得走回去,对,是啊,会让你熟睡,有一个单独的故事,我们将在一两周内讨论,每一个都有意义,是啊,是啊。

所以我想,所以我想我还不太清楚,所有的 cpus都会打断,当有人发送一些东西,你身体不好,取决于高度编程的斑块,你知道路,你其实,六美程序牌匾是只有一个 cpu,所有的 cpu都可能被打断,不过。

没关系,如何清理中断,好的,所以如果你回到那个小集团,你知道当你被打断的时候,你叫这个车牌认领,还有那个 cpu,那个特定的核心会得到,会得到智商,然后舔舔的人就会记得我现在用的是什么,也不给别人。

原来如此,好的,所以我们其中一个会随机得到,或者没有,啊,你可以给木板编程,你会说,呃,你知道吗,可能是前辈或者一两个,哦,我看到就像,原来如此,我在里面看到了,是啊,是啊,牌匾里的代码实际上决定了。

向谁,找谁麻烦,是啊,就像一条捷径,里面没有密码,就像,我想,你知道吗,哦,是啊,好吧好吧,好的,我是 CPU的,是啊,我是,是啊,我刚想说,戏在哪里冷跑,但是是的,这不是代码,是硬件,好的,有道理。

谢谢,星期三见,不我有个问题,与多个核心同时运行的想法有关,所以我想我回想起,就像他们中的一个,实验室里有,说一个质数的问题,我们看到的图案实际上像树叶的风格,但通常是,因为锁就在附近,就像普西。

来自多个进程的输入 c调用可以交错,意思是像一个单一的 printf 不能保证是一个人才,没问题,好的,有道理,谢谢,不客气,我有个小问题,是的,嗯,所以我记得读在这样的。

读数显示计时器中断是在机器模式下处理的,是的 我想知道我们在做陷阱实验室的时候 是在哪里处理的,比如在排骨实验室切换到机器模式的地方,然后我们所做的,是啊,好的,很好的问题,呃,所以你知道,呃那个。

所以我调出了代码,呃,所以如果你看看你的开始,正确,这些开始房间在结束模式,在时间的开始,当机器启动并设定计时器 检查,所以现在我们在里面塔玛拉和单独的节目中扮演克林特,它是用来产生中断的本地中断器。

当时钟中断时,基本上这里可能是最重要的功能,它使机器模式陷阱处理程序功能障碍,所以这是书面集会,当时间中断发生时,该函数将被调用,所以当你的内核运行用户模式或者监控器模式时,克林特产生了一个中断。

提高了线,它将切换到机器模式 并以同样的方式调用这个功能 时间效应,基本上相当于我们看到的主管模式和用户模式,好的检查,如果你看看内核,我们看过维雅上校,对,我是说,这是一个函数,看看它是否存储了很多。

这是定时器,基本上就是,同样的想法,假设它是一对夫妇注册,这样它就可以做一个工作,它唯一做的工作,实际上这6行代码,或者别的什么,五行七行的代码,基本上它是做什么的,如果你知道,产生未来中断的索赔。

然后基本上向主管提出一个软件中断,所以这是主管模式,好的,然后在mrt点,让我们假设内核在一个定时器芯片或者一个芯片,所以它变成了机器模式和红色,然后从机器模式返回到监工模式对吧。

如果中断项目在此时以主管模式启用,也许那个主管在主管软件中断时会产生,哦好吧,你知道吗,基本上内核也会做同样的事情,基本上你会去找维克上校,你知道吗,保存,还原所有寄存器,啊啊。

然后转到内核陷阱 内核陷阱会看到,原来如此,所以为什么要改变,到机器模式 首先在喜欢,它到底是做什么的,我不知道重点是什么,但这就像硬件工作,好的,抓到你了,我实际上做了更多的工作,是啊,是啊,是啊。

是啊,有一些原因,我想为什么你知道计时器是人机模式,但是呃,从我们的角度来看,你知道会很好,如果我们能把定时器的中断地址直接委托给主管模式,从来不用时间遥控器,但你知道这对这个特殊的芯片不起作用,好的。

有道理,非常感谢,不客气,哦,实际上我已经跟进了这部分代码,所以继续问问题,请,是啊,我看到它分配了三二,你去了64,但它似乎只用了四个,他们中没有三个,右星确实看到了,是擦伤,是啊。

我想它分配的比必要的多,剧本是三二,呃,这是一个划伤区,啊原因,呃,所以你对每一个 cpu都是正确的,呃,一定会有一些,划痕空间在那里,我想我们有三个或类似的 cpus,所以基本上分配得太多了,嗯。

但没有坏处,它分配得太多了,好的,好的,所以它只用刮擦,零划痕一划痕,零还是四和五,所以如果你有空,好的,所以从0到3,四个了,四到五到六,所以是六个整数,呃,我想我们有,我们在跑。

我们可以用四个 CPU,但我们有三个,但我们可以运行六次,二十四,就像一个溜冰者,我们分配了太多的内存,我不记得我以前想过这个,呃,我不记得具体去哪了,因为这两个人来自,所以也许再检查一下。

所以我可以重建,你,好吧,同样的商店,他们都是四个,它们都是8个字节,对,是啊,呃,它,好的,我再打给你,我完全想不起来了,你知道为什么,是的,好有道理。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/ffef2399f630dec609e3b2538259bb70_11.png

非常感谢,不客气 好问题。

P9:Lecture 10 - Multiprocessors and Locks 中文版 - MCATIN-麦可汀留学 - BV1rS4y1n7y1

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_0.png

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_1.png

是啊,他一定不是很好,呃,如果你好,埃里卡怎么样,你的懒人怎么样,我也觉得可以,嗯,我的咖啡和咖啡也有问题,但得到了那个结果,所以是的,好好好,是啊,我认为这是一个你可能不会想到的棘手案例。

当你开始编程的时候,但幸运的是,用户测试将为您找到它,实验室进行得很顺利,嗯,我还没说完,其实,呃,我不担心抄袭,我想,但你应该,肯德尔加纳怎么样,我想,在大多数情况下,那个,对我来说还不错。

可能最奇怪的是,只是想弄清楚它什么时候跌到了堆栈的边界以下,好好,好的,好吧,是时候开始了,呃,欢迎收看下一堂课,无论你在哪里,无论什么时区,所以今天的课是关于锁的,你可能在以前的课上见过散步。

或者至少以某种方式和他们保持联系,所以这个讲座是一个概念性的讲座,呃可能会和一些,呃,你以前见过的锁的东西,但我们会有更多的内核和操作系统焦点,这改变了一些事情,嗯,所以开始吧,你知道吗。

让我们提醒自己,为什么我们需要锁,嗯,你知道吗,我想我们的出发点是,呃,想要使用多个核心,他们想使用多个核心来获得性能,所以即使应用程序真的想在多个核心上运行,据推测,它想瞄准这些分数或离开。

应用程序可以调用系统调用,所以内核必须能够处理,是服装系统打来的电话,这意味着,你知道吗,如果系统调用在不同的进程上并行运行,呃,他们实际上可以访问,如你所见,实际上 x6有很多共享数据结构。

你知道什么是质子结构,或者你知道蜱,或者你知道稍后我们会看到缓冲缓存,实际上有很多共享数据结构,所以如果你有过敏反应,你知道一个数据结构,其中一个是作家,另一个课程是读者,你知道我们基本上需要街区。

你知道协调这些更新,共享数据结构,所以读者 c系统视图,所以我们需要街区,你知道,你知道控制共享或正确共享,从某种意义上说,这有点令人沮丧,因为你知道我们想要这个平行的轴,呃,我们要运行多个,呃。

在不同的路线上 平行地一致呼叫,呃,但不幸的是你知道,如果他们共享数据结构,知道他们需要锁,锁,没有连载,呃,基本上是运营,所以事实上,你知道吗,最后的锁会限制性能,所以我们的处境有点棘手。

你知道正确的地方,我们需要锁,你知道吗,但为了表现,呃,他们不好,但你知道这将是生活中的一个事实,你知道我们会想办法的,呃,但这是最高水平,呃,在这里你知道也许只是真的,你知道吗,呃,带上引擎盖。

为什么应用程序实际上要多个课程,这确实与你的技术趋势有关,你知道在过去,几十年后,你知道有些经典的图表,使得这些观点,所以让我,你知道吗,把其中一个调出来,有一个有点复杂的图表,呃。

但是你知道在 x轴和 y轴上有几年,有,你知道单位,它们是不同类型的单位,取决于,你知道我们在看哪条线,但真正要看的是,在过去的几年里发生的事情 是在过去的几十年里,从2000年开始,时钟频率不再增加。

呃,所以,基本上这是有平台或恒定的,结果是,你知道基本上核心的单一威胁性能,你也知道基本上已经达到了某种极限,你知道的或者想出来的,呃,但另一方面,当然考虑到,在同一时期,电阻器的数量应该仍然在增加。

所以如果你不喜欢,你知道吗,使用晶体管做一个单一的过程 或跑得更快,你知道吗,唯一的其他选择基本上是你有多个部分,你看,的确,从2001年开始,或者从2000年初开始,你知道吗,当然,号码已经消失了。

所以有一个应用程序再次表现,你知道吗,呃,你知道吗,可以依靠单和弦,它基本上要利用多个核心,这也意味着 如果一个应用程序,你知道内核密集,还是密集,你知道吗,但是像一个服务器。

那就意味着操作系统也必须是,呃,你知道吗,在多个核心上高效运行,所以这就是主要原因,呃,你知道我们对,呃,你知道吗,并行性,你知道吗,在内核内部,有什么问题吗?好的,我见过,我假设你以前见过这些图表。

但提醒我们,你知道所有讨论的出发点是什么,嗯,为什么罗,你知道吗,已经暗示过了,他们在那里是为了,呃,正确性,呃,你知道吗,如果我们有,你知道吗,读者和作家,或共享数据结构,你也知道。

出问题的是我们想避免比赛条件,所以如果你没有锁,你知道我们冒着风险,你知道吗,如果我们共享数据结构,我们会提高我们的条件,结果是比赛的条件是,挺烦人的,嗯,所以首先我们对它的实际情况有了一点了解。

让我们看看,我们就,让我们在 xv 6中创建一个比赛条件,看看它实际上是如何表现出来的,然后明白看看实际发生了什么。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_3.png

或者你知道函数 k 3,呃,在 calc dot c中,你知道吗,所以这是在你释放页面后冻结的函数,它把它放在免费名单上,内核有一个非常简单的数据结构,为了保留所有三页的免费列表。

所以一个人需要一个页面来真正祝贺球场上的人,你在这里又看到了,那个,内存分配器有一个锁,来了,麦洛克,在这里,它实际上更新了页面中刚刚被释放的免费列表,或者争论自由,所以我们要做的是。

只需评论这两个获得释放 基本上,呃,标记,你知道吗,获得锁,把锁打开,你知道吗,所以这个,这个,这段代码位于,那曾经是,是啊,现在没有更多了,它不再被执行了,从原子上来说,让 q u这样编译它。

你知道吗,在我运行它之前,我注意到实际上我们已经被解雇了,实际上,呃,可能打了一些电话,可能是3号,就像你可能,如你所知,所以实际上一切似乎都很顺利,所以让我们运行用户测试,或许你知道。

稍微考虑一下这个很有趣,现在,你期望什么,这行得通吗?这样不行吗?任何尝试过的人,我想它可能会丢失一些页面,但也许不会因为,嗯,可能就不会出现种族状况,是啊,其中一件事是,呃,我们提出的条件是。

他们可能不会发生,所以让我们运行用户测试,看看实际发生了什么,所以在这里我们启动它,花点时间,祖姆可能会有点抱怨,因为你知道运行了很多 给我的机器装了很多东西,正确。

如果你可能知道这里模拟自由课程的原因,追索权可能并行运行,你知道到目前为止一切都很好,你知道我们已经开始通过测试了,稍微慢一点,因为我同时运行变焦,让我们再等一会儿,看看发生了什么,让我们回到幻灯片上。

我们一会儿再回来,看看到底发生了什么,但你知道,正如所指出的,你知道吗,这些比赛条件可能出现,也可能出现不正确,因为通常情况下 你知道每一个核心或每次我们打电话的时候,三,呃,这两行是原子式执行的,呃。

就像他们对锁所做的那样,那就没问题了,唯一的问题是 如果我们同时执行两个处理器的两个威胁,所以它介于两者之间,但是看看这个,其实,你知道吗,当我说话的时候,你知道我们看到,其实大家都很恐慌。

所以有些比赛状况会引起恐慌,呃,还有其他的比赛条件会像提到的那样出现,或者像我提到的,会显得不够,你知道有些免费页面会丢失一些页面,所以基本上用户测试一直运行到最后,但抱怨说得很好,你弄丢了几页。

你所有的用户测试回合,好的,所以这些比赛条件会以不同的方式表现出来,呃,他们可能会发生,他们可能不会发生,呃,很明显这里发生了什么事,所以让我们试着理解 你知道吗,什么,实际上,呃,哪里出了问题。

回到 sli。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_5.png

所以你知道,你脑子里应该有的画面,所以我们运行的是多个核心,所以你的 cpu零,所以 cpu零执行指令,两者都有联系,你知道记忆,对,如果你回顾过去,回想一下这个方案的原理图 我们之前展示过几次。

实际上有一个 dram控制器,你知道实际上连接,你知道所有的舞台都在那里,或者所有的记忆都是活的,我要把记忆放大一点,因为我想,有地方画画,所以基本上我们的树列表,你知道吗,住在,在记忆中。

假设上面有一个两页的免费列表,嗯嗯,你两个都知道,呃,两个 CPU,你知道吗,差不多同时呼叫 k 3。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_7.png

再看一下代码,只是为了确保我们脑子里有这个正确的想法,所以我们看 k 3,你知道他们在某个地方通过了,我们要用一个财政报告。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_9.png

你知道进入免费名单,嗯,所以你知道 cpu 0,呃,你知道一个r,它指向一些,你知道免费页面,呃,也许是的,让我用另一种颜色,所以如果你知道我有简历,我也指着某页,你知道我们想加入免费名单,有意义。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_11.png

所以你知道我们回头看代码,你知道他们做的第一件事就是,你知道更新我们的下一个指向指向免费列表。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_13.png

假设你知道 cpu 1先运行,你知道它会做什么,我们会把它的指针,你知道一开始,你知道哪里的字段列表指向,如果你知道 cpon在同一时间运行,呃然后呃,你知道它可以运行,你在知道之前就知道了。

CPU零执行第二条指令,所以它实际上可能会做同样的事情,你知道它实际上也可能运行第一条指令并更新,嗯,并将指针更新为,所以现在两者都是,你知道吗,一个来自cpu 1,一个来自cpu 0,一个来自cpo。

一个指向字段列表的开头,空闲列表也指向字段列表的开头,所以现在有两个剩余的指令正在危险中执行,所以我们会回顾一遍又一遍。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_15.png

你知道密码,你知道剩下的指令正在执行,实际上是将空闲列表更新为指向 r。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_17.png

你知道吗,呃所以呃,你知道吗,你知道吗,零和一个将执行,这些指令可能在大致相同的时间完全相同,但有一个会先走对了,只有一个共享内存,所以首先会有一个更新,另一个是第二个,所以我们假设,你知道先走。

你很清楚,然后会发生什么好的事情,三个名单会指向他的简历,呃,然后 cpu 2运行,所以现在 cp 2运行 x这个 x指令,所以它要做的是,它实际上是为了更新,你知道吗,免费点对点列表。

所以这里有一个免费名单,它实际上会指向,那真的是通过了吗,所以你知道,现在有什么设置,正确,我们我们输了,你知道吗,基本上一页,你知道实际上,CPU零 x撤退实际上最终根本不在空闲列表上。

所以我们把那一页弄丢了,嗯,这是一个,你知道吗,糟糕的特定结果,当然,这可能是更糟糕的结果,因为可能会有更多的 CPU尝试做这个免费列表,他们可以观察新的一个,可以观察到三个是暂时指向 CPU的。

零就是,呃,所以我们开始用这口井,然后立即更新免费列表,你知道通过呃,第二个 cpu,所以你参与的越多,你知道吗,大概是吧,我们实际上可以得到比最后一页更奇怪的结果,我们也是,你知道吗,问你知道密码。

你知道怎么相信这个吗,解决这个问题的一个很常见的方法是使用锁,所以让我更详细地讲一下锁,那是什么,你知道吗,什么是锁抽象,好吧,呃,它只是一个物体,就像任何其他类型的,呃,内核中的对象,然后它有一个。

他们只是,实际上有一种叫做结构锁的东西,你知道有一些领域,你知道保持锁的状态,它有一个非常简单的应用程序接口,你知道有一个后天的,实际上,只有两个对这个抽象的调用,哪个,带着指针的车,呃。

一把锁敲了一下,然后释放了,你知道这实际上也需要一个点 通过锁结构基本上更新,你知道锁对象,基本上灯光的规则,你知道吗,这里的规则是,执行以下规则:只有一个过程,你知道吗,可以进入或获得锁。

任何特定的时间点,只有一个过程能够成功地获得锁,访客试图同时获得锁的任何其他过程,所以这个序列,你知道吗,指示,你知道吗,介于,你知道吗,唱诗班和领唱,常被称为关键部分,它被称为关键部分的一个原因是。

把保险丝指令放在一起,呃,你知道吗,需要做更新,你知道我们共享的数据结构,它以原子的方式被锁保护着,呃,基本上如果你有多个指令,你知道在车和释放之间 他们都是一起被处决的 或者没有。

所以从来没有这样的情况,基本上,关键部分的这些指令是交错的,就像我们在比赛中看到的那样,实际上这正是避免这些比赛条件的原因,有什么关于锁阻塞的问题吗,很多锁,事实上,六号有很多锁。

有很多锁的原因是因为你知道,你知道散步连载,你知道行刑的权利,您知道的两个进程想要进入这个关键部分,只有一个人成功,另一个负责关键部分,在第一次之后,它完成了,所以根本就没有什么相似之处。

所以如果内核只有一个锁,你知道这通常被称为大内核锁,基本上所有的系统调用,呃,在内核中被序列化,你知道你会,你知道吗,只是一个启动记录大内核锁的调用,你知道做任何它需要做的事,然后释放大内核锁。

然后基本上返回使用空间,然后第二个系统调用运行,所以我们有一个并行的应用程序,你知道一旦我们并行运行系统调用,突然间你知道所有的系统调用都是串行的,如果我们只有一把锁,所以像 x6这样的程序。

你知道很多锁,因为至少你知道,然后我们可以得到一些并行性,你知道吗,如果你知道两个系统调用,例如,使用两种不同的散步方式,你知道它们实际上可以完全平行地运行,呃没有任何呃 零,因为他们基本上,你知道吗。

呃,使用不同的行走来序列化,现在,呃,有几点很重要,在这个界面上没有人真正执行,你知道你把获取和释放,你知道这取决于程序,所以如果你想让某个特定的代码是原子的,然后由开发人员来把这些获取版本。

我们将清楚地看到,呃,你可以想象,现在有一点,这可能很棘手,所以重要的是要认识到,你知道锁定实际上并不是自动为你完成的,一切都要由开发商来解决,将锁与数据结构关联,并确保你知道适当的获取释放是死的。

所以很明显就像你知道的那样,平行线的行走极限,因此它们的极限性能,所以这就提出了一个问题,什么时候走,你知道吗,我给你一个非常保守的规则,但这是一个很好的起点来思考事情,所以保守的规则。

或者准则是一个更好的措辞 如果你知道两个过程,一个是,其中一个是,作家或更新者,所以这意味着它实际上要修改以共享数据结构,然后您需要一个数据结构的锁,所以这是一个保守的规则,你知道吗,当你在编程时。

你有一个被多个进程访问的数据结构,一个人可以成为作家,在这一点上,你应该想想,好的,有可能是比赛状态,想在比赛条件下避免这种情况,把锁插进去,我会用一个锁保证这种情况不会发生,呃,在某些方面过于严格。

有些情况下你知道没关系,如果两个进程实际上是一个共享的数据结构,一个是写入器,嗯,尤其是,有一些编程风格叫做无块编程,才是这种情况真正发生的地方,你可能想做一个无锁编程。

这基本上是为了获得更好的性能或更多的并行性,无锁程序很棘手,呃,更棘手的是,你知道吗,用区块编程,你知道我们会在学期结束时讨论这个问题,尤其是在操作系统内核中,但也许在这堂课和这学期剩下的大部分时间里。

我们会考虑用散步,你知道,这已经够难的了,你知道吗,只是用锁,不是那么直白的食客,嗯,所以它在一只手上一点点,呃,太严了,呃,因为你并不总是,呃需要,还有一些要输的案子,呃,你可能想输。

你想实际上可能想使用锁,你知道执行一些其他的,比如,如果你看printf,如果我们通过一条流到 printf,你知道 x6内核试图,至少,你知道吗,使整个字符串以原子方式打印。

你也知道这不涉及共享数据结构,呃,但是在这种情况下 使用锁还是很有用的,因为我们希望输出被序列化,所以这条规则不是,你知道完美,但这是一个很好的指导方针,有什么问题吗?嗯,我有个问题,不是关于这个规则。

但是有没有可能两个进程同时获得锁,因此能够改变结构,是啊,否,所以锁的部分契约,抽象是不可能做到的,原型同时获得行走,如果规则是,没有,从来没有两个代理真正获得锁的情况,可以同时握锁。

我们一会儿就会看到如何实现这一点,但是 API或所需的规范,在任何给定的时间点上都只有一个锁,或者零,嗯好吧,所以呃,你知道吗,如果我们看到编程块在这里,呃可能会有问题 因为这些比赛条件,嗯是的,现在。

当然我们在第三场比赛中看到的特殊赛况,或者我们在克鲁创造的东西 在某些方面看起来很容易被发现,你知道的事实是,如果你用王牌检测工具,它会立刻找到它,嗯,但你知道这是更棘手的案子。

所以你可能会想 为什么你不喜欢,使,你知道你做锁或者可以做锁,自动,所以你会遵循我刚才说的这个简单的规则吗,你知道吗,如果我们看到一个共享的数据结构。

然后您就知道了 操作和共享数据结构基本上应该需要遍历,你知道我们应该,我们应该和数据结构联系在一起,每次手术,那就是,呃,在那上面执行,呃,在数据结构上获取并释放锁,所以我们可以这么想,可能是六个条件。

就像每一个结构,你知道吗,有很多,当我们做任何与这个结构相关的事情时,这个锁是自动获得的,结果这个太死板了,这就是为什么你知道走路不是自动的,所以我们的操作系统,下面是一个操作系统的示例。

假设我们有一个类似重新命名的电话,将文件名从一个目录移动到另一个目录,假设我们有一个,我们会把它重新命名为,所以我们在目录中找到了文件名,D 1 x,你知道我们把它改名为,所以,呃,你大概。

如果我们遵循严格的规则,或者像这个自动锁定的规则,接下来会发生的是,你知道这是规则,我们有两个对象,我们有一对一对,所以我们要遵守规则,基本上是自动规则,你知道我们锁定了一个,街区,D一个。

然后你知道我们做第二部分更新,你知道两个街区,二,然后我们就这样做了,这是一种假设的方案 当你想象,如果我们做自动行走,这个例子的重点是你会得到错误的结果,为什么这是一个有问题的计划,为什么这行不通。

所以要考虑的是这个时期,所以我们已经完成了第一步,我们还没完成第二步,然而,另一个过程又能观察到什么呢?文件就会消失,是啊,你知道吗,这是在第一步和第二步之间,文件不存在,这显然是错误的。

因为文件确实存在,只是被重命名,呃,它从不指向时间,它真的不存在,呃,但通过这种方式,只是看起来文件可能实际上并不存在,即使它这样做,真正正确的,你知道吗,我们需要解决这个问题,我们实际上锁定了。

在两个第一重新命名的开始,然后擦除并添加,然后释放,这有意义吗,这里有个例子,我们有一个手术需要多个锁,而且锁不能与两个对象相关联,这就是这个操作的论点,必须是这样的情况,实际上操作本身首先需要两个块。

然后执行操作,所以这个自动锁定系统,你知道吗,直接可能的,会有很多案子,这不是因为运行或只是使用方案,至少会遇到问题,问题,所以我们可以说当我们访问一个数据结构,我们只需要进入。

或者我们必须获取与所有数据结构相关联的所有锁,我们一开始就需要一个,是啊,所以这是做这件事的一种方式,我觉得那很快,基本上就是有一个很大的内核锁,哦,好的,然后你你在手腕上,基本上你不再有并行性了。

所以你想做得更好,对,是啊,我觉得这就是紧张,你知道你可以让事情变得简单,基本上就是所谓的,你知道吗,核心绿堵,呃,但你知道你很放松,失去业绩,否则你可能会失去表现,取决于储物柜的内容是否有意义,谢谢。

呃,所以锁定透视,所以有不同的方法,呃,想想锁,你知道有三种常见的,还有呃,你知道吗,三个都去,也许这可以帮助你思考锁,也许其中一个是你最喜欢的,你可以用这个作为你思考的方式,不过。

看到这一点可能会有所帮助,关于锁 实际上有不同的思维方式,所以,首先,你知道吗,其中一个想法是,避免,如果正确使用锁,则丢失更新或帮助,你知道吗,锁可以帮助避免丢失更新,如果你想想,呃。

你知道丢失的更新基本上是,我们失去了一个更新到 k 3,通过在里面放散步,你知道实际上我们并没有失去更新,所以这是一种思考方式,一种很低级的方式,呃,另一种想法是,你知道你可以做锁,锁。

所以这里有一个关键部分的观点,你知道我们需要走一段路,我们做了一大堆步骤或说明,我们执行他们被释放的指示,基本上整个关键部分作为一个原子操作来执行,这就是所谓的,也是一个很好的方法,你知道想想锁。

然后是第三个,呃,你知道吗,那可能会有帮助,锁的作用真的是锁的健康吗,呃,保持不变,您知道它所保护的共享数据结构的不变式,呃,真正的情况是在唱诗班之前,如果没有锁,你知道,当我们获得锁并做一些操作时。

这个不变量是成立的,那么不变量可能会暂时违反它,但在我们发布的时候,所以如果你想想我们的免费名单,如果你知道不变量是,那就是,你知道三个指针指向另一个指针,所有的三页都在一个列表上,暂时违反了。

呃在这一点上,在三人组的中间,因为就像多个指针,实际上指向自由列表的开始,嗯,然后是建立在它的最后,所以在你知道为你知道,预先列出的,它有一个不那么复杂的变体,但是对于更复杂的共享数据结构。

这可能是一种有益的思维方式,实际上这把锁对你的作用,嗯,所以即使是在这种情况下,你知道所有三个锁透视或合理透视,嗯嗯你知道,也许他们中的一个,你知道你带来的比其他任何一个都多,和,你知道吗。

以此作为你思考锁的方式,关于这一点有什么问题吗?好的,嗯所以我现在要跑几件事,呃,呃,呃,算是吧,呃,你知道吗,不受欢迎的特性,或者这实际上可能发生在锁上,你也知道,我们知道锁是解决正确性问题所必需的。

你知道吗,为了避免这些比赛条件,但在不合适的时候走进来,使用不当,也可以介绍自己的一套问题,所以我想谈谈,所以显而易见的是,当然是僵局,你知道吗,或例子,你知道最简单的情况,你知道有点无聊。

但我是说你一边工作 一边想着,你知道如果你做了一个收购,你知道一把锁,所以你开始了关键部分,在关键的部分,你做了另一个唱诗班在相同的步行,会发生什么,第二收购方能否成功,根据我们早先给出的规格,呃。

你知道这不应该被允许,所以基本上第二个收购者必须阻止第一个收购者,把锁打开,但那是,你知道整个过程,所以基本上这将导致一个死锁,这是死锁的一个简单例子,呃,也许现在很有趣,事实上,这是一个僵局。

有62个文本,你知道吗,因为当它看到同样的过程 需要再次走同样的路,呃,实际上,你知道吗,引起恐慌,更有趣的情况是 当多次行走和参与,呃,让我们来看前面的例子,假设我们有以下一个,我们有一个。

或者也许应该是一个,我们有两个,你知道吗,执行 v名称,两个 cpu同时执行,重新命名,以另一种方式,在另一个方向,你知道的,两个一个,你知道我说 b只是为了让名字不同。

所以这里要观察的关键是 CPU 1,你知道运行一个从 d 1到 d 2的重命名和 cpu,呃2号正好相反,把名字从 d 2改为 d 1,所以让我们假设我们实际上按照它们的论点的顺序获得了行走。

所以正确的做法是在这种情况下,你知道我们会得到两把锁,从前面的例子中我们知道,这其实很重要,所以获取,做一把锁,你也知道,假设他们真的跑了,你知道吗,真正并行的,所以在这一点上,你知道吗。

另一个人可能会首先得到这两个,因为这是它的第一个论点,现在,当然,两个两个想要两个,所以我会试着抓住你们两个,会成功吗,我不会成功的,因为你知道,另一个人,你知道实际上有锁。

所以这个人会停在这里 不再继续,让我们看看另一个 cpu,中队二号取得中队二号,它的第二个论证不需要 done,他打算打电话给,它将被称为获取,它能继续吗 不,它将无法进行正确的。

因为 cpu 1实际上锁定了 d 1,所以在这里我们,你知道吗,有时这被称为致命的拥抱,你知道吗,呃是,你知道吗,因为我们获得订单的方式,我们订的,我们相当,锁实际上会导致死锁,这个例子有意义吗。

这是一个更高级的例子,对僵局,你知道这不是一个明显的,呃,问题,从某种意义上说,这个解决方案是合理的,简单,呃,解决方案是你知道如果你有多个块,然后你得订购锁,所有的行动都必须按照这个顺序进行。

所以如果你是系统设计师,您必须决定您知道所有日志对象的全局顺序是什么,例如,在这种情况下,你可能想说,d一应该总是在d二之前订购,这意味着,当我们执行重命名时,生命的法则是,我们总是得到,呃。

较低的数字,呃,目录第一,在我们确定高度或顺序之前,这将确保基本上有一个全球秩序,你知道这个特殊的情况 你就是不可能,呃,因为你知道,对这家伙的封锁命令将被执行,这个家伙将以相同的全球顺序获得锁。

你知道一二,我们没有这些致命的英国人,没道理,有什么问题吗?所以这说明了一个小问题,即使喜欢好吧,所以它修复,你知道这种僵局的问题,因为有全球秩序,而没有,只是这个秩序是全球性的,嗯,这是一个。

有点问题,呃,在设计系统时,因为嗯,等一下,是啊,如果你想想,你知道吗,像是步行点餐,呃,你知道这必须是全球性的,所以如果一个模块,1米,你知道吗,调用和方法,还有项圈,你知道吗,一克,你知道吗。

可能需要意识到,或者你需要知道,也不知道买了什么锁,或获奖散步,你知道吗,两种用途,呃,因为呃,如果你知道,为了呃,再用几组积木,你知道如果我们遵循我们的步行点餐规则,呃,它获得了所有的锁,你知道吗。

从 f和 g一起,实际上在一些,呃,全球秩序,所以这真的意味着,在最近的术语中,我两个的永恒,一定是我能看见的,所以你知道,一个可以确保你真的知道电话,两个以适当的方式在里面。

从某些方面来说 这是对抽象的违反,对,你知道抽象是完美的,你知道吗,我真的不知道,我们需要知道 M2是如何实现的,不幸的是,锁,一个常见的例子就是,你知道吗,有两个可能会泄露给他们一个。

因为我们真的需要知道,所以当你设计一个更大的系统时,呃,你知道吗,这使得模块化更加复杂,哦,对不起,我只是在想,它需要是一个完整的,嗯,锁的顺序,或者会不会有一些街区,嗯,以任何方式订购,他们,是啊。

看情况,如果像 f和 g,你知道吗,共享任何锁,对,是啊,例如,如果你喜欢看 xv 6,是一系列锁的顺序,因为一些街区和其他锁无关,所以他们知道他们从来没有一起获得,所以如果他们从来没有一起获得。

它们只是连接在一起的块集,如果你愿意,然后只有,您必须确保一个特定的锁集中的顺序是全局的,所以正确的是没有,不一定要全球订货,但就像你知道的所有功能一样,操纵相同的共享类型的锁 它。

他们需要就全球秩序达成一致,谢谢,为了,好的,所以一个,你知道吗,另一种呃挑战,呃带盒子的,呃,你知道吗,我们现在看到了两个挑战,一是僵局,一是模块化,呃,第二个,呃,挑战或第三个挑战只是锁和性能。

你知道吗,我已经暗示过几次了,但它需要足够重要来强调,所以基本上如果我们想,呃,业绩,您需要拆分数据结构,对,所以如果你有一个很大的内核锁,呃,这将限制您的性能 基本上在一个单一的 cpu中的性能。

如果你愿意,你知道吗,业绩,呃,你想让它和众多的cpus一起表演音阶,你得把它们分开,您需要拆分数据结构,最好的分裂,你知道吗,不明显,也可能是个挑战,你知道吗,例如,你知道吗。

是否将锁与每个目录关联起来,你应该把这把锁和,我知道,你知道吗,您是否应该将锁与每个进程关联起来,呃,还是更好,以不同的方式分割数据结构,如果你改变一下,你知道你要重新设计锁的规则,然后他们必须确保。

你知道吗,你仍然保持着内核想要保持的不变性,如果你开锁,你还得重写代码,或者你可能需要写,可能需要你编写代码,结果发现基本上,你应该重构,你知道吗,你的内核的一部分 或者你的程序的一部分。

通过分解数据结构获得更好的表单,OPS,引入更多散步,你知道这只是,工作量很大,你得仔细考虑 保持射程,你想要维护的,你得重写代码,所以总的来说,这只是一个很大的工作,他们不容易,呃,所以这有点消极。

你知道吗,新闻点,对,因为你知道你想要更好的表现,这表明你知道更多的散步,呃,但你知道这实际上是一个,你知道吗,很多工作,所以一般的食谱,你知道怎么去,这是为了,你知道开始,当然,绿色街区,再测量。

所以不管怎么样,在你的内核上运行一堆应用程序,看看你是否真的得到了,如果他们真的利用了多个漏洞,你就会加快速度,如果他们这样做了,你知道你基本上做对了,如果你不加快速度,你的锁设计已经足够好了。

基本上这意味着某个锁是竞争的,多个进程试图获得相同的锁,因此它们被连载,你就知道你得重新考虑,然后你需要重新设计,但关键是你想被引导,你知道吗,通过这些,呃,测量,因为它可能是衰变,你知道一些模块。

当然,绿块只是不经常平行调用,因此没有必要重新设计,因为重新设计是一项艰巨的工作,你知道吗,你也知道 这也会使关于代码的推理变得复杂,你知道吗,然后你知道这是个好主意 实际上不做重新设计是,不用了。

所以一般来说,一个好的形式规则是,你知道吗,从航向积木开始,度量争用是否出现在这些锁之一中,然后重新设计系统的这一部分,这样你就会变得更好,并行性,到目前为止,所有的问题都有意义吗,好的,让我们看看。

呃,呃,所以让我们看看它在 x6和呃,你知道一些代码,你知道,所以,你可以理解一点。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_19.png

所以我要回到我们的,呃。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_21.png

对着这个屏幕,我真的很需要这个,我想看你的作品 因为你知道,我们开始讨论锁定那里,在一个,星期一我想再仔细看看,现在我们对锁有了更多的了解,同时也说明了一些有趣的观点,所以首先,呃,你知道吗,结果发现。

你知道吗,你所知道的一切,它在看,你知道吗,结果发现 UART实际上只有一把锁,所以你可以认为这是合理的,粗粒,呃,在这个特定的时刻设计,至少对你来说 艺术,尤其是那把锁,你知道吗,保护,呃。

基本上是 uart传输缓冲区和正确的指针和读取指针,所以当我们传送,你知道吗,右边的指针指向传输缓冲区中的下一个空闲槽,三个指针是下一个需要传输的插槽,所以让我回去把它用完,所以案例研究,有一个阅读点。

有一个右指针或写读索引和读索引,这个必须去纽约展示,这位是作家,printf可能会将字符插入到这个缓冲区中,所以你知道我们可以看到的是锁,呃,你知道吗,锁有多个角色,一是要基本保护好这个数据结构。

数据结构必须是方差,即继续阅读,你知道吗,右边和 r和 w之间的任何东西,我们的角色需要,任何介于 w和 r之间的东西实际上都是空的,插槽和锁基本上帮助我们保持不变,你知道吗,让我们看看合唱团。

所以在这里你可以看到,你知道吗,你知道的第一件事就是,你知道吗,抢锁再贴人物,如果缓冲区里有个地方,你知道印刷,把锁打开,所以如果两个进程同时调用 c,那么这个锁将确保你知道。

第一个进程中的一个字符进入第一个插槽,然后第二个过程的第二个字符,你知道在下一个插槽,我们知道它们最后会出现在同一个位置上,对,所以这就像一个明显的例子,在那里散步,你知道吗,帮助我们,避免比赛条件。

否则第二个进程可能会覆盖,你知道第一个过程是性格,所以这是一部分,所以如果你去看一个我们之前做了一点,如果你看看开始,你知道吗,我们看到更多的事情在发生,呃,呃,我们实际上看到,呃,如果缓冲区不是。

你知道吗,呃,如果缓冲区不是空的,然后你知道我们知道基本上有一堆呃,正在进步或被发送的字符,你知道锁,你知道吗,确保我们不会真的覆盖其中任何一个,所以任何排在队伍末尾的东西,呃,实际上是被美国艺术。

我们确保我们基本上不会修改或干扰,因为你知道,抓住一把锁,最后,越来越多的事情是,权利,你知道去注册登记,就像寄存器,其中只有一个,你知道吗,基本上这把锁确保了,呃,你要记住,你是开始被称为与锁举行。

当然基本上只有一个作家,所以另一种不变量,或者锁定执行的另一个方面,是硬件寄存器,有一个作家,现在我还想谈一件事,那就是,呃,你知道吗,如果你刚刚做对了,硬件做好了,然后有一个中断。

你知道我们在你开始之前就知道了,对吧,你知道我们要做的颜色,它在衣领上需要散步,以确保我们不会有多个实体向正确的寄存器写入,嗯所以呃,查看器中断本身可以平行运行,你知道吗。

使用另一个叫做 printf的进程,所以我们有一个叫做printf的进程,它运行一个cpu 0,在cpu上,实际上是用r来中断,因为也许它什么也没做,所以它准备好了,在任何特定的时间点中断,它会的。

呃开始吧,必须是正确的,你知道我们想确保有一个作家,你知道这些硬件寄存器,或保护,你知道吗,传输缓冲区的不变性,你知道我们有获取锁,所以事实是,呃在第六季,实际上这打断了,你知道吗,能跑,你知道吗。

因此,驱动程序的下半部分可以真正并发地运行,在不同的处理器上,驱动程序的上半部分,所以,因此,你知道吗,中断功能也需要步行,事实上,在这种情况下,呃,你知道吗,它需要一把锁,然后叫你开始,把锁打开。

我马上回来,因为在实现锁的过程中,以这样一种方式,这实际上是正确的,实际上你应该担心的是,我很快就会说的,让我推迟到我到达那里,好的,所以任何关于这个的问题都是在美国艺术中使用锁的简单例子,好的。

让我来,呃,手指,请让我谈谈实现锁,所以规范是只有一个进程可以获得锁,在任何时间点都不会有一个以上的锁,你知道我们知道如何看待和理解,其实,你是如何以这样一种方式实现步行的,这种方式实际上是保证的。

但让我先写一个破锁,呃,这样我们就明白了 你知道挑战是什么,呃,还是一个破产的收购者,所以现在我们知道真正的挑战是什么,呃,所以这是我破了的,呃,所以,你知道它的作用是,接下来是,它有一个无限的循环。

当一个,锁定为零,意思是没人拿着它,然后继续打电话的人,她抓起木头,所以我们锁定了一个,你也知道,在这一点上,我们得到了锁,所以我们再也没有什么可做的了,如果我们没有被锁定,因为当锁是一把的时候。

这意味着有人在持锁,所以我们不停地旋转 等着绕着圈圈转一遍又一遍,直到你知道整个锁被称为释放,它将锁定,将锁定为零,你也知道,那么这个特定的实现有什么问题呢,我想两个进程可能会读出它没有同时锁定,是啊。

是的,所以这里有一个比赛条件,对啊,我只是想确保比赛就在这里,我们基本上可以有两个 CPU进来,你知道吗,所以如果我们用时间图和 cpu 1,呃,CPU零,你知道这是一个声明。

可能这两个都是 cpu 1,你知道达到声明 a,呃,和cpu 0,cpu,the,um,0,1,对于每个语句a,所以他们都看到锁定为零,然后双方执行 b,在这里他们看到锁定为零,这家伙看到锁定为零。

所以它们都执行语句 b 并且都获得了一个锁,你知道违反了,你知道这个特殊功能的规格是有意义的,结果是,你知道吗,来解决这个问题 并得到一个正确的实现,有多种方法可以做到这一点。

但最常见的方法是基本上依靠一个特殊的硬件指令,一种动脉指令 基本上它的作用是,它做这个测试,从原子上来说,嗯所以呃,解决这个问题的办法是,你的想法是,你知道在风险五,你知道这个指令实际上是原子记忆操作。

SWAP,呃,我们要用,呃,它基本上可以归结为测试,然后是一套,你也知道,基本上有了硬件保证 你会,所以,当你拿着这个,呃,需要两个论点或三个论点,地址,寄存器,从本质上讲,硬件的作用。

只是在概念上它基本上锁定了地址,如果你愿意,我稍后会讲到这一点,但洛克的地址是,输入一个临时变量,你知道吗,在那个特定地址的实际值,将 r 1的值写入该地址,然后将原来在地址处的值放入,呃,临时的值。

也就是原来的值,实际上是地址在一个r 2中,然后基本上在散步和返回,你知道在这把锁里,如果你能保证这个测试,你知道在哪里测试的结果返回到 r 2,集合实际上发生在原子上,这是一个硬件指令,呃。

大多数处理器都是这样的 半弧形指令,因为这是一种方便的方法来实现块,嗯,所以基本上我们所做的就是,我们降低了它的原子性,呃,从,或者软件锁的实现 基本上是硬件锁的实现。

所以处理器可能会以非常不同的方式实现这一点,所以基本上,呃,指令集本身就像一个规范,它实际上没有说它是如何实现的,这在实际执行中非常依赖,这取决于,关于记忆系统到底是如何工作的,例如。

我如果你知道预算处理器,呃,共享一个内存控制器,并重写到内存,那么内存控制器实际上可以支持这个操作,你基本上可以在,基本上我们在一个特定的地址散步,你知道吗,然后让你知道一个处理器做两个操作。

或者如果我们指示,然后基本上解锁,因为所有的处理器和重写都要经过这个内存控制器,内存控制器可以进行排序或阻塞,呃如果,呃,通常是公共汽车仲裁者真正能做到这一点,公共汽车仲裁者支持。

基本上是在原子波中执行两个内存操作,呃,如果是,如果处理器有缓存,呃,那就是典型的协议资金的一部分,在这里 kehere的协议将确保,如果有作家,你知道那个特别的,缓存行。

你知道我们想要更新的值最终会出现在一个缓存中,然后你知道,基本上,处理器可以通过两个操作锁定单个缓存线,所以你知道这其中的含义,有很多不同的方法,呃,但从概念上讲,呃,呃,怎么回事,就像你走在地址上。

你读出了原值,你存储了新的值,把旧的价值还给你,这有意义吗,为了做到这一点,看看我们如何使用这个指令,让我们看看在 x中获取租赁的实现,五,六,然后我们会揭露一些其他有趣的细节,所以让我先,呃。

启动旋转锁定,呃,像年龄一样旋转,如你所见,很简单,呃,你有一个平坦的块,就像我们都铎王朝的法典一样,然后它有两个其他的东西来调试,即锁和 cpu的名称,最后一个,当前的 cpu实际上持有锁。

这主要是为了打印调试消息,例如,如果你在同一个 cpu上做了两个,呃,所以让我们从唱诗班开始,嗯,让我们先看看这个循环,所以这实际上是,呃,我刚才说的那种测试,结果发现在。

c标准实际上定义了其中一个原子操作,所以嗯,所以c标准实际上有一个函数,它说,你知道吗,同步锁定,测试和设置,基本上它指定了我刚才描述的行为,每个处理器基本上都需要,你知道实施,呃,这种行为。

因为大多数处理器都有匹配的测试和硬件指令,事实证明这是一个合理的,对于流程实现来说很简单,所以,事实上,如果你看看内核,你知道吗,我们可以查看装配说明,你知道风险5处理器,是呃,是否需要装配说明?

这里是原子交换指令,呃,如你所见,呃,如果你知道原子交换,基本上就是登记,五,呃,作为输入,结果也是5分,一个是地址的保留,呃,如果不平等,呃,我们回来了,否则基本上我们就会回到,你知道,呃,跳回,呃。

仔细检查,在这里改变正确的东西,呃,六分之四,如果不等于去公牛二十加二十二,有点难以计算,但基本上在一种情况下,在另一种情况下,我们分支回来,呃,所以这使得只看 c代码更容易,我们进去吧。

如果没有锁的话 会发生什么,呃,1。我锁得好,值多少钱?锁将为零,所以我们称之为测试,接下来会发生的是,我们写一个,是啊,走进来,但是返回先前的值,所以如果前一个值是零,那我们就没事了。

因为这意味着没有人拿着锁,我们失败了,我们已经完成了这个循环,假设日志值是1,所以锁实际上是锁得很好的,这条指令有什么作用?上面写着,你知道旧的价值,把它放在现场,正确,这实际上是一个在这种情况下。

然后在那个地方写一个新的,但这改变不了什么,因为有一个锁已经被锁定了,函数会返回一个,表明之前的某个人,已经锁上了,所以在这种情况下 它不等于零,所以我们会旋转,我们会一直旋转 直到锁定目标,呃。

在发行版中发生了零级和缩放级,有什么问题吗?所以现在,基本上,你知道吗,我们来看看相应的,这里是,呃,释放行动,如果你再把内核看成 m,那个指示,呃,所以让我们希望发布可能是在它发布后。

所以这个版本实际上也使用了这个原子交换指令,把0加到1里,所以基本上可以保证这个原子更新,你知道我锁门还是锁门,把零写入 lk锁定是原子操作,你们中的许多人都问为什么不,你知道吗,把存储指令还原为0。

有人想知道为什么这可能不起作用吗,或者有什么问题,因为其他进程可能会在日志中写入1或者再写入0,但情况可能是这样,对吧,是啊,好吧,有可能是好的,所以可能有两个进程或者两个 cpus,同时给我写信,对。

呃,但我认为真正的问题是对很多人来说,我经常假设这也是你当你做一个单一的存储指令,这有点像原子操作,呃,但情况并不总是这样,你知道吗,例如,如果你和它真的依赖于像这样的架构实现,例如。

如果高速缓存协议工作,或者高速缓存系统使用高速缓存线工作,其中缓存线可能大于整数,通常比整数大,接下来发生的是,第一个操作是加载缓存行,然后更新缓存行,所以实际上存储指令基本上有两个微操作。

你可能会得到错误的结果,所以我,你知道到缺乏,你知道吗,必须准确理解任何硬件实现,以及整数操作是否是原子操作,或者写到64位,六十四,呃有点,内存值和原子操作,你知道吗。

我们使用的风险五操作是保证执行原子,这有意义吗,好的,好的,所以只是为了,呃,只是为了让你开心,呃,原子交换不是唯一存在的指令,呃,这是冒险飞行手册,你也知道,列出了一系列原子操作,所以原子的末端。

一种原子矿石,有一个最大的分钟,所有的人都可以在原子操作中读写一个值,所以在这个具体的实现中,我还想指出一些其他的事情,呃,让我重新开始,回去获取,呃,所以获取功能首先要做的事情之一,呃,是吗?

呃关闭中断,如果能理解为什么会这样,因此,我要回到我们的考试代码,你再考虑一下,所以我们要考虑这个案子,acquis实际上可能执行不正确,没有关闭爆发,所以我们要考虑的是 如果我们去找你的种子。

在这里你知道,假设你把 c加起来,获得锁,但没有关闭,打断了可能发生的事情,给大家几秒钟考虑,但如果你有一个想法或为什么它可能是错误的,你知道就像跳进去,嗯,也许它可能会被打断,因为,嗯,因为钟表。

然后发生了一些事情,它需要打印一些其他的东西,它再次尝试你的推杆 c,锁已经被拿走了,是啊,这可能是一种可能的情况,呃,有一个更直接的例子,所以我们只能说你可以看到,你知道吗,格雷洛克。

你基本上是在传输一些字符,所以你刚刚完成了传输字符,它是做什么的,如果成本是中断,正确,你在打断美国,你要打断的是同一个锁,你知道它是正确的,接下来会发生什么。

如果只有一个 cpu 所以没有其他 cpu,中断可能运行良好的地方,我们陷入僵局,对,因为当前的 cpu控制着锁,作为你的一部分,后来中断了,它试图做的第一件事实际上是很多已经持有的东西,实际上你知道。

在 xp6的情况下,你知道我们会恐慌的,你知道吗,因为你知道,同样的 cpu实际上是在尝试再次获得同样的行走,所以基本上,你知道什么是获取或旋转锁的交易,是两种不同类型的并发性,你知道一个。

两个不同的 cpu之间有某种并发性,我们要确保,例如,如果中断函数运行在不同的 cpu上,基本上我们不会在传输缓冲器上得到加薪,但如果它们运行在同一个 cpu中,我们得确保这仍然是一个话题。

这并没有被打断,所以我们在唱诗班里把纱线关掉,它们只有在释放结束时才会再次打开,当锁被释放的时候,在这一点上,它又安全了,你知道接受这些干扰,因为锁实际上不再释放了,不再拥有它 不再拥有健康。

这有意义吗,好的,在这个实现中还有一件更微妙的事情 我想谈谈,嗯,我们需要处理的问题,呃,这就是记忆排序,所以呃,例子,如果你想想,你知道锁是,比方说获得,锁住了,你知道对一个,也许我们有一个关键部分。

在这个部分中,你知道x是除了x加一之外的,然后要求释放,你知道说锁到零,所以你可以考虑在一个特定的,CPU,你知道吗,这些是正在执行的指令,如果代码仅仅是顺序的,处理器,导管,或重新订购指示,你知道吗。

只是为了得到更好的表现,所以,例如,呃,如果它是一个连续的流,把这个指示移到之后,这会改变单一执行流的正确性吗,你知道不对,你不知道和它有什么关系,所以完全没问题,如果是顺序执行,你知道吗。

散步后搬家了,呃,对数零,所以你知道,在单曲上,单个串行执行,没关系,事实上,所以实际上,处理器,你知道吗,一直这样,你知道他们确实有效地执行东西或期望执行指令,所以这基本上可以导致这些指令重组。

编译器也这样做,你知道,也许优化,你知道一些代码路径,它还会重新排序指令,只要它喜欢,你知道吗,结果是相同的连续执行,但很明显在同时执行的过程中,这将是一场灾难,对 因为如果需要什么,这是我们的释放。

基本上我们所做的,我们把关键部分移到了获取版本之外,这是完全不正确的,所以在并行执行中 这是错误的错误,所以你知道在硬件中禁止或告诉编译器,你不知道这样做,呃,有一种东西叫做记忆栅栏,或者我同步的东西。

呃,有一条指令基本上是说,就像之前的存储一样,你不能越过这一点,所以释放有这个,呃获取有这个,所以,例如,这个 x加 x加1,如果这是在唱诗班之后更新的,在释放之前,那个 x加 x加1必须在。

你知道这个特定的内存同步点,所以不会是,不会有麻烦的,呃,内存排序,所以这就是为什么同步在那里同步的原因,在发行的时候,唱诗班里还有一个,这有意义吗,我有个问题,是的,嗯,是按照惯例,港口,所以我想。

我想,编译器可以发现在获得锁之前有一条指令,在锁被释放后 它也可以被移动,是啊嗯,会发生这种事吗,还是会遇到障碍 看到你会看到,你知道吗,在这种情况下,格雷格收购有一个障碍,释放是一个障碍。

所以在锁定之前发生的任何事情都被设置为一,在那之前就会发生,也不会通过那个指令,所以这是一个障碍,所以你会的,这是第二道障碍,所以这意味着在这里之前的任何指示都留在这里。

两者之间的任何指示都将发生在两者之间,在获取和释放之间,接下来的指示,你知道吗,释放后会留下来,好的,所以我们要跑,呃,你知道吗,接近尾声,呃,所以让我,只是实际上,呃,在这里结束,所以呃,所以锁。

你知道吗,块对正确性有好处,呃,为了表现我们可以这么做,这有点令人沮丧 正确 因为我们是我们,你知道吗,进入锁基本上是为了在空中执行时获得正确性,但锁实际上限制了并行执行,所以一锁和两锁很复杂。

你将会体验到,在我们将要做的一些实验室里,事实上,从现在开始,我们会看到锁店一直出现,这将给我们,呃,你知道吗,至少,你知道一些想法,你得明白,比如为什么会有锁,你知道他们保护的是什么。

但如果你做并行编程,它们是与生俱来的,你知道你需要使用锁,所以你知道,如果你想避免锁引起的并发症,你知道有几件事你可以做,你知道吗,不要分享,如果你不需要,如果您没有共享的数据结构。

这些比赛条件不能发生,如此如此如此,你不需要锁,所以你不需要复杂的程序,但你知道,通常,您会有一些共享的数据结构,你会做的,呃,你需要锁,我觉得应该从粗粒度开始,然后移动到细粒度,如有必要。

根据你知道的测量结果 你想确定,我们确保锁实际上是竞争的,你知道在你真正,你开始重新设计,最后,你知道吗,使用种族探测器,其中一个比赛检测工具,这样你就可以发现问题或比赛条件,因为你知道你把锁放错了。

或者你把获取释放物的地方放错了,事实上,你知道你仍然有种族,好的,所以这是一个关于散步的快速介绍,呃,我们将讨论更多关于锁的问题,基本上在这学期剩下的时间里,我们将进一步讨论无锁程序。

最后看看内核是怎么做的,好的,所以让我停在这里,所以任何要去别的地方的人都可以去,呃,但如果你还有什么问题,请随意,请随便问他们,我们在聊天的时候有个问题,栅栏的指示不是不必要的吗。

因为 amo交换指令可以有获取释放顺序,是啊,呃,好的,所以呃,所以这两件事,呃,那里有同步结构,对于编译器和硬件来说,是啊,我要跳下去了,呃,开始办公时间。

但我认为在你如何做到这一点上 还有更多的问题,对于编译器,只有编译器注意到正在编译的体系结构,所以我们会知道它是否真的需要插入适当的栅栏,你知道吗,不管是什么建筑在运行,不管它有什么记忆一致性模型。

所以这个讨论有点复杂,每个硬件都有一个内存模型,编译器必须决定,你知道吗,给定特定架构的内存模型,它实际上能做什么 它不能做什么,我想我的问题是,就像栅栏指示只会变得不必要,如果你叫弹药,SWAP。

就像把,在那里它将与,你知道吗,编译器排序,然后记忆顺序,和失序,是啊,使用围栏指令的机械,只有在你做dot rl的情况下,指令才是不必要的,所以它似乎不会察觉,那么你会怎么做呢。

所以编译器在它的末端强制执行排序,但你已经用亚马逊网站,是啊,所以就像,呃,如果你,这是个很好的问题,你知道这是一个更复杂的,获取这些实现是指我们专门获取一个版本,人工林或人工林。

为什么我们可能会做比我们漂亮的地方更复杂的事情,当然,仅仅通过发出栅栏指令,但这有点复杂,嗯,所以如果你,呃,如果你对这个感兴趣,嗯,风险驱动的记忆模型是一个非常复杂的,所以如果你看一下之前的说明书。

有整整一章,你知道记忆订单,并告诉你他们必须把什么,在这种特殊情况下 编译器应该做的事情,所以你是说编译器会发现,我们把装配指令放在里面,它不会自己重新排序任何内存访问,当然同步了。

这个同步库函数是一个库函数对吧,它可以以不同的方式实现,这是一个特殊的实现,库函数由编译器提供,呃,但是有没有让编译器做优化的选项,在那里它自己移动货物和存储,是的,你这样做,你如何防止这种情况。

而不发出栅栏指示,这不是真的,我想我的意思是,呃,也许呃,我的意思是基本上,这表明,同步基本上告诉编译器和硬件,但编译器实际上可以以不同的方式实现同步,它知道它不能移动东西。

但它不必发布和围栏关于风险五的指示,它知道它在风险五的情况下以一种特殊的方式运行,但风险不是,五记忆模型,就像足够松散的地方,在那里故障的机器可以重组东西,所以你确实需要,就像收购,就像拥有好的,好的。

所以他们更多的是,它们是比同步更复杂的界面,哪一个给编译器编写者更多的,它给了编程更多的自由,会给编译器一些解耦,编译器部分和处理器部分,例如,我觉得有一面旗子你可以插进去,你知道说这是一种释放。

一致同步,你知道我不知道我脑袋里的细节,但你可以调查一下,这是一种粗粒度的界面,有更多的细粒度接口,给程序员更多的控制,我有个问题,一是,嗯,您喜欢在一个处理器上有多个线程吗?

你的辩论方式和我们在多人案件中的辩论方式大致相同吗,所以你能重复一下那个问题吗,只是为了确保,I,所以我们没有,我想我不认为我们真的谈到了多个线程,我们主要讨论的是多 CPU,所以对于多个线程来说,嗯。

我想解决办法和,当你有多个 cpus时,就像,你们也有同样的观点吗,或多或少,呃,至少在概念上是正确的思考方式,所以如果你有多个线程,但是只有一个 cpu,你还是想确保,呃,内核代码序列以原子方式执行。

所以你仍然需要有一个关键部分的概念,呃,您可能不需要显式的锁或释放,但你确实需要一种方法来打开 断断续续,在一段特定的代码中,所以如果你看看旧的操作系统内核,他们通常不会在内核中锁定唱诗班。

因为他们认为他们运行在一个单一的处理器上,但它们确实有锁之类的东西,你知道吗,基本上关闭中断,打开和关闭中断,好的,原来如此,嗯,我的另一个问题是,嗯,实际上在幻灯片上,是啊,是吗?是啊。

这种情况下 阅读总是会落后,我不明白,是啊,好的,所以这个去展示,是啊,不管里面有什么,你知道吗,基本上这是需要显示的字符序列,而且作者基本上是在添加越来越多的字符对吧,所以作者是这样写的。

基本上你认识的读者会跟随作者对吧,因为你知道不能打印 没有,你知道吗,放进缓冲器了吗,就像你知道你是什么 把东西放在展示上,你知道吗,我们会,你知道吗,开始基本上把第一个字符,就像在这个插槽里。

你知道吗,在显示器上,同时 printf可以进来,多台打印机,你知道他们在这里放了更多的角色,所以正确的指针基本上就站在这里,当这个字符显示出来的时候,你就会知道 uart会向上移动这个指针,呃。

显示下一个字符,所以 URI总是落后于作者,直到它赶上了,如果 r和 w是一样的,在这一点上,基本上这意味着缓冲区中不再有字符了,哦好吧,原来如此,这就更有道理了,好的,非常感谢,还有问题吗?

只剩下这里了,好的。

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_23.png

https://gitee.com/OpenDocCN/cs-notes-zh/raw/master/docs/mit-6s081-os/img/b9a5b7552f4dd1cee6570fbae1468305_24.png

获取《信号与系统》(奥本海默教材)的学习笔记或参考资料,可以参考以下几个途径: 1. **课程资源**:许多高校会将《信号与系统》课程的讲义、课件和习题解答发布在学校的官方网站或学习管理系统中。例如,MIT OpenCourseWare 提供了相关的课程材料,包括视频讲座、作业和考试题目等[^1]。 2. **在线教育平台**:像Coursera、edX这样的在线教育平台上可能有基于奥本海默教材的教学内容,这些课程通常包含详细的讲解以及配套练习。 3. **专业书籍**:除了宋知用老师的《MATLAB在语音信号分析与合成的应用》外,还可以查找其他辅助书籍来帮助理解《信号与系统》,比如《Signals and Systems Using MATLAB》by Luis F. Chaparro 或者《Schaum's Outline of Signals and Systems》这类辅导书往往提供了更多例题解析。 4. **学术社区与论坛**:加入一些专注于电子工程或通信领域的论坛如Stack Exchange中的Signal Processing板块,在那里你可以找到很多关于该主题的学习讨论及资源分享。 5. **图书馆资源**:访问当地大学图书馆或者使用Interlibrary Loan服务借阅相关图书;同时也可以搜索电子版PDF文档,但请注意遵守版权法规。 6. **自制笔记**:随着对书中概念的理解加深,尝试自己整理一份个人笔记是非常有价值的。可以从每章的关键术语开始记录,并逐步扩展到公式推导过程和个人见解。 7. **实践项目**:通过实际应用所学知识解决具体问题也是巩固理论的好方法。试着用MATLAB或其他编程语言实现书中的例子,并探索它们如何应用于现实世界的问题中。 ```python # 示例代码 - 使用Python绘制一个简单的连续时间信号图形 import numpy as np import matplotlib.pyplot as plt t = np.linspace(0, 1, 400) # 时间向量 f = 5 # 频率 x = np.sin(2 * np.pi * f * t) # 正弦波信号 plt.figure(figsize=(10, 4)) plt.plot(t, x) plt.title('Continuous-Time Sine Wave') plt.xlabel('Time [s]') plt.ylabel('Amplitude') plt.grid(True) plt.show() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值