<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>学习札记</title><description>纸船</description><link>https://github.com/Ryan-1125/Notes-Papership/</link><language>zh_CN</language><item><title>字节跳动 - 前端面试笔记</title><link>https://github.com/posts/%E5%AD%97%E8%8A%82%E9%9D%A2%E8%AF%95/</link><guid isPermaLink="true">https://github.com/posts/%E5%AD%97%E8%8A%82%E9%9D%A2%E8%AF%95/</guid><description>字节跳动 - 前端面试笔记</description><pubDate>Sat, 08 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;一、自我介绍&lt;/h2&gt;
&lt;p&gt;面试官您好！我叫刘惠安，现在就读中山大学软件工程学院，目前大二，这次来面试的岗位是国际化广告平台前端开发实习生。&lt;/p&gt;
&lt;p&gt;现阶段我主要深耕算法基础以及软件工程相关知识，平常坚持刷算法题锻炼编码思维；前端属于我本学期着手入门学习的方向，基础概念已经有所了解，后续我会投入时间快速补齐岗位需要的前端技能。&lt;/p&gt;
&lt;p&gt;在校时间我完成过几个编程项目，我主要承担核心代码编写、功能调试以及 BUG 排查的工作，平常独立做过 Qt、C# 管理系统项目，也积攒了不少调试经验。本学期我运用学到的前端知识，搭建了个人博客网站。最开始从 GitHub 克隆了 Astro 开源博客框架作为基底，之后自主进行定制改造。在做项目的过程之中养成了自主排查 bug、自学技术文档、还有和ai交流的习惯。&lt;/p&gt;
&lt;p&gt;我平时喜欢钻研代码，能够沉下心钻研技术，学习接受速度比较快，也认同字节的奋斗文化。如果能够获得这次实习机会，我愿意从基础任务做起，尽快熟悉团队业务、补齐前端知识体系，踏实完成分配的工作。&lt;/p&gt;
&lt;p&gt;我之前阅读过岗位信息，对字节和这个岗位做的事都非常感兴趣，所以很荣幸今天能够有这么一个交流的机会。希望咱们今天沟通愉快~&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;二、在校学习类问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你现在大二，专业课都学了哪些？哪一门你学得最好？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;现阶段修读过 C/C++ 程序设计、各类公共数学课程、大学英语；专业核心课包含数据结构、计算机网络、操作系统、计算机组成原理。
几门专业课里面我最为擅长数据结构，平时也一直在 LeetCode 刷题巩固，算法思维算是我的长处，前端相关课程暂时还没有深入学习。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;平时学业压力怎么样，假如之后来实习，可以保证出勤时间吗？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;课业压力尚可，我已经规划好了时间。上课和实习时间能够错开，每周可以保证四天到岗，可以稳定投入工作，日常课业不会耽误实习任务的进度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;为什么大二就着急出来找实习？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;在校课程大多偏向理论知识，缺少真实业务的开发经验。我希望提早进入企业项目，亲身感受团队的开发模式，同时补齐前端方面的实操经验，锻炼工程动手能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你的绩点如何？如何看待成绩？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我的绩点四点出头，专业排名处于前 20%~50% 区间。
我认可课业成绩能够检验基础知识，但相比纸面分数，我更看重代码实操能力，课余大部分空余时间用来刷算法题、独立编写各类练习项目。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;学校有没有组队做过课程大作业？你在团队里面负责什么？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;有进行过课程小组作业。在团队之中我主要承担核心代码编写、功能调试以及 BUG 排查的工作，平常独立做过 Qt、C# 管理系统项目，也积攒了不少调试经验。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;平时在学校会怎样安排自己每天的学习时间？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我的时间安排比较灵活。上完课程之后，我会拆分空余时段，一部分用来巩固专业课，剩下的时间练习算法题、钻研新项目，最近分出一部分精力入门前端基础知识。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;除上课之外，平时自学哪些技术？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;日常最主要的就是刷算法题目，训练自己的数据结构思维；近期刚刚开启前端基础的学习，从零熟悉原生前端相关知识。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;有没有参加过竞赛？收获是什么？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;有参加过程序类竞赛。竞赛主要带给我实战做题的经验，训练了我面对题目快速思考、调试代码的能力，也让我意识到算法基础对于程序员十分关键。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你软件工程专业，为什么选择投递前端岗位？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;软件工程方向覆盖面很广，我希望接触可视化页面开发。字节国际化广告平台的业务很吸引我，我愿意从零起步深耕前端，在岗位当中积累业务开发经验。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;专业课当中哪一块学得最吃力，你怎么攻克？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我当初感觉操作系统这门课程难度偏高，概念抽象不容易吃透。我的解决方式是反复翻看课本，查找网上通俗的讲解，动手整理思维导图，遇到难懂的知识点就拆分成分块，一点点消化理解。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;在校有没有担任学生职务，做过哪些事务？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我现在是班里的组织委员。平时主要负责对接党组织相关通知，落实好班级相关党务工作，协助通知同学各类事项，做好班级的联络工作，这段经历也锻炼了我的沟通能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;之后大三的规划是什么，实习之后打算深耕前端吗？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;大三我有考研的备选计划，最后的决定也会参考本次实习的体验。
如果这次实习收获不错，我会好好深耕前端方向，积累工程能力；不管后续选择哪条道路，企业实战经验对我而言都十分宝贵&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;三、求职、性格、情景问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你为什么选择字节跳动，为什么选择广告平台前端岗位？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;首先我很认同字节“和优秀的人，做有挑战的事”的理念，其次字节在互联网行业的技术氛围很好，很看重工程师基础能力，尤其算法和工程实操，非常适合我现阶段积累实战经验。
至于国际化广告平台方向，我认为广告业务十分看重前端页面展示、数据看板、海外端适配，业务场景丰富。我希望借着前端岗位接触商业化业务，同时锻炼自己的编码能力，因此投递该岗位。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你了解国际化广告平台是干什么业务的吗？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我的理解是平台面向海外广告业务，主要服务广告投放、数据看板、投放参数配置、投放效果的数据展示，依靠前端页面给到运营和产品人员可视化的操作后台。
我清楚这一岗位需要适配海外业务场景，后续入职之后我会仔细熟悉完整业务流程。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你现在前端基础薄弱，准备怎么补齐短板？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我会规划好分阶段补齐。
第一阶段，先吃透岗位刚需的原生 JavaScript、页面布局这些基础知识，可以应付日常基础开发；
第二阶段，跟着团队的业务代码阅读项目源码，熟悉团队技术栈、代码规范；
平常空闲时间我会整理笔记，遇到不懂的地方查阅官方文档，向前辈请教。
我本身刷题、做项目的自学习惯不错，可以较快跟上进度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;实习之后交给你的都是简单琐碎的基础工作，你可以接受吗？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我完全能够接受。我现在只是大二学生，本身缺少企业项目经验，琐碎的基础任务也是熟悉项目结构、代码规范、业务流程最好的途径。
我会认真对待每一件基础任务，保证交付质量，在做事的过程当中观察成熟工程师的写法，慢慢积攒经验，之后再承担更加复杂的工作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;如果团队里面交给你的任务你不会，你怎么处理？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;第一步先梳理清楚任务需求，自己先查阅文档、搜索资料，独立尝试思考可行方案；
经过自己钻研之后依旧卡住，我会整理清楚自己卡在哪一处，带着自己的思路和疑问再去请教带教同事，不会一碰到难题就直接发问。
做完任务之后我会做好复盘笔记，避免之后重复踩同类问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;和同事意见不一样的时候你如何处理？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我会先耐心听完对方完整的想法，对比我们两个人方案各自的优缺点；
一切以业务需求、代码可维护性、性能作为判断标准，不带入主观情绪。
要是我思路有疏漏就接纳对方合理建议；如果我有可行的依据，就清晰说出我的考量，最后和带教同学商议选出最合适的方案。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;平常写代码到深夜，能否承受高强度的工作节奏？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我平时在校准备算法刷题、独立编写项目的时候，也经常会为了调试程序投入较长时间，可以适应高强度的工作节奏。
我能够保证上班期间专注投入，遇上业务繁忙的时候配合团队进度；同时我也会合理规划时间，保持自己的效率。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你有什么缺点？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;目前我的短板主要在于前端整套工程化相关知识储备不足，Vue、打包工具这类内容我才刚刚开始接触。
不过我已经开始从零起步学习前端基础，后续实习期间跟着项目实操，尽快补齐这块短板。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你的优势是什么？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;第一是算法基础，我一直在坚持刷 LeetCode，具备基础的数据结构与算法思维；&lt;/li&gt;
&lt;li&gt;第二是喜欢自主折腾项目，之前独立写过好几款应用软件，习惯独立排查各类 Bug；&lt;/li&gt;
&lt;li&gt;第三自学能力和耐心比较充足，碰到陌生技术愿意查阅外文文档，能够沉下心适应新的技术栈。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;之后可以接受短期紧急赶需求吗？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我可以接受短期紧急的需求。我明白互联网项目偶尔会有上线、迭代的时间节点，遇上紧急任务我会优先配合团队进度，调整自己的时间，保质保量做完手上任务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;实习期望收获什么？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;首先希望熟悉国际化广告平台真实的业务逻辑，了解企业级前端项目的代码规范和开发流程；
其次把在校学到的算法基础落地到真实业务当中，补齐我的前端技术短板；
最后学习团队工程师处理问题的思路，养成工业级的开发习惯，为之后的技术发展积累经验。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你平时怎么缓解压力？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;当刷题或者开发项目压力比较大的时候，我会短暂停下代码，稍微放松一会，整理一遍现在卡住的问题。
平时空闲也会出门走动，调整精神状态，休息结束之后重新梳理问题，冷静地分步解决难题，不会被焦虑影响状态。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;有没有同时投递别的公司？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;近期有少量投递，但是我最心仪的就是字节这边的实习机会。比起单纯拿到一份实习，我更加看重业务平台质量和团队氛围，所以现在重心都放在本次面试准备上。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;面试结束你有什么想问我的？（三选一，推荐第一个）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;如果我顺利通过面试，实习生刚入职前期主要需要熟悉哪些业务或者技术栈？&lt;/li&gt;
&lt;li&gt;咱们前端实习生日常主要负责哪一类开发任务？&lt;/li&gt;
&lt;li&gt;对于大二零基础入门前端的实习生，您这边有什么学习上面的建议？&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;避雷小结&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;只要问到 Vue、Webpack、前端手写难题，统一话术：
&quot;这块我现阶段只是简单听说，目前前端刚入门，之后进入实习我会结合业务代码系统学习。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;四、项目开发类问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;介绍一下你印象最深的一个项目&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我印象最深的是我独立搭建的个人博客网站。最开始从 GitHub 克隆了 Astro 开源博客框架作为基底，之后自主进行定制改造。
整套站点拆分为两大板块，一块用来当做日常博客，存放我的出游游记、日常随笔；另一部分改造为专属笔记站点，用来收纳我备考 408 整理的专业课笔记、学习感悟。开发全程我学会 HTML、CSS、Markdown 语法，中途最难的任务便是接入评论区功能，攻克难题之后我还把这次排坑心得记录到我的笔记站点当中。代码依靠 GitHub 做版本管控。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;当初你为什么想要做这个项目&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我平时喜爱记录生活，经常会外出游玩，希望有一处专属站点存放游记和日常随笔。
之后我备考计算机考研 408 科目，平时积攒了大量学习笔记，纸质笔记不方便随时查阅。于是我萌生想法，改造一份相同框架搭建笔记站，线上存放学习资料，方便随时随地翻看复习，因此着手搭建这个项目。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;整个项目你一个人完成还是多人协作？你的角色？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;项目由我一个人独立完成。我承担全部工作：框架初始化、页面定制、功能增减、bug 调试、版本管理、后期内容维护，全程由自己规划需求和开发节奏。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;项目当中用到了哪些技术，为什么选用该技术？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;用到 Astro、HTML、CSS、Markdown，使用 GitHub Git 管理代码。
选择 Astro 框架有三点理由：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Astro 主打静态站点，博客、笔记网站属于静态页面，不需要很重的前端框架，加载速度很快；&lt;/li&gt;
&lt;li&gt;框架原生就支持 Markdown 文档，非常适配我书写笔记、随笔的需求；&lt;/li&gt;
&lt;li&gt;网上成熟的开源模板较多，文档完善，对于刚入门前端的我上手门槛更低。
我没有选用 Vue Press 这类工具，是因为它框架偏重，而我只需要简洁轻量化的静态博客工具。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;开发期间遇到最难解决的 bug 是什么？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;最难的就是新增评论区功能。最开始照搬网上教程接入评论组件之后，出现组件渲染异常、页面布局错乱，部分页面加载之后评论模块直接无法显示，折腾很久才调试完毕。
解决之后我将整个踩坑流程整理成学习笔记存放在笔记站点，方便之后复盘。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;调试问题的时候你的排查步骤&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;第一步先观察报错信息，查看浏览器控制台的报错；
第二步定位是布局样式问题还是组件加载的问题，先单独拆分评论组件进行测试；
第三步翻阅 Astro 官方文档、搜索相关问题；
多次尝试无果之后借助 AI 排查我代码当中疏漏；
最后一点点修改参数，反复刷新页面测试，直到功能正常。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;项目有哪些不足之处，如果现在重做你会怎么优化？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;现存缺点&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;很多功能只是照搬模板修改，对于 Astro 底层原理理解比较浅显；&lt;/li&gt;
&lt;li&gt;移动端适配做得比较粗糙；&lt;/li&gt;
&lt;li&gt;评论区功能较为基础，缺少消息提醒、评论分页；&lt;/li&gt;
&lt;li&gt;文件管理比较杂乱，没有严格拆分静态资源、配置文件。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;重做优化方案&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;整理项目目录结构，规范文件夹分类；&lt;/li&gt;
&lt;li&gt;完善移动端屏幕适配；&lt;/li&gt;
&lt;li&gt;升级评论模块；&lt;/li&gt;
&lt;li&gt;抽出空闲时间深入看懂模板源码，而不是单纯修改表层配置。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;有没有使用 Git 管理你的项目代码？日常怎么操作？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;有，全程依靠 GitHub 管理代码。
日常流程：本地修改代码之后 &lt;code&gt;git add&lt;/code&gt; 添加文件、&lt;code&gt;git commit&lt;/code&gt; 提交备注、&lt;code&gt;git push&lt;/code&gt; 推送至远程仓库；如果需要尝试新功能，后续我也可以新建分支进行试验，防止主干代码出错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;项目的数据从哪里来，怎么处理数据？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;站点所有内容都是我自己产出：游记、日常随笔、408 各科学习笔记。
我使用 Markdown 编写文档，Astro 框架能够直接解析 md 文件渲染成网页，我只需要做好文档分类，划分博客文件夹和笔记文件夹即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;在写代码的时候你会注重代码规范吗？平时怎么规范命名？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我有留意基础的代码规范。文件夹、文件名称全部采用易懂的英文名称；CSS 类名见名知意；配置项按照功能分区。
因为是个人项目，前期规范相对宽松，做完这个项目之后，我现在意识到命名规范的重要性，后续开发会更加留意。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;开发过程有没有参考外文文档？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Astro 拥有海外官网，同时配有国内中文版本文档。遇到难懂的配置时我优先浏览中文文档，部分中文资料讲解简略的时候，我便查阅原版外文官方文档寻找说明，也锻炼了我阅读技术文档的能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;做项目时卡很久没有思路，你会怎么办&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;首先反复查看官方文档，核对参数配置；接着上网搜索同类开发者遇到的同款问题；自己分段注释代码、缩小出错范围；长时间卡住之后就借助 AI 指出我的代码隐患；
问题解决之后，我会记下这次踩坑点，写到我的笔记网站，防止之后重复犯错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;有没有尝试过多版本迭代、新增功能？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;存在迭代。初始版本只是单纯的博客站点；后续复制框架、二次改造新增独立笔记站；后期又追加评论区功能，中间也多次调整页面样式、排版，持续进行微调优化。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你的项目可以解决什么实际需求？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;第一，承载我的旅行随笔、日常生活记录；&lt;/li&gt;
&lt;li&gt;第二，线上存放 408 的学习笔记，不受纸质书本约束，任何时候浏览器就能够查阅；&lt;/li&gt;
&lt;li&gt;第三，我可以将自己开发项目的踩坑心得保存进去，充当我的技术知识库。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;对比网上现成的程序，你的项目优势在哪&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;完全贴合我个人的使用需求，分为博客区和学习笔记区，是定制化的双站点结构，普通开源模板大多只具备单一博客功能；&lt;/li&gt;
&lt;li&gt;所有学习踩坑记录都沉淀在站内，属于我专属的知识库；&lt;/li&gt;
&lt;li&gt;在改造途中我没有直接照搬全套功能，按需删减不需要的插件，网站整体更加轻量化。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;做完项目之后你的技术上面获得哪些成长&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;入门熟悉 HTML、CSS、Markdown 语法，初次接触前端静态站点框架；&lt;/li&gt;
&lt;li&gt;熟练掌握 Git、GitHub 的基础版本控制操作；&lt;/li&gt;
&lt;li&gt;学会查阅中英文官方文档、独立排查前端页面 bug；&lt;/li&gt;
&lt;li&gt;养成了记录开发踩坑笔记的习惯；&lt;/li&gt;
&lt;li&gt;明白了搭建项目先理清自身需求，再挑选适配框架的思路。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;专属避坑提示&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;面试官如果继续深挖 Astro 原理、打包机制、前端渲染原理这类深度问题，直接使用固定话术：
&quot;我当时主要基于模板进行功能改造，框架底层原理目前还没有深入研究，如果顺利入职之后，我会借着岗位的前端项目系统深耕相关知识。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;五、前端 &amp;amp; 技术基础问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;HTML 和 HTML5 新增标签有哪些你清楚吗&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;普通 HTML 常用标签像 div、p、span、a、ul li 这些；
HTML5 新增了语义化标签，例如 header、footer、nav、section、article，相比普通 div，可以让标签具备语义，利于可读性和搜索引擎识别，其余新增标签我只做过简单了解。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;行内元素、块级元素区别&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;块级元素独占一行，可以设置宽高，例如 div、p；&lt;/li&gt;
&lt;li&gt;行内元素不会独占一行，横向排布，无法直接设置宽高，代表有 span、a；&lt;/li&gt;
&lt;li&gt;还有行内块元素，可以同行摆放并且自定义宽高，像 img。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CSS 盒模型是什么&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;盒模型决定元素占用的空间，从内向外依次是 content 内容、padding 内边距、border 边框、margin 外边距。
分为标准盒模型和 IE 怪异盒模型；标准模式宽高只代表 content，怪异模式宽高包含 content+padding+border。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Flex 简单属性你了解多少&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我清楚 flex 弹性布局，可以很方便完成页面元素排布。
常用父容器属性有 &lt;code&gt;display:flex&lt;/code&gt;、&lt;code&gt;justify‑content&lt;/code&gt; 主轴对齐、&lt;code&gt;align‑items&lt;/code&gt; 侧轴对齐；
子项了解 &lt;code&gt;flex‑grow&lt;/code&gt;、&lt;code&gt;flex‑shrink&lt;/code&gt;，现阶段只掌握这些最基础的用法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;var、let、const 的区别&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;var 存在变量提升，函数作用域，可以重复声明；&lt;/li&gt;
&lt;li&gt;let 和 const 具备块级作用域，不存在变量提升，不能够重复声明；&lt;/li&gt;
&lt;li&gt;let 可以修改变量的值；const 用来声明常量，基础数据类型不可修改。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JS 闭包是什么，简单举例子&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;闭包就是内层函数访问外层函数作用域当中的变量，外层函数执行结束之后，它的变量依旧可以被内层函数保留。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;function outer(){
    let num = 1;
    return function inner(){
        console.log(num)
    }
}
let fn = outer();
fn()
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;好处是可以保存私有变量；缺点是容易造成内存占用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;原型和原型链&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;每一个函数都会自带 prototype 原型对象；实例对象有 &lt;code&gt;__proto__&lt;/code&gt; 指向构造函数的原型。
原型链就是实例顺着 &lt;code&gt;__proto__&lt;/code&gt; 向上一层一层查找原型，构成的查找链条，访问属性的时候逐层向上搜索直到 null。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JS 异步有哪几种处理方式&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;回调函数、定时器、Promise、async await、生成器 generator，还有浏览器的事件监听。日常我主要了解回调以及 Promise。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Promise 的基础作用&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Promise 专门用来处理异步任务，可以解决回调地狱的问题；有 pending、resolved、rejected 三种状态，可以通过 then 链式调用处理异步成功和失败之后的逻辑。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;有没有接触过 Vue？学到哪一步了？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;最近刚开始入门前端，仅仅简单浏览过 Vue 基础概念，还没有深入搭建项目，实习之后我会抓紧系统学习。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;前端页面加载的全过程&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;首先 DNS 解析域名获取服务器 IP；之后建立 TCP 连接；浏览器发送 HTTP 请求；服务器响应并且返回 HTML 资源；浏览器解析 HTML，生成 DOM 树和 CSSOM 树，合成渲染树，接着布局、绘制页面，途中加载脚本、图片等资源。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;什么是跨域，简单说下成因&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;同源协议、域名、端口三者只要有一项不一样，就属于不同源；浏览器出于安全的同源策略，会阻止不同源之间发起请求，于是就出现跨域限制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;平时刷算法刷到什么程度、大概刷了多少题？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;目前 Leet Code 主要刷热题 100 当中的简单题目，现在已经完成十几道。我现阶段重点练习数组、哈希、链表这类基础题型，保证基础题能够手写出来，也是这次面试最主要准备的内容。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你最喜欢哪一类算法题型（数组、哈希、双指针等）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;我比较偏爱哈希表和双指针类型的题目，思路直观，适合处理数组查找、去重、比对元素这类场景，上手的时候相对顺手。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;手撕算法卡住的时候你一般怎么思考？&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;首先我会先用暴力解法实现一遍，先保证能够得出结果；之后观察暴力解法哪里存在重复运算，思考可不可以借助哈希、双指针降低时间复杂度；
如果长时间没有头绪，我会列举几组测试用例手动推演寻找规律；实在卡住之后我会向面试官说明我现在的卡点，寻求一点提示。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;前端答题通用兜底话术&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;一旦问到生命周期、webpack、节流防抖、深拷贝、组件通信这类进阶考点：
&quot;目前我只停留在前端基础阶段，这类进阶知识还没有仔细钻研，后续进入实习之后我依托项目代码进行学习。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;应试小贴士&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;所有回答点到即止，说完基础定义之后不要主动延伸；&lt;/li&gt;
&lt;li&gt;面试官继续深挖就启用兜底话术，委婉表明现阶段只是入门；&lt;/li&gt;
&lt;li&gt;把话题重心往你的算法刷题、独立项目上面引导。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>企业级网络架构与组网实验核心总结</title><link>https://github.com/posts/%E8%AE%A1%E7%BD%91%E7%BB%84%E7%BD%91%E5%AE%9E%E9%AA%8C%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://github.com/posts/%E8%AE%A1%E7%BD%91%E7%BB%84%E7%BD%91%E5%AE%9E%E9%AA%8C%E6%80%BB%E7%BB%93/</guid><description>基于 Cisco Packet Tracer 的组网实战总结</description><pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;计算机网络组网实验总结&lt;/h1&gt;
&lt;p&gt;在进行企业级网络拓扑的设计与部署时，合理选择设备型号、理清工程实施流程并熟练掌握 Cisco IOS 命令行指令，是保障网络系统高效收敛与稳定运行的基石。本文对 Packet Tracer 中的常用设备、传输介质、标准工程配置流程及核心指令体系进行系统性梳理。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、 常用模拟设备与传输介质分类&lt;/h2&gt;
&lt;h3&gt;1. 终端设备 (End Devices)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PC-PT&lt;/strong&gt;：标准用户终端主机，在实验中用于验证客户端的 IP 获取（DHCP/静态）及端到端连通性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server-PT&lt;/strong&gt;：通用服务器设备，支持在软件内一键开启 HTTP（Web）、DNS（域名解析）、DHCP（地址动态分配）、EMAIL 以及 FTP 等应用层服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 交换设备 (Switching Devices)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;二层交换机（如 2960-24TT）&lt;/strong&gt;：属于数据链路层设备，用于局域网内部终端的接入，支持 VLAN 划分、Access/Trunk 链路类型配置，不支持三层 IP 路由。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;三层交换机（如 3560-24PS）&lt;/strong&gt;：具备网络层路由功能的多层交换机。在企业网中通常部署于核心层（Core Layer），支持启用 &lt;code&gt;ip routing&lt;/code&gt; 激活路由功能，配置虚拟接口（SVI）充当各 VLAN 的默认网关，并可直接本地部署 DHCP 地址池。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 路由设备 (Routing Devices)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cisco 2811 / 1941 路由器&lt;/strong&gt;：网络层核心寻址设备。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内置接口&lt;/strong&gt;：2811 路由器自带 2 个 FastEthernet 接口；1941 路由器自带 2 个 GigabitEthernet 接口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;硬件扩展&lt;/strong&gt;：在广域网连接中，若需采用 PPP 或 HDLC 封装，必须在物理面板（关闭电源状态下）加装 &lt;strong&gt;&lt;code&gt;WIC-2T&lt;/code&gt;（双端口高速串行 WAN 接口卡）&lt;/strong&gt; 模块，从而提供物理 &lt;code&gt;Serial&lt;/code&gt; 串口。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 传输介质 (Cables)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直通线 (Copper Straight-Through)&lt;/strong&gt;：用于连接异种设备（如 PC 到交换机，交换机到路由器）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;交叉线 (Copper Cross-Over)&lt;/strong&gt;：用于连接同种设备（如交换机到交换机，路由器到路由器，PC 直连路由器）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;串口线 (Serial DCE/DTE)&lt;/strong&gt;：用于广域网串行链路互联。DCE（数据通信设备）端在模拟器中带有时钟标志，需在物理接口下配置 &lt;code&gt;clock rate 64000&lt;/code&gt; 提供同步时钟频率。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;二、 组网实验的标准工程实施流程&lt;/h2&gt;
&lt;p&gt;为了规避配置冲突并提高排错（Troubleshooting）效率，网络工程部署推荐遵循以下自底向上的五个阶段：&lt;/p&gt;
&lt;p&gt;【拓扑设计与加装模块】 → 【局域网 VLAN 与网关】 → 【广域网链路与封装】 → 【内/外网路由收敛】 → 【出口 NAT 与安全控制】&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;物理拓扑设计与硬件准备&lt;/strong&gt;：摆放设备并进行物理连线。若使用串行链路，需在关机状态下为相应路由器加装 &lt;code&gt;WIC-2T&lt;/code&gt; 模块。默认情况下，由于路由器物理接口处于 &lt;code&gt;shutdown&lt;/code&gt; 状态，整网连线指示灯呈现红色。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;局域网（LAN）基础建设&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;为需要静态指定 IP 的终端（服务器、特定部门主机）配置 IP、掩码和网关。&lt;/li&gt;
&lt;li&gt;在二层交换机上划分业务 VLAN，将 Access 端口划入对应 VLAN，并配置级联线为 Trunk 模式。&lt;/li&gt;
&lt;li&gt;在三层交换机上配置各 VLAN 的 SVI 虚拟接口 IP 地址，并开启路由功能。配置 DHCP 服务器排除范围与地址池，实现部分客户端的主机自动获取 IP。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;广域网（WAN）物理激活与封装&lt;/strong&gt;：为各路由器物理接口配置 IP 地址，执行 &lt;code&gt;no shutdown&lt;/code&gt; 激活接口，使拓扑指示灯转为绿色。根据需求，在串行链路上配置 &lt;code&gt;encapsulation ppp&lt;/code&gt; 及 CHAP/PAP 安全验证。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路由收敛（静态与动态）&lt;/strong&gt;：在内网核心部署静态/默认路由指向出口；在公网（ISP 核心）运行 OSPF 等动态路由协议并进行静态路由重分布，实现全网路由表的收敛与可达。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全控制、NAT 转换与持久化备份&lt;/strong&gt;：配置边界路由器的 NAT 静态映射与 PAT 端口复用；配置 ACL 访问控制列表和 VTY 远程管理密码。验证无误后，执行 &lt;code&gt;write memory&lt;/code&gt; 备份运行配置至 NVRAM 中。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;三、 Cisco IOS 核心 CLI 命令速查&lt;/h2&gt;
&lt;h3&gt;1. 模式切换命令&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;enable&lt;/code&gt;：由用户模式进入特权模式，提示符由 &lt;code&gt;&amp;gt;&lt;/code&gt; 变为 &lt;code&gt;#&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;configure terminal&lt;/code&gt;（简写 &lt;code&gt;conf t&lt;/code&gt;）：进入全局配置模式，提示符变为 &lt;code&gt;(config)#&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;interface [接口名称]&lt;/code&gt;（简写 &lt;code&gt;int [接口名称]&lt;/code&gt;）：进入特定接口配置模式，提示符变为 &lt;code&gt;(config-if)#&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 接口基本配置命令&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ip address [IP地址] [子网掩码]&lt;/code&gt;：为指定物理接口或虚拟接口配置网络层 IP 地址。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no shutdown&lt;/code&gt;（简写 &lt;code&gt;no shut&lt;/code&gt;）：激活并启用当前接口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no switchport&lt;/code&gt;：在三层交换机物理接口下使用，用于将二层交换端口升级为三层路由接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 数据保存与恢复&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;write memory&lt;/code&gt;（简写 &lt;code&gt;wr&lt;/code&gt;）：在特权模式下使用，将当前正在运行的配置（Running-config）保存至启动配置（Startup-config）中，避免断电丢失。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no ip domain-lookup&lt;/code&gt;：全局配置模式命令，关闭思科默认的域名解析服务，防止输入错误命令时系统因尝试广播解析而导致控制台卡死。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 诊断与状态查询（特权模式下，配置模式下需在命令前加 &lt;code&gt;do&lt;/code&gt;）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;show ip interface brief&lt;/code&gt;（简写 &lt;code&gt;sh ip int brief&lt;/code&gt;）：快速查看所有物理与虚拟接口的 IP、物理状态（Status）和协议状态（Protocol）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;show ip route&lt;/code&gt;（简写 &lt;code&gt;sh ip route&lt;/code&gt;）：查看当前路由表。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;show ip ospf neighbor&lt;/code&gt;：查看当前路由器的 OSPF 邻居关系表，确认邻接状态是否达到 &lt;code&gt;FULL&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;show ip dhcp binding&lt;/code&gt;：在 DHCP 服务器上运行，查看已动态分配的 IP 地址与客户端 MAC 地址的绑定信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;在网络工程的设计与调试中，理解数据包在介质中的传输机理、各类网络协议的协同交互以及路由器在网络层的转发逻辑，是分析和解决网络故障的理论核心。以下对 Packet Tracer 仿真平台下的协议工作原理及路由器的底层转发流程进行深度剖析。&lt;/p&gt;
&lt;h2&gt;四、 Packet Tracer 仿真实现与 ICMP（Ping）工作机制&lt;/h2&gt;
&lt;h3&gt;1. 仿真模式（Simulation Mode）的底层机理&lt;/h3&gt;
&lt;p&gt;Packet Tracer 采用的是&lt;strong&gt;离散事件仿真（Discrete Event Simulation）&lt;strong&gt;模型。在仿真模式下，软件将网络中传输的数据抽象为&lt;/strong&gt;协议数据单元（PDU，Protocol Data Unit）&lt;/strong&gt;，并以“虚拟信封”的可视化形式展示。当用户发起网络请求时，仿真引擎会模拟数据在物理介质上的电信号或光信号传输，并在每个网络节点（如主机、交换机、路由器）上，自底向上严格按照 OSI 七层模型或 TCP/IP 四层架构进行数据的解封装与重新封装。通过这种方式，用户可以实时观察到数据包在每一层（Layer 1 到 Layer 7）的报头信息变化（Inbound/Outbound PDU Details）。&lt;/p&gt;
&lt;h3&gt;2. ICMP（Ping）协议的交互流程&lt;/h3&gt;
&lt;p&gt;Ping 是基于**网络控制报文协议（ICMP，Internet Control Message Protocol）**实现的诊断工具，用于测试网络目的主机的可达性。其底层工作流程如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;构建请求&lt;/strong&gt;：源主机（如 PC_A）在应用层发起 Ping 命令后，网络层会构建一个 &lt;strong&gt;ICMP Echo Request（回显请求报文，Type 8, Code 0）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;层层封装&lt;/strong&gt;：该 ICMP 报文作为数据载荷被送往网络层，封装上源 IP 和目的 IP 报头；接着送往数据链路层，封装成以太网帧（Ethernet II）。若此时源主机本地 ARP 缓存表中没有网关的 MAC 地址，则会暂时挂起 Ping 报文，先发送 ARP 广播请求解析网关 MAC 地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中途转发&lt;/strong&gt;：数据帧通过物理介质发送，中途经过交换机进行二层 MAC 地址转发，经过路由器进行三层 IP 路由寻址，直至到达目的主机。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回显响应&lt;/strong&gt;：目的主机接收到该数据帧后，通过 MAC 校验和 IP 校验自底向上解封装。当识别出这是一个 ICMP Echo Request 时，目的主机会在本地构建一个 &lt;strong&gt;ICMP Echo Reply（回显应答报文，Type 0, Code 0）&lt;/strong&gt;，并将源 IP 与目的 IP 对调，重新封装后原路返回给源主机。源主机收到该应答后，计算时间差并输出 Ping 成功的信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;五、 企业级多协议的协同运作机制&lt;/h2&gt;
&lt;p&gt;在复杂的用户上网场景中，从获取 IP 到成功访问网页，底层经历了多种网络协议的接力与协同配合：&lt;/p&gt;
&lt;p&gt;【DHCP 动态分配】 → 【ARP 地址解析】 → 【OSPF 拓扑收敛】 → 【NAT/PAT 出口转换】&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;DHCP 协议（动态主机配置）&lt;/strong&gt;：客户端主机接入网络后，通过发送 DHCP Discover 广播报文启动业务。三层核心交换机（或专用服务器）作为 DHCP 服务器，通过 Offer、Request、Ack 报文交互，为客户端动态分配 IP 地址、子网掩码、默认网关和 DNS 服务器地址。此步骤为后续的网络通信奠定了三层身份基础。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ARP 协议（地址解析协议）&lt;/strong&gt;：当主机需要向网关或同一网段的其他主机发送数据，但只知道其 IP 地址而不知道其 MAC 地址时，会发送 ARP 请求广播。对端主机回应 ARP 单播应答。ARP 协议无缝连接了网络层与数据链路层，是局域网内部数据封装与物理发送的先决条件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OSPF 协议（开放最短路径优先）&lt;/strong&gt;：在运营商骨干网（WAN）内部，多台路由器通过周期性发送 Hello 报文建立并维护邻居关系，通过交换 LSA（链路状态通告）同步链路状态数据库（LSDB）。随后，各路由器运行 SPF（最短路径树）算法，自动计算出无环的最优路由条目并加载到路由表中，实现了公网路由的动态、快速收敛。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT/PAT 协议（网络地址转换）&lt;/strong&gt;：当私网（LAN）内部的 PC 访问外网时，出口边界路由器会拦截该私网数据包，将其私有源 IP 转换为公网出口 IP（若是 PAT，则会同时修改并映射 TCP/UDP 的源端口号），并在本地维护一张 NAT 转换表（Translation Table）。当外网回包时，路由器根据该表项反向转换目的 IP 和端口，从而实现了私网 IP 对公网的合法、安全访问。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;六、 路由器三层数据包转发的底层工作流程&lt;/h2&gt;
&lt;p&gt;当一个 IP 数据包到达路由器的物理接口时，路由器内部会经历一套严密的“接收、解封装、查表、重新封装、发送”的转发决策流程：&lt;/p&gt;
&lt;h3&gt;1. 物理层与数据链路层接收&lt;/h3&gt;
&lt;p&gt;路由器的物理接口接收到物理介质传来的电信号或光信号，并将其转化为二进制比特流。数据链路层接收后，首先读取数据帧的帧头，检查&lt;strong&gt;目的 MAC 地址&lt;/strong&gt;是否为该路由器接收接口的 MAC 地址（或者是广播/组播 MAC）。如果是，则计算循环冗余校验码（FCS）进行帧校验。校验无误后，路由器会将二层帧头（MAC 报头）和帧尾剥离，提取出网络层的 &lt;strong&gt;IP 数据包&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;2. 网络层首部校验与 TTL 处理&lt;/h3&gt;
&lt;p&gt;路由器网络层接收到 IP 数据包后，首先检查 IP 报头中的版本号，并计算首部校验和（Header Checksum）以确保数据包在传输过程中未被损坏。
接着，路由器会将 IP 首部中的**生存时间（TTL，Time to Live）**值减 1。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 TTL 减 1 后等于 0，路由器会直接丢弃该数据包，并向源 IP 地址发送一个 ICMP Time Exceeded（时间超时）报文。&lt;/li&gt;
&lt;li&gt;如果 TTL 大于 0，路由器则继续进行转发流程。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 最长匹配路由表检索（Longest Match Lookup）&lt;/h3&gt;
&lt;p&gt;路由器读取 IP 报头中的&lt;strong&gt;目的 IP 地址&lt;/strong&gt;，并在本地路由表中进行检索。检索遵循&lt;strong&gt;最长前缀匹配原则&lt;/strong&gt;（即子网掩码位数越长、网段越精确的条目越优先匹配）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直连网段匹配&lt;/strong&gt;：如果目的 IP 属于路由器的某个直连网段，路由器会直接跳转到数据链路层，准备通过该直连物理接口发送。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非直连网段匹配&lt;/strong&gt;：如果匹配到静态路由、动态路由（OSPF）或默认路由条目，路由器会提取出该条目指定的&lt;strong&gt;下一跳（Next-hop）IP 地址&lt;/strong&gt;和&lt;strong&gt;本地出接口（Outgoing Interface）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;未匹配成功&lt;/strong&gt;：如果路由表中没有任何条目能够匹配该目的 IP，且路由器上未配置默认路由（Gateway of last resort），路由器会直接丢弃该包，并向源 IP 发送 ICMP Destination Unreachable（目的不可达）报文。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 下一跳 MAC 地址解析与二层重新封装&lt;/h3&gt;
&lt;p&gt;确定了出接口与下一跳 IP 后，路由器需要将数据包重新封装成二层数据帧。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ARP 缓存表查询&lt;/strong&gt;：路由器会在本地 ARP 缓存表中查询下一跳 IP 地址对应的 MAC 地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ARP 广播（未命中时）&lt;/strong&gt;：如果在 ARP 表中未找到，路由器会从出接口发送一个 ARP 广播请求，解析出下一跳的物理 MAC 地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重新封装&lt;/strong&gt;：获取到下一跳 MAC 后，路由器会构建全新的二层帧头。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;源 MAC 地址&lt;/strong&gt;修改为：该路由器&lt;strong&gt;出接口&lt;/strong&gt;的物理 MAC 地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目的 MAC 地址&lt;/strong&gt;修改为：&lt;strong&gt;下一跳&lt;/strong&gt;设备的物理 MAC 地址（如果是广域网串行链路，则直接封装为 PPP 帧头）。&lt;/li&gt;
&lt;li&gt;IP 报头中的源 IP 和目的 IP 在此过程中&lt;strong&gt;保持完全不变&lt;/strong&gt;（除非在边界运行了 NAT 转换）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 物理层发送&lt;/h3&gt;
&lt;p&gt;新封装好的数据帧被传送给物理层，物理层根据物理接口的技术规范（如以太网双绞线或串行光纤），将数据帧调制为相应的电平信号或光信号，发送到物理介质上，最终传输至下一跳网络设备。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;七、补充&lt;/h2&gt;
&lt;h3&gt;1. 补充：常见故障排查思路（Troubleshooting）&lt;/h3&gt;
&lt;p&gt;这是实验报告和面试中最具“含金量”的部分。你可以增加一个 &lt;strong&gt;“实验常见‘翻车’点”&lt;/strong&gt; 模块：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;物理层：&lt;/strong&gt; 为什么连线是红色的？（设备关机、未执行 &lt;code&gt;no shut&lt;/code&gt;、连接了错误的接口、广域网线缆选错 DTE/DCE）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VLAN/链路：&lt;/strong&gt; 为什么网关 Ping 通了但跨网段不通？（由于级联口忘了配置为 &lt;code&gt;Trunk&lt;/code&gt;，导致带有 VLAN Tag 的数据包被二层交换机丢弃）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路由层：&lt;/strong&gt; 路由表里有条目但依然不通？（检查是否忽略了“往返路由”——数据包能过去，但目标网段没有回来的路由）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT 匹配：&lt;/strong&gt; NAT 配置了但上不了网？（检查 &lt;code&gt;ip nat inside/outside&lt;/code&gt; 接口方向是否挂反）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 补充：经典实验场景架构&lt;/h3&gt;
&lt;p&gt;虽然你提到了流程，但可以具体点出两个最经典的组网模型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;单臂路由 (Router-on-a-Stick)：&lt;/strong&gt; 如果实验中没有三层交换机，如何利用路由器的一个物理口加上子接口（Sub-interface）配合 &lt;code&gt;dot1q&lt;/code&gt; 封装来实现跨 VLAN 通信。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;三层交换核心架构：&lt;/strong&gt; 强调“网关下沉”到三层交换机（MSW），而不是在路由器上，这是现代企业网的主流架构。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 补充：安全与管理（进阶配置）&lt;/h3&gt;
&lt;p&gt;让你的组网显得更真实（企业级）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;远程管理 (VTY)：&lt;/strong&gt; 补充 &lt;code&gt;line vty 0 4&lt;/code&gt; 的配置，设置密码和 &lt;code&gt;login&lt;/code&gt;，说明如何从 PC 远程 Telnet 到交换机。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ACL 匹配逻辑：&lt;/strong&gt; 补充 ACL 的“末尾隐含拒绝（Deny all）”原则，以及 Standard ACL（靠近目的地）与 Extended ACL（靠近源）的部署位置差异。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 格式优化：使用 Astro 的特性&lt;/h3&gt;
&lt;p&gt;既然你用的是 Astro Pure 主题，可以利用一些 Markdown 增强功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;使用 Mermaid 流程图：&lt;/strong&gt; (如果你的主题支持) 用代码画一个简单的三层架构图或转发流程图。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 Callout (警告/提示框)：&lt;/strong&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; 在三层交换机上配置完 VLAN 后，别忘了输入 &lt;code&gt;ip routing&lt;/code&gt;，否则它只是个“贵一点的二层交换机”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码高亮优化：&lt;/strong&gt; 确保所有的 CLI 指令都在代码块中，并标注为 &lt;code&gt;bash&lt;/code&gt; 或 &lt;code&gt;cisco&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 补充：实验反思（个人思考）&lt;/h3&gt;
&lt;p&gt;作为博文，增加一小段个人体会能提升文章深度：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;仿真与现实的差距：&lt;/strong&gt; 提到 Packet Tracer 毕竟是模拟器，很多高级特性（如复杂的 BGP 策略或真正的硬件报错）无法复现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协议开销的理解：&lt;/strong&gt; 通过仿真模式观察到 ARP 报文在 Ping 之前“抢占”总线的过程，加深了对“无协议不网络”的理解。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>深入理解操作系统 学习笔记</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F--%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F--%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><description>深入理解操作系统 笔记汇总</description><pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;第一章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第一章的主要内容是 : 操作系统的一些知识&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;操作系统是什么？&lt;/h3&gt;
&lt;p&gt;用户角度：操作系统是一个控制软件&lt;/p&gt;
&lt;p&gt;&amp;lt;!--more--&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;管理应用程序&lt;/li&gt;
&lt;li&gt;为应用程序提供服务&lt;/li&gt;
&lt;li&gt;杀死应用程序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;程序角度：操作系统是资源管理器&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;管理外设、分配资源&lt;/li&gt;
&lt;li&gt;抽象
&lt;ul&gt;
&lt;li&gt;将CPU抽象成进程&lt;/li&gt;
&lt;li&gt;将磁盘抽象成文件&lt;/li&gt;
&lt;li&gt;将内存抽象成地址空间&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;操作系统层次&lt;/h3&gt;
&lt;p&gt;位于硬件之上，应用程序之下。&lt;/p&gt;
&lt;h3&gt;操作系统的界面和内核&lt;/h3&gt;
&lt;p&gt;Linux Windows Android 的界面属于外壳(Shell) ，而不是内核(kernel)。操作系统研究的是内核，处于Shell之下。&lt;/p&gt;
&lt;h3&gt;操作系统内部组件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CPU调度器&lt;/li&gt;
&lt;li&gt;物理内存管理&lt;/li&gt;
&lt;li&gt;虚拟内存管理&lt;/li&gt;
&lt;li&gt;文件系统管理&lt;/li&gt;
&lt;li&gt;中断处理与设备驱动&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;操作系统特征&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;并发
&lt;ul&gt;
&lt;li&gt;一段时间内运行多个进程（并行 : 一个时间点运行多个进程，一般要求有多个CPU)&lt;/li&gt;
&lt;li&gt;需要OS管理和调度&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;共享
&lt;ul&gt;
&lt;li&gt;“同时”共享&lt;/li&gt;
&lt;li&gt;互斥共享&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;虚拟
&lt;ul&gt;
&lt;li&gt;让每一个用户觉得的有一个计算机专门为他服务&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;异步
&lt;ul&gt;
&lt;li&gt;程序是走走停停，而不是一直运行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;第二章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第二章的主要内容是 : 操作系统的异常/中断/系统调用&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;探究计算机启动过程&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;作用解析&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Disk : 存放OS和Bootloader&lt;/p&gt;
&lt;p&gt;BOIS : 基于I/O处理系统&lt;/p&gt;
&lt;p&gt;Bootloader : 加载OS，将OS放入内存&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Disk&lt;/p&gt;
&lt;p&gt;|___ OS&lt;/p&gt;
&lt;p&gt;|___ Bootloader&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;开机流程&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;BIOS&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开机后，寻找显卡和执行BIOS (此时, &lt;code&gt;CS : IP = 0xF000 : 0xFFF0&lt;/code&gt;,  CS/IP 两个寄存器)&lt;/li&gt;
&lt;li&gt;将Bootloader从磁盘的引导扇区加载到0x7C00 (Bootloader一共占用512M字节的内存)&lt;/li&gt;
&lt;li&gt;跳转到 &lt;code&gt;CS : IP = 0x0000 : 0x7C00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bootloader&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将操作系统的代码和数据从硬盘加载到内存中&lt;/li&gt;
&lt;li&gt;跳转到操作系统的起始地址&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;中断、异常、系统调用&lt;/h2&gt;
&lt;h3&gt;中断&lt;/h3&gt;
&lt;p&gt;中断来源于外设，来自不同的硬件设备的计时器和网络的中断。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;中断流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;硬件：设置中断标记（CPU初始化）&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将内部、外部事件设置中断标记&lt;/li&gt;
&lt;li&gt;中断事件的ID&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;软件&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;保存当前处理状态（寄存器之类的一些数据）&lt;/li&gt;
&lt;li&gt;根据中断事件的ID跳转到中断服务程序，中断服务程序处理&lt;/li&gt;
&lt;li&gt;清楚中断标记&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;异常&lt;/h3&gt;
&lt;p&gt;异常来源于不良的应用程序，非法指令或者其他坏的处理状态（如：内存出错）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;异常处理流程&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;保存现场&lt;/li&gt;
&lt;li&gt;异常处理
&lt;ol&gt;
&lt;li&gt;杀死了产生异常的程序&lt;/li&gt;
&lt;li&gt;重新执行异常指令&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;恢复现场&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;系统调用&lt;/h3&gt;
&lt;p&gt;系统调用来源于应用程序，应用程序主动向操作系统发出服务请求。程序访问主要是通过高层次的API，而不是直接调用系统调用函数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;APIs&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Win32 API 用于Windows&lt;/li&gt;
&lt;li&gt;POSIX API 用于 POSIX-based systems（包括UNIX，LINUX，Mac OS X）&lt;/li&gt;
&lt;li&gt;Java API 用于JAVA虚拟机&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通常情况下，每个系统调用有对应的序号
&lt;ul&gt;
&lt;li&gt;系统调用接口根据这些序号来维护表的索引&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;系统调用接口调用内核态中预期的系统调用
&lt;ul&gt;
&lt;li&gt;并返回系统调用的状态和其他任何返回值&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用户不需要知道系统调用是如何实现的
&lt;ul&gt;
&lt;li&gt;只需要获取API和了解操作系统将什么作为返回结果&lt;/li&gt;
&lt;li&gt;操作系统接口的细节大部分都隐藏在API中&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;用户态&lt;/strong&gt;：操作系统运行中，CPU处于的特权级别，不能直接执行特权指令&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;内核态&lt;/strong&gt;：操作系统运行中，CPU处于的特权级别，可以执行任何一条指令&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;系统调用&lt;/strong&gt;：触发CPU从用户态到内核态的转换，切换程序和内核的堆栈，需要一定的开销&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;跨越操作系统边界的开销（值得的且必须的，保证了操作系统的安全性）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在执行时间上的开销超过程序调用&lt;/li&gt;
&lt;li&gt;开销：
&lt;ul&gt;
&lt;li&gt;建立中断、异常、系统调用号与对应服务例程映射关系的初始化开销&lt;/li&gt;
&lt;li&gt;建立内核堆栈&lt;/li&gt;
&lt;li&gt;验证参数&lt;/li&gt;
&lt;li&gt;内核态映射到用户态的地址空间（更新页面映射权限）&lt;/li&gt;
&lt;li&gt;内核态独立地址空间（TLB）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;区别&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;源头&lt;/th&gt;
&lt;th&gt;处理时间&lt;/th&gt;
&lt;th&gt;响应&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;中断&lt;/td&gt;
&lt;td&gt;外设&lt;/td&gt;
&lt;td&gt;异步&lt;/td&gt;
&lt;td&gt;持续，对应用程序透明&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;异常&lt;/td&gt;
&lt;td&gt;应用程序意向不到的行为&lt;/td&gt;
&lt;td&gt;同步&lt;/td&gt;
&lt;td&gt;杀死或重新执行指令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;系统调用&lt;/td&gt;
&lt;td&gt;应用程序请求系统提供服务&lt;/td&gt;
&lt;td&gt;同步或异步&lt;/td&gt;
&lt;td&gt;等待和持续&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;异步：应用程序不知道什么时候会发生中断&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;同步：执行到某一条指令一定会发生该事件&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;为什么应用程序不能直接访问硬件？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在计算机运行时，内核是被信任的第三方&lt;/li&gt;
&lt;li&gt;只有内核可以执行特权指令&lt;/li&gt;
&lt;li&gt;为了方便应用程序&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;第三章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第三章的主要内容是 : 操作系统的物理内存管理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;计算机体系结构&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;CPU&lt;/li&gt;
&lt;li&gt;内存&lt;/li&gt;
&lt;li&gt;I/O&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;内存分层体系&lt;/h2&gt;
&lt;h3&gt;内存分层体系&lt;/h3&gt;
&lt;p&gt;运行内存(主存) / 磁盘(虚拟内存). 主存是在运行程序时所需要保存的数据空间，而磁盘是用于持久化数据保存的数据空间.&lt;/p&gt;
&lt;p&gt;CPU可以访问的内存包括两大类 : 寄存器 / cache(L1缓存 / L2缓存)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;层次&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;微处理器(CPU访问)&lt;/p&gt;
&lt;p&gt;|___CPU寄存器 / L1缓存&lt;/p&gt;
&lt;p&gt;|___L2缓存&lt;/p&gt;
&lt;p&gt;主存(程序访问)&lt;/p&gt;
&lt;p&gt;磁盘(程序访问)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从CPU寄存器到磁盘，读写速度不断降低，单位成本不断降低，大小不断增大。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;内存管理目标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;抽象：逻辑地址空间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保护：独立地址空间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;共享：访问相同内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟：更多的地址空间&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;内存管理方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;程序重定位&lt;/li&gt;
&lt;li&gt;分段&lt;/li&gt;
&lt;li&gt;分页&lt;/li&gt;
&lt;li&gt;虚拟内存&lt;/li&gt;
&lt;li&gt;按需分页虚拟内存&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;实现高度依赖于硬件, 其中内存管理单元(MMU)负责处理CPU的内存访问请求&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;地址空间&lt;/h2&gt;
&lt;p&gt;地址空间的定义&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;物理地址空间 —— 硬件支持的地址空间( address : [0, Max_sys] )&lt;/li&gt;
&lt;li&gt;逻辑地址空间 —— 一个运行在程序所拥有的的内存范围( address : [0, Max_prog] )&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;连续内存分配&lt;/h2&gt;
&lt;h3&gt;内存碎片问题&lt;/h3&gt;
&lt;p&gt;内存碎片问题指的是空闲的内存无法被利用&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;外部碎片 : 分配单元间的未使用内存&lt;/li&gt;
&lt;li&gt;内部碎片 : 分配单元内的未使用内存&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;分区的动态分配&lt;/h3&gt;
&lt;p&gt;分区的动态分配方式有以下三种 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一匹配分配 : 在内存中找到第一个比需求大的空闲块, 分配给应用程序&lt;/li&gt;
&lt;li&gt;最优适配分配 : 在内存中找到最小的空闲块, 分配给应用程序&lt;/li&gt;
&lt;li&gt;最差适配分配 : 在内存中找到最大的空闲块, 分配给应用程序&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;分配方式的区别&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;分配方式&lt;/th&gt;
&lt;th&gt;第一匹配分配&lt;/th&gt;
&lt;th&gt;最优适配分配&lt;/th&gt;
&lt;th&gt;最差适配分配&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;分配方式实现需求&lt;/td&gt;
&lt;td&gt;1. 按地址排序的空闲块列表&amp;lt;br /&amp;gt;2. 分配需要寻找一个合适的分区&amp;lt;br /&amp;gt;3. 重分配需要检查是否可以合并相邻空闲分区&lt;/td&gt;
&lt;td&gt;1. 按尺寸排序的空闲块列表&amp;lt;br /&amp;gt;2. 分配需要寻找一个合适的分区&amp;lt;br /&amp;gt;3. 重分配需要检查是否可以合并相邻空闲分区&lt;/td&gt;
&lt;td&gt;1. 按尺寸排序的空闲块列表&amp;lt;br /&amp;gt;2. 分配最大的分区&amp;lt;br /&amp;gt;3. 重分配需要检查是否可以合并相邻空闲分区&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;优势&lt;/td&gt;
&lt;td&gt;简单 / 易于产生更大空闲块&lt;/td&gt;
&lt;td&gt;比较简单 / 大部分分配是小尺寸时高效&lt;/td&gt;
&lt;td&gt;分配很快 / 大部分分配是中尺寸时高效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;劣势&lt;/td&gt;
&lt;td&gt;产生外部碎片 / 不确定性&lt;/td&gt;
&lt;td&gt;产生外部碎片 / 重分配慢 / 产生很多没用的微小碎片&lt;/td&gt;
&lt;td&gt;产生外部碎片 / 重分配慢 / 易于破碎大的空闲块以致大分区无法被分配&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;三种分配方式并无优劣之分，因为我们无法判断内存请求的大小&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;碎片整理方法&lt;/h2&gt;
&lt;p&gt;可以看到的是，三种分区动态分配的方式都会产生外部碎片，因此我们可以对碎片进行一定的整理来解决碎片问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;压缩式碎片整理&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;重置程序以合并碎片&lt;/li&gt;
&lt;li&gt;要求所有程序是动态可重置的&lt;/li&gt;
&lt;li&gt;问题 :
&lt;ul&gt;
&lt;li&gt;何时重置 ? (在程序处于等待状态时才可以重置)&lt;/li&gt;
&lt;li&gt;需要考虑内存拷贝的开销&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;交换式碎片整理&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;运行程序需要更多的内存时，抢占等待的程序并且回收它们的内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;问题 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;哪些程序应该被回收 ?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;情况 :&lt;/p&gt;
&lt;p&gt;运行中 : P3&lt;/p&gt;
&lt;p&gt;等待中 : P1 P2 P4&lt;/p&gt;
&lt;p&gt;内存分布 -&amp;gt; 主存 : OS / P1 / P3 / P2 / P4 磁盘 : 空&lt;/p&gt;
&lt;p&gt;当P3程序需要更大的内存时 -&amp;gt;&lt;/p&gt;
&lt;p&gt;内存分布 -&amp;gt; 主存 : OS / P1 / P3 / P2         磁盘 : P4&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;第四章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第四章的主要内容是：操作系统的非连续内存分配&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第三章介绍的是连续内存管理, 即 : 操作系统加载到内存以及程序加载到内存中时, 分配一块连续的空闲(内存)块. 但是容易出现碎片问题, 这一章介绍的非连续内存分配可以有效的减少碎片的出现.&lt;/p&gt;
&lt;h2&gt;非连续内存分配的必要性&lt;/h2&gt;
&lt;h3&gt;连续内存分配的缺点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;分配给一个程序的物理内存是连续的&lt;/li&gt;
&lt;li&gt;内存利用率低&lt;/li&gt;
&lt;li&gt;有外碎片 / 内碎片的问题&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;非连续内存分配的优点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;一个程序的物理地址空间是非连续的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;更好的内存利用和管理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;允许共享代码与数据(共享库等...)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持动态加载和动态链接&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;非连续内存分配的缺点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;建立虚拟地址和物理地址的转换难度大&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;软件方案&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;硬件方案(采用硬件方案) : 分段 / 分页&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;非连续内存分配&lt;/h2&gt;
&lt;h3&gt;分段(Segmentation)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;段 :&lt;/strong&gt; 在程序中会有来自不同文件的函数 ; 在程序执行时, 不同的数据也有不同的字段, 比如 : 堆 / 栈 / .bss / .data 等&lt;/p&gt;
&lt;p&gt;**分段 : ** 更好的分离和共享&lt;/p&gt;
&lt;p&gt;程序的分段地址空间如下图所示 :&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://camo.githubusercontent.com/9db208b68e844759210535e7ec1929186e60dae2153ac930d2e662a782ca7bd1/68747470733a2f2f692e6c6f6c692e6e65742f323032302f31312f31312f4e416e7a484b6a3574454d343136682e6a7067&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分段寻址方案&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;逻辑地址空间连续，但是物理地址空间不连续，使用映射机制进行关联.&lt;/p&gt;
&lt;p&gt;一个段 : 一个内存&quot;块&quot;&lt;/p&gt;
&lt;p&gt;程序访问内存地址需要 : 一个二维的二元组(s, addr) → (段号, 地址)&lt;/p&gt;
&lt;p&gt;操作系统维护一张段表, 存储(段号, 物理地址中的起始地址, 长度限制)&lt;/p&gt;
&lt;p&gt;物理地址 : 段表中的起始地址 + 二元组中的偏移地址&lt;/p&gt;
&lt;h3&gt;分页(Paging)&lt;/h3&gt;
&lt;h4&gt;分页地址空间&lt;/h4&gt;
&lt;p&gt;划分物理内存至固定大小的帧(Frame)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大小是2的幂, 512 / 4096 / 8192&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;划分逻辑地址空间至相同大小的页(Page)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大小是2的幂, 512 / 4096 / 8192&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建立方案 → 转换逻辑地址为物理地址(pages to frames)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;页表&lt;/li&gt;
&lt;li&gt;MMU / TLB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;帧(Frame)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;物理内存被分割为大小相等的帧. 一个内存物理地址是一个二元组(f, o) → (帧号, 帧内偏移)&lt;/p&gt;
&lt;p&gt;帧号 : F位, 共有2^F个帧&lt;/p&gt;
&lt;p&gt;帧内偏移 : S位, 每帧有2^S个字节&lt;/p&gt;
&lt;p&gt;物理地址 = 2^S * f + o&lt;/p&gt;
&lt;p&gt;(例子 : 16-bit地址空间, 9-bit(512 byte) 大小的页帧 物理地址 = (3,6) 物理地址 = 2^9 * 3 + 6 = 1542)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;分页和分段的最大区别 : 这里的 S 是一个固定的数, 而分段中的长度限制不定&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;页(Page)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个程序的逻辑地址空间被划分为大小相等的页. 页内偏移的大小 = 帧内偏移的大小 页号大小 &amp;lt;&amp;gt; 帧号大小&lt;/p&gt;
&lt;p&gt;一个逻辑地址是一个二元组(p, o) → (页号, 页内偏移)&lt;/p&gt;
&lt;p&gt;页号 : P位, 共有2^P个页&lt;/p&gt;
&lt;p&gt;页内偏移 : S位, 每页有2^S个字节&lt;/p&gt;
&lt;p&gt;虚拟地址  = 2^S * p + o&lt;/p&gt;
&lt;h4&gt;页寻址方案&lt;/h4&gt;
&lt;p&gt;操作系统维护一张页表, 页表保存了逻辑地址——物理地址之间的映射关系&lt;/p&gt;
&lt;p&gt;存储 : (页号, 帧号)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;逻辑地址空间应当大于物理内存空间&lt;/li&gt;
&lt;li&gt;页映射到帧&lt;/li&gt;
&lt;li&gt;页是连续的虚拟内存&lt;/li&gt;
&lt;li&gt;帧是非连续的物理内存(有助于减少碎片的产生)&lt;/li&gt;
&lt;li&gt;不是所有的页都有对应的帧&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;页表(Page Table)&lt;/h3&gt;
&lt;h4&gt;页表概述&lt;/h4&gt;
&lt;p&gt;每一个运行的程序都有一个页表&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;属于程序运行状态, 会动态变化&lt;/li&gt;
&lt;li&gt;PTBR : 页表基址寄存器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;转换流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CPU根据程序的page的页号的若干位, 计算出索引值index, 在页表中搜索这个index, 得到的是帧号, 帧号和原本的offset组成物理地址.&lt;/p&gt;
&lt;p&gt;页表中还有一些特殊标志位&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dirty bit,&lt;/li&gt;
&lt;li&gt;resident bit, (0 : 对应的物理页帧在内存中不存在 ; 1 : 存在)&lt;/li&gt;
&lt;li&gt;clock / reference bit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;转换实例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;16位地址的系统&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;32KB的物理内存&lt;/li&gt;
&lt;li&gt;每页的 1024 byte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;逻辑地址空间 : (4, 0) ... (3, 1023)&lt;/p&gt;
&lt;p&gt;页表 :&lt;/p&gt;
&lt;p&gt;Flags |  Frame nums&lt;/p&gt;
&lt;p&gt;1 0 1    0 0 0 0 0          → 内存访问异常(可能要杀死程序)&lt;/p&gt;
&lt;p&gt;0 1 1    0 0 1 0 0           → 页帧是4 偏移是 1023 → 物理地址 (4, 1023)&lt;/p&gt;
&lt;h4&gt;分页机制的性能问题&lt;/h4&gt;
&lt;p&gt;访问一个内存单元需要2次内存访问&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一次用于获取页表项&lt;/li&gt;
&lt;li&gt;一次用于访问数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;页表可能非常大&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;64位机器如果每页1024字节, 那么一个页表的大小会是多少？(2^64 / 2^10 = 2^54 存放不下)&lt;/li&gt;
&lt;li&gt;每一个运行的程序都需要有一个页表&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如何处理？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓存(Caching)&lt;/li&gt;
&lt;li&gt;间接(Indirection)访问&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;转换后备缓冲区(TLB)&lt;/h4&gt;
&lt;p&gt;缓解时间问题&lt;/p&gt;
&lt;p&gt;Translation Look-aside Buffer(TLB) 是一个缓冲区. CPU中有快表TLB(可以将经常访问的页表存放在这边)&lt;/p&gt;
&lt;p&gt;缓存近期访问的页帧转换表项&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TLB使用关联内存实现, 具备快速访问性能&lt;/li&gt;
&lt;li&gt;如果TLB命中, 物理页号可以很快被获取&lt;/li&gt;
&lt;li&gt;如果TLB未命中, 对应的表项被更新到TLB中(x86的CPU由硬件实现, 其他的可能是由操作系统实现)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;二级/多级页表&lt;/h4&gt;
&lt;p&gt;时间换空间&lt;/p&gt;
&lt;p&gt;二级页表&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将页号分为两个部分, 页表分为两个, 一级页号对应一级页表, 二级页号对应二级页表.&lt;/li&gt;
&lt;li&gt;一级页号查表获得在二级页表的起始地址, 地址加上二级页号的值, 在二级页表中获得帧号&lt;/li&gt;
&lt;li&gt;节约了一定的空间, 在一级页表中如果resident bit = 0, 可以使得在二级页表中不存储相关index,而只有一张页表的话, 这一些index都需要保留&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多级页表&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过把页号分为k个部分, 来实现多级间接页表, 建立一棵页表&quot;树&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;反向页表&lt;/h4&gt;
&lt;p&gt;解决大地址空间问题&lt;/p&gt;
&lt;p&gt;目的 : 根据帧号获得页号&lt;/p&gt;
&lt;p&gt;反向页表只需要存在一张即可&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有大地址空间(64-bits), 前向映射页表变得繁琐. 比如 : 使用了5级页表&lt;/li&gt;
&lt;li&gt;不是让页表与逻辑地址空间的大小相对应, 而是当页表与物理地址空间的大小相对应. 逻辑地址空间增长速度快于物理地址空间&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;基于页寄存器(Page Registers)的方案&lt;/h5&gt;
&lt;p&gt;存储 (帧号, 页号) 使得表大小与物理内存大小相关, 而与逻辑内存关联减小.&lt;/p&gt;
&lt;p&gt;每一个帧和一个寄存器关联, 寄存器内容包括 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;resident bit : 此帧是否被占用&lt;/li&gt;
&lt;li&gt;occupier : 对应的页号 p&lt;/li&gt;
&lt;li&gt;protection bits : 保护位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;物理内存大小是 : 4096 * 4096 = 4K * 4KB = 16 MB&lt;/li&gt;
&lt;li&gt;页面大小是 : 4096 bytes = 4 KB&lt;/li&gt;
&lt;li&gt;页帧数 : 4096 = 4 K&lt;/li&gt;
&lt;li&gt;页寄存器使用的空间(假设8 bytes / register) : 8 * 4096 = 32 Kbytes&lt;/li&gt;
&lt;li&gt;页寄存器带来的额外开销 : 32K / 16M = 0.2%&lt;/li&gt;
&lt;li&gt;虚拟内存大小 : 任意&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优势 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;转换表的大小相对于物理内存来说很小&lt;/li&gt;
&lt;li&gt;转换表的大小跟逻辑地址空间的大小无关&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;劣势 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要的信息对调了, 即根据帧号可以找到页号&lt;/li&gt;
&lt;li&gt;如何转换回来? (如何根据页号找到帧号)&lt;/li&gt;
&lt;li&gt;在需要在反向页表中搜索想要的页号&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;基于关联内存(associative memory)的方案&lt;/h5&gt;
&lt;p&gt;硬件设计复杂, 容量不大, 需要放置在CPU中&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果帧数较少, 页寄存器可以被放置在关联内存中&lt;/li&gt;
&lt;li&gt;在关联内存中查找逻辑页号
&lt;ul&gt;
&lt;li&gt;成功 : 帧号被提取&lt;/li&gt;
&lt;li&gt;失败 : 页错误异常 (page fault)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;限制因素:
&lt;ul&gt;
&lt;li&gt;大量的关联内存非常昂贵(难以在单个时钟周期内完成 ; 耗电)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;基于哈希(hash)的方案&lt;/h5&gt;
&lt;p&gt;哈希函数 : h(PID, p) 从 PID 标号获得页号&lt;/p&gt;
&lt;p&gt;在反向页表中通过哈希算法来搜索一个页对应的帧号&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对页号做哈希计算, 为了在帧表中获取对应的帧号&lt;/li&gt;
&lt;li&gt;页 i 被放置在表 f(i) 位置, 其中 f 是设定的哈希函数&lt;/li&gt;
&lt;li&gt;为了查找页 i , 执行下列操作 :
&lt;ul&gt;
&lt;li&gt;计算哈希函数 f(i) 并且使用它作为页寄存器表的索引, 获取对应的页寄存器&lt;/li&gt;
&lt;li&gt;检查寄存器标签是否包含 i, 如果包含, 则代表成功, 否则失败&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;第五章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第五章的主要内容是：操作系统的虚拟内存管理技术&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;虚拟内存的起因&lt;/h2&gt;
&lt;p&gt;使用硬盘/磁盘使更多的程序在有限的内存中运行&lt;/p&gt;
&lt;p&gt;理想的存储器 : 更大更快更便宜和非易失性的存储区&lt;/p&gt;
&lt;h2&gt;覆盖技术&lt;/h2&gt;
&lt;p&gt;如果是程序太大, 超出了内存的容量, 可以采用手动的概率(overlay)技术, 只把需要的指令和数据保存在内存当中&lt;/p&gt;
&lt;p&gt;目的 : 是在较小的可用内存中运行较大的程序, 常用于多道程序系统, 与分区存储管理配合使用.&lt;/p&gt;
&lt;p&gt;原理 :&lt;/p&gt;
&lt;p&gt;把程序按照其自身逻辑结构, 划分为若干个功能上相互独立的程序模块, 那些不会同时执行的模块共享同一块内存区域, 按时间先后来运行.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必要部分(常用功能)的代码和数据常驻内存;&lt;/li&gt;
&lt;li&gt;可选部分(不常用功能)在其他程序模块中实现, 平时存放在外存中, 在需要用到时才装入内存;&lt;/li&gt;
&lt;li&gt;不存在调用关系的模块不必同时装入到内存, 从而可以相互覆盖, 即这些模块共用一个分区.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;也就是说,程序松耦合的部分可以按需装入内存,不需要的时候放在外存中,多个不常用部分共用一个分区.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;p&gt;A(20k) &lt;strong&gt;&lt;strong&gt;B(50k) ____ D(30k)
| ____ C(30k) ____ E(20k)
|&lt;/strong&gt;&lt;/strong&gt; F(40k)&lt;/p&gt;
&lt;p&gt;因此不需要将整个程序190k的数据全部放入内存中, 而是划分为 常驻区(20k) 覆盖区0(50k) 覆盖区1(40k) 压缩至了110k的内存空间使用&lt;/p&gt;
&lt;p&gt;缺点 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;由程序员来把一个大的程序划分为若干个小的功能模块, 并确定各个模块之间的覆盖关系, 费时费力, 增加了编程的复杂度;&lt;/li&gt;
&lt;li&gt;覆盖模块并从外存装入内存, 实际上是以时间延长来换取空间节省.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;交换技术&lt;/h2&gt;
&lt;p&gt;如果是程序太多, 超过了内存的容量, 可以采用自动的交换(swapping)技术, 把暂时不能执行的程序送到外存中&lt;/p&gt;
&lt;p&gt;目的 : 多道程序在内存时, 让正在运行的程序或需要运行的程序获得更多的内存资源&lt;/p&gt;
&lt;p&gt;原理 :&lt;/p&gt;
&lt;p&gt;可将暂时不能运行的程序送到外存, 从而获得空闲内存空间. 操作系统把一个进程的整个地址空间的内容保存到外存中(换出 swap out), 而将外存中的某个进程的地址空间读入到内存中(换入 swap in). 换入换出内容的大小为整个程序的地址空间.&lt;/p&gt;
&lt;p&gt;存在问题 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;交换时机的确定 : 何时需要发生交换? 只当内存空间不够或有不够的危险时换出;&lt;/li&gt;
&lt;li&gt;交换区的大小 : 必须足够大以存放所有用户进程的所有内存映像的拷贝, 必须能够对这些内存映像进行直接存取&lt;/li&gt;
&lt;li&gt;程序换入时的重定位 : 换出后再换入的内存位置一定要在原来的位置上嘛?(可能出现寻址问题) 最好采用动态地址映射的方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;覆盖技术和交换技术的对比&lt;/h2&gt;
&lt;p&gt;特点 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;覆盖只能发生在那些相互之间没有调用关系的程序模块之间, 因此程序员必须给出程序内的各个模块之间的逻辑覆盖结构.&lt;/li&gt;
&lt;li&gt;交换技术是以在内存中的程序大小为单位进行的, 它不需要程序员给出各个模块之间的逻辑覆盖结构.&lt;/li&gt;
&lt;li&gt;换言之, 交换发生在内存中程序与管理程序或操作系统之间, 而覆盖则发生在运行程序的内部.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在内存不够用的情形下, 可以采用覆盖技术和交换技术, 但是 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;覆盖技术 : 需要程序要自己把整个程序划分为若干个小的功能模块, 并确定各个模块之间的覆盖关系, 增加了程序员的负担.&lt;/li&gt;
&lt;li&gt;交换技术 : 以进程作为交换的单位, 需要把进程的整个地址空间都换入换出, 增加了处理器的开销.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;虚拟内存管理技术&lt;/h2&gt;
&lt;p&gt;如果想要在有限容量的内存中, 以更小的页粒度为单位装入更多更大的程序, 可以采用自动的虚拟存储技术&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;目标&lt;/p&gt;
&lt;p&gt;像覆盖技术那样, 不是把程序的所有内容都放在内存中, 因而能够运行比当前的空闲内存空间还要大的程序. 但做的更好, 由操作系统自动来完成, 无需程序员的干涉.&lt;/p&gt;
&lt;p&gt;像交换技术那样, 能够实现进程在内存与外存之间的交换, 因而获得更多的空闲内存空间. 但做的更好, 只对进程的部分内容在内存和外存之间进行交换.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;程序局部性原理&lt;/p&gt;
&lt;p&gt;程序的局部性原理(principle of locality) : 指程序在执行过程中的一个较短时期, 所执行的指令地址和指令的操作数地址, 分别局限于一定的区域.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;时间局部性 : 一条指令的一次执行和下次执行, 一个数据的一次访问和下次访问都集中在一个较短时期内 ;&lt;/li&gt;
&lt;li&gt;空间局部性 : 当前指令和邻近的几条指令, 当前访问的数据和邻近的几个数据都集中在一个较小区域内.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;程序的局部性原理表明, 从理论上来说, 虚拟存储技术是能够实现的. 而且在实现了以后应该是能够取得一个满意的效果.&lt;/p&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;题目描述 :
页面大小为4k, 分配给每个进程的物理页面是1. 
在一个进程中, 定义了如下的二维数组 int A[1024][1024]. 该数组按行存放在内存, 每一行放在一个页面中.
考虑一下程序的编写方法对缺页率的影响?

程序编写方法1 : (发生了1024*1024次缺页中断)
for(j = 0; j &amp;lt; 1024; j++)
		for(i = 0; i &amp;lt; 1024; i++)
				A[i][j] = 0;

程序编写方法2 : (发生了1024次缺页中断)
for(i = 0; i &amp;lt; 1024; i++)
		for(j = 0; j &amp;lt; 1024; j++)
				A[i][j] = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本概念&lt;/p&gt;
&lt;p&gt;可以在页式或段式内存管理的基础上实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在装入程序时, 不必将其全部装入内存, 而只需将当前需要执行的部分页面或段装入到内存中, 就可以让程序开始执行;&lt;/li&gt;
&lt;li&gt;在程序执行过程中, 如果需执行的指令或访问的数据尚未在内存中(称为缺页或缺段), 则由处理器通知操作系统将相应的页面或段调入到内存, 然后继续执行程序;&lt;/li&gt;
&lt;li&gt;另一方面, 操作系统将内存中暂时不使用的页面或段调出保存在外存上, 从而腾出更多空闲内存空间存放将要装入的程序以及将要调入的页面或段.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本特征&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大的用户空间 : 通过把物理内存和外存相结合, 提供给用户的虚拟内存空间通常大于实际的物理内存, 即实现了这两者的分离. 如32位的虚拟地址理论上可以访问4GB, 而可能计算机上仅有256M的物理内存, 但硬盘容量大于4GB.&lt;/li&gt;
&lt;li&gt;部分交换 : 与交换技术相比较, 虚拟存储的调入和调出是对部分虚拟地址空间进行的;&lt;/li&gt;
&lt;li&gt;不连续性 : 物理内存分配的不连续性, 虚拟地址空间使用的不连续性.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟页式内存管理&lt;/p&gt;
&lt;p&gt;页式内存管理&lt;/p&gt;
&lt;p&gt;页表 : 完成逻辑页到物理页帧的映射&lt;/p&gt;
&lt;p&gt;根据页号去页表中寻找索引, 先查看 resident bit 是否为0, 0表示不存在, 1表示映射关系存在, 获得帧号加上原本的偏移, 获得了物理地址.&lt;/p&gt;
&lt;p&gt;虚拟页式内存管理&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;大部分虚拟存储系统都采用虚拟页式存储管理技术, 即在页式存储管理的基础上, 增加请求调页和页面置换功能.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本思路&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当一个用户程序要调入内存运行时, 不是将该程序的所有页面都装入内存, 而是只装入部分的页面, 就可启动程序运行.&lt;/li&gt;
&lt;li&gt;在运行的过程中, 如果发现要运行的程序或要访问的数据不再内存, 则向系统发出缺页的中断请求, 系统在处理这个中断时, 将外存中相应的页面调入内存, 使得该程序能够继续运行.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;页表表项&lt;/p&gt;
&lt;p&gt;逻辑页号 | 访问位 | 修改位 | 保护位 | 驻留位 | 物理页帧号&lt;/p&gt;
&lt;p&gt;驻留位 : 表示该页是在内存中还是在外存.&lt;/p&gt;
&lt;p&gt;保护位 : 表示允许对该页做何种类型的访问, 如只读, 可读写, 可执行等&lt;/p&gt;
&lt;p&gt;修改位 : 表示此页在内存中是否被修改过. 当系统回收该物理页面时, 根据此位来决定是否把它的内容写回外存&lt;/p&gt;
&lt;p&gt;访问位 : 如果该页被访问过(包括读写操作), 则设置此位. 用于页面置换算法.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺页中断处理过程 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果在内存中有空闲的物理页面, 则分配一物理页帧f, 然后转第4步; 否则转到第2步;&lt;/li&gt;
&lt;li&gt;采用某种页面置换算法, 选择一个将被替换的物理页帧f, 它所对应的逻辑页为q, 如果该页在内存期间被修改过, 则需要把它写回外存;&lt;/li&gt;
&lt;li&gt;对q所对应的页表项修改, 把驻留位置为0;&lt;/li&gt;
&lt;li&gt;将需要访问的页p装入到物理页面f当中;&lt;/li&gt;
&lt;li&gt;修改p所对应的页表项的内容, 把驻留位置为1, 把物理页帧号置为f;&lt;/li&gt;
&lt;li&gt;重新运行被中断是指令.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;在何处保存未被映射的页?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;能够简单地识别在二级存储器中的页&lt;/li&gt;
&lt;li&gt;交换空间(磁盘或者文件) : 特殊格式, 用于存储未被映射的页面&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;后备存储(二级存储) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个虚拟地址空间的页面可以被映射到一个文件(在二级存储中)的某个位置&lt;/li&gt;
&lt;li&gt;代码段 : 映射到可执行二进制文件&lt;/li&gt;
&lt;li&gt;动态加载的共享库程序段 : 映射到动态调用的库文件&lt;/li&gt;
&lt;li&gt;其他段 : 可能被映射到交换文件(swap file)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟内存性能&lt;/p&gt;
&lt;p&gt;为了便于理解分页的开销, 使用有效存储器访问时间 effective memory access time (EAT)&lt;/p&gt;
&lt;p&gt;EAT = 访存时间 * 页表命中几率 + page fault处理时间 * page fault几率&lt;/p&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;p&gt;访存时间 : 10 ns&lt;/p&gt;
&lt;p&gt;磁盘访问时间 : 5 ms&lt;/p&gt;
&lt;p&gt;参数 p  = page fault 几率&lt;/p&gt;
&lt;p&gt;参数 q = dirty page 几率(对页面写操作)&lt;/p&gt;
&lt;p&gt;EAT = 10*(1-p) + 5000000*p*(1+q)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;第六章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第六章的主要内容是：操作系统的虚拟内存管理技术中的页面置换算法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;功能与目标&lt;/h2&gt;
&lt;p&gt;功能 : 当缺页中断发生, 需要调入新的页面而内存已满时, 选择内存当中哪个物理页面被置换.&lt;/p&gt;
&lt;p&gt;目标 : 尽可能地减少页面的换进换出次数(即缺页中断的次数). 具体来说, 把未来不再使用的或短期内较少使用的页面换出, 通常只能在局部性原理指导下依据过去的统计数据来进行预测.&lt;/p&gt;
&lt;p&gt;页面锁定 : 用于描述必须常驻内存的操作系统的关键部分或时间关键的应用进程. 实现的方式是 : 在页表中添加锁定标记位(lock bit).&lt;/p&gt;
&lt;h2&gt;实验设置与评价方法&lt;/h2&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;p&gt;记录一个进程对页访问的一个轨迹&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;举例 : 虚拟地址跟踪(页号, 偏移)...
&lt;ul&gt;
&lt;li&gt;(3,0) (1,9) (4,1) (2,1) (5,3) (2,0) ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;生成的页面轨迹
&lt;ul&gt;
&lt;li&gt;3, 1, 4, 2, 5, 2, 1, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;模拟一个页面置换的行为并且记录产生页缺失数的数量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;更少的缺失, 更好的性能&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;局部页面置换算法&lt;/h2&gt;
&lt;h3&gt;最优页面置换算法&lt;/h3&gt;
&lt;p&gt;基本思路 : 当一个缺页中断发生时, 对于保存在内存当中的每一个逻辑页面, 计算在它的下一次访问之前, 还需等待多长时间, 从中选择等待时间最长的那个, 作为被置换的页面.&lt;/p&gt;
&lt;p&gt;这是一种理想情况, 在实际系统中是无法实现的, 因为操作系统无法知道每一个页面要等待多长时间以后才会再次被访问.&lt;/p&gt;
&lt;p&gt;可用作其他算法的性能评价的依据.(在一个模拟器上运行某个程序, 并记录每一次的页面访问情况, 在第二遍运行时即可使用最优算法)&lt;/p&gt;
&lt;h3&gt;先进先出算法&lt;/h3&gt;
&lt;p&gt;基本思路 : 选择在内存中驻留时间最长的页面淘汰. 具体来说, 系统维护着一个链表, 记录了所有位于内存当中的逻辑页面. 从链表的排列顺序来看, 链首页面的驻留时间最长, 链尾页面的驻留时间最短. 当发生一个缺页中断时, 把链首页面淘汰出去, 并把新的页面添加到链表的末尾.&lt;/p&gt;
&lt;p&gt;性能较差, 调出的页面有可能是经常要访问的页面. 并且有 belady现象. FIFO算法很少单独使用.&lt;/p&gt;
&lt;h3&gt;最近最久未使用算法&lt;/h3&gt;
&lt;p&gt;LRU(Least Recently Used)&lt;/p&gt;
&lt;p&gt;基本思路 : 当一个缺页中断发生时, 选择最久未使用的那个页面, 并淘汰.&lt;/p&gt;
&lt;p&gt;它是对最优页面置换算法的一个近似, 其依据是程序的局部性原理, 即在最近一小段时间(最近几条指令)内, 如果某些页面被频繁地访问, 那么再将来的一小段时间内, 他们还可能会再一次被频繁地访问. 反过来说, 如果过去某些页面长时间未被访问, 那么在将来它们还可能会长时间地得不到访问.&lt;/p&gt;
&lt;p&gt;LRU算法需要记录各个页面使用时间的先后顺序, 开销比较大.&lt;/p&gt;
&lt;p&gt;两种可能的实现方法是 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统维护一个页面链表, 最近刚刚使用过的页面作为首节点, 最久未使用的作为尾结点. 再一次访问内存时, 找出相应的页面, 把它从链表中摘下来, 再移动到链表首. 每次缺页中断发生时, 淘汰链表末尾的页面.&lt;/li&gt;
&lt;li&gt;设置一个活动页面栈, 当访问某页时, 将此页号压入栈顶, 然后, 考察栈内是否有与此页面相同的页号, 若有则抽出. 当需要淘汰一个页面时, 总是选择栈底的页面, 它就是最久未使用的.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;时钟页面置换算法&lt;/h3&gt;
&lt;p&gt;基本思路 :&lt;/p&gt;
&lt;p&gt;需要用到页表项的访问位, 当一个页面被装入内存时, 把该位初始化为0. 然后如果这个页面被访问, 则把该位置设为1;&lt;/p&gt;
&lt;p&gt;把各个页面组织成环形链表(类似钟表面), 把指针指向最老的页面(最先进来);&lt;/p&gt;
&lt;p&gt;当发生一个缺页中断时, 考察指针所指向的最老页面, 若它的访问位为0, 立即淘汰; 若访问位为0, 然后指针往下移动一格. 如此下去, 直到找到被淘汰的页面, 然后把指针移动到下一格.&lt;/p&gt;
&lt;p&gt;流程 :&lt;/p&gt;
&lt;p&gt;如果访问页在物理内存中, 访问位置1.&lt;/p&gt;
&lt;p&gt;如果不在物理页, 从指针当前指向的物理页开始, 如果访问位0, 替换当前页, 指针指向下一个物理页; 如果访问位为1, 置零以后访问下一个物理页再进行判断. 如果所有物理页的访问位都被清零了, 又回到了第一次指针所指向的物理页进行替换.&lt;/p&gt;
&lt;h3&gt;二次机会算法&lt;/h3&gt;
&lt;p&gt;因为考虑到时钟页面置换算法, 有时候会把一些 dirty bit 为1(有过写操作)的页面进行置换, 这样的话, 代价会比较大. 因此, 可以结合访问位和脏位一起来决定应该置换哪一页.&lt;/p&gt;
&lt;p&gt;used   dirty     →  used   dirty&lt;/p&gt;
&lt;p&gt;0         0                  replace&lt;/p&gt;
&lt;p&gt;0         1                  0         0&lt;/p&gt;
&lt;p&gt;1          0                 0         0&lt;/p&gt;
&lt;p&gt;1          1                 0         1&lt;/p&gt;
&lt;p&gt;相当于说, 替换的优先级, 没有读写也没写过, 那么直接走, 如果写过或者访问过, 那么给你一次机会, 如果又写过, 又访问过, 那么久给你两次机会.&lt;/p&gt;
&lt;h3&gt;最不常用算法&lt;/h3&gt;
&lt;p&gt;Least Frequently used, LFU&lt;/p&gt;
&lt;p&gt;基本思路 : 当一个缺页中断发生时, 选择访问次数最少的那个页面, 并淘汰.&lt;/p&gt;
&lt;p&gt;实现方法 : 对每一个页面设置一个访问计数器, 每当一个页面被访问时, 该页面的访问计数器加1. 当发生缺页中断时, 淘汰计数值最小的那个页面.&lt;/p&gt;
&lt;p&gt;LRU和LFU的对比 : LRU考察的是多久未访问, 时间越短越好. 而LFU考察的是访问的次数和频度, 访问次数越多越好.&lt;/p&gt;
&lt;h3&gt;Belady现象(科学家名字)&lt;/h3&gt;
&lt;p&gt;在采用FIFO算法时, 有时会出现分配的物理页面数增加, 缺页率反而提高的异常现象;&lt;/p&gt;
&lt;p&gt;出现原因 : FIFO算法的置换特征与进程访问内存的动态特征是矛盾的, 与置换算法的目标是不一致的(即替换较少使用的页面), 因此, 被他置换出去的页面不一定是进程不会访问的.&lt;/p&gt;
&lt;h3&gt;LRU / FIFO 和 Clock 的比较&lt;/h3&gt;
&lt;p&gt;LRU和FIFO都是先进先出的思路, 只不过LRU是针对页面最近访问时间来进行排序, 所以需要在每一次页面访问的时候动态地调整各个页面之间的先后顺序(有一个页面的最近访问时间变了). 而FIFO是针对页面进入内存的时间来进行排序, 这个时间是固定不变的, 所以各个页面之间的先后顺序是固定的. 如果一个页面在进入内存后没有被访问, 那么它的最近访问时间就是它进入内存的时间. 换句话说, 如果内存当中的所有页面都未曾访问过, 那么LRU算法就退化为了FIFO算法.&lt;/p&gt;
&lt;p&gt;例如 : 给进程分配3个物理页面, 逻辑页面的访问顺序是 : 1,2,3,4,5,6,1,2,3 ...&lt;/p&gt;
&lt;h2&gt;全局页面置换算法&lt;/h2&gt;
&lt;h3&gt;工作集模型&lt;/h3&gt;
&lt;p&gt;前面介绍的各种页面置换算法, 都是基于一个前提, 即程序的局部性原理. 但是此原理是否成立?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果局部性原理不成立, 那么各种页面置换算法就没有说明分别, 也没有什么意义. 例如 : 假设进程对逻辑页面的访问顺序是1,2,3,4,5,6,6,7,8,9..., 即单调递增, 那么在物理页面数有限的前提下, 不管采用何种置换算法, 每次的页面访问都必然导致缺页中断.&lt;/li&gt;
&lt;li&gt;如果局部性原理是成立的, 那么如何来证明它的存在, 如何来对它进行定量地分析? 这就是工作集模型.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;工作集&lt;/h3&gt;
&lt;p&gt;工作集 : 一个进程当前正在使用的逻辑页面集合.&lt;/p&gt;
&lt;p&gt;可以使用一个二元函数 W(t, delta) 来表示 :&lt;/p&gt;
&lt;p&gt;t 是当前的执行时刻;&lt;/p&gt;
&lt;p&gt;delta 称为工作集窗口, 即一个定长的页面访问的时间窗口;&lt;/p&gt;
&lt;p&gt;W(t, delta) = 在当前时刻 t 之前的 delta 时间窗口当中的所有页面所组成的集合(随着 t 的变化, 该集合也在不断的变化)&lt;/p&gt;
&lt;p&gt;|W(t, delta)| 是工作集的大小, 即逻辑页的数量.&lt;/p&gt;
&lt;p&gt;工作集大小的变化 : 进程开始执行后, 随着访问新页面逐步建立较稳定的工作集. 当内存访问的局部性区域的位置大致稳定时, 工作集大小也大致稳定; 局部性区域的位置改变时, 工作集快速扩张和收缩过渡到下一个稳定值.&lt;/p&gt;
&lt;h3&gt;常驻集&lt;/h3&gt;
&lt;p&gt;常驻集是指在当前时刻, 进程实际驻留在内存当中的页面集合.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工作集是进程在运行过程中固有的性质, 而常驻集取决于系统分配给进程的物理页面数目, 以及所采用的页面置换算法;&lt;/li&gt;
&lt;li&gt;如果一个进程的整个工作集都在内存当中, 即常驻集 包含 工作集, 那么进程将很顺利地运行, 而不会造成太多的缺页中断(直到工作集发生剧烈变动, 从而过渡到另一个状态);&lt;/li&gt;
&lt;li&gt;当进程常驻集的大小达到某个数目之后, 再给它分配更多的物理页面, 缺页率也不会明显下降.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;工作集页置换算法&lt;/h3&gt;
&lt;p&gt;当工作集窗口在滑动过程中, 如果页面不在集合中, 那么就会直接丢失这个不在窗口中页面, 而不会等待缺页中断再丢弃.&lt;/p&gt;
&lt;h3&gt;缺页率置换算法&lt;/h3&gt;
&lt;p&gt;可变分配策略 : 常驻集大小可变. 例如 : 每个进程在刚开始运行的时候, 先根据程序大小给它分配一定数目的物理页面, 然后在进程运行过程中, 再动态地调整常驻集的大小.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可采用全局页面置换的方式, 当发生一个缺页中断时, 被置换的页面可以是在其他进程当中, 各个并发进程竞争地使用物理页面.&lt;/li&gt;
&lt;li&gt;优缺点 : 性能较好, 但增加了系统开销.&lt;/li&gt;
&lt;li&gt;具体实现 : 可以使用缺页率算法来动态调整常驻集的大小.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;缺页率 : 表示 &quot;缺页次数 / 内存访问次数&quot;&lt;/p&gt;
&lt;p&gt;影响因素 : 页面置换算法, 分配给进程的物理页面数目, 页面本身的大小, 程序的编写方法.&lt;/p&gt;
&lt;h3&gt;抖动问题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;如果分配给一个进程的物理页面太少, 不能包含整个的工作集, 即常驻集 属于 工作集, 那么进程将会造成很多的缺页中断, 需要频繁的在内存与外存之间替换页面, 从而使进程的运行速度变得很慢, 我们把这种状态称为 &quot;抖动&quot;.&lt;/li&gt;
&lt;li&gt;产生抖动的原因 : 随着驻留内存的进程数目增加, 分配给每个进程的物理页面数不断就减小, 缺页率不断上升. 所以OS要选择一个适当的进程数目和进程需要的帧数, 以便在并发水平和缺页率之间达到一个平衡.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;第七章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第七章的主要内容是：进程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;进程(process)描述&lt;/h2&gt;
&lt;h3&gt;进程定义&lt;/h3&gt;
&lt;p&gt;进程 : 一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程.&lt;/p&gt;
&lt;h3&gt;进程的组成&lt;/h3&gt;
&lt;p&gt;进程包括 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序的代码&lt;/li&gt;
&lt;li&gt;程序处理的数据&lt;/li&gt;
&lt;li&gt;程序计数器中的值, 指示下一条将运行的指令&lt;/li&gt;
&lt;li&gt;一组通用的寄存器的当前值, 堆, 栈&lt;/li&gt;
&lt;li&gt;一组系统资源(如打开的文件)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进程和程序的联系 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序是产生进程的基础&lt;/li&gt;
&lt;li&gt;程序的每次运行构成不同的进程&lt;/li&gt;
&lt;li&gt;进程是程序功能的体现&lt;/li&gt;
&lt;li&gt;通过多次执行, 一个程序可以对应多个进程, 通过调用关系, 一个进程可包括多个程序.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进程和程序的区别 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程是动态的,  程序是静态的 : 程序是有序代码的集合. 进程是程序的执行, 进程有核心态 / 用户态.&lt;/li&gt;
&lt;li&gt;进程是暂时的, 程序是永久的. 进程是一个状态变化的过程, 程序可以长久保存.&lt;/li&gt;
&lt;li&gt;进程和程序的组成不同 : 进程的组成包括程序, 数据和进程控制块(进程状态信息)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;进程的特点&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;动态性&lt;/strong&gt; : 可动态地创建, 结果进程;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;并发性&lt;/strong&gt; : 进程可以被独立调度并占用处理机运行; (并发:一段, 并行:一时刻)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;独立性&lt;/strong&gt; : 不同进程的工作不相互影响;(页表是保障措施之一)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;制约性&lt;/strong&gt; : 因访问共享数据, 资源或进程间同步而产生制约.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;抛出了一个问题 : 如果你要设计一个OS, 怎么样来实现其中的进程管理机制?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;进程控制结构&lt;/h3&gt;
&lt;p&gt;描述进程的数据结构 : 进程控制块 (Process Control Block)&lt;/p&gt;
&lt;p&gt;操作系统为每个进程都维护了一个PCB, 用来保存与该进程有关的各种状态信息.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程控制块 :&lt;/strong&gt; 操作系统管理控制进程运行所用的信息集合.&lt;/p&gt;
&lt;p&gt;进程的创建 : 为该进程生成一个PCB&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程的终止 :&lt;/strong&gt; 回收它的PCB&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程的组织管理 :&lt;/strong&gt; 通过对PCB的组织管理来实现&lt;/p&gt;
&lt;p&gt;(PCB具体包含什么信息? 如何组织的? 进程的状态转换?)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PCB有以下三大类信息 :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程标志信息. 如本进程的标志, 本进程的产生者标志(父进程标志). 用户标志&lt;/li&gt;
&lt;li&gt;处理机状态信息保存区 : 保存进程的运行现场信息 :
&lt;ul&gt;
&lt;li&gt;用户可见寄存器. 用户程序可以使用的数据, 地址等寄存器&lt;/li&gt;
&lt;li&gt;控制和状态寄存器. 如程序计数器(PC), 程序状态字(PSW)&lt;/li&gt;
&lt;li&gt;栈指针. 过程调用, 系统调用, 中断处理和返回时需要用到它&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程控制信息
&lt;ul&gt;
&lt;li&gt;调度和状态信息. 用于操作系统调度进程并占用处理机使用.&lt;/li&gt;
&lt;li&gt;进程间通信信息. 为支持进程间与通信相关的各种标志, 信号, 信件等, 这些信息都存在接收方的进程控制块中.&lt;/li&gt;
&lt;li&gt;存储管理信息. 包含有指向本进程映像存储空间的数据结构.&lt;/li&gt;
&lt;li&gt;进程所用资源. 说明由进程打开, 使用的系统资源. 如打开的文件等.&lt;/li&gt;
&lt;li&gt;有关数据结构的链接信息. 进程可以连接到一个进程队列中, 或连接到相关的其他进程的PCB.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;进程的组织方式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;链表 : 同一状态的进程其PCB成一链表, 多个状态对应多个不同的链表.(各状态的进程形成不同的链表 : 就绪链表, 阻塞链表)&lt;/p&gt;
&lt;p&gt;索引表 : 同一状态的进程归入一个index表(由index指向PCB), 多个状态对应多个不同的index表(各状态的进行形成不同的索引表 : 就绪索引表, 阻塞索引表)&lt;/p&gt;
&lt;h2&gt;进程状态(state)&lt;/h2&gt;
&lt;h3&gt;进程的生命期管理&lt;/h3&gt;
&lt;h4&gt;进程创建&lt;/h4&gt;
&lt;p&gt;引起进程创建的3个主要事件 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统初始化;&lt;/li&gt;
&lt;li&gt;用户请求创建一个新进程;&lt;/li&gt;
&lt;li&gt;正在运行的进程执行了创建进程的系统调用.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;进程运行&lt;/h4&gt;
&lt;p&gt;内核选择一个就绪的进程, 让它占用处理机并执行&lt;/p&gt;
&lt;p&gt;(为何选择?如何选择?)&lt;/p&gt;
&lt;h4&gt;进程等待(阻塞)&lt;/h4&gt;
&lt;p&gt;在以下情况下, 进程等待(阻塞):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;请求并等待系统服务, 无法马上完成&lt;/li&gt;
&lt;li&gt;启动某种操作, 无法马上完成&lt;/li&gt;
&lt;li&gt;需要的数据没有到达&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;进程只能自己阻塞自己, 因为只有进程自身才能知道何时需要等待某种事件的发生.&lt;/p&gt;
&lt;h4&gt;进程唤醒&lt;/h4&gt;
&lt;p&gt;唤醒进程的原因 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;被阻塞进程需要的资源可被满足&lt;/li&gt;
&lt;li&gt;被阻塞进程等待的事件到达&lt;/li&gt;
&lt;li&gt;将该进程的PCB插入到就绪队列&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;进程只能被别的进程或操作系统唤醒&lt;/p&gt;
&lt;h4&gt;进程结束&lt;/h4&gt;
&lt;p&gt;在以下四种情况下, 进程结束 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正常退出(自愿)&lt;/li&gt;
&lt;li&gt;错误退出(自愿)&lt;/li&gt;
&lt;li&gt;致命错误(强制性)&lt;/li&gt;
&lt;li&gt;被其他进程杀死(强制性)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;进程状态变化模型&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;进程的三种基本状态 :&lt;/strong&gt; 进程在生命结束前处于三种基本状态之一.&lt;/p&gt;
&lt;p&gt;不同系统设置的进程状态数目不同.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三种基本状态&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;运行状态(Running) : 当一个进程正在处理机上运行时&lt;/li&gt;
&lt;li&gt;就绪状态(Ready) : 一个进程获得了除处理机之外的一切所需资源, 一旦得到处理机即可运行&lt;/li&gt;
&lt;li&gt;等待状态(阻塞状态 Blocked) : 一个进程正在等待某一时间而暂停运行时. 如等待某资源, 等待输入/输出完成.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;进程其它的基本状态&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;创建状态(New) : 一个进程正在被创建, 还没被转到就绪状态之前的状态&lt;/p&gt;
&lt;p&gt;结束状态(Exit): 一个进程正在从系统中消失时的状态, 这是因为进程结束或由于其它原因所导致.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;可能的状态变化如下 :&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;NULL → New : 一个新进程被产生出来执行一个程序&lt;/p&gt;
&lt;p&gt;New → Ready: 当进程创建完成并初始化后, 一切就绪准备运行时, 变为就绪状态&lt;/p&gt;
&lt;p&gt;Ready → Running  : 处于就绪态的进程被进程调度程序选中后, 就分配到处理机上来运行&lt;/p&gt;
&lt;p&gt;Running → Exit   : 当进程表示它已经完成或者因出错, 当前运行进程会由操作系统作结束处理&lt;/p&gt;
&lt;p&gt;Running → Ready  : 处于运行状态的进程在其运行过程中, 由于分配它的处理机时间片用完而让出处理机&lt;/p&gt;
&lt;p&gt;Running → Blocked: 当进程请求某样东西且必须等待时&lt;/p&gt;
&lt;p&gt;Blocked → Ready  : 当进程要等待某事件到来时, 它从阻塞状态变到就绪状态&lt;/p&gt;
&lt;h3&gt;进程挂起&lt;/h3&gt;
&lt;p&gt;进程挂起, 为了合理且充分地利用系统资源.&lt;/p&gt;
&lt;p&gt;进程在挂起状态时, 意味着进程没有占用内存空间, 处在挂起状态的进程映像在磁盘上.(把进程放到磁盘上)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两种挂起状态&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;阻塞挂起状态 : 进程在外存并等待某事件的出现;&lt;/li&gt;
&lt;li&gt;就绪挂起状态 : 进程在外存, 但只要进入内存, 即可运行.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;与挂起相关的状态转换&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;挂起 :&lt;/strong&gt; 把一个进程从内存转到外存, 可能有以下几种情况 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;阻塞到阻塞挂起 : 没有进程处于就绪状态或就绪进程要求更多内存资源时, 会进行这种转换, 以提交新进程或运行时就绪进程.&lt;/li&gt;
&lt;li&gt;就绪到就绪挂起 : 当有高优先级阻塞(系统认为会很快就绪的)进程和低优先级就绪进程时, 系统会选择挂起低优先级就绪进程.&lt;/li&gt;
&lt;li&gt;运行到就绪挂起 : 对抢先式分时系统, 当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时, 系统可能会把运行进程转导就绪挂起状态.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;在外存时的状态转换 :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;阻塞挂起到就绪挂起 : 当有阻塞挂起因相关事件出现时, 系统会把阻塞挂起进程转换为就绪挂起进程.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解挂, 激活 :&lt;/strong&gt; 把一个进程从外存转到内存; 可能有以下几种情况 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;就绪挂起到就绪 : 没有就绪进程或挂起就绪进程优先级高于就绪进程时, 会进行这种转换.&lt;/li&gt;
&lt;li&gt;阻塞挂起到阻塞 : 当一个进程释放足够内存时, 系统会把一个高优先级阻塞挂起(系统认为会很快出现所等待的事件)进程转换为阻塞进程.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;抛出一个问题 : OS怎么通过PCB和定义的进程状态来管理PCB, 帮助完成进程的调度过程?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;状态队列&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;由操作系统来维护一组队列, 用来表示系统当中所有进程的当前状态;&lt;/li&gt;
&lt;li&gt;不同的状态分别用不同的队列来表示(就绪队列, 各种类型的阻塞队列);&lt;/li&gt;
&lt;li&gt;每个进程的PCB都根据它的状态加入到相应的队列当中, 当一个进程的状态发生变化时, 它的PCB从一个状态中脱离出来, 加入到另外一个队列.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;线程(thread)&lt;/h2&gt;
&lt;h3&gt;为什么使用线程?&lt;/h3&gt;
&lt;p&gt;实例 : 编写一个MP3播放软件.&lt;/p&gt;
&lt;p&gt;核心功能 : (1)从MP3音频文件中读取数据; (2)对数据进行解压缩; (3)把解压缩后的音频数据播放出来.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//单进程方式
while(1){
	Read();
	Decompress();
	Play();
}
//问题: 播放出来的声音能否连贯? 各个函数之间不是并发执行, 影响资源的使用效率.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;//多进程
//进程1
while(1){
	Read();
}
//进程2
while(1){
	Decompress();
}
//进程3
while(1){
	Play();
}
//问题: 进程之间如何通信,共享数据?另外,维护进程的系统开销较大:
//创建进程时,分配资源,建立PCB;撤销进程时,回收资源,撤销PCB;进程切换时,保存当前进程的状态信息
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此需要提出一种新的实体, 满足以下特征:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;实体之间可以并发执行;&lt;/li&gt;
&lt;li&gt;实体之间共享相同的地址空间.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这实体就是线程.&lt;/p&gt;
&lt;h3&gt;什么是线程&lt;/h3&gt;
&lt;p&gt;线程是进程当中的一条执行流程.&lt;/p&gt;
&lt;p&gt;从两个方面重新理解进程:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从资源组合的角度: 进程把一组相关的资源组合起来,构成了一个资源平台(环境),包括地址空间(代码段,数据段),打开的文件等各种资源;&lt;/li&gt;
&lt;li&gt;从运行的角度: 代码在这个资源平台上的一条执行流程(线程).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;线程 = 进程 - 共享资源&lt;/p&gt;
&lt;h3&gt;线程的优缺点&lt;/h3&gt;
&lt;p&gt;线程的优点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个进程中可以同时存在多个线程;&lt;/li&gt;
&lt;li&gt;各个线程之间可以并发地执行;&lt;/li&gt;
&lt;li&gt;各个线程之间可以共享地址空间和文件等资源.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;线程的缺点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;一个线程崩溃, 会导致其所属进程的所有线程崩溃.(给它了&quot;权限&quot;就得有更高的&quot;责任&quot;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线程所需的资源&lt;/p&gt;
&lt;p&gt;不同的线程需要独立的寄存器和堆栈, 共享代码,数据和文件等.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;线程和进程的比较&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;进程是资源分配单位, 线程是CPU调度单位;&lt;/li&gt;
&lt;li&gt;进程拥有一个完整的资源平台, 而线程只独享必不可少的资源, 如寄存器和栈;&lt;/li&gt;
&lt;li&gt;线程同样具有就绪,阻塞和执行三种基本状态,同样具有状态之间的转换关系;&lt;/li&gt;
&lt;li&gt;线程能减少并发执行的时间和空间开销:
&lt;ul&gt;
&lt;li&gt;线程的创建时间比进程短;(直接利用所属进程的一些状态信息)&lt;/li&gt;
&lt;li&gt;线程的终止时间比进程短;(不需要考虑把这些状态信息给释放)&lt;/li&gt;
&lt;li&gt;同一进程内的线程切换时间比进程短;(同一进程不同线程的切换不需要切换页表)&lt;/li&gt;
&lt;li&gt;由于同一进程的各线程之间共享内存和文件资源, 可直接进行不通过内核的通信.(直接通过内存地址读写资源)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;线程的实现&lt;/h3&gt;
&lt;p&gt;主要有三种线程的实现方式:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户线程  : 在用户空间实现; POSIX Pthreads, Mach C-threads, Solaris threads&lt;/li&gt;
&lt;li&gt;内核线程  : 在内核中实现; Windows, Solaris, Linux&lt;/li&gt;
&lt;li&gt;轻量级进程: 在内核中实现,支持用户线程; Solaris&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;用户线程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;操作系统只能看到进程, 看不到线程, 线程的TCB在线程库中实现;&lt;/p&gt;
&lt;p&gt;在用户空间实现的线程机制, 它不依赖于操作系统的内核, 由一组用户级的线程库来完成线程的管理, 包括进程的创建,终止,同步和调度等.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;由于用户线程的维护由相应的进程来完成(通过线程库函数),不需要操作系统内核了解用户进程的存在,可用于不支持线程技术的多进程操作系统;&lt;/li&gt;
&lt;li&gt;每个进程都需要它自己私有的线程控制块(TCB)列表,用来跟踪记录它的各个线程的状态信息(PC,栈指针,寄存器),TCB由线程库函数来维护;&lt;/li&gt;
&lt;li&gt;用户线程的切换也是由线程库函数来完成,无需用户态/核心态切换,所以速度特别快;&lt;/li&gt;
&lt;li&gt;允许每个进程拥有自定义的线程调度算法.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户线程的缺点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;阻塞性的系统调用如何实现?如果一个线程发起系统调用而阻塞,则整个进程在等待;&lt;/li&gt;
&lt;li&gt;当一个线程开始运行时,除非它主动地交出CPU的使用权,否则它所在的进程当中的其他线程将无法运行;&lt;/li&gt;
&lt;li&gt;由于时间片分配给进程,所以与其他进程比,在多线程执行时,每个线程得到的时间片较少,执行会较慢.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;内核线程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;操作系统能够看到进程也可能看到线程,线程在内核中实现;&lt;/p&gt;
&lt;p&gt;内核线程是在操作系统的内核当中实现的一种线程机制,由操作系统的内核来完成线程的创建,终止和管理.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在支持内核线程的操作系统中,由内核来维护进程和线程的上下文信息(PCB和TCB);&lt;/li&gt;
&lt;li&gt;线程的创建,终止和切换都是通过系统调用,内核函数的方式来进行,由内核来完成,因此系统开销较大;&lt;/li&gt;
&lt;li&gt;在一个进程当中,如果某个内核线程发起系统调用而被阻塞,并不会影响其他内核线程的运行;&lt;/li&gt;
&lt;li&gt;时间片分配给线程,多线程的进程获得更多CPU时间;&lt;/li&gt;
&lt;li&gt;Windows NT 和 Windows 2000/XP 支持内核线程.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;轻量级进程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它是内核支持的用户线程.一个进程可以有一个或多个轻量化进程,每个量级进程由一个单独的内核线程来支持.(Solaris,Linux)&lt;/p&gt;
&lt;h2&gt;上下文切换&lt;/h2&gt;
&lt;p&gt;停止当前运行进程(从运行状态变成其他状态),并且调度其他进程(转变为运行状态)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必须在切换之前存储许多部分的进程上下文&lt;/li&gt;
&lt;li&gt;必须能够在之后恢复他们,所以进程不能显示它曾经被暂停过&lt;/li&gt;
&lt;li&gt;必须快速(上下文切换时非常频繁)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要存储什么上下文?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;寄存器(PC,SP...),CPU状态等信息&lt;/li&gt;
&lt;li&gt;一些时候可能会费时,所以我们应该尽可能避免&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;操作系统为活跃进程准备了进程控制块&lt;/p&gt;
&lt;p&gt;操作系统将进程控制块放置在一个合适的队列中&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;就绪队列&lt;/li&gt;
&lt;li&gt;等待IO队列(每个设备的队列)&lt;/li&gt;
&lt;li&gt;僵尸队列&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;进程控制&lt;/h2&gt;
&lt;h3&gt;创建进程&lt;/h3&gt;
&lt;p&gt;fork()的简单实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对子进程分配内存&lt;/li&gt;
&lt;li&gt;复制父进程的内存和CPU寄存器到子进程&lt;/li&gt;
&lt;li&gt;开销昂贵&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在99%的情况下,我们在调用fork()之后调用exec()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在fork()操作中内存复制是没有作用的&lt;/li&gt;
&lt;li&gt;子进程将可能关闭打开的文件和连接&lt;/li&gt;
&lt;li&gt;开销因此是最高的&lt;/li&gt;
&lt;li&gt;为什么不能结合它们在一个调用中(OS/2, windows)?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;vfork()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个创建进程的系统调用,不需要创建一个同样的内存映像&lt;/li&gt;
&lt;li&gt;一些时候称为轻量级fork()&lt;/li&gt;
&lt;li&gt;子进程应该几乎立即调用exec()&lt;/li&gt;
&lt;li&gt;现在不再使用如果我们使用 copy on write 技术&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;加载和执行进程&lt;/h3&gt;
&lt;p&gt;系统调用exec()加载程序取代当前运行的进程&lt;/p&gt;
&lt;p&gt;exec()调用允许一个进程&quot;加载&quot;一个不同的程序并且在main开始执行(事实上 _start)&lt;/p&gt;
&lt;p&gt;它允许一个进程指定参数的数量(argc)和它字符串参数数组(argv)&lt;/p&gt;
&lt;p&gt;如果调用成功(相同的进程,不同的程序)&lt;/p&gt;
&lt;p&gt;代码,stack,heap重写&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int pid = fork(); //创建子进程
if(pid == 0) {    //子进程
	exec_status = exec(&quot;calc&quot;, argc, argv0,argv1,...);
	printf(&quot;Why would I execute?&quot;);
} else if(pid &amp;gt; 0) { //父进程
	printf(&quot;Whose your daddy?&quot;);
	...
	child_status = wait(pid);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;等待和终止进程&lt;/h3&gt;
&lt;p&gt;wait()系统调用是被父进程用来等待子进程的结束&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个子进程向父进程返回一个值,所以父进程必须接受这个值并处理&lt;/li&gt;
&lt;li&gt;wait()系统调用担任这个要求
&lt;ul&gt;
&lt;li&gt;它使父进程去睡眠来等待子进程的结束&lt;/li&gt;
&lt;li&gt;当一个子进程调用exit()的时候,操作系统解锁父进程,并且将通过exit()传递得到的返回值作为wait调用的一个结果(连同子进程的pid一起)如果这里没有子进程存活,wait()立刻返回&lt;/li&gt;
&lt;li&gt;当然,如果这里有为父进程的僵尸等待,wait()立即返回其中一个值(并且解除僵尸状态)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程结束执行之后,它调用exit()&lt;/li&gt;
&lt;li&gt;这个系统调用:
&lt;ul&gt;
&lt;li&gt;将这程序的&quot;结果&quot;作为一个参数&lt;/li&gt;
&lt;li&gt;关闭所有打开的文件,连接等等&lt;/li&gt;
&lt;li&gt;释放内存&lt;/li&gt;
&lt;li&gt;释放大部分支持进程的操作系统结构&lt;/li&gt;
&lt;li&gt;检查是否父进程是存活着的:
&lt;ul&gt;
&lt;li&gt;如果是的话,它保留结果的值直到父进程需要它;在这种情况里,进程没有真正死亡,但是它进入了僵尸状态&lt;/li&gt;
&lt;li&gt;如果没有,它释放所有的数据结构,这个进程死亡&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;清理所有等待的僵尸进程&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程终止是最终的垃圾收集(资源回收)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;第八章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第八章的主要内容是：调度算法(感觉清华这门课程前几章比较精彩,后续讲的有点混乱)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;h3&gt;上下文切换&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;切换CPU的当前任务, 从一个进程/线程到另一个&lt;/li&gt;
&lt;li&gt;保存当前进程/线程在PCB/TCB中的执行上下文(CPU状态)&lt;/li&gt;
&lt;li&gt;读取下一个进程/线程的上下文&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CPU调度&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;从就绪队列中挑选一个进程/线程作为CPU将要运行的下一个进程/线程&lt;/li&gt;
&lt;li&gt;调度程序: 挑选进程/线程的内核函数(通过一些调度策略)&lt;/li&gt;
&lt;li&gt;什么时候进行调度?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;内核运行调度程序的条件(满足一条即可)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一个进程从运行状态切换到等待状态&lt;/li&gt;
&lt;li&gt;一个进程被终结&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;不可抢占&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;调度程序必须等待事件结束&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;可以抢占&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;调度程序在中断被相应后执行&lt;/li&gt;
&lt;li&gt;当前的进程从运行切换到就绪, 或者一个进程从等待切换到就绪&lt;/li&gt;
&lt;li&gt;当前运行的进程可以被换出&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;调度原则&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;调度策略&lt;/p&gt;
&lt;p&gt;人们通常都需要&quot;更快&quot;的服务&lt;/p&gt;
&lt;p&gt;什么是更快?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传输文件时的高带宽&lt;/li&gt;
&lt;li&gt;玩游戏时的低延迟&lt;/li&gt;
&lt;li&gt;这两个因素是独立的&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;和水管类比&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;低延迟: 喝水的时候想要一打开水龙头水就流出来&lt;/li&gt;
&lt;li&gt;高带宽: 给游泳池充水时希望从水龙头里同时流出大量的水,并且不介意是否存在延迟&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们的目标:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;减少响应时间: 及时处理用户的输出并且尽快将输出提供给用户&lt;/li&gt;
&lt;li&gt;减少平均响应时间的波动: 在交互系统中,可预测性比高差异性低平均更重要&lt;/li&gt;
&lt;li&gt;增加吞吐量: 减少开销(操作系统开销,上下文切换);系统资源的高效率用(CPU,IO设备)&lt;/li&gt;
&lt;li&gt;减少等待时间: 减少每个进程的等待时间&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;程序执行模型&lt;/p&gt;
&lt;p&gt;执行模型 : 程序在CPU突发和IO中交替&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个调度决定都是关于在下一个CPU突发时将哪个工作交给CPU&lt;/li&gt;
&lt;li&gt;在时间分片机制下,线程可能在结束当前CPU突发前被迫放弃CPU&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;评价指标&lt;/p&gt;
&lt;p&gt;CPU使用率: CPU处于忙状态所占时间的百分比&lt;/p&gt;
&lt;p&gt;吞吐量: 在单位时间内完成的进程数量&lt;/p&gt;
&lt;p&gt;周转时间: 一个进程从初始化到结束,包括所有等待时间所花费的时间&lt;/p&gt;
&lt;p&gt;等待时间: 进程在就绪队列中的总时间&lt;/p&gt;
&lt;p&gt;响应时间: 从一个请求被提交到产生第一次相应所花费的总时间&lt;/p&gt;
&lt;p&gt;各指标在操作系统上的表现:&lt;/p&gt;
&lt;p&gt;低延迟调度增加了交互式表现(如果移动了鼠标,但是屏幕中的光标却没动,我们可能会重启电脑)&lt;/p&gt;
&lt;p&gt;操作系统需要保证低吞吐量不受影响(我想要结束长时间的编程,所以操作系统必须不时进行调度,即使存在许多交互任务)&lt;/p&gt;
&lt;p&gt;吞吐量是操作系统的计算带宽&lt;/p&gt;
&lt;p&gt;响应时间是操作系统的计算延迟&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;公平的目标&lt;/p&gt;
&lt;p&gt;举例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保证每个进程占用相同的CPU时间&lt;/li&gt;
&lt;li&gt;这公平嘛?如果一个用户比其他用户运行更多的进程怎么办&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;举例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保证每个进程都等待相同的时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;公平通常会增加平均响应时间&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;调度算法&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FCFS(先来先服务)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;First come, First Served&lt;/p&gt;
&lt;p&gt;如果进程在执行中阻塞,队列中的下一个会得到CPU&lt;/p&gt;
&lt;p&gt;优点: 简单&lt;/p&gt;
&lt;p&gt;缺点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;平均等待时间波动较大&lt;/li&gt;
&lt;li&gt;花费时间少的任务可能排在花费时间长的任务后面&lt;/li&gt;
&lt;li&gt;可能导致IO和CPU之间的重叠处理(CPU密集型进程会导致IO设备闲置时,IO密集型进程也在等待)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SPN(SJF) SRT(短进程优先(短作业优先)短剩余时间优先)[最优平均等待时间]&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Shortest Process Next(Shortest Job First) Shortest Remaining Time&lt;/p&gt;
&lt;p&gt;选择预测的完成时间来将任务入队&lt;/p&gt;
&lt;p&gt;可以是抢占的或者是不可抢占的&lt;/p&gt;
&lt;p&gt;可能导致饥饿&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;连续的短任务流会使场任务饥饿&lt;/li&gt;
&lt;li&gt;短任务可用时的任何场任务的CPU时间都会增加平均等待时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要预测未来&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;怎么预估下一个CPU突发的持续时间&lt;/li&gt;
&lt;li&gt;简单的解决: 询问用户&lt;/li&gt;
&lt;li&gt;如果用户欺骗就杀死进程&lt;/li&gt;
&lt;li&gt;如果不知道怎么办?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HRRN(最高响应比优先)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Highest Response Ratio Next&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Round Robin(轮循)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用时间切片和抢占来轮流执行任务&lt;/p&gt;
&lt;p&gt;在叫做量子(或者时间切片)的离散单元中分配处理器&lt;/p&gt;
&lt;p&gt;时间片结束时,切换到下一个准备好的进程&lt;/p&gt;
&lt;p&gt;花销: 额外的上下文切换&lt;/p&gt;
&lt;p&gt;时间量子太大:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;等待时间过长&lt;/li&gt;
&lt;li&gt;极限情况退化成FCFS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;时间量子太小:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;反应迅速&lt;/li&gt;
&lt;li&gt;吞吐量由于大量的上下文切换开销受到影响&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目标:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选择一个合适的时间量子&lt;/li&gt;
&lt;li&gt;经验规则: 维持上下文切换开销处于1%以内&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multilevel Feedback Queues(多级反馈队列)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;优先级队列中的轮循&lt;/p&gt;
&lt;p&gt;就绪队列被划分成独立的队列: 比如前台(交互),后台(批处理)&lt;/p&gt;
&lt;p&gt;每个队列拥有自己的调度策略: 比如前台(RR),后台(FCFS)&lt;/p&gt;
&lt;p&gt;调度必须在队列间进行:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;固定优先级: 先处理前台,然后处理后台;可能导致饥饿&lt;/li&gt;
&lt;li&gt;时间切片: 每个队列都得到一个确定的能够调度其进程的CPU总时间;比如80%使用RR的前台,20%使用FCFS的后台&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个进程可以在不同的队列中移动&lt;/p&gt;
&lt;p&gt;例如,n级优先级-优先级调度在所有级别中,RR在每个级别中&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;时间量子大小随优先级级别增加而增加&lt;/li&gt;
&lt;li&gt;如果任务在当前的时间量子中没有完成,则降到下一个优先级&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优点: CPU密集型任务的优先级下降很快;IO密集型任务停留在高优先级&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fair Share Scheduling(公平共享调度)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;FSS控制用户对系统资源的访问&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一些用户组比其他用户组更重要&lt;/li&gt;
&lt;li&gt;保证不重要的组无法垄断资源&lt;/li&gt;
&lt;li&gt;未使用的资源按照每个组所分配的资源的比例来分配&lt;/li&gt;
&lt;li&gt;没有达到资源使用率目标的组获得更高的优先级&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;评价方式&lt;/h2&gt;
&lt;p&gt;确定性建模: 确定一个工作量,然后计算每个算法的表现&lt;/p&gt;
&lt;p&gt;队列模型: 用来处理随机工作负载的数学方法&lt;/p&gt;
&lt;p&gt;实现/模拟: 建立一个允许算法运行实际数据的系统;最灵活,最具一般性&lt;/p&gt;
&lt;h2&gt;实时调度&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;实时系统&lt;/p&gt;
&lt;p&gt;定义: 正确性依赖于其时间和功能两方面的一个操作系统&lt;/p&gt;
&lt;p&gt;性能指标: 时间约束的及时性;速度和平均性能相对不重要&lt;/p&gt;
&lt;p&gt;主要特征: 时间约束的可预测性&lt;/p&gt;
&lt;p&gt;分类:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强实时系统: 需要在保证时间内完成重要的任务,必须完成&lt;/li&gt;
&lt;li&gt;弱实时系统: 要求重要的进程的优先级更高,尽量完成,并非必须&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;任务(工作单元): 一次计算,一次文件读取,一次信息传递等&lt;/p&gt;
&lt;p&gt;属性: 去的进展所需要的资源;定时参数.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;单调速率(RM)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最佳静态优先级调度&lt;/li&gt;
&lt;li&gt;通过周期安排优先级&lt;/li&gt;
&lt;li&gt;周期越短优先级越高&lt;/li&gt;
&lt;li&gt;执行周期最短的任务&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;截止日期最早优先(EDF)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最佳的动态优先级调度&lt;/li&gt;
&lt;li&gt;Deadline越早优先级越高&lt;/li&gt;
&lt;li&gt;执行Deadline最早的任务&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;多处理器调度&lt;/h2&gt;
&lt;p&gt;多处理器的CPU调度更复杂:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多个相同的单处理器组成一个多处理器&lt;/li&gt;
&lt;li&gt;优点: 复杂共享&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对称多处理器(SMP)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个处理器运行自己的调度程序&lt;/li&gt;
&lt;li&gt;需要在调度程序中同步&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;优先级反转&lt;/h2&gt;
&lt;p&gt;可以发生在任务基于优先级的可抢占的调度机制中&lt;/p&gt;
&lt;p&gt;当系统内的环境强制使高优先级任务等待低优先级任务时发生&lt;/p&gt;
&lt;h1&gt;第九章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第九章的主要内容是：同步&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;第一章到第八章内容, 到目前为止&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多道程序设计: 现代操作系统的重要特性&lt;/li&gt;
&lt;li&gt;并行很有用(为什么?) 提示: 多个并发实体: CPU IO 用户 等&lt;/li&gt;
&lt;li&gt;进程,线程: 操作系统抽象出来用于支持多道程序设计&lt;/li&gt;
&lt;li&gt;CPU调度: 实现多道程序设计的机制&lt;/li&gt;
&lt;li&gt;调度算法: 不同的策略&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;独立的线程:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不和其他线程共享资源或状态&lt;/li&gt;
&lt;li&gt;确定性: 输入状态决定结果&lt;/li&gt;
&lt;li&gt;可重现: 能够重现起始条件, IO&lt;/li&gt;
&lt;li&gt;调度顺序不重要&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;合作线程:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在多个线程中共享状态&lt;/li&gt;
&lt;li&gt;不确定性&lt;/li&gt;
&lt;li&gt;不可重现&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不确定性和不可重现意味着bug可能是间歇性发生的&lt;/p&gt;
&lt;p&gt;进程,线程;计算机,设备需要合作&lt;/p&gt;
&lt;p&gt;合作优点:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;共享资源
&lt;ul&gt;
&lt;li&gt;一台电脑,多个用户&lt;/li&gt;
&lt;li&gt;一个银行存款余额,多台ATM机&lt;/li&gt;
&lt;li&gt;嵌入式系统&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;加速
&lt;ul&gt;
&lt;li&gt;IO操作和计算可以重叠&lt;/li&gt;
&lt;li&gt;多处理器&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模块化
&lt;ul&gt;
&lt;li&gt;将大程序分解成小程序 gcc会调用cpp,cc1,cc2,as,ld&lt;/li&gt;
&lt;li&gt;使系统易于扩展&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;程序可以调用函数fork()来创建一个新的进程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作系统需要分配一个新的并且唯一的进程ID&lt;/li&gt;
&lt;li&gt;因此在内核中,这个系统调用会运行 new_pid = next_pid++;&lt;/li&gt;
&lt;li&gt;翻译成机器指令:
&lt;ul&gt;
&lt;li&gt;Load next_pid Reg1&lt;/li&gt;
&lt;li&gt;STORE Reg1 new_pid&lt;/li&gt;
&lt;li&gt;INC Reg1&lt;/li&gt;
&lt;li&gt;STORE Reg1 next_pid&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;假设两个进程并发执行&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果next_pid等于100, 那么其中一个进程得到的ID应该是100, 另一个进程的ID应该是101, next_pid应该增加到102&lt;/li&gt;
&lt;li&gt;可能在INC前进行了上下文切换, 最终导致两个进程的pid都是100,而next_pid也是101&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;无论多个线程的指令序列怎样交替执行,程序都必须正常工作&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多线程程序具有不确定性和不可重现的特点&lt;/li&gt;
&lt;li&gt;不经过专门设计,调试难度很高&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不确定性要求并行程序的正确性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先思考清楚问题,把程序的行为设计清楚&lt;/li&gt;
&lt;li&gt;切忌给予着手编写代码,碰到问题再调试&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;一些概念&lt;/h2&gt;
&lt;p&gt;前面的现象称为Race Condition(竞态条件)&lt;/p&gt;
&lt;p&gt;系统缺陷: 结果依赖于并发执行或者时间的顺序,时间&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不确定性&lt;/li&gt;
&lt;li&gt;不可重现&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;怎么样避免竞态?&lt;/p&gt;
&lt;p&gt;Atomic Operator(原子操作)&lt;/p&gt;
&lt;p&gt;原子操作是指一次不存在任何终端或者失败的执行&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;该执行成功结束&lt;/li&gt;
&lt;li&gt;或者根本没有执行&lt;/li&gt;
&lt;li&gt;并且不应发生任何部分执行的状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实际上操作往往不是原子的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有些看上去是原子操作,实际上不是&lt;/li&gt;
&lt;li&gt;连x++这样的简单语句,实际上是由三条指令构成的&lt;/li&gt;
&lt;li&gt;有时候甚至连单条假期指令都不是原子的(Pipeline,super-scalar,out-of-order,pape fault)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;临界区(Critical section)是指进程中的一段需要访问共享资源并且当另一个进程处于相应代码区域时便不会被执行的代码区域&lt;/p&gt;
&lt;p&gt;互斥(Mutual exclusion)是指当一个 进程处于临界区并访问共享资源时,没有其他进程会处于临界区并且访问任何相同的共享资源&lt;/p&gt;
&lt;p&gt;死锁(Dead lock)是指两个或以上进程,在相互等待完成特定任务,而最终没法将自身任务进行下去&lt;/p&gt;
&lt;p&gt;饥饿(Starvation)是指一个可执行的进程,被调度器持续忽略,以至于虽然处于可执行状态却不被执行&lt;/p&gt;
&lt;h2&gt;临界区&lt;/h2&gt;
&lt;p&gt;互斥: 同一时间临界区中最多存在一个线程&lt;/p&gt;
&lt;p&gt;Progress: 如果一个线程想要进入临界区,那么它最终会成功&lt;/p&gt;
&lt;p&gt;有限等待: 如果一个线程i处于入口区,那么在i的请求被接受之前,其他线程进入临界区的时间是有限制的&lt;/p&gt;
&lt;p&gt;无忙等待(可选): 如果一个进程在等待进入临界区,那么在它可以进入之前会被挂起&lt;/p&gt;
&lt;h2&gt;方法1:禁用硬件中断&lt;/h2&gt;
&lt;p&gt;没有中断,没有上下文切换,因此没有并发&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;硬件将中断处理延迟到中断被启用之后&lt;/li&gt;
&lt;li&gt;大多数现代计算机体系结构都提供指令来完成&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进入临界区&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;禁用中断&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;离开临界区&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开启中断&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一旦中断被禁用,线程就无法被停止&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;整个系统都会为你停下来&lt;/li&gt;
&lt;li&gt;可能导致其他线程处于饥饿状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要是临界区可以任意长怎么办?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;无法限制响应中断所需的时间(可能存在硬件影响)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要小心使用,适合于较小的操作&lt;/p&gt;
&lt;h2&gt;方法2:基于软件的解决方案&lt;/h2&gt;
&lt;p&gt;满足进程Pi和Pj之间互斥的经典的基于软件的解决方法(1981年)&lt;/p&gt;
&lt;p&gt;使用两个共享数据项&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;int turn; //指示该谁进入临界区&lt;/li&gt;
&lt;li&gt;bool flag[]; //指示进程是否准备好进入临界区&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进入临界区:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flag[i] = true;
turn = j;
while(flag[j] &amp;amp;&amp;amp; turn == j);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;退出临界区:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flag[i] = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实例:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;do{
	flag[i] = true;
	turn = j;
	while(flag[j] &amp;amp;&amp;amp; turn == j);
	CRITICAL SECTION
	flag[i] = false;
	REMAINDER SECTION
}while(true);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bakery 算法(N个进程的临界区)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进入临界区之前,进程接收一个数字&lt;/li&gt;
&lt;li&gt;得到的数字最小的进入临界区&lt;/li&gt;
&lt;li&gt;如果进程Pi和Pj收到相同的数字,那么如果i&amp;lt;j,Pi先进入临界区,否则Pj先进入临界区&lt;/li&gt;
&lt;li&gt;编号方案总是按照枚举的增加顺序生成数字&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dekker算法(1965): 第一个针对双线程例子的正确解决方案&lt;/p&gt;
&lt;p&gt;Bakery算法(1979): 针对n线程的临界区问题解决方案&lt;/p&gt;
&lt;p&gt;复杂: 需要两个进程的共享数据项&lt;/p&gt;
&lt;p&gt;需要忙等待: 浪费CPU时间&lt;/p&gt;
&lt;p&gt;没有硬件保证的情况下无真正的软件解决方案: Perterson算法需要原子的LOAD和STORE指令&lt;/p&gt;
&lt;h2&gt;方法3:更高级的抽象&lt;/h2&gt;
&lt;p&gt;硬件提供了一些原语&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;像中断禁用, 原子操作指令等&lt;/li&gt;
&lt;li&gt;大多数现代体系结构都这样&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;操作系统提供更高级的编程抽象来简化并行编程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例如,锁,信号量&lt;/li&gt;
&lt;li&gt;从硬件原语中构建&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;锁是一个抽象的数据结构&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个二进制状态(锁定,解锁),两种方法&lt;/li&gt;
&lt;li&gt;Lock::Acquire() 锁被释放前一直等待,然后得到锁&lt;/li&gt;
&lt;li&gt;Lock::Release() 锁释放,唤醒任何等待的进程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用锁来编写临界区&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前面的例子变得简单起来:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lock_next_pid-&amp;gt;Acquire();
new_pid = next_pid++;
lock_next_pid-&amp;gt;Release();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大多数现代体系结构都提供特殊的原子操作指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过特殊的内存访问电路&lt;/li&gt;
&lt;li&gt;针对单处理器和多处理器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Test-and-Set 测试和置位&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从内存中读取值&lt;/li&gt;
&lt;li&gt;测试该值是否为1(然后返回真或假)&lt;/li&gt;
&lt;li&gt;内存值设置为1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;交换&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;交换内存中的两个值&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;bool TestandSet(bool *target){
		bool rv = *target;
		*target = true;
		return rv;
}

void Exchange(bool *a, bool *b){
		bool tmp = *a;
		*a = *b;
		*b = tmp;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;总结&lt;/p&gt;
&lt;p&gt;锁是更高等级的编程抽象&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥可以使用锁来实现&lt;/li&gt;
&lt;li&gt;通常需要一定等级的硬件支持&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常用的三种实现方法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;禁用中断(仅限于单处理器)&lt;/li&gt;
&lt;li&gt;软件方法(复杂)&lt;/li&gt;
&lt;li&gt;原子操作指令(单处理器或多处理器均可)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可选的实现内容:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有忙等待&lt;/li&gt;
&lt;li&gt;无忙等待&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;第十章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第十章的主要内容是：信号量和管程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;信号量&lt;/h2&gt;
&lt;p&gt;信号量的抽象数据类型&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个整形(sem),具有两个原子操作&lt;/li&gt;
&lt;li&gt;P(): sem减一,如果sem&amp;lt;0,等待,否则继续&lt;/li&gt;
&lt;li&gt;V(): sem加一,如果sem≤0,唤醒一个等待的P&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;信号量是整数&lt;/p&gt;
&lt;p&gt;信号量是被保护的变量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化完成后,唯一改变一个信号量的值的办法是通过P()和V()&lt;/li&gt;
&lt;li&gt;操作必须是原子&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;P()能够阻塞,V()不会阻塞&lt;/p&gt;
&lt;p&gt;我们假定信号量是公平的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没有线程被阻塞在P()仍然堵塞如果V()被无限频繁调用(在同一个信号量)&lt;/li&gt;
&lt;li&gt;在实践中,FIFO经常被使用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两个类型信号量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;二进制信号量: 可以是0或1&lt;/li&gt;
&lt;li&gt;计数信号量: 可以取任何非负数&lt;/li&gt;
&lt;li&gt;两者相互表现(给定一个可以实现另一个)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;信号量可以用在2个方面&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥&lt;/li&gt;
&lt;li&gt;条件同步(调度约束——一个线程等待另一个线程的事情发生)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;信号量使用&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;用二进制信号量实现的互斥&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mutex = new Semaphore(1);

mutex-&amp;gt;P();
...
mutex-&amp;gt;V();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用二进制信号量实现的调度约束&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;condition = new Semaphore(0);

//Thread A
...
condition-&amp;gt;P(); //等待线程B某一些指令完成之后再继续运行,在此阻塞
...

//Thread B
...
condition-&amp;gt;V(); //信号量增加唤醒线程A
...
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个线程等待另一个线程处理事情&lt;/p&gt;
&lt;p&gt;比如生产东西或消费东西(生产者消费者模式),互斥(锁机制)是不够的&lt;/p&gt;
&lt;p&gt;有界缓冲区的生产者-消费者问题&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个或者多个生产者产生数据将数据放在一个缓冲区里&lt;/li&gt;
&lt;li&gt;单个消费者每次从缓冲区取出数据&lt;/li&gt;
&lt;li&gt;在任何一个时间只有一个生产者或消费者可以访问该缓冲区&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正确性要求&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在任何一个时间只能有一个线程操作缓冲区(互斥)&lt;/li&gt;
&lt;li&gt;当缓冲区为空时,消费者必须等待生产者(调度,同步约束)&lt;/li&gt;
&lt;li&gt;当缓存区满,生产者必须等待消费者(调度,同步约束)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个约束用一个单独的信号量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;二进制信号量互斥&lt;/li&gt;
&lt;li&gt;一般信号量 fullBuffers&lt;/li&gt;
&lt;li&gt;一般信号了 emptyBuffers&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class BoundedBuffer{
		mutex = new Semaphore(1);
		fullBuffers = new Semaphore(0);   //说明缓冲区初始为空
 		emptyBuffers = new Semaphore(n);  //同时可以有n个生产者来生产
};

BoundedBuffer::Deposit(c){
		emptyBuffers-&amp;gt;P();
		mutex-&amp;gt;P();
		Add c to the buffer;
		mutex-&amp;gt;V();
		fullBuffers-&amp;gt;V();
}

BoundedBuffer::Remove(c){
		fullBuffers-&amp;gt;P();
		mutex-&amp;gt;P();
		Remove c from buffer;
		mutex-&amp;gt;V();
		emptyBuffers-&amp;gt;V();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;信号量实现&lt;/h2&gt;
&lt;p&gt;使用硬件原语&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;禁用中断&lt;/li&gt;
&lt;li&gt;原子指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;类似锁&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;禁用中断&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Semaphore{
		int sem;
		WaitQueue q;
};

Semaphore::P(){
		--sem;
		if(sem &amp;lt; 0){
				Add this thread t to q;
				block(p);
		}
};

Semaphore::V(){
		++sem;
		if(sem &amp;lt;= 0){
				Remove a thread t from q;
				wakeup(t);
		}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;信号量的双用途&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥和条件同步&lt;/li&gt;
&lt;li&gt;但等待条件是独立的互斥&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;读,开发代码比较困难&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序员必须非常精通信号量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;容易出错&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用的信号量已经被另一个线程占用&lt;/li&gt;
&lt;li&gt;忘记释放信号量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不能够处理死锁问题&lt;/p&gt;
&lt;h2&gt;管程&lt;/h2&gt;
&lt;p&gt;目的: 分离互斥和条件同步的关注&lt;/p&gt;
&lt;p&gt;什么是管程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个锁: 指定临界区&lt;/li&gt;
&lt;li&gt;0或者多个条件变量: 等待,通知信号量用于管程并发访问共享数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一般方法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;收集在对象,模块中的相关共享数据&lt;/li&gt;
&lt;li&gt;定义方法来访问共享数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lock&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lock::Acquire() 等待直到锁可用,然后抢占锁&lt;/li&gt;
&lt;li&gt;Lock::Release() 释放锁,唤醒等待者如果有&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Condition Variable&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;允许等待状态进入临界区
&lt;ul&gt;
&lt;li&gt;允许处于等待(睡眠)的线程进入临界区&lt;/li&gt;
&lt;li&gt;某个时刻原子释放锁进入睡眠&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Wait() operation
&lt;ul&gt;
&lt;li&gt;释放锁,睡眠,重新获得锁放回&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Signal() operation(or broadcast() operation)
&lt;ul&gt;
&lt;li&gt;唤醒等待者(或者所有等待者),如果有&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要维持每个条件队列&lt;/li&gt;
&lt;li&gt;线程等待的条件等待signal()&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Condition{
		int numWaiting = 0;
		WaitQueue q;
};

Condition::Wait(lock){
		numWaiting++;
		Add this thread t to q;
		release(lock);
		schedule(); //need mutex
		require(lock);
}

Condition::Signal(){
		if(numWaiting &amp;gt; 0){
				Remove a thread t from q;
				wakeup(t); //need mutex
				numWaiting--;
		}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;管程解决生产者-消费者问题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BoundedBuffer{
		Lock lock;
		int count = 0;  //buffer 为空
		Condition notFull, notEmpty;
};

BoundedBuffer::Deposit(c){
		lock-&amp;gt;Acquire();    //管程的定义:只有一个线程能够进入管程
		while(count == n)
				notFull.Wait(&amp;amp;lock); //释放前面的锁
		Add c to the buffer;
		count++;
		notEmpty.Signal();
		lock-&amp;gt;Release();
}

BoundedBuffer::Remove(c){
		lock-&amp;gt;Acquire();
		while(count == 0)
				notEmpty.Wait(&amp;amp;lock);
		Remove c from buffer;
		count--;
		notFull.Signal();
		lock-&amp;gt;Release();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开发,调试并行程序很难&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非确定性的交叉指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同步结构&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;锁: 互斥&lt;/li&gt;
&lt;li&gt;条件变量: 有条件的同步&lt;/li&gt;
&lt;li&gt;其他原语: 信号量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;怎么样有效地使用这些结构&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;制定并遵循严格的程序设计风格,策略&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;经典同步问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;读者-写者问题&lt;/p&gt;
&lt;p&gt;动机: 共享数据的访问&lt;/p&gt;
&lt;p&gt;两种类型的使用者: 读者(不修改数据) 写者(读取和修改数据)&lt;/p&gt;
&lt;p&gt;问题的约束:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;允许同一时间有多个读者,但在任何时候只有一个写者&lt;/li&gt;
&lt;li&gt;当没有写者时,读者才能访问数据&lt;/li&gt;
&lt;li&gt;当没有读者和写者时,写者才能访问数据&lt;/li&gt;
&lt;li&gt;在任何时候只能有一个线程可以操作共享变量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多个并发进程的数据集共享&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;读者: 只读数据集;他们不执行任何更新&lt;/li&gt;
&lt;li&gt;写者: 可以读取和写入&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;共享数据&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据集&lt;/li&gt;
&lt;li&gt;信号量CountMutex初始化为1&lt;/li&gt;
&lt;li&gt;信号量WriteMutex初始化为1&lt;/li&gt;
&lt;li&gt;整数Rcount初始化为0(当前读者个数)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;读者优先设计&lt;/p&gt;
&lt;p&gt;只要有一个读者处于活动状态, 后来的读者都会被接纳.如果读者源源不断的出现,那么写者使用处于阻塞状态.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//信号量实现
//writer
sem_wait(WriteMutex);
write;
sem_post(WriteMutex);

//reader
sem_wait(CountMutex);
if(Rcount == 0)
		sem_wait(WriteMutex); //确保后续不会有写者进入
++Rcount;
read;
--Rcount;
if(Rcount == 0)
		sem_post(WriteMutex); //全部读者全部离开才能唤醒写者
sem_post(CountMutex);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写者优先设计&lt;/p&gt;
&lt;p&gt;一旦写者就绪,那么写者会尽可能的执行写操作.如果写者源源不断的出现的话,那么读者就始终处于阻塞状态.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//writer
Database::Write(){
		Wait until readers/writers;
		write database;
		check out - wake up waiting readers/writers;
}
//reader
Database::Read(){
		Wait until no writers;
		read database;
		check out - wake up waiting writers;
}

//管程实现
AR = 0; // # of active readers
AW = 0; // # of active writers
WR = 0; // # of waiting readers
WW = 0; // # of waiting writers
Condition okToRead;
Condition okToWrite;
Lock lock;
//writer
Public Database::Write(){
		//Wait until no readers/writers;
		StartWrite();
		write database;
		//check out - wake up waiting readers/writers;
		DoneWrite();
}

Private Database::StartWrite(){
		lock.Acquire();
		while((AW + AR) &amp;gt; 0){
				WW++;
				okToWrite.wait(&amp;amp;lock);
				WW--;		
		}
		AW++;
		lock.Release();
}

Private Database::DoneWrite(){
		lock.Acquire();
		AW--;
		if(WW &amp;gt; 0){
				okToWrite.signal();
		}
		else if(WR &amp;gt; 0){
				okToRead.broadcast(); //唤醒所有reader 
		}
		lock.Release();
}

//reader
Public Database::Read(){
		//Wait until no writers;
		StartRead();
		read database;
		//check out - wake up waiting writers;
		DoneRead();
}

Private Database::StartRead(){
		lock.Acquire();
		while(AW + WW &amp;gt; 0){    //关注等待的writer,体现出写者优先
				WR++;
				okToRead.wait(&amp;amp;lock);
				WR--;
		}
		AR++;
		lock.Release();
}

private Database::DoneRead(){
		lock.Acquire();
		AR--;
		if(AR == 0 &amp;amp;&amp;amp; WW &amp;gt; 0){  //只有读者全部没有了,才需要唤醒
				okToWrite.signal();
		}
		lock.Release();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;哲学家就餐问题(学习自 &lt;a href=&quot;http://github.com/cyc2018&quot;&gt;github.com/cyc2018&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;共享数据:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bowl of rice(data set)&lt;/li&gt;
&lt;li&gt;Semaphone fork [5] initialized to 1&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;#define N 5
#define LEFT (i + N - 1) % N // 左邻居
#define RIGHT (i + 1) % N    // 右邻居
#define THINKING 0
#define HUNGRY   1
#define EATING   2
typedef int semaphore;
int state[N];                // 跟踪每个哲学家的状态
semaphore mutex = 1;         // 临界区的互斥，临界区是 state 数组，对其修改需要互斥
semaphore s[N];              // 每个哲学家一个信号量

void philosopher(int i) {
    while(TRUE) {
        think(i);
        take_two(i);
        eat(i);
        put_two(i);
    }
}

void take_two(int i) {
    down(&amp;amp;mutex);
    state[i] = HUNGRY;
    check(i);
    up(&amp;amp;mutex);
    down(&amp;amp;s[i]); // 只有收到通知之后才可以开始吃，否则会一直等下去
}

void put_two(i) {
    down(&amp;amp;mutex);
    state[i] = THINKING;
    check(LEFT); // 尝试通知左右邻居，自己吃完了，你们可以开始吃了
    check(RIGHT);
    up(&amp;amp;mutex);
}

void eat(int i) {
    down(&amp;amp;mutex);
    state[i] = EATING;
    up(&amp;amp;mutex);
}

// 检查两个邻居是否都没有用餐，如果是的话，就 up(&amp;amp;s[i])，使得 down(&amp;amp;s[i]) 能够得到通知并继续执行
void check(i) {         
    if(state[i] == HUNGRY &amp;amp;&amp;amp; state[LEFT] != EATING &amp;amp;&amp;amp; state[RIGHT] !=EATING) {
        state[i] = EATING;
        up(&amp;amp;s[i]);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;第十一章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第十一章的主要内容是：死锁和进程通信&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;死锁问题&lt;/h2&gt;
&lt;p&gt;一组阻塞的进程持有一种资源等待获取另一个进程所占有的一个资源&lt;/p&gt;
&lt;p&gt;示例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统有2个磁带驱动器&lt;/li&gt;
&lt;li&gt;P1和P2各有一个,都需要另外一个&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;系统模型&lt;/h2&gt;
&lt;p&gt;资源类型R1,R2,..,Rm(CPU, memory space, IO devices)&lt;/p&gt;
&lt;p&gt;每个资源类型Ri有Wi个实例.&lt;/p&gt;
&lt;p&gt;每个进程使用资源如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;require,get ← free resource&lt;/li&gt;
&lt;li&gt;use,hold    ← requested,used resource&lt;/li&gt;
&lt;li&gt;release     ← free resource&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可重复使用的资源&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在一个时间只能有一个进程使用且不能被删除&lt;/li&gt;
&lt;li&gt;进程获得资源,后来释放由其他进程重用&lt;/li&gt;
&lt;li&gt;处理器,IO通道,主和副存储器,设备和数据结构,如文件,数据库和信号量&lt;/li&gt;
&lt;li&gt;如果每个进程拥有一个资源并请求其他资源,死锁可能发生&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用资源&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建和销毁&lt;/li&gt;
&lt;li&gt;在IO缓存区的中断,信号,消息,信息&lt;/li&gt;
&lt;li&gt;如果接收消息阻塞可能会发生死锁&lt;/li&gt;
&lt;li&gt;可能少见的组合事件会引起死锁&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;资源分配图&lt;/p&gt;
&lt;p&gt;一组顶点V和边E的集合&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;V有两种类型:
&lt;ul&gt;
&lt;li&gt;P={P1,P2,...,Pn},集合包括系统中的所有进程&lt;/li&gt;
&lt;li&gt;R={R1,R2,...,Rm},集合包括系统中的所有资源类型&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;requesting,claiming edge - directed edge Pi → Rj&lt;/li&gt;
&lt;li&gt;assignment,holding  edge - directed edge Rj → Pi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基本情况&lt;/p&gt;
&lt;p&gt;如果图中不包含循环:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没有死锁&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果图中包含循环:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果每个资源类只有一个实例,那么死锁&lt;/li&gt;
&lt;li&gt;如果每个资源类有几个实例,可能死锁&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;死锁特征&lt;/h2&gt;
&lt;p&gt;死锁出现一定会出现以下四个条件,但是出现以下四个条件不一定死锁:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥: 在一个时间只能有一个进程使用资源&lt;/li&gt;
&lt;li&gt;持有并等待: 进程保持至少一个资源正在等待获取其他进程持有的额外资源&lt;/li&gt;
&lt;li&gt;无抢占: 一个资源只能被进程资源释放,进程已经完成了它的任务之后&lt;/li&gt;
&lt;li&gt;循环等待: 存在等待进程集合{P0,P1,...,Pn},P0正在等待P1所占用的资源,P1正在等待P2占用的资源...Pn-1在等待Pn的资源,Pn正在等待P0所占用的资源&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;死锁处理方法&lt;/h2&gt;
&lt;p&gt;常见方法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确保系统永远不会进入死锁状态&lt;/li&gt;
&lt;li&gt;运行系统进入死锁状态,然后恢复.&lt;/li&gt;
&lt;li&gt;忽略这个问题,假装系统中从来没有发生死锁,用于大多数操作系统,包括UNIX&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Deadlock Prevention    预防&lt;/h3&gt;
&lt;p&gt;限制申请方式&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥 - 共享资源不是必须的,必须占用非共享资源&lt;/li&gt;
&lt;li&gt;占用并等待 - 必须保证当一个进程请求的资源,它不持有任何其他资源
&lt;ul&gt;
&lt;li&gt;需要进程请求并分配其所有资源,它开始执行之前或允许进程请求资源仅当进程没有资源&lt;/li&gt;
&lt;li&gt;资源利用率低,可能发生饥饿&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;无抢占 -
&lt;ul&gt;
&lt;li&gt;如果进程占有某些资源,并请求其他不能被立即分配的资源,则释放当前正占有的资源&lt;/li&gt;
&lt;li&gt;被抢占资源添加到资源列表中&lt;/li&gt;
&lt;li&gt;只有当它能够获得旧的资源以及它请求新的资源,进程可以得到执行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;循环等待 - 对所有资源类型进行排序,并要求每个进程按照资源的顺序进行申请&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Deadlock Avoidance     避免&lt;/h3&gt;
&lt;p&gt;需要系统具有一些额外的先验信息提供&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最简单和最有效的模式是要求每个进程声明它可能需要的每个类型资源的最大数目&lt;/li&gt;
&lt;li&gt;资源的分配状态是通过限定提供与分配的资源数量,和进程的最大需求&lt;/li&gt;
&lt;li&gt;死锁避免算法动态检查的资源分配状态,以确保永远不会有一个环形等待状态&lt;/li&gt;
&lt;li&gt;当一个进程请求可用资源,系统必须判断立即分配是否能使系统处于安全状态&lt;/li&gt;
&lt;li&gt;系统处于安全状态指: 针对所有进程,存在安全序列&lt;/li&gt;
&lt;li&gt;序列&amp;lt;P1,P2,...,Pn&amp;gt;是安全的: 针对每个Pi,Pi要求的资源能够由当前可用的资源+所有的Pj持有的资源来满足,其中j&amp;lt;i.
&lt;ul&gt;
&lt;li&gt;如果Pi资源的需求不是立即可用,那么Pi可以等到所有Pj完成&lt;/li&gt;
&lt;li&gt;当Pi完成后,Pi+1可以得到所需要的资源,执行,返回所分配的资源,并终止.&lt;/li&gt;
&lt;li&gt;用同样的方法,Pi+2,Pi+3和Pn能获得其所需的资源.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果系统处于安全状态→无死锁&lt;/li&gt;
&lt;li&gt;如果系统处于不安全状态→可能死锁&lt;/li&gt;
&lt;li&gt;避免死锁: 确保系统永远不会进入不安全状态&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Deadlock Detection     检测&lt;/h3&gt;
&lt;p&gt;每个资源类型单一实例&lt;/p&gt;
&lt;p&gt;Maintain wait-for graph&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;节点是进程&lt;/li&gt;
&lt;li&gt;Pi→Pj: Pi等待Pj&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;定期调用检测算法来搜索图中是否存在循环&lt;/p&gt;
&lt;p&gt;算法需要n^2次操作,n是图中顶点的数目&lt;/p&gt;
&lt;p&gt;数据结构:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Available: 长度为M的向量表示每种类型可用资源的数量&lt;/li&gt;
&lt;li&gt;Allocation: 一个nxm矩阵定义了当前分配给各个进程每种类型资源的数量,如果Alocation[i, j] = k, 进程Pi拥有资源Rj的k个实例&lt;/li&gt;
&lt;li&gt;Request: 一个nxm矩阵表示各进程的当前请求.如果Request[i, j] = k,表示进程Pi请求k个资源Pj的实例&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;具体算法(跳过了,看视频)&lt;/p&gt;
&lt;p&gt;检查算法使用&lt;/p&gt;
&lt;p&gt;何时,使用什么样的频率来检测依赖于:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;死锁多久可能会发生?&lt;/li&gt;
&lt;li&gt;多少进程需要被回滚? one for each disjoint cycle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果检测算法多次被调用,有可能是资源图有多个循环,所以我们无法分辨出多个可能死锁进程中的哪些&quot;造成&quot;死锁&lt;/p&gt;
&lt;h3&gt;Recovery from Deadlock 恢复&lt;/h3&gt;
&lt;p&gt;终止所有的死锁进程&lt;/p&gt;
&lt;p&gt;在一个时间内终止一个进程直到死锁消除&lt;/p&gt;
&lt;p&gt;终止进程的顺序应该是:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程的优先级&lt;/li&gt;
&lt;li&gt;进程运行了多久以及需要多少时间才能完成&lt;/li&gt;
&lt;li&gt;进程占用的资源&lt;/li&gt;
&lt;li&gt;进程完成需要的资源&lt;/li&gt;
&lt;li&gt;多少进程需要被终止&lt;/li&gt;
&lt;li&gt;进程是交互还是批处理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选择一个受孩子 - 最小的成本&lt;/p&gt;
&lt;p&gt;回滚 - 返回到一些安全状态,重启进程到安全状态&lt;/p&gt;
&lt;p&gt;饥饿 - 同一进程可能一直被选作受害者,包括回滚的数量&lt;/p&gt;
&lt;h2&gt;IPC&lt;/h2&gt;
&lt;h3&gt;概述&lt;/h3&gt;
&lt;p&gt;进程通信的机制及同步&lt;/p&gt;
&lt;p&gt;不使用共享变量的进程通信&lt;/p&gt;
&lt;p&gt;IPC facility 提供2个操作:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;send(message) - 消息大小固定或者可变&lt;/li&gt;
&lt;li&gt;receive(message)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果P和Q想通信,需要:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在它们之间建立通信链路&lt;/li&gt;
&lt;li&gt;通过send/recevie交换消息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通信链路的实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;物理(例如,共享内存,硬件总线)&lt;/li&gt;
&lt;li&gt;逻辑(例如,逻辑属性)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;直接通信&lt;/h3&gt;
&lt;p&gt;进程必须正确的命名对方:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;send(P, message) - 发送消息到进程P&lt;/li&gt;
&lt;li&gt;receive(Q, message) - 从进程Q接收信息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通信链路的属性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自动建立链路&lt;/li&gt;
&lt;li&gt;一条链路恰好对应一对通信进程&lt;/li&gt;
&lt;li&gt;每对进程之间只有一个链路存在&lt;/li&gt;
&lt;li&gt;链路可以是单向的,但通常是双向的&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;间接通信&lt;/h3&gt;
&lt;p&gt;定向从消息队列接收消息&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个消息对垒都有一个唯一的ID&lt;/li&gt;
&lt;li&gt;只有它们共享了一个消息队列,进程才能够通信&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通信链路的属性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只有进程共享一个共同的消息队列,才建立链路&lt;/li&gt;
&lt;li&gt;链接可以与许多进程相关联&lt;/li&gt;
&lt;li&gt;每对进程可以共享多个通信链路&lt;/li&gt;
&lt;li&gt;链接可以是单向或者双向&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;操作&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建一个新的消息队列&lt;/li&gt;
&lt;li&gt;通过消息队列发送和接收消息&lt;/li&gt;
&lt;li&gt;销毁消息队列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;原语的定义如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;send(A, message)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;receive(A, message)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通信链路缓冲&lt;/p&gt;
&lt;p&gt;通信链路缓存大小:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;0容量 - 0 message : 发送方必须等待接收方&lt;/li&gt;
&lt;li&gt;有限容量 - n messages的有限长度 : 发送方必须等待,如果队列满&lt;/li&gt;
&lt;li&gt;无限容量 - 无限长度 : 发送方不需要等待&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;信号&lt;/h3&gt;
&lt;p&gt;信号Signal&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;软件中断通知事件处理&lt;/li&gt;
&lt;li&gt;Examples: SIGFPE, SIGKILL, SIGUSRI, SIGSTOP, SIGCONT&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接收到信号时会发生什么?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;catch:  指定信号处理函数被调用&lt;/li&gt;
&lt;li&gt;ignore: 依靠操作系统的默认操作(abort, memory dump, suspend or resume process)&lt;/li&gt;
&lt;li&gt;mask:   闭塞信号因此不会传送(可能是暂时的,当处理同样类型的信号)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不足:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不能传输要交换的任何数据&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;管道&lt;/h3&gt;
&lt;p&gt;数据交换&lt;/p&gt;
&lt;p&gt;子进程从父进程继承文件描述符(0 stdin, 1 stdout, 2 stderr)&lt;/p&gt;
&lt;p&gt;进程不知道(或不关心)从键盘,文件,程序读取或写入到终端,文件,程序.&lt;/p&gt;
&lt;p&gt;例如: $ ls | more (两个进程, 管道是缓存,对于ls来说是stdout,对于more来说是stdin )&lt;/p&gt;
&lt;h3&gt;消息队列&lt;/h3&gt;
&lt;p&gt;消息队列按FIFO来管理消息&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;message: 作为一个字节序列存储&lt;/li&gt;
&lt;li&gt;message queues: 消息数组&lt;/li&gt;
&lt;li&gt;FIFO &amp;amp;  FILO configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;共享内存&lt;/h3&gt;
&lt;p&gt;进程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个进程都有私有地址空间&lt;/li&gt;
&lt;li&gt;在每个地址空间内,明确地设置了共享内存段&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;快速,方便地共享数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不足&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必须同步数据访问&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最快的方法&lt;/p&gt;
&lt;p&gt;一个进程写另一个进程立即可见&lt;/p&gt;
&lt;p&gt;没有系统调用干预&lt;/p&gt;
&lt;p&gt;没有数据复制&lt;/p&gt;
&lt;p&gt;不提供同步&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Socket&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;第十二章&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;第十二章的主要内容是：文件管理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;基本概念&lt;/h2&gt;
&lt;h3&gt;文件系统和文件&lt;/h3&gt;
&lt;p&gt;文件系统: 一种用于持久性存储的系统抽象&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在存储上: 组织,控制,导航,访问和检索数据&lt;/li&gt;
&lt;li&gt;在大多数计算机系统包含文件系统&lt;/li&gt;
&lt;li&gt;个人电脑,服务器,笔记本电脑&lt;/li&gt;
&lt;li&gt;ipod,tivo,机顶盒,手机,电脑&lt;/li&gt;
&lt;li&gt;google可能也是由一个文件系统构成的&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文件: 文件系统中的一个单元的相关数据在操作系统中的抽象&lt;/p&gt;
&lt;p&gt;文件系统的功能:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分配文件磁盘空间
&lt;ul&gt;
&lt;li&gt;管理文件块(哪一块属于哪一个文件)&lt;/li&gt;
&lt;li&gt;管理空闲空间(哪一块是空闲的)&lt;/li&gt;
&lt;li&gt;分配算法(策略)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管理文件集合
&lt;ul&gt;
&lt;li&gt;定位文件及其内容&lt;/li&gt;
&lt;li&gt;命名: 通过名字找到文件的接口&lt;/li&gt;
&lt;li&gt;最常见: 分层文件系统&lt;/li&gt;
&lt;li&gt;文件系统类型(组织文件的不同方式)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;提供的便利及特征
&lt;ul&gt;
&lt;li&gt;保护: 分层来保护数据安全&lt;/li&gt;
&lt;li&gt;可靠性,持久性: 保持文件的持久即使发生崩溃,媒体错误,攻击等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文件和块:&lt;/p&gt;
&lt;p&gt;文件属性: 名称,类型,位置,大小,保护,创建者,创建时间,最久修改时间...&lt;/p&gt;
&lt;p&gt;文件头: 在存储元数据中保存了每个文件的信息,保存文件的属性,跟踪哪一块存储块属于逻辑上文件结构的哪个偏移&lt;/p&gt;
&lt;h3&gt;文件描述符&lt;/h3&gt;
&lt;p&gt;文件使用模式:&lt;/p&gt;
&lt;p&gt;使用程序必须在使用前先&quot;打开&quot;文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;f = open(name, flag);
...
... = read(f, ...);
...
close(f);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;内核跟踪每个进程打开的文件:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作系统为每个进程维护一个打开文件表&lt;/li&gt;
&lt;li&gt;一个打开文件描述符是这个表中的索引&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要元数据来管理打开文件:&lt;/p&gt;
&lt;p&gt;文件指针: 指向最近的一次读写位置,每个打开了这个文件的进程都这个指针&lt;/p&gt;
&lt;p&gt;文件打开计数: 记录文件打开的次数 - 当最后一个进程关闭了文件时,允许将其从打开文件表中移除&lt;/p&gt;
&lt;p&gt;文件磁盘位置: 缓存数据访问信息&lt;/p&gt;
&lt;p&gt;访问权限: 每个程序访问模式信息&lt;/p&gt;
&lt;p&gt;用户视图: 持久的数据结构&lt;/p&gt;
&lt;p&gt;系统访问接口:&lt;/p&gt;
&lt;p&gt;字节的集合(UNIX)&lt;/p&gt;
&lt;p&gt;系统不会关心你想存储在磁盘上的任何的数据结构&lt;/p&gt;
&lt;p&gt;操作系统内部视角:&lt;/p&gt;
&lt;p&gt;块的集合(块是逻辑转换单元,而扇区是物理转换单元)&lt;/p&gt;
&lt;p&gt;块大小&amp;lt;&amp;gt; 扇区大小: 在UNIX中, 块的大小是 4KB&lt;/p&gt;
&lt;p&gt;当用户说: 给我2-12字节空间时会发生什么?&lt;/p&gt;
&lt;p&gt;获取字节所在的快&lt;/p&gt;
&lt;p&gt;返回快内对应部分&lt;/p&gt;
&lt;p&gt;如果要写2-12字节?&lt;/p&gt;
&lt;p&gt;获取块&lt;/p&gt;
&lt;p&gt;修改块内对应部分&lt;/p&gt;
&lt;p&gt;写回块&lt;/p&gt;
&lt;p&gt;在文件系统中的所有操作都是在整个块空间上进行的: &lt;code&gt;getc()&lt;/code&gt; &lt;code&gt;putc()&lt;/code&gt; 即使每次只访问1字节的数据,也会缓存目标数据4096字节(一个磁盘块)&lt;/p&gt;
&lt;p&gt;用户怎么访问文件: 在系统层面需要知道用户的访问模式&lt;/p&gt;
&lt;p&gt;顺序访问: 按字节依次读取(几乎所有的访问都是这种方式)&lt;/p&gt;
&lt;p&gt;随机访问: 从中间读写(不常用,但是仍然重要,如: 虚拟内存支持文件,内存页存储在文件中;更加快速,不希望获取文件中间的内容的时候也必须先获取块内所有字节)&lt;/p&gt;
&lt;p&gt;内容访问: 通过特征&lt;/p&gt;
&lt;p&gt;文件内部结构:&lt;/p&gt;
&lt;p&gt;无结构: 单词,比特的队列&lt;/p&gt;
&lt;p&gt;简单记录结构: 列,固定长度,可变长度&lt;/p&gt;
&lt;p&gt;复杂结构: 格式化的文档(word, PDF), 可执行文件, ...&lt;/p&gt;
&lt;p&gt;多用户系统中的文件共享是很必要的&lt;/p&gt;
&lt;p&gt;访问控制:&lt;/p&gt;
&lt;p&gt;谁能够获得哪些文件的哪些访问权限&lt;/p&gt;
&lt;p&gt;访问模式: 读,写,执行,删除,列举等&lt;/p&gt;
&lt;p&gt;文件访问控制列表(ACL):&lt;/p&gt;
&lt;p&gt;&amp;lt;文件实体, 权限&amp;gt;&lt;/p&gt;
&lt;p&gt;UNIX模式:&lt;/p&gt;
&lt;p&gt;&amp;lt;用户|组|所有人,读|写|可执行&amp;gt;&lt;/p&gt;
&lt;p&gt;用户ID识别用户,表明每个用户所允许的权限及保护模式&lt;/p&gt;
&lt;p&gt;组ID允许用户组成组,并指定了组访问权限&lt;/p&gt;
&lt;p&gt;指定多用户,客户如何同时访问共享文件:&lt;/p&gt;
&lt;p&gt;和过程同步算法相似&lt;/p&gt;
&lt;p&gt;因磁盘IO和网络延迟而设计简单&lt;/p&gt;
&lt;p&gt;UNIX文件系统(UFS)语义:&lt;/p&gt;
&lt;p&gt;对打开文件的写入内容立即对其他打开同一文件的其他用户可见&lt;/p&gt;
&lt;p&gt;共享文件指针允许多用户同时读取和写入文件&lt;/p&gt;
&lt;p&gt;会话语义:&lt;/p&gt;
&lt;p&gt;写入内容只有当文件关闭时可见&lt;/p&gt;
&lt;p&gt;锁:&lt;/p&gt;
&lt;p&gt;一些操作系统和文件系统提供该功能&lt;/p&gt;
&lt;h3&gt;目录&lt;/h3&gt;
&lt;p&gt;文件以目录的方式组织起来&lt;/p&gt;
&lt;p&gt;目录是一类特殊的文件: 每个目录都包含了一张表&amp;lt;name, pointer to file header&amp;gt;&lt;/p&gt;
&lt;p&gt;目录和文件的树形结构: 早期的文件系统是扁平的(只有一层目录)&lt;/p&gt;
&lt;p&gt;层次名称空间: /spell/mail/prt/first  /programs/p/list&lt;/p&gt;
&lt;p&gt;典型操作:&lt;/p&gt;
&lt;p&gt;搜索文件&lt;/p&gt;
&lt;p&gt;创建文件&lt;/p&gt;
&lt;p&gt;删除文件&lt;/p&gt;
&lt;p&gt;枚举目录&lt;/p&gt;
&lt;p&gt;重命名文件&lt;/p&gt;
&lt;p&gt;在文件系统中遍历一个路径&lt;/p&gt;
&lt;p&gt;操作系统应该只允许内核模式修改目录: 确保映射的完整性,应用程序能够读目录(ls)&lt;/p&gt;
&lt;p&gt;文件名的线性列表,包含了指向数据块的指针: 编程简单,执行耗时&lt;/p&gt;
&lt;p&gt;Hash表 - hash数据结构的线性表: 减少目录搜索时间,碰撞,固定大小&lt;/p&gt;
&lt;p&gt;名字解析: 逻辑名字转换成物理资源(如文件)的过程:&lt;/p&gt;
&lt;p&gt;在文件系统中: 到实际文件的文件名(路径)&lt;/p&gt;
&lt;p&gt;遍历文件目录直到找到目标文件&lt;/p&gt;
&lt;p&gt;举例: 解析&quot;/bin/ls&quot;:&lt;/p&gt;
&lt;p&gt;读取root的文件头(在磁盘固定位置)&lt;/p&gt;
&lt;p&gt;读取root的数据块: 搜索bin项&lt;/p&gt;
&lt;p&gt;读取bin的文件头&lt;/p&gt;
&lt;p&gt;读取bin的数据块: 搜索ls项&lt;/p&gt;
&lt;p&gt;读取ls的文件头&lt;/p&gt;
&lt;p&gt;当前工作目录:&lt;/p&gt;
&lt;p&gt;每个进程都会指向一个文件目录用于解析文件名&lt;/p&gt;
&lt;p&gt;允许用户指定相对路径来代替绝对路径&lt;/p&gt;
&lt;p&gt;一个文件系统需要先挂载才能被访问&lt;/p&gt;
&lt;p&gt;一个未挂载的文件系统被挂载在挂载点上&lt;/p&gt;
&lt;h3&gt;文件别名&lt;/h3&gt;
&lt;p&gt;两个或多个文件名关联同一个文件:&lt;/p&gt;
&lt;p&gt;硬链接: 多个文件项指向一个文件&lt;/p&gt;
&lt;p&gt;软链接: 以快捷方式指向其他文件&lt;/p&gt;
&lt;p&gt;通过存储真实文件的逻辑名称来实现&lt;/p&gt;
&lt;p&gt;如果删除一个有别名的文件会如何呢? : 这个别名将成为一个悬空指针&lt;/p&gt;
&lt;p&gt;Backpointers 方案:&lt;/p&gt;
&lt;p&gt;每个文件有一个包含多个backpointers的列表,所以删除所有的Backpointers&lt;/p&gt;
&lt;p&gt;backpointers使用菊花链管理&lt;/p&gt;
&lt;p&gt;添加一个间接层: 目录项数据结构&lt;/p&gt;
&lt;p&gt;链接: 已存在文件的另外一个名字(指针)&lt;/p&gt;
&lt;p&gt;链接处理: 跟随指针来定位文件&lt;/p&gt;
&lt;p&gt;我们如何保证没有循环呢?&lt;/p&gt;
&lt;p&gt;只允许到文件的链接, 不允许在子目录的链接&lt;/p&gt;
&lt;p&gt;每增加一个新的链接都用循环检测算法确定是否合理&lt;/p&gt;
&lt;p&gt;限制路径可遍历文件目录的数量&lt;/p&gt;
&lt;h3&gt;文件系统种类&lt;/h3&gt;
&lt;p&gt;磁盘文件系统: 文件存储在数据存储设备上,如磁盘; 例如: FAT,NTFS,ext2,3,ISO9660等&lt;/p&gt;
&lt;p&gt;数据库文件系统: 文件根据其特征是可被寻址的; 例如: WinFS&lt;/p&gt;
&lt;p&gt;日志文件系统: 记录文件系统的修改,事件; 例如: journaling file system&lt;/p&gt;
&lt;p&gt;网络,分布式文件系统: 例如: NFS,SMB,AFS,GFS&lt;/p&gt;
&lt;p&gt;特殊,虚拟文件系统&lt;/p&gt;
&lt;h2&gt;虚拟文件系统&lt;/h2&gt;
&lt;p&gt;分层结构:&lt;/p&gt;
&lt;p&gt;顶层: 文件,文件系统API&lt;/p&gt;
&lt;p&gt;上层: 虚拟(逻辑)文件系统 (将所有设备IO,网络IO全抽象成为文件,使得接口一致)&lt;/p&gt;
&lt;p&gt;底层: 特定文件系统模块&lt;/p&gt;
&lt;p&gt;目的: 对所有不同文件系统的抽象&lt;/p&gt;
&lt;p&gt;功能:&lt;/p&gt;
&lt;p&gt;提供相同的文件和文件系统接口&lt;/p&gt;
&lt;p&gt;管理所有文件和文件系统关联的数据结构&lt;/p&gt;
&lt;p&gt;高效查询例程,遍历文件系统&lt;/p&gt;
&lt;p&gt;与特定文件系统模块的交互&lt;/p&gt;
&lt;p&gt;数据结构:&lt;/p&gt;
&lt;p&gt;卷[第四声]控制块(UNIX: &quot;superblock&quot;)&lt;/p&gt;
&lt;p&gt;每个文件系统一个&lt;/p&gt;
&lt;p&gt;文件系统详细信息&lt;/p&gt;
&lt;p&gt;块,块大小,空余块,计数,指针等&lt;/p&gt;
&lt;p&gt;文件控制块(UNIX: &quot;vnode&quot; or &quot;inode&quot;)&lt;/p&gt;
&lt;p&gt;每个文件一个&lt;/p&gt;
&lt;p&gt;文件详细信息&lt;/p&gt;
&lt;p&gt;许可,拥有者,大小,数据库位置等&lt;/p&gt;
&lt;p&gt;目录节点(Linux: &quot;dentry&quot;)&lt;/p&gt;
&lt;p&gt;每个目录项一个(目录和文件)&lt;/p&gt;
&lt;p&gt;将目录项数据结构及树形布局编码成树形数据结构&lt;/p&gt;
&lt;p&gt;指向文件控制块,父节点,项目列表等&lt;/p&gt;
&lt;p&gt;其中: 卷控制块(每个文件系统一个),文件控制块(每个文件一个),目录节点(每个目录项一个)&lt;/p&gt;
&lt;p&gt;持续存储在二级存储中: 在分配在存储设备中的数据块中&lt;/p&gt;
&lt;p&gt;当需要时加载进内存:&lt;/p&gt;
&lt;p&gt;卷控制块: 当文件系统挂载时进入内存&lt;/p&gt;
&lt;p&gt;文件控制块: 当文件被访问时进入内存&lt;/p&gt;
&lt;p&gt;目录节点: 在遍历一个文件路径时进入内存&lt;/p&gt;
&lt;h2&gt;数据块缓存&lt;/h2&gt;
&lt;p&gt;数据块按需读入内存:&lt;/p&gt;
&lt;p&gt;提供 &lt;code&gt;read()&lt;/code&gt; 操作&lt;/p&gt;
&lt;p&gt;预读: 预先读取后面的数据块&lt;/p&gt;
&lt;p&gt;数据块使用后被缓存:&lt;/p&gt;
&lt;p&gt;假设数据将会再次被使用&lt;/p&gt;
&lt;p&gt;写操作可能被缓存和延迟写入&lt;/p&gt;
&lt;p&gt;两种数据块缓存方式:&lt;/p&gt;
&lt;p&gt;普通缓冲区缓存&lt;/p&gt;
&lt;p&gt;页缓存: 同一缓存数据块和内存页&lt;/p&gt;
&lt;p&gt;分页要求: 当需要一个页时才将其载入内存&lt;/p&gt;
&lt;p&gt;支持存储: 一个页(在虚拟地址空间中)可以被映射到一个本地文件中(在二级存储中)&lt;/p&gt;
&lt;h2&gt;打开文件的数据结构&lt;/h2&gt;
&lt;p&gt;打开文件描述:&lt;/p&gt;
&lt;p&gt;每个被打开的文件一个&lt;/p&gt;
&lt;p&gt;文件状态信息&lt;/p&gt;
&lt;p&gt;目录项,当前文件指针,文件操作设置等&lt;/p&gt;
&lt;p&gt;打开文件表:&lt;/p&gt;
&lt;p&gt;一个进程一个&lt;/p&gt;
&lt;p&gt;一个系统级的&lt;/p&gt;
&lt;p&gt;每个卷控制块也会保存一个列表&lt;/p&gt;
&lt;p&gt;所以如果有文件被打开将不能被卸载&lt;/p&gt;
&lt;p&gt;一些操作系统和文件系统提供该功能&lt;/p&gt;
&lt;p&gt;调节对文件的访问&lt;/p&gt;
&lt;p&gt;强制和劝告:&lt;/p&gt;
&lt;p&gt;强制 - 根据锁保持情况和需求拒绝访问&lt;/p&gt;
&lt;p&gt;劝告 - 进程可以查找锁的状态来决定怎么做&lt;/p&gt;
&lt;h2&gt;文件分配&lt;/h2&gt;
&lt;p&gt;大多数文件都很小:&lt;/p&gt;
&lt;p&gt;需要对小文件提供强力的支持&lt;/p&gt;
&lt;p&gt;块空间不能太小&lt;/p&gt;
&lt;p&gt;一些文件非常大:&lt;/p&gt;
&lt;p&gt;必须支持大文件(64-bit 文件偏移)&lt;/p&gt;
&lt;p&gt;大文件访问需要相当高效&lt;/p&gt;
&lt;p&gt;如何为一个文件分配数据块&lt;/p&gt;
&lt;p&gt;分配方式:&lt;/p&gt;
&lt;p&gt;连续分配&lt;/p&gt;
&lt;p&gt;链式分配&lt;/p&gt;
&lt;p&gt;索引分配&lt;/p&gt;
&lt;p&gt;指标:&lt;/p&gt;
&lt;p&gt;高效: 如存储利用(外部碎片)&lt;/p&gt;
&lt;p&gt;表现: 如访问速度&lt;/p&gt;
&lt;p&gt;连续分配:&lt;/p&gt;
&lt;p&gt;文件头指定起始块和长度&lt;/p&gt;
&lt;p&gt;位置,分配策略: 最先匹配,最佳匹配,...&lt;/p&gt;
&lt;p&gt;优势: 文件读取表现好;高效的顺序和随机访问&lt;/p&gt;
&lt;p&gt;劣势: 碎片;文件增长问题&lt;/p&gt;
&lt;p&gt;链式分配:&lt;/p&gt;
&lt;p&gt;文件以数据块链表方式存储&lt;/p&gt;
&lt;p&gt;文件头包含了到第一块和最后一块的指针&lt;/p&gt;
&lt;p&gt;优势: 创建,增大,缩小很容易;没有碎片&lt;/p&gt;
&lt;p&gt;劣势: 不可能进行真正的随机访问;可靠性&lt;/p&gt;
&lt;p&gt;索引分配:&lt;/p&gt;
&lt;p&gt;为每个文件创建一个名为索引数据块的非数据数据块(到文件数据块的指针列表)&lt;/p&gt;
&lt;p&gt;文件头包含了索引数据块&lt;/p&gt;
&lt;p&gt;优势: 创建,增大,缩小很容易;没有碎片;支持直接访问&lt;/p&gt;
&lt;p&gt;劣势: 当文件很小时,存储索引的开销大;处理大文件难&lt;/p&gt;
&lt;h2&gt;空闲空间列表&lt;/h2&gt;
&lt;p&gt;跟踪在存储中的所有未分配的数据块&lt;/p&gt;
&lt;p&gt;空闲空间列表存储在哪里?&lt;/p&gt;
&lt;p&gt;空闲空间列表的最佳数据结构怎么样?&lt;/p&gt;
&lt;p&gt;用位图代表空闲数据块列表: 11111101101110111 如果 i = 0表明数据块i是空闲的,反之是分配的&lt;/p&gt;
&lt;p&gt;使用简单但是可能会是一个big vector:&lt;/p&gt;
&lt;p&gt;160GB disk → 40M blocks → 5MB worth of bits&lt;/p&gt;
&lt;p&gt;然而,如果空闲空间在磁盘中均匀分布,那么再找到&quot;0&quot;之前需要扫描 磁盘上数据块总数 / 空闲块的数目&lt;/p&gt;
&lt;p&gt;需要保护:&lt;/p&gt;
&lt;p&gt;指向空闲列表的指针&lt;/p&gt;
&lt;p&gt;位图:&lt;/p&gt;
&lt;p&gt;必须保存在磁盘上;在内存和磁盘拷贝可能有所不同;不允许block[i]在内存中的状态为bit[i]=1而在磁盘中bit[i]=0&lt;/p&gt;
&lt;p&gt;解决:&lt;/p&gt;
&lt;p&gt;在磁盘上设置bit[i] = 1; 分配block[i]; 在内存中设置bit[i] = 1&lt;/p&gt;
&lt;h2&gt;多磁盘管理 - RAID&lt;/h2&gt;
&lt;p&gt;通常磁盘通过分区来最大限度减小寻道时间:&lt;/p&gt;
&lt;p&gt;一个分区是一个柱面的集合&lt;/p&gt;
&lt;p&gt;每个分区都是逻辑上独立的磁盘&lt;/p&gt;
&lt;p&gt;分区: 硬件磁盘的一种适合操作系统指定格式的划分&lt;/p&gt;
&lt;p&gt;卷: 一个拥有一个文件系统实例的可访问的存储空间(通常常驻在磁盘的单个分区上)&lt;/p&gt;
&lt;p&gt;使用多个并行磁盘来增加: 吞吐量(通过并行),可靠性和可用性(通过冗余)&lt;/p&gt;
&lt;p&gt;RAID - 冗余磁盘阵列: 各种磁盘管理技术;RAID levels: 不同RAID分类,如RAID-0,RAID-1,RAID-5&lt;/p&gt;
&lt;p&gt;实现: 在操作系统内核: 存储,卷管理; RAID硬件控制器(IO)&lt;/p&gt;
&lt;p&gt;RAID-0&lt;/p&gt;
&lt;p&gt;数据块分成多个子块, 存储在独立的磁盘中: 和内存交叉相似&lt;/p&gt;
&lt;p&gt;通过更大的有效块大小来提供更大的磁盘带宽&lt;/p&gt;
&lt;p&gt;RAID-1&lt;/p&gt;
&lt;p&gt;可靠性成倍增长&lt;/p&gt;
&lt;p&gt;读取性能线性增加(向两个磁盘写入,从任何一个读取)&lt;/p&gt;
&lt;p&gt;RAID-4&lt;/p&gt;
&lt;p&gt;数据块级磁带配有专用奇偶校验磁盘: 允许从任意一个故障磁盘中恢复&lt;/p&gt;
&lt;p&gt;条带化和奇偶校验按byte-by-byte或者bit-by-bit: RAID-0,4,5: block-wise ;RAID-3: bit-wise&lt;/p&gt;
&lt;p&gt;RAID-5&lt;/p&gt;
&lt;p&gt;每个条带快有一个奇偶校验块,允许有一个磁盘错误&lt;/p&gt;
&lt;p&gt;RAID-6&lt;/p&gt;
&lt;p&gt;两个冗余块,有一种特殊的编码方式,允许两个磁盘错误&lt;/p&gt;
&lt;h2&gt;磁盘调度&lt;/h2&gt;
&lt;p&gt;读取或写入时,磁头必须被定位在期望的磁道,并从所期望的扇区开始&lt;/p&gt;
&lt;p&gt;寻道时间: 定位到期望的磁道所花费的时间&lt;/p&gt;
&lt;p&gt;旋转延迟: 从扇区的开始处到到达目的处花费的时间&lt;/p&gt;
&lt;p&gt;平均旋转延迟时间 = 磁盘旋转一周时间的一半&lt;/p&gt;
&lt;p&gt;寻道时间是性能上区别的原因&lt;/p&gt;
&lt;p&gt;对单个磁盘,会有一个IO请求数目&lt;/p&gt;
&lt;p&gt;如果请求是随机的,那么会表现很差&lt;/p&gt;
&lt;p&gt;FIFO:&lt;/p&gt;
&lt;p&gt;按顺序处理请求&lt;/p&gt;
&lt;p&gt;公平对待所有进程&lt;/p&gt;
&lt;p&gt;在有很多进程的情况下,接近随机调度的性能&lt;/p&gt;
&lt;p&gt;最短服务优先:&lt;/p&gt;
&lt;p&gt;选择从磁臂当前位置需要移动最少的IO请求&lt;/p&gt;
&lt;p&gt;总是选择最短寻道时间&lt;/p&gt;
&lt;p&gt;skan:&lt;/p&gt;
&lt;p&gt;磁臂在一个方向上移动,满足所有为完成的请求,直到磁臂到达该方向上最后的磁道&lt;/p&gt;
&lt;p&gt;调换方向&lt;/p&gt;
&lt;p&gt;c-skan:&lt;/p&gt;
&lt;p&gt;限制了仅在一个方向上扫描&lt;/p&gt;
&lt;p&gt;当最后一个磁道也被访问过了后,磁臂返回到磁盘的另外一端再次进行扫描&lt;/p&gt;
&lt;p&gt;c-loop(c-skan改进):&lt;/p&gt;
&lt;p&gt;磁臂先到达该方向上最后一个请求处,然后立即反转&lt;/p&gt;
</content:encoded></item><item><title>深入理解操作系统 第12章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-12/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-12/</guid><description>文件管理</description><pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第十二章的主要内容是：文件管理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;基本概念&lt;/h2&gt;
&lt;h3&gt;文件系统和文件&lt;/h3&gt;
&lt;p&gt;文件系统: 一种用于持久性存储的系统抽象&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在存储上: 组织,控制,导航,访问和检索数据&lt;/li&gt;
&lt;li&gt;在大多数计算机系统包含文件系统&lt;/li&gt;
&lt;li&gt;个人电脑,服务器,笔记本电脑&lt;/li&gt;
&lt;li&gt;ipod,tivo,机顶盒,手机,电脑&lt;/li&gt;
&lt;li&gt;google可能也是由一个文件系统构成的&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文件: 文件系统中的一个单元的相关数据在操作系统中的抽象&lt;/p&gt;
&lt;p&gt;文件系统的功能:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分配文件磁盘空间
&lt;ul&gt;
&lt;li&gt;管理文件块(哪一块属于哪一个文件)&lt;/li&gt;
&lt;li&gt;管理空闲空间(哪一块是空闲的)&lt;/li&gt;
&lt;li&gt;分配算法(策略)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管理文件集合
&lt;ul&gt;
&lt;li&gt;定位文件及其内容&lt;/li&gt;
&lt;li&gt;命名: 通过名字找到文件的接口&lt;/li&gt;
&lt;li&gt;最常见: 分层文件系统&lt;/li&gt;
&lt;li&gt;文件系统类型(组织文件的不同方式)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;提供的便利及特征
&lt;ul&gt;
&lt;li&gt;保护: 分层来保护数据安全&lt;/li&gt;
&lt;li&gt;可靠性,持久性: 保持文件的持久即使发生崩溃,媒体错误,攻击等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文件和块:&lt;/p&gt;
&lt;p&gt;文件属性: 名称,类型,位置,大小,保护,创建者,创建时间,最久修改时间...&lt;/p&gt;
&lt;p&gt;文件头: 在存储元数据中保存了每个文件的信息,保存文件的属性,跟踪哪一块存储块属于逻辑上文件结构的哪个偏移&lt;/p&gt;
&lt;h3&gt;文件描述符&lt;/h3&gt;
&lt;p&gt;文件使用模式:&lt;/p&gt;
&lt;p&gt;使用程序必须在使用前先&quot;打开&quot;文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;f = open(name, flag);
...
... = read(f, ...);
...
close(f);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;内核跟踪每个进程打开的文件:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作系统为每个进程维护一个打开文件表&lt;/li&gt;
&lt;li&gt;一个打开文件描述符是这个表中的索引&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要元数据来管理打开文件:&lt;/p&gt;
&lt;p&gt;文件指针: 指向最近的一次读写位置,每个打开了这个文件的进程都这个指针&lt;/p&gt;
&lt;p&gt;文件打开计数: 记录文件打开的次数 - 当最后一个进程关闭了文件时,允许将其从打开文件表中移除&lt;/p&gt;
&lt;p&gt;文件磁盘位置: 缓存数据访问信息&lt;/p&gt;
&lt;p&gt;访问权限: 每个程序访问模式信息&lt;/p&gt;
&lt;p&gt;用户视图: 持久的数据结构&lt;/p&gt;
&lt;p&gt;系统访问接口:&lt;/p&gt;
&lt;p&gt;字节的集合(UNIX)&lt;/p&gt;
&lt;p&gt;系统不会关心你想存储在磁盘上的任何的数据结构&lt;/p&gt;
&lt;p&gt;操作系统内部视角:&lt;/p&gt;
&lt;p&gt;块的集合(块是逻辑转换单元,而扇区是物理转换单元)&lt;/p&gt;
&lt;p&gt;块大小&amp;lt;&amp;gt; 扇区大小: 在UNIX中, 块的大小是 4KB&lt;/p&gt;
&lt;p&gt;当用户说: 给我2-12字节空间时会发生什么?&lt;/p&gt;
&lt;p&gt;获取字节所在的快&lt;/p&gt;
&lt;p&gt;返回快内对应部分&lt;/p&gt;
&lt;p&gt;如果要写2-12字节?&lt;/p&gt;
&lt;p&gt;获取块&lt;/p&gt;
&lt;p&gt;修改块内对应部分&lt;/p&gt;
&lt;p&gt;写回块&lt;/p&gt;
&lt;p&gt;在文件系统中的所有操作都是在整个块空间上进行的: &lt;code&gt;getc()&lt;/code&gt; &lt;code&gt;putc()&lt;/code&gt; 即使每次只访问1字节的数据,也会缓存目标数据4096字节(一个磁盘块)&lt;/p&gt;
&lt;p&gt;用户怎么访问文件: 在系统层面需要知道用户的访问模式&lt;/p&gt;
&lt;p&gt;顺序访问: 按字节依次读取(几乎所有的访问都是这种方式)&lt;/p&gt;
&lt;p&gt;随机访问: 从中间读写(不常用,但是仍然重要,如: 虚拟内存支持文件,内存页存储在文件中;更加快速,不希望获取文件中间的内容的时候也必须先获取块内所有字节)&lt;/p&gt;
&lt;p&gt;内容访问: 通过特征&lt;/p&gt;
&lt;p&gt;文件内部结构:&lt;/p&gt;
&lt;p&gt;无结构: 单词,比特的队列&lt;/p&gt;
&lt;p&gt;简单记录结构: 列,固定长度,可变长度&lt;/p&gt;
&lt;p&gt;复杂结构: 格式化的文档(word, PDF), 可执行文件, ...&lt;/p&gt;
&lt;p&gt;多用户系统中的文件共享是很必要的&lt;/p&gt;
&lt;p&gt;访问控制:&lt;/p&gt;
&lt;p&gt;谁能够获得哪些文件的哪些访问权限&lt;/p&gt;
&lt;p&gt;访问模式: 读,写,执行,删除,列举等&lt;/p&gt;
&lt;p&gt;文件访问控制列表(ACL):&lt;/p&gt;
&lt;p&gt;&amp;lt;文件实体, 权限&amp;gt;&lt;/p&gt;
&lt;p&gt;UNIX模式:&lt;/p&gt;
&lt;p&gt;&amp;lt;用户|组|所有人,读|写|可执行&amp;gt;&lt;/p&gt;
&lt;p&gt;用户ID识别用户,表明每个用户所允许的权限及保护模式&lt;/p&gt;
&lt;p&gt;组ID允许用户组成组,并指定了组访问权限&lt;/p&gt;
&lt;p&gt;指定多用户,客户如何同时访问共享文件:&lt;/p&gt;
&lt;p&gt;和过程同步算法相似&lt;/p&gt;
&lt;p&gt;因磁盘IO和网络延迟而设计简单&lt;/p&gt;
&lt;p&gt;UNIX文件系统(UFS)语义:&lt;/p&gt;
&lt;p&gt;对打开文件的写入内容立即对其他打开同一文件的其他用户可见&lt;/p&gt;
&lt;p&gt;共享文件指针允许多用户同时读取和写入文件&lt;/p&gt;
&lt;p&gt;会话语义:&lt;/p&gt;
&lt;p&gt;写入内容只有当文件关闭时可见&lt;/p&gt;
&lt;p&gt;锁:&lt;/p&gt;
&lt;p&gt;一些操作系统和文件系统提供该功能&lt;/p&gt;
&lt;h3&gt;目录&lt;/h3&gt;
&lt;p&gt;文件以目录的方式组织起来&lt;/p&gt;
&lt;p&gt;目录是一类特殊的文件: 每个目录都包含了一张表&amp;lt;name, pointer to file header&amp;gt;&lt;/p&gt;
&lt;p&gt;目录和文件的树形结构: 早期的文件系统是扁平的(只有一层目录)&lt;/p&gt;
&lt;p&gt;层次名称空间: /spell/mail/prt/first  /programs/p/list&lt;/p&gt;
&lt;p&gt;典型操作:&lt;/p&gt;
&lt;p&gt;搜索文件&lt;/p&gt;
&lt;p&gt;创建文件&lt;/p&gt;
&lt;p&gt;删除文件&lt;/p&gt;
&lt;p&gt;枚举目录&lt;/p&gt;
&lt;p&gt;重命名文件&lt;/p&gt;
&lt;p&gt;在文件系统中遍历一个路径&lt;/p&gt;
&lt;p&gt;操作系统应该只允许内核模式修改目录: 确保映射的完整性,应用程序能够读目录(ls)&lt;/p&gt;
&lt;p&gt;文件名的线性列表,包含了指向数据块的指针: 编程简单,执行耗时&lt;/p&gt;
&lt;p&gt;Hash表 - hash数据结构的线性表: 减少目录搜索时间,碰撞,固定大小&lt;/p&gt;
&lt;p&gt;名字解析: 逻辑名字转换成物理资源(如文件)的过程:&lt;/p&gt;
&lt;p&gt;在文件系统中: 到实际文件的文件名(路径)&lt;/p&gt;
&lt;p&gt;遍历文件目录直到找到目标文件&lt;/p&gt;
&lt;p&gt;举例: 解析&quot;/bin/ls&quot;:&lt;/p&gt;
&lt;p&gt;读取root的文件头(在磁盘固定位置)&lt;/p&gt;
&lt;p&gt;读取root的数据块: 搜索bin项&lt;/p&gt;
&lt;p&gt;读取bin的文件头&lt;/p&gt;
&lt;p&gt;读取bin的数据块: 搜索ls项&lt;/p&gt;
&lt;p&gt;读取ls的文件头&lt;/p&gt;
&lt;p&gt;当前工作目录:&lt;/p&gt;
&lt;p&gt;每个进程都会指向一个文件目录用于解析文件名&lt;/p&gt;
&lt;p&gt;允许用户指定相对路径来代替绝对路径&lt;/p&gt;
&lt;p&gt;一个文件系统需要先挂载才能被访问&lt;/p&gt;
&lt;p&gt;一个未挂载的文件系统被挂载在挂载点上&lt;/p&gt;
&lt;h3&gt;文件别名&lt;/h3&gt;
&lt;p&gt;两个或多个文件名关联同一个文件:&lt;/p&gt;
&lt;p&gt;硬链接: 多个文件项指向一个文件&lt;/p&gt;
&lt;p&gt;软链接: 以快捷方式指向其他文件&lt;/p&gt;
&lt;p&gt;通过存储真实文件的逻辑名称来实现&lt;/p&gt;
&lt;p&gt;如果删除一个有别名的文件会如何呢? : 这个别名将成为一个悬空指针&lt;/p&gt;
&lt;p&gt;Backpointers 方案:&lt;/p&gt;
&lt;p&gt;每个文件有一个包含多个backpointers的列表,所以删除所有的Backpointers&lt;/p&gt;
&lt;p&gt;backpointers使用菊花链管理&lt;/p&gt;
&lt;p&gt;添加一个间接层: 目录项数据结构&lt;/p&gt;
&lt;p&gt;链接: 已存在文件的另外一个名字(指针)&lt;/p&gt;
&lt;p&gt;链接处理: 跟随指针来定位文件&lt;/p&gt;
&lt;p&gt;我们如何保证没有循环呢?&lt;/p&gt;
&lt;p&gt;只允许到文件的链接, 不允许在子目录的链接&lt;/p&gt;
&lt;p&gt;每增加一个新的链接都用循环检测算法确定是否合理&lt;/p&gt;
&lt;p&gt;限制路径可遍历文件目录的数量&lt;/p&gt;
&lt;h3&gt;文件系统种类&lt;/h3&gt;
&lt;p&gt;磁盘文件系统: 文件存储在数据存储设备上,如磁盘; 例如: FAT,NTFS,ext2,3,ISO9660等&lt;/p&gt;
&lt;p&gt;数据库文件系统: 文件根据其特征是可被寻址的; 例如: WinFS&lt;/p&gt;
&lt;p&gt;日志文件系统: 记录文件系统的修改,事件; 例如: journaling file system&lt;/p&gt;
&lt;p&gt;网络,分布式文件系统: 例如: NFS,SMB,AFS,GFS&lt;/p&gt;
&lt;p&gt;特殊,虚拟文件系统&lt;/p&gt;
&lt;h2&gt;虚拟文件系统&lt;/h2&gt;
&lt;p&gt;分层结构:&lt;/p&gt;
&lt;p&gt;顶层: 文件,文件系统API&lt;/p&gt;
&lt;p&gt;上层: 虚拟(逻辑)文件系统 (将所有设备IO,网络IO全抽象成为文件,使得接口一致)&lt;/p&gt;
&lt;p&gt;底层: 特定文件系统模块&lt;/p&gt;
&lt;p&gt;目的: 对所有不同文件系统的抽象&lt;/p&gt;
&lt;p&gt;功能:&lt;/p&gt;
&lt;p&gt;提供相同的文件和文件系统接口&lt;/p&gt;
&lt;p&gt;管理所有文件和文件系统关联的数据结构&lt;/p&gt;
&lt;p&gt;高效查询例程,遍历文件系统&lt;/p&gt;
&lt;p&gt;与特定文件系统模块的交互&lt;/p&gt;
&lt;p&gt;数据结构:&lt;/p&gt;
&lt;p&gt;卷[第四声]控制块(UNIX: &quot;superblock&quot;)&lt;/p&gt;
&lt;p&gt;每个文件系统一个&lt;/p&gt;
&lt;p&gt;文件系统详细信息&lt;/p&gt;
&lt;p&gt;块,块大小,空余块,计数,指针等&lt;/p&gt;
&lt;p&gt;文件控制块(UNIX: &quot;vnode&quot; or &quot;inode&quot;)&lt;/p&gt;
&lt;p&gt;每个文件一个&lt;/p&gt;
&lt;p&gt;文件详细信息&lt;/p&gt;
&lt;p&gt;许可,拥有者,大小,数据库位置等&lt;/p&gt;
&lt;p&gt;目录节点(Linux: &quot;dentry&quot;)&lt;/p&gt;
&lt;p&gt;每个目录项一个(目录和文件)&lt;/p&gt;
&lt;p&gt;将目录项数据结构及树形布局编码成树形数据结构&lt;/p&gt;
&lt;p&gt;指向文件控制块,父节点,项目列表等&lt;/p&gt;
&lt;p&gt;其中: 卷控制块(每个文件系统一个),文件控制块(每个文件一个),目录节点(每个目录项一个)&lt;/p&gt;
&lt;p&gt;持续存储在二级存储中: 在分配在存储设备中的数据块中&lt;/p&gt;
&lt;p&gt;当需要时加载进内存:&lt;/p&gt;
&lt;p&gt;卷控制块: 当文件系统挂载时进入内存&lt;/p&gt;
&lt;p&gt;文件控制块: 当文件被访问时进入内存&lt;/p&gt;
&lt;p&gt;目录节点: 在遍历一个文件路径时进入内存&lt;/p&gt;
&lt;h2&gt;数据块缓存&lt;/h2&gt;
&lt;p&gt;数据块按需读入内存:&lt;/p&gt;
&lt;p&gt;提供 &lt;code&gt;read()&lt;/code&gt; 操作&lt;/p&gt;
&lt;p&gt;预读: 预先读取后面的数据块&lt;/p&gt;
&lt;p&gt;数据块使用后被缓存:&lt;/p&gt;
&lt;p&gt;假设数据将会再次被使用&lt;/p&gt;
&lt;p&gt;写操作可能被缓存和延迟写入&lt;/p&gt;
&lt;p&gt;两种数据块缓存方式:&lt;/p&gt;
&lt;p&gt;普通缓冲区缓存&lt;/p&gt;
&lt;p&gt;页缓存: 同一缓存数据块和内存页&lt;/p&gt;
&lt;p&gt;分页要求: 当需要一个页时才将其载入内存&lt;/p&gt;
&lt;p&gt;支持存储: 一个页(在虚拟地址空间中)可以被映射到一个本地文件中(在二级存储中)&lt;/p&gt;
&lt;h2&gt;打开文件的数据结构&lt;/h2&gt;
&lt;p&gt;打开文件描述:&lt;/p&gt;
&lt;p&gt;每个被打开的文件一个&lt;/p&gt;
&lt;p&gt;文件状态信息&lt;/p&gt;
&lt;p&gt;目录项,当前文件指针,文件操作设置等&lt;/p&gt;
&lt;p&gt;打开文件表:&lt;/p&gt;
&lt;p&gt;一个进程一个&lt;/p&gt;
&lt;p&gt;一个系统级的&lt;/p&gt;
&lt;p&gt;每个卷控制块也会保存一个列表&lt;/p&gt;
&lt;p&gt;所以如果有文件被打开将不能被卸载&lt;/p&gt;
&lt;p&gt;一些操作系统和文件系统提供该功能&lt;/p&gt;
&lt;p&gt;调节对文件的访问&lt;/p&gt;
&lt;p&gt;强制和劝告:&lt;/p&gt;
&lt;p&gt;强制 - 根据锁保持情况和需求拒绝访问&lt;/p&gt;
&lt;p&gt;劝告 - 进程可以查找锁的状态来决定怎么做&lt;/p&gt;
&lt;h2&gt;文件分配&lt;/h2&gt;
&lt;p&gt;大多数文件都很小:&lt;/p&gt;
&lt;p&gt;需要对小文件提供强力的支持&lt;/p&gt;
&lt;p&gt;块空间不能太小&lt;/p&gt;
&lt;p&gt;一些文件非常大:&lt;/p&gt;
&lt;p&gt;必须支持大文件(64-bit 文件偏移)&lt;/p&gt;
&lt;p&gt;大文件访问需要相当高效&lt;/p&gt;
&lt;p&gt;如何为一个文件分配数据块&lt;/p&gt;
&lt;p&gt;分配方式:&lt;/p&gt;
&lt;p&gt;连续分配&lt;/p&gt;
&lt;p&gt;链式分配&lt;/p&gt;
&lt;p&gt;索引分配&lt;/p&gt;
&lt;p&gt;指标:&lt;/p&gt;
&lt;p&gt;高效: 如存储利用(外部碎片)&lt;/p&gt;
&lt;p&gt;表现: 如访问速度&lt;/p&gt;
&lt;p&gt;连续分配:&lt;/p&gt;
&lt;p&gt;文件头指定起始块和长度&lt;/p&gt;
&lt;p&gt;位置,分配策略: 最先匹配,最佳匹配,...&lt;/p&gt;
&lt;p&gt;优势: 文件读取表现好;高效的顺序和随机访问&lt;/p&gt;
&lt;p&gt;劣势: 碎片;文件增长问题&lt;/p&gt;
&lt;p&gt;链式分配:&lt;/p&gt;
&lt;p&gt;文件以数据块链表方式存储&lt;/p&gt;
&lt;p&gt;文件头包含了到第一块和最后一块的指针&lt;/p&gt;
&lt;p&gt;优势: 创建,增大,缩小很容易;没有碎片&lt;/p&gt;
&lt;p&gt;劣势: 不可能进行真正的随机访问;可靠性&lt;/p&gt;
&lt;p&gt;索引分配:&lt;/p&gt;
&lt;p&gt;为每个文件创建一个名为索引数据块的非数据数据块(到文件数据块的指针列表)&lt;/p&gt;
&lt;p&gt;文件头包含了索引数据块&lt;/p&gt;
&lt;p&gt;优势: 创建,增大,缩小很容易;没有碎片;支持直接访问&lt;/p&gt;
&lt;p&gt;劣势: 当文件很小时,存储索引的开销大;处理大文件难&lt;/p&gt;
&lt;h2&gt;空闲空间列表&lt;/h2&gt;
&lt;p&gt;跟踪在存储中的所有未分配的数据块&lt;/p&gt;
&lt;p&gt;空闲空间列表存储在哪里?&lt;/p&gt;
&lt;p&gt;空闲空间列表的最佳数据结构怎么样?&lt;/p&gt;
&lt;p&gt;用位图代表空闲数据块列表: 11111101101110111 如果 i = 0表明数据块i是空闲的,反之是分配的&lt;/p&gt;
&lt;p&gt;使用简单但是可能会是一个big vector:&lt;/p&gt;
&lt;p&gt;160GB disk → 40M blocks → 5MB worth of bits&lt;/p&gt;
&lt;p&gt;然而,如果空闲空间在磁盘中均匀分布,那么再找到&quot;0&quot;之前需要扫描 磁盘上数据块总数 / 空闲块的数目&lt;/p&gt;
&lt;p&gt;需要保护:&lt;/p&gt;
&lt;p&gt;指向空闲列表的指针&lt;/p&gt;
&lt;p&gt;位图:&lt;/p&gt;
&lt;p&gt;必须保存在磁盘上;在内存和磁盘拷贝可能有所不同;不允许block[i]在内存中的状态为bit[i]=1而在磁盘中bit[i]=0&lt;/p&gt;
&lt;p&gt;解决:&lt;/p&gt;
&lt;p&gt;在磁盘上设置bit[i] = 1; 分配block[i]; 在内存中设置bit[i] = 1&lt;/p&gt;
&lt;h2&gt;多磁盘管理 - RAID&lt;/h2&gt;
&lt;p&gt;通常磁盘通过分区来最大限度减小寻道时间:&lt;/p&gt;
&lt;p&gt;一个分区是一个柱面的集合&lt;/p&gt;
&lt;p&gt;每个分区都是逻辑上独立的磁盘&lt;/p&gt;
&lt;p&gt;分区: 硬件磁盘的一种适合操作系统指定格式的划分&lt;/p&gt;
&lt;p&gt;卷: 一个拥有一个文件系统实例的可访问的存储空间(通常常驻在磁盘的单个分区上)&lt;/p&gt;
&lt;p&gt;使用多个并行磁盘来增加: 吞吐量(通过并行),可靠性和可用性(通过冗余)&lt;/p&gt;
&lt;p&gt;RAID - 冗余磁盘阵列: 各种磁盘管理技术;RAID levels: 不同RAID分类,如RAID-0,RAID-1,RAID-5&lt;/p&gt;
&lt;p&gt;实现: 在操作系统内核: 存储,卷管理; RAID硬件控制器(IO)&lt;/p&gt;
&lt;p&gt;RAID-0&lt;/p&gt;
&lt;p&gt;数据块分成多个子块, 存储在独立的磁盘中: 和内存交叉相似&lt;/p&gt;
&lt;p&gt;通过更大的有效块大小来提供更大的磁盘带宽&lt;/p&gt;
&lt;p&gt;RAID-1&lt;/p&gt;
&lt;p&gt;可靠性成倍增长&lt;/p&gt;
&lt;p&gt;读取性能线性增加(向两个磁盘写入,从任何一个读取)&lt;/p&gt;
&lt;p&gt;RAID-4&lt;/p&gt;
&lt;p&gt;数据块级磁带配有专用奇偶校验磁盘: 允许从任意一个故障磁盘中恢复&lt;/p&gt;
&lt;p&gt;条带化和奇偶校验按byte-by-byte或者bit-by-bit: RAID-0,4,5: block-wise ;RAID-3: bit-wise&lt;/p&gt;
&lt;p&gt;RAID-5&lt;/p&gt;
&lt;p&gt;每个条带快有一个奇偶校验块,允许有一个磁盘错误&lt;/p&gt;
&lt;p&gt;RAID-6&lt;/p&gt;
&lt;p&gt;两个冗余块,有一种特殊的编码方式,允许两个磁盘错误&lt;/p&gt;
&lt;h2&gt;磁盘调度&lt;/h2&gt;
&lt;p&gt;读取或写入时,磁头必须被定位在期望的磁道,并从所期望的扇区开始&lt;/p&gt;
&lt;p&gt;寻道时间: 定位到期望的磁道所花费的时间&lt;/p&gt;
&lt;p&gt;旋转延迟: 从扇区的开始处到到达目的处花费的时间&lt;/p&gt;
&lt;p&gt;平均旋转延迟时间 = 磁盘旋转一周时间的一半&lt;/p&gt;
&lt;p&gt;寻道时间是性能上区别的原因&lt;/p&gt;
&lt;p&gt;对单个磁盘,会有一个IO请求数目&lt;/p&gt;
&lt;p&gt;如果请求是随机的,那么会表现很差&lt;/p&gt;
&lt;p&gt;FIFO:&lt;/p&gt;
&lt;p&gt;按顺序处理请求&lt;/p&gt;
&lt;p&gt;公平对待所有进程&lt;/p&gt;
&lt;p&gt;在有很多进程的情况下,接近随机调度的性能&lt;/p&gt;
&lt;p&gt;最短服务优先:&lt;/p&gt;
&lt;p&gt;选择从磁臂当前位置需要移动最少的IO请求&lt;/p&gt;
&lt;p&gt;总是选择最短寻道时间&lt;/p&gt;
&lt;p&gt;skan:&lt;/p&gt;
&lt;p&gt;磁臂在一个方向上移动,满足所有为完成的请求,直到磁臂到达该方向上最后的磁道&lt;/p&gt;
&lt;p&gt;调换方向&lt;/p&gt;
&lt;p&gt;c-skan:&lt;/p&gt;
&lt;p&gt;限制了仅在一个方向上扫描&lt;/p&gt;
&lt;p&gt;当最后一个磁道也被访问过了后,磁臂返回到磁盘的另外一端再次进行扫描&lt;/p&gt;
&lt;p&gt;c-loop(c-skan改进):&lt;/p&gt;
&lt;p&gt;磁臂先到达该方向上最后一个请求处,然后立即反转&lt;/p&gt;
</content:encoded></item><item><title>博客修改默认主题指南</title><link>https://github.com/posts/theme/</link><guid isPermaLink="true">https://github.com/posts/theme/</guid><description>已设置博客站默认主题为亮色，笔记站默认主题为暗色</description><pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Astro博客修改默认主题教程&lt;/h1&gt;
&lt;p&gt;核心目标：修改博客首次打开默认显示主题，保留「跟随系统」「手动切换暗色」功能，不影响原有主题切换逻辑，适配Astro模板。&lt;/p&gt;
&lt;h2&gt;一、核心修改逻辑&lt;/h2&gt;
&lt;p&gt;Astro博客主题控制依赖2个核心文件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;全局布局文件 &lt;code&gt;src/layouts/Layout.astro&lt;/code&gt;（主题初始化脚本）&lt;/li&gt;
&lt;li&gt;工具文件 &lt;code&gt;src/utils/setting-utils.ts&lt;/code&gt;（主题读取/保存逻辑）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;二、具体修改步骤&lt;/h2&gt;
&lt;h3&gt;第一步：修改 Layout.astro 主题初始化脚本&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;打开文件：&lt;code&gt;src/layouts/Layout.astro&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;找到头部 &lt;code&gt;is:inline&lt;/code&gt; 主题脚本&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;找到核心代码行，只修改1处默认值：&lt;/p&gt;
&lt;p&gt;原代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const theme = localStorage.getItem(&apos;theme&apos;) || DEFAULT_THEME;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改后：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 关键：无本地存储时，默认亮色
const theme = localStorage.getItem(&apos;theme&apos;) || LIGHT_MODE;//或者DARK_MODE
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保留脚本中所有原有逻辑（包括 &lt;code&gt;switch&lt;/code&gt; 里的 &lt;code&gt;AUTO_MODE&lt;/code&gt;、&lt;code&gt;DARK_MODE&lt;/code&gt; 分支），&lt;strong&gt;不要删除任何代码&lt;/strong&gt;，确保跟随系统、手动切换功能正常。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;第二步：修改 setting-utils.ts 主题读取默认值&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;打开文件：&lt;code&gt;src/utils/setting-utils.ts&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;找到 &lt;code&gt;getStoredTheme&lt;/code&gt; 函数，只修改1处默认返回值：&lt;/p&gt;
&lt;p&gt;原代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function getStoredTheme(): LIGHT_DARK_MODE {
	return (localStorage.getItem(&quot;theme&quot;) as LIGHT_DARK_MODE) || DEFAULT_THEME;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改后：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function getStoredTheme(): LIGHT_DARK_MODE {
	// 关键：无本地存储时，默认亮色（同步Layout逻辑）
	return (localStorage.getItem(&quot;theme&quot;) as LIGHT_DARK_MODE) || LIGHT_MODE;//或者DARK_MODE
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;第三步：可选优化（提升体验，避免闪屏）&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;Layout.astro&lt;/code&gt; 的 &lt;code&gt;html&lt;/code&gt; 标签上，默认添加 &lt;code&gt;light&lt;/code&gt; 类，防止页面加载时闪一下暗色：&lt;/p&gt;
&lt;p&gt;原代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;html lang={siteLang} class=&quot;bg-[var(--page-bg)] transition text-[14px] md:text-[16px]&quot;
        data-overlayscrollbars-initialize
    &amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改后:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;html lang={siteLang} class=&quot;bg-[var(--page-bg)] transition text-[14px] md:text-[16px] light&quot;
        data-overlayscrollbars-initialize
    &amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;四、验证修改效果&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;本地重启项目：终端执行 &lt;code&gt;pnpm dev&lt;/code&gt;（或 &lt;code&gt;npm run dev&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;首次打开博客：无论电脑/手机系统是亮色还是暗色，均默认显示亮色&lt;/li&gt;
&lt;li&gt;测试主题切换：清除浏览器本地存储（或用无痕模式打开）：再次默认显示亮/暗色&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>深入理解操作系统 第11章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-11/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-11/</guid><description>死锁和进程通信</description><pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第十一章的主要内容是：死锁和进程通信&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;死锁问题&lt;/h2&gt;
&lt;p&gt;一组阻塞的进程持有一种资源等待获取另一个进程所占有的一个资源&lt;/p&gt;
&lt;p&gt;示例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统有2个磁带驱动器&lt;/li&gt;
&lt;li&gt;P1和P2各有一个,都需要另外一个&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;系统模型&lt;/h2&gt;
&lt;p&gt;资源类型R1,R2,..,Rm(CPU, memory space, IO devices)&lt;/p&gt;
&lt;p&gt;每个资源类型Ri有Wi个实例.&lt;/p&gt;
&lt;p&gt;每个进程使用资源如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;require,get ← free resource&lt;/li&gt;
&lt;li&gt;use,hold    ← requested,used resource&lt;/li&gt;
&lt;li&gt;release     ← free resource&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可重复使用的资源&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在一个时间只能有一个进程使用且不能被删除&lt;/li&gt;
&lt;li&gt;进程获得资源,后来释放由其他进程重用&lt;/li&gt;
&lt;li&gt;处理器,IO通道,主和副存储器,设备和数据结构,如文件,数据库和信号量&lt;/li&gt;
&lt;li&gt;如果每个进程拥有一个资源并请求其他资源,死锁可能发生&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用资源&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建和销毁&lt;/li&gt;
&lt;li&gt;在IO缓存区的中断,信号,消息,信息&lt;/li&gt;
&lt;li&gt;如果接收消息阻塞可能会发生死锁&lt;/li&gt;
&lt;li&gt;可能少见的组合事件会引起死锁&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;资源分配图&lt;/p&gt;
&lt;p&gt;一组顶点V和边E的集合&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;V有两种类型:
&lt;ul&gt;
&lt;li&gt;P={P1,P2,...,Pn},集合包括系统中的所有进程&lt;/li&gt;
&lt;li&gt;R={R1,R2,...,Rm},集合包括系统中的所有资源类型&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;requesting,claiming edge - directed edge Pi → Rj&lt;/li&gt;
&lt;li&gt;assignment,holding  edge - directed edge Rj → Pi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基本情况&lt;/p&gt;
&lt;p&gt;如果图中不包含循环:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没有死锁&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果图中包含循环:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果每个资源类只有一个实例,那么死锁&lt;/li&gt;
&lt;li&gt;如果每个资源类有几个实例,可能死锁&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;死锁特征&lt;/h2&gt;
&lt;p&gt;死锁出现一定会出现以下四个条件,但是出现以下四个条件不一定死锁:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥: 在一个时间只能有一个进程使用资源&lt;/li&gt;
&lt;li&gt;持有并等待: 进程保持至少一个资源正在等待获取其他进程持有的额外资源&lt;/li&gt;
&lt;li&gt;无抢占: 一个资源只能被进程资源释放,进程已经完成了它的任务之后&lt;/li&gt;
&lt;li&gt;循环等待: 存在等待进程集合{P0,P1,...,Pn},P0正在等待P1所占用的资源,P1正在等待P2占用的资源...Pn-1在等待Pn的资源,Pn正在等待P0所占用的资源&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;死锁处理方法&lt;/h2&gt;
&lt;p&gt;常见方法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确保系统永远不会进入死锁状态&lt;/li&gt;
&lt;li&gt;运行系统进入死锁状态,然后恢复.&lt;/li&gt;
&lt;li&gt;忽略这个问题,假装系统中从来没有发生死锁,用于大多数操作系统,包括UNIX&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Deadlock Prevention    预防&lt;/h3&gt;
&lt;p&gt;限制申请方式&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥 - 共享资源不是必须的,必须占用非共享资源&lt;/li&gt;
&lt;li&gt;占用并等待 - 必须保证当一个进程请求的资源,它不持有任何其他资源
&lt;ul&gt;
&lt;li&gt;需要进程请求并分配其所有资源,它开始执行之前或允许进程请求资源仅当进程没有资源&lt;/li&gt;
&lt;li&gt;资源利用率低,可能发生饥饿&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;无抢占 -
&lt;ul&gt;
&lt;li&gt;如果进程占有某些资源,并请求其他不能被立即分配的资源,则释放当前正占有的资源&lt;/li&gt;
&lt;li&gt;被抢占资源添加到资源列表中&lt;/li&gt;
&lt;li&gt;只有当它能够获得旧的资源以及它请求新的资源,进程可以得到执行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;循环等待 - 对所有资源类型进行排序,并要求每个进程按照资源的顺序进行申请&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Deadlock Avoidance     避免&lt;/h3&gt;
&lt;p&gt;需要系统具有一些额外的先验信息提供&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最简单和最有效的模式是要求每个进程声明它可能需要的每个类型资源的最大数目&lt;/li&gt;
&lt;li&gt;资源的分配状态是通过限定提供与分配的资源数量,和进程的最大需求&lt;/li&gt;
&lt;li&gt;死锁避免算法动态检查的资源分配状态,以确保永远不会有一个环形等待状态&lt;/li&gt;
&lt;li&gt;当一个进程请求可用资源,系统必须判断立即分配是否能使系统处于安全状态&lt;/li&gt;
&lt;li&gt;系统处于安全状态指: 针对所有进程,存在安全序列&lt;/li&gt;
&lt;li&gt;序列&amp;lt;P1,P2,...,Pn&amp;gt;是安全的: 针对每个Pi,Pi要求的资源能够由当前可用的资源+所有的Pj持有的资源来满足,其中j&amp;lt;i.
&lt;ul&gt;
&lt;li&gt;如果Pi资源的需求不是立即可用,那么Pi可以等到所有Pj完成&lt;/li&gt;
&lt;li&gt;当Pi完成后,Pi+1可以得到所需要的资源,执行,返回所分配的资源,并终止.&lt;/li&gt;
&lt;li&gt;用同样的方法,Pi+2,Pi+3和Pn能获得其所需的资源.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果系统处于安全状态→无死锁&lt;/li&gt;
&lt;li&gt;如果系统处于不安全状态→可能死锁&lt;/li&gt;
&lt;li&gt;避免死锁: 确保系统永远不会进入不安全状态&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Deadlock Detection     检测&lt;/h3&gt;
&lt;p&gt;每个资源类型单一实例&lt;/p&gt;
&lt;p&gt;Maintain wait-for graph&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;节点是进程&lt;/li&gt;
&lt;li&gt;Pi→Pj: Pi等待Pj&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;定期调用检测算法来搜索图中是否存在循环&lt;/p&gt;
&lt;p&gt;算法需要n^2次操作,n是图中顶点的数目&lt;/p&gt;
&lt;p&gt;数据结构:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Available: 长度为M的向量表示每种类型可用资源的数量&lt;/li&gt;
&lt;li&gt;Allocation: 一个nxm矩阵定义了当前分配给各个进程每种类型资源的数量,如果Alocation[i, j] = k, 进程Pi拥有资源Rj的k个实例&lt;/li&gt;
&lt;li&gt;Request: 一个nxm矩阵表示各进程的当前请求.如果Request[i, j] = k,表示进程Pi请求k个资源Pj的实例&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;具体算法(跳过了,看视频)&lt;/p&gt;
&lt;p&gt;检查算法使用&lt;/p&gt;
&lt;p&gt;何时,使用什么样的频率来检测依赖于:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;死锁多久可能会发生?&lt;/li&gt;
&lt;li&gt;多少进程需要被回滚? one for each disjoint cycle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果检测算法多次被调用,有可能是资源图有多个循环,所以我们无法分辨出多个可能死锁进程中的哪些&quot;造成&quot;死锁&lt;/p&gt;
&lt;h3&gt;Recovery from Deadlock 恢复&lt;/h3&gt;
&lt;p&gt;终止所有的死锁进程&lt;/p&gt;
&lt;p&gt;在一个时间内终止一个进程直到死锁消除&lt;/p&gt;
&lt;p&gt;终止进程的顺序应该是:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程的优先级&lt;/li&gt;
&lt;li&gt;进程运行了多久以及需要多少时间才能完成&lt;/li&gt;
&lt;li&gt;进程占用的资源&lt;/li&gt;
&lt;li&gt;进程完成需要的资源&lt;/li&gt;
&lt;li&gt;多少进程需要被终止&lt;/li&gt;
&lt;li&gt;进程是交互还是批处理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选择一个受孩子 - 最小的成本&lt;/p&gt;
&lt;p&gt;回滚 - 返回到一些安全状态,重启进程到安全状态&lt;/p&gt;
&lt;p&gt;饥饿 - 同一进程可能一直被选作受害者,包括回滚的数量&lt;/p&gt;
&lt;h2&gt;IPC&lt;/h2&gt;
&lt;h3&gt;概述&lt;/h3&gt;
&lt;p&gt;进程通信的机制及同步&lt;/p&gt;
&lt;p&gt;不使用共享变量的进程通信&lt;/p&gt;
&lt;p&gt;IPC facility 提供2个操作:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;send(message) - 消息大小固定或者可变&lt;/li&gt;
&lt;li&gt;receive(message)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果P和Q想通信,需要:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在它们之间建立通信链路&lt;/li&gt;
&lt;li&gt;通过send/recevie交换消息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通信链路的实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;物理(例如,共享内存,硬件总线)&lt;/li&gt;
&lt;li&gt;逻辑(例如,逻辑属性)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;直接通信&lt;/h3&gt;
&lt;p&gt;进程必须正确的命名对方:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;send(P, message) - 发送消息到进程P&lt;/li&gt;
&lt;li&gt;receive(Q, message) - 从进程Q接收信息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通信链路的属性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自动建立链路&lt;/li&gt;
&lt;li&gt;一条链路恰好对应一对通信进程&lt;/li&gt;
&lt;li&gt;每对进程之间只有一个链路存在&lt;/li&gt;
&lt;li&gt;链路可以是单向的,但通常是双向的&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;间接通信&lt;/h3&gt;
&lt;p&gt;定向从消息队列接收消息&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个消息对垒都有一个唯一的ID&lt;/li&gt;
&lt;li&gt;只有它们共享了一个消息队列,进程才能够通信&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通信链路的属性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只有进程共享一个共同的消息队列,才建立链路&lt;/li&gt;
&lt;li&gt;链接可以与许多进程相关联&lt;/li&gt;
&lt;li&gt;每对进程可以共享多个通信链路&lt;/li&gt;
&lt;li&gt;链接可以是单向或者双向&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;操作&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建一个新的消息队列&lt;/li&gt;
&lt;li&gt;通过消息队列发送和接收消息&lt;/li&gt;
&lt;li&gt;销毁消息队列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;原语的定义如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;send(A, message)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;receive(A, message)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通信链路缓冲&lt;/p&gt;
&lt;p&gt;通信链路缓存大小:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;0容量 - 0 message : 发送方必须等待接收方&lt;/li&gt;
&lt;li&gt;有限容量 - n messages的有限长度 : 发送方必须等待,如果队列满&lt;/li&gt;
&lt;li&gt;无限容量 - 无限长度 : 发送方不需要等待&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;信号&lt;/h3&gt;
&lt;p&gt;信号Signal&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;软件中断通知事件处理&lt;/li&gt;
&lt;li&gt;Examples: SIGFPE, SIGKILL, SIGUSRI, SIGSTOP, SIGCONT&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接收到信号时会发生什么?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;catch:  指定信号处理函数被调用&lt;/li&gt;
&lt;li&gt;ignore: 依靠操作系统的默认操作(abort, memory dump, suspend or resume process)&lt;/li&gt;
&lt;li&gt;mask:   闭塞信号因此不会传送(可能是暂时的,当处理同样类型的信号)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不足:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不能传输要交换的任何数据&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;管道&lt;/h3&gt;
&lt;p&gt;数据交换&lt;/p&gt;
&lt;p&gt;子进程从父进程继承文件描述符(0 stdin, 1 stdout, 2 stderr)&lt;/p&gt;
&lt;p&gt;进程不知道(或不关心)从键盘,文件,程序读取或写入到终端,文件,程序.&lt;/p&gt;
&lt;p&gt;例如: $ ls | more (两个进程, 管道是缓存,对于ls来说是stdout,对于more来说是stdin )&lt;/p&gt;
&lt;h3&gt;消息队列&lt;/h3&gt;
&lt;p&gt;消息队列按FIFO来管理消息&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;message: 作为一个字节序列存储&lt;/li&gt;
&lt;li&gt;message queues: 消息数组&lt;/li&gt;
&lt;li&gt;FIFO &amp;amp;  FILO configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;共享内存&lt;/h3&gt;
&lt;p&gt;进程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个进程都有私有地址空间&lt;/li&gt;
&lt;li&gt;在每个地址空间内,明确地设置了共享内存段&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;快速,方便地共享数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不足&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必须同步数据访问&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最快的方法&lt;/p&gt;
&lt;p&gt;一个进程写另一个进程立即可见&lt;/p&gt;
&lt;p&gt;没有系统调用干预&lt;/p&gt;
&lt;p&gt;没有数据复制&lt;/p&gt;
&lt;p&gt;不提供同步&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Socket&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>王道408-数据结构</title><link>https://github.com/posts/%E7%8E%8B%E9%81%93408-%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/</link><guid isPermaLink="true">https://github.com/posts/%E7%8E%8B%E9%81%93408-%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/</guid><description>(draft)图片待修复</description><pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;!--#数据结构--&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;数据结构的笔记不是看王道视频整理的，而是根据南京邮电大学的数据结构课程整理的。希望各位同学不要混淆，当然，考408的同学也可以参考。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;1  绪论&lt;/h2&gt;
&lt;h3&gt;1.1算法的基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;程序与算法的区别和联系&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;联系：程序是计算机指令的有序集合，是算法用某种程序设计语言的表述，是算法在计算机上的具体实现。
区别：在语言描述上不同，程序必须是用规定的程序设计语言来写，而算法的描述形式包括自然语言、伪代码、流程图和程序语言等;算法所描述的步骤—定是有限的，而程序可以无限地执行下去，比如一个死循环可以称为程序，但不能称为算法。&lt;/p&gt;
&lt;h3&gt;1.2数据结构的基本概念&lt;/h3&gt;
&lt;p&gt;数据结构是由某一数据对象及该对象中所有数据元素之间的关系组成的。数据结构包括数据的逻辑结构，存储结构及数据的运算三方面的内容。&lt;/p&gt;
&lt;h3&gt;1.3数据抽象和抽象数据类型&lt;/h3&gt;
&lt;p&gt;抽象数据类型（ADT）是一个数据模型以及在其上定义的运算集合。最主要的特征是数据封装和信息隐蔽。&lt;/p&gt;
&lt;h3&gt;1.4描述数据结构和算法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;算法的五个特征&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;输入：0个及以上&lt;/li&gt;
&lt;li&gt;输出：1个及以上&lt;/li&gt;
&lt;li&gt;可行性&lt;/li&gt;
&lt;li&gt;确定性&lt;/li&gt;
&lt;li&gt;有穷性&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;1.5算法分析的基本方法&lt;/h3&gt;
&lt;p&gt;算法的时间复杂度一般是指程序运行从开始到结束所需的时间。&lt;/p&gt;
&lt;p&gt;算法执行时间需通过依据该算法编制的程序在计算机上运行所消耗的时间来度量。&lt;/p&gt;
&lt;p&gt;度量算法执行时间的方法：事后统计法，事前估计法&lt;/p&gt;
&lt;p&gt;影响算法时间效率最主要因素是问题规模。&lt;/p&gt;
&lt;h2&gt;2  线性表&lt;/h2&gt;
&lt;h3&gt;2.1线性表的定义及基本操作&lt;/h3&gt;
&lt;h3&gt;2.2线性表的顺序存储&lt;/h3&gt;
&lt;h4&gt;1. 数据结构定义&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#define MaxSize 100   //定义顺序表的最大长度
typedef struct {
   ElemType data[MaxSize];
   int length;       //顺序表的当前长度
}SqlList;            //顺序表的类型定义
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 基本操作&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;插入操作&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;判断索引是否越界&lt;/li&gt;
&lt;li&gt;判断元素个数是否大于最大长度&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;索引 i 之后的元素往后移一位&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;将元素e放入位置 i 处&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;删除操作&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;判断索引是否越界&lt;/li&gt;
&lt;li&gt;删除索引 i 处的元素 e&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;将 i 后边的元素向前移一位&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2.3线性表的链接存储&lt;/h3&gt;
&lt;h4&gt;1.单链表&lt;/h4&gt;
&lt;h5&gt;1.数据结构定义&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;typedef struct LNode{
    ElemType data;         //数据域
    struct LNode *next;    //指针域
}LNode, *LinkList;
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2. 基本操作&lt;/h5&gt;
&lt;blockquote&gt;
&lt;p&gt;插入——头插法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c20307dcfc101224aaeb52b26682c6f1.png&quot; alt=&quot;image-20220919094648714&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将结点s插入到a1前面&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;s.next = L.next
L.next = s
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;插入——尾插法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/fef55f1f6ea195cc961787fc2540f689.png&quot; alt=&quot;image-20220919094717108&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将结点 s 插到最后&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;r.next = s    //r为指向尾结点的指针
r = s    
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;删除操作&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/56d7ac91db2aec975242b5c9a5e0bab6.png&quot; alt=&quot;image-20220919094741430&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;删除结点p后边的结点q&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;p.next = p.next.next
free(q)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 双链表&lt;/h4&gt;
&lt;h5&gt;1. 数据结构定义&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f0a797669f2032e5d898f3be6de74b71.png&quot; alt=&quot;image-20220919094135965&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct DNode{
    ElemType data;
    struct Dnode *prior,*next;  //前驱和后继指针
}Dnode,*DLinkList;
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2. 基本操作&lt;/h5&gt;
&lt;blockquote&gt;
&lt;p&gt;插入操作&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4ca19316f4925de76091cd1a7c2a8338.png&quot; alt=&quot;image-20220919094904951&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;s.next = p.next      //第一步
p.next.prior = s     //第二步
s.prior = p          //第三步
p.next  = s          //第四步
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;删除操作&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b0b6fc1dd583b320cf2a2d4e6d4efc7c.png&quot; alt=&quot;image-20220919100233234&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;p.next = q.next
q.next.prior = p
free(q)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 循环链表&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;循环单链表&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/118c483d8ffdc428677cf23cdb0c8a26.png&quot; alt=&quot;image-20220919100512832&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;循环双链表&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b9df813023726a6723b2643f364c1320.png&quot; alt=&quot;image-20220919100540976&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.静态链表&lt;/h4&gt;
&lt;h5&gt;1. 概念&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;静态链表是借助数组来描述线性表的链式存储结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/745abb8a395af3ac894ccad2756288ad.png&quot; alt=&quot;image-20220919100729273&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;2. 数据结构&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;#define MaxSize 50 //静态链表的最大长度
typedef struct{
    ElemType data;
    int next;      //下一个元素的数组下标
}SLinkList[MaxSize];
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3  栈和队列&lt;/h2&gt;
&lt;h3&gt;3.1栈和队列的基本概念&lt;/h3&gt;
&lt;h3&gt;3.2栈和队列的顺序存储结构&lt;/h3&gt;
&lt;h4&gt;1. 栈的顺序存储&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#define MaxSize 50;
typedef struct{
    ElemType data[MaxSize];  //存放栈中元素
    int top;                 //栈顶指针
} SqStack;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 栈的基本操作&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f63c3b836cc6fa9602531283753d6783.png&quot; alt=&quot;image-20220919101728348&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化时栈顶指针 top 指向 -1&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;入栈&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;S.data[++S.top] = x; //指针先加1，再入栈
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;出栈&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;x = S.data[S.top--]; //先出栈，指针再减1 
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.共享栈&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/a7e08414bbea46f212be6484b2c29a46.png&quot; alt=&quot;image-20220919102322840&quot; /&gt;&lt;/p&gt;
&lt;p&gt;两个栈的栈顶指针都指向栈顶元素，top0=-1时0号栈为空，top1=MaxSize时1号栈为空;仅当两个栈顶指针相邻(top1-top0=1）时，判断为栈满。当0号栈进栈时top0先加1再赋值，1号栈进栈时top1先减1再赋值:出栈时则刚好相反。&lt;/p&gt;
&lt;h4&gt;4. 队列的顺序存储&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#define MaxSize 50
typedef struct{
    ElemType data[MaxSize];
    int front,rear;     //队头指针和队尾指针
}SqQueue;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;初始状态（队空条件)：Q.front== Q.rear ==0。&lt;/li&gt;
&lt;li&gt;进队操作：队不满时，先送值到队尾元素，再将队尾指针加1。&lt;/li&gt;
&lt;li&gt;出队操作：队不空时，先取队头元素值，再将队头指针加1。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ec8d9531588313aa40be9eabd1a795a0.png&quot; alt=&quot;image-20220919103608190&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.循环队列&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;区分队满和队空的三种方法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;三种方法进栈，入栈操作都一样&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进栈：队尾指针进1——Q.rear = (Q.rear+1)%MaxSize&lt;/li&gt;
&lt;li&gt;出栈：队首指针进1——Q.front = (Q.front+1)%MaxSize&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;牺牲一个存储单元&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/a9c2647888753e4af37788acbd0374c9.png&quot; alt=&quot;image-20220919104528598&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;队满条件： (Q.rear+1)%MaxSize == Q.front。&lt;/li&gt;
&lt;li&gt;队空条件：Q.front==Q.rear&lt;/li&gt;
&lt;li&gt;队列中元素的个数: (Q.rear－Q.front+MaxSize) % Maxsize。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;类型中增设表示元素个数的数据成员。这样，队空的条件为Q.size=0;队满的条件为Q.size=MaxSize。这两种情况都有Q.front=Q.rear&lt;/li&gt;
&lt;li&gt;类型中增设tag 数据成员，以区分是队满还是队空。tag等于0时，若因删除导致Q.front=Q.rear，则为队空;tag等于1时，若因插入导致Q.front==Q.rear,则为队满。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.3栈和队列的链式存储结构&lt;/h3&gt;
&lt;h4&gt;1. 栈的链式存储&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5bc11cc82d9bd752faf9814581e2f51b.png&quot; alt=&quot;image-20220919102447029&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct Linknode{
    ElemType data;
    struct Linknode *next;
} LiStack;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;出栈和进栈都在链表的表头进行&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 队列的链式存储&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;typedef struct LinkNode{         //链式队列结点
    ElemType data;
    struct LinkNode *next;
}LinkNode; 

typedef struct{                 //链式队列
     LinkNode *front,*rear;     //队头和队尾指针
}LinkQueue;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;队列判空&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Q.front = Q.rear
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;入队&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;//创建一个新结点s
Q.rear-&amp;gt;next = s
Q.rear = s
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;出队&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;LinkNode *p = Q.front-&amp;gt;next;       //p为队列的第一个结点，Q.front指向头结点
Q.front-&amp;gt;next = p-&amp;gt;next;
if (Q.rear == p){                 //队列中只有一个元素
    Q.rear = Q.front;             //队尾指针指向头结点
}
free(p);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. 双端队列&lt;/h4&gt;
&lt;p&gt;双端队列是指允许两端都可以进行入队和出队操作的队列。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5e454486df0e395051f447eed035a421.png&quot; alt=&quot;image-20220919110919339&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输出受限的双端队列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4631f91b157dfacf4f5016052138d8ee.png&quot; alt=&quot;image-20220919111010794&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入受限的双端队列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/db8b986bb3beada9da8f0fbf7963abe4.png&quot; alt=&quot;image-20220919111110381&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.4表达式计算（栈）&lt;/h3&gt;
&lt;p&gt;中缀表达式，后缀表达式，前缀表达式&lt;/p&gt;
&lt;h3&gt;3.5递归（栈）&lt;/h3&gt;
&lt;p&gt;将递归算法变为非递归算法&lt;/p&gt;
&lt;h2&gt;4 数组&lt;/h2&gt;
&lt;h3&gt;4.1数组的基本概念&lt;/h3&gt;
&lt;h3&gt;4.2特殊矩阵&lt;/h3&gt;
&lt;h4&gt;1. 对称矩阵的压缩存储&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;按行优先，下标从 0 开始&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b66fbc3253f6facc5bd257e2634beb5b.png&quot; alt=&quot;image-20220920092159201&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. 三角矩阵的压缩存储&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;下三角矩阵（按行优先，下标从 0 开始）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/3e99871cd1b1bcdc51511b15da2ca5fb.png&quot; alt=&quot;image-20220920092627208&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下三角矩阵（按行优先，下标从 0 开始）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/2a05e615cc58538881b126ef94208ee3.png&quot; alt=&quot;image-20220920093712556&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/038754e8c34fc7abad1350b2870aab1b.png&quot; alt=&quot;image-20220920093728064&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.三对角矩阵&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;按行优先，下标从 0 开始&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d52e4374812a5381b973f5550bb1ef88.png&quot; alt=&quot;image-20220920094054711&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.3稀疏矩阵&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;将非零元素及相应的行和列构成一个三元组（行标，列表，值）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/fc368d1af4179c51b0f18a655bd2cce3.png&quot; alt=&quot;image-20220920094332301&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以按行优先（行三元组表）或者列优先（列三元组表）存在在一个三元组中。&lt;/li&gt;
&lt;li&gt;稀疏矩阵快速转置算法所需的num数组与k数组
&lt;ol&gt;
&lt;li&gt;num[j]统计稀疏矩阵A中列号为 j 的==非零元素==个数&lt;/li&gt;
&lt;li&gt;k[j] 统计稀疏矩阵A中列号从 0 到 j-1 的非零元素个数之和。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5  树和二叉树&lt;/h2&gt;
&lt;h3&gt;5.1树的基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;度为 m 的树中第 i 层上至多有 m^(i-1) 个节点&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9743b10f3beb621e3329993c3b28f81f.png&quot; alt=&quot;image-20220920094849460&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2二叉树&lt;/h3&gt;
&lt;h4&gt;5.2.1二叉树的定义及主要特征&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;非空二叉树上的叶子结点数等于度为2的结点数加1，即n0 =n2 + 1。&lt;/li&gt;
&lt;li&gt;非空二叉树上第k层上至多有 2^(k-1) 个结点(k≥1)。&lt;/li&gt;
&lt;li&gt;高度为h的二叉树至多有2^h - 1个结点（h≥1)。&lt;/li&gt;
&lt;li&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0145a2373e8347a0b70311450025104f.png&quot; alt=&quot;image-20220920095444503&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5.2.2二叉树的顺序存储和链式存储&lt;/h4&gt;
&lt;h5&gt;1. 顺序存储&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/07836c9ab170be5c137097359207c415.png&quot; alt=&quot;image-20220920100110230&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;2.链式存储&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e2445c7fba81a0f6bf3a4038bee96084.png&quot; alt=&quot;image-20220920100214079&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;链式数据结构定义&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;typedef struct BiTNode{
    ElemType data;
    struct BiTNode *lchild,*rchild;  //左，右孩子指针
}BiTNode,*BiTree;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;==在含有n个结点的二叉链表中，含有 n+1 个空链域==&lt;/p&gt;
&lt;h4&gt;5.2.3二叉树的遍历&lt;/h4&gt;
&lt;h5&gt;1.先序遍历&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;void PreOrder(BiTree T){
    if (T!=null){
        visit(I);
        PreOrder(T-&amp;gt;lchild);
        PreOrder(T-&amp;gt;rchild);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2.中序遍历&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;void InOrder(BiTree T){
    if (T!=null){
        visit(I);
        InOrder(T-&amp;gt;lchild);
        visit(I);
        InOrder(T-&amp;gt;rchild);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;3.后序遍历&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;void PostOrder(BiTree T){
    if (T!=null){
        visit(I);
        PostOrder(T-&amp;gt;lchild);
        PostOrder(T-&amp;gt;rchild);
        visit(I);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;4. 层次遍历&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;void LevelOrder(BiTree T){
    InitQueue(Q);        //初始化辅助队列
    BiTree p;
    EnQueue(Q,T);        //将根结点入队
    while (!IsEmpty(Q)){
        DeQueue(Q,p);    //删除队头元素，并用p返回（赋值给p）
        visit(p);
        if (p-&amp;gt;lchild!=null)
            EnQueue(Q,p-&amp;gt;lchild);
         if (p-&amp;gt;rchild!=null)
            EnQueue(Q,p-&amp;gt;rchild);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5.2.4 线索二叉树的基本概念和构造&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;概念&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;线索二叉树是以一定的规则将二叉树中的结点排列成一个线性序列，从而得到几种遍历序列，使得该序列中的每个结点（第一个和最后一个结点除外）都有一个直接前驱和直接后继。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;引入线索二叉树是为了加快查找结点前驱和后继的速度&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;构造&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/a0f30e64e8fc551d80314f73a45efb35.png&quot; alt=&quot;image-20220920104922000&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e4b36b2b98fd46f180781489c8a28e2c.png&quot; alt=&quot;image-20220920104939458&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;数据结构&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;typedef struct ThreadNode{
    ElemType data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;
}ThreadNode,*ThreadTree;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;通过中序遍历对二叉树线索化&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ee2a0859baee96bbf6344d050a5a4e7c.png&quot; alt=&quot;image-20220920105715332&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void Inthread(ThreadTree &amp;amp;p,ThreadTree &amp;amp;pre){
    if (p!=null){
        Inthread(p-&amp;gt;lchild,pre);    //递归，线索化左子树
        if (p-&amp;gt;lchild == NULL){
            p-&amp;gt;lchild = pre;
            p-&amp;gt;ltag = 1;
        }
        if (pre!=NULL &amp;amp;&amp;amp; pre-&amp;gt;rchild==NULL){
            pre-&amp;gt;rchild = p;
            pre-&amp;gt;rtag = 1;
        }
        pre = p;  //标记当前结点为刚刚访问过的结点
        Inthread(p-&amp;gt;rchild,pre);     //递归，线索化右子树
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;通过中序遍历建立中序搜索二叉树&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;void CreateInThread(ThreadTree T){
    ThreadTree pre = null;
    if (T!=null){
        Inthread(T,pre);           //线索化二叉树
        pre-&amp;gt;rchild = NULL;
        pre-&amp;gt;rtag = 1;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3树和森林&lt;/h3&gt;
&lt;h4&gt;5.3.1树的存储结构&lt;/h4&gt;
&lt;h5&gt;1.双亲表示法&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/eec2a0882bab3a620401b5b6904c41a7.png&quot; alt=&quot;image-20220920110932973&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MAX_TREE_SIZE 100     //树中最多结点数
typedef struct{
    ElemType data;
    int parent;              //双亲位置域
}PTNode;
typedef struct{
    PTNode nodes[MAX_TREE_SIZE];
    int n;                  //结点数
}PTree;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该存储结构利用了每个结点（根结点除外）只有唯一双亲的性质，可以很快得到每个结点的双亲结点，但求结点的孩子时需要遍历整个结构。&lt;/p&gt;
&lt;h5&gt;2.孩子表示法&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/76cc74d9cc79e16c84e305d767f1554f.png&quot; alt=&quot;image-20220920112245285&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这种存储方式寻找子女的操作非常直接，而寻找双亲的操作需要遍历n个结点中孩子链表指针域所指向的n个孩子链表。&lt;/p&gt;
&lt;h5&gt;3.孩子兄弟表示法（左孩子，右兄弟）&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b6c7bb527d43a437d894bf493396a53d.png&quot; alt=&quot;image-20220920112615626&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct CSNode{
     Elemtype data;
     struct CSNOde *nextchild,*nextsibling; 
}CSNode,*CSTree;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种存储表示法比较灵活，其最大的优点是可以方便地实现树转换为二叉树的操作，易于查找结点的孩子等,但缺点是从当前结点查找其双亲结点比较麻烦。&lt;/p&gt;
&lt;h4&gt;5.3.2森林和二叉树的转换&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;将森林中每棵树转换成二叉树（左孩子右兄弟）&lt;/li&gt;
&lt;li&gt;将第二个二叉树插到 第一个二叉树的右孩子处，以此类推&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4244bdaf96fcf9af23693c3a91189f50.png&quot; alt=&quot;image-20220920113245580&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.3.3树和森林的遍历&lt;/h4&gt;
&lt;p&gt;树的后根遍历也叫做中根遍历&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0e82e1bbba00a7f0acef33ef7a9220f6.png&quot; alt=&quot;image-20220920113534100&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.4树和二叉树的应用&lt;/h3&gt;
&lt;h4&gt;5.4.1二叉排序树&lt;/h4&gt;
&lt;h5&gt;1.查找&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;BSTNOde *BST_Search(BiTree T,ElemType key){
    while (T!=NULL &amp;amp;&amp;amp; T-&amp;gt;data!=key){
        if (key&amp;lt;T-&amp;gt;data)
            T = T-&amp;gt;lchild;
        else
            T = T-&amp;gt;rchild; 
    }
    return T;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2.插入&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;插入的结点一定是一个新添加的叶结点&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;int BST_Insert(BiTree &amp;amp;T,KeyType k){
    if (T==null){
        T = (BiTree)malloc(sizeof(BSTNode));
        T-&amp;gt;data = k;
        T-&amp;gt;lchild = T-&amp;gt;rchild = null;
        return 1;
    }
    else if (k==T-&amp;gt;data){  //树中存在相同关键字的结点，插入失败
        return 0;
    }
    else if (k&amp;lt;T-&amp;gt;data){
        return BST_Insert(T-&amp;gt;lchild,k);
    }
    else
        return BST_Insert(T-&amp;gt;rchild,k);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;3.删除&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;叶结点，直接删除&lt;/li&gt;
&lt;li&gt;只有一颗左（右）子树，直接删除，左（右）子树接替&lt;/li&gt;
&lt;li&gt;左右子树都有，让结点z的直接后继（或直接前驱）代替z，然后在子树中删除直接后继，变为第一或第二种情况&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/bd85e1cb3c6c2a1ba5d84d9c5c9cb4ab.png&quot; alt=&quot;image-20220921085318354&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;4. 查找性能&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;二叉排序树的查找效率主要取决于树的高度。&lt;/li&gt;
&lt;li&gt;平均查找长度为O(log2n)，最坏情况下平均查找长度为O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5.4.2 平衡二叉树&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;平均查找长度为O(log2n)&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;1. 插入&lt;/h5&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/937f1788b653b9bbee4930f96e3e2897.png&quot; alt=&quot;image-20220921090129561&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/fc70ee392f5aaf3d65f6721dbc36563c.png&quot; alt=&quot;image-20220921090242165&quot; /&gt;&lt;/p&gt;
&lt;p&gt;例：以关键字序列{16,3,7,11,9,26,18,14,15}构造一颗AVL树（二叉平衡树）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/cff33d4b58e650ef9f2a8acce16c5964.png&quot; alt=&quot;image-20220921091004360&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.4.3哈夫曼(Huffman)树和哈夫曼编码&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;哈夫曼树的构造&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;给定n个权值分别为W1, W2,……,Wn的结点，构造哈夫曼树的算法描述如下:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将这n个结点分别作为n棵仅含一个结点的二叉树，构成森林F。&lt;/li&gt;
&lt;li&gt;构造一个新结点，从F中选取两棵根结点权值最小的树作为新结点的左、右子树，并且将新结点的权值置为左、右子树上根结点的权值之和。&lt;/li&gt;
&lt;li&gt;从F中删除刚才选出的两棵树，同时将新得到的树加入F中。&lt;/li&gt;
&lt;li&gt;重复步骤2和3，直至F中只剩下一棵树为止。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;6  图&lt;/h2&gt;
&lt;h3&gt;6.1图的基本概念&lt;/h3&gt;
&lt;h3&gt;6.2图的存储及基本操作&lt;/h3&gt;
&lt;h4&gt;6.2.1邻接矩阵法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/954a7e5a5fb28e8c943d09c5407ee117.png&quot; alt=&quot;image-20220921091647501&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define MaxVertexNum 100      //顶点数目的最大值
typedef char VertexType;      //顶点的数据类型
typedef int EdgeType          //带权图边上权值的数据类型
typedef struct{
    VertexType Vex[MaxVertexNum];    //顶点表
    EdgeType Edge[MaxVertexNum][MaxVertexNum]; //边表
    int vexnum,arcnum;        //图的当前顶点数和弧数
}MGraph;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/33bc8113763b65e75211874b335c572f.png&quot; alt=&quot;image-20220921092245382&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.2.2邻接表表示法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#define MaxVertexNum 100      //图中顶点数目的最大值
typedef struct ArcNode{       //边表结点
    int adjvex;              
    struct ArcNode *next;    
}ArcNode;
typedef struct VNode{        //顶点表结点
    VertexType data;         //顶点信息
    ArcNode *first;          //指向第一条以赴该节点的指针
}VNode,AdjList[MaxVertexNum];
typedef struct{
    AdjList vertices;    //邻接表
    int vexnum,arcnum;   //图中顶点数和弧数
}ALGraph;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/052a54d94b46f8301e14f752a7c6b584.png&quot; alt=&quot;image-20220921093723288&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.3图的遍历&lt;/h3&gt;
&lt;h4&gt;6.3.1深度优先搜索&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool visited [MAX VERTEX NUM];//访问标记数组
void BFSTraverse(Graph G){   //对图G进行广度优先遍历
    for(i=0;i&amp;lt;G.vexnum;++i)
        visited[i]=FALSE;   //访问标记数组初始化
    InitQueue (Q);          //初始化辅助队列Q
    for(i=0;i&amp;lt;G.vexnum; ++i)//从0号顶点开始遍历
        if(!visited[i])     //对每个连通分量调用一次BFS
            BFS(G,i);       //vi未访问过,从vi开始BFS
    
void BFS(Graph G, int v){ //从顶点v出发,广度优先遍历图G
    visit(v);             //访问初始顶点V
    visited[v]=TRUE;      //对v做已访问标记
    Enqueue(Q, v);        //顶点v入队列Q
    while(!isEmpty (Q)){
        DeQueue(Q, v);    //顶点v出队列
        for(w=FirstNeighbor(G,v);w&amp;gt;=0;w=NextNeighbor(G,v,w)){      //检测v所有邻接点
            if(!visited[w]){ //w为v的尚未访问的邻接顶点
            visit(w);        //访问顶点w
            visited[w]=TRUE; //对w做已访问标记
            EnQueue(Q,w);    //顶点w入队列
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;性能分析&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;最坏情况下，空间复杂度O(|V|)&lt;/li&gt;
&lt;li&gt;采用领接表存储，时间复杂度为 O(|V|+|E|)。采用邻接矩阵存储，时间复杂度为 O(|V|^2)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;6.3.2广度优先搜索&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;bool visited[MAX_VERTEX_NUM];      //访问标记数组
void  DFSTraverse(Graph G){        //对图G进行深度优先遍历
    for(v=0;v&amp;lt;G.vexnum;++v)
       visited[v] =FALSE;          //初始化已访问标记数据
    for (v=0;v&amp;lt;G.vexnum;++v)       //本代码中是从v=0开始遍历
       if (!visited[v])
          DFS(G,v);
}
void DFS (Graph G,int v){         //从顶点v出发，深度优先遍历图G
    visit(v) ;                    //访问顶点v
    visited[v]=TRUE;              //设已访问标记
    for(w=FirstNeighbor(G,v);w&amp;gt;=0;w=NextNeighbor(G,v,w))
    if (!visited[w])              //w为v的尚未访问的邻接顶点
        DFS(G,w);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;性能分析&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;空间复杂度O(|V|)&lt;/li&gt;
&lt;li&gt;采用领接表存储，时间复杂度为 O(|V|+|E|)。采用邻接矩阵存储，时间复杂度为 O(|V|^2)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.4图的基本应用&lt;/h3&gt;
&lt;h4&gt;6.4.1拓扑排序&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;步骤&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;从AOV网中选择一个没有前驱的顶点并输出。&lt;/li&gt;
&lt;li&gt;从网中删除该顶点和所有以它为起点的有向边。&lt;/li&gt;
&lt;li&gt;重复①和②直到当前的AOV网为空或当前网中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;算法实现&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;bool Topologicalsort (Graph G){
    InitStack(S);    //初始化栈,存储入度为0的顶点
    for (int i=0;i&amp;lt;G.vexnum;i++)
      if(indegree[i]==0)
        Push(s,i);   //将所有入度为0的顶点进栈
    int count=0;     //计数,记录当前已经输出的顶点数
    while(!IsEmpty(S)){     //栈不空,则存在入度为0的顶点
        Pop (s,i);          //栈顶元素出栈
        print[count++]=i;   //输出顶点i
        for(p=G.vertices[i].firstarc;p;p=p-&amp;gt;nextarc){
        //将所有i指向的顶点的入度减1，并且将入度减为0的顶点压入栈S
            v=p-&amp;gt;adjvex;
            if(!(--indegree[v]))
               Push (s,v);    //入度为0.入栈
            )
        }
        if (count&amp;lt;G.vexnum)
            return false;     //排序失败，有向图中有回路
        else
            return true;      //拓扑排序成功
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6e459fc5376ba82c91f384a288364ffc.png&quot; alt=&quot;image-20220921102730530&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c4da191f7cda1246243f1191f264a06a.png&quot; alt=&quot;image-20220921102749943&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.4.2关键路径&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;正向找最大，反向找最小&lt;/li&gt;
&lt;li&gt;最早时间=最晚时间 ：关键路径&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6ac12457360c48e5ff7a41d786968b1c.png&quot; alt=&quot;image-20220921104759547&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.4.3 最小代价生成树&lt;/h4&gt;
&lt;h5&gt;1.Prim算法&lt;/h5&gt;
&lt;p&gt;初始时从图中任取一顶点（如顶点1）加入树T，此时树中只含有一个顶点，之后选择一个与当前T中顶点集合距离最近的顶点，并将该顶点和相应的边加入T，每次操作后T中的顶点数和边数都增1。以此类推，直至图中所有的顶点都并入T，得到的T就是最小生成树。此时T中必然有n-1条边。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/72a40ffa4a9e090cefd472906dc57a47.png&quot; alt=&quot;image-20220921100546087&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;2.Kruskal算法&lt;/h5&gt;
&lt;p&gt;初始时为只有n个顶点而无边的非连通图T= {V,{}}，每个顶点自成一个连通分量，然后==按照边的权值由小到大的顺序，不断选取当前未被选取过且权值最小的边==，若该边依附的顶点落在T中不同的连通分量上，则将此边加入T，否则舍弃此边而选择下一条权值最小的边。以此类推，直至T中所有顶点都在一个连通分量上。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/85ec04ef8d3baf4197fdbf8c9b1a8665.png&quot; alt=&quot;image-20220921100821070&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.4.4最短路径&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c2f332fbd67997373b611a35434ff7bc.png&quot; alt=&quot;image-20220921101736790&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/19b3cafb293c5abd213ed98a01b1e22d.png&quot; alt=&quot;image-20220921102425462&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;7  搜索（Search）&lt;/h2&gt;
&lt;h3&gt;7.1搜索的基本概念&lt;/h3&gt;
&lt;h3&gt;7.2顺序搜索法&lt;/h3&gt;
&lt;p&gt;王海艳数据结构版&lt;/p&gt;
&lt;h4&gt;1.无序表的顺序搜索&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int Search(listSet L,ElemType x){
    int i;
    for (i=0;i&amp;lt;n;i++){
        if (L.element[i]==x)
            return i;
    }
    return -1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;搜索成功的情况下平均查找长度    ASL = (n+1)/2&lt;/li&gt;
&lt;li&gt;搜索失败的情况下平均查找长度    ASL = n&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. 有序表的顺序搜索&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int Search(listSet L,ElemType x){
    int i;
    for (i=0;L.element[i]&amp;lt;x;i++){
        if (L.element[i]==x)
            return i;
    }
    return -1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;搜索成功的情况下平均查找长度    ASL = (n+1)/2&lt;/li&gt;
&lt;li&gt;搜索失败的情况下平均查找长度    ASL = n/2+2&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7.3二分搜索法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int Binary Search(SeqList L,ElemType key)
    int low=0,high=L.TableLen-1,mid;
    while.(low&amp;lt;=high){
        mid=(low+high)/2;   //取中间位置
        if(L.elem[mid] ==key)
           return mid;      //查找成功则返回所在位置
        else if(L.elem[mid]&amp;gt;key)
           high=mid-1;      //从前半部分继续查找
        else
           low=mid+1;       //从后半部分继续查找
    }
    return-1;               //查找失败,返回-1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;时间复杂度为O(log2n)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;判定树&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f04ef596d3d7a4216c3096e71f2ec98e.png&quot; alt=&quot;image-20220921111436093&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在等概率情况下，查找成功(圆形结点)的ASL=(1×1+2×2＋3x4＋4×4)/11 =3，查找不成功（方形结点）的ASL=(3x4＋4x8)/12= 11/3。&lt;/p&gt;
&lt;h3&gt;7.4 B-树及其基本操作&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;B树适用于外搜索的理由&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;B树的每个节点可以存储多个关键字,它将节点大小设置为磁盘页的大小、充分利用了磁盘预读的功能。每次读取磁盘页时就会读取整个节点。也正因每个节点存储着非常多个关键字、树的深度就会非常的小。进而要执行的磁盘读取操作次数就会非常少，更多的是在内存中对读取进来的数据进行查找。&lt;/p&gt;
&lt;h4&gt;1.插入&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ee043579772ea1548efb4a5603c1610a.png&quot; alt=&quot;image-20220921113634166&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. 删除&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4d64a0309e672f8c2a26fcc764897f49.png&quot; alt=&quot;image-20220921113943110&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ad70e1281812fd0358879a1695e8aa39.png&quot; alt=&quot;image-20220921113957628&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3. B-树的特性&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6c3c5eff849de7a80ab045927fc2bb3f.png&quot; alt=&quot;image-20220921114124493&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;7.5散列(Hash)表&lt;/h3&gt;
&lt;h4&gt;1.概念&lt;/h4&gt;
&lt;p&gt;散列函数：一个把查找表中的关键字映射成该关键字对应的地址的函数，记为Hash(key)=Addr&lt;/p&gt;
&lt;p&gt;散列(Hash)表：根据关键字而直接进行访问的数据结构。&lt;/p&gt;
&lt;p&gt;常用的散列函数:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;直接定址法：H(key)=a x key + b&lt;/li&gt;
&lt;li&gt;除留余数法：H(key)=key % p&lt;/li&gt;
&lt;li&gt;平方取中法&lt;/li&gt;
&lt;li&gt;折叠法：将关键字值从左到右划分成位数相等的若干个部分值（位数与散列地址位数相等），部分值叠加，获得散列地址。如果计算结果超过地址位数，则将最高位去掉。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;处理冲突的方法:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;开放定址法
取定某一增量序列后，对应的处理方法就是确定的。通常有以下4种取法：&lt;/p&gt;
&lt;p&gt;①线性探测法。
②平方探测法(==二次探测法==)
③再散列法
④伪随机序列法&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;拉链法&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;2. 线性探测法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/cc5e57f96477cfbd3f7828903aff77bb.png&quot; alt=&quot;image-20220921114829925&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.平方散列法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d05b482dd1f231f7c964acfbcc201c28.png&quot; alt=&quot;image-20220921115324253&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4. 再散列法（双散列法）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/8d48ee2799601f8ede83bf9bc49eb2f7.png&quot; alt=&quot;image-20220921115942319&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;==易错：第二个散列函数是下一个探测地址的增量==&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5. 伪随机序列法&lt;/h4&gt;
&lt;p&gt;当di=伪随机数序列时，称为伪随机序列法。&lt;/p&gt;
&lt;h4&gt;6.拉链法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/933809ad95e921122f72a95c58175844.png&quot; alt=&quot;image-20221031111156221&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ASL失败 = （2+3+1+0+0+2+0+0+0+0+0）/11 = 8/11      (王道书上的方法，以这个为准)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7.6搜索算法的分析及应用&lt;/h3&gt;
&lt;h2&gt;8  内排序&lt;/h2&gt;
&lt;h3&gt;8.1排序的基本概念&lt;/h3&gt;
&lt;h3&gt;8.2简单选择排序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;选择最小元素，放在队列前端&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/2e554b6edc5ecb760711d8651b330a3f.png&quot; alt=&quot;image-20220921163727171&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void SelectSort(ElemType A[],int n){
    for (i=0;i&amp;lt;n-1;i++){
         min=i;
        for (j=i+1;j&amp;lt;n;j++){
            if (A[i]&amp;lt;A[min])
                min=j;
        }
        if (min!=i)
            swap(A[i],A[min]);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.3直接插入排序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一个一个往前移&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c3f8f6498bb172c45980aa7aa03678e7.png&quot; alt=&quot;image-20220921163638212&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void InsertSort(ElemType A[],int n){
    int i,j;
    for (i=2;i&amp;lt;=n;i++){
        if (A[i]&amp;lt;A[i-1]){
           A[0]=A[i];               //复制为哨兵，A[0]不存放元素
           for (j=i-1;A[0]&amp;lt;A[j];--j)//找到最后一个比A[0]的元素   
               A[j+1] = A[j];       //依次往后移
            A[j+1] = A[0];          //将A[0]插入
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.4冒泡排序(bubble sort)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一个一个比较&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/50b481b01c2ed29d8d23fb3c6c2f4bb6.png&quot; alt=&quot;image-20220921165307850&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下边代码是每趟排序选出一个最小位置，与上图过程不一致&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;void BubbleSort(ElemType A[],int n){
    for (i=0;i&amp;lt;n-1;i++){
        flag = false;
        for (j=n-1;j&amp;gt;i;j--)
            if (A[j-1]&amp;gt;A[j]){
                swap(A[j-1],A[j]);
                flag = true;
            }
        if (flag = false)
            return;           //本趟遍历没有发生交换，说明已经有序
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.5希尔排序(shell sort)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;增量之间的插入排序（常用增量）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1501677abce0a55c5eb2fefdd177fd3c.png&quot; alt=&quot;image-20220921170125199&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void sort(Comparabel[] a){
    int len=a.length;
    int h=1;
    while (h&amp;lt;len/2)
        h=2*h+1;
    while(h&amp;gt;=1){
        for (int i=h;i&amp;lt;len;i++){
            for (int j=i;j&amp;gt;=h &amp;amp;&amp;amp; less(a[j],a[j-h]);j-=h)
                exch(a,j,j-h);
        }
        h=h/2;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.6快速排序&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;王道&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/3769025c8cdd402d43007569576aa5c2.png&quot; alt=&quot;image-20220921171940038&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/489c71d81b1a7ff336926c66f8b5d604.png&quot; alt=&quot;image-20220921172005033&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void QuickSort (ElemType A[],int low,int high){
    if(low&amp;lt;high){   //递归跳出的条件
        //Partition ()就是划分操作，将表A[low…high]划分为满足上述条件的两个子表
        int pivotpos=Partition(A,low, high); //划分
        QuickSort(A,low,pivotpos-1); //依次对两个子表进行递归排序
        QuickSort(A,pivotpos+1,high);
    }
}
int Partition(ElemType A[],int low,int high){ //一趟划分
    ElemType pivot=A [low];//将当前表中第一个元素设为枢轴，对表进行划分
    while(low&amp;lt;high){       //循环跳出条件
        while(low&amp;lt;high&amp;amp;&amp;amp;A[high]&amp;gt;=pivot) 
            --high;
        A[low]=A[high];   //将比枢轴小的元素移动到左端
        while(low&amp;lt;high&amp;amp;&amp;amp;A[low]&amp;lt;=pivot) 
            ++low;
        A[high]=A[low]; //将比枢轴大的元素移动到右端
        A[low]=pivot;   //枢轴元素存放到最终位置
        return low;     //返回存放枢轴的最终位置
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;南邮&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1847b4ee0cf4f452ab0f31f7f9e4324a.png&quot; alt=&quot;image-20221205113630535&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d0829a5d913283ce872b7124a0e40a0c.png&quot; alt=&quot;image-20221205113721395&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;示例&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;待排序序列 48  36  68  72  12  48  02
第一趟    (12  36  02  48)  48  (72  68) 
第二趟    (02)  12  (36  48)  48  (72  68)       对左边进行排序
第三趟    02  12  36  48  48  72  68             对（36,48）进行排序 
第四趟    02  12  36  48  48  68  72             对右边进行排序
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.7堆排序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;①建堆     ②m/2    ③建大根堆     ④与最后一个换&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/3c69db6a831c575e3b552c52460689c7.png&quot; alt=&quot;image-20220921172714370&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.8两路合并排序(merge sort)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;分组再结合&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1ccb8375c67a36aeab59efda4d7c8fc8.png&quot; alt=&quot;image-20220922090326830&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//将前后两个相邻的顺序表合成一个
ElemType *B = (ElemType *)malloc((n+1)*sizeof(ElemType));   //辅助数组B
//A[low,mid],A[mid+1,high]有序，将他们两个合并到一起
void merge(ElemType A[],int low,int mid,int high){
    for (int k=low;k&amp;lt;=high;k++){
        B[k]=A[k];
    }
    for (i=low,j=mid+1,k=i;i&amp;lt;=mid&amp;amp;&amp;amp;j&amp;lt;=high;k++){
        if (B[i]&amp;lt;B[j])
            A[k]=B[i++];
        else
            A[k]=B[j++];
    }
    //若一个表未检测完，复制
    while (i&amp;lt;=mid)
        A[k++]=B[i++];
    while (j&amp;lt;=high)
        A[k++]=B[j++];
}

//归并排序
void MergeSort(ElemType A[],int low,int high){
    if (low&amp;lt;high){
        int mid=(low+high)/2;
        MergeSort(A,low,mid);
        MergeSOrt(A,mid+1,high);
        merge(A,low,mid,high);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.9基数排序&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/a2a87cdd0017c5db4b025a9dd8b363e4.png&quot; alt=&quot;image-20220922091544472&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.10各种内部排序算法的比较&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/814f043ae4f009c1751228698986a46c.png&quot; alt=&quot;image-20220922093415193&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不稳定性：==快些(希)选==一==堆==好友来聊天&lt;/p&gt;
&lt;p&gt;时间快：==快些归队==&lt;/p&gt;
&lt;h3&gt;8.11内部排序算法的应用&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;若==n较小==，可以采用==选择排序或插入排序==。当记录本身信息量较大时，使用选择排序较好。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若==初始状态已基本有序==，可以选用==插入排序或冒泡排序==。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==所需的辅助空间：堆排序&amp;lt;快速排序&amp;lt;归并排序==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;交换次数最小的排序是简单选择排序&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;排序速度与数据的初始排列状态没有关系的是简单选择排序。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>王道408-计组</title><link>https://github.com/posts/%E7%8E%8B%E9%81%93408-%E8%AE%A1%E7%BB%84/</link><guid isPermaLink="true">https://github.com/posts/%E7%8E%8B%E9%81%93408-%E8%AE%A1%E7%BB%84/</guid><description>(draft)图片待修复</description><pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;!--#计算机组成原理--&amp;gt;&lt;/p&gt;
&lt;h2&gt;1.计算机系统概述&lt;/h2&gt;
&lt;h3&gt;1. 计算机发展历程&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/02a710dc677a4aeb46c57a3fa3d8989a.png&quot; alt=&quot;image-20220124155140426&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.计算机系统的组成&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;冯诺依曼体系结构：以运算器为核心&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0d742dfebdb1ecb04e4064161f067207.png&quot; alt=&quot;image-20220124155439445&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;冯诺依曼体系结构的特点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/44eec20e497534bd818ef08472a396b8.png&quot; alt=&quot;image-20220124163921396&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;现在计算机体系结构：以内存为核心&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d09d3489652ef1429feae311861ccf9e.png&quot; alt=&quot;image-20220124155514424&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU，主机，外设&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6cc37c8878488314eacda92998e54616.png&quot; alt=&quot;image-20220124155601121&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.存储器&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;主存储器由存储体，MAR，MDR组成&lt;/li&gt;
&lt;li&gt;和右边的图进行类比，MAR存储CPU想要获取数据的地址，在存储体中找到后放在MDR中，CPU从MDR中把数据取走。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f68eea908c0096c58553543d9665a4bb.png&quot; alt=&quot;image-20220124161217722&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;存储体是由一个个存储单元组成的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==存储字：存储单元中二进制代码的集合==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==存储字长：存储单元中二进制代码的位数==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/7acde24209c1b10b3379eb779c000236.png&quot; alt=&quot;image-20220124161625621&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.运算器&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/8c5124abda8b51f5383bb39c0e2fc798.png&quot; alt=&quot;image-20220124162029858&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5. 控制器&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/af03fabce192609ca3ed350b03be6122.png&quot; alt=&quot;image-20220124162149845&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6. 计算机的工作过程（重点）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;程序被编译成二进制存储在主存中&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c113fde7332d03c5c6c7d400d4d79bf9.png&quot; alt=&quot;image-20220124162251737&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;工作过程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;（MAR）：表示MAR里边的值为0&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;M(MAR) ：主存储器所指向的存储单元里边的值&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OP（IR）：取操作码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ad（IR）：取地址码&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0d9c2d68262a80f4fa80595623d62d5d.png&quot; alt=&quot;image-20220124162438267&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/a1673e01ad3e109d5f03afaa038b208e.png&quot; alt=&quot;image-20220124163303409&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;7. 计算机的层次结构&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c05bd84fd952372bdbadceb6c90742ed.png&quot; alt=&quot;image-20220124164821189&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;注意编译程序和解释程序的区别&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;编译程序:将高级语言编写的源程序全部语句一次全部翻译成机器语言程序，而后再执行机器语言程序（只需翻译一次)&lt;/p&gt;
&lt;p&gt;解释程序将源程序的一条语句翻译成对应于机器语言的语句，并立即执行。紧接着再翻译下一句(每次执行都要翻译)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/db91312669c9c643d5793cbb6c39ad96.png&quot; alt=&quot;image-20220124164939418&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.计算机的性能指标&lt;/h3&gt;
&lt;h4&gt;1. 存储器&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/2e48d9ad8d1e09f14110519e9b98bbf8.png&quot; alt=&quot;image-20220124165253241&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. CPU&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/35961aa013861c4f3dfc295df302bfb9.png&quot; alt=&quot;image-20220124170445476&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5a609bde1d19810e6f9a1a1b3c301d57.png&quot; alt=&quot;image-20220124170634769&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.系统整体的性能指标&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f9c41c015f846bc3a03799885986f825.png&quot; alt=&quot;image-20220124170834659&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4. 思考&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ca2000736b8e25a38198436d22b4761c.png&quot; alt=&quot;image-20220124171228641&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;2. 数据的表示和运算&lt;/h2&gt;
&lt;h3&gt;1.进位计数制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;其他进制转十进制&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/76b5829c5c4a002e3b6e0725eadaa15a.png&quot; alt=&quot;image-20220124172254378&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;二进制&amp;lt;——&amp;gt; 八进制，十六进制  (注意：小数部分也是从右往左算)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/70bce46b02cbff05a0dee21a62ca1d6c.png&quot; alt=&quot;image-20220124172619709&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;十进制——&amp;gt;任意进制（整数部分）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d630b11c2ebe1373fbcdad4789359d00.png&quot; alt=&quot;image-20220124192353762&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;十进制——&amp;gt;任意进制（小数部分）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1962e0e7ae094806aec8f9a42327dfc9.png&quot; alt=&quot;image-20220124192439652&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;十进制转二进制（拼凑法，适用于十进制数不是很大的情况）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0af86154a6252fdbcfe74795eb658f87.png&quot; alt=&quot;image-20220124192728894&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;真值和机器数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/bfb591fa8b1feba075a98f8dc8963343.png&quot; alt=&quot;image-20220124192704256&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.BCD码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;概念&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/284b13dd902e31e7a16256ec8f0dff76.png&quot; alt=&quot;image-20220126171803519&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;8421码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5af9efeb92a0395784cfaf676a7184e2.png&quot; alt=&quot;image-20220126171920070&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;8421码相加&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;可以转换成十进制相加后，再转换成8421码&lt;/li&gt;
&lt;li&gt;机算方法：不在范围内，加6。如图所示&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/2c88f12b4d17df8062e3c519256efef6.png&quot; alt=&quot;image-20220126172432712&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;其他表示方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d775879b3681a6a167b3f227728b26d0.png&quot; alt=&quot;image-20220126174706248&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3. 字符与字符串&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ASCII码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b192b4d6a8969cf0b80c90fb5b39318d.png&quot; alt=&quot;image-20220126180204687&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;汉字的表示和编码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d0606334df791d89bbdbdb8eee5cf174.png&quot; alt=&quot;image-20220126180314162&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符串的表示形式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ead70133284d8954e91d1ce8222c0930.png&quot; alt=&quot;image-20220126180403191&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;知识回顾&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/67af3ff025a022aca5010fc0a56abc6c.png&quot; alt=&quot;image-20220126180437047&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.奇偶校验码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;校验原理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/abf6b9c8e685c652fcc436d397aa9124.png&quot; alt=&quot;image-20220126180907544&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;奇偶校验码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/db82eb0dda2d50492d8d5480706bc78e.png&quot; alt=&quot;image-20220126181124538&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;硬件实现&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0077fc747242d5ee263d3aeb2155c5a5.png&quot; alt=&quot;image-20220126192604899&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.海明码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;设计思想&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d5c3e653cd52a0128b9600575afc86a5.png&quot; alt=&quot;image-20220126192014802&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;求解步骤&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/33ff3e364df54c0f723e8720103e8dc9.png&quot; alt=&quot;image-20220126195838236&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0bbdc09794447efebdf3260bf2d5c5a8.png&quot; alt=&quot;image-20220126193248739&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全校验&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;海明码只能纠错一位（一个数据传错时，可以纠正）&lt;/li&gt;
&lt;li&gt;可以检错两位&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/cf2d2120a4b5e16a27eaf3ea00030811.png&quot; alt=&quot;image-20220126195918658&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6. 循环冗余校验码（CRC码）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本思想&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/fd865ebcc58b996325bda3a3ca10b25c.png&quot; alt=&quot;image-20220126200208426&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TxXHrXvO-1644306008648)(C:\Users\86155\AppData\Roaming\Typora\typora-user-images\image-20220126201152982.png)]&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/65d2af0eb9f902bd379b23081072e5b9.png&quot; alt=&quot;image-20220126202544654&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CRC校验码计算过程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d441a84b2f302233f75ba534afd0bb1d.png&quot; alt=&quot;image-20220126202718290&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;7. 定点数的表示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定点数和浮点数的概念&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f44b40864ec58946b471be7dc35cc79b.png&quot; alt=&quot;image-20220127144253289&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;无符号数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/73817f209a92e820eef643db4c867011.png&quot; alt=&quot;image-20220127144714178&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/59fb3a03fc275ce5e5333a30d769fa0c.png&quot; alt=&quot;image-20220127145309171&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;反码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/77c9db0581910657f44d1964ed07c331.png&quot; alt=&quot;image-20220127145448551&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;补码(高频考点)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5daed5d8076559abc4a911fd285b9277.png&quot; alt=&quot;image-20220127150050213&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;移码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/a59cc0ea2e5bdb5b7eefbabf10825f1d.png&quot; alt=&quot;image-20220127150230867&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;技巧&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/77565155a6ba1ab85593bb2ce70c38e0.png&quot; alt=&quot;image-20220127150633042&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;补码的作用（注意：==执行加法运算时，符号位一起参加运算==）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/56d1878a71fe4999ffbdb3349235e41f.png&quot; alt=&quot;image-20220127152758634&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.移位运算&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;原码的算数移位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/feba8c10befb09b3bcc1f255901a2852.png&quot; alt=&quot;image-20220127153359639&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;反码的算数移位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/029151dcfd30f6b1a83d6e136ec7fae9.png&quot; alt=&quot;image-20220127153607298&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;补码的算数移位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b0dfd4611c62cb16cc8fcbb317cc0870.png&quot; alt=&quot;image-20220127153830245&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总结&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ce062992ae7eb7f207f1da23725ed586.png&quot; alt=&quot;image-20220127153942017&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;逻辑移位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e46c366d0e8fa27d54b932da55f96f34.png&quot; alt=&quot;image-20220127154131912&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;9. 溢出判断&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d0bc7fc767a3def6f90c47c48e51e4ef.png&quot; alt=&quot;image-20220127161103371&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;符号扩展&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/243c3922ffd4f1eb2ced2c14d12a31fd.png&quot; alt=&quot;image-20220127161325545&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;10.原码乘法运算&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;机器实现（视频 P18节）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/78a6d46b6cfb08d73fbfaac0760f10d5.png&quot; alt=&quot;image-20220127163743939&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;11.补码乘法运算&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;机器实现&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9725aaa09c3de568822601e510897875.png&quot; alt=&quot;image-20220127215747197&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;手算步骤&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/281c62665c158815b5262af7436e8d6d.png&quot; alt=&quot;image-20220127215810158&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;12.原码除法运算&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;机器运算过程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ded52c58ba10c98b23733febae1b3619.png&quot; alt=&quot;image-20220128125621231&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;恢复余数法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/83b84c96e274adbb91a6e1a7cf57bfef.png&quot; alt=&quot;image-20220128131154037&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;加减交替法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ef636573ba7e44f3a8f2329004a56c5c.png&quot; alt=&quot;image-20220128131225276&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;13.补码除法运算&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/89e42a4e83a55d0c93db4cffd827a76e.png&quot; alt=&quot;image-20220128130246826&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;14.强制类型转换&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/fe4e9e607725a012bf889eac83020997.png&quot; alt=&quot;image-20220128131830289&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;15.数据的存储和排列&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;大小端模式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/70c30e4e6eba63bca5aaca0ed620175e.png&quot; alt=&quot;image-20220128132209812&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;边界对齐&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/383ef119b26cce46beb66b29f3e64bc7.png&quot; alt=&quot;image-20220128141621511&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;16. 浮点数的表示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;浮点数的表示&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/3f004271ffb074d29a89097f7f692c19.png&quot; alt=&quot;image-20220128144507292&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;尾数的规格化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/beda3a665f18598df6efd691863b7123.png&quot; alt=&quot;image-20220128144600960&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9f2e640dd5fe0f0835d07bdd6a23ad75.png&quot; alt=&quot;image-20220128144703337&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;17. IEEE 754 浮点数标准&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;标准&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d7dab764cc8be22585efb3eea2b3d638.png&quot; alt=&quot;image-20220128145831536&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;示例（十进制转换为浮点数）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e5715df560c9d84606aec8d7d16c3a50.png&quot; alt=&quot;image-20220128150220191&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;示例（浮点数转换为十进制数）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1571af1358f0063996617047dc45e43b.png&quot; alt=&quot;image-20220128150436743&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单精度浮点数表示的最大值，最小值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/99a90a5c0aeb7cfae67effd64b7185b1.png&quot; alt=&quot;image-20220128150646292&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;特殊数值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/83c20f87d1edcf4162cc3b9c04575c53.png&quot; alt=&quot;image-20220128151026706&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;18.浮点数的运算&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;和十进制的科学计数法进行类比&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1cbece81e03f93d69e2b7b0643192608.png&quot; alt=&quot;image-20220128152136960&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;浮点数加减法（P26）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/7d6dde27997a8eecb1da0bb355d926ac.png&quot; alt=&quot;image-20220128154425894&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;舍入&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f80c7a91e8efe65cbcf6d055b96c6e7c.png&quot; alt=&quot;image-20220128155152407&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强制类型转换&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e4d37e1734a0490dac5242ee752764e2.png&quot; alt=&quot;image-20220128155130204&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;19. 电路的基本原理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本的逻辑运算&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/68b8dae05c7ede4993032ee4e3db0da2.png&quot; alt=&quot;image-20220128160058822&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;复合逻辑&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b0677889bbf3665b2f1a1857b4c57686.png&quot; alt=&quot;image-20220128160542357&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;3.存储系统&lt;/h2&gt;
&lt;h3&gt;1. 主存简单模型和寻址概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;存储器芯片的基本结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/423b74ad7e7e08968578560d6bd96d31.png&quot; alt=&quot;image-20220130152344905&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/003d51260e7bad7114d37b17f5c9ee21.png&quot; alt=&quot;image-20220130153001312&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.存储器RAM&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;两种RAM的比较&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/17560478b446da55fc278e4bf9a93354.png&quot; alt=&quot;image-20220130154140182&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DRAM的刷新&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/baebd71847dbe523dfa73d507356e353.png&quot; alt=&quot;image-20220130154625070&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5728d02dd8306c5e454e0d9d918793c3.png&quot; alt=&quot;image-20220130155833007&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.ROM(只读存储器)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;为什么需要ROM？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为RAM里面的数据容易丢失。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/be79e376b083e15a08a798c44148a926.png&quot; alt=&quot;image-20220130160929183&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.存储器基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;存储器的分类&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e19327ba563202f16647ab96cc14f031.png&quot; alt=&quot;image-20220130161242889&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/83c86d359338b14ddb49fa3a73f6f86f.png&quot; alt=&quot;image-20220130161406470&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能指标&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;数据的宽度：在一个存储周期中存储的位数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f2d8db9bf23cdec9cad89cae71af1341.png&quot; alt=&quot;image-20220130161648937&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;层次化结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9cec0f570f2bf7a9116f1fdecf1a73da.png&quot; alt=&quot;image-20220130161822725&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5. 主存与CPU的连接&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CS：片选线（决定主存是否工作）这种写法是高电平有效&lt;/li&gt;
&lt;li&gt;WE：读写控制线&lt;/li&gt;
&lt;li&gt;A0~A13：地址线&lt;/li&gt;
&lt;li&gt;D0~D7：数据线&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/68259062c7825a0fe6ef511ef258f423.png&quot; alt=&quot;image-20220130162636865&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;译码器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ee2e2431ecfb8b6eb5ed8b8e1c3dc85f.png&quot; alt=&quot;image-20220130163242147&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;线选法和译码线选法的区别&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c3d1919bb0c5329a009395701c3722c8.png&quot; alt=&quot;image-20220130164058401&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.主存与CPU的连接（例题）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;分析&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d6b19c970b4c0d39dfbc865273f481cc.png&quot; alt=&quot;image-20220130165155365&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;图解&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e96610eb020b5c068e0d62b746ccd510.png&quot; alt=&quot;image-20220130165622310&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;7.双端口RAM和多模块存储器&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;双端口RAM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0520fd6af1b5b45788fc3755538256e1.png&quot; alt=&quot;image-20220130170115628&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多模块存储器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9e88f85a59961a2f558d07b164208187.png&quot; alt=&quot;image-20220130170354942&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;高位交叉编址和低位交叉编址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6ce2ca6e1b6c894a3745d7346d108e8f.png&quot; alt=&quot;image-20220130171004832&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;流水线问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/3a737ac8ddb6ab4948968973a02b57dc.png&quot; alt=&quot;image-20220130171717868&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.局部性原理及性能分析&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;局部性原理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1ee585f3fe89769c88c71948ff3f3c52.png&quot; alt=&quot;image-20220130173154035&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;性能分析&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/fe4361526bff45580c95e54900646a14.png&quot; alt=&quot;image-20220130173324687&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题（蓝色字体为答案）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/37eb0beab3cf0f5d54050b68dbb7a9c0.png&quot; alt=&quot;image-20220130173700544&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;9. Cache 地址映射&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Cache的工作原理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/74ea13954ee570d59f029025080582e4.png&quot; alt=&quot;image-20220131103221888&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地址映射——全相连映射&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9a60d0f599f53932bd4991a7741f77d4.png&quot; alt=&quot;image-20220131103738879&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地址映射——直接映射&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/49ae7e29dcd06d79dbdfd96bac3beb8e.png&quot; alt=&quot;image-20220131104100785&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地址映射——组相连映射&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/400e4fa5b9577c7402ce176e25084f23.png&quot; alt=&quot;image-20220131104445504&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;10.cache替换算法及写策略&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;替换算法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;LRU侧重于近期表现，LFU侧重于全局表现&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/7e93085202817fd85045421151803a4a.png&quot; alt=&quot;image-20220131105043802&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f344b92cf3683c9dd713132be62f4acf.png&quot; alt=&quot;image-20220131110126624&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;命中时的写策略&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;写回法&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/660fe68614678877b3096c872e77fedb.png&quot; alt=&quot;image-20220131110455683&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;全写法&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/cf1789c20d296cc26a4318086250c8dc.png&quot; alt=&quot;image-20220131110555484&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;未命中时的写策略&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;写分配法&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4cb3f213bf0102205ef0122ca249ee47.png&quot; alt=&quot;image-20220131110657919&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;非写分配法&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/90d2a4c66bc6f3c384a30d8ca10d9cdb.png&quot; alt=&quot;image-20220131110744533&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;11. 虚拟存储器&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;概念&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/30998481e1537c790c70d8119f204fd4.png&quot; alt=&quot;image-20220131111953580&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;页式虚拟存储器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c8d41a23e97320cc84215244d117c8e8.png&quot; alt=&quot;image-20220131112322122&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;段式虚拟存储器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0af91f965dc52a1f06dea16639f0415a.png&quot; alt=&quot;image-20220131112408107&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;段页式虚拟存储器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c1815e1e31586719ec49f66eec404198.png&quot; alt=&quot;image-20220131112443405&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;快表&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/93b54bcfe8c938298a9b1a8c85d37768.png&quot; alt=&quot;image-20220131112516038&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;4.指令系统&lt;/h2&gt;
&lt;h3&gt;1.指令格式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指令的含义&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/02bbdd7b906ebb9cfef1fa4bb67e79a3.png&quot; alt=&quot;image-20220131112840832&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;指令的格式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/10f052685bd16d5982bc2898eac5f1ce.png&quot; alt=&quot;image-20220131113124046&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地址码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d7b67fa0e0cee9fa562480625db7dce2.png&quot; alt=&quot;image-20220131113711077&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;扩展操作码举例&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/2be54ac08a4a9341db64af49fc99f291.png&quot; alt=&quot;image-20220131114025995&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;扩展操作码的规范&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9f01306f3784475e39097f5e936f2c98.png&quot; alt=&quot;image-20220131114229234&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;指令操作码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1fe15c5378b3d4c28371da2d9879fdcd.png&quot; alt=&quot;image-20220131114639178&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作类型&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6d9c84e38626ccc27582c02fd47e96ba.png&quot; alt=&quot;image-20220131114747086&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.指令寻址&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;数据存放&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/00a70ced4248a902180b127168f446c3.png&quot; alt=&quot;image-20220131115415882&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;指令寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4c46b30a75623eaaf91a7fdf0d68c1b9.png&quot; alt=&quot;image-20220131120018553&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.数据寻址1&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;操作数类型&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0a5c3da8ee73de4a4f105a15b8679d65.png&quot; alt=&quot;image-20220203160315336&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地址码的构成&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;寻址特征：采用哪一种方式进行寻址&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;通过寻址特征和形式地址求出有效地址&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/99f7120999049c27aa5be090c260f3af.png&quot; alt=&quot;image-20220203160601853&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;立即寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f97ef6551888a49a4c9cd5166bedacc3.png&quot; alt=&quot;image-20220203160950788&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/8ca4c770a2f12ae2fe2cc3614224f05d.png&quot; alt=&quot;image-20220203161136445&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;间接寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c84cb527ed7348b60f60c4ce857b1b20.png&quot; alt=&quot;image-20220203161401565&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;寄存器寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/863a1789926d860246fafe3f77c053eb.png&quot; alt=&quot;image-20220203161526792&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;寄存器间接寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/88c0a67f0ad5d5ed2373f72ca4279980.png&quot; alt=&quot;image-20220203161628168&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;隐含寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/84f97ceb329a64262036babbd7b0f8e4.png&quot; alt=&quot;image-20220203161742257&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总结&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/eab5e612e236b278726765295e05d51c.png&quot; alt=&quot;image-20220203161816204&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.数据寻址2——偏移寻址&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基址寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0839dbe971032805d3488a5567e07c7c.png&quot; alt=&quot;image-20220203162240926&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;变址寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/2aff49fbb7e7942d7cb5f857e3522971.png&quot; alt=&quot;image-20220203163048908&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;相对寻址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/bc35823a6ef7117bfaa7225b54ac1d51.png&quot; alt=&quot;image-20220203163432186&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;相对寻址举例&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4d3d9872e97010192d74e40210d6aca7.png&quot; alt=&quot;image-20220203164528487&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总结&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/43af5cf152cd8c7471bda7ada33847cf.png&quot; alt=&quot;image-20220203164700915&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5. 数据寻址——堆栈寻址&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d4f1b3932ae23d9900310d877c87039a.png&quot; alt=&quot;image-20220203165103334&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.CISC和RISC&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本含义&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f93f51dc007c5d16e13e310de64b3e06.png&quot; alt=&quot;image-20220203165740882&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;特点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/476e3c268554fea1b25a175fd3913b89.png&quot; alt=&quot;image-20220203165907312&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;5.中央处理器&lt;/h2&gt;
&lt;h3&gt;1.CPU功能和基本结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CPU的功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/8ef54f5cd0f96218eba5d9cffd9d782c.png&quot; alt=&quot;image-20220204141306234&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;运算器和控制器的功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1bef490ea2a0ba7594116e0b3be37c2a.png&quot; alt=&quot;image-20220204141524507&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;运算器的基本结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/067dd73e78e63d91db2a7109d5419ace.png&quot; alt=&quot;image-20220204142113636&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/a5b52300a053e81d4807e2c9f57264dd.png&quot; alt=&quot;image-20220204142656159&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;控制器的基本结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/729c87285927df4dc3d17473924f0454.png&quot; alt=&quot;image-20220204143337083&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU的基本结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e91388d44a089f1e1dd985b86b7d321e.png&quot; alt=&quot;image-20220204143620270&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.指令周期的数据流&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指令周期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5231da7517bf94ccf01b583bbc3f3248.png&quot; alt=&quot;image-20220204143912635&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;指令周期流程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/37b97c4a6a7c63cd482bbd18ec64476f.png&quot; alt=&quot;image-20220204144244474&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;取指周期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5d0865173bf37f409c72153118c5f82d.png&quot; alt=&quot;image-20220204144352993&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;间址周期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b111933c89c971f901ade5fc2e835f93.png&quot; alt=&quot;image-20220204144727112&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中断周期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b72431e4a7a219f78cba3d8b62035613.png&quot; alt=&quot;image-20220204145045629&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;指令执行方案&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ce974cc705636c1da2a6e137f92f92df.png&quot; alt=&quot;image-20220204145230729&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3. 数据通路1——CPU内部单总线通路&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;数据通路&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/bf791906bc51a8494fcae4c13f58d64e.png&quot; alt=&quot;image-20220204145502071&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU内部单总线方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在介绍这个之前，首先区别一下系统总线和内部总线。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;内部总线&lt;/strong&gt;是指同一部件，如CPU内部连接各寄存器及运算部件之间的总线;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;系统总线&lt;/strong&gt;是指同一台计算机系统的各部件，如CPU、内存、通道和各类l/o接口间互相连接的总线。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/299f7ce274c39526bf3bae0077037c00.png&quot; alt=&quot;image-20220204150409836&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;取指周期&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e9a4fc61f7f07642d57ee3448cb2a5cd.png&quot; alt=&quot;image-20220204150745077&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;间址周期&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/aecdafc3280657c24549a99760f75527.png&quot; alt=&quot;image-20220204151034301&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;执行周期&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/cc42f75142a981dcf2d678f4d6d482e8.png&quot; alt=&quot;image-20220204151252258&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4. 数据通路2——专用数据通路&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;取指周期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/eb878cd01aa9e2a77fd9a83692e1b7c5.png&quot; alt=&quot;image-20220204151703479&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;第一问&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/149506470ff9dfcd05299450739702ec.png&quot; alt=&quot;image-20220204151933329&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第二问&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b93f8bd8a7868ba93695c23af8d835e9.png&quot; alt=&quot;image-20220204152000380&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第三问&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d0364a58a1e7d390b4a0122e541aedc4.png&quot; alt=&quot;image-20220204152313291&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第四问&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/3db4609b1dbc21b388afbbbfb8489a96.png&quot; alt=&quot;image-20220204152446588&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第五问&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/681a71954c4ac6a92ddee62b274bedde.png&quot; alt=&quot;image-20220204152550115&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;第六问&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4c12179b1b5b56c989fc1a4a754f8f20.png&quot; alt=&quot;image-20220204152634574&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5. 控制器1——硬布线&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;控制器的结构和功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/004c585d0e185e5235b44bce1b1ce8ec.png&quot; alt=&quot;image-20220204155824810&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;控制单元的输入和输出&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ae935f72800910a18bb6e6f4605ec23a.png&quot; alt=&quot;image-20220204155733033&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU的控制方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f93bb13264d6bd252ab185d135941008.png&quot; alt=&quot;image-20220204155634351&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.控制器2——微程序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;控制器的设计思路&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0cdd18c80546e8d4c27eecf2176d0eae.png&quot; alt=&quot;image-20220204160400055&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;微程序的基本思想&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1915634168c52172102e2df2364da6fb.png&quot; alt=&quot;image-20220205121414411&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;微程序控制器的基本结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/15615464947d8ce7ab5d62e3ef5e8751.png&quot; alt=&quot;image-20220205121747933&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;控制存储器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c458dd1e10ba66483cb287a9b191b95f.png&quot; alt=&quot;image-20220205122032395&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;微指令的格式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/eef827072c42523f56fd88fb72cd0257.png&quot; alt=&quot;image-20220205122525266&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;微指令的编码方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/609b9c92cd0e63e07c3e49926f9b98de.png&quot; alt=&quot;image-20220205134043538&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ba538019cea94c99b68e66a8631ce7b8.png&quot; alt=&quot;image-20220205134142489&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;微指令的地址形成方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;前两种理解，后四种了解（不重要）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5c08a5b96b9719a17fe7a38b3bcc0455.png&quot; alt=&quot;image-20220205134325290&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下地址方式例题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/2519755457bd50091d3df04bc11dc11c.png&quot; alt=&quot;image-20220205135318575&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一些易混淆的概念&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;指令=微程序&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;微程序由多个微指令组成&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;微指令由多个微命令组成&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;微命令是微操作的控制信号&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;微操作是微命令的执行过程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/80814de282fdac2d673542871ec8dd51.png&quot; alt=&quot;image-20220205135623551&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;7.指令流水线的概念及性能指标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指令流水的定义&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/8185ff5c2707835d8e775b74290277b6.png&quot; alt=&quot;image-20220205140219456&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1969ed1f4a2cc2ae3c132aad66357503.png&quot; alt=&quot;image-20220205140757971&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;流水线的表示方法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d2b98f6eb2c471aeca775704d8323b68.png&quot; alt=&quot;image-20220205140709591&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;流水线的性能指标&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;吞吐率&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/cfac0068303b38656d451818f4d0d7a7.png&quot; alt=&quot;image-20220205141557742&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;加速比&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4f02bc3e41e1ab66b95b9e60cfffc891.png&quot; alt=&quot;image-20220205141822268&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;效率&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/2758e42187a15fef6205cbfff69b4f5b.png&quot; alt=&quot;image-20220205142015561&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.影响流水线的因素及分类&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;机器周期的设置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/24c8d0701755fa5c3d79f42e4c06eba2.png&quot; alt=&quot;image-20220205142525993&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;影响流水线的因素&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;结构相关&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/4221835a3645c501404205e906b793aa.png&quot; alt=&quot;image-20220205142420789&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;数据相关&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6b614c4aa27e666e7101e7bd9973a8ef.png&quot; alt=&quot;image-20220205142724384&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/fe8f8badfbe3e4d4a65ad6b9bd71e8a4.png&quot; alt=&quot;image-20220205143035517&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据相关的一些概念&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;RAW（read after write）：写后读&lt;/p&gt;
&lt;p&gt;另外两个同上&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6a18d21b437631d877b0425c15c7d121.png&quot; alt=&quot;image-20220205143316267&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;控制相关&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/766191cc9cda386de83aa94941452803.png&quot; alt=&quot;image-20220205143549521&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总结&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6363c466e3751710969b026b20be3e29.png&quot; alt=&quot;image-20220205143611590&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;流水线的分类&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/76136dc1ad238b749353ee1985a4016e.png&quot; alt=&quot;image-20220205143705278&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9b91d39dc308ed5c0c863a642bc8505a.png&quot; alt=&quot;image-20220205143723927&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;流水新的多发技术&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;超标量技术&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/96e1f2e928899566f3c705d83a81c1c9.png&quot; alt=&quot;image-20220205143838745&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;超流水技术&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/3bf2ecbc9ab6c673b2204a094f7b8df3.png&quot; alt=&quot;image-20220205143937880&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;超长指令字&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/15d4070b9449f1bd15541c5e30f3d213.png&quot; alt=&quot;image-20220205144032581&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;6.总线&lt;/h2&gt;
&lt;h3&gt;1.总线的概念和分类&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;总线的特点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9f34910e789573828f796bbc6c4555e2.png&quot; alt=&quot;image-20220206132533972&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总线特性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c229c23f044dd4947cc99470d25961d7.png&quot; alt=&quot;image-20220206132827567&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总线的分类&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/db57a052b7c34604e0edd472a1d89f35.png&quot; alt=&quot;image-20220207135755282&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;串行总线与并行总线&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/34fdb011672d31a55f47d8a4864a38b8.png&quot; alt=&quot;image-20220207135911160&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;按总线功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d05d3c876def28f6ef05836a356de0d3.png&quot; alt=&quot;image-20220207145722749&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统总线的结构&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;单总线结构&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/44c81dddc9d03a4f02d5436d403379e0.png&quot; alt=&quot;image-20220207150008889&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;双总线结构&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b3a9e56b01eb6ce3e0e9fc977d6b440a.png&quot; alt=&quot;image-20220207150221254&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;三总线结构&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/bb65e367a83d641bf904ef31077ac3fb.png&quot; alt=&quot;image-20220207150904024&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.性能指标&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c2e78707f1d41fde9843446d7c70f708.png&quot; alt=&quot;image-20220207151751475&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f6b77bcf436c5d5c35b46108b259e077.png&quot; alt=&quot;image-20220207152007687&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f7059eb54f65348e8ad27c502652bb5b.png&quot; alt=&quot;image-20220207152906097&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;带宽例题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6eadf5945673568ab81fff23690eaa1a.png&quot; alt=&quot;image-20220207152737201&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;上节遗留问题：串行总线和并行总线哪个速度更快&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1e1ec43570b543626f2c025d3d94478f.png&quot; alt=&quot;image-20220207152646974&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总结&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e7514f1e2ad780ca121af19da83f2c02.png&quot; alt=&quot;image-20220207153237319&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3. 总线仲裁&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本特点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/95c142936a186720034b45e0a530590c.png&quot; alt=&quot;image-20220207153156272&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集中仲裁方式（重要）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;链式查询方式&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b23048ea9ef6cf64740f41fa50ba4a37.png&quot; alt=&quot;image-20220207153710538&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;计数器查询方式&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/80e9e8f54ed80d2f506ae2e7130f9d1c.png&quot; alt=&quot;image-20220207154102188&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;独立请求方式&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/723b4d633bd33db8c07e2a09f4876ec8.png&quot; alt=&quot;image-20220207154404507&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;三种方式总结&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d5b36c4d6101191de00767ac82dab06e.png&quot; alt=&quot;image-20220207154449055&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分布仲裁方式（了解即可）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e7061c4e1d63d041f961a24624c52d71.png&quot; alt=&quot;image-20220207154559945&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4. 总线操作和定时&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;总线传输的四个阶段&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/59b7d88403fb4791a062b2bdfbd92962.png&quot; alt=&quot;image-20220207160527516&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同步定时方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c3937f5d7650f8f22d65dde91830204c.png&quot; alt=&quot;image-20220207160831129&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;异步定时方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/bd8f974e11d8c06aa5381c94e4119159.png&quot; alt=&quot;image-20220207161255282&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e78f836b7080ee163a6b499496c1f03f.png&quot; alt=&quot;image-20220207161158530&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题（数据传输率）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/9d77c6bc37e56b2aca560838667ff0f2.png&quot; alt=&quot;image-20220207161520965&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;半同步通信（扩展）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/e6325d80c76ac0af0c918c38c34d0386.png&quot; alt=&quot;image-20220207161625989&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分离式通信（拓展）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/d7630f8bcb64c151afffeb96a6f946c9.png&quot; alt=&quot;image-20220207161715786&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5. 总线标准&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本特点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0e935e19bcdf8de4ec26280ecba6c8db.png&quot; alt=&quot;image-20220207162010796&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;7. 输入输出系统&lt;/h2&gt;
&lt;h3&gt;1. IO系统基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;演变过程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ae710eeef44dd5c8e4f7eaabc7ea7fca.png&quot; alt=&quot;image-20220207163204606&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IO系统基本组成&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b91d0fdf897af253d24a3474c6c46b35.png&quot; alt=&quot;image-20220207163129561&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IO方式简介&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f8cfbc81913e1b8d9fa839a402ff5c7c.png&quot; alt=&quot;image-20220207163711678&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2. 输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;外部设备&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/3ec0457da7c81545c94c667f545f2fc8.png&quot; alt=&quot;image-20220208134546442&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显示器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f67c6e7cc671b13c37f9c6c70e063ecc.png&quot; alt=&quot;image-20220208135025276&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3. 外存储器&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本概念&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/cac3dd2699e1686f4bceaa5d48ed010a.png&quot; alt=&quot;image-20220208135334706&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;磁盘存储器&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;磁盘设备的组成&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f2c8e60975b5e269e76ed2768afb8736.png&quot; alt=&quot;image-20220208135742975&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;性能指标&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/88286c7a2fdf147f8dd59b50f3316d92.png&quot; alt=&quot;image-20220208140554007&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;存取时间计算过程&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/11486e02043670684904cd4f07e848a8.png&quot; alt=&quot;image-20220208140315461&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;磁盘地址和磁盘的工作过程&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/764033bcd75c18dc3d4c2c447fa57950.png&quot; alt=&quot;image-20220208140835206&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4. IO接口&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/aa090629d28a78a03255ceb83c919b06.png&quot; alt=&quot;image-20220208141144133&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基本结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/8a64bf60c12d6e33a1be70d616fb33a5.png&quot; alt=&quot;image-20220208141543148&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接口与端口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/245786be60e5e2a66ae07663a20b411d.png&quot; alt=&quot;image-20220208141723953&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IO端口及其编址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ae19b5b4eec653b51c778be6398645a4.png&quot; alt=&quot;image-20220208142103849&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IO接口的类型&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/7ab2db51615070107338bc8961f66e83.png&quot; alt=&quot;image-20220208142227284&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5. 程序查询方式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;程序查询方式流程图&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/8525ecd9f47d2021004b175cfaf5d8dd.png&quot; alt=&quot;image-20220208143347520&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接口结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0a4d8bcd6b063a939d541e3ec0e0d138.png&quot; alt=&quot;image-20220208143536105&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f309317758153751487fb239f13a55d9.png&quot; alt=&quot;image-20220208144328807&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总结&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c93ed76afb6bb56c7ebc2ae2c0e16bba.png&quot; alt=&quot;image-20220208144140914&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6. 中断系统&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本概念&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0bfba0d181ad75e2322c10e3d21f546d.png&quot; alt=&quot;image-20220208144639975&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分类&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/995e2ab13c03b36efbd8b9b93cb853c5.png&quot; alt=&quot;image-20220208144748709&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中断请求标记&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/09cd5270d6c0e2eef407a6a21c3bb82b.png&quot; alt=&quot;image-20220208144954465&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中断判优&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6043488ff2c7980cea3c6ae5d2c1df87.png&quot; alt=&quot;image-20220208145216623&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优先级设置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/f84916242d072e1fc5bb9cd2ce6c30c8.png&quot; alt=&quot;image-20220208145242843&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中断处理过程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c8cc53ddd6100b5b364855a6807f63c1.png&quot; alt=&quot;image-20220208145431043&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中断隐指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/ff729feb5e3ac1a8ef166617196845ac.png&quot; alt=&quot;image-20220208145809520&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中断服务程序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/c6c09dca12b4b5c9695d12f25f95a7b4.png&quot; alt=&quot;image-20220208145920443&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单重中断和多重中断&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/b60288e78bbae841420ca3faa6bf3c63.png&quot; alt=&quot;image-20220208150150476&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中断屏蔽技术&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/59954ac309e2bc127822ce8c0625c8ed.png&quot; alt=&quot;image-20220208150354883&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中断屏蔽技术 例题（重要，需掌握）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/74efaa3764468815effa7fd86c4df220.png&quot; alt=&quot;image-20220208150716402&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;7. 程序中断方式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;程序中断方式过程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;恢复现场即算做下一次启动&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/a0490c5c7eecae8e3fa6c7d75846a713.png&quot; alt=&quot;image-20220208151033847&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题 第一问&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/cb0142bdfb50dfeefaa96bfbd782807d.png&quot; alt=&quot;image-20220208151244322&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例题 第二问 （重要）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CPI：执行一条指令所需要的时钟周期&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/31c3db331bff6b0e750f04f788dcf5a5.png&quot; alt=&quot;image-20220208151751342&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8. DMA方式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DMA 控制器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/125358f20d7d56388e1f3db8618e45e2.png&quot; alt=&quot;image-20220208152102933&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5a3696be5efb774099514cce3c048c37.png&quot; alt=&quot;image-20220208152326186&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DMA传送方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/8456ff7530c051a2f81b49adc127bb12.png&quot; alt=&quot;image-20220208152619958&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DMA方式的特点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/1345331edfc90adad64be58cb64f5de8.png&quot; alt=&quot;image-20220208152708447&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DMA方式与中断方式对比&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/642c44cc2ccb9669a4d3507c1c584e13.png&quot; alt=&quot;image-20220208152756433&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU占用情况 中断方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/0fffb63f5685a68577631bd58d2f7291.png&quot; alt=&quot;image-20220208153306469&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU占用情况 DMA方式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/6c7a83dc2fa9df3c47f49cb158d07d34.png&quot; alt=&quot;image-20220208153659050&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;总结&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://img-blog.csdnimg.cn/img_convert/5b9e231dbf4abb84a4d155fda0168e07.png&quot; alt=&quot;image-20220208153841685&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>王道408-计网</title><link>https://github.com/posts/%E7%8E%8B%E9%81%93408-%E8%AE%A1%E7%BD%91/</link><guid isPermaLink="true">https://github.com/posts/%E7%8E%8B%E9%81%93408-%E8%AE%A1%E7%BD%91/</guid><description>408笔记</description><pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;!--#计算机网络--&amp;gt;&lt;/p&gt;
&lt;h2&gt;1.概述&lt;/h2&gt;
&lt;h3&gt;1.1概念&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;含义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;计算机网络：是一个将分散的。具有独立功能的计算机系统，通过&lt;strong&gt;通信设备和线路&lt;/strong&gt;连接起来，由功能完善的&lt;strong&gt;软件&lt;/strong&gt;实现资源共享和信息传递的系统。&lt;/p&gt;
&lt;p&gt;简单点说，计算机网络是互联的，自治的计算机集合。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;功能&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;数据通信（连通性）&lt;/li&gt;
&lt;li&gt;资源共享&lt;/li&gt;
&lt;li&gt;分布式处理&lt;/li&gt;
&lt;li&gt;提高可靠性&lt;/li&gt;
&lt;li&gt;负载均衡&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;组成&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;组成部分： 硬件，软件，协议&lt;/li&gt;
&lt;li&gt;工作方式&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;边缘部分 用户直接使用（C/S方式——服务端客户端方式，P2P方式——PeertoPeer，不太理解）&lt;/li&gt;
&lt;li&gt;核心部分 为边缘部分服务&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;功能组成&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;通信子网  实现数据通信&lt;/li&gt;
&lt;li&gt;资源子网  实现资源共享/数据处理&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;分类&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-0.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;1.2性能指标&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;速率&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;速率即数据率或称数据传输率或比特率（bit）。即将数据从主机传到信道上的速度。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一般位简写为小写字母“b”，字节简写为大写字母“B”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-1.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;带宽&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;计算机网络中，带宽用来表示网络的通信线路传送数据的能力，通常是指单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。单位是“比特每秒”，b/s，kb/s，Mb/s，Gb/s。&lt;/p&gt;
&lt;p&gt;以豌豆射手举例，假设1b/s是指每秒发出一个炮弹，那2b/s就是每秒发出两个炮弹，但是炮弹飞行的速度是不变的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;吞吐量&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;表示在单位时间内通过某个网络（或信道、接口）的数据量。单位b/s，kb/s，Mb/s等。&lt;/p&gt;
&lt;p&gt;吞吐量受网络的带宽或网络的额定速率的限制。&lt;/p&gt;
&lt;p&gt;以公园为例，假设公园每秒最多进100人（带宽），但是现在只有30个人进入公园，所以吞吐量就是30人/s.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;时延&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;指数据（报文/分组/比特流）从网络（或链路）的一端传送到另一端所需的时间。也叫延迟或迟延。单位是s。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-2.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;排队时延和处理时延类似于火车站排队进站时的场景，排队时间就是排队时延，安检时间就是处理时间。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;时延带宽积&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;时延带宽积=传播时延 X 带宽&lt;/p&gt;
&lt;p&gt;时延带宽积又称为以比特为单位的链路长度。即“&lt;strong&gt;某段链路现在有多少比特&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-3.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;往返时延RTT&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从发送方发送数据开始，到发送方收到接收方的确认（接收方收到数据后立即发送确认），总共经历的时延。&lt;/p&gt;
&lt;p&gt;RTT = 往返传播时延（传播时延*2）+ 末端处理时延（基本忽略不计）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;利用率&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-4.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;1.3分层结构&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;分层的基本原则&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;各层之间相互独立，每层只实现一种相对独立的功能。&lt;/li&gt;
&lt;li&gt;每层之间界面自然清晰，易于理解，相互交流尽可能少。&lt;/li&gt;
&lt;li&gt;结构上可分割开。每层都采用最合适的技术来实现。&lt;/li&gt;
&lt;li&gt;保持下层对上层的独立性，上层单向使用下层提供的服务。&lt;/li&gt;
&lt;li&gt;整个分层结构应该能促进标准化工作。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;分层结构中的概念&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;1.实体:第n层中的活动元素称为n层实体。同一层的实体叫对等实体。&lt;/p&gt;
&lt;p&gt;2.协议:为进行网络中的&lt;strong&gt;对等实体&lt;/strong&gt;数据交换而建立的规则、标准或约定称为网络协议。【水平】&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;语法:规定传输数据的格式&lt;/li&gt;
&lt;li&gt;语义:规定所要完成的功能&lt;/li&gt;
&lt;li&gt;同步:规定各种操作的顺序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;3.接口（访问服务点SAP） :上层使用下层服务的入口。&lt;/p&gt;
&lt;p&gt;4.服务:下层为相邻上层提供的功能调用。【垂直】&lt;/p&gt;
&lt;p&gt;上一层的PDU变成下一层的SDU。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-5.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;1.4 OSI参考模型&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;五层参考模型是为了方便学习而划分出来的，实际上并没有这个模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-6.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用OSI模型时的通信过程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-7.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接下来会介绍一下各个层的作用&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;应用层&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所用能和用户交互产生网络流量的程序。如QQ，浏览器等&lt;/p&gt;
&lt;p&gt;典型的应用层服务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文件传输（FTP）&lt;/li&gt;
&lt;li&gt;电子邮件（SMTP）&lt;/li&gt;
&lt;li&gt;万维网（HTTP）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;表示层&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用于处理在两个通信系统中交换信息的表示方式（语法和语义）&lt;/p&gt;
&lt;p&gt;功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据格式变换（翻译官）&lt;/li&gt;
&lt;li&gt;数据加密解密&lt;/li&gt;
&lt;li&gt;数据压缩和恢复&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;会话层&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;向表示层实体/用户进程提供建立连接并在连接上有序地传输数据。这是会话，也是建立同步(SYN)。&lt;/p&gt;
&lt;p&gt;功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;建立、管理、终止会话&lt;/li&gt;
&lt;li&gt;使用校验点可使会话在通信失效时从校验点/同步点继续恢复通信，实现数据同步。适用于传输大文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;传输层&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;负责主机中两个进程的通信，即端到端的通信。传输单位是报文段或用户数据报。&lt;/p&gt;
&lt;p&gt;功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可靠传输，不可靠传输（一般用于小文件）&lt;/li&gt;
&lt;li&gt;差错控制（用来处理传输过程中出现的错误）&lt;/li&gt;
&lt;li&gt;流量控制（当两台主机处理数据的速度不匹配时，会对速度过快的主机进行控制）&lt;/li&gt;
&lt;li&gt;复用分用
&lt;ul&gt;
&lt;li&gt;复用:多个应用层进程可同时使用下面运输层的服务。&lt;/li&gt;
&lt;li&gt;分用:运输层把收到的信息分别交付给上面应用层中相应的进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;网络层&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;主要任务是把&lt;strong&gt;分组&lt;/strong&gt;从源端传到目的端，为分组交换网上的不同主机提供通信服务。&lt;/p&gt;
&lt;p&gt;网络层传输单位是数据报。把数据报进行切割后，就是分组。&lt;/p&gt;
&lt;p&gt;功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;路由选择（选择最合适的路由来传递数据）&lt;/li&gt;
&lt;li&gt;流量控制&lt;/li&gt;
&lt;li&gt;差错控制&lt;/li&gt;
&lt;li&gt;拥塞控制（若所有结点都来不及接受分组，而要丢弃大量分组的话，网络就处于拥塞状态。因此要采取一定撒施，缓解这种拥塞。）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;数据链路层&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;主要任务是把网络层传下来的数据报组装成帧。&lt;/p&gt;
&lt;p&gt;数据链路层/链路层的传输单位是帧。&lt;/p&gt;
&lt;p&gt;功能:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;成帧（定义帧的开始和结束）&lt;/li&gt;
&lt;li&gt;差错控制&lt;/li&gt;
&lt;li&gt;流量控制&lt;/li&gt;
&lt;li&gt;访问（接入）控制（控制对信道的访问）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;物理层&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;主要任务是在物理媒体上实现比特流的透明传输。&lt;/p&gt;
&lt;p&gt;物理层传输单位是比特。&lt;/p&gt;
&lt;p&gt;透明传输:指不管所传数据是什么样的比特组合,都应当能够在链路上传送。&lt;/p&gt;
&lt;p&gt;功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义接口特性&lt;/li&gt;
&lt;li&gt;定义传输模式
&lt;ul&gt;
&lt;li&gt;单工     接受方和发送方已经确定，不能更改&lt;/li&gt;
&lt;li&gt;半双工   介于两者之间。同一时间段内，只能有一个接受方或发送方，但是身份可以调换。&lt;/li&gt;
&lt;li&gt;双工     可以同时当做接收方和发送方，类似于打电话&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;定义传输速率&lt;/li&gt;
&lt;li&gt;比特同步&lt;/li&gt;
&lt;li&gt;比特编码&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.5 TCP/IP模型&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-8.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;2.物理层&lt;/h2&gt;
&lt;h3&gt;2.1物理层概念&lt;/h3&gt;
&lt;p&gt;物理层解决如何在连接各种计算机的传输媒体上传输数据比特流，而不是指具体的传输媒体。&lt;/p&gt;
&lt;p&gt;物理层主要任务 : 确定与传输媒体接口有关的一些特性（即定义标准）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;物理层的几个特性&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;机械特性：定义物理连接的特性，规定物理连接时所采用的规格、接口形状、&lt;strong&gt;引线数目、引脚数量&lt;/strong&gt;和排列情况。&lt;/li&gt;
&lt;li&gt;电气特性：规定传输二进制位时，线路上信号的&lt;strong&gt;电压范围&lt;/strong&gt;、阻抗匹配、传输速率和距离限制等。&lt;/li&gt;
&lt;li&gt;功能特性：指明某条线上出现的某一&lt;strong&gt;电平表示何种意义&lt;/strong&gt;，接口部件的信号线的用途。&lt;/li&gt;
&lt;li&gt;规程特性(过程特性）：定义各条物理线路的工作规程和时序关系。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2数据通信基础知识&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;数据通信模型&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;调制解调器就是路由器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-9.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;相关术语&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;通信的目的是传送消息（消息:语音、文字、图像、视频等）。&lt;/li&gt;
&lt;li&gt;数据data:传送信息的实体，通常是有意义的符号序列。&lt;/li&gt;
&lt;li&gt;信号:数据的电气/电磁的表现，是&lt;strong&gt;数据在传输过程中的存在形式&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-10.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;信源:产生和发送数据的源头。&lt;/li&gt;
&lt;li&gt;信宿:接收数据的终点。&lt;/li&gt;
&lt;li&gt;信道:信号的传输媒介。一般用来表示向某一个方向传送信息的介质，因此一条通信线路往往包含一条发送信道和一条接收信道。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-11.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;数据通信的方式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;单工通信：只有一个方向的通信而没有反方向的交互，&lt;strong&gt;仅需要一条信道&lt;/strong&gt;。（广播）&lt;/li&gt;
&lt;li&gt;半双工通信：通信的双方都可以发送或接收信息，但任何一方都不能同时发送和接收，&lt;strong&gt;需要两条信道&lt;/strong&gt;。（对讲机）&lt;/li&gt;
&lt;li&gt;全双工通信：通信双方可以同时发送和接受信息，也需要两条信道。（打电话）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-12.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;串行传输&amp;amp;并行传输&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;串行传输：将表示一个字符的8位二进制数按由低位到高位的顺序依次发送。&lt;/p&gt;
&lt;p&gt;特点：速度慢，费用低，适合远距离&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-13.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;并行传输：将表示一个字符的8位二进制数同时通过8条信道发送。、&lt;/p&gt;
&lt;p&gt;特点：速度快，费用高，适合近距离。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-14.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.3 码元，速率，波特&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;码元&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;码元是指用一个固定时长的&lt;strong&gt;信号波形&lt;/strong&gt;（数字脉冲)，代表不同离散数值的基本波形，是数字通信中数字信号的计量单位，这个时长内的信号称为k进制码元，而该时长称为码元宽度。当码元的离散状态有M个时(M大于2)，此时码元为M进制码元。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1码元可以携带多个比特的信息量&lt;/strong&gt;。例如，在使用二进制编码时，只有两种不同的码元，一种代表0状态，另一种代表1状态。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-15.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;例如：K进制码元——4进制码元——&amp;gt;码元的离散状态有4个——&amp;gt;4种高低不同的信号波形 00、01、10、11(四种不同的电平)，这时一个码元携带两个比特的信息量。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;速率&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;速率也叫数据率，是指数据的传输速率，表示单位时间内传输的数据量。可以用码元传输速率和信息传输速率表示。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;码元传输速率:别名码元速率、波形速率、调制速率、符号速率等，它表示单位时间内数字通信系统所传输的码元个数（也可称为脉冲个数或信号变化的次数)，单位是&lt;strong&gt;波特（Baud)&lt;/strong&gt;。&lt;strong&gt;1波特表示数字通信系统每秒传输一个码元。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;信息传输速率:别名信息速率、比特率等，&lt;strong&gt;表示单位时间内数字通信系统传输的二进制码元个数&lt;/strong&gt;（即比特数)，单位是比特/秒(b/s )&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;关系:若一个码元携带n bit的信息量，则M Baud的码元传输速率所对应的信息传输速率为M×n bit/s。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.4 奈氏准则，香农定理&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;失真&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;影响失真程度的因素: 1.码元传输速率 ⒉信号传输距离 3.噪声干扰﹐4.传输媒体质量&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-16.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;码间串扰&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;码间串扰:接收端收到的信号波形失去了码元之间清晰界限的现象。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-17.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;奈氏准则&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;奈氏准则:在理想低通（无噪声，带宽受限)条件下，为了避免码间串扰，极限码元传输速率为2W Baud，W是信道带宽，单位是Hz。&lt;/p&gt;
&lt;p&gt;注意：只有在这两个公式这带宽才用Hz! !&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-18.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在任何信道中，码元传输的速率是有上限的。若传输速率超过此上限，就会出现严重的码间串扰问题，使接收端对码元的完全正确识别成为不可能。&lt;/li&gt;
&lt;li&gt;信道的频带越宽（即能通过的信号高频分量越多)，就可以用更高的速率进行码元的有效传输。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;奈氏准则给出了码元传输速率的限制，但并没有对信息传输速率给出限制。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;由于码元的传输速率受奈氏准则的制约，所以要提高数据的传输速率，就必须设法使每个码元能携带更多个比特的信息量，这就需要采用多元制的调制方法。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;香农定理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;噪声存在于所有的电子设备和通信信道中。由于噪声随机产生，它的瞬时值有时会很大，因此噪声会使接收端对码元的判决产生错误。但是噪声的影响是相对的，若信号较强，那么噪声影响相对较小。因此，信噪比就很重要**。信噪比=信号的平均功率/噪声的平均功率，常记为S/N，并用分贝(dB）作为度量单位**，即:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-19.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;香农定理:在带宽受限且有噪声的信道中，为了不产生误差，信息的数据传输速率有上限值。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-20.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;信道的带宽或信道中的信噪比越大，则信息的极限传输速率就越高。&lt;/li&gt;
&lt;li&gt;对一定的传输带宽和一定的信噪比，信息传输速率的上限就确定了。&lt;/li&gt;
&lt;li&gt;只要信息的传输速率低于信道的极限传输速率，就一定能找到某种方法来实现无差错的传输。&lt;/li&gt;
&lt;li&gt;香农定理得出的为极限信息传输速率，实际信道能达到的传输速率要比它低不少。&lt;/li&gt;
&lt;li&gt;从香农定理可以看出，若信道带宽W或信噪比S/N没有上限（不可能），那么信道的极限信息传输速率也就没有上限。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;两者对比&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-21.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.5编码和调制&lt;/h3&gt;
&lt;h4&gt;1.基带信号与宽带信号&lt;/h4&gt;
&lt;p&gt;基带信号：将数字信号1和0直接用两种不同的电压表示，再送到&lt;strong&gt;数字信道&lt;/strong&gt;上去传输（基带传输）。&lt;/p&gt;
&lt;p&gt;宽带信号：将基带信号进行调制后形成的频分复用模拟信号，再传送到&lt;strong&gt;模拟信道&lt;/strong&gt;上去传输（宽带传输）。&lt;/p&gt;
&lt;p&gt;在传输距离较近时，计算机网络采用基带传输方式（近距离衰减小，从而信号内容不易发生变化)&lt;/p&gt;
&lt;p&gt;在传输距离较远时，计算机网络采用宽带传输方式(远距离衰减大，即使信号变化大也能最后过滤出来基带信号)&lt;/p&gt;
&lt;h4&gt;2.编码和调制&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-22.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.数字数据编码为数字信号&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;非归零编码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;高1低0&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-23.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;曼彻斯特编码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;将一个码元分成两个相等的间隔，前一个间隔为低电平后一个间隔为高电平表示码元1;码元o则正好相反。也可以采用相反的规定。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;每一个码元都被调成两个电平，所以数据传输速率只有调制速率的1/2。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-24.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;差分曼彻斯特编码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;常用于局域网传输，其规则是:若码元为1，则前半个码元的电平与上一个码元的后半个码元的电平相同，若为0，则相反。&lt;/p&gt;
&lt;p&gt;该编码的特点是，在每个码元的中间，都有一次电平的跳转，可以实现自同步，且抗干扰性强于曼彻斯特编码。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-25.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.数字数据调制为模拟信号&lt;/h4&gt;
&lt;p&gt;数字数据调制技术在发送端将数字信号转换为模拟信号，而在接收端将模拟信号还原为数字信号，分别对应于调制解调器的调制和解调过程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-26.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.6 物理层传输介质&lt;/h3&gt;
&lt;p&gt;传输介质也称传输媒体/传输媒介，它就是数据传输系统中在发送设备和接收设备之间的&lt;strong&gt;物理通路&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;==传输媒体并不是物理层。==&lt;/p&gt;
&lt;p&gt;传输媒体在物理层的下面，因为物理层是体系结构的第一层，因此有时称传输媒体为0层。在传输媒体中传输的是信号，但传输媒体并不知道所传输的信号代表什么意思。&lt;strong&gt;但物理层规定了电气特性，因此能够识别所传送的比特流。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-27.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.7 物理层设备&lt;/h3&gt;
&lt;h4&gt;1.中继器&lt;/h4&gt;
&lt;p&gt;诞生原因:由于存在损耗，在线路上传输的信号功率会逐渐衰减，衰减到一定程度时将造成信号失真，因此会导致接收错误。&lt;/p&gt;
&lt;p&gt;中继器的功能:对信号进行&lt;strong&gt;再生和还原&lt;/strong&gt;，对衰减的信号进行放大，保持与原数据相同，以增加信号传输的距离，延长网络的长度。&lt;/p&gt;
&lt;p&gt;中继器的两端: 两端的网络部分是网段，而不是子网，适用于完全相同的两类网络的互连，且两个网段速率要相同。（后面会讲解网段，子网的相关内容）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中继器只将任何电缆段上的数据发送到另一段电缆上，它&lt;strong&gt;仅作用于信号的电气部分&lt;/strong&gt;，并不管数据中是否有错误数据或不适于网段的数据。&lt;/li&gt;
&lt;li&gt;两端可连相同媒体，也可连不同媒体。&lt;/li&gt;
&lt;li&gt;中继器两端的网段一定要是同一个协议。（中继器不会存储转发，傻)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.集线器（多口中继器）&lt;/h4&gt;
&lt;p&gt;集线器的功能:对信号进行&lt;strong&gt;再生放大转发&lt;/strong&gt;，对衰减的信号进行放大，接着转发到其他所有（除输入端口外）处于工作状态的端口上，以增加信号传输的距离，延长网络的长度。不具备信号的定向传送能力，是一个共享式设备。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-28.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;3.数据链路层&lt;/h2&gt;
&lt;h3&gt;3.1 功能概述&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;一些基本概念&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;结点:主机、路由器&lt;/li&gt;
&lt;li&gt;链路﹔网络中两个结点之间的&lt;strong&gt;物理通道&lt;/strong&gt;，链路的传输介质主要有双绞线、光纤和微波。分为有线链路、无线链路。&lt;/li&gt;
&lt;li&gt;数据链路︰网络中两个结点之间的&lt;strong&gt;逻辑通道&lt;/strong&gt;，把实现控制数据传输协议的硬件和软件加到链路上就构成数据链路&lt;/li&gt;
&lt;li&gt;帧:链路层的协议数据单元，&lt;strong&gt;封装网络层数据报&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;数据链路层负责通过一条链路从一个结点向另一个物理链路直接相连的相邻结点传送帧。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;功能概述&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;数据链路层在物理层提供服务的基础上向网络层提供服务，其最基本的服务是&lt;strong&gt;将源自网络层来的数据可靠地传输到相邻节点的目标机网络&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;层&lt;/strong&gt;。其主要作用是加强物理层传输原始比特流的功能，将物理层提供的可能出错的物理连接改造成为逻辑上无差错的数据链路，使之对网&lt;/p&gt;
&lt;p&gt;络层表现为一条无差错的链路。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;漫画解释&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-29.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;功能&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;为网络层提供服务。（无确认无连接服务，有确认无连接服务，有确认面向连接服务。）&lt;/li&gt;
&lt;li&gt;链路管理，即连接的建立、维持、释放（用于面向连接的服务）。&lt;/li&gt;
&lt;li&gt;组帧。&lt;/li&gt;
&lt;li&gt;流量控制。&lt;/li&gt;
&lt;li&gt;差错控制（帧错/位错）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.2 封装成帧&amp;amp;透明传输&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;封装成帧就是在一段数据的前后部分添加首部和尾部，这样就构成了一个帧。接收端在收到物理层上交的比特流后，就能根据首部和尾部的标记，从收到的比特流中识别帧的开始和结束。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;首部和尾部包含许多的控制信息，他们的一个重要作用:&lt;strong&gt;帧定界（确定帧的界限）&lt;/strong&gt;。帧同步:接收方应当能从接收到的二进制比特流中区分出帧的起始和终止。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;组帧的四种方法: 1.字符计数法，2.字符(节）填充法，3.零比特填充法，4.违规编码法。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-30.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;透明传输&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;透明传输是指不管所传数据是什么样的比特组合，都应当能够在链路上传送。因此，链路层就“看不见”有什么妨碍数据传输的东西。&lt;/p&gt;
&lt;p&gt;当所传数据中的比特组合恰巧与某一个控制信息完全一样时，就必须采取适当的措施，使收方不会将这样的数据误认为是某种控制信息。这样才能保证数据链路层的传输是透明的。&lt;/p&gt;
&lt;p&gt;下面来介绍这几种方法。&lt;/p&gt;
&lt;h4&gt;1.字符计数法&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;帧首部使用一个计数字段（第一个字节，八位）来标明帧内字符数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-31.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这样做有一个很大的缺陷，就是第一个数错误的话，剩下的就全部错误了。因此不经常使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.字符填充法&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;联想java中的转义字符。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-32.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.零比特填充法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-33.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;操作:&lt;/p&gt;
&lt;p&gt;1.在发送端，扫描整个信息字段，只要连续5个1，就立即填入1个0。
⒉.在接收端收到一个帧时，先找到标志字段确定边界，再用硬件对比特流进行扫描。发现连续5个1时，就把后面的0删除。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-34.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;保证了透明传输:在传送的比特流中可以传送任意比特组合，而不会引起对帧边界的判断错误。&lt;/p&gt;
&lt;h4&gt;4.违规编码法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-35.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以用“高-高”，“低-低”来定界帧的起始和终止。&lt;/p&gt;
&lt;p&gt;由于字节计数法中Count字段的脆弱性（其值若有差错将导致灾难性后果）及字符填充实现上的复杂性和不兼容性，&lt;strong&gt;目前较普遍使用的帧同步法是比特填充和违规编码法。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;3.3 差错控制（检错编码）&lt;/h3&gt;
&lt;h4&gt;1.差错从哪来&lt;/h4&gt;
&lt;p&gt;概括来说，传输中的差错都是由于噪声引起的。&lt;/p&gt;
&lt;p&gt;全局性：1.由于线路本身电气特性所产生的随机噪声(热噪声)，是信道固有的，随机存在的。
解决办法:提高信噪比来减少或避免干扰。(对传感器下手)&lt;/p&gt;
&lt;p&gt;局部性⒉.外界特定的短暂原因所造成的冲击噪声，是产生差错的主要原因。
解决办法:通常利用编码技术来解决。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;差错种类&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-36.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-37.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.差错控制（位错）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-38.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;冗余编码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在数据发送之前，先按某种关系附加上一定的冗余位，构成一个符合某一规则的码字后再发送。当要发送的有效数据变化时，相应的冗余位也随之变化，使码字遵从不变的规则。接收端根据收到码字是否仍符合原规则，从而判断是否出错。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;奇偶校验码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;注意：“1”的个数包括检验元&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-39.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CRC循环校验码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-40.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;接收端如何检错
把收到的每一个帧都除以同样的除数，然后检查得到的余数R。&lt;/p&gt;
&lt;p&gt;1.余数为0，判定这个帧没有差错，接受。
2.余数为不为0，判定这个帧有差错（无法确定到位），丢弃。&lt;/p&gt;
&lt;p&gt;FCS的生成以及接收端CRC检验都是由硬件实现，处理很迅速，因此不会延误数据的传输。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.4 差错控制（纠错编码——海明码）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;海明码:发现双比特错，纠正单比特错。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-41.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.确定校验码的位数r&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-42.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.确定校验码和数据的位置&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;校验码放在2的几次方的位置，如2^0,2^1,2^2.......&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-43.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.求出校验码的值&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;首先写出数据位对应的二进制，二进制位数由数据位最后一位（在下面的例子中为10）确定。所以是4位。&lt;/li&gt;
&lt;li&gt;P1的值是所有二进制最后一位为1的实际值异或为0。 如何解释？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;二进制最后一位为1的有 P1,D1,D2,D4,D5。 对他们代表的实际值进行异或。P1设为x&lt;/p&gt;
&lt;p&gt;x⊕1⊕0⊕1⊕0=0    所以 x=0   即P1=0。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-44.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.检错并纠错&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-45.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对所有要校验的位进行异或运算。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;eg：对第一位进行异或运算。&lt;/p&gt;
&lt;p&gt;P1⊕D1⊕D2⊕D4⊕D5=1。&lt;/p&gt;
&lt;p&gt;其他位一样。按从高到低的顺序进行排序。 为 0101=5。 所以判定第五位是错的。纠错即可。&lt;/p&gt;
&lt;h3&gt;3.5 流量控制和可靠传输机制&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;较高的发送速度和较低的接收能力&lt;/strong&gt;的不匹配，会造成传输出错，因此流量控制也是数据链路层的一项重要工作。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;数据链路层和传输层流量控制的区别&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;数据链路层的流量控制是点对点的，而传输层的流量控制是端到端的。&lt;/p&gt;
&lt;p&gt;数据链路层流量控制手段:接收方收不下就不回复确认。&lt;/p&gt;
&lt;p&gt;传输层流量控制手段:接收端给发送端一个窗口公告。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;流量控制的方法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-46.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;三者的区别&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-47.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可靠传输，流量控制，滑动窗口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-48.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.6  停止——等待协议&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-49.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下面来介绍一下停等协议的几种应用情况&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;1.无差错情况&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-50.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.数据帧丢失或者帧出错&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-51.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.ACK（确认帧）丢失&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-52.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.ACK迟到&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-53.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.信道利用率&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-54.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-55.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.7 后退N帧协议（GBN）&lt;/h3&gt;
&lt;h4&gt;1.图解&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-56.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.GBK发送方必须响应的三件事&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;上层的调用
上层要发送数据时，发送方先检查发送窗口是否已满，如果未满，则产生一个帧并将其发送;如果窗口已满，发送方只需将数据返回给上层，暗示上层窗口已满。上层等一会再发送。(实际实现中，发送方可以缓存这些数据，窗口不满时再发送帧)。&lt;/li&gt;
&lt;li&gt;收到了一个ACK
GBN协议中，对n号帧的确认采用&lt;strong&gt;累积确认&lt;/strong&gt;的方式，&lt;strong&gt;标明接收方已经收到n号帧和它之前的全部帧。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;超时事件
协议的名字为后退N帧/回退N帧，来源于出现丢失和时延过长帧时发送方的行为。就像在停等协议中一样，定时器将再次用于恢复数据帧或确认帧的丢失。&lt;strong&gt;如果出现超时，发送方重传所有已发送但未被确认的帧。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3.GBN接收方要做的事&lt;/h4&gt;
&lt;p&gt;如果正确收到n号帧，并且按序，那么接收方为n帧发送一个ACK，并将该帧中的数据部分交付给上层。&lt;/p&gt;
&lt;p&gt;其余情况都丢弃帧，&lt;strong&gt;并为最近按序接收的帧重新发送ACK&lt;/strong&gt;（如图所示）。接收方无需缓存任何失序帧，只需要维护一个信息: expectedseqnum(下一个按序接收的帧序号）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-57.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.滑动窗口的长度&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;若采用n个比特对帧编号，那么发送窗口的尺寸 w 应满足:1≤w≤2^n-1&lt;/strong&gt;。因为发送窗口尺寸过大，就会使得接收方无法区别新帧和旧帧。&lt;/p&gt;
&lt;h4&gt;5.性能分析&lt;/h4&gt;
&lt;p&gt;优点：因连续发送数据帧而提高了信道利用率。&lt;/p&gt;
&lt;p&gt;缺点：在重传时必须把原来已经正确传送的数据帧重传，使传送效率降低。&lt;/p&gt;
&lt;p&gt;选择重传协议可以解决这个问题。&lt;/p&gt;
&lt;h3&gt;3.8 选择重传协议（SR）&lt;/h3&gt;
&lt;p&gt;那么如何解决后退N帧协议的缺点呢？&lt;/p&gt;
&lt;p&gt;解决办法:设置单个确认，同时加大接收窗口，设置接收缓存，缓存乱序到达的帧。&lt;/p&gt;
&lt;h4&gt;1.滑动窗口&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-58.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.SR发送方必须响应的三件事&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;上层的调用
从上层收到数据后，SR发送方检查下一个可用于该帧的序号，如果序号位于发送窗口内，则发送数据帧;否则就像GBN一样，要么将数据缓存，要么返回给上层之后再传输。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;收到了一个ACK
如果收到ACK，加入该帧序号在窗口内，则SR发送方将那个被确认的帧标记为已接收。如果该帧序号是窗口的下界（最左边第一个窗口对应的序号)，则窗口向前移动到具有最小序号的未确认帧处。如果窗口移动了并且有序号在窗口内的未发送帧，则发送这些帧。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;超时事件&lt;/p&gt;
&lt;p&gt;每个帧都有自己的定时器，一个超时事件发生后只重传一个帧。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3.SR接收方要做的事&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-59.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.滑动窗口长度&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-60.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.9 信道划分介质访问控制&lt;/h3&gt;
&lt;h4&gt;1.传输数据用到的两种链路&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;点对点链路 :  两个相邻节点通过一个链路相连，没有第三者。应用:PPP协议，常用于广域网。&lt;/li&gt;
&lt;li&gt;广播式链路 :  所有主机共享通信介质。应用:早期的总线以太网、无线局域网，常用于局域网。典型拓扑结构:总线型、星型（逻辑总线型)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.介质访问控制&lt;/h4&gt;
&lt;p&gt;介质访问控制的内容就是，采取一定的措施，使得两对节点之间的通信不会发生互相干扰的情况。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-61.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.信道划分介质访问控制&lt;/h4&gt;
&lt;p&gt;信道划分介质访问控制:将使用介质的每个设备与来自同一信道上的其他设备的通信隔离开，把时域和频域资源合理地分配给网络上的设备。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-62.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下面来介绍几种多路复用技术。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.频分多路复用 FDM&lt;/h4&gt;
&lt;p&gt;用户在分配到一定的频带后，在通信过程中自始至终都占用这个频带。频分复用的所有用户在同样的时间占用不同的带宽（频率带宽）资源。
优点：充分利用传输介质带宽，系统效率较高 ; 由于技术比较成熟，实现也比较容易。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-63.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.时分多路复用TDM&lt;/h4&gt;
&lt;p&gt;将时间划分为一段段等长的时分复用帧（TDM帧)。每一个时分复用的用户在每一个TDM帧中占用固定序号的时隙，所有用户轮流占用信道。&lt;/p&gt;
&lt;p&gt;这样做有一个弊端，就是当ABC都不传输数据的时候，D仍然要等待，不能直接传输。信道利用率只有1/4.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-64.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5. 改进时分复用——统计时分复用STDM&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ABCD中任何一个想传输数据时，直接发送即可。不用等到自己的时间段，信道利用率大大提高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-65.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.波分多路复用WDM&lt;/h4&gt;
&lt;p&gt;波分多路复用就是&lt;strong&gt;光的频分多路复用&lt;/strong&gt;，在一根光纤中传输多种不同波长（频率）的光信号，由于波长（频率)不同，所以各路光信号互不干扰，最后再用波长分解复用器将各路波长分解出来。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-66.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;7.码分多路复用（CDMA）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;码分多址(CDMA）是码分复用的一种方式。&lt;/li&gt;
&lt;li&gt;1个比特分为多个码片/芯片(chip），&lt;strong&gt;每一个站点被指定一个唯一的m位的芯片序列&lt;/strong&gt;。发送1时站点发送芯片序列，发送0时发送芯片序列反码（通常把0写成-1）。&lt;/li&gt;
&lt;li&gt;如何不打架:多个站点同时发送数据的时候，要求各个站点芯片序列相互正交。&lt;/li&gt;
&lt;li&gt;如何合并:各路数据在信道中被线性相加。&lt;/li&gt;
&lt;li&gt;如何分离:合并的数据和源站规格化内积。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个部分比较难理解，建议结合书和视频多看几遍。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;接下来会将 随机访问介质访问控制 中的几种协议。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3.10 ALOHA 协议&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ALOHA协议分为两种，纯ALOHA协议和时隙ALOHA协议。接下来分别介绍他们。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;1.纯ALOHA协议&lt;/h4&gt;
&lt;p&gt;纯ALOHA协议思想:不监听信道，不按时间槽发送，随机重发。&lt;strong&gt;想发就发&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-67.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.时隙ALOHA协议&lt;/h4&gt;
&lt;p&gt;时隙ALOHA协议的思想:把时间分成若干个相同的时间片，所有用户在时间片开始时刻同步接入网络信道，若发生冲突，则必须等到下一个时间片开始时刻再发送。 &lt;strong&gt;控制想发就发的随意性&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-68.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.11 CSMA协议&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CS&lt;/strong&gt;:载波侦听/监听，每一个站在&lt;strong&gt;发送数据之前&lt;/strong&gt;要检测一下总线上是否有其他计算机在发送数据。&lt;/p&gt;
&lt;p&gt;如何检测？&lt;/p&gt;
&lt;p&gt;当几个站同时在总线上发送数据时，总线上的信号电压摆动值将会增大（互相叠加)。当一个站检测到的信号电压摆动值超过一定门限值时，就认为总线上至少有两个站同时在发送数据，表明产生了碰撞，即发生了冲突。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MA&lt;/strong&gt; : 多点接入，表示许多计算机以多点接入的方式连接在一根总线上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;协议思想:发送帧之前，监听信道。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-69.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.   1—坚持CSMA&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;坚持指的是对于监听信道忙之后的坚持。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;1-坚持CSMA思想:如果一个主机要发送消息，那么它先监听信道。
&lt;strong&gt;空闲则直接传输，不必等待。&lt;/strong&gt;
&lt;strong&gt;忙则一直监听，直到空闲马上传输。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果有冲突(一段时间内未收到肯定回复〉，则等待一个随机长的时间再监听，重复上述过程。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优点:只要媒体空闲，站点就马上发送，避免了媒体利用率的损失。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺点:假如有两个或两个以上的站点有数据要发送，冲突就不可避免。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.非坚持CSMA&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;非坚持指的是对于监听信道忙之后就不继续监听。&lt;/li&gt;
&lt;li&gt;非坚持CSMA思想:如果一个主机要发送消息，那么它先监听信道。
&lt;strong&gt;空闲则直接传输，不必等待。&lt;/strong&gt;
&lt;strong&gt;忙则等待一个随机的时间之后再进行监听。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;优点:采用随机的重发延迟时间可以减少冲突发生的可能性。&lt;/li&gt;
&lt;li&gt;缺点:可能存在大家都在延迟等待过程中，使得媒体仍可能处于空闲状态，媒体使用率降低。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3.P—坚持CSMA&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;p-坚持指的是对于监听信道空闲的处理。&lt;/li&gt;
&lt;li&gt;p-坚持CSMA思想:如果一个主机要发送消息，那么它先监听信道。
&lt;strong&gt;空闲则以p概率直接传输，不必等待;概率1-p等待到下一个时间槽再传输。忙则等待一个随机的时间之后再进行监听。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;优点:既能像非坚持算法那样减少冲突，又能像1-坚持算法那样减少媒体空闲时间的这种方案。&lt;/li&gt;
&lt;li&gt;发生冲突后还是要坚持把数据帧发送完，造成了浪费。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4. 对比总结&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-70.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.12 CSMA/CD协议&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CS&lt;/strong&gt;:载波侦听/监听，每一个站在&lt;strong&gt;发送数据之前&lt;/strong&gt;以及&lt;strong&gt;发送数据时&lt;/strong&gt;都要检测一下总线上是否有其他计算机在发送数据。&lt;/p&gt;
&lt;p&gt;MA:多点接入，表示许多计算机以多点接入的方式连接在一根总线上。（总线型网络）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CD:&lt;strong&gt;碰撞检测（冲突检测)，&lt;/strong&gt;“边发送边监听”&lt;/strong&gt;，适配器边发送数据边检测信道上信号电压的变化情况，以便判断自己在发送数据时其他站是否也在发送数据。  （半双工网络）&lt;/p&gt;
&lt;p&gt;问：监听之后为啥还会发生冲突呢？&lt;/p&gt;
&lt;p&gt;答：&lt;strong&gt;因为电磁波在总线上总是以有限的速率传播的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;问：最迟多久才能知道自己发送的数据没和别人碰撞?&lt;/p&gt;
&lt;p&gt;答：最多是两倍的总线端到端的传播时延（2T)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总线的端到端往返传播时延只要经过2z时间还没有检测到碰撞，就能肯定这次发送不会发生碰撞。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;问：如何确定碰撞后的重传时机?&lt;/p&gt;
&lt;p&gt;答：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-71.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;若连续多次发生冲突,就表明可能有较多的站参与争用信道。使用此算法可使重传需要推迟的平均时间随重传次数的增大而增大，因而减小发生碰撞的概率，有利于整个系统的稳定。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;最小帧长问题&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果帧的长度太小，可能在站点停止传送之前，就已经传送完了。因此需要确定一个最小帧长。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-72.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.13 CSMA/CA协议&lt;/h3&gt;
&lt;p&gt;CSMA/CA主要用于无线局域网。CSMA/CD主要用于有线局域网。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-73.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;工作原理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;发送数据前，先检测信道是否空闲。&lt;/li&gt;
&lt;li&gt;空闲则发出RTS (request to send），RTS包括发射端的地址、接收端的地址、下一份数据将持续发送的时间等信息;&lt;/li&gt;
&lt;li&gt;信道忙则等待。&lt;/li&gt;
&lt;li&gt;接收端收到RTS后，将响应CTS (clear to send )。&lt;/li&gt;
&lt;li&gt;发送端收到CTS后，开始发送数据帧（&lt;strong&gt;同时预约信道:发送方告知其他站点自己要传多久数据&lt;/strong&gt;）。接收端收到数据帧后，将用CRC来检验数据是否正确，正确则响应ACK帧。&lt;/li&gt;
&lt;li&gt;发送方收到ACK就可以进行下一个数据帧的发送，若没有则一直重传至规定重发次数为止(采用二进制指数退避算法来确定随机的推迟时间)。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;CSMA/CA 和 CSMA/CD 对比&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-74.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.14 轮训访问介质控制访问&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;优点： 既要不产生冲突，又要发送时占全部带宽。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;1.轮训协议&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-75.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.令牌传递协议&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-76.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以下几节的内容都不是重点，了解即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3.15 局域网基本概念和体系结构&lt;/h3&gt;
&lt;p&gt;局域网(Local Area Network):简称LAN，是指在某一区域内由多台计算机互联成的计算机组，使用广播信道。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;特点1:覆盖的地理范围较小，只在一个相对独立的局部范围内联，如一座或集中的建筑群内。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;特点2:使用专门铺设的传输介质（双绞线、同轴电缆）进行联网，数据传输速率高（10Mb/s~10Gb/s）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;特点3:通信延迟时间短，误码率低，可靠性较高。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;特点4:各站为平等关系，共享传输信道。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;特点5:多采用分布式控制和广播式通信，能进行广播和组播。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;决定局域网的主要要素为:网络拓扑，传输介质与介质访问控制方法。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;1.拓扑结构&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-77.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.传输介质&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-78.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.介质访问控制方法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-79.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.MAC子层和LLC子层&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-80.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.16 以太网&lt;/h3&gt;
&lt;h4&gt;1.概述&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-81.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. 提供无连接，不可靠的服务&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;无连接:发送方和接收方之间无“握手过程”。&lt;/li&gt;
&lt;li&gt;不可靠:不对发送方的数据帧编号，接收方不向发送方进行确认，差错帧直接丢弃，差错纠正由高层负责&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;以太网只实现无差错接收，不实现可靠传输。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3.     10BASE—T以太网&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-82.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4. 适配器与MAC地址&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;计算机与外界有局域网的连接是通过通信适配器的。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-83.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5. MAC帧&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-84.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.17  PPP协议和HDLC协议&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;在介绍这两个协议之前，先了解一下广域网。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;1.广域网&lt;/h4&gt;
&lt;p&gt;广域网（WAN，Wide Area Network)，通常跨接很大的物理范围，所覆盖的范围从几十公里到几千公里，它能连接多个城市或国家，或横跨几个洲并能提供远距离通信，形成国际性的远程网络。&lt;/p&gt;
&lt;p&gt;广域网的通信子网主要使用&lt;strong&gt;分组交换技术&lt;/strong&gt;。广域网的通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网，它将分布在不同地区的局域网或计算机系统互连起来，达到&lt;strong&gt;资源共享&lt;/strong&gt;的目的。如因特网（Internet)是世界范围内最大的广域网。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;局域网强调信息传递，广域网强调资源共享。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-85.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.PPP协议&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;特点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;点对点协议PPP (Point-to-Point Protocol)是目前使用最广泛的数据链路层协议，用户使用拨号电话接入因特网时一般都使用PPP协议。
&lt;strong&gt;只支持全双工链路。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;满足的要求&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;简单      对于链路层的帧，无需纠错，无需序号，无需流量控制。&lt;/li&gt;
&lt;li&gt;封装成帧   帧定界符&lt;/li&gt;
&lt;li&gt;透明传输      与帧定界符一样比特组合的数据应该如何处理:异步线路用字节填充，同步线路用比特填充。&lt;/li&gt;
&lt;li&gt;多种网络层协议    封装的IP数据报可以采用多种协议。&lt;/li&gt;
&lt;li&gt;多种类型链路     串行/并行，同步/异步，电/光....&lt;/li&gt;
&lt;li&gt;差错检测    错就丢弃。&lt;/li&gt;
&lt;li&gt;检测连接状态       链路是否正常工作。&lt;/li&gt;
&lt;li&gt;最大传送单元      数据部分最大长度MTU。&lt;/li&gt;
&lt;li&gt;网络层地址协商      知道通信双方的网络层地址。&lt;/li&gt;
&lt;li&gt;数据压缩协商&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;无需满足的要求&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;纠错&lt;/li&gt;
&lt;li&gt;流量控制&lt;/li&gt;
&lt;li&gt;序号&lt;/li&gt;
&lt;li&gt;不支持多点线路&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;组成部分&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-86.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PPP协议的状态图&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-87.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PPP协议的帧格式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-88.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.HDLC协议&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;简介&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-89.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HDLC的站&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-90.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;帧格式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-91.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.两种协议对比&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-92.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.18 链路层设备&lt;/h3&gt;
&lt;h4&gt;1.网桥&lt;/h4&gt;
&lt;p&gt;网桥根据&lt;strong&gt;MAC帧的目的地址&lt;/strong&gt;对帧进行&lt;strong&gt;转发和过滤&lt;/strong&gt;。当网桥收到一个帧时，并不向所有接口转发此帧，而是先检查此帧的目的MAC地址，然后再确定将该帧转发到哪一个接口，或者是把它丢弃（即过滤）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-93.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;网桥分为透明网桥和源路由网桥。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;透明网桥:“透明”指以太网上的站点并不知道所发送的帧将经过哪几个网桥，是一种即插即用设备（通过自学习算法来提高效率）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;源路由网桥:在发送帧时，把详细的最佳路由信息（路由最少/时间最短）放在帧的首部中。&lt;/p&gt;
&lt;p&gt;如何发现最佳路由信息呢？&lt;/p&gt;
&lt;p&gt;方法 : 源站以广播方式向欲通信的目的站发送一个发现帧。可能有好几个路径到达目的栈，发现帧会记录到达的时间，返回给源站，由此确定最佳路由信息。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-94.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.以太网交换机&lt;/h4&gt;
&lt;p&gt;以太网交换机就是多端口的网桥。现在基本上都在使用以太网交换机。分为两类。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-95.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.冲突域和广播域&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;冲突域:在同一个冲突域中的每一个节点都能收到所有被发送的帧。简单的说就是同一时间内只能有一台设备发送信息的范围。&lt;/li&gt;
&lt;li&gt;广播域:网络中能接收任一设备发出的广播帧的所有设备的集合。简单的说如果站点发出一个广播信号，所有能接收收到这个信号的设备范围称为一个广播域。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;重点&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-96.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;4.网络层&lt;/h2&gt;
&lt;h3&gt;4.1  网络层功能概述&lt;/h3&gt;
&lt;p&gt;主要任务是把分组从源端传到目的端，为分组交换网上的不同主机提供通信服务。网络层传输单位是数据报。&lt;/p&gt;
&lt;p&gt;分组和数据报的关系：把数据报进行切割之后，就是分组。&lt;/p&gt;
&lt;p&gt;功能一:路由选择与分组转发（最佳路径）&lt;/p&gt;
&lt;p&gt;功能二:异构网络互联&lt;/p&gt;
&lt;p&gt;功能三:拥塞控制（若所有结点都来不及接受分组，而要丢弃大量分组的话，网络就处于拥塞状态。因此要采取一定措施，缓解这种拥塞。）&lt;/p&gt;
&lt;p&gt;方法：开环控制（静态）闭环控制（动态）&lt;/p&gt;
&lt;h3&gt;4.2 数据交换方式&lt;/h3&gt;
&lt;h4&gt;1.电路交换&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-97.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优缺点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-98.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.报文交换&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-99.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优缺点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-100.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.分组交换&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-101.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优缺点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-102.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.三种方式对比&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-103.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5. 传输单元名词辨析&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;应用层——&amp;gt; 报文&lt;/li&gt;
&lt;li&gt;传输层——&amp;gt; 报文段&lt;/li&gt;
&lt;li&gt;网络层——&amp;gt; IP数据报，分组（数据报拆分后就是分组）&lt;/li&gt;
&lt;li&gt;数据链路层——&amp;gt; 帧&lt;/li&gt;
&lt;li&gt;物理层——&amp;gt; 比特流&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;6. 数据报方式 &amp;amp;&amp;amp; 虚电路方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;数据报方式为网络层提供&lt;strong&gt;无连接服务&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;无连接服务:不事先为分组的传输确定传输路径，每个分组独立确定。传输路径，不同分组传输路径可能不同。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚电路方式为网络层提供&lt;strong&gt;连接服务&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;连接服务:首先为分组的传输确定传输路径（建立连接），然后沿该路径（连接）传输系列分组，系列分组传输路径相同，传输结束后拆除连接。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;数据报&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-104.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;虚电路&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-111.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对比&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-105.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.3  IP数据报格式&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-106.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;详细图解  (中间的标识，标志，偏移量在下一小节中进行讲解)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-107.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.4 IP数据报分片&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-108.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中间三个的含义&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-109.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下面通过一个例题来加深理解。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;主要看偏移量。&lt;/p&gt;
&lt;p&gt;数据部分的第一个字节位置为0。所以数据报片1的偏移量为0。 数据报片2起始地址为1400B。 偏移量单位为8B,所以偏移量为1400/8=175。 数据报片3同理。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-110.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.5 分类的IPv4地址&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一个路由器有多个IP地址，每一个端口都有一个IP地址&lt;/li&gt;
&lt;li&gt;IP地址由网络号和主机号组成。&lt;/li&gt;
&lt;li&gt;为了人类方便识别，我们经常使用点分十进制来表示IP地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-112.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;IP地址的分类&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;根据网络号所占字节的多少，将IP地址分为5类。之所以会减一，减二，是因为含有一些特殊的IP地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-113.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;其中包含一些特殊的IP地址，如图所示。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-114.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;私有IP地址（网段个数就是网络号的个数）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-115.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.6 网络地址转换NAT&lt;/h3&gt;
&lt;p&gt;因为普通的路由器不能将局域网里的数据发送到因特网。所以需要 NAT 来实现它。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;网络地址转换NAT&lt;/strong&gt;(Network Address Translation) : 在专用网连接到因特网的路由器上安装NAT软件，安装了NAT软件的路由器叫NAT路由器，它至少有一个有效的外部全球IP地址。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;实现原理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;里边含有一张NAT转换表，包含局域网里边的主机和因特网里边的一个IP地址。局域网里边的主机请求数据时 会进行IP地址转换，&lt;/p&gt;
&lt;p&gt;以此来获取数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-116.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.7 子网划分与子网掩码&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;分类的IP地址的弱点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;IP地址空间的利用率有时很低。&lt;/li&gt;
&lt;li&gt;两级IP地址不够灵活。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;子网划分&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-117.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;子网掩码&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;子网掩码与IP地址逐位相与，就得到子网网络地址。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-118.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用子网分组时的转发&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-119.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解释&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;提取目的IP地址与路由表中的子网掩码进行与运算，如果找到，直接交付。没有找到，进行第三步。&lt;/li&gt;
&lt;li&gt;路由表中有一行是特定主机路由，查看目的IP的地址是否等于主机路由，等于交付。不等于进行第四步。&lt;/li&gt;
&lt;li&gt;目的IP地址与路由表中的每一个子网掩码进行与运算，有则交付。没有进入第五步。&lt;/li&gt;
&lt;li&gt;跳转到另一个路由，重复上述步骤。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.8 无分类变址 CIDR&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-120.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;构成超网&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-121.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过习题来练习一下&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-122.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;前24位为网络前缀，后8位又进行了子网划分。通过子网掩码来确定子网掩码的位数。258转换为二进制为 11111000。 子网位数为5，最大子网个数为 2^5 =32, 分配地址个数 为 2^3 -2=6。 (减2 是因为不能全为0或全为1)。&lt;/p&gt;
&lt;h3&gt;4.9 ARP协议&lt;/h3&gt;
&lt;p&gt;由于在实际网络的链路上传送数据帧时，最终必须使用MAC地址，如图所示。因此我们需要一种协议来获取MAC地址。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-123.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ARP协议&lt;/strong&gt; : 完成主机或路由器IP地址到MAC地址的映射。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ARP协议使用过程:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;检查ARP高速缓存，有对应表项则写入MAC帧，没有则用目的MAC地址为FF-FF-FF-FF-FF-FF的帧封装并广播ARP请求分组，同一局域网中所有主机都能收到该请求。目的主机收到请求后就会向源主机单播一个ARP响应分组，源主机收到后将此映射写入ARP缓存(10-20min更新一次）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ARP协议4种典型情况:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;主机A发给本网络上的主机B:用ARP找到主机B的硬件地址;&lt;/li&gt;
&lt;li&gt;主机A发给另一网络上的主机B:用ARP找到本网络上一个路由器（网关）的硬件地址;&lt;/li&gt;
&lt;li&gt;路由器发给本网络的主机A:用ARP找到主机A的硬件地址;&lt;/li&gt;
&lt;li&gt;路由器发给另一网络的主机B:用ARP找到本网络上的一个路由器的硬件地址。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;具体过程如图所示&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-124.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.10 DHCP协议&lt;/h3&gt;
&lt;p&gt;主机可以静态和动态的获取IP地址。静态是管理员事先分配好主机的IP地址，子网掩码，默认网关等。动态则是依靠DHCP协议来完成的。接下来介绍一下DHCP协议。&lt;/p&gt;
&lt;p&gt;动态主机配置协议（DHCP）是&lt;strong&gt;应用层协议&lt;/strong&gt;，使用&lt;strong&gt;客户/服务器&lt;/strong&gt;方式，客户端和服务端通过&lt;strong&gt;广播方式&lt;/strong&gt;进行交互，基于UDP。DHCP提供即插即用联网的机制，主机可以从服务器动态获取IP地址、子网掩码、默认网关、DNS服务器名称与IP地址,允许&lt;strong&gt;地址重用&lt;/strong&gt;，支持&lt;strong&gt;移动用户加入网络&lt;/strong&gt;，支持&lt;strong&gt;在用 地址续租&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;使用过程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-125.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.11 ICMP协议&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;作用&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-126.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从图中可以看出ICMP和传输层紧密相连。因此它的作用就是&lt;strong&gt;为了更有效地转发IP数据报和提高交付成功的机会。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;构成&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-127.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从图中可以看出ICMP报文分为差错报文和询问报文。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;差错报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;终点不可达:当路由器或主机不能交付数据报时就向源点发送终点不可达报文。（&lt;strong&gt;无法交付&lt;/strong&gt;）&lt;/li&gt;
&lt;li&gt;时间超过:当路由器收到生存时间TTL=0的数据报时，除丢弃该数据报外，还要向源点发送时间超过报文。当终点在预先规定的时间内不能收到一个数据报的全部数据报片时，就把已收到的数据报片都丢弃，并向源点发送时间超过报文。（&lt;strong&gt;TTL=0&lt;/strong&gt;）&lt;/li&gt;
&lt;li&gt;参数问题:当路由器或目的主机收到的数据报的首部中有的字段的值不正确时，就丢弃该数据报，并向源点发
送参数问题报文。（&lt;strong&gt;首部字段有问题&lt;/strong&gt;）&lt;/li&gt;
&lt;li&gt;改变路由（重定向)︰路由器把改变路由报文发送给主机，让主机知道下次应将数据报发送给另外的路由器(可通过更好的路由）。（&lt;strong&gt;有更好的路由&lt;/strong&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;差错报文构成&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-128.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不发送ICMP报文的情况&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;对ICMP差错报告报文不再发送ICMP差错报告报文。&lt;/li&gt;
&lt;li&gt;对第一个分片的数据报片的所有后续数据报片都不发送ICMP差错报告报文。&lt;/li&gt;
&lt;li&gt;对具有组播地址的数据报都不发送ICMP差错报告报文。&lt;/li&gt;
&lt;li&gt;对具有特殊地址（如127.0.0.0或0.0.0.0）的数据报不发送ICMP差错报告报文。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;ICMP询问报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;回送请求和回答报文&lt;/p&gt;
&lt;p&gt;主机或路由器向特定目的主机发出的询问，收到此报文的主机必须给源主机或路由器发送ICMP回送回答报文。(&lt;strong&gt;测试目的站是否可达以及了解其相关状态&lt;/strong&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;时间戳请求和回答报文&lt;/p&gt;
&lt;p&gt;请某个主机或路由器回答当前的日期和时间。(&lt;strong&gt;用来进行时钟同步和测量时间&lt;/strong&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;ICMP的应用&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-129.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.12 IPv6数据报&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;IPv6从根本上解决了IPv4地址不够用的问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;添加的功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;快速处理/转发数据报&lt;/li&gt;
&lt;li&gt;支持QoS（Qos (Quality of Service，服务质量）指一个网络能够利用各种基础技术，&lt;strong&gt;为指定的网络通信提供更好的服务能力&lt;/strong&gt;,是网络的一种安全机制，是用来解决网络延迟和阻塞等问题的一种技术。）&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;1.数据报格式&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-130.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;详细的数据报格式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-131.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. IPv4和IPv6的区别&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;IPv6将地址从32位（4B）扩大到&lt;strong&gt;128位（16B）&lt;/strong&gt;，更大的地址空间。&lt;/li&gt;
&lt;li&gt;IPv6将IPv4的校验和字段彻底移除，以减少每跳的处理时间。&lt;/li&gt;
&lt;li&gt;lPv6将IPv4的可选字段移出首部，变成了&lt;strong&gt;扩展首部&lt;/strong&gt;，成为灵活的首部格式，路由器通常不对扩展首部进行检查，大大提高了路由器的处理效率。&lt;/li&gt;
&lt;li&gt;IPv6支持&lt;strong&gt;即插即用（即自动配置）&lt;/strong&gt;，不需要DHCP协议。&lt;/li&gt;
&lt;li&gt;IPv6首部长度必须是&lt;strong&gt;8B的整数倍&lt;/strong&gt;，IPv4首部是4B的整数倍。&lt;/li&gt;
&lt;li&gt;IPv6只能&lt;strong&gt;在主机处分片&lt;/strong&gt;，IPv4可以在路由器和主机处分片。&lt;/li&gt;
&lt;li&gt;ICMPv6:附加报文类型“分组过大”。&lt;/li&gt;
&lt;li&gt;IPv6支持资源的预分配，支持实时视像等要求，保证一定的带宽和时延的应用。&lt;/li&gt;
&lt;li&gt;IPv6取消了协议字段，改成下一个首部字段。&lt;/li&gt;
&lt;li&gt;IPv6取消了总长度字段，改用有效载荷长度字段。&lt;/li&gt;
&lt;li&gt;IPv6取消了服务类型字段。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3.IPv6的表示形式&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-132.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4. IPv6向IPv4过度策略&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;双栈协议&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;**双协议栈技术就是指在一台设备上同时启用lPv4协议栈和IPv6协议栈。**这样的话，这台设备既能和IPv4网络通信，又能和IPv6网络通信。如果这台设备是一个路由器，那么这台路由器的不同接口上，分别配置了IPv4地址和IPv6地址，并很可能分别连接了IPv4网络和IPv6网络。如果这台设备是一个计算机，那么它将同时拥有IPv4地址和IPv6地址，并具备同时处理这两个协议地址的功能。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;隧道技术&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通过使用互联网络的基础设施在网络之间传递数据的方式。使用隧道传递的数据（或负载)可以是不同协议的数据帧或包。**隧道协议将其它协议的数据帧或包重新封装然后通过隧道发送。**意思就是将IPv6作为IPv4的数据部分，继续传输。到时候解封装。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-133.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.13 路由算法与路由协议概述&lt;/h3&gt;
&lt;h4&gt;1. 作用&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;每一个路由里边都有一张路由表。表的结构如下图所示。由路由算法来填写表中内容，找出最佳路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-134.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. 分类&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-135.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-136.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3. 分层次的路由选择协议&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;出现原因&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;（1）因特网规模很大
（2）许多单位不想让外界知道自己的路由选择协议，但还想连入因特网&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;解决办法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;自治系统AS&lt;/strong&gt;:在单一的技术管理下的一组路由器，而这些路由器使用一种AS内部的路由选择协议和共同的度量以确定分组在该AS内的路由，同时还使用一种AS之间的路由协议以确定在AS之间的路由。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一个AS内的所有网络都属于一个行政单位来管辖，一个自治系统的所有路由器在本自治系统内都必须连通。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;4.14 RIP协议（距离向量算法）&lt;/h3&gt;
&lt;p&gt;RIP是一种分布式的&lt;strong&gt;基于距离向量&lt;/strong&gt;的路由选择协议，是因特网的协议标准，&lt;strong&gt;最大优点是简单&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;RIP协议要求网络中每一个路由器都维护从它自己到其他每一个目的网络的唯一最佳距离记录(即一组距离）。距离:通常为“跳数”，即从源端口到目的端口所经过的路由器个数，经过一个路由器跳数+1。特别的，从一路由器到直接连接的网络距离为1。RIP允许一条路由最多只能包含15个路由器，因此距离为16表示网络不可达。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RIP的特点&lt;/strong&gt;:当网络出现故障时，要经过比较长的时间(例如数分钟)才能将此信息传送到所有的路由器，“慢收敛”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RIP协议只适用于小型互联网。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img src=&quot;image-137.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;路由表如何更新？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-138.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过例题来加深理解&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-139.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.15 OSPF协议（链路状态算法）&lt;/h3&gt;
&lt;p&gt;开放最短路径优先OSPF协议 :“开放”标明OSPF协议不是受某一家厂商控制，而是公开发表的 ; “最短路径优先”是因为使用了Dijkstra提出的最短路径算法SPF。&lt;/p&gt;
&lt;p&gt;OSPF最主要的特征就是&lt;strong&gt;使用分布式的链路状态协议&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-140.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;链路状态路由算法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;每个路由器发现它的邻居结点【HELLO问候分组】，并了解邻居节点的网络地址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置到它的每个邻居的成本度量metric。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;构造【DD数据库描述分组】，向邻站给出自己的链路状态数据库中的所有链路状态项目的摘要信息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果DD分组中的摘要自己都有，则邻站不做处理;如果有没有的或者是更新的，则发送【LSR链路状态请求分组】请求自己没有的和比自己更新的信息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;收到邻站的LSR分组后，发送【LSu链路状态更新分组】进行更新。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;更新完毕后，邻站返回一个【LSAck链路状态确认分组】进行确认。&lt;/p&gt;
&lt;p&gt;只要一个路由器的链路状态发生变化:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;泛洪发送【LSu链路状态更新分组】进行更新。&lt;/li&gt;
&lt;li&gt;更新完毕后，其他站返回一个【LSAck链路状态确认分组】进行确认。&lt;/li&gt;
&lt;li&gt;使用Dijkstra根据自己的链路状态数据库构造到其他节点间的最短路径。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;其他特点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;OSPF直接用&lt;strong&gt;IP数据报&lt;/strong&gt;传送。&lt;/li&gt;
&lt;li&gt;每隔30min，要刷新一次数据库中的链路状态。&lt;/li&gt;
&lt;li&gt;由于一个路由器的链路状态只涉及到与相邻路由器的连通状态，因而与整个互联网的规模并无直接关系。因此当互联网规模很大时，&lt;strong&gt;OSPF协议要比距离向量协议RIP好得多。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;OSPF不存在坏消息传的慢的问题，它的收敛速度很快。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.16 BGP协议&lt;/h3&gt;
&lt;p&gt;自治系统之间的使用的协议就是BGP协议。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-141.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;和谁交换？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-142.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;交换信息的过程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;BGP所交换的网络可达性的信息就是要&lt;strong&gt;到达某个网络所要经过的一系列AS&lt;/strong&gt;。当BGP发言人互相交换了网络可达性的信息后，各BGP发言人就根据所采用的策略从收到的路由信息中找出到达各AS的较好路由。如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-143.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-144.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;特点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;BGP支持&lt;strong&gt;CIDR&lt;/strong&gt;，因此BGP的路由表也就应当包括目的网络前缀、下一跳路由器，以及到达该目的网络所要经过的各个自治系统序列。&lt;/li&gt;
&lt;li&gt;在 BGP刚刚运行时，BGP的邻站是交换整个的 BGP路由表。但以后只需要&lt;strong&gt;在发生变化时更新有变化的部分&lt;/strong&gt;。这样做对节省网络带宽和减少路由器的处理开销都有好处。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;BGP报文类型&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-145.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.17 三种协议比较&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;RIP是一种分布式的基于&lt;strong&gt;距离向量&lt;/strong&gt;的内部网关路由选择协议，通过广播UDP报文来交换路由信息。&lt;/li&gt;
&lt;li&gt;OSPF是一个内部网关协议，要交换的信息量较大，应使报文的长度尽量短，所以不使用传输层协议（如UDP或TCP)，而是直接采用IP。&lt;/li&gt;
&lt;li&gt;BGP是一个外部网关协议，在不同的自治系统之间交换路由信息，由于网络环境复杂，需要保证可靠传输所以采用TCP。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-146.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.18 IP组播&lt;/h3&gt;
&lt;h4&gt;1. IP数据报的三种传送方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;单播： 一对一&lt;/li&gt;
&lt;li&gt;广播： 一对多（多指的所有）&lt;/li&gt;
&lt;li&gt;组播： 一对多（多指的特定主机）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-147.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. IP组播地址&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-148.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3. 硬件组播&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-149.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4. IGMP协议和组播路由选择协议&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IGMP协议让路由器知道本局域网上是否有主机(的进程)参加或退出了某个组播组。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;IGMP工作阶段&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-150.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;组播路由选择协议目的是找出以源主机为根节点的组播转发树。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;构造树可以避免在路由器之间兜圈子。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对不同的多播组对应于不同的多播转发树；同一个多播组，对不同的源点也会有不同的多播转发树。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;组播路由选择的算法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;可以去了解一下，这里不做过多讲解。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-151.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.19 移动IP&lt;/h3&gt;
&lt;h4&gt;1. 移动IP的相关术语&lt;/h4&gt;
&lt;p&gt;移动IP技术是移动结点(计算机/服务器等)以固定的网络IP地址，实现跨越不同网段的漫游功能，并保证了基于网络IP的网络权限在漫游过程中不发生任何改变。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;移动结点                  具有永久IP地址的移动设备。&lt;/li&gt;
&lt;li&gt;归属代理（本地代理）       一个移动结点拥有的就“居所”称为归属网络，在归属网络中代表移动节点执行移动管理功能的实体叫做归属代理。&lt;/li&gt;
&lt;li&gt;外部代理（外地代理）       在外部网络中帮助移动节点完成移动管理功能的实体称为外部代理。&lt;/li&gt;
&lt;li&gt;永久地址（归属地址/主地址）  移动站点在归属网络中的原始地址。&lt;/li&gt;
&lt;li&gt;转交地址（辅地址）         移动站点在外部网络使用的临时地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.通信过程&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;A进入外部网络&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-152.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A进入下一个外部网络或者回到了归属网络&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-153.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.20 网络层设备&lt;/h3&gt;
&lt;p&gt;网络层设备主要是路由器。&lt;/p&gt;
&lt;p&gt;路由器是一种具有多个输入端口和多个输出端口的专用计算机，其任务是&lt;strong&gt;转发分组&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-154.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输入端口对线路上收到的分组的处理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;输入端口中的查找和转发功能在路由器的交换功能中是最重要的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-155.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;输出端口将交换结构传送来的分组发送到线路&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;若路由器处理分组的速率赶不上分组进入队列的速率，则队列的存储空间最终必定减少到零，这就使后面再进入队列的分组由于没有存储空间而只能被丢弃。&lt;/li&gt;
&lt;li&gt;路由器中的输入或输出队列产生溢出是造成分组丢失的重要原因。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-156.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;三层设备的区别&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-157.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;路由表与路由转发&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;路由表根据路由选择算法得出的，主要用途是&lt;strong&gt;路由选择&lt;/strong&gt;，总用软件来实现。结构如下图所示。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-158.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;转发表由路由表得来，可以用软件实现，也可以用特殊的硬件来实现。&lt;strong&gt;转发表必须包含完成转发功能所必需的信息&lt;/strong&gt;，在转发表的每一行必须包含从要到达的目的网络到输出端口和某些MAC地址信息的映射。&lt;/p&gt;
&lt;h2&gt;5.传输层&lt;/h2&gt;
&lt;h3&gt;5.1 概述&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;传输层是&lt;strong&gt;主机&lt;/strong&gt;才有的层次。&lt;/li&gt;
&lt;li&gt;它使用网络层提供的服务为应用层提供服务。&lt;/li&gt;
&lt;li&gt;功能：&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;提供进程与进程之间的通信。&lt;/li&gt;
&lt;li&gt;复用和分用（后边会详细解释的）&lt;/li&gt;
&lt;li&gt;对收到的报文进行差错检测。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;传输层最重要的两个协议——TCP和UDP。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-159.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;寻址和端口&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;复用:应用层所有的应用进程都可以通过传输层再传输到网络层。&lt;/li&gt;
&lt;li&gt;分用:传输层从网络层收到数据后交付指明的应用进程。&lt;/li&gt;
&lt;li&gt;逻辑端口/软件端口 :  端口是传输层的SAP，&lt;strong&gt;标识主机中的应用进程&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;端口号只有本地意义，在因特网中不同计算机的相同端口是没有联系的。&lt;/li&gt;
&lt;li&gt;端口号长度为16bit，能表示65536个不同的端口号。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-160.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;几个常用的端口号&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-161.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.2 UDP协议&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;UDP在IP数据报服务之上添加了复用分用和差错检测的功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;主要特点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;UDP是&lt;strong&gt;无连接&lt;/strong&gt;的，减少开销和发送数据之前的时延&lt;/li&gt;
&lt;li&gt;UDP使用最大努力交付，即&lt;strong&gt;不保证可靠交付&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;UDP是&lt;strong&gt;面向报文&lt;/strong&gt;的，适合一次性传输少量数据的网络应用。&lt;/li&gt;
&lt;li&gt;UDP无拥塞控制，适合很多实时应用。&lt;/li&gt;
&lt;li&gt;UDP首部开销小，8B，TCP 20B。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;image-162.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;UDP的首部格式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-163.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;UDP校验&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;这部分我暂时也没看懂，所以不做太多解释。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-164.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-165.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.3 TCP协议&lt;/h3&gt;
&lt;h4&gt;1.TCP协议的特点&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-166.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字节流发送如图所示&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-167.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. TCP报文段首部格式&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-168.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-169.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-170.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.4 TCP连接管理&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-171.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.建立连接&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-172.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;假设运行在一台主机（客户）上的一个进程想与另一台主机（服务器）上的一个进程建立一条连接，客户应用进程首先通知客户TCP，他想建立一个与服务器上某个进程之间的连接，客户中的TCP会用以下步骤与服务器中的TCP建立一条TCP连接:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SYN 连接请求报文&lt;/li&gt;
&lt;li&gt;seq  序号&lt;/li&gt;
&lt;li&gt;ACK 确认连接&lt;/li&gt;
&lt;li&gt;ack 确认号&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-173.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.释放连接&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-174.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参与一条TCP连接的两个进程中的任何一个都能终止该连接，连接结束后，主机中的“资源”（缓存和变量）将被释放。
&lt;img src=&quot;image-175.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.5 TCP可靠传输&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-176.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可靠传输： 保证接收方进程从缓存区读出的字节流与发送方发出的字节流是完全一样的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TCP协议实现可靠传输的机制&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;校验（与UDP校验一样，增加伪首部）&lt;/li&gt;
&lt;li&gt;序号&lt;/li&gt;
&lt;li&gt;确认&lt;/li&gt;
&lt;li&gt;重传&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;5.6 TCP流量控制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;流量控制:让发送方慢点，要让接收方来得及接收。&lt;/li&gt;
&lt;li&gt;TCP利用&lt;strong&gt;滑动窗口机制&lt;/strong&gt;实现流量控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在通信过程中，接收方根据自己接收缓存的大小，动态地调整发送方的发送窗口大小，即接收窗口rwnd(接收方设置确认报文段的窗口字段来将rwnd通知给发送方），发送方的发送窗口取接收窗口rwnd和拥塞窗口cwnd的最小值。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-177.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.7 TCP拥塞控制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;出现拥塞的条件
对资源需求的总和&amp;gt;可用资源&lt;/li&gt;
&lt;li&gt;网络中有许多资源同时呈现供应不足→网络性能变坏→网络吞吐量将随输入负荷增大而下降&lt;/li&gt;
&lt;li&gt;拥塞控制
防止过多的数据注入到网络中。&lt;strong&gt;全局性&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;拥塞控制和流量控制的区别&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;拥塞控制是全局性的。&lt;/p&gt;
&lt;p&gt;流量控制是点对点的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-178.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在介绍拥塞控制算法之前，先设定几个前提条件，便于理解。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-179.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.慢开始和拥塞避免&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;刚开始进行指数增长，到达 ssthresh 之后，进行加法增长。&lt;/li&gt;
&lt;li&gt;遇到网络拥塞之后，降到初始值，重复之前的步骤。新的 ssthresh 设为 拥塞时窗口大小的一半。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-180.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. 快重传和快回复&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;前面的步骤和之前一样。不同的是降低拥塞窗口的时机和大小不同。如图所示，当收到3个重复的确认时，执行快重传算法，拥塞窗口降到原来的一般。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-181.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;6.应用层&lt;/h2&gt;
&lt;h3&gt;6.1 概述&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;应用层对应用程序的通信提供服务。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;应用层协议定义:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;应用进程交换的报文类型，请求还是响应?&lt;/li&gt;
&lt;li&gt;各种报文类型的语法，如报文中的各个字段及其详细描述。&lt;/li&gt;
&lt;li&gt;字段的语义，即包含在字段中的信息的含义。&lt;/li&gt;
&lt;li&gt;进程何时、如何发送报文，以及对报文进行响应的规则。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;应用层的功能&lt;/th&gt;
&lt;th&gt;对应的协议&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;文件传输，访问和管理&lt;/td&gt;
&lt;td&gt;FTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;电子邮件&lt;/td&gt;
&lt;td&gt;SMTP，POP3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;虚拟终端&lt;/td&gt;
&lt;td&gt;HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;查询服务和远程作业登录&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;6.2 网络应用模型&lt;/h3&gt;
&lt;h4&gt;1. 客户/服务器（C/S）模型&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-182.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. P2P模型&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-183.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.3 DNS系统&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DNS服务的作用: 将域名解析成IP地址。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;1.域名&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;com有一个点，就是“根”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-184.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.DNS服务器&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-185.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.域名解析过程&lt;/h4&gt;
&lt;p&gt;域名解析主要有两种方法： ①递归（考别人）②迭代（靠自己）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-186.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.4 文件传输协议FTP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;文件传送协议FTP (File Transfer Protocol)提供不同种类主机系统（硬、软件体系等都可以不同)之间的文件传输能力。&lt;/li&gt;
&lt;li&gt;FTP是基于客户/服务器（C/S）的协议。&lt;/li&gt;
&lt;li&gt;用户通过一个客户机程序连接至在远程计算机上运行的服务器程序。&lt;/li&gt;
&lt;li&gt;依照 FTP协议提供服务，进行文件传送的计算机就是FTP服务器。&lt;/li&gt;
&lt;li&gt;连接FTP服务器，遵循FTP协议与服务器传送文件的电脑就是FTP客户端。&lt;/li&gt;
&lt;li&gt;FTP传输模式
&lt;ol&gt;
&lt;li&gt;文本模式:ASCIl模式，以文本序列传输数据;&lt;/li&gt;
&lt;li&gt;二进制模式: Binary模式，以二进制序列传输数据。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;工作原理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-187.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-188.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.5 电子邮件&lt;/h3&gt;
&lt;h4&gt;1.信息格式&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-189.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.组成结构&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-190.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.SMAP协议&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;SMTP规定了在两个相互通信的SMTP进程之间应如何交换信息。&lt;/li&gt;
&lt;li&gt;负责发送邮件的SMTP进程就是SMTP客户，负责接收邮件的进程就是SMTP服务器。&lt;/li&gt;
&lt;li&gt;SMTP规定了14条命令（几个字母）和21种应答信息（三位数字代码+简单文字说明）。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;SMAP通信的三个阶段&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-191.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4. MIME&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-192.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5. 邮局协议 POP3&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-193.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6. 国际报文存取协议 IMAP&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;IMAP协议比POP协议复杂。当用户PC上的IMAP客户程序打开IMAP服务器的邮箱时，用户可以看到邮箱的首部，若用户需要打开某个邮件，该邮件才上传到用户的计算机上。&lt;/li&gt;
&lt;li&gt;IMAP可以让用户在不同的地方使用不同的计算机随时上网阅读处理邮件，还允许只读取邮件中的某一个部分（先看正文，有WiFi的时候再下载附件）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;7.基于万维网的电子协议&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-194.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.6 万维网和HTTP协议&lt;/h3&gt;
&lt;h4&gt;1.万维网概述&lt;/h4&gt;
&lt;p&gt;万维网www（World Wide web）是一个大规模的、联机式的信息储藏所/资料空间，是无数个网络站点和网页的集合。&lt;/p&gt;
&lt;p&gt;我们需要一个统一标识符（URL）来区分万维网上的各种资源。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-195.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;用户通过点击&lt;a href=&quot;https://papership.top&quot;&gt;超链接&lt;/a&gt;获取资源，这些资源通过超文本传输协议HTTP传送给使用者。&lt;/p&gt;
&lt;p&gt;万维网以&lt;strong&gt;客户/服务器&lt;/strong&gt;方式工作，用户使用的浏览器就是万维网客户程序，万维网文档所驻留的主机运行服务器程序。&lt;/p&gt;
&lt;p&gt;万维网使用超文本标记语言HTML，使得万维网页面设计者可以很方便地从一个界面的链接转到另一个界面，并能够在自己的屏幕上显示出来。&lt;/p&gt;
&lt;h4&gt;2.HTTP&lt;/h4&gt;
&lt;p&gt;HTTP协议定义了浏览器（万维网客户进程）怎样向万维网服务器请求万维网文档，以及服务器怎样把文档传送给浏览器。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-196.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HTTP协议的特点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-197.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;报文结构&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-198.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-199.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>王道408-操作系统</title><link>https://github.com/posts/%E7%8E%8B%E9%81%93408-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/</link><guid isPermaLink="true">https://github.com/posts/%E7%8E%8B%E9%81%93408-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/</guid><description>(draft)图片待修复</description><pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;!--#操作系统（OS）--&amp;gt;
该笔记来自github的笔记仓库：
::github{repo=&quot;helloworldzsq/wang-dao-408-notes/blob/master/408&quot;}&lt;/p&gt;
&lt;h2&gt;1.计算机系统概述&lt;/h2&gt;
&lt;h3&gt;1.简介&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;什么是操作系统？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;操作系统（Operating Ststem， OS）是指控制和管理整个计算机系统的硬件和软件资源，并合理地组织调度计算机的工作和资源的分配，以提供给用户和其他软件方便的接口和环境，它是计算机系统中最基本的系统软件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;操作系统在系统中的位置&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;!--&lt;img src=&quot;image.png&quot; alt=&quot;alt text&quot; /&gt;--&amp;gt;
&lt;img src=&quot;image-1.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;操作系统的功能和目标&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;系统资源的管理者&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;文件管理&lt;/li&gt;
&lt;li&gt;内存管理&lt;/li&gt;
&lt;li&gt;处理机（CPU）管理&lt;/li&gt;
&lt;li&gt;设备（计算机硬件，例如摄像头）管理&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;用户和计算机硬件之间的接口&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;image-2.png&quot; alt=&quot;alt text&quot; /&gt;
3. 对硬件机器的拓展&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;操作系统的四个特征&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;并发&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;并发是指两个或多个事件在同一时间间隔内发生。这些事件在宏观上是同时发生的，在微观上是交替发生的。&lt;/p&gt;
&lt;p&gt;易混淆的概念——并行：两个或多个事件在同一时刻同时发生&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;共享&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;共享即资源共享，是指系统中的资源内存中多个并发执行的进程共同使用。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-3.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;所谓的“同时”往往是宏观上的，而在微观上，这些进程可能是交替地对该资源进行访问的(即分时共享)&lt;/p&gt;
&lt;p&gt;生活实例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥共享方式:使用QQ和微信视频。同一时间段内摄像头只能分配给其中一个进程。&lt;/li&gt;
&lt;li&gt;同时共享方式:使用QQ发送文件A，同时使用微信发送文件B。宏观上看，两边都在同时读取并发送文件，说明两个进程都在访问硬盘资源，从中读取数据。微观上看，两个进程是交替着访问硬盘的。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;虚拟&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;虚拟是指把一个物理上的实体变为若干个逻辑上的对应物。物理实体（前者）是实际存在的，而逻辑上对应物（后者）是用户感受到的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-4.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;异步&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;异步是指，在多道程序环境下，允许多个程序并发执行，但&lt;strong&gt;由于资源有限，进程的执行不是一贯到底的，而是走走停停，以不可预知的速度向前推进，这就是进程的异步性。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只有系统拥有并发性，才有可能导致异步性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.操作系统的运行机制和体系结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-5.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-6.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-7.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作系统的内核&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由于内核划分功能的不同，内核分为大内核和微内核。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-8.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大内核和微内核的优缺点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-9.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;类比:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;操作系统的体系结构问题与企业的管理问题很相似。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内核就是企业的管理层，负责一些重要的工作。只有管理层才能执行特权指令，普通员工只能执行非特权指令。用户态、核心态之间的切换相当于普通员工和管理层之间的工作交接&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;大内核:企业初创时体量不大，管理层的人会负责大部分的事情。优点是效率高;缺点是组织结构混乱，难以维护。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;微内核:随着企业体量越来越大，管理层只负责最核心的一些工作。优点是组织结构清晰，方便维护;缺点是效率低。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.中断和异常&lt;/h3&gt;
&lt;h4&gt;1.概念和作用&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;中断&lt;/strong&gt;是指计算机运行过程中，出现某些意外情况需主机干预时，机器能自动停止正在运行的程序并转入处理新情况的程序，处理完毕后又返回原被暂停的程序继续运行。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当中断发生时，CPU立即进入核心态&lt;/li&gt;
&lt;li&gt;当中断发生后，当前运行的进程暂停运行，并由操作系统内核对中断进行处理。&lt;/li&gt;
&lt;li&gt;对于不同的中断信号，会进行不同的处理。&lt;/li&gt;
&lt;li&gt;有了中断，才能实现多道程序并发执行。&lt;/li&gt;
&lt;li&gt;“用户态→核心态”是通过中断实现的，&lt;strong&gt;并且中断是唯一途径&lt;/strong&gt;。“核心态→用户态”的切换是通过执行一个特权指令，将程序状态字（ PSW）的标志位设置为 “用户态”。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;2.分类&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;中断信号的来源来自CPU内部称为内中断，外部称为外中断。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-10.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.系统调用&lt;/h3&gt;
&lt;h4&gt;1.含义&lt;/h4&gt;
&lt;p&gt;“系统调用”是操作系统提供给应用程序（程序员/编程人员）使用的接口，可以理解为一种可供应用程序调用的特殊函数，应用程序可以发出系统调用请求来获得操作系统的服务。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-11.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.作用&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;应用程序通过系统调用请求操作系统的服务&lt;/strong&gt;。系统中的各种共享资源都由操作系统统一掌管，因此在用户程序中，凡是与资源有关的操作（如存储分配、I/o操作、文件管理等），都必须通过系统调用的方式向操作系统提出服务请求，由操作系统代为完成。&lt;strong&gt;这样可以保证系统的稳定性和安全性，防止用户进行非法操作。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;3.系统调用和库函数的区别&lt;/h4&gt;
&lt;p&gt;编程语言（c,java）中里边有很多库函数，其实它们（不是所有的库函数）就是将系统调用封装起来，隐藏一些细节，使上层进行系统调用更加方便。&lt;/p&gt;
&lt;h4&gt;4.其他&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;系统调用发生在用户态，对系统调用的处理发生在核心态。&lt;/li&gt;
&lt;li&gt;执行陷入指令（自陷指令或访管指令）会处理内中断，使处理器（CPU）从用户态进入核心态。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2.进程管理&lt;/h2&gt;
&lt;h3&gt;1.进程的定义，组成，组织方式，特征&lt;/h3&gt;
&lt;h4&gt;1.进程&lt;/h4&gt;
&lt;p&gt;程序： 就是指令序列&lt;/p&gt;
&lt;p&gt;引入多道程序（CPU可以并发执行多个程序）之后，为了方便操作系统进行管理，引入了进程，进程实体的概念。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PCB，程序段，数据段三部分构成了进程实体（也叫作进程映像）&lt;/strong&gt;。一般情况下，我们把进程实体简称为进程。&lt;/p&gt;
&lt;p&gt;例如，所谓创建进程，实质上是创建进程实体中的PCB;而撤销进程，实质上是撤销进程实体中的PCB.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PCB是进程存在的唯一标识。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-12.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.进程的组织方式&lt;/h4&gt;
&lt;p&gt;在一个系统中，通常有数十、数百乃至数千个PCB。为了能对他们加以有效的管理，应该用适当的方式把这些PCB组织起来。
注: 进程的组成讨论的是一个进程内部由哪些部分构成的问题，而进程的组织讨论的是多个进程之间的组织方式问题。&lt;/p&gt;
&lt;p&gt;相当于java中的链表和数组。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-13.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.进程的特征&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-14.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.进程的状态和转换&lt;/h3&gt;
&lt;h4&gt;1.状态&lt;/h4&gt;
&lt;p&gt;进程是程序的一次执行。在这个过程中，进程的状态会有各种变化。为了方便各个进程的管理，操作系统将进程划分为几个状态。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-15.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;除此之外，进程还有两种状态。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-16.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.转换&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-17.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.进程控制&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;含义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;进程控制就是要实现进程状态转换。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;实现&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;**进程控制由原语实现。**所谓原语，一般是指由若干条指令组成的程序段，用来实现某个特定功能，&lt;strong&gt;在执行过程中不可被中断&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;原语采用 “关中断指令” 和 “开中断指令” 来实现。 注意： 原语运行在核心态。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-18.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么原语是如何实现进程状态的转换呢？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;更新PCB中的信息（如修改进程状态标志、将运行环境保存到PCB、从PCB恢复运行环境)
a.所有的进程控制原语一定都会修改进程状态标志
b.剥夺当前运行进程的CPU使用权必然需要保存其运行环境
c.某进程开始运行前必然要恢复期运行环境&lt;/li&gt;
&lt;li&gt;将PCB插入合适的队列&lt;/li&gt;
&lt;li&gt;分配/回收资源&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;具体实现如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-19.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.进程通信&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;含义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;进程通信就是进程之间的信息交换。&lt;/p&gt;
&lt;p&gt;为了保证安全，一个进程不能直接访问另一个进程的地址空间。&lt;/p&gt;
&lt;p&gt;操作系统提供了三种方法：共享存储，消息传递，管道通信。&lt;/p&gt;
&lt;h4&gt;1.共享存储&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-20.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.管道通信&lt;/h4&gt;
&lt;p&gt;1.管道只能采用半双工通信，某一时间段内只能实现单向的传输。如果要实现双向同时通信，则需要设置两个管道。
2.各进程要互斥地访问管道。
3.数据以字符流的形式写入管道，当管道写满时，写进程的write()系统调用将被阻塞，等待读进程将数据取走。当读进程将数据全部取后，管道变空，此时读进程的read()系统调用将被阻塞。
4.&lt;strong&gt;如果没写满，就不允许读。如果没读空，就不允许写。&lt;/strong&gt;
5．数据一旦被读出，就从管道中被抛弃，这就意味着&lt;strong&gt;读进程最多只能有一个，否则可能会有读错数据的情况。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-21.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.消息传递&lt;/h4&gt;
&lt;p&gt;进程间的数据交换以格式化的消息为单位。进程通过操作系统提供的“发送消息/接收消息” 两个原语进行数据交换。&lt;/p&gt;
&lt;p&gt;类似于Http协议。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-22.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.线程概念和多线程模型&lt;/h3&gt;
&lt;h4&gt;1.概念&lt;/h4&gt;
&lt;p&gt;有的进程需要同时做很多事，例如用QQ来进行聊天，发送文件等，而传统的进程只能&lt;strong&gt;串行&lt;/strong&gt;执行一系列程序。因此，引入“线程”，来增加并发度。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;可以把线程理解为轻量级的进程。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线程是一个基本的CPU执行单元，也是程序执行流的最小单位。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引入线程后，进程作为除CPU之外的系统资源的分配单元。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-23.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.线程分类&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;用户级线程&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;用户级线程由应用程序通过线程库实现。&lt;strong&gt;所有的线程管理工作都由应用程序负责（包括线程切换）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;用户级线程中，线程切换可以在用户态下即可完成，&lt;strong&gt;无需操作系统干预。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;在用户看来，是有多个线程。但是在操作系统内核看来，并意识不到线程的存在。（用户级线程对用户不透明，对操作系统透明）&lt;/li&gt;
&lt;li&gt;可以这样理解，“用户级线程”就是“从用户视角看能看到的线程”。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;内核级线程&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;内核级线程的管理工作由操作系统内核完成。线程调度、切换等工作都由内核负责，因此内核级线程的切换必然需要在核心态下才能完成。&lt;/li&gt;
&lt;li&gt;可以这样理解，“内核级线程”就是“从操作系统内核视角看能看到的线程”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;==操作系统只“看得见”内核级线程，因此只有内核级线程才是处理机分配的单位。==&lt;/p&gt;
&lt;h3&gt;6.多线程模型&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;多对一模型&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;多对一模型:多个用户及线程映射到一个内核级线程。每个用户进程只对应一个内核级线程。&lt;/li&gt;
&lt;li&gt;优点:用户级线程的切换在用户空间即可完成，不需要切换到核心态，&lt;strong&gt;线程管理的系统开销小，效率高&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;缺点:当一个用户级线程被阻塞后，整个进程都会被阻塞，并发度不高。&lt;strong&gt;多个线程不可在多核处理机上并行运行&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-24.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一对一模型&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;一对一模型:一个用户级线程映射到一个内核级线程。&lt;/li&gt;
&lt;li&gt;优点:当一个线程被阻塞后，别的线程还可以继续执行，并发能力强。&lt;strong&gt;多线程可在多核处理机上并行执行。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;缺点:一个用户进程会占用多个内核级线程，线程切换由操作系统内核完成，需要切换到核心态，&lt;strong&gt;因此线程管理的成本高，开销大。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-25.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;多对多模型&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;多对多模型:n用户级线程映射到m个内核级线程(n &amp;gt;=m)。每个用户进程对应m个内核级线程。&lt;/li&gt;
&lt;li&gt;克服了多对一模型并发度不高的缺点，又克服了一对一模型中一个用户进程占用太多内核级线程，开销太大的缺点。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-26.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;7.处理机调度的概念和层次&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;含义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在多道程序系统中，进程的数量往往是多于处理机的个数的，这样不可能同时并行地处理各个进程。&lt;/p&gt;
&lt;p&gt;处理机调度，就是从就绪队列中按照一定的算法选择一个进程并将处理机分配给它运行，以实现进程的并发执行。&lt;/p&gt;
&lt;p&gt;调度分为三个层次，分别为高级调度，中级调度，初级调度。&lt;/p&gt;
&lt;h4&gt;1.高级调度&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;由于内存空间有限，有时无法将用户提交的作业全部放入内存，因此就需要确定某种规则来决定将作业调入内存的顺序。&lt;/li&gt;
&lt;li&gt;高级调度（作业调度）。按一定的原则从外存上处于后备队列的作业中挑选一个(或多个）作业，给他们分配内存等必要资源，并建立相应的进程（建立PCB)，以使它（们）获得竞争处理机的权利。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高级调度是辅存（外存）与内存之间的调度&lt;/strong&gt;。每个作业只调入一次，调出一次。作业调入时会建立相应的PCB，作业调出时才撤销PCB。&lt;strong&gt;高级调度主要是指调入的问题&lt;/strong&gt;，因为只有调入的时机需要操作系统来确定，调出的时机必然是作业运行结束才调出。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-27.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.中级调度&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;引入了虚拟存储技术之后，可将暂时不能运行的进程调至外存等待。等它重新具备了运行条件且内存又稍有空闲时，再重新调入内存。&lt;strong&gt;这么做的目的是为了提高内存利用率和系统吞吐量&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;暂时调到外存等待的进程状态为挂起状态。值得注意的是，PCB并不会一起调到外存，而是会常驻内存。PCB中会记录进程数据在外存中的存放位置，进程状态等信息，操作系统通过内存中的PCB来保持对各个进程的监控、管理。被挂起的进程PCB会被放到的挂起队列中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中级调度（内存调度），就是要决定将哪个处于挂起状态的进程重新调入内存。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;一个进程可能会被多次调出、调入内存，因此&lt;strong&gt;中级调度发生的频率要比高级调度更高。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-28.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;补充知识：进程的七状态模型&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;暂时被调到外存等待的进程状态称为挂起状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;挂起状态又可以进一步细分为就绪挂起，堵塞挂起两种状态。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-29.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.低级调度&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;低级调度（进程调度），其主要任务是按照某种方法和策略&lt;strong&gt;从就绪队列中选取一个进程，将处理机分配给它。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;进程调度是操作系统中最基本的一种调度，在一般的操作系统中都必须配置进程调度。&lt;/li&gt;
&lt;li&gt;进程调度的频率很高，一般几十毫秒一次。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-30.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.三种调度的联系和对比&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-31.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.进程调度的时机，切换过程和方式&lt;/h3&gt;
&lt;h4&gt;1.进程调度的时机&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-32.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;临界资源： 一个时间段内只允许一个进程使用的资源。各进程需要互斥的访问临界资源。&lt;/p&gt;
&lt;p&gt;临界区：访问临界资源的那段代码。&lt;/p&gt;
&lt;p&gt;内核程序临界区一般是用来访问某种内核数据结构的，比如进程的就绪队列。&lt;/p&gt;
&lt;h4&gt;2.进程调度的方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;非剥夺调度方式，又称非抢占方式。即，只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达，当前进程依然会继续使用处理机，直到该进程终止或主动要求进入阻塞态。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;优点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实现简单,系统开销小但是无法及时处理紧急任务，适合于早期的批处理系统&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;剥夺调度方式，又称抢占方式。当一个进程正在处理机上执行时，如果有一个更重要或更紧迫的进程需要使用处理机，则立即暂停正在执行的进程，将处理机分配给更重要紧迫的那个进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;优点&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可以优先处理更紧急的进程，也可实现让各进程按时间片轮流执行的功能（通过时钟中断）。适合于分时操作系统、实时操作系统&lt;/p&gt;
&lt;h4&gt;3.进程的切换与过程&lt;/h4&gt;
&lt;p&gt;“狭义的进程调度”与“进程切换”的区别:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;狭义的进程调度指的是从就绪队列中选中一个要运行的进程。(这个进程可以是刚刚被暂停执行的进程，也可能是另一个进程，后一种情况就需要进程切换）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程切换是指一个进程让出处理机，由另一个进程占用处理机的过程。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;广义的进程调度包含了选择一个进程和进程切换两个步骤。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;进程切换的过程主要完成了:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1.对原来运行进程各种数据的保存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2.对新的进程各种数据的恢复(如:程序计数器、程序状态字、各种数据寄存器等处理机现场信息，这些信息一般保存在进程控制块)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意 : &lt;strong&gt;进程切换是有代价的&lt;/strong&gt;，因此如果过于频繁的进行进程调度、切换，必然会使整个系统的效率降低，使系统大部分时间都花在了进程切换上，而真正用于执行进程的时间减少。&lt;/p&gt;
&lt;h3&gt;9.调度算法的评价指标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CPU利用率： CPU&quot;忙碌&quot;的时间占总时间的比例。&lt;/li&gt;
&lt;li&gt;系统吞吐量：单位时间内完成作业的数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-33.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;周转时间：是指从作业被提交给系统开始，到作业完成为止的时间间隔。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（作业）周转时间=作业完成时的时间-作业提交时间&lt;/p&gt;
&lt;p&gt;平均周转时间=各作业周转时间之和/作业数&lt;/p&gt;
&lt;p&gt;由于在周转时间相同的情况下，运行时间不同的作业，给用户的感觉是不一样的，所以提出了带权周转时间的概念。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-34.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;等待时间：指进程/作业处于等待处理机状态时间之和，等待时间越长，用户满意度越低。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于进程来说，等待时间就是指进程建立后等待被服务的时间之和，在等待I/O完成的期间其实进程也是在被服务的，所以不计入等待时间。
对于作业来说，不仅要考虑建立进程后的等待时间，还要加上作业在外存后备队列中等待的时间。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;响应时间：指从用户提交请求到首次产生响应所用的时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.调度算法（1）&lt;/h3&gt;
&lt;h4&gt;1.先来先服务（FCFS，First Come First Serve）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-35.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.短作业优先（SJF，Shortest Job First）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-36.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.高响应比优先（HRRN，Highest Response Ratio Next）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-37.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.三种算法对比&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-38.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注:这几种算法主要关心对用户的公平性、平均周转时间、平均等待时间等评价系统整体性能的指标，但是不关心“响应时间”，也并不区分任务的紧急程度，因此对于用户来说，交互性很糟糕。&lt;strong&gt;因此这三种算法一般适合用于早期的批处理系统&lt;/strong&gt;，当然，FCFS算法也常结合其他的算法使用，在现在也扮演着很重要的角色。而适合用于交互式系统的调度算法将在下个小节介绍...&lt;/p&gt;
&lt;h3&gt;11.调度算法（2）&lt;/h3&gt;
&lt;h4&gt;1.时间片轮转调度（RR，Round-Robin）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-39.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果时间片太大，使得每个进程都可以在一个时间片内就完成，则时间片轮转调度算法退化为先来先服务调度算法，并且会增大进程响应时间。因此时间片不能太大。&lt;/li&gt;
&lt;li&gt;另一方面，进程调度、切换是有时间代价的(保存、恢复运行环境)，因此如果时间片太小，会导致进程切换过于频繁，系统会花大量的时间来处理进程切换，从而导致实际用于进程执行的时间比例减少。可见时间片也不能太小。&lt;/li&gt;
&lt;li&gt;一般来说，设计时间片要让切换进程的开销占比不超过1%。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.优先级调度算法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-40.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-41.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.多级反馈队列调度算法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-42.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过下边的例子来加深理解&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-43.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.总结&lt;/h4&gt;
&lt;p&gt;比起早期的批处理操作系统来说，由于计算机造价大幅降低，因此之后出现的交互式操作系统（包括分时操作系统、实时操作系统等）更注重系统的响应时间、公平性、平衡性等指标。而这几种算法恰好也能较好地满足交互式系统的需求。因此&lt;strong&gt;这三种算法适合用于交互式系统。&lt;/strong&gt;(比如UNIX使用的就是多级反馈队列调度算法)&lt;/p&gt;
&lt;h3&gt;12.进程同步与进程互斥&lt;/h3&gt;
&lt;h4&gt;1.进程同步&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;通过进程通信——管道通信的例子来了解什么是进程同步。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-44.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;读进程和写进程并发地运行，由于并发必然导致异步性，因此“写数据”和“读数据”两个操作执行的先后顺序是不确定的。而实际应用中，又必须按照“写数据→读数据”的顺序来执行的。&lt;strong&gt;如何解决这种异步问题，就是“进程同步”所讨论的内容。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;含义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同步亦称直接制约关系，它是指为完成某种任务而建立的两个或多个进程，这些进程因为需要在某些位置上协调它们的工作次序而产生的制约关系。进程间的直接制约关系就是源于它们之间的相互合作。&lt;/p&gt;
&lt;h4&gt;2.进程互斥&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;我们把一个时间段内只允许一个进程使用的资源称为临界资源。&lt;/li&gt;
&lt;li&gt;许多物理设备（比如摄像头、打印机）都属于临界资源。此外还有许多变量、数据、内存缓冲区等都属于临界资源。&lt;/li&gt;
&lt;li&gt;对临界资源的访问，必须互斥地进行。互斥，亦称间接制约关系。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;进程互斥指当一个进程访问某临界资源时，另一个想要访问该临界资源的进程必须等待。当前访问临界资源的进程访问结束，释放该资源之后，另一个进程才能去访问临界资源。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-45.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;对临界资源的互斥访问，可以在逻辑上分为如下四个部分:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-46.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;为了实现对临界资源的互斥访问，同时保证系统整体性能，需要遵循以下原则:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;空闲让进。临界区空闲时，可以允许一个请求进入临界区的进程立即进入临界区;&lt;/li&gt;
&lt;li&gt;忙则等待。当已有进程进入临界区时，其他试图进入临界区的进程必须等待;&lt;/li&gt;
&lt;li&gt;有限等待。对请求访问的进程，应保证能在有限时间内进入临界区（保证不会饥饿)&lt;/li&gt;
&lt;li&gt;让权等待。当进程不能进入临界区时，应立即释放处理机，防止进程忙等待。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;13.进程互斥的软件实现方法&lt;/h3&gt;
&lt;h4&gt;1.单标志法&lt;/h4&gt;
&lt;p&gt;算法思想:每个进程在访问完临界区后会把使用临界区的权限转交给另一个进程。也就是说&lt;strong&gt;每个进程进入临界区的权限只能被另一个进程赋予&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;代码实现&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-47.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;解释&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;turn的初值为0，即刚开始只允许0号进程进入临界区。
若P1先上处理机运行，则会一直卡在⑤。直到P1的时间片用完，发生调度，切换 P0上处理机运行。代码①不会卡住P0，P0可以正常访问临界区，在 P0访问临界区期间即时切换回P1，P1依然会卡在⑤。只有P0在退出区将turn改为1后，P1才能进入临界区。
因此，&lt;strong&gt;该算法可以实现“同一时刻最多只允许一个进程访问临界区”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;turn表示当前允许进入临界区的进程号，而只有当前允许进入临界区的进程在访问了临界区之后，才会修改turn的值。&lt;/p&gt;
&lt;p&gt;也就是说，对于临界区的访问，一定是按P0→P1→P0→P1→......这样轮流访问。这种必须“轮流访问”带来的问题是，如果此时允许进入临界区的进程是P0，而P0一直不访问临界区，那么虽然此时临界区空闲，但是并不允许P1访问。
因此，&lt;strong&gt;单标志法存在的主要问题是:违背“空闲让进”原则。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;2.双标志先检查法&lt;/h4&gt;
&lt;p&gt;算法思想:设置一个布尔型数组flag[]，数组中各个元素用来标记各进程想进入临界区的意愿，比如“flag[0] =ture”意味着0号进程 P0现在想要进入临界区。每个进程在进入临界区之前先检查当前有没有别的进程想进入临界区，如果没有，则把自身对应的标志flag[li]设为true，之后开始访问临界区。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-48.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;若按照①⑤②⑥③⑦....的顺序执行，P0和P1将会同时访问临界区。因此，&lt;strong&gt;双标志先检查法的主要问题是:违反“忙则等待”原则。&lt;/strong&gt;
原因在于，进入区的“检查”和“上锁”两个处理不是一气呵成的。“检查”后，“上锁”前可能发生进程切换。&lt;/p&gt;
&lt;h4&gt;3.双标志后检查法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-49.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.Peterson算法&lt;/h4&gt;
&lt;p&gt;算法思想:双标志后检查法中，两个进程都争着想进入临界区，但是谁也不让谁，最后谁都无法进入临界区。Gary L.Peterson想到了一种方法，&lt;strong&gt;如果双方都争着想进入临界区，那可以让进程尝试“孔融让梨”，主动让对方先使用临界区。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-50.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Peterson算法用软件方法解决了&lt;strong&gt;进程互斥问题，遵循了空闲让进、忙则等待、有限等待&lt;/strong&gt;三个原则，但是依然未遵循让权等待的原则。&lt;/li&gt;
&lt;li&gt;Peterson算法相较于之前三种软件解决方案来说，是最好的，但依然不够好。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;14.进程互斥的硬件实现方法&lt;/h3&gt;
&lt;h4&gt;1.中断屏蔽方法&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-51.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.TestAndSet指令&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-52.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.Swap指令&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-53.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;15.信号量机制&lt;/h3&gt;
&lt;h4&gt;1.什么是信号量&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;用户进程可以通过使用操作系统提供的&lt;strong&gt;一对原语&lt;/strong&gt;来对信号量进行操作，从而很方便的实现了进程互斥、进程同步。&lt;/li&gt;
&lt;li&gt;**信号量其实就是一个变量（可以是一个整数，也可以是更复杂的记录型变量），**可以用一个信号量来表示系统中某种资源的数量，比如:系统中只有一台打印机，就可以设置一个初值为1的信号量。&lt;/li&gt;
&lt;li&gt;原语是一种特殊的程序段，其执行只能一气呵成，不可被中断。原语是由关中断/开中断指令实现的。&lt;/li&gt;
&lt;li&gt;一对原语: wait(S)原语和 signal(S)原语，可以把原语理解为我们自己写的函数，函数名分别为 wait和 signal，括号里的信号量s其实就是函数调用时传入的一个参数。&lt;/li&gt;
&lt;li&gt;wait、signal原语常简称为P、V操作（来自荷兰语proberen和 verhogen）。因此，做题的时候常把wait(S)、 signal(S)两个操作分别写为P(S)、V(S)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.整型信号量&lt;/h4&gt;
&lt;p&gt;用一个整数型的变量作为信号量，用来表示系统中某种资源的数量。&lt;/p&gt;
&lt;p&gt;与普通整数变量的区别：对信号量的操作只有三种，初始化，P操作，V操作。&lt;/p&gt;
&lt;p&gt;下面以打印机为例：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-54.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.记录型信号量&lt;/h4&gt;
&lt;p&gt;整型信号量的缺陷是存在“忙等”问题，因此人们又提出了“记录型信号量”，即用&lt;strong&gt;记录型数据结构表示的信号量&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-55.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;16.用信号量实现进程互斥，同步，前驱关系&lt;/h3&gt;
&lt;h4&gt;1.信号量机制实现进程互斥&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;分析并发进程的关键活动，划定临界区（如:对临界资源打印机的访问就应放在临界区）&lt;/li&gt;
&lt;li&gt;设置互斥信号量mutex，初值为1&lt;/li&gt;
&lt;li&gt;在临界区之前执行P(mutex)&lt;/li&gt;
&lt;li&gt;在临界区之后执行V(mutex)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;image-56.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意： 对不同的临界资源（如摄像头，打印机）需要设置不同的互斥信号量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P、V操作必须成对出现。缺少P(mutex)就不能保证临界资源的互斥访问。缺少V(mutex)会导致资源永不被释放，等待进程永不被唤醒。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;2.信号量机制实现进程同步&lt;/h4&gt;
&lt;p&gt;进程同步：要让各并发进程按要求有序的进行。&lt;/p&gt;
&lt;p&gt;那么如何实现呢？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;分析什么地方需要实现“同步关系”，即必须保证“一前一后”执行的两个操作（或两句代码）&lt;/li&gt;
&lt;li&gt;设置同步信号量s,初始为0&lt;/li&gt;
&lt;li&gt;在“前操作”之后执行v(S)&lt;/li&gt;
&lt;li&gt;在“后操作”之前执行P(S)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面通过一个例子来解释，要求：进程2的代码4必须在进程1的代码2之后执行。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-57.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.信号量机制实现前驱关系&lt;/h4&gt;
&lt;p&gt;进程P1中有句代码S1，P2中有句代码S2 ...P... P6中有句代码S6。这些代码要求按如下前驱图所示的顺序来执行:
其实每一对前驱关系都是一个进程同步问题（需要保证一前一后的操作），因此，
1．要为每一对前驱关系各设置一个同步变量&lt;/p&gt;
&lt;p&gt;2．在“前操作”之后对相应的同步变量执行V操作&lt;/p&gt;
&lt;p&gt;3.在“后操作”之前对相应的同步变量执行Р操作&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-58.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;17.生产者——消费者问题&lt;/h3&gt;
&lt;h4&gt;1.问题描述&lt;/h4&gt;
&lt;p&gt;系统中有一组生产者进程和一组消费者进程，生产者进程每次生产一个产品放入缓冲区，消费者进程每次从缓冲区中取出一个产品并使用。(注:这里的“产品”理解为某种数据)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生产者、消费者共享一个初始为空、大小为n的缓冲区。&lt;/li&gt;
&lt;li&gt;只有缓冲区没满时，生产者才能把产品放入缓冲区，否则必须等待。&lt;/li&gt;
&lt;li&gt;只有缓冲区不空时，消费者才能从中取出产品，否则必须等待。&lt;/li&gt;
&lt;li&gt;缓冲区是临界资源，各进程必须互斥地访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-59.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.问题分析&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-60.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.如何实现&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-61.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.能够改变相邻P，V的顺序&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-62.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;18.多生产者——多消费者&lt;/h3&gt;
&lt;h4&gt;1.问题描述&lt;/h4&gt;
&lt;p&gt;桌子上有一只盘子，每次只能向其中放入一个水果。爸爸专向盘子中放苹果，妈妈专向盘子中放橘子，儿子专等着吃盘子中的橘子，女儿专等着吃盘子中的苹果。只有盘子空时，爸爸或妈妈才可向盘子中放一个水果。仅当盘子中有自己需要的水果时，儿子或女儿可以从盘子中取出水果。用PV操作实现上述过程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-63.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.如何实现&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-64.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;问题：可不可以不使用问题信号量？&lt;/p&gt;
&lt;p&gt;结论:即使不设置专门的互斥变量mutex，也不会出现多个进程同时访问盘子的现象&lt;/p&gt;
&lt;p&gt;原因在于:本题中的缓冲区大小为1，在任何时刻，apple、orange、plate三个同步信号量中最多只有一个是1。因此在任何时刻,最多只有一个进程的P操作不会被阻塞，并顺利地进入临界区...&lt;/p&gt;
&lt;p&gt;如果盘子（缓冲区）数量为2，可能会出现两个进程同时访问缓冲区的情况，有可能导致两个进程写入缓冲区的数据相互覆盖的情况。&lt;/p&gt;
&lt;h4&gt;3.总结&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;在生产者-消费者问题中，如果缓冲区大小为1，那么有可能不需要设置互斥信号量就可以实现互斥访问缓冲区的功能。当然，这不是绝对的，要具体问题具体分析。&lt;/li&gt;
&lt;li&gt;建议:在考试中如果来不及仔细分析，可以加上互斥信号量，保证各进程一定会互斥地访问缓冲区。但需要注意的是，实现互斥的P操作一定要在实现同步的P操作之后，否则可能引起“死锁”。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;19.吸烟者问题&lt;/h3&gt;
&lt;h4&gt;1.问题描述&lt;/h4&gt;
&lt;p&gt;假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它，但是要卷起并抽掉一支烟，抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中，第一个拥有烟草、第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种材料，供应者每次将两种材料放桌子上，拥有剩下那种材料的抽烟者卷一根烟并抽掉它，并给供应者进程一个信号告诉完成了，供应者就会放另外两种材料再桌上，这个过程一直重复(让三个抽烟者轮流地抽烟)。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-65.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.如何解决&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-66.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;20.读者——写者问题&lt;/h3&gt;
&lt;h4&gt;1.问题描述&lt;/h4&gt;
&lt;p&gt;有读者和写者两组并发进程，共享一个文件，当两个或两个以上的读进程同时访问共享数据时不会产生副作用，但若某个写进程和其他进程（读进程或写进程）同时访问共享数据时则可能导致数据不一致的错误。因此要求:&lt;/p&gt;
&lt;p&gt;①允许多个读者可以同时对文件执行读操作;&lt;/p&gt;
&lt;p&gt;②只允许一个写者往文件中写信息;&lt;/p&gt;
&lt;p&gt;③任一写者在完成写操作之前不允许其他读者或写者工作;&lt;/p&gt;
&lt;p&gt;④写者执行写操作前，应让已有的读者和写者全部退出。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-67.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.如何实现&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-68.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;潜在的问题：只要读进程还在读，写进程就要一直堵塞等待，可能会饿死。因此在这种算法中，读进程优先。下面来实现“ 先来先服务”算法，这样就不会导致写进程饿死。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-69.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.总结&lt;/h4&gt;
&lt;p&gt;读者-写者问题为我们解决复杂的互斥问题提供了一个参考思路。
其核心思想在于&lt;strong&gt;设置了一个计数器count用来记录当前正在访问共享文件的读进程数&lt;/strong&gt;。我们可以用count的值来判断当前进入的进程是否是第一个/最后一个读进程，从而做出不同的处理。
另外，对count变量的检查和赋值不能一气呵成导致了一些错误，&lt;strong&gt;如果需要实现“一气呵成”，自然应该想到用互斥信号量。&lt;/strong&gt;
最后，还要认真体会我们是如何解决“写进程饥饿”问题的。&lt;/p&gt;
&lt;h3&gt;21.哲学家吃饭&lt;/h3&gt;
&lt;h4&gt;1.问题描述&lt;/h4&gt;
&lt;p&gt;一张圆桌上坐着5名哲学家，每两个哲学家之间的桌上摆一根筷子，桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐，哲学家在思考时，并不影响他人。只有当哲学家饥饿时，才试图拿起左、右两根筷子（一根一根地拿起）。如果筷子已在他人手上，则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐，当进餐完毕后，放下筷子继续思考。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-70.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.问题分析&lt;/h4&gt;
&lt;p&gt;1.关系分析。系统中有5个哲学家进程，5位哲学家与左右邻居对其中间筷子的访问是互斥关系。&lt;/p&gt;
&lt;p&gt;2.整理思路。这个问题中只有互斥关系，但与之前遇到的问题不同的是，每个哲学家进程需要同时持有两个临界资源才能开始吃饭。如何避免临界资源分配不当造成的死锁现象，是哲学家问题的精髓。
3.信号量设置。定义互斥信号量数组chopstick[5]={1,1,1,1,1}，用于实现对5个筷子的互斥访问。并对哲学家按0~4编号，哲学家i左边的筷子编号为i，右边的筷子编号为(i+1)%5。&lt;/p&gt;
&lt;h4&gt;3.如何实现&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;如果使用下图所示的方法，则会导致死锁问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-71.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;那么如何解决呢？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;①可以对哲学家进程施加一些限制条件，比如最多允许四个哲学家同时进餐。这样可以保证至少有一个哲学家是可以拿到左右两只筷子的&lt;/p&gt;
&lt;p&gt;②要求奇数号哲学家先拿左边的筷子，然后再拿右边的筷子，而偶数号哲学家刚好相反。用这种方法可以保证如果相邻的两个奇偶号哲学家都想吃饭，那么只会有其中一个可以拿起第一只筷子，另一个会直接阻塞。这就避免了占有一支后再等待另一只的情况。&lt;/p&gt;
&lt;p&gt;③仅当一个哲学家左右两支筷子都可用时才允许他抓起筷子。&lt;/p&gt;
&lt;p&gt;下面用代码实现第三种方式。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-72.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;22.管程&lt;/h3&gt;
&lt;h4&gt;1. 为什么引入管程？&lt;/h4&gt;
&lt;p&gt;信号量机制存在的问题 : 编写程序困难、易出错。 因此人们想设计一种机制，让程序员写程序时不需要再关注复杂的PV操作，让写代码更轻松。1973年，Brinch Hansen首次在程序设计语言(Pascal)中引入了“管程”成分――一种高级同步机制。&lt;/p&gt;
&lt;h4&gt;2.管程的定义和基本特征&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;管程相当于对临界区资源进行抽象而编写的一个类。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;管程是一种特殊的软件模块，有这些部分组成:&lt;/p&gt;
&lt;p&gt;1.局部于管程的共享数据结构说明;     （一个类）&lt;/p&gt;
&lt;p&gt;2.对该数据结构进行操作的一组过程;    （类中的方法）&lt;/p&gt;
&lt;p&gt;3．对局部于管程的共享数据设置初始值的语句;   （类中的变量）&lt;/p&gt;
&lt;p&gt;4.管程有一个名字。 （类名）&lt;/p&gt;
&lt;p&gt;管程的基本特征:&lt;/p&gt;
&lt;p&gt;1．局部于管程的数据只能被局部于管程的过程所访问;      （类中变量有自己的作用范围）&lt;/p&gt;
&lt;p&gt;**2.一个进程只有通过调用管程内的过程才能进入管程访问共享数据; ** &lt;strong&gt;这种互斥特性是由编译器来实现的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3．每次仅允许一个进程在管程内执行某个内部过程。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;3.java中类似于管程的机制（单例模式）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-73.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;23.死锁&lt;/h3&gt;
&lt;h4&gt;1.含义&lt;/h4&gt;
&lt;p&gt;在并发环境下，各进程因竞争资源而造成的一种互相等待对方手里的资源，导致各进程都阻塞，都无法向前推进的现象，就是“死锁“。&lt;/p&gt;
&lt;p&gt;发生死锁后若无外力干涉,这些进程都将无法向前推进。&lt;/p&gt;
&lt;h4&gt;2.死锁，饥饿，死循环的区别&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;死锁:各进程互相等待对方手里的资源，导致各进程都阻塞，无法向前推进的现象。&lt;/li&gt;
&lt;li&gt;饥饿:由于长期得不到想要的资源，某进程无法向前推进的现象。比如:在短进程优先（SPF）算法中，若有源源不断的短进程到来，则长进程将一直得不到处理机，从而发生长进程“饥饿”。&lt;/li&gt;
&lt;li&gt;死循环:某进程执行过程中一直跳不出某个循环的现象。有时是因为程序逻辑bug 导致的，有时是程序员故意设计的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-74.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.死锁产生的必要条件&lt;/h4&gt;
&lt;p&gt;产生死锁必须同时满足一下四个条件，只要其中任一条件不成立，死锁就不会发生。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;互斥条件&lt;/strong&gt;:只有对必须互斥使用的资源的争抢才会导致死锁(如哲学家的筷子、打印机设备）。像内存、扬声器这样可以同时让多个进程使用的资源是不会导致死锁的(因为进程不用阻塞等待这种资源）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不剥夺条件&lt;/strong&gt;:进程所获得的资源在未使用完之前，不能由其他进程强行夺走，只能主动释放。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求 和 保持条件&lt;/strong&gt;:进程已经保持了至少一个资源，但又提出了新的资源请求，而该资源又被其他进程占有，此时请求进程被阻塞，但又对自己已有的资源保持不放。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;循环等待条件&lt;/strong&gt;:存在一种进程资源的循环等待链，链中的每一个进程已获得的资源同时被下一个进程所请求。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意 : 发生死锁时一定有循环等待 , 但是发生循环等待时未必死锁（&lt;strong&gt;循环等待是死锁的必要不充分条件&lt;/strong&gt;)&lt;/p&gt;
&lt;p&gt;如果同类资源数大于1，则即使有循环等待，也未必发生死锁。但如果系统中每类资源都只有一个，那循环等待就是死锁的充分必要条件了。&lt;/p&gt;
&lt;h4&gt;4.什么时候会发生死锁&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;对系统资源的竞争&lt;/strong&gt;。各进程对不可剥夺的资源（如打印机）的竞争可能引起死锁，对可剥夺的资源(CPU)的竞争是不会引起死锁的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;进程推进顺序非法&lt;/strong&gt;。请求和释放资源的顺序不当，也同样会导致死锁。例如，并发执行的进程P1、P2分别申请并占有了资源R1、R2，之后进程p1又紧接着申请资源R2，而进程p2又申请资源R1,两者会因为申请的资源被对方占有而阻塞，从而发生死锁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;信号量的使用不当也会造成死锁&lt;/strong&gt;。如生产者-消费者问题中，如果实现互斥的P操作在实现同步的P操作之前，就有可能导致死锁。(可以把互斥信号量、同步信号量也看做是一种抽象的系统资源)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;总之，对不可剥夺资源的不合理分配，可能导致死锁。&lt;/p&gt;
&lt;h4&gt;5.死锁的处理策略&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;预防死锁。破坏死锁产生的四个必要条件中的一个或几个。&lt;/li&gt;
&lt;li&gt;避免死锁。用某种方法防止系统进入不安全状态，从而避免死锁（银行家算法)&lt;/li&gt;
&lt;li&gt;死锁的检测和解除。允许死锁的发生，不过操作系统会负责检测出死锁的发生，然后采取某种措施解除死锁。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;24.预防死锁&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-75.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;25.避免死锁&lt;/h3&gt;
&lt;h4&gt;11.什么是安全序列&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-76.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-77.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所谓安全序列，就是指如果系统按照这种序列分配资源，则每个进程都能顺利完成。只要能找出一个安全序列，系统就是安全状态。当然，安全序列可能有多个。&lt;/li&gt;
&lt;li&gt;如果分配了资源之后，系统中找不出任何一个安全序列，系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利的执行下去。当然，如果有进程提前归还了一些资源，那系统也有可能重新回到安全状态，不过我们在分配资源之前总是要考虑到最坏的情况。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如果系统处于安全状态，就一定不会发生死锁。如果系统进入不安全状态，则可能会发生死锁。（不安全状态未必就是发生了死锁，但发生死锁时一定是在不安全状态）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态，以此决定是否答应资源分配请求。这也是“银行家算法”的核心思想。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.银行家算法&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;数据结构:&lt;/strong&gt;
长度为m的一维数组 Available表示还有多少可用资源&lt;/p&gt;
&lt;p&gt;n&lt;em&gt;m矩阵Max表示各进程对资源的最大需求数&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;n&lt;/em&gt;m矩阵Allocation表示已经给各进程分配了多少资源&lt;/p&gt;
&lt;p&gt;Max - Allocation = Need矩阵表示各进程最多还需要多少资源&lt;/p&gt;
&lt;p&gt;用长度为m的一位数组Request表示进程此次申请的各种资源数&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;银行家算法步骤:&lt;/strong&gt;
①检查此次申请是否超过了之前声明的最大需求数&lt;/p&gt;
&lt;p&gt;②检查此时系统剩余的可用资源是否还能满足这次请求&lt;/p&gt;
&lt;p&gt;③试探着分配，更改各数据结构&lt;/p&gt;
&lt;p&gt;④用安全性算法检查此次分配是否会导致系统进入不安全状态&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;安全性算法步骤:&lt;/strong&gt;
检查当前的剩余可用资源是否能满足某个进程的最大需求，如果可以，就把该进程加入安全序列，并把该进程持有的资源全部回收。
不断重复上述过程，看最终是否能让所有进程都加入安全序列。&lt;/p&gt;
&lt;h3&gt;26.死锁的检测和解除&lt;/h3&gt;
&lt;h4&gt;1.死锁的检测&lt;/h4&gt;
&lt;p&gt;为了能对系统是否已发生了死锁进行检测，必须:&lt;/p&gt;
&lt;p&gt;①用某种数据结构来保存资源的请求和分配信息;
②提供一种算法，利用上述信息来检测系统是否已进入死锁状态。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-78.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果系统中剩余的可用资源数足够满足进程的需求，那么这个进程暂时是不会阻塞的，可以顺利地执行下去。如果这个进程执行结束了把资源归还系统，就可能使某些正在等待资源的进程被激活，并顺利地执行下去。相应的，这些被激活的进程执行完了之后又会归还一些资源，这样可能又会激活另外一些阻塞的进程.
&lt;strong&gt;如果按上述过程分析，最终能消除所有边，就称这个图是可完全简化的。此时一定没有发生死锁（相当于能找到一个安全序列）。&lt;/strong&gt;
如果最终不能消除所有边，那么此时就是发生了死锁。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;死锁定理:如果某时刻系统的资源分配图是不可完全简化的，那么此时系统死锁。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;2.死锁的解除&lt;/h4&gt;
&lt;p&gt;一旦检测出死锁的发生，就应该立即解除死锁。
补充:并不是系统中所有的进程都是死锁状态，用死锁检测算法化简资源分配图后，还连着边的那些进程就是死锁进程
解除死锁的主要方法有 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;资源剥夺法。挂起（暂时放到外存上）某些死锁进程，并抢占它的资源，将这些资源分配给其他的死锁进程。但是应防止被挂起的进程长时间得不到资源而饥饿。&lt;/li&gt;
&lt;li&gt;撤销进程法（或称终止进程法）。强制撤销部分、甚至全部死锁进程，并剥夺这些进程的资源。这种方式的优点是实现简单，但所付出的代价可能会很大。因为有些进程可能已经运行了很长时间，已经接近结束了，一旦被终止可谓功亏一篑，以后还得从头再来&lt;/li&gt;
&lt;li&gt;进程回退法。让一个或多个死锁进程回退到足以避免死锁的地步。这就要求系统要记录进程的历史信息，设置还原点。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如何选择对哪些进程动手？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;进程优先级   (优先级低的)&lt;/li&gt;
&lt;li&gt;已执行多长时间 （执行时间短的）&lt;/li&gt;
&lt;li&gt;还要多久能完成   （时间长的进行处理）&lt;/li&gt;
&lt;li&gt;进程己经使用了多少资源    （资源多的）&lt;/li&gt;
&lt;li&gt;进程是交互式的还是批处理式的   （进行批处理的）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;3.内存管理&lt;/h2&gt;
&lt;h3&gt;1.基础知识&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;内存是用于存放数据的硬件。程序执行前需要先放到内存中才能被CPU处理。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-79.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;相对地址和绝对地址&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;编译时产生的指令只关心“相对地址”，实际放入内存中时再想办法根据起始位置得到“绝对地址”。&lt;/strong&gt;
Eg: 编译时只需确定变量x存放的相对地址是100（也就是说相对于进程在内存中的起始地址而言的地址）。CPU 想要找到x在内存中的实际存放位置，只需要用进程的起始地址+100即可。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相对地址又称逻辑地址，绝对地址又称物理地址。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;写程序到程序运行&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-80.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.内存管理&lt;/h3&gt;
&lt;p&gt;操作系统对内存进行管理，那么要管理哪些内容呢？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内存空间的分配和回收&lt;/li&gt;
&lt;li&gt;操作系统需要提供某种技术从逻辑上对内存空间进行扩充。&lt;/li&gt;
&lt;li&gt;操作系统需要提供地址转换功能，负责程序的逻辑地址与物理地址的转换&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;image-81.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;操作系统需要提供内存保护功能。保证各进程在各自存储空间内运行，互不干扰&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;内存保护可采取两种方法:&lt;/p&gt;
&lt;p&gt;方法一:在CPU中设置一对上、下限寄存器，存放进程的上、下限地址。进程的指令要访问某个地址时，CPU检查是否越界。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-82.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;方法二:采用重定位寄存器（又称基址寄存器）和界地址寄存器（又称限长寄存器）进行越界检查。重定位寄存器中存放的是进程的起始物理地址。界地址寄存器中存放的是进程的最大逻辑地址。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-83.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.覆盖和交换&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-84.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.覆盖技术&lt;/h4&gt;
&lt;p&gt;覆盖技术的思想 : &lt;strong&gt;将程序分为多个段（多个模块）&lt;/strong&gt;。常用的段常驻内存，不常用的段在需要时调入内存。
内存中分为一个“固定区”和若干个“覆盖区”。需要常驻内存的段放在“固定区”中，调入后就不再调出（除非运行结束）不常用的段放在“覆盖区”，需要用到时调入内存，用不到时调出内存。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-85.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-86.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;必须由程序员声明覆盖结构，操作系统完成自动覆盖。**缺点:对用户不透明，增加了用户编程负担。**覆盖技术只用于早期的操作系统中，现在已成为历史。&lt;/p&gt;
&lt;h4&gt;2.交换技术&lt;/h4&gt;
&lt;p&gt;交换（对换）技术的设计思想: &lt;strong&gt;内存空间紧张时，系统将内存中某些进程暂时换出外存，把外存中某些已具备运行条件的进程换入内存（进程在内存与磁盘间动态调度）&lt;/strong&gt;。之前讲的中级调度（内存调度）就是为这个服务的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-87.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;1.应该在外存（磁盘）的什么位置保存被换出的进程?&lt;/p&gt;
&lt;p&gt;具有对换功能的操作系统中，通常把&lt;strong&gt;磁盘空间分为文件区和对换区&lt;/strong&gt;两部分。文件区主要用于存放文件，主要追求存储空间的利用率，因此对文件区空间的管理采用&lt;strong&gt;离散分配方式&lt;/strong&gt;;对换区空间只占磁盘空间的小部分，&lt;strong&gt;被换出的进程数据就存放在对换区&lt;/strong&gt;。由于对换的速度直接影响到系统的整体速度，因此对换区空间的管理主要追求换入换出速度，因此通常对换区采用&lt;strong&gt;连续分配&lt;/strong&gt;方式（学过文件管理章节后即可理解）。总之，对换区的I/O速度比文件区的更快。&lt;/p&gt;
&lt;p&gt;2.什么时候应该交换?&lt;/p&gt;
&lt;p&gt;交换通常在许多进程运行且内存吃紧时进行，而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页，就说明内存紧张，此时可以换出一些进程;如果缺页率明显下降，就可以暂停换出。&lt;/p&gt;
&lt;p&gt;3.应该换出哪些进程?&lt;/p&gt;
&lt;p&gt;可优先换出阻塞进程;可换出优先级低的进程;为了防止优先级低的进程在被调入内存后很快又被换出，有的系统还会考虑进程在内存的驻留时间...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(注意:PCB会常驻内存，不会被换出外存)&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;4.连续分配管理方式&lt;/h3&gt;
&lt;p&gt;连续分配：指为用户进程分配的必须是一个连续的内存空间。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-88.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.单一连续分配&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;在单一连续分配方式中，内存被分为系统区和用户区。系统区通常位于内存的低地址部分，用于存放操作系统相关数据;用户区用于存放用户进程相关数据。&lt;/li&gt;
&lt;li&gt;内存中只能有一道用户程序，用户程序独占整个用户区空间。&lt;/li&gt;
&lt;li&gt;优点: 实现简单 ;无外部碎片;可以采用覆盖技术扩充内存;不一定需要采取内存保护（eg:早期的 PC操作系统MS-DOS）。&lt;/li&gt;
&lt;li&gt;缺点:只能用于单用户、单任务的操作系统中;有内部碎片;存储器利用率极低。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-89.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.固定分区分配&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-90.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-91.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.动态分区分配&lt;/h4&gt;
&lt;p&gt;动态分区分配又称为可变分区分配。&lt;strong&gt;这种分配方式不会预先划分内存分区，而是在进程装入内存时，根据进程的大小动态地建立分区，并使分区的大小正好适合进程的需要&lt;/strong&gt;。因此系统分区的大小和数目是可变的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用这种方式的话，我们需要考虑以下三个问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;系统要用什么样的数据结构记录内存的使用情况?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;image-92.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当很多个空闲分区都能满足需求时,应该选择哪个分区进行分配?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;使用动态分区算法，这个将在下一小节进行详细介绍。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如何进行分区的分配与回收操作?&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;如何分配  -----------&amp;gt; 使用动态分区算法之后，修改数据结构即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-93.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如何回收  -----------&amp;gt; 牢记一点即可，会把相邻的空闲区域合并为一个。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-94.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.内部碎片和外部碎片&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;动态分区分配没有内部碎片，但是有外部碎片。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内部碎片&lt;/strong&gt;，分配给某进程的内存区域中，如果有些部分没有用上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外部碎片&lt;/strong&gt;，是指内存中的某些空闲分区由于太小而难以利用。&lt;/li&gt;
&lt;li&gt;如果内存中空闲空间的总和本来可以满足某进程的要求，但由于进程需要的是一整块连续的内存空间，因此这些
进程“碎片”不能满足进程的需求。可以通过&lt;strong&gt;紧凑&lt;/strong&gt;（(拼凑，Compaction)技术来解决外部碎片。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.动态分区分配算法&lt;/h3&gt;
&lt;p&gt;动态分区分配算法:在动态分区分配方式中，当很多个空闲分区都能满足需求时，应该选择哪个分区进行分配?&lt;/p&gt;
&lt;h4&gt;1.首次适应算法&lt;/h4&gt;
&lt;p&gt;算法思想: 每次都从低地址开始查找，&lt;strong&gt;找到第一个能满足大小的空闲分区&lt;/strong&gt;。
如何实现:空闲分区以地址递增的次序排列。每次分配内存时顺序查找空闲分区链(或空闲分区表），找到大小能满足要求的第一个空闲分区。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-95.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.最佳适应算法&lt;/h4&gt;
&lt;p&gt;算法思想:由于动态分区分配是一种连续分配方式，为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时能有连续的大片空间，可以尽可能多地留下大片的空闲区，即，&lt;strong&gt;优先使用更小的空闲区&lt;/strong&gt;。
如何实现:空闲分区按容量递增次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表)，找到大小能满足要求的第一个空闲分区。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-96.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.最大适应算法&lt;/h4&gt;
&lt;p&gt;又称最坏适应算法(Largest Fit)
算法思想:为了解决最佳适应算法的问题――即留下太多难以利用的小碎片，可以在&lt;strong&gt;每次分配时优先使用最大的连续空闲区&lt;/strong&gt;，这样分配后剩余的空闲区就不会太小，更方便使用。
如何实现:空闲分区按容量递减次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表），找到大小能满足要求的第一个空闲分区。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-97.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.临近适应算法&lt;/h4&gt;
&lt;p&gt;算法思想:首次适应算法每次都从链头开始查找的。这可能会导致低地址部分出现很多小的空闲分区，而每次分配查找时，都要经过这些分区，因此也增加了查找的开销。&lt;strong&gt;如果每次都从上次查找结束的位置开始检索，就能解决上述问题。&lt;/strong&gt;
如何实现:空闲分区以地址递增的顺序排列(可排成一个循环链表）。每次分配内存时从上次查找结束的位置开始查找空闲分区链（或空闲分区表），找到大小能满足要求的第一个空闲分区。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-98.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.四种算法对比&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-99.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6.基本分页存储管理&lt;/h3&gt;
&lt;p&gt;连续分配:为用户进程分配的必须是一个连续的内存空间。&lt;/p&gt;
&lt;p&gt;非连续分配:为用户进程分配的可以是一些分散的内存空间。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-100.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;基本分页存储管理的思想――把内存分为一个个相等的小分区,再按照分区大小把进程拆分成一个个小部分。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;将内存空间分为一个个大小相等的分区（比如:每个分区4KB），每个分区就是一个“页框”，或称“页帧”、“内存块”、“物理块”。每个页框有一个编号，即“页框号”(或者“内存块号”、“页帧号”、“物理块号”)页框号从0开始。&lt;/p&gt;
&lt;p&gt;将用户进程的地址空间也分为与页框大小相等的一个个区域，称为“页”或“页面”。每个页面也有一个编号，即“页号”，页号也是从0开始。
(注:进程的最后一个页面可能没有一个页框那么大。因此，页框不能太大，否则可能产生过大的内部碎片)&lt;/p&gt;
&lt;p&gt;操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。也就是说，进程的页面与内存的页框有一一对应的关系。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;各个页面不必连续存放，也不必按先后顺序来，可以放到不相邻的各个页框中。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;思考： 将进程地址空间分页之后，操作系统该如何实现逻辑地址到物理地址的转换?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;要算出逻辑地址对应的页号&lt;/li&gt;
&lt;li&gt;要知道该页号对应页面在内存中的起始地址&lt;/li&gt;
&lt;li&gt;要算出逻辑地址在页面内的“偏移量”&lt;/li&gt;
&lt;li&gt;物理地址 = 页面始址＋页内偏移量&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如何计算:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;页号=逻辑地址/页面长度(取除法的整数部分)&lt;/li&gt;
&lt;li&gt;页内偏移量 = 逻辑地址%页面长度（取除法的余数部分)&lt;/li&gt;
&lt;li&gt;页面在内存中的起始位置:操作系统需要用某种数据结构记录进程各个页面的起始位置。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;举例:&lt;/p&gt;
&lt;p&gt;页号=80 / 50= 1
页内偏移量=80 % 50 = 30
1号页在内存中存放的起始位置450&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-101.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;思考: 如何知道该页号对应页面在内存中的起始地址?&lt;/p&gt;
&lt;p&gt;操作系统为每一个进程创建一个页表?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-102.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如何理解每个页表项的长度是相同的，页号是“隐含的”？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-103.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;7.基本地址变换机构&lt;/h3&gt;
&lt;p&gt;基本地址变换机构可以&lt;strong&gt;借助进程的页表将逻辑地址转换为物理地址&lt;/strong&gt;。
通常会在系统中设置一个页表寄存器(PTR），存放页表在内存中的起始地址F和页表长度M。进程未执行时，页表的始址和页表长度放在进程控制块(PCB）中，当进程被调度时，操作系统内核会把它们放到页表寄存器中。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;执行流程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-104.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;页表项长度，页表长度，页面大小&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;页表长度是指这个页表中总共有几个页表项，即总共有多少页。页面大小是指一个页面占多大的存储空间。页表项长度是指每个页表项占多大的存储空间。&lt;/p&gt;
&lt;p&gt;通过下面这个例子来理解页表项长度。&lt;/p&gt;
&lt;p&gt;Eg:假设某系统物理内存大小为4GB，页面大小为4KB，内存总共会被分为2^32/ 2^12=2^20个内存块，因此内存块号的范围应该是0~2^20 - 1。因此至少要20个二进制位才能表示这么多的内存块号，因此至少要3个字节才够(每个字节8个二进制位，3个字节共24个二进制位)。每个块号用三个字节来表示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-105.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.具有快表的地址变换机构&lt;/h3&gt;
&lt;h4&gt;1.局部性原理&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;时间局部性&lt;/strong&gt;:如果执行了程序中的某条指令，那么不久后这条指令很有可能再次执行;如果某个数据被访问过，不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
&lt;strong&gt;空间局部性&lt;/strong&gt;:一旦程序访问了某个存储单元，在不久之后，其附近的存储单元也很有可能被访问。(因为很多数据在内存中都是连续存放的)&lt;/p&gt;
&lt;p&gt;上小节介绍的基本地址变换机构中，每次要访问一个逻辑地址，都需要查询内存中的页表。由于局部性原理，可能连续很多次查到的都是同一个页表项。既然如此，能否利用这个特性减少访问页表的次数呢?&lt;/p&gt;
&lt;h4&gt;2.快表&lt;/h4&gt;
&lt;p&gt;快表，又称联想寄存器（TLB），是一种&lt;strong&gt;访问速度比内存快很多的高速缓冲存储器&lt;/strong&gt;，用来存放当前访问的若干页表项，以加速地址变换的过程。与此对应，内存中的页表常称为慢表。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;执行流程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-106.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;9.两级页表&lt;/h3&gt;
&lt;p&gt;两级页表的出现主要是为了解决单级页表的问题。那么单级页表有什么问题呢？&lt;/p&gt;
&lt;p&gt;问题一:页表必须连续存放，因此当页表很大时，需要占用很多个连续的页框。
问题二:没有必要让整个页表常驻内存，因为进程在一段时间内可能只需要访问某几个特定的页面。&lt;/p&gt;
&lt;h4&gt;1.解决问题一&lt;/h4&gt;
&lt;p&gt;我们可以回想以下当初是如何解决进程必须连续的问题 ？&lt;/p&gt;
&lt;p&gt;我们可以把页表放在不同的页框中，再用一个表来记录各个各个子页表所在位置，我们把这个表叫做页目录表（外层页表，顶级页表）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-107.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.解决问题二&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-108.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3. 其他细节&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;若采用多级页表机制，则各级页表的大小不能超过一个页面&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;两级页表的访存次数分析（假设没有快表机构）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;第一次访存:访问内存中的页目录表&lt;/li&gt;
&lt;li&gt;第二次访存:访问内存中的二级页表&lt;/li&gt;
&lt;li&gt;第三次访存:访问目标内存单元&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;==N级页表访问一个逻辑地址需要N+1次访问内存。==&lt;/p&gt;
&lt;h3&gt;10.基本分段存储管理方式&lt;/h3&gt;
&lt;h4&gt;1.分段&lt;/h4&gt;
&lt;p&gt;进程的地址空间:&lt;strong&gt;按照程序自身的逻辑关系划分为若干个段&lt;/strong&gt;，每个段都有一个段名（在低级语言中，程序员使用段名来编程)，每段从0开始编址。
内存分配规则 : 以段为单位进行分配，每个段在内存中占据连续空间，但各段之间可以不相邻。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-109.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分段系统的逻辑地址结构由段号（段名）和段内地址（段内偏移量）所组成。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-110.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;段号的位数决定了每个进程最多可以分几个段。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;段内地址位数决定了每个段的最大长度是多少。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;2.段表&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-111.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.段内寻址&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-112.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.分段，分页对比&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;页是信息的物理单位。分页的主要目的是为了实现离散分配，提高内存利用率。分页仅仅是系统管理上的需要，完全是系统行为，对用户是不可见的。&lt;/li&gt;
&lt;li&gt;段是信息的逻辑单位。分段的主要目的是更好地满足用户需求。一个段通常包含着一组属于一个逻辑模块的信息。分段对用户是可见的，用户编程时需要显式地给出段名。&lt;/li&gt;
&lt;li&gt;页的大小固定且由系统决定。段的长度却不固定，决定于用户编写的程序。&lt;/li&gt;
&lt;li&gt;分页的用户进程地址空间是一维的，程序员只需给出一个记忆符即可表示一个地址。&lt;/li&gt;
&lt;li&gt;分段的用户进程地址空间是二维的，程序员在标识一个地址时，既要给出段名，也要给出段内地址。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分段比分页更容易实现信息的共享和保护。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;11.段页式管理方式&lt;/h3&gt;
&lt;h4&gt;1.分页，分段的优缺点&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-113.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;既然两者都有优缺点，那么可不可以把他们结合起来呢？答案当然是可以的。如下图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-114.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.段页式管理的逻辑结构&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-115.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;段号的位数决定了每个进程最多可以分几个段&lt;/p&gt;
&lt;p&gt;页号位数决定了每个段最大有多少页&lt;/p&gt;
&lt;p&gt;页内偏移量决定了页面大小、内存块大小是多少&lt;/p&gt;
&lt;h4&gt;3.段内寻址&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-116.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;12.虚拟内存&lt;/h3&gt;
&lt;h4&gt;1.传统存储管理方式的特征和缺点&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;一次性:作业必须一次性全部装入内存后才能开始运行。这会造成两个问题:①作业很大时，不能全部装入内存，导致大作业无法运行;②当大量作业要求运行时，由于内存无法容纳所有作业，因此只有少量作业能运行，导致多道程序并发度下降。&lt;/li&gt;
&lt;li&gt;驻留性:一旦作业被装入内存，就会一直驻留在内存中，直至作业运行结束。事实上，在一个时间段内，只需要访问作业的一小部分数据即可正常运行，这就导致了内存中会驻留大量的、暂时用不到的数据，浪费了宝贵的内存资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-117.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.虚拟内存的定义和特征&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;基于局部性原理（忘记的话，可以到第8节查看），在程序装入时，可以将程序中很快会用到的部分装入内存，暂时用不到的部分留在外存，就可以让程序开始执行。&lt;/li&gt;
&lt;li&gt;在程序执行过程中，当所访问的信息不在内存时，由操作系统负责将所需信息从外存调入内存，然后继续执行程序。&lt;/li&gt;
&lt;li&gt;若内存空间不够，由操作系统负责将内存中暂时用不到的信息换出到外存。&lt;/li&gt;
&lt;li&gt;在操作系统的管理下，在用户看来似乎有一个比实际内存大得多的内存，这就是虚拟内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;易混知识点:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;虚拟内存的最大容量是由计算机的地址结构（ CPU寻址范围）确定的&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;虚拟内存的实际容量= min(内存和外存容量之和，CPU寻址范围)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如:  某计算机地址结构为32位，按字节编址，内存大小为512MB，外存大小为2GB.&lt;/p&gt;
&lt;p&gt;则虚拟内存的最大容量为2^32B= 4GB  。 虚拟内存的实际容量=min (2^32B,512MB+2GB)= 2GB+512MB&lt;/p&gt;
&lt;p&gt;虚拟内存有一下三个主要特征:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多次性:无需在作业运行时一次性全部装入内存，而是允许被分成多次调入内存。&lt;/li&gt;
&lt;li&gt;对换性:在作业运行时无需一直常驻内存，而是允许在作业运行过程中，将作业换入、换出。&lt;/li&gt;
&lt;li&gt;虚拟性:从逻辑上扩充了内存的容量，使用户看到的内存容量，远大于实际的容量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;虚拟内存的实现需要建立在离散分配的内存管理方式基础上。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-118.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;13.请求分页管理方式&lt;/h3&gt;
&lt;h4&gt;1.页表机制&lt;/h4&gt;
&lt;p&gt;与基本分页管理相比，请求分页管理中，为了实现“请求调页”，&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;操作系统需要知道每个页面是否已经调入内存;&lt;/li&gt;
&lt;li&gt;如果还没调入，那么也需要知道该页面在外存中存放的位置。&lt;/li&gt;
&lt;li&gt;当内存空间不够时，要实现“页面置换”，操作系统需要通过某些指标来决定到底换出哪个页面;&lt;/li&gt;
&lt;li&gt;有的页面没有被修改过，就不用再浪费时间写回外存。有的页面修改过，就需要将外存中的旧数据覆盖，因此，操作系统也需要记录各个页面是否被修改的信息。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此页表会增加四个字段来上面的信息。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-119.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.缺页中断机制&lt;/h4&gt;
&lt;p&gt;假设此时要访问逻辑地址 = (页号，页内偏移量）= (0，1024)&lt;/p&gt;
&lt;p&gt;在请求分页系统中，每当要访问的页面不在内存时，便产生一个缺页中断，然后由操作系统的缺页中断处理程序处理中断。&lt;/p&gt;
&lt;p&gt;此时缺页的进程阻塞，放入阻塞队列，调页完成后再将其唤醒，放回就绪队列。&lt;/p&gt;
&lt;p&gt;如果内存中有空闲块，则为进程分配一个空闲块，将所缺页面装入该块，并修改页表中相应的页表项。&lt;/p&gt;
&lt;p&gt;如果内存中没有空闲块，则由页面置换算法选择一个页面淘汰，若该页面在内存期间被修改过，则要将其写回外存。未修改过的页面不用写回外存。&lt;/p&gt;
&lt;p&gt;缺页中断是因为当前执行的指令想要访问的目标页面未调入内存而产生的，因此属于内中断&lt;/p&gt;
&lt;p&gt;一条指令在执行期间，可能产生多次缺页中断。(如: copy AtoB，即将逻辑地址A中的数据复制到逻辑地址B，而A、B属于不同的页面，则有可能产生两次中断)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-120.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.地址变换&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-121.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.补充知识点&lt;/h4&gt;
&lt;p&gt;①只有“写指令”才需要修改“修改位”。并且，一般来说只需修改快表中的数据，只有要将快表项删除时才需要写回内存中的慢表。这样可以减少访存次数。&lt;/p&gt;
&lt;p&gt;②和普通的中断处理一样，缺页中断处理依然需要保留CPU现场。&lt;/p&gt;
&lt;p&gt;③需要用某种“页面置换算法”来决定一个换出页面（下节内容)&lt;/p&gt;
&lt;p&gt;④换入/换出页面都需要启动慢速的I/o操作，可见，如果换入/换出太频繁，会有很大的开销。&lt;/p&gt;
&lt;p&gt;⑤页面调入内存后，需要修改慢表，同时也需要将表项复制到快表中。&lt;/p&gt;
&lt;h3&gt;14.页面置换算法&lt;/h3&gt;
&lt;h4&gt;1.最佳置换算法&lt;/h4&gt;
&lt;p&gt;最佳置换算法（OPT，Optimal):每次选择淘汰的页面将是以后永不使用，或者在最长时间内不再被访问的页面，这样可以保证最低的缺页率。&lt;/p&gt;
&lt;p&gt;最佳置换算法可以保证最低的缺页率，但实际上，只有在进程执行的过程中才能知道接下来会访问到的是哪个页面。操作系统无法提前预判页面访问序列。因此，&lt;strong&gt;最佳置换算法是无法实现的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-122.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.先进先出置换算法&lt;/h4&gt;
&lt;p&gt;先进先出置换算法（FIFO):每次选择淘汰的页面是最早进入内存的页面&lt;/p&gt;
&lt;p&gt;实现方法:把调入内存的页面根据调入的先后顺序排成一个队列，需要换出页面时选择队头页面即可。队列的最大长度取决于系统为进程分配了多少个内存块。&lt;/p&gt;
&lt;p&gt;Belady异常―一当为进程分配的物理块数增大时，缺页次数不减反增的异常现象。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;只有FIFO算法会产生Belady异常&lt;/strong&gt;。另外，FIFO算法虽然实现简单，但是该算法与进程实际运行时的规律不适应，因为先进入的页面也有可能最经常被访问。因此，&lt;strong&gt;算法性能差。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-123.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.最近最久未使用算法&lt;/h4&gt;
&lt;p&gt;最近最久未使用置换算法（LRU，least recently used):每次淘汰的页面是最近最久未使用的页面。&lt;/p&gt;
&lt;p&gt;实现方法:赋予每个页面对应的页表项中，用访问字段记录该页面自上次被访问以来所经历的时间t。当需要淘汰一个页面时，选择现有页面中t值最大的，即最近最久未使用的页面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-124.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.时钟置换算法&lt;/h4&gt;
&lt;p&gt;最佳置换算法性能最好，但无法实现;先进先出置换算法实现简单，但算法性能差;最近最久未使用置换算法性能好，是最接近OPT算法性能的，但是实现起来需要专门的硬件支持，算法开销大。&lt;/p&gt;
&lt;p&gt;时钟置换算法是一种性能和开销较均衡的算法，又称CLOCK算法，或最近未用算法(NRU，NotRecently Used)&lt;/p&gt;
&lt;p&gt;简单的CLOCK 算法实现方法:为每个页面设置一个访问位，再将内存中的页面都通过链接指针链接成一个循环队列。当某页被访问时，其访问位置为1。当需要淘汰一个页面时，只需检查页的访问位。如果是0，就选择该页换出;如果是1，则将它置为0，暂不换出，继续检查下一个页面，若第一轮扫描中所有页面都是1，则将这些页面的访问位依次置为0后，再进行第二轮扫描（第二轮扫描中一定会有访问位为0的页面，&lt;strong&gt;因此简单的CLOCK算法选择一个淘汰页面最多会经过两轮扫描）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-125.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.改进型的时钟置换算法&lt;/h4&gt;
&lt;p&gt;简单的时钟置换算法仅考虑到一个页面最近是否被访问过。事实上，如果被淘汰的页面没有被修改过，就不需要执行I/o操作写回外存。只有被淘汰的页面被修改过时，才需要写回外存。
因此，除了考虑一个页面最近有没有被访问过之外，操作系统还应考虑页面有没有被修改过。在其他条件都相同时，应优先淘汰没有修改过的页面，避免I/o操作。这就是改进型的时钟置换算法的思想。修改位=0，表示页面没有被修改过;修改位=1，表示页面被修改过。
为方便讨论，用（访问位，修改位）的形式表示各页面状态。如（1，1)表示一个页面近期被访问过，且被修改过。&lt;/p&gt;
&lt;p&gt;算法规则: 将所有可能被置换的页面排成一个循环队列
第一轮:从当前位置开始扫描到第一个（0，0）的帧用于替换。本轮扫描不修改任何标志位
第二轮:若第一轮扫描失败，则重新扫描，查找第一个（0，1）的帧用于替换。本轮将所有扫描过的帧访问位设为0
第三轮:若第二轮扫描失败，则重新扫描，查找第一个(0，0）的帧用于替换。本轮扫描不修改任何标志位
第四轮:若第三轮扫描失败，则重新扫描，查找第一个（0，1）的帧用于替换。
由于第二轮已将所有帧的访问位设为0，因此经过第三轮、第四轮扫描一定会有一个帧被选中，因此&lt;strong&gt;改进型CLOCK置换算法选择一个淘汰页面最多会进行四轮扫描&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-126.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.五种算法对比&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-127.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;15.页面分配策略&lt;/h3&gt;
&lt;h4&gt;1.页面分配，置换策略&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;驻留集:指请求分页存储管理中给进程分配的物理块的集合。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在采用了虚拟存储技术的系统中，驻留集大小一般小于进程的总大小。&lt;/p&gt;
&lt;p&gt;若驻留集太小，会导致缺页频繁，系统要花大量的时间来处理缺页，实际上用于进程推进的时间很少。&lt;/p&gt;
&lt;p&gt;驻留集太大，又会导致多道程序并发度下降，资源利用率降低。所以应该选择一个合适的驻留集大小。&lt;/p&gt;
&lt;p&gt;固定分配:操作系统为每个进程分配一组固定数目的物理块，在进程运行期间大小不变。即，&lt;strong&gt;驻留集大小不变。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;可变分配:先为每个进程分配一定数目的物理块，在进程运行期间，可根据情况做适当的增加或减少。即，&lt;strong&gt;驻留集大小可变&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;局部置换:发生缺页时只能选进程自己的物理块进行置换。&lt;/p&gt;
&lt;p&gt;全局置换:可以将操作系统保留的空闲物理块分配给缺页进程，也可以将别的进程持有的物理块置换到外存，再分配给缺页进程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-128.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下面来分别介绍这几种方式。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;固定分配局部置换:系统为每个进程分配一定数量的物理块，在整个运行期间都不改变。若进程在运行中发生缺页，则只能从该进程在内存中的页面中选出一页换出，然后再调入需要的页面。这种策略的缺点是:很难在刚开始就确定应为每个进程分配多少个物理块才算合理。(采用这种策略的系统可以根据进程大小、优先级、或是根据程序员给出的参数来确定为一个进程分配的内存块数)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可变分配全局置换:刚开始会为每个进程分配一定数量的物理块。操作系统会保持一个空闲物理块队列。当某进程发生缺页时，从空闲物理块中取出一块分配给该进程;若已无空闲物理块，则可选择一个未锁定的页面换出外存，再将该物理块分配给缺页的进程。采用这种策略时，只要某进程发生缺页都将获的物理块，仅当空闲物理块用完时，系统才选择一个未锁定的页面调出。被选择调出的页可能是进程中任意一个进程的页，因此被选中的这个进程物理块会减少，缺页率会增加。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可变分配局部置换： 刚开始会为每个进程分配一定数量的物理块，当某进程发生缺页时，只允许从该进程自己的物理块中选出一个换出外存。如果进程在运行中频繁地缺页，系统会为该进程多分配几个物理块，直至该进程缺页率趋势适当，反之，如果进程在运行中缺页率特别低，则可适当减少分配给该进程的物理块。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可变分配全局置换:只要缺页就给分配新物理块
可变分配局部置换:要根据发生缺页的频率来动态地增加或减少进程的物理块&lt;/p&gt;
&lt;h4&gt;2.何时调入页面&lt;/h4&gt;
&lt;p&gt;1．预调页策略:根据局部性原理（主要是空间局部性），一次调入若干个相邻的页面可能比一次调入一个页面更高效。但如果提前调入的页面中大多数都没被访问过，则又是低效的。因此可以预测不久之后可能访问到的页面，将它们预先调入内存，但目前预测成功率只有50%左右。故这种策略主要用于进程的首次调入，由程序员指出应该先调入哪些部分。&lt;strong&gt;它是运行前调入&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;2．请求调页策略:进程在运行期间发现缺页时才将所缺页面调入内存。由这种策略调入的页面一定会被访问到，但由于每次只能调入一页，而每次调页都要磁盘l/o操作，因此I/o开销较大。&lt;strong&gt;它是运行时调入&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;3.从何处调入页面&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;系统拥有足够的对换区空间:页面的调入、调出都是在内存与对换区之间进行，这样可以保证页面的调入、调出速度很快。在进程运行前，需将进程相关的数据从文件区复制到对换区。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;image-129.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;系统缺少足够的对换区空间:凡是不会被修改的数据都直接从文件区调入，由于这些页面不会被修改，因此换出时不必写回磁盘，下次需要时再从文件区调入即可。对于可能被修改的部分，换出时需写回磁盘对换区，下次需要时再从对换区调入。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;image-130.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;UNIX方式:运行之前进程有关的数据全部放在文件区，故未使用过的页面，都可从文件区调入。若被使用过的页面需要换出，则写回对换区，下次需要时从对换区调入。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;image-131.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.抖动（颠簸）现象，工作集&lt;/h4&gt;
&lt;p&gt;刚刚换出的页面马上又要换入内存，刚刚换入的页面马上又要换出外存，这种频繁的页面调度行为称为抖动，或颠簸。&lt;strong&gt;产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数&lt;/strong&gt;（分配给进程的物理块不够)。&lt;/p&gt;
&lt;p&gt;为进程分配的物理块太少，会使进程发生抖动现象。为进程分配的物理块太多，又会降低系统整体的并发度，降低某些资源的利用率&lt;/p&gt;
&lt;p&gt;为了研究为应该为每个进程分配多少个物理块，Denning提出了进程“工作集”的概念。&lt;/p&gt;
&lt;p&gt;驻留集:指请求分页存储管理中给进程分配的内存块的集合。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;工作集:指在某段时间间隔里，进程实际访问页面的集合。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一般来说，驻留集大小不能小于工作集大小，否则进程运行过程中将频繁缺页。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-132.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;4.文件系统&lt;/h2&gt;
&lt;h3&gt;1.文件管理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;文件――就是一组有意义的信息/数据集合。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个文件有哪些属性？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;文件名:由创建文件的用户决定文件名，主要是为了方便用户找到文件，同一目录下不允许有重名文件。&lt;/li&gt;
&lt;li&gt;标识符:一个系统内的各文件标识符唯一，对用户来说毫无可读性，因此标识符只是操作系统用于区分各个文件的一种内部名称。&lt;/li&gt;
&lt;li&gt;类型:指明文件的类型&lt;/li&gt;
&lt;li&gt;位置:文件存放的路径（让用户使用）、在外存中的地址(操作系统使用，对用户不可见)&lt;/li&gt;
&lt;li&gt;大小:指明文件大小创建时间、上次修改时间文件所有者信息&lt;/li&gt;
&lt;li&gt;保护信息:对文件进行保护的访问控制信息&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件分为有结构文件和无结构文件。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-133.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作系统向上（用户和应用程序）提供的功能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-134.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2.文件的逻辑结构&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-135.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;按文件是否有结构分类，可以分为无结构文件、有结构文件两种。&lt;/p&gt;
&lt;p&gt;无结构文件:文件内部的数据就是一系列二进制流或字符流组成。又称“流式文件”。如:Windows操作系统中的.txt文件。&lt;/p&gt;
&lt;p&gt;有结构文件:由一组相似的记录组成，又称“记录式文件”。每条记录又若干个数据项组成。如:数据库表文件。一般来说，每条记录有一个数据项可作为关键字（作为识别不同记录的ID）&lt;/p&gt;
&lt;p&gt;我们主要研究有结构文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-136.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.顺序文件&lt;/h4&gt;
&lt;p&gt;顺序文件:文件中的记录一个接一个地顺序排列（逻辑上），记录可以是定长的或可变长的。各个记录在物理上可以顺序存储（相当于数组）或链式存储（相当于链表）。&lt;/p&gt;
&lt;p&gt;顺序存储又可以分为串结构和顺序结构。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-137.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么这几种存储方式可以快速找到第i个记录对应的地址呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-138.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;结论:定长记录的顺序文件，若物理上采用顺序存储，则可实现随机存取;若能再保证记录的顺序结构，则可实现快速检索(即根据关键字快速找到对应记录)&lt;/p&gt;
&lt;h4&gt;2.索引文件&lt;/h4&gt;
&lt;p&gt;思考：对于可变长记录文件，要找到第i个记录，必须先顺序第查找前i-1个记录,但是很多应用场景中又必须使用可变长记录。如何解决这个问题?&lt;/p&gt;
&lt;p&gt;这时我们可以建立一张索引表来快速找到第i个记录。如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-139.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;**索引表本身是定长记录的顺序文件。**因此可以快速找到第i个记录对应的索引项。&lt;/p&gt;
&lt;p&gt;可将关键字作为索引号内容，若按关键字顺序排列，则还可以支持按照关键字折半查找。&lt;/p&gt;
&lt;p&gt;每当要增加/删除一个记录时，需要对索引表进行修改。由于索引文件有很快的检索速度，因此主要用于对信息处理的及时性要求比较高的场合。&lt;/p&gt;
&lt;p&gt;另外，可以用不同的数据项建立多个索引表。如:学生信息表中，可用关键字“学号”建立一张索引表。也可用“姓名”建立一张索引表。这样就可以根据“姓名”快速地检索文件了。(Eg: SQL就支持根据某个数据项建立索引的功能)&lt;/p&gt;
&lt;h4&gt;3.索引顺序文件&lt;/h4&gt;
&lt;p&gt;思考索引文件的缺点:每个记录对应一个索引表项，因此索引表可能会很大。比如:文件的每个记录平均只占8字节，而每个索引表项占32个字节，那么索引表都要比文件内容本身大4倍，这样对存储空间的利用率就太低了。&lt;/p&gt;
&lt;p&gt;那么如何解决呢？&lt;/p&gt;
&lt;p&gt;我们可以建立一个索引顺序文件。&lt;/p&gt;
&lt;p&gt;索引顺序文件是索引文件和顺序文件思想的结合。索引顺序文件中，同样会为文件建立一张索引表，但不同的是:&lt;strong&gt;并不是每个记录对应一个索引表项，而是一组记录对应一个索引表项。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在本例中，学生记录按照学生姓名的开头字母进行分组。每个分组就是一个顺序文件，分组内的记录不需要按关键字排序。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-140.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.多级索引顺序文件&lt;/h4&gt;
&lt;p&gt;为了进一步提高检索效率，可以为顺序文件建立多级索引表。&lt;/p&gt;
&lt;p&gt;例如，对于一个含10^6个记录的文件，可先为该文件建立一张低级索引表，每100个记录为一组，故低级索引表中共有10000个表项(即10000个定长记录)，再把这10000个定长记录分组，每组100个，为其建立顶级索引表，故顶级索引表中共有100个表项。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-141.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.文件目录&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-142.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目录本身就是一种有结构文件，由一条条记录组成。每条记录对应一个在该放在该目录下的文件。如图所示&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-143.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.文件控制块（FCB）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;目录文件中的一条记录就是一个“文件控制块（FCB）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;FCB的有序集合称为“文件目录”，一个FCB就是一个文件目录项。&lt;/p&gt;
&lt;p&gt;FCB中包含了文件的基本信息（文件名、物理地址、逻辑结构、物理结构等），存取控制信息（是否可读/可写、禁止访问的用户名单等），使用信息（如文件的建立时间、修改时间等）。&lt;/p&gt;
&lt;p&gt;最重要，最基本的还是文件名、文件存放的物理地址。&lt;/p&gt;
&lt;h4&gt;2.单级目录结构&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-144.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.二级目录结构&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-145.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.多级目录结构（树形目录结构）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-146.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;树形目录结构可以很方便地对文件进行分类，层次结构清晰，也能够更有效地进行文件的管理和保护。但是，树形结构不便于实现文件的共享。为此，提出了“无环图目录结构”。&lt;/p&gt;
&lt;h4&gt;5.无环图目录结构&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-147.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以用不同的文件名指向同一个文件，甚至可以指向同一个目录(共享同一目录下的所有内容）。&lt;/p&gt;
&lt;p&gt;需要为每个共享结点设置一个共享计数器，用于记录此时有多少个地方在共享该结点。用户提出删除结点的请求时，只是删除该用户的FCB、并使共享计数器减1，并不会直接删除共享结点。&lt;/p&gt;
&lt;p&gt;只有共享计数器减为0时，才删除结点。&lt;/p&gt;
&lt;p&gt;注意:共享文件不同于复制文件。在共享文件中，由于各用户指向的是同一个文件，因此只要其中一个用户修改了文件数据，那么所有用户都可以看到文件数据的变化。&lt;/p&gt;
&lt;h4&gt;6.索引结点（对FCB的改进）&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-148.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当找到文件名对应的目录项时，才需要将索引结点调入内存，索引结点中记录了文件的各种信息，包括文件在外存中的存放位置，根据“存放位置”即可找到文件。&lt;/p&gt;
&lt;p&gt;存放在外存中的索引结点称为“磁盘索引结点”，当索引结点放入内存后称为“内存索引结点”。相比之下内存索引结点中需要增加一些信息，比如:文件是否被修改、此时有几个进程正在访问该文件等。&lt;/p&gt;
&lt;h3&gt;4.文件分配方式（文件物理结构）&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-149.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在介绍这些分配方式之前，先介绍一下什么是文件块，磁盘块。&lt;/p&gt;
&lt;p&gt;在内存管理中，进程的逻辑地址空间被分为一个一个页面。&lt;/p&gt;
&lt;p&gt;同样的，在外存管理中，为了方便对文件数据的管理，文件的逻辑地址空间也被分为了一个一个的文件“块”。&lt;/p&gt;
&lt;p&gt;于是文件的逻辑地址也可以表示为（逻辑块号，块内地址）的形式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;用户通过逻辑地址来操作自己的文件，操作系统要负责实现从逻辑地址到物理地址的映射。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-150.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.连续分配&lt;/h4&gt;
&lt;p&gt;**连续分配方式要求每个文件在磁盘上占有一组连续的块。**如图所示&lt;/p&gt;
&lt;p&gt;用户给出要访问的逻辑块号，操作系统找到该文件对应的目录项（FCB)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;物理块号=起始块号＋逻辑块号&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当然，还需要检查用户提供的逻辑块号是否合法（逻辑块号≥长度就不合法）&lt;/p&gt;
&lt;p&gt;优点:支持顺序访问和直接访问（即随机访问——）；连续分配的文件在顺序访问时速度最快&lt;/p&gt;
&lt;p&gt;缺点:不方便文件拓展;存储空间利用率低，会产生磁盘碎片&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-151.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.链接分配——隐式链接&lt;/h4&gt;
&lt;p&gt;链接分配采取离散分配的方式，可以为文件分配离散的磁盘块。分为隐式链接和显式链接两种。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从逻辑块号到物理块号的转变&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户给出要访问的逻辑块号i，操作系统找到该文件对应的目录项(FCB)&lt;/p&gt;
&lt;p&gt;从目录项中找到起始块号（即0号块），将0号逻辑块读入内存，由此知道1号逻辑块存放的物理块号，于是读入1号逻辑块，再找到2号逻辑块的存放位置......以此类推。&lt;/p&gt;
&lt;p&gt;因此，读入i号逻辑块，总共需要i+1次磁盘l/O。&lt;/p&gt;
&lt;p&gt;结论:采用链式分配（隐式链接）方式的文件，只支持顺序访问，不支持随机访问，查找效率低。另外，指向下一个盘块的指针也需要耗费少量的存储空间。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-152.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.链接分配——显式链接&lt;/h4&gt;
&lt;p&gt;把用于链接文件各物理块的指针显式地存放在一张表中。即文件分配表（FAT，File Allocation Table）。如图所示&lt;/p&gt;
&lt;p&gt;注意:一个磁盘仅设置一张FAT。开机时，将FAT读入内存，并常驻内存。FAT的各个表项在物理上连续存储，且每一个表项长度相同，因此“物理块号”字段可以是隐含的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从逻辑块号到物理块号的转变&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户给出要访问的逻辑块号i，操作系统找到该文件对应的目录项( FCB)&lt;/p&gt;
&lt;p&gt;从目录项中找到起始块号，若i&amp;gt;0，则查询内存中的文件分配表FAT，往后找到i号逻辑块对应的物理块号。逻辑块号转换成物理块号的过程不需要读磁盘操作。&lt;/p&gt;
&lt;p&gt;结论:采用链式分配（显式链接）方式的文件，支持顺序访问，也支持随机访问（想访问i号逻辑块时，并不需要依次访问之前的 0~i-1号逻辑块)，由于块号转换的过程不需要访问磁盘，因此相比于隐式链接来说，访问速度快很多。&lt;/p&gt;
&lt;p&gt;显然，显式链接也不会产生外部碎片，也可以很方便地对文件进行拓展。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-153.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.两种链接分配方式总结&lt;/h4&gt;
&lt;p&gt;隐式链接――除文件的最后一个盘块之外，每个盘块中都存有指向下一个盘块的指针。文件目录包括文件第一块的指针和最后一块的指针。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优点:很方便文件拓展，不会有碎片问题，外存利用率高。&lt;/li&gt;
&lt;li&gt;缺点:只支持顺序访问，不支持随机访问，查找效率低，指向下一个盘块的指针也需要耗费少量的存储空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;显式链接――把用于链接文件各物理块的指针显式地存放在一张表中，即文件分配表（FAT，FileAllocation Table)。一个磁盘只会建立一张文件分配表。开机时文件分配表放入内存，并常驻内存。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优点:很方便文件拓展，不会有碎片问题，外存利用率高，并且支持随机访问。相比于隐式链接来说，地址转换时不需要访问磁盘，因此文件的访问效率更高。&lt;/li&gt;
&lt;li&gt;缺点:文件分配表的需要占用一定的存储空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5.索引分配&lt;/h4&gt;
&lt;p&gt;索引分配允许文件离散地分配在各个磁盘块中，&lt;strong&gt;系统会为每个文件建立一张索引表&lt;/strong&gt;，索引表中记录了文件的各个逻辑块对应的物理块（索引表的功能类似于内存管理中的页表――建立逻辑页面到物理页之间的映射关系）。&lt;strong&gt;索引表存放的磁盘块称为索引块。文件数据存放的磁盘块称为数据块。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从逻辑块号到物理块号的转变&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户给出要访问的逻辑块号i，操作系统找到该文件对应的目录项（FCB）&lt;/p&gt;
&lt;p&gt;从目录项中可知索引表存放位置，将索引表从外存读入内存，并查找索引表即可知道i号逻辑块在外存中的存放位置。&lt;/p&gt;
&lt;p&gt;可见，索引分配方式可以支持随机访问。文件拓展也很容易实现(只需要给文件分配一个空闲块，并增加一个索引表项即可)&lt;/p&gt;
&lt;p&gt;但是索引表需要占用一定的存储空间&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-154.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果一个文件的索引表太大，一个磁盘块放不下，那么如何解决呢？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以用以下三种方式解决。&lt;/p&gt;
&lt;p&gt;①链接方案:如果索引表太大，一个索引块装不下，那么可以将多个索引块链接起来存放。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-155.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;②多层索引:建立多层索引（原理类似于多级页表）。使第一层索引块指向第二层的索引块。还可根据文件大小的要求再建立第三层、第四层索引块。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-156.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;③混合索引:多种索引分配方式的结合。例如，一个文件的顶级索引表中，既包含直接地址索引（直接指向数据块），又包含一级间接索引（指向单层索引表）、还包含两级间接索引（指向两层索引表）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-157.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;总结&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;①链接方案 : 如果索引表太大，一个索引块装不下，那么可以将多个索引块链接起来存放。缺点:若文件很大，索引表很长，就需要将很多个索引块链接起来。想要找到i号索引块，必须先依次读入0~i-1号索引块，这就导致磁盘I/O次数过多，查找效率低下。&lt;/p&gt;
&lt;p&gt;②多层索引:建立多层索引(原理类似于多级页表）。使第一层索引块指向第二层的索引块。还可根据文件大小的要求再建立第三层、第四层索引块。采用K层索引结构，且顶级索引表未调入内存，则访问一个数据块只需要K+1次读磁盘操作。缺点:即使是小文件，访问一个数据块依然需要K+1次读磁盘。&lt;/p&gt;
&lt;p&gt;③混合索引:多种索引分配方式的结合。例如，一个文件的顶级索引表中，既包含直接地址索引（直接指向数据块），又包含一级间接索引（指向单层索引表)、还包含两级间接索引(指向两层索引表)。优点:对于小文件来说，访问一个数据块所需的读磁盘次数更少。&lt;/p&gt;
&lt;h4&gt;6.三种分配方式总结&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-158.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.对空闲磁盘块的管理（文件存储空间管理）&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-159.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.文件卷&lt;/h4&gt;
&lt;p&gt;存储空间的划分:将物理磁盘划分为一个个文件卷(逻辑卷、逻辑盘)。所谓的文件卷就相当于电脑上的C盘，D盘等。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-160.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.空闲表法&lt;/h4&gt;
&lt;p&gt;为一个磁盘创建一个表，来存储空闲磁盘块的位置。&lt;/p&gt;
&lt;p&gt;如何分配磁盘块 : 与内存管理中的动态分区分配很类似，为一个文件分配连续的存储空间。同样可采用首次适应、最佳适应、最坏适应等算法来决定要为文件分配哪个区间。&lt;/p&gt;
&lt;p&gt;如何回收磁盘块:与内存管理中的动态分区分配很类似，当回收某个存储区时需要有四种情况―—①回收区的前后都没有相邻空闲区;②回收区的前后都是空闲区;③回收区前面是空闲区;④回收区后面是空闲区。总之，回收时需要注意表项的合并问题。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-161.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.空闲链表法&lt;/h4&gt;
&lt;p&gt;空闲链表发分为空闲盘块链和空闲盘区链。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-162.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;1.空闲盘块链&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;操作系统保存着链头、链尾指针。&lt;/li&gt;
&lt;li&gt;如何分配:若某文件申请K个盘块，则从链头开始依次摘下K个盘块分配，并修改空闲链的链头指针。&lt;/li&gt;
&lt;li&gt;如何回收:回收的盘块依次挂到链尾，并修改空闲链的链尾指针。&lt;/li&gt;
&lt;li&gt;适用于离散分配的物理结构。为文件分配多个盘块时可能要重复多次操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-163.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;2.空闲盘区链&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;操作系统保存着链头、链尾指针。&lt;/li&gt;
&lt;li&gt;如何分配:若某文件申请K个盘块，则可以采用首次适应、最佳适应等算法，从链头开始检索，按照算法规则找到一个大小符合要求的空闲盘区，分配给文件。若没有合适的连续空闲块，也可以将不同盘区的盘块同时分配给一个文件，注意分配后可能要修改相应的链指针、盘区大小等数据。&lt;/li&gt;
&lt;li&gt;如何回收:若回收区和某个空闲盘区相邻，则需要将回收区合并到空闲盘区中。若回收区没有和任何空闲区相邻，将回收区作为单独的一个空闲盘区挂到链尾。&lt;/li&gt;
&lt;li&gt;离散分配、连续分配都适用。为一个文件分配多个盘块时效率更高&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-164.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.位示图法&lt;/h4&gt;
&lt;p&gt;位示图:每个二进制位对应一个盘块。在本例中，“0”代表盘块空闲，“1”代表盘块已分配。位示图一般用连续的“字”来表示，如本例中一个字的字长是16位，字中的每一位对应一个盘块。因此可以用(字号，位号）对应一个盘块号。当然有的题目中也描述为（行号，列号)&lt;/p&gt;
&lt;p&gt;(字号,位号)=(i j)  的二进制位对应的盘块号 b= ni + j&lt;/p&gt;
&lt;p&gt;b号盘块对应的字号i = b/n，位号j = b%n。&lt;/p&gt;
&lt;p&gt;如何分配:若文件需要K个块，&lt;/p&gt;
&lt;p&gt;①顺序扫描位示图，找到K个相邻或不相邻的“0”;&lt;/p&gt;
&lt;p&gt;②根据字号、位号算出对应的盘块号，将相应盘块分配给文件;&lt;/p&gt;
&lt;p&gt;③将相应位设置为“1”。&lt;/p&gt;
&lt;p&gt;如何回收:&lt;/p&gt;
&lt;p&gt;①根据回收的盘块号计算出对应的字号、位号;&lt;/p&gt;
&lt;p&gt;②将相应二进制位设为“0”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-165.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.成组链接法&lt;/h4&gt;
&lt;p&gt;空闲表法、空闲链表法不适用于大型文件系统，因为空闲表或空闲链表可能过大。UNIX系统中采用了成组链接法对磁盘空闲块进行管理。
文件卷的目录区中专门用一个磁盘块作为“超级块”，当系统启动时需要将超级块读入内存。并且要保证内存与外存中的“超级块”数据一致。如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-166.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;超级块中存储的内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-167.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如何分配?
Eg :需要100个空闲块
①检查第一个分组的块数是否足够。100=100，是足够的。&lt;/p&gt;
&lt;p&gt;②分配第一个分组中的100个空闲块。但是由于300号块内存放了再下一组的信息，因此300号块的数据需要复制到超级块中。&lt;/p&gt;
&lt;p&gt;Eg :需要1个空闲块
①检查第一个分组的块数是否足够。1&amp;lt;100，因此是足够的。&lt;/p&gt;
&lt;p&gt;②分配第一个分组中的1个空闲块，并修改相应数据&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如何回收?
Eg :假设每个分组最多为100个空闲块，此时第一个分组已有99个块，还要再回收一块。&lt;/p&gt;
&lt;p&gt;Eg : 假设每个分组最多为100个空闲块，此时第一个分组已有100个块，还要再回收一块。需要将超级块中的数据复制到新回收的块中，并修改超级块的内容，让新回收的块成为第一个分组。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.文件的基本操作&lt;/h3&gt;
&lt;h4&gt;1.创建文件&lt;/h4&gt;
&lt;p&gt;进行Create系统调用时，需要提供的几个主要参数:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;所需的外存空间大小（如:一个盘块，即1KB）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件存放路径（“D:/Demo”）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件名（这个地方默认为“新建文本文档.txt”）&lt;/p&gt;
&lt;p&gt;操作系统在处理Create系统调用时，主要做了两件事:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;1.在外存中找到文件所需的空间（结合上小节学习的空闲链表法、位示图、成组链接法等管理策略，找到空闲空间）
2.根据文件存放路径的信息找到该目录对应的目录文件（此处就是 D:/Demo目录），在目录中创建该文件对应的目录项。目录项中包含了文件名、文件在外存中的存放位置等信息。&lt;/p&gt;
&lt;h4&gt;2.删除文件&lt;/h4&gt;
&lt;p&gt;进行Delete系统调用时，需要提供的几个主要参数:&lt;/p&gt;
&lt;p&gt;1.文件存放路径（“D:/Demo”）&lt;/p&gt;
&lt;p&gt;2.文件名（“test.txt”）&lt;/p&gt;
&lt;p&gt;操作系统在处理Delete系统调用时，主要做了几件事:&lt;/p&gt;
&lt;p&gt;1．根据文件存放路径找到相应的目录文件，从目录中找到文件名对应的目录项。&lt;/p&gt;
&lt;p&gt;2．根据该目录项记录的文件在外存的存放位置、文件大小等信息，回收文件占用的磁盘块。(回收磁盘块时，根据空闲表法、空闲链表法、位图法等管理策略的不同，需要做不同的处理)&lt;/p&gt;
&lt;p&gt;3.从目录表中删除文件对应的目录项。&lt;/p&gt;
&lt;h4&gt;3.打开文件&lt;/h4&gt;
&lt;p&gt;在很多操作系统中，在对文件进行操作之前，要求用户先使用open系统调用“打开文件”，需要提供的几个主要参数:
1．文件存放路径（“D:/Demo”）&lt;/p&gt;
&lt;p&gt;2.文件名（ “test.txt”）&lt;/p&gt;
&lt;p&gt;3．要对文件的操作类型（如:r只读;rw读写等）&lt;/p&gt;
&lt;p&gt;操作系统在处理open系统调用时，主要做了几件事:&lt;/p&gt;
&lt;p&gt;1．根据文件存放路径找到相应的目录文件，从目录中找到文件名对应的的目录项，并检查该用户是否有指定的操作权限。&lt;/p&gt;
&lt;p&gt;2．将目录项复制到内存中的“打开文件表”中。并将对应表目的编号返回给用户。之后用户使用打开文件表的编号来指明要操作的文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-168.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要注意的是，有两张打开文件表，一个是进程自带的，另一个是系统的（只有一张）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-169.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.关闭文件&lt;/h4&gt;
&lt;p&gt;进程使用完文件后，要“关闭文件”。
操作系统在处理Close系统调用时，主要做了几件事:&lt;/p&gt;
&lt;p&gt;1.将进程的打开文件表相应表项删除&lt;/p&gt;
&lt;p&gt;2.回收分配给该文件的内存空间等资源&lt;/p&gt;
&lt;p&gt;3.系统打开文件表的打开计数器count 减1，若count =0，则删除对应表项。&lt;/p&gt;
&lt;h4&gt;5.读文件&lt;/h4&gt;
&lt;p&gt;进程使用read系统调用完成写操作。&lt;/p&gt;
&lt;p&gt;需要指明是哪个文件（在支持“打开文件”操作的系统中，只需要提供文件在打开文件表中的索引号即可），&lt;/p&gt;
&lt;p&gt;还需要指明要读入多少数据（如:读入1KB）、&lt;/p&gt;
&lt;p&gt;指明读入的数据要放在内存中的什么位置。&lt;/p&gt;
&lt;p&gt;操作系统在处理read 系统调用时，会从读指针指向的外存中，将用户指定大小的数据读入用户指定的内存区域中。&lt;/p&gt;
&lt;h4&gt;6.写文件&lt;/h4&gt;
&lt;p&gt;进程使用write系统调用完成写操作，&lt;/p&gt;
&lt;p&gt;需要指明是哪个文件（在支持“打开文件”操作的系统中，只需要提供文件在打开文件表中的索引号即可），&lt;/p&gt;
&lt;p&gt;还需要指明要写出多少数据（如:写出1KB)、&lt;/p&gt;
&lt;p&gt;写回外存的数据放在内存中的什么位置&lt;/p&gt;
&lt;p&gt;操作系统在处理write系统调用时，会从用户指定的内存区域中，将指定大小的数据写回写指针指向的外存。&lt;/p&gt;
&lt;h3&gt;7.文件共享&lt;/h3&gt;
&lt;h4&gt;1.基于索引节点的共享方式（硬链接）&lt;/h4&gt;
&lt;p&gt;知识回顾:索引结点，是一种文件目录瘦身策略。由于检索文件时只需用到文件名，因此可以将除了文件名之外的其他信息放到索引结点中。这样目录项就只需要包含文件名、索引结点指针。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-170.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;索引结点中设置一个链接计数变量 count，用于表示链接到本索引结点上的用户目录项数。&lt;/strong&gt;
若count =2，说明此时有两个用户目录项链接到该索引结点上，或者说是有两个用户在共享此文件。若某个用户决定“删除”该文件，则只是要把用户目录中与该文件对应的目录项删除，且索引结点的count值减1。
若count&amp;gt;0，说明还有别的用户要使用该文件，暂时不能把文件数据删除，否则会导致指针悬空。当count =0时系统负责删除文件。&lt;/p&gt;
&lt;h4&gt;2.基于符号链的共享方式（软链接）&lt;/h4&gt;
&lt;p&gt;当User3访问“ccc”时，操作系统判断文件“ccc”属于Link类型文件，于是会根据其中记录的路径层层查找目录，最终找到User1的目录表中的“aaa”表项，于是就找到了文件1的索引结点。类似于快捷方式。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-171.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.文件保护&lt;/h3&gt;
&lt;h4&gt;1.口令保护&lt;/h4&gt;
&lt;p&gt;为文件设置一个“口令”(如: abc112233），用户请求访问该文件时必须提供“口令”。&lt;/p&gt;
&lt;p&gt;口令一般存放在文件对应的FCB或索引结点中。用户访问文件前需要先输入“口令”，操作系统会将用户提供的口令与FCB中存储的口令进行对比，如果正确，则允许该用户访问文件。&lt;/p&gt;
&lt;p&gt;优点:保存口令的空间开销不多，验证口令的时间开销也很小。&lt;/p&gt;
&lt;p&gt;缺点:正确的“口令”存放在系统内部，不够安全。&lt;/p&gt;
&lt;h4&gt;2.加密保护&lt;/h4&gt;
&lt;p&gt;使用某个“密码”对文件进行加密，在访问文件时需要提供正确的“密码”才能对文件进行正确的解密。
Eg:一个最简单的加密算法――异或加密。
假设用于加密/解密的“密码”为“01001”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-172.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;优点:保密性强，不需要在系统中存储“密码”。&lt;/p&gt;
&lt;p&gt;缺点:编码/译码，或者说加密/解密要花费一定时间。&lt;/p&gt;
&lt;h4&gt;3.访问控制&lt;/h4&gt;
&lt;p&gt;在每个文件的FCB（或索引结点）中增加一个访问控制列表(Access-Control List, ACL)，该表中记录了各个用户可以对该文件执行哪些操作。如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-173.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;精简的访问列表:以“组”为单位，标记各“组”用户可以对文件执行哪些操作。如:分为系统管理员、文件主、文件主的伙伴、其他用户几个分组。当某用户想要访问文件时，系统会检查该用户所属的分组是否有相应的访问权限。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-174.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.总结&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-175.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;9.文件的层次结构&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-176.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;用一个例子来辅助记忆文件系统的层次结构:&lt;/p&gt;
&lt;p&gt;假设某用户请求删除文件“D:/工作目录/学生信息.xlsx”的最后100条记录。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用户需要通过操作系统提供的接口发出上述请求一用户接口。&lt;/li&gt;
&lt;li&gt;由于用户提供的是文件的存放路径，因此需要操作系统一层一层地查找目录，找到对应的目录项――文件目录系统&lt;/li&gt;
&lt;li&gt;不同的用户对文件有不同的操作权限，因此为了保证安全，需要检查用户是否有访问权限―一存取控制模块（存取控制验证层)&lt;/li&gt;
&lt;li&gt;验证了用户的访问权限之后，需要把用户提供的“记录号”转变为对应的逻辑地址――逻辑文件系统与文件信息缓冲区&lt;/li&gt;
&lt;li&gt;知道了目标记录对应的逻辑地址后，还需要转换成实际的物理地址――物理文件系统&lt;/li&gt;
&lt;li&gt;要删除这条记录，必定要对磁盘设备发出请求――设备管理程序模块&lt;/li&gt;
&lt;li&gt;删除这些记录后，会有一些盘块空闲，因此要将这些空闲盘块回收――辅助分配模块。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;10.磁盘结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;磁盘的表面由一些磁性物质组成，可以用这些磁性物质来记录二进制数据&lt;/li&gt;
&lt;li&gt;磁盘的盘面被划分成一个个磁道。这样的一个“圈”就是一个磁道。&lt;/li&gt;
&lt;li&gt;一个磁道又被划分成一个个扇区，每个扇区就是一个“磁盘块”。各个扇区存放的数据量相同（如1KB）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-177.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有盘面中相对位置相同的磁道组成柱面。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-178.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可用(柱面号，盘面号，扇区号）来定位任意一个“磁盘块”。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可根据该地址读取一个“块”
①根据“柱面号”移动磁臂，让磁头指向指定柱面;
②激活指定盘面对应的磁头;
③磁盘旋转的过程中，指定的扇区会从磁头下面划过，这样就完成了对指定扇区的读/写。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;磁盘分类&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-179.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;11.磁盘调度算法&lt;/h3&gt;
&lt;h4&gt;1.一次磁盘读/写操作需要的时间&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-180.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;寻找时间&lt;/strong&gt;(寻道时间)Ts:在读/写数据前，将磁头移动到指定磁道所花的时间。
①启动磁头臂是需要时间的。假设耗时为s;
②移动磁头也是需要时间的。假设磁头匀速移动，每跨越一个磁道耗时为m，总共需要跨越n条磁道。则:
寻道时间Ts = s + m*n&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;延迟时间&lt;/strong&gt;T:通过旋转磁盘，使磁头定位到目标扇区所需要的时间。设磁盘转速为r(单位:转/秒，或转/分），则平均所需的延迟时间T=(1/2)*(1/r)= 1/2r。&lt;/p&gt;
&lt;p&gt;1/r就是转一圈需要的时间。找到目标扇区平均需要转半圈，因此再乘以1/2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;传输时间&lt;/strong&gt;Tt:从磁盘读出或向磁盘写入数据所经历的时间，假设磁盘转速为r，此次读/写的字节数为b，每个磁道上的字节数为N。则:
传输时间Tt = (1/r)*(b/N) = b/(rN)&lt;/p&gt;
&lt;p&gt;每个磁道要可存N字节的数据，因此b字节的数据需要b/N个磁道才能存储。而读/写一个磁道所需的时间刚好又是转一圈所需要的时间1/r。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;总的平均存取时间    T=Ts+ 1/2r + b/(rN)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;延迟时间和传输时间都与磁盘转速相关，且为线性相关。而转速是硬件的固有属性，因此操作系统也无法优化延迟时间和传输时间。但是操作系统的磁盘调度算法会直接影响寻道时间。&lt;/p&gt;
&lt;h4&gt;2.先来先服务算法&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;根据进程请求访问磁盘的先后顺序进行调度。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-181.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3. 最短寻找时间优先（SSTF）&lt;/h4&gt;
&lt;p&gt;SSTF算法会优先处理的磁道是与当前磁头最近的磁道。可以保证每次的寻道时间最短，但是并不能保证总的寻道时间最短。(其实就是贪心算法的思想，只是选择眼前最优，但是总体未必最优)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-182.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.扫描算法（SCAN）&lt;/h4&gt;
&lt;p&gt;SSTF算法会产生饥饿的原因在于:磁头有可能在一个小区域内来回来去地移动。为了防止这个问题，可以规定，只有磁头移动到最外侧磁道的时候才能往内移动，移动到最内侧磁道的时候才能往外移动。这就是扫描算法（SCAN)的思想。由于磁头移动的方式很像电梯，因此也叫电梯算法。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-183.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.LOOK调度算法&lt;/h4&gt;
&lt;p&gt;扫描算法（SCAN)中，只有到达最边上的磁道时才能改变磁头移动方向，事实上，处理了184号磁道的访问请求之后就不需要再往右移动磁头了。LOOK调度算法就是为了解决这个问题，如果在磁头移动方向上已经没有别的请求，就可以立即改变磁头移动方向。(边移动边观察，因此叫LOOK)。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-184.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.循环扫描算法（C—SCAN）&lt;/h4&gt;
&lt;p&gt;SCAN算法对于各个位置磁道的响应频率不平均，而C-SCAN算法就是为了解决这个问题。规定只有磁头朝某个特定方向移动时才处理磁道访问请求，而返回时直接快速移动至起始端而不处理任何请求。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-185.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;7.C-LOOK调度算法&lt;/h4&gt;
&lt;p&gt;C-SCAN 算法的主要缺点是只有到达最边上的磁道时才能改变磁头移动方向，并且磁头返回时不一定需要返回到最边缘的磁道上。C-LOOK算法就是为了解决这个问题。如果磁头移动的方向上已经没有磁道访问请求了，就可以立即让磁头返回，并且磁头只需要返回到有磁道访问请求的位置即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-186.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;12.减少延迟时间的方法&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;image-187.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;假设要连续读取橙色区域的2、3、4扇区:
磁头读取一块的内容（也就是一个扇区的内容）后，需要一小段时间处理，而盘片又在不停地旋转
因此，如果2、3号扇区相邻着排列，则读完2号扇区后无法连续不断地读入3号扇区
必须等盘片继续旋转，3号扇区再次划过磁头，才能完成扇区读入。&lt;/p&gt;
&lt;p&gt;结论:磁头读入一个扇区数据后需要一小段时间处理，如果逻辑上相邻的扇区在物理上也相邻，则读入几个连续的逻辑扇区，可能需要很长的“延迟时间”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-188.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;1.交替编号&lt;/h4&gt;
&lt;p&gt;若采用交替编号的策略，即让逻辑上相邻的扇区在物理上有一定的间隔，可以使读取连续的逻辑扇区所需要的延迟时间更小。如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-189.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.磁盘地址结构的设计&lt;/h4&gt;
&lt;p&gt;思考:为什么磁盘的物理地址是（柱面号，盘面号，扇区号），而不是(盘面号，柱面号，扇区号)？&lt;/p&gt;
&lt;p&gt;答:读取地址连续的磁盘块时，采用（柱面号，盘面号，扇区号）的地址结构可以减少磁头移动消耗的时间&lt;/p&gt;
&lt;p&gt;注：不是很理解&lt;/p&gt;
&lt;h4&gt;3.错位命名&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-190.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-191.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;13.磁盘的管理&lt;/h3&gt;
&lt;h4&gt;1.磁盘初始化&lt;/h4&gt;
&lt;p&gt;磁盘初始化:
Step 1:进行低级格式化（物理格式化），将磁盘的各个磁道划分为扇区。一个扇区通常可分为头、数据区域（如512B大小）、尾三个部分组成。管理扇区所需要的各种数据结构一般存放在头、尾两个部分，包括扇区校验码（如奇偶校验、CRC循环冗余校验码等，校验码用于校验扇区中的数据是否发生错误）&lt;/p&gt;
&lt;p&gt;Step 2:将磁盘分区，每个分区由若干柱面（磁道）组成（即分为我们熟悉的C盘、D盘、E盘）&lt;/p&gt;
&lt;p&gt;step 3:进行逻辑格式化，创建文件系统。包括创建文件系统的根目录、初始化存储空间管理所用的数据结构（如位示图、空闲分区表）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-192.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.引导块&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;计算机开机时需要进行一系列初始化的工作，这些初始化工作是通过执行初始化程序（自举程序）完成的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;初始化程序可以放在ROM(只读存储器）中。ROM中的数据在出厂时就写入了，并且以后不能再修改 。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;初始化程序程序（自举程序）放在ROM中存在什么问题?万一需要更新自举程序，将会很不方便，因为ROM中的数据无法更改。如何解决呢?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ROM中只存放很小的“自举装入程序”。开机时计算机先运行“自举装入程序”，通过执行该程序就可找到引导块，并将完整的“自举程序”读入内存，完成初始化&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完整的自举程序放在磁盘的启动块(即引导块/启动分区)上，启动块位于磁盘的固定位置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;拥有启动分区的磁盘称为启动磁盘或系统磁盘（c:盘)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3.坏块的管理&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;坏了、无法正常使用的扇区就是“坏块”。这属于硬件故障，操作系统是无法修复的。应该将坏块标记出来，以免错误地使用到它。&lt;/li&gt;
&lt;li&gt;对于简单的磁盘，可以在逻辑格式化时（建立文件系统时）对整个磁盘进行坏块检查，标明哪些扇区是坏扇区，比如:在FAT表上标明。（在这种方式中，坏块对操作系统不透明）&lt;/li&gt;
&lt;li&gt;对于复杂的磁盘，磁盘控制器（磁盘设备内部的一个硬件部件）会维护一个坏块链表。
在磁盘出厂前进行低级格式化（物理格式化）时就将坏块链进行初始化。
会保留一些“备用扇区”，用于替换坏块。这种方案称为扇区备用。且这种处理方式中，坏块对操作系统透明。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5.I/O管理&lt;/h2&gt;
&lt;h3&gt;1.I/O设备的概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;“I/O” 就是“输入/输出”(Input/Output)&lt;/li&gt;
&lt;li&gt;I/O设备就是可以将数据输入到计算机，或者可以接收计算机输出数据的外部设备，属于计算机中的硬件部件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.I/O控制器&lt;/h3&gt;
&lt;h4&gt;1.含义&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;CPU无法直接控制l/O设备的机械部件，因此I/O设备还要有一个电子部件作为CPU和I/O设备机械部件之间的“中介”，用于实现CPU对设备的控制。&lt;/li&gt;
&lt;li&gt;这个电子部件就是I/O控制器，又称设备控制器。CPU可控制I/o控制器，又由I/O控制器来控制设备的机械部件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2.功能&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-193.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.组成&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-194.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3.I/O控制方式&lt;/h3&gt;
&lt;h4&gt;1.程序直接控制方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;完成一次读/写操作的流程图(以读操作为例)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-195.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;流程图&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-196.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.中断驱动方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;由于程序直接控制方式CPU利用率低，忙等，所以提出了中断驱动方式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-197.png&quot; alt=&quot;alt text&quot; /&gt;
&lt;img src=&quot;image-198.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.DMA方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;虽然中断驱动方式解决了程序直接控制方式的问题，但是每一次只能读/写一个字，导致CPU频繁切换，耗费了很多时间。于是人们又发明了DMA方式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-199.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DMA控制器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-200.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-201.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.通道控制方式&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;通道控制方式是为了解决DMA方式连续存储的问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-202.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-203.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.四种方式总结&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-204.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4.I/O软件层次结构&lt;/h3&gt;
&lt;h4&gt;1.知识总览&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-205.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.用户层软件&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;用户层软件实现了与用户交互的接口，用户可直接使用该层提供的、与I/o操作相关的库函数对设备进行操作。&lt;/li&gt;
&lt;li&gt;用户层软件将用户请求翻译成格式化的I/o请求，并通过“系统调用”请求操作系统内核的服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-206.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.设备独立性软件&lt;/h4&gt;
&lt;p&gt;设备独立性软件，又称设备无关性软件。与设备的硬件特性无关的功能几乎都在这一层实现。&lt;/p&gt;
&lt;p&gt;主要功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;向上层提供统一的调用接口（如read/write系统调用）&lt;/li&gt;
&lt;li&gt;设备的保护。（原理类似与文件保护。设备被看做是一种特殊的文件，不同用户对各个文件的访问权限是不一样的，同理，对设备的访问权限也不一样。）&lt;/li&gt;
&lt;li&gt;差错处理（设备独立性软件需要对一些设备的错误进行处理）&lt;/li&gt;
&lt;li&gt;设备的分配与回收&lt;/li&gt;
&lt;li&gt;数据缓冲区管理（可以通过缓冲技术屏蔽设备之间数据交换单位大小和传输速度的差异）&lt;/li&gt;
&lt;li&gt;建立逻辑设备名到物理设备名的映射关系;根据设备类型选择调用相应的驱动程序&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用户或用户层软件发出I/o操作相关系统调用的系统调用时，需要指明此次要操作的I/o设备的逻辑设备名（eg:去学校打印店打印时，需要选择打印机1/打印机2/打印机3，其实这些都是逻辑设备名）
设备独立性软件需要通过“逻辑设备表（LUT，Logical UnitTable）”来确定逻辑设备对应的物理设备，并找到该设备对应的设备驱动程序。如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-207.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;操作系统系统可以采用两种方式管理逻辑设备表(LUT) :
第一种方式，整个系统只设置一张LUT，这就意味着所有用户不能使用相同的逻辑设备名，因此这种方式只适用于单用户操作系统。&lt;/p&gt;
&lt;p&gt;第二种方式，为每个用户设置一张LUT，各个用户使用的逻辑设备名可以重复，适用于多用户操作系统。系统会在用户登录时为其建立一个用户管理进程，而LUT就存放在用户管理进程的PCB中。&lt;/p&gt;
&lt;h4&gt;4.驱动设备&lt;/h4&gt;
&lt;p&gt;思考:为何不同的设备需要不同的设备驱动程序?&lt;/p&gt;
&lt;p&gt;不同设备的内部硬件特性也不同，这些特性只有厂家才知道，因此厂家须提供与设备相对应的驱动程序，CPU执行驱动程序的指令序列，来完成设置设备寄存器，检查设备状态等工作。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-208.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.中断处理程序&lt;/h4&gt;
&lt;p&gt;当I/o任务完成时，I/o控制器会发送一个中断信号，系统会根据中断信号类型找到相应的中断处理程序并执行。中断处理程序的处理流程如下:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-209.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.总结&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-210.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5.I/O核心子系统&lt;/h3&gt;
&lt;h4&gt;1.知识总览&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-211.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这些功能在哪些层次上实现呢？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-212.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.I/O调度&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;I/O调度:用某种算法确定一个好的顺序来处理各个I/o请求。&lt;/strong&gt;
如:磁盘调度（先来先服务算法、最短寻道优先算法、SCAN算法、C-SCAN算法、LOOK算法、C-LOOK算法）。&lt;/p&gt;
&lt;p&gt;当多个磁盘I/o请求到来时，用某种调度算法确定满足I/o请求的顺序。&lt;/p&gt;
&lt;p&gt;同理，打印机等设备也可以用先来先服务算法、优先级算法、短作业优先等算法来确定IV/o调度顺序。&lt;/p&gt;
&lt;h4&gt;3.设备保护&lt;/h4&gt;
&lt;p&gt;操作系统需要实现文件保护功能，不同的用户对各个文件有不同的访问权限（如:只读、读和写等）。
在UNIx系统中，设备被看做是一种特殊的文件，每个设备也会有对应的FCB。当用户请求访问某个设备时，系统根据FCB中记录的信息来判断该用户是否有相应的访问权限，以此实现“设备保护”的功能。(参考“文件保护”小节)&lt;/p&gt;
&lt;h3&gt;6.假脱机技术（SPOOLing技术）&lt;/h3&gt;
&lt;h4&gt;1.脱机技术&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;手工操作阶段:主机直接从l/o设备获得数据，由于设备速度慢，主机速度很快。人机速度矛盾明显，主机要浪费很多时间来等待设备。因此在批处理阶段引入了脱机输入/输出技术（用磁带完成）.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tips:为什么称为“脱机”？一脱离主机的控制进行的输入/输出操作。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-213.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.假脱机技术&lt;/h4&gt;
&lt;p&gt;“假脱机技术”，又称“SPOOLing 技术”，用软件的方式模拟脱机技术。SPOQLing系统的组成如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“输入井”模拟脱机输入时的磁带，用于收容I/o设备输入的数据&lt;/li&gt;
&lt;li&gt;“输出井”模拟脱机输出时的磁带，用于收容用户进程输出的数据&lt;/li&gt;
&lt;li&gt;“输入进程”模拟脱机输入时的外围控制机&lt;/li&gt;
&lt;li&gt;“输出进程”模拟脱机输出时的外围控制机&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-214.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;要实现SPOOLing 技术，必须要有多道程序技术的支持。系统会建立“输入进程”和“输出进程”。&lt;/p&gt;
&lt;h3&gt;7.设备的分配和回收&lt;/h3&gt;
&lt;h4&gt;1.知识总览&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-215.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.设备分配时考虑的因素&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-216.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;1.固有属性&lt;/h5&gt;
&lt;p&gt;设备的固有属性可分为三种:独占设备、共享设备、虚拟设备。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;独占设备—— 一个时段只能分配给一个进程（如打印机）&lt;/li&gt;
&lt;li&gt;共享设备――可同时分配给多个进程使用（如磁盘），各进程往往是宏观上同时共享使用设备，而微观上交替使用。&lt;/li&gt;
&lt;li&gt;虚拟设备――采用SPOOLing 技术将独占设备改造成虚拟的共享设备，可同时分配给多个进程使用（如采用SPOOLing技术实现的共享打印机）&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;2.分配算法&lt;/h5&gt;
&lt;p&gt;先来先服务，优先级高者优先，短任务优先.......&lt;/p&gt;
&lt;h5&gt;3.安全性&lt;/h5&gt;
&lt;p&gt;从进程运行的安全性上考虑，设备分配有两种方式:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;安全分配方式&lt;/strong&gt;:为进程分配一个设备后就将进程阻塞，本次I/O完成后才将进程唤醒。(eg:考虑进程请求打印机打印输出的例子)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个时段内每个进程只能使用一个设备&lt;/li&gt;
&lt;li&gt;优点:破坏了“请求和保持”条件，不会死锁&lt;/li&gt;
&lt;li&gt;缺点:对于一个进程来说，CPU和I/o设备只能串行工作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;不安全分配方式&lt;/strong&gt;:进程发出I/o请求后，系统为其分配I/O设备，进程可继续执行，之后还可以发出新的I/o请求。只有某个l/o请求得不到满足时才将进程阻塞。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个进程可以同时使用多个设备&lt;/li&gt;
&lt;li&gt;优点:进程的计算任务和I/o任务可以并行处理，使进程迅速推进&lt;/li&gt;
&lt;li&gt;缺点:有可能发生死锁(死锁避免、死锁的检测和解除)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3.静态分配和动态分配&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;静态分配:进程运行前为其分配全部所需资源，运行结束后归还资源。破坏了“请求和保持”条件，不会发生死锁&lt;/li&gt;
&lt;li&gt;动态分配:进程运行过程中动态申请设备资源&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.设备分配中的数据结构&lt;/h4&gt;
&lt;p&gt;“设备、控制器、通道”之间的关系:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-217.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;**设备控制表（DCT）:**系统为每个设备配置一张DCT，用于记录设备情况&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-218.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;**控制器控制表(COCT):**每个设备控制器都会对应一张COCT。操作系统根据COCT的信息对控制器进行操作和管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-219.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;**通道控制表（CHCT）:**每个通道都会对应一张CHCT。操作系统根据CHCT的信息对通道进行操作和管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-220.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;系统设备表(SDT)&lt;/strong&gt;:记录了系统中全部设备的情况，每个设备对应一个表目。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-221.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5.设备分配的步骤&lt;/h4&gt;
&lt;p&gt;①根据进程请求的物理设备名查找SDT（注:物理设备名是进程请求分配设备时提供的参数）&lt;/p&gt;
&lt;p&gt;②根据SDT找到DCT，若设备忙碌则将进程PCB挂到设备等待队列中，不忙碌则将设备分配给进程。&lt;/p&gt;
&lt;p&gt;③根据DCT找到COCT，若控制器忙碌则将进程PCB挂到控制器等待队列中，不忙碌则将控制器分配给进程。&lt;/p&gt;
&lt;p&gt;④根据COCT找到CHCT，若通道忙碌则将进程PCB挂到通道等待队列中，不忙碌则将通道分配给进程。&lt;/p&gt;
&lt;p&gt;注∶只有设备、控制器、通道三者都分配成功时，这次设备分配才算成功，之后便可后动/O设备进行数据传送。&lt;/p&gt;
&lt;p&gt;缺点:
①用户编程时必须使用“物理设备名”，底层细节对用户不透明，不方便编程&lt;/p&gt;
&lt;p&gt;②若换了一个物理设备，则程序无法运行&lt;/p&gt;
&lt;p&gt;③若进程请求的物理设备正在忙碌，则即使系统中还有同类型的设备，进程也必须阻塞等待&lt;/p&gt;
&lt;p&gt;改进方法:建立逻辑设备名与物理设备名的映射机制，用户编程时只需提供逻辑设备名&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-222.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.设备分配步骤的改进&lt;/h4&gt;
&lt;p&gt;①根据进程请求的逻辑设备名查找SDT(注:用户编程时提供的逻辑设备名其实就是“设备类型”)&lt;/p&gt;
&lt;p&gt;②查找SDT，找到用户进程指定类型的、并且空闲的设备，将其分配给该进程。操作系统在逻辑设备表（LUT）中新增一个表项。&lt;/p&gt;
&lt;p&gt;③根据DCT找到cOCT，若控制器忙碌则将进程PCB挂到控制器等待队列中，不忙碌则将控制器分配给进程。&lt;/p&gt;
&lt;p&gt;④根据COCT找到CHCT，若通道忙碌则将进程PCB挂到通道等待队列中，不忙碌则将通道分配给进程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-223.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;逻辑设备表&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-224.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;8.缓冲区管理&lt;/h3&gt;
&lt;h4&gt;1.知识总览&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;image-225.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2.含义和作用&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;含义&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;缓冲区是一个存储区域，可以由专门的硬件寄存器组成，也可利用内存作为缓冲区。&lt;/p&gt;
&lt;p&gt;使用硬件作为缓冲区的成本较高，容量也较小，一般仅用在对速度要求非常高的场合（如存储器管理中所用的联想寄存器，由于对页表的访问频率极高，因此使用速度很快的联想寄存器来存放页表项的副本）&lt;/p&gt;
&lt;p&gt;一般情况下，更多的是利用内存作为缓冲区，“设备独立性软件”的缓冲区管理就是要组织管理好这些缓冲区&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;作用&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;image-226.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3.单缓冲&lt;/h4&gt;
&lt;p&gt;假设某用户进程请求某种块设备读入若干块的数据。若采用单缓冲的策略，操作系统会在主存中为其分配一个缓冲区（若题目中没有特别说明，一个缓冲区的大小就是一个块）。如图所示
&lt;strong&gt;注意:当缓冲区数据非空时，不能往缓冲区冲入数据，只能从缓冲区把数据传出;当缓冲区为空时，可以往缓冲区冲入数据，但必须把缓冲区充满以后，才能从缓冲区把数据传出。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-227.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;处理一块数据的平均时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-228.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4.双缓冲&lt;/h4&gt;
&lt;p&gt;假设某用户进程请求某种块设备读入若干块的数据。若采用双缓冲的策略，操作系统会在主存中为其分配两个缓冲区（若题目中没有特别说明，一个缓冲区的大小就是一个块）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设T&amp;gt;C+M&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-229.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设T&amp;lt;C+M&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;image-230.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结论:采用双缓冲策略，处理一个数据块的平均耗时为Max (T,C+M)&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;5.循环缓冲区&lt;/h4&gt;
&lt;p&gt;将多个大小相等的缓冲区链接成一个循环队列。
注:以下图示中，橙色表示已充满数据的缓冲区，绿色表示空缓冲区。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-231.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6.缓冲池&lt;/h4&gt;
&lt;p&gt;缓冲池由系统中共用的缓冲区组成。&lt;/p&gt;
&lt;p&gt;这些缓冲区按使用状况可以分为:空缓冲队列、装满输入数据的缓冲队列(输入队列)、装满输出数据的缓冲队列（输出队列）。&lt;/p&gt;
&lt;p&gt;另外，根据一个缓冲区在实际运算中扮演的功能不同，又设置了四种工作缓冲区:用于收容输入数据的工作缓冲区(hin)、用于提取输入数据的工作缓冲区（sin）、用于收容输出数据的工作缓冲区（hout） 、用于提取输出数据的工作缓冲区(sout)。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-232.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>深入理解操作系统 第10章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-10/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-10/</guid><description>信号量和管程</description><pubDate>Mon, 30 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第十章的主要内容是：信号量和管程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;信号量&lt;/h2&gt;
&lt;p&gt;信号量的抽象数据类型&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个整形(sem),具有两个原子操作&lt;/li&gt;
&lt;li&gt;P(): sem减一,如果sem&amp;lt;0,等待,否则继续&lt;/li&gt;
&lt;li&gt;V(): sem加一,如果sem≤0,唤醒一个等待的P&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;信号量是整数&lt;/p&gt;
&lt;p&gt;信号量是被保护的变量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化完成后,唯一改变一个信号量的值的办法是通过P()和V()&lt;/li&gt;
&lt;li&gt;操作必须是原子&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;P()能够阻塞,V()不会阻塞&lt;/p&gt;
&lt;p&gt;我们假定信号量是公平的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;没有线程被阻塞在P()仍然堵塞如果V()被无限频繁调用(在同一个信号量)&lt;/li&gt;
&lt;li&gt;在实践中,FIFO经常被使用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两个类型信号量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;二进制信号量: 可以是0或1&lt;/li&gt;
&lt;li&gt;计数信号量: 可以取任何非负数&lt;/li&gt;
&lt;li&gt;两者相互表现(给定一个可以实现另一个)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;信号量可以用在2个方面&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥&lt;/li&gt;
&lt;li&gt;条件同步(调度约束——一个线程等待另一个线程的事情发生)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;信号量使用&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;用二进制信号量实现的互斥&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mutex = new Semaphore(1);

mutex-&amp;gt;P();
...
mutex-&amp;gt;V();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用二进制信号量实现的调度约束&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;condition = new Semaphore(0);

//Thread A
...
condition-&amp;gt;P(); //等待线程B某一些指令完成之后再继续运行,在此阻塞
...

//Thread B
...
condition-&amp;gt;V(); //信号量增加唤醒线程A
...
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个线程等待另一个线程处理事情&lt;/p&gt;
&lt;p&gt;比如生产东西或消费东西(生产者消费者模式),互斥(锁机制)是不够的&lt;/p&gt;
&lt;p&gt;有界缓冲区的生产者-消费者问题&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个或者多个生产者产生数据将数据放在一个缓冲区里&lt;/li&gt;
&lt;li&gt;单个消费者每次从缓冲区取出数据&lt;/li&gt;
&lt;li&gt;在任何一个时间只有一个生产者或消费者可以访问该缓冲区&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正确性要求&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在任何一个时间只能有一个线程操作缓冲区(互斥)&lt;/li&gt;
&lt;li&gt;当缓冲区为空时,消费者必须等待生产者(调度,同步约束)&lt;/li&gt;
&lt;li&gt;当缓存区满,生产者必须等待消费者(调度,同步约束)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个约束用一个单独的信号量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;二进制信号量互斥&lt;/li&gt;
&lt;li&gt;一般信号量 fullBuffers&lt;/li&gt;
&lt;li&gt;一般信号了 emptyBuffers&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class BoundedBuffer{
		mutex = new Semaphore(1);
		fullBuffers = new Semaphore(0);   //说明缓冲区初始为空
 		emptyBuffers = new Semaphore(n);  //同时可以有n个生产者来生产
};

BoundedBuffer::Deposit(c){
		emptyBuffers-&amp;gt;P();
		mutex-&amp;gt;P();
		Add c to the buffer;
		mutex-&amp;gt;V();
		fullBuffers-&amp;gt;V();
}

BoundedBuffer::Remove(c){
		fullBuffers-&amp;gt;P();
		mutex-&amp;gt;P();
		Remove c from buffer;
		mutex-&amp;gt;V();
		emptyBuffers-&amp;gt;V();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;信号量实现&lt;/h2&gt;
&lt;p&gt;使用硬件原语&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;禁用中断&lt;/li&gt;
&lt;li&gt;原子指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;类似锁&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;禁用中断&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Semaphore{
		int sem;
		WaitQueue q;
};

Semaphore::P(){
		--sem;
		if(sem &amp;lt; 0){
				Add this thread t to q;
				block(p);
		}
};

Semaphore::V(){
		++sem;
		if(sem &amp;lt;= 0){
				Remove a thread t from q;
				wakeup(t);
		}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;信号量的双用途&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥和条件同步&lt;/li&gt;
&lt;li&gt;但等待条件是独立的互斥&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;读,开发代码比较困难&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序员必须非常精通信号量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;容易出错&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用的信号量已经被另一个线程占用&lt;/li&gt;
&lt;li&gt;忘记释放信号量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不能够处理死锁问题&lt;/p&gt;
&lt;h2&gt;管程&lt;/h2&gt;
&lt;p&gt;目的: 分离互斥和条件同步的关注&lt;/p&gt;
&lt;p&gt;什么是管程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个锁: 指定临界区&lt;/li&gt;
&lt;li&gt;0或者多个条件变量: 等待,通知信号量用于管程并发访问共享数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一般方法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;收集在对象,模块中的相关共享数据&lt;/li&gt;
&lt;li&gt;定义方法来访问共享数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lock&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lock::Acquire() 等待直到锁可用,然后抢占锁&lt;/li&gt;
&lt;li&gt;Lock::Release() 释放锁,唤醒等待者如果有&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Condition Variable&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;允许等待状态进入临界区
&lt;ul&gt;
&lt;li&gt;允许处于等待(睡眠)的线程进入临界区&lt;/li&gt;
&lt;li&gt;某个时刻原子释放锁进入睡眠&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Wait() operation
&lt;ul&gt;
&lt;li&gt;释放锁,睡眠,重新获得锁放回&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Signal() operation(or broadcast() operation)
&lt;ul&gt;
&lt;li&gt;唤醒等待者(或者所有等待者),如果有&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要维持每个条件队列&lt;/li&gt;
&lt;li&gt;线程等待的条件等待signal()&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Condition{
		int numWaiting = 0;
		WaitQueue q;
};

Condition::Wait(lock){
		numWaiting++;
		Add this thread t to q;
		release(lock);
		schedule(); //need mutex
		require(lock);
}

Condition::Signal(){
		if(numWaiting &amp;gt; 0){
				Remove a thread t from q;
				wakeup(t); //need mutex
				numWaiting--;
		}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;管程解决生产者-消费者问题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BoundedBuffer{
		Lock lock;
		int count = 0;  //buffer 为空
		Condition notFull, notEmpty;
};

BoundedBuffer::Deposit(c){
		lock-&amp;gt;Acquire();    //管程的定义:只有一个线程能够进入管程
		while(count == n)
				notFull.Wait(&amp;amp;lock); //释放前面的锁
		Add c to the buffer;
		count++;
		notEmpty.Signal();
		lock-&amp;gt;Release();
}

BoundedBuffer::Remove(c){
		lock-&amp;gt;Acquire();
		while(count == 0)
				notEmpty.Wait(&amp;amp;lock);
		Remove c from buffer;
		count--;
		notFull.Signal();
		lock-&amp;gt;Release();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开发,调试并行程序很难&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非确定性的交叉指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同步结构&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;锁: 互斥&lt;/li&gt;
&lt;li&gt;条件变量: 有条件的同步&lt;/li&gt;
&lt;li&gt;其他原语: 信号量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;怎么样有效地使用这些结构&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;制定并遵循严格的程序设计风格,策略&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;经典同步问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;读者-写者问题&lt;/p&gt;
&lt;p&gt;动机: 共享数据的访问&lt;/p&gt;
&lt;p&gt;两种类型的使用者: 读者(不修改数据) 写者(读取和修改数据)&lt;/p&gt;
&lt;p&gt;问题的约束:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;允许同一时间有多个读者,但在任何时候只有一个写者&lt;/li&gt;
&lt;li&gt;当没有写者时,读者才能访问数据&lt;/li&gt;
&lt;li&gt;当没有读者和写者时,写者才能访问数据&lt;/li&gt;
&lt;li&gt;在任何时候只能有一个线程可以操作共享变量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多个并发进程的数据集共享&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;读者: 只读数据集;他们不执行任何更新&lt;/li&gt;
&lt;li&gt;写者: 可以读取和写入&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;共享数据&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据集&lt;/li&gt;
&lt;li&gt;信号量CountMutex初始化为1&lt;/li&gt;
&lt;li&gt;信号量WriteMutex初始化为1&lt;/li&gt;
&lt;li&gt;整数Rcount初始化为0(当前读者个数)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;读者优先设计&lt;/p&gt;
&lt;p&gt;只要有一个读者处于活动状态, 后来的读者都会被接纳.如果读者源源不断的出现,那么写者使用处于阻塞状态.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//信号量实现
//writer
sem_wait(WriteMutex);
write;
sem_post(WriteMutex);

//reader
sem_wait(CountMutex);
if(Rcount == 0)
		sem_wait(WriteMutex); //确保后续不会有写者进入
++Rcount;
read;
--Rcount;
if(Rcount == 0)
		sem_post(WriteMutex); //全部读者全部离开才能唤醒写者
sem_post(CountMutex);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写者优先设计&lt;/p&gt;
&lt;p&gt;一旦写者就绪,那么写者会尽可能的执行写操作.如果写者源源不断的出现的话,那么读者就始终处于阻塞状态.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//writer
Database::Write(){
		Wait until readers/writers;
		write database;
		check out - wake up waiting readers/writers;
}
//reader
Database::Read(){
		Wait until no writers;
		read database;
		check out - wake up waiting writers;
}

//管程实现
AR = 0; // # of active readers
AW = 0; // # of active writers
WR = 0; // # of waiting readers
WW = 0; // # of waiting writers
Condition okToRead;
Condition okToWrite;
Lock lock;
//writer
Public Database::Write(){
		//Wait until no readers/writers;
		StartWrite();
		write database;
		//check out - wake up waiting readers/writers;
		DoneWrite();
}

Private Database::StartWrite(){
		lock.Acquire();
		while((AW + AR) &amp;gt; 0){
				WW++;
				okToWrite.wait(&amp;amp;lock);
				WW--;		
		}
		AW++;
		lock.Release();
}

Private Database::DoneWrite(){
		lock.Acquire();
		AW--;
		if(WW &amp;gt; 0){
				okToWrite.signal();
		}
		else if(WR &amp;gt; 0){
				okToRead.broadcast(); //唤醒所有reader 
		}
		lock.Release();
}

//reader
Public Database::Read(){
		//Wait until no writers;
		StartRead();
		read database;
		//check out - wake up waiting writers;
		DoneRead();
}

Private Database::StartRead(){
		lock.Acquire();
		while(AW + WW &amp;gt; 0){    //关注等待的writer,体现出写者优先
				WR++;
				okToRead.wait(&amp;amp;lock);
				WR--;
		}
		AR++;
		lock.Release();
}

private Database::DoneRead(){
		lock.Acquire();
		AR--;
		if(AR == 0 &amp;amp;&amp;amp; WW &amp;gt; 0){  //只有读者全部没有了,才需要唤醒
				okToWrite.signal();
		}
		lock.Release();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;哲学家就餐问题(学习自 &lt;a href=&quot;http://github.com/cyc2018&quot;&gt;github.com/cyc2018&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;共享数据:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bowl of rice(data set)&lt;/li&gt;
&lt;li&gt;Semaphone fork [5] initialized to 1&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;#define N 5
#define LEFT (i + N - 1) % N // 左邻居
#define RIGHT (i + 1) % N    // 右邻居
#define THINKING 0
#define HUNGRY   1
#define EATING   2
typedef int semaphore;
int state[N];                // 跟踪每个哲学家的状态
semaphore mutex = 1;         // 临界区的互斥，临界区是 state 数组，对其修改需要互斥
semaphore s[N];              // 每个哲学家一个信号量

void philosopher(int i) {
    while(TRUE) {
        think(i);
        take_two(i);
        eat(i);
        put_two(i);
    }
}

void take_two(int i) {
    down(&amp;amp;mutex);
    state[i] = HUNGRY;
    check(i);
    up(&amp;amp;mutex);
    down(&amp;amp;s[i]); // 只有收到通知之后才可以开始吃，否则会一直等下去
}

void put_two(i) {
    down(&amp;amp;mutex);
    state[i] = THINKING;
    check(LEFT); // 尝试通知左右邻居，自己吃完了，你们可以开始吃了
    check(RIGHT);
    up(&amp;amp;mutex);
}

void eat(int i) {
    down(&amp;amp;mutex);
    state[i] = EATING;
    up(&amp;amp;mutex);
}

// 检查两个邻居是否都没有用餐，如果是的话，就 up(&amp;amp;s[i])，使得 down(&amp;amp;s[i]) 能够得到通知并继续执行
void check(i) {         
    if(state[i] == HUNGRY &amp;amp;&amp;amp; state[LEFT] != EATING &amp;amp;&amp;amp; state[RIGHT] !=EATING) {
        state[i] = EATING;
        up(&amp;amp;s[i]);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>博客添加友链功能指南</title><link>https://github.com/posts/friends/</link><guid isPermaLink="true">https://github.com/posts/friends/</guid><description>博客站新增友链！欢迎交换友链~</description><pubDate>Sun, 29 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Astro博客添加友链：从0到1全流程&lt;/h1&gt;
&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;在个人博客中，&lt;strong&gt;友链（Friends）&lt;/strong&gt; 是连接不同创作者、互相交流的重要功能。
本篇文章将带你&lt;strong&gt;一步一步&lt;/strong&gt;在 Astro 博客中完整实现友链功能，包括：页面创建、数据配置、类型校验、国际化、导航栏接入、类型声明修复，全程可直接复用，无冗余代码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：基于 Astro 构建的静态博客，已拥有布局、国际化、导航配置体系。
&lt;strong&gt;最终效果&lt;/strong&gt;：独立友链页面 + 响应式卡片 + 数据化配置 + 多语言支持 + 完整 MD 前言渲染。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、整体思路&lt;/h2&gt;
&lt;p&gt;我们采用 &lt;strong&gt;数据与页面分离&lt;/strong&gt; 的方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;页面&lt;/strong&gt;：负责展示样式与布局&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据&lt;/strong&gt;：放在 Markdown 中统一管理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型&lt;/strong&gt;：使用 Astro Content Collection 做类型安全&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;导航&lt;/strong&gt;：接入现有导航系统&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;国际化&lt;/strong&gt;：支持多语言切换
这样后续&lt;strong&gt;增删友链只需要改一个 MD 文件&lt;/strong&gt;，完全不用动页面代码。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;二、最终文件结构&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;src/
├── pages/
│   └── friends.astro         # 友链页面（自动路由）
├── content/
│   ├── spec/
│   │   └── friends.md        # 友链数据配置
│   └── config.ts             # 集合类型定义
├── types/
│   └── config.ts             # 导航路由枚举
├── constants/
│   └── link-presets.ts       # 导航路由映射
├── i18n/
│   ├── i18nKey.ts            # 多语言键名
│   └── languages/
│       ├── en.ts
│       └── zh_CN.ts…
└── env.d.ts                  # 类型声明修复文件
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;三、正式开始搭建&lt;/h2&gt;
&lt;h3&gt;1. 创建友链页面&lt;/h3&gt;
&lt;p&gt;文件路径：&lt;code&gt;src/pages/friends.astro&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这是友链的&lt;strong&gt;展示页面&lt;/strong&gt;，使用你博客现有的布局 &lt;code&gt;MainGridLayout.astro&lt;/code&gt;，&lt;strong&gt;无需修改布局文件&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
// src/pages/friends.astro
import { getEntry, render } from &quot;astro:content&quot;;
import Markdown from &quot;@components/misc/Markdown.astro&quot;;
import I18nKey from &quot;../i18n/i18nKey&quot;;
import { i18n } from &quot;../i18n/translation&quot;;
import MainGridLayout from &quot;../layouts/MainGridLayout.astro&quot;;

// 从 content/spec/friends.md 读取友链数据
const friendEntry = await getEntry(&quot;spec&quot;, &quot;friends&quot;);

// 错误处理
if (!friendEntry) {
  throw new Error(&quot;Friends content not found&quot;);
}

// 解析 MD 内容与数据
const { Content } = await render(friendEntry);
const { friends = [], myInfo } = friendEntry.data;
---

&amp;lt;MainGridLayout title={i18n(I18nKey.friends)}&amp;gt;
  &amp;lt;div class=&quot;flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32&quot;&amp;gt;
    &amp;lt;div class=&quot;card-base z-10 px-9 py-6 relative w-full&quot;&amp;gt;

      &amp;lt;div class=&quot;mb-6 text-90&quot;&amp;gt;
        &amp;lt;Markdown&amp;gt;
          &amp;lt;Content /&amp;gt;
        &amp;lt;/Markdown&amp;gt;
      &amp;lt;/div&amp;gt;

      {myInfo &amp;amp;&amp;amp; (
        &amp;lt;div class=&quot;bg-[var(--btn-plain-bg-hover)] p-5 rounded-xl border-l-4 border-[var(--primary)] mb-10&quot;&amp;gt;
          &amp;lt;p class=&quot;font-bold mb-2 text-90 flex items-center&quot;&amp;gt;
            &amp;lt;span class=&quot;w-2 h-2 rounded-full bg-[var(--primary)] mr-2&quot;&amp;gt;&amp;lt;/span&amp;gt;
            本站信息
          &amp;lt;/p&amp;gt;
          &amp;lt;ul class=&quot;space-y-1 text-75 text-sm &quot;&amp;gt;
            &amp;lt;li&amp;gt;名称：{myInfo.name}&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;介绍：{myInfo.introduction}&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;链接：&amp;lt;code class=&quot;bg-black/5 dark:bg-white/10 px-1 rounded&quot;&amp;gt;{myInfo.link}&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;头像：&amp;lt;code class=&quot;bg-black/5 dark:bg-white/10 px-1 rounded text-xs&quot;&amp;gt;{myInfo.avatar}&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;
          &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
      )}

      &amp;lt;div class=&quot;h-[1px] w-full bg-black/5 dark:bg-white/10 my-8&quot;&amp;gt;&amp;lt;/div&amp;gt;

      &amp;lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-4&quot;&amp;gt;
        {friends.map((friend) =&amp;gt; (
          &amp;lt;a
            href={friend.link}
            target=&quot;_blank&quot;
            rel=&quot;noopener noreferrer&quot;
            class=&quot;flex items-center p-4 rounded-xl hover:bg-[var(--btn-plain-bg-hover)] transition-all group active:scale-95 border border-transparent hover:border-[var(--primary)]/20 shadow-sm&quot;
          &amp;gt;
            &amp;lt;img
              src={friend.avatar}
              alt={friend.name}
              class=&quot;w-16 h-16 rounded-full border-2 border-[var(--primary)] shadow-sm mr-4 shrink-0 transition-transform group-hover:rotate-12 object-cover&quot;
            /&amp;gt;
            &amp;lt;div class=&quot;overflow-hidden&quot;&amp;gt;
              &amp;lt;div class=&quot;font-bold text-lg text-90 transition-all group-hover:text-[var(--primary)] truncate&quot;&amp;gt;
                {friend.name}
              &amp;lt;/div&amp;gt;
              &amp;lt;div class=&quot;text-sm text-50 line-clamp-1 italic&quot;&amp;gt;
                {friend.introduction}
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/a&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;

    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/MainGridLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 配置友链数据（核心）&lt;/h3&gt;
&lt;p&gt;文件路径：&lt;code&gt;src/content/spec/friends.md&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;所有友链信息&lt;strong&gt;只在这里修改&lt;/strong&gt;，格式严格遵循 YAML。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
# 本站信息（别人添加你的友链时使用）
myInfo:
  name: 旅行家说
  introduction: 去走上一座山吧，走在夕阳下
  link: https://blogs.papership.top
  avatar: https://blogs.papership.top/favicon/avatar.png

# 友链列表
friends:
  - name: 友人A
    introduction: 热爱技术与生活
    link: https://aaa.com
    avatar: https://aaa.com/avatar.png
---

# 友链说明
欢迎各位朋友交换友链～
请使用上方本站信息进行申请，我会尽快通过！
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 配置 Content Collection 类型&lt;/h3&gt;
&lt;p&gt;文件路径：&lt;code&gt;src/content/config.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;作用：让 Astro 识别友链数据结构，避免格式错误。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { defineCollection, z } from &quot;astro:content&quot;;

// 文章集合（原有代码）
const postsCollection = defineCollection({
  schema: z.object({
    title: z.string(),
    published: z.date(),
    updated: z.date().optional(),
    draft: z.boolean().optional().default(false),
    description: z.string().optional().default(&quot;&quot;),
    image: z.string().optional().default(&quot;&quot;),
    tags: z.array(z.string()).optional().default([]),
    category: z.string().optional().nullable().default(&quot;&quot;),
    lang: z.string().optional().default(&quot;&quot;),
    prevTitle: z.string().default(&quot;&quot;),
    prevSlug: z.string().default(&quot;&quot;),
    nextTitle: z.string().default(&quot;&quot;),
    nextSlug: z.string().default(&quot;&quot;),
  }),
});

// 新增：友链数据类型定义
const specCollection = defineCollection({
  schema: z.object({
    myInfo: z.object({
      name: z.string(),
      introduction: z.string(),
      link: z.string(),
      avatar: z.string(),
    }),
    friends: z.array(
      z.object({
        name: z.string(),
        introduction: z.string(),
        link: z.string(),
        avatar: z.string(),
      })
    ),
  }),
});

export const collections = {
  posts: postsCollection,
  spec: specCollection,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 修复 Markdown 组件 IDE 报红&lt;/h3&gt;
&lt;p&gt;文件路径：&lt;code&gt;src/env.d.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键步骤&lt;/strong&gt;：解决 &lt;code&gt;Markdown&lt;/code&gt; 组件导入报红问题，不影响运行，仅优化开发体验。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// &amp;lt;reference types=&quot;astro/client&quot; /&amp;gt;

// 声明 Markdown 组件类型，消除 IDE 报错
declare module &apos;@components/misc/Markdown.astro&apos; {
  import type { ComponentType } from &apos;astro&apos;;
  const Markdown: ComponentType;
  export default Markdown;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 配置导航栏路由&lt;/h3&gt;
&lt;h4&gt;5.1 路由枚举&lt;/h4&gt;
&lt;p&gt;文件路径：&lt;code&gt;src/types/config.ts&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export enum LinkPreset {
  Home = 0,
  Archive = 1,
  About = 2,
  Friends = 3, // 新增友链
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5.2 路由映射&lt;/h4&gt;
&lt;p&gt;文件路径：&lt;code&gt;src/constants/link-presets.ts&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { i18n } from &quot;../i18n/translation&quot;;
import I18nKey from &quot;../i18n/i18nKey&quot;;
import { LinkPreset, type NavBarLink } from &quot;../types/config&quot;;

export const LinkPresets: { [key in LinkPreset]: NavBarLink } = {
  [LinkPreset.Home]: { name: i18n(I18nKey.home), url: &quot;/&quot; },
  [LinkPreset.Archive]: { name: i18n(I18nKey.archive), url: &quot;/archive/&quot; },
  [LinkPreset.About]: { name: i18n(I18nKey.about), url: &quot;/about/&quot; },
  [LinkPreset.Friends]: { name: i18n(I18nKey.friends), url: &quot;/friends/&quot; }, // 新增
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. 国际化配置（多语言）&lt;/h3&gt;
&lt;h4&gt;6.1 新增语言键&lt;/h4&gt;
&lt;p&gt;文件路径：&lt;code&gt;src/i18n/i18nKey.ts&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export enum I18nKey {
  home = &quot;home&quot;,
  archive = &quot;archive&quot;,
  about = &quot;about&quot;,
  friends = &quot;friends&quot;, // 新增
  // ...其他键
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.2 添加语言文本&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;英文&lt;/strong&gt;：&lt;code&gt;src/i18n/languages/en.ts&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  // ...
  [I18nKey.friends]: &quot;Friends&quot;,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;简体中文&lt;/strong&gt;：&lt;code&gt;src/i18n/languages/zh_CN.ts&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  // ...
  [I18nKey.friends]: &quot;友链&quot;,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其他语言可按相同格式补充。&lt;/p&gt;
&lt;h2&gt;四、启动查看&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问地址：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost:4321/friends
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ 能正常访问即配置成功！&lt;/p&gt;
&lt;h2&gt;五、常见问题排查&lt;/h2&gt;
&lt;h3&gt;1. 页面 404&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;检查文件是否在 &lt;code&gt;src/pages/friends.astro&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;重启开发服务器&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 数据读取失败&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;检查 &lt;code&gt;friends.md&lt;/code&gt; 缩进是否正确（YAML 严格缩进）&lt;/li&gt;
&lt;li&gt;检查 &lt;code&gt;content/config.ts&lt;/code&gt; 是否配置 &lt;code&gt;spec&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 导航栏不显示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;检查 &lt;code&gt;LinkPreset&lt;/code&gt; 枚举是否添加&lt;/li&gt;
&lt;li&gt;检查 &lt;code&gt;link-presets.ts&lt;/code&gt; 是否映射&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. Markdown 组件报红&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;已在 &lt;code&gt;env.d.ts&lt;/code&gt; 中添加类型声明，重启 IDE 即可解决&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. MD 前言标题不显示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;无需额外配置，原有 &lt;code&gt;Markdown&lt;/code&gt; 组件会自动渲染标题与样式&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;六、后记&lt;/h2&gt;
&lt;p&gt;整个友链功能遵循 Astro 官方最佳实践，结构清晰、易于扩展。
后续你只需要在 &lt;code&gt;friends.md&lt;/code&gt; 中添加友链信息，即可自动展示在页面上，非常适合长期维护。&lt;/p&gt;
&lt;p&gt;如果你也在使用 Astro 搭建博客，希望这篇文章能帮到你！&lt;/p&gt;
</content:encoded></item><item><title>深入理解操作系统 第9章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-9/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-9/</guid><description>同步</description><pubDate>Sun, 29 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第九章的主要内容是：同步&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;第一章到第八章内容, 到目前为止&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多道程序设计: 现代操作系统的重要特性&lt;/li&gt;
&lt;li&gt;并行很有用(为什么?) 提示: 多个并发实体: CPU IO 用户 等&lt;/li&gt;
&lt;li&gt;进程,线程: 操作系统抽象出来用于支持多道程序设计&lt;/li&gt;
&lt;li&gt;CPU调度: 实现多道程序设计的机制&lt;/li&gt;
&lt;li&gt;调度算法: 不同的策略&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;独立的线程:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不和其他线程共享资源或状态&lt;/li&gt;
&lt;li&gt;确定性: 输入状态决定结果&lt;/li&gt;
&lt;li&gt;可重现: 能够重现起始条件, IO&lt;/li&gt;
&lt;li&gt;调度顺序不重要&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;合作线程:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在多个线程中共享状态&lt;/li&gt;
&lt;li&gt;不确定性&lt;/li&gt;
&lt;li&gt;不可重现&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不确定性和不可重现意味着bug可能是间歇性发生的&lt;/p&gt;
&lt;p&gt;进程,线程;计算机,设备需要合作&lt;/p&gt;
&lt;p&gt;合作优点:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;共享资源
&lt;ul&gt;
&lt;li&gt;一台电脑,多个用户&lt;/li&gt;
&lt;li&gt;一个银行存款余额,多台ATM机&lt;/li&gt;
&lt;li&gt;嵌入式系统&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;加速
&lt;ul&gt;
&lt;li&gt;IO操作和计算可以重叠&lt;/li&gt;
&lt;li&gt;多处理器&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模块化
&lt;ul&gt;
&lt;li&gt;将大程序分解成小程序 gcc会调用cpp,cc1,cc2,as,ld&lt;/li&gt;
&lt;li&gt;使系统易于扩展&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;程序可以调用函数fork()来创建一个新的进程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作系统需要分配一个新的并且唯一的进程ID&lt;/li&gt;
&lt;li&gt;因此在内核中,这个系统调用会运行 new_pid = next_pid++;&lt;/li&gt;
&lt;li&gt;翻译成机器指令:
&lt;ul&gt;
&lt;li&gt;Load next_pid Reg1&lt;/li&gt;
&lt;li&gt;STORE Reg1 new_pid&lt;/li&gt;
&lt;li&gt;INC Reg1&lt;/li&gt;
&lt;li&gt;STORE Reg1 next_pid&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;假设两个进程并发执行&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果next_pid等于100, 那么其中一个进程得到的ID应该是100, 另一个进程的ID应该是101, next_pid应该增加到102&lt;/li&gt;
&lt;li&gt;可能在INC前进行了上下文切换, 最终导致两个进程的pid都是100,而next_pid也是101&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;无论多个线程的指令序列怎样交替执行,程序都必须正常工作&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多线程程序具有不确定性和不可重现的特点&lt;/li&gt;
&lt;li&gt;不经过专门设计,调试难度很高&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不确定性要求并行程序的正确性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先思考清楚问题,把程序的行为设计清楚&lt;/li&gt;
&lt;li&gt;切忌给予着手编写代码,碰到问题再调试&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;一些概念&lt;/h2&gt;
&lt;p&gt;前面的现象称为Race Condition(竞态条件)&lt;/p&gt;
&lt;p&gt;系统缺陷: 结果依赖于并发执行或者时间的顺序,时间&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不确定性&lt;/li&gt;
&lt;li&gt;不可重现&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;怎么样避免竞态?&lt;/p&gt;
&lt;p&gt;Atomic Operator(原子操作)&lt;/p&gt;
&lt;p&gt;原子操作是指一次不存在任何终端或者失败的执行&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;该执行成功结束&lt;/li&gt;
&lt;li&gt;或者根本没有执行&lt;/li&gt;
&lt;li&gt;并且不应发生任何部分执行的状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实际上操作往往不是原子的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有些看上去是原子操作,实际上不是&lt;/li&gt;
&lt;li&gt;连x++这样的简单语句,实际上是由三条指令构成的&lt;/li&gt;
&lt;li&gt;有时候甚至连单条假期指令都不是原子的(Pipeline,super-scalar,out-of-order,pape fault)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;临界区(Critical section)是指进程中的一段需要访问共享资源并且当另一个进程处于相应代码区域时便不会被执行的代码区域&lt;/p&gt;
&lt;p&gt;互斥(Mutual exclusion)是指当一个 进程处于临界区并访问共享资源时,没有其他进程会处于临界区并且访问任何相同的共享资源&lt;/p&gt;
&lt;p&gt;死锁(Dead lock)是指两个或以上进程,在相互等待完成特定任务,而最终没法将自身任务进行下去&lt;/p&gt;
&lt;p&gt;饥饿(Starvation)是指一个可执行的进程,被调度器持续忽略,以至于虽然处于可执行状态却不被执行&lt;/p&gt;
&lt;h2&gt;临界区&lt;/h2&gt;
&lt;p&gt;互斥: 同一时间临界区中最多存在一个线程&lt;/p&gt;
&lt;p&gt;Progress: 如果一个线程想要进入临界区,那么它最终会成功&lt;/p&gt;
&lt;p&gt;有限等待: 如果一个线程i处于入口区,那么在i的请求被接受之前,其他线程进入临界区的时间是有限制的&lt;/p&gt;
&lt;p&gt;无忙等待(可选): 如果一个进程在等待进入临界区,那么在它可以进入之前会被挂起&lt;/p&gt;
&lt;h2&gt;方法1:禁用硬件中断&lt;/h2&gt;
&lt;p&gt;没有中断,没有上下文切换,因此没有并发&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;硬件将中断处理延迟到中断被启用之后&lt;/li&gt;
&lt;li&gt;大多数现代计算机体系结构都提供指令来完成&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进入临界区&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;禁用中断&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;离开临界区&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开启中断&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一旦中断被禁用,线程就无法被停止&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;整个系统都会为你停下来&lt;/li&gt;
&lt;li&gt;可能导致其他线程处于饥饿状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要是临界区可以任意长怎么办?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;无法限制响应中断所需的时间(可能存在硬件影响)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要小心使用,适合于较小的操作&lt;/p&gt;
&lt;h2&gt;方法2:基于软件的解决方案&lt;/h2&gt;
&lt;p&gt;满足进程Pi和Pj之间互斥的经典的基于软件的解决方法(1981年)&lt;/p&gt;
&lt;p&gt;使用两个共享数据项&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;int turn; //指示该谁进入临界区&lt;/li&gt;
&lt;li&gt;bool flag[]; //指示进程是否准备好进入临界区&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进入临界区:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flag[i] = true;
turn = j;
while(flag[j] &amp;amp;&amp;amp; turn == j);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;退出临界区:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flag[i] = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实例:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;do{
	flag[i] = true;
	turn = j;
	while(flag[j] &amp;amp;&amp;amp; turn == j);
	CRITICAL SECTION
	flag[i] = false;
	REMAINDER SECTION
}while(true);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bakery 算法(N个进程的临界区)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进入临界区之前,进程接收一个数字&lt;/li&gt;
&lt;li&gt;得到的数字最小的进入临界区&lt;/li&gt;
&lt;li&gt;如果进程Pi和Pj收到相同的数字,那么如果i&amp;lt;j,Pi先进入临界区,否则Pj先进入临界区&lt;/li&gt;
&lt;li&gt;编号方案总是按照枚举的增加顺序生成数字&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dekker算法(1965): 第一个针对双线程例子的正确解决方案&lt;/p&gt;
&lt;p&gt;Bakery算法(1979): 针对n线程的临界区问题解决方案&lt;/p&gt;
&lt;p&gt;复杂: 需要两个进程的共享数据项&lt;/p&gt;
&lt;p&gt;需要忙等待: 浪费CPU时间&lt;/p&gt;
&lt;p&gt;没有硬件保证的情况下无真正的软件解决方案: Perterson算法需要原子的LOAD和STORE指令&lt;/p&gt;
&lt;h2&gt;方法3:更高级的抽象&lt;/h2&gt;
&lt;p&gt;硬件提供了一些原语&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;像中断禁用, 原子操作指令等&lt;/li&gt;
&lt;li&gt;大多数现代体系结构都这样&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;操作系统提供更高级的编程抽象来简化并行编程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例如,锁,信号量&lt;/li&gt;
&lt;li&gt;从硬件原语中构建&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;锁是一个抽象的数据结构&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个二进制状态(锁定,解锁),两种方法&lt;/li&gt;
&lt;li&gt;Lock::Acquire() 锁被释放前一直等待,然后得到锁&lt;/li&gt;
&lt;li&gt;Lock::Release() 锁释放,唤醒任何等待的进程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用锁来编写临界区&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;前面的例子变得简单起来:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lock_next_pid-&amp;gt;Acquire();
new_pid = next_pid++;
lock_next_pid-&amp;gt;Release();
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大多数现代体系结构都提供特殊的原子操作指令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过特殊的内存访问电路&lt;/li&gt;
&lt;li&gt;针对单处理器和多处理器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Test-and-Set 测试和置位&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从内存中读取值&lt;/li&gt;
&lt;li&gt;测试该值是否为1(然后返回真或假)&lt;/li&gt;
&lt;li&gt;内存值设置为1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;交换&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;交换内存中的两个值&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;bool TestandSet(bool *target){
		bool rv = *target;
		*target = true;
		return rv;
}

void Exchange(bool *a, bool *b){
		bool tmp = *a;
		*a = *b;
		*b = tmp;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;总结&lt;/p&gt;
&lt;p&gt;锁是更高等级的编程抽象&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥可以使用锁来实现&lt;/li&gt;
&lt;li&gt;通常需要一定等级的硬件支持&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常用的三种实现方法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;禁用中断(仅限于单处理器)&lt;/li&gt;
&lt;li&gt;软件方法(复杂)&lt;/li&gt;
&lt;li&gt;原子操作指令(单处理器或多处理器均可)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可选的实现内容:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有忙等待&lt;/li&gt;
&lt;li&gt;无忙等待&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>深入理解操作系统 第8章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-8/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-8/</guid><description>调度算法</description><pubDate>Sat, 28 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第八章的主要内容是：调度算法 (感觉清华这门课程前几章比较精彩,后续讲的有点混乱)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;h3&gt;上下文切换&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;切换CPU的当前任务, 从一个进程/线程到另一个&lt;/li&gt;
&lt;li&gt;保存当前进程/线程在PCB/TCB中的执行上下文(CPU状态)&lt;/li&gt;
&lt;li&gt;读取下一个进程/线程的上下文&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CPU调度&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;从就绪队列中挑选一个进程/线程作为CPU将要运行的下一个进程/线程&lt;/li&gt;
&lt;li&gt;调度程序: 挑选进程/线程的内核函数(通过一些调度策略)&lt;/li&gt;
&lt;li&gt;什么时候进行调度?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;内核运行调度程序的条件(满足一条即可)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一个进程从运行状态切换到等待状态&lt;/li&gt;
&lt;li&gt;一个进程被终结&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;不可抢占&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;调度程序必须等待事件结束&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;可以抢占&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;调度程序在中断被相应后执行&lt;/li&gt;
&lt;li&gt;当前的进程从运行切换到就绪, 或者一个进程从等待切换到就绪&lt;/li&gt;
&lt;li&gt;当前运行的进程可以被换出&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;调度原则&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;调度策略&lt;/p&gt;
&lt;p&gt;人们通常都需要&quot;更快&quot;的服务&lt;/p&gt;
&lt;p&gt;什么是更快?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传输文件时的高带宽&lt;/li&gt;
&lt;li&gt;玩游戏时的低延迟&lt;/li&gt;
&lt;li&gt;这两个因素是独立的&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;和水管类比&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;低延迟: 喝水的时候想要一打开水龙头水就流出来&lt;/li&gt;
&lt;li&gt;高带宽: 给游泳池充水时希望从水龙头里同时流出大量的水,并且不介意是否存在延迟&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们的目标:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;减少响应时间: 及时处理用户的输出并且尽快将输出提供给用户&lt;/li&gt;
&lt;li&gt;减少平均响应时间的波动: 在交互系统中,可预测性比高差异性低平均更重要&lt;/li&gt;
&lt;li&gt;增加吞吐量: 减少开销(操作系统开销,上下文切换);系统资源的高效率用(CPU,IO设备)&lt;/li&gt;
&lt;li&gt;减少等待时间: 减少每个进程的等待时间&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;程序执行模型&lt;/p&gt;
&lt;p&gt;执行模型 : 程序在CPU突发和IO中交替&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个调度决定都是关于在下一个CPU突发时将哪个工作交给CPU&lt;/li&gt;
&lt;li&gt;在时间分片机制下,线程可能在结束当前CPU突发前被迫放弃CPU&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;评价指标&lt;/p&gt;
&lt;p&gt;CPU使用率: CPU处于忙状态所占时间的百分比&lt;/p&gt;
&lt;p&gt;吞吐量: 在单位时间内完成的进程数量&lt;/p&gt;
&lt;p&gt;周转时间: 一个进程从初始化到结束,包括所有等待时间所花费的时间&lt;/p&gt;
&lt;p&gt;等待时间: 进程在就绪队列中的总时间&lt;/p&gt;
&lt;p&gt;响应时间: 从一个请求被提交到产生第一次相应所花费的总时间&lt;/p&gt;
&lt;p&gt;各指标在操作系统上的表现:&lt;/p&gt;
&lt;p&gt;低延迟调度增加了交互式表现(如果移动了鼠标,但是屏幕中的光标却没动,我们可能会重启电脑)&lt;/p&gt;
&lt;p&gt;操作系统需要保证低吞吐量不受影响(我想要结束长时间的编程,所以操作系统必须不时进行调度,即使存在许多交互任务)&lt;/p&gt;
&lt;p&gt;吞吐量是操作系统的计算带宽&lt;/p&gt;
&lt;p&gt;响应时间是操作系统的计算延迟&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;公平的目标&lt;/p&gt;
&lt;p&gt;举例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保证每个进程占用相同的CPU时间&lt;/li&gt;
&lt;li&gt;这公平嘛?如果一个用户比其他用户运行更多的进程怎么办&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;举例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保证每个进程都等待相同的时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;公平通常会增加平均响应时间&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;调度算法&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FCFS(先来先服务)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;First come, First Served&lt;/p&gt;
&lt;p&gt;如果进程在执行中阻塞,队列中的下一个会得到CPU&lt;/p&gt;
&lt;p&gt;优点: 简单&lt;/p&gt;
&lt;p&gt;缺点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;平均等待时间波动较大&lt;/li&gt;
&lt;li&gt;花费时间少的任务可能排在花费时间长的任务后面&lt;/li&gt;
&lt;li&gt;可能导致IO和CPU之间的重叠处理(CPU密集型进程会导致IO设备闲置时,IO密集型进程也在等待)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SPN(SJF) SRT(短进程优先(短作业优先)短剩余时间优先)[最优平均等待时间]&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Shortest Process Next(Shortest Job First) Shortest Remaining Time&lt;/p&gt;
&lt;p&gt;选择预测的完成时间来将任务入队&lt;/p&gt;
&lt;p&gt;可以是抢占的或者是不可抢占的&lt;/p&gt;
&lt;p&gt;可能导致饥饿&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;连续的短任务流会使场任务饥饿&lt;/li&gt;
&lt;li&gt;短任务可用时的任何场任务的CPU时间都会增加平均等待时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要预测未来&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;怎么预估下一个CPU突发的持续时间&lt;/li&gt;
&lt;li&gt;简单的解决: 询问用户&lt;/li&gt;
&lt;li&gt;如果用户欺骗就杀死进程&lt;/li&gt;
&lt;li&gt;如果不知道怎么办?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;HRRN(最高响应比优先)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Highest Response Ratio Next&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Round Robin(轮循)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用时间切片和抢占来轮流执行任务&lt;/p&gt;
&lt;p&gt;在叫做量子(或者时间切片)的离散单元中分配处理器&lt;/p&gt;
&lt;p&gt;时间片结束时,切换到下一个准备好的进程&lt;/p&gt;
&lt;p&gt;花销: 额外的上下文切换&lt;/p&gt;
&lt;p&gt;时间量子太大:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;等待时间过长&lt;/li&gt;
&lt;li&gt;极限情况退化成FCFS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;时间量子太小:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;反应迅速&lt;/li&gt;
&lt;li&gt;吞吐量由于大量的上下文切换开销受到影响&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目标:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选择一个合适的时间量子&lt;/li&gt;
&lt;li&gt;经验规则: 维持上下文切换开销处于1%以内&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multilevel Feedback Queues(多级反馈队列)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;优先级队列中的轮循&lt;/p&gt;
&lt;p&gt;就绪队列被划分成独立的队列: 比如前台(交互),后台(批处理)&lt;/p&gt;
&lt;p&gt;每个队列拥有自己的调度策略: 比如前台(RR),后台(FCFS)&lt;/p&gt;
&lt;p&gt;调度必须在队列间进行:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;固定优先级: 先处理前台,然后处理后台;可能导致饥饿&lt;/li&gt;
&lt;li&gt;时间切片: 每个队列都得到一个确定的能够调度其进程的CPU总时间;比如80%使用RR的前台,20%使用FCFS的后台&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个进程可以在不同的队列中移动&lt;/p&gt;
&lt;p&gt;例如,n级优先级-优先级调度在所有级别中,RR在每个级别中&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;时间量子大小随优先级级别增加而增加&lt;/li&gt;
&lt;li&gt;如果任务在当前的时间量子中没有完成,则降到下一个优先级&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优点: CPU密集型任务的优先级下降很快;IO密集型任务停留在高优先级&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fair Share Scheduling(公平共享调度)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;FSS控制用户对系统资源的访问&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一些用户组比其他用户组更重要&lt;/li&gt;
&lt;li&gt;保证不重要的组无法垄断资源&lt;/li&gt;
&lt;li&gt;未使用的资源按照每个组所分配的资源的比例来分配&lt;/li&gt;
&lt;li&gt;没有达到资源使用率目标的组获得更高的优先级&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;评价方式&lt;/h2&gt;
&lt;p&gt;确定性建模: 确定一个工作量,然后计算每个算法的表现&lt;/p&gt;
&lt;p&gt;队列模型: 用来处理随机工作负载的数学方法&lt;/p&gt;
&lt;p&gt;实现/模拟: 建立一个允许算法运行实际数据的系统;最灵活,最具一般性&lt;/p&gt;
&lt;h2&gt;实时调度&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;实时系统&lt;/p&gt;
&lt;p&gt;定义: 正确性依赖于其时间和功能两方面的一个操作系统&lt;/p&gt;
&lt;p&gt;性能指标: 时间约束的及时性;速度和平均性能相对不重要&lt;/p&gt;
&lt;p&gt;主要特征: 时间约束的可预测性&lt;/p&gt;
&lt;p&gt;分类:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强实时系统: 需要在保证时间内完成重要的任务,必须完成&lt;/li&gt;
&lt;li&gt;弱实时系统: 要求重要的进程的优先级更高,尽量完成,并非必须&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;任务(工作单元): 一次计算,一次文件读取,一次信息传递等&lt;/p&gt;
&lt;p&gt;属性: 去的进展所需要的资源;定时参数.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;单调速率(RM)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最佳静态优先级调度&lt;/li&gt;
&lt;li&gt;通过周期安排优先级&lt;/li&gt;
&lt;li&gt;周期越短优先级越高&lt;/li&gt;
&lt;li&gt;执行周期最短的任务&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;截止日期最早优先(EDF)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最佳的动态优先级调度&lt;/li&gt;
&lt;li&gt;Deadline越早优先级越高&lt;/li&gt;
&lt;li&gt;执行Deadline最早的任务&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;多处理器调度&lt;/h2&gt;
&lt;p&gt;多处理器的CPU调度更复杂:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多个相同的单处理器组成一个多处理器&lt;/li&gt;
&lt;li&gt;优点: 复杂共享&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对称多处理器(SMP)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个处理器运行自己的调度程序&lt;/li&gt;
&lt;li&gt;需要在调度程序中同步&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;优先级反转&lt;/h2&gt;
&lt;p&gt;可以发生在任务基于优先级的可抢占的调度机制中&lt;/p&gt;
&lt;p&gt;当系统内的环境强制使高优先级任务等待低优先级任务时发生&lt;/p&gt;
</content:encoded></item><item><title>深入理解操作系统 第7章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-7/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-7/</guid><description>进程</description><pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第七章的主要内容是：进程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;进程(process)描述&lt;/h2&gt;
&lt;h3&gt;进程定义&lt;/h3&gt;
&lt;p&gt;进程 : 一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程.&lt;/p&gt;
&lt;h3&gt;进程的组成&lt;/h3&gt;
&lt;p&gt;进程包括 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序的代码&lt;/li&gt;
&lt;li&gt;程序处理的数据&lt;/li&gt;
&lt;li&gt;程序计数器中的值, 指示下一条将运行的指令&lt;/li&gt;
&lt;li&gt;一组通用的寄存器的当前值, 堆, 栈&lt;/li&gt;
&lt;li&gt;一组系统资源(如打开的文件)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进程和程序的联系 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序是产生进程的基础&lt;/li&gt;
&lt;li&gt;程序的每次运行构成不同的进程&lt;/li&gt;
&lt;li&gt;进程是程序功能的体现&lt;/li&gt;
&lt;li&gt;通过多次执行, 一个程序可以对应多个进程, 通过调用关系, 一个进程可包括多个程序.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进程和程序的区别 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程是动态的,  程序是静态的 : 程序是有序代码的集合. 进程是程序的执行, 进程有核心态 / 用户态.&lt;/li&gt;
&lt;li&gt;进程是暂时的, 程序是永久的. 进程是一个状态变化的过程, 程序可以长久保存.&lt;/li&gt;
&lt;li&gt;进程和程序的组成不同 : 进程的组成包括程序, 数据和进程控制块(进程状态信息)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;进程的特点&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;动态性&lt;/strong&gt; : 可动态地创建, 结果进程;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;并发性&lt;/strong&gt; : 进程可以被独立调度并占用处理机运行; (并发:一段, 并行:一时刻)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;独立性&lt;/strong&gt; : 不同进程的工作不相互影响;(页表是保障措施之一)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;制约性&lt;/strong&gt; : 因访问共享数据, 资源或进程间同步而产生制约.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;抛出了一个问题 : 如果你要设计一个OS, 怎么样来实现其中的进程管理机制?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;进程控制结构&lt;/h3&gt;
&lt;p&gt;描述进程的数据结构 : 进程控制块 (Process Control Block)&lt;/p&gt;
&lt;p&gt;操作系统为每个进程都维护了一个PCB, 用来保存与该进程有关的各种状态信息.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程控制块 :&lt;/strong&gt; 操作系统管理控制进程运行所用的信息集合.&lt;/p&gt;
&lt;p&gt;进程的创建 : 为该进程生成一个PCB&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程的终止 :&lt;/strong&gt; 回收它的PCB&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程的组织管理 :&lt;/strong&gt; 通过对PCB的组织管理来实现&lt;/p&gt;
&lt;p&gt;(PCB具体包含什么信息? 如何组织的? 进程的状态转换?)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PCB有以下三大类信息 :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程标志信息. 如本进程的标志, 本进程的产生者标志(父进程标志). 用户标志&lt;/li&gt;
&lt;li&gt;处理机状态信息保存区 : 保存进程的运行现场信息 :
&lt;ul&gt;
&lt;li&gt;用户可见寄存器. 用户程序可以使用的数据, 地址等寄存器&lt;/li&gt;
&lt;li&gt;控制和状态寄存器. 如程序计数器(PC), 程序状态字(PSW)&lt;/li&gt;
&lt;li&gt;栈指针. 过程调用, 系统调用, 中断处理和返回时需要用到它&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程控制信息
&lt;ul&gt;
&lt;li&gt;调度和状态信息. 用于操作系统调度进程并占用处理机使用.&lt;/li&gt;
&lt;li&gt;进程间通信信息. 为支持进程间与通信相关的各种标志, 信号, 信件等, 这些信息都存在接收方的进程控制块中.&lt;/li&gt;
&lt;li&gt;存储管理信息. 包含有指向本进程映像存储空间的数据结构.&lt;/li&gt;
&lt;li&gt;进程所用资源. 说明由进程打开, 使用的系统资源. 如打开的文件等.&lt;/li&gt;
&lt;li&gt;有关数据结构的链接信息. 进程可以连接到一个进程队列中, 或连接到相关的其他进程的PCB.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;进程的组织方式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;链表 : 同一状态的进程其PCB成一链表, 多个状态对应多个不同的链表.(各状态的进程形成不同的链表 : 就绪链表, 阻塞链表)&lt;/p&gt;
&lt;p&gt;索引表 : 同一状态的进程归入一个index表(由index指向PCB), 多个状态对应多个不同的index表(各状态的进行形成不同的索引表 : 就绪索引表, 阻塞索引表)&lt;/p&gt;
&lt;h2&gt;进程状态(state)&lt;/h2&gt;
&lt;h3&gt;进程的生命期管理&lt;/h3&gt;
&lt;h4&gt;进程创建&lt;/h4&gt;
&lt;p&gt;引起进程创建的3个主要事件 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统初始化;&lt;/li&gt;
&lt;li&gt;用户请求创建一个新进程;&lt;/li&gt;
&lt;li&gt;正在运行的进程执行了创建进程的系统调用.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;进程运行&lt;/h4&gt;
&lt;p&gt;内核选择一个就绪的进程, 让它占用处理机并执行&lt;/p&gt;
&lt;p&gt;(为何选择?如何选择?)&lt;/p&gt;
&lt;h4&gt;进程等待(阻塞)&lt;/h4&gt;
&lt;p&gt;在以下情况下, 进程等待(阻塞):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;请求并等待系统服务, 无法马上完成&lt;/li&gt;
&lt;li&gt;启动某种操作, 无法马上完成&lt;/li&gt;
&lt;li&gt;需要的数据没有到达&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;进程只能自己阻塞自己, 因为只有进程自身才能知道何时需要等待某种事件的发生.&lt;/p&gt;
&lt;h4&gt;进程唤醒&lt;/h4&gt;
&lt;p&gt;唤醒进程的原因 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;被阻塞进程需要的资源可被满足&lt;/li&gt;
&lt;li&gt;被阻塞进程等待的事件到达&lt;/li&gt;
&lt;li&gt;将该进程的PCB插入到就绪队列&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;进程只能被别的进程或操作系统唤醒&lt;/p&gt;
&lt;h4&gt;进程结束&lt;/h4&gt;
&lt;p&gt;在以下四种情况下, 进程结束 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正常退出(自愿)&lt;/li&gt;
&lt;li&gt;错误退出(自愿)&lt;/li&gt;
&lt;li&gt;致命错误(强制性)&lt;/li&gt;
&lt;li&gt;被其他进程杀死(强制性)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;进程状态变化模型&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;进程的三种基本状态 :&lt;/strong&gt; 进程在生命结束前处于三种基本状态之一.&lt;/p&gt;
&lt;p&gt;不同系统设置的进程状态数目不同.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三种基本状态&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;运行状态(Running) : 当一个进程正在处理机上运行时&lt;/li&gt;
&lt;li&gt;就绪状态(Ready) : 一个进程获得了除处理机之外的一切所需资源, 一旦得到处理机即可运行&lt;/li&gt;
&lt;li&gt;等待状态(阻塞状态 Blocked) : 一个进程正在等待某一时间而暂停运行时. 如等待某资源, 等待输入/输出完成.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;进程其它的基本状态&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;创建状态(New) : 一个进程正在被创建, 还没被转到就绪状态之前的状态&lt;/p&gt;
&lt;p&gt;结束状态(Exit): 一个进程正在从系统中消失时的状态, 这是因为进程结束或由于其它原因所导致.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;可能的状态变化如下 :&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;NULL → New : 一个新进程被产生出来执行一个程序&lt;/p&gt;
&lt;p&gt;New → Ready: 当进程创建完成并初始化后, 一切就绪准备运行时, 变为就绪状态&lt;/p&gt;
&lt;p&gt;Ready → Running  : 处于就绪态的进程被进程调度程序选中后, 就分配到处理机上来运行&lt;/p&gt;
&lt;p&gt;Running → Exit   : 当进程表示它已经完成或者因出错, 当前运行进程会由操作系统作结束处理&lt;/p&gt;
&lt;p&gt;Running → Ready  : 处于运行状态的进程在其运行过程中, 由于分配它的处理机时间片用完而让出处理机&lt;/p&gt;
&lt;p&gt;Running → Blocked: 当进程请求某样东西且必须等待时&lt;/p&gt;
&lt;p&gt;Blocked → Ready  : 当进程要等待某事件到来时, 它从阻塞状态变到就绪状态&lt;/p&gt;
&lt;h3&gt;进程挂起&lt;/h3&gt;
&lt;p&gt;进程挂起, 为了合理且充分地利用系统资源.&lt;/p&gt;
&lt;p&gt;进程在挂起状态时, 意味着进程没有占用内存空间, 处在挂起状态的进程映像在磁盘上.(把进程放到磁盘上)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两种挂起状态&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;阻塞挂起状态 : 进程在外存并等待某事件的出现;&lt;/li&gt;
&lt;li&gt;就绪挂起状态 : 进程在外存, 但只要进入内存, 即可运行.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;与挂起相关的状态转换&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;挂起 :&lt;/strong&gt; 把一个进程从内存转到外存, 可能有以下几种情况 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;阻塞到阻塞挂起 : 没有进程处于就绪状态或就绪进程要求更多内存资源时, 会进行这种转换, 以提交新进程或运行时就绪进程.&lt;/li&gt;
&lt;li&gt;就绪到就绪挂起 : 当有高优先级阻塞(系统认为会很快就绪的)进程和低优先级就绪进程时, 系统会选择挂起低优先级就绪进程.&lt;/li&gt;
&lt;li&gt;运行到就绪挂起 : 对抢先式分时系统, 当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时, 系统可能会把运行进程转导就绪挂起状态.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;在外存时的状态转换 :&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;阻塞挂起到就绪挂起 : 当有阻塞挂起因相关事件出现时, 系统会把阻塞挂起进程转换为就绪挂起进程.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解挂, 激活 :&lt;/strong&gt; 把一个进程从外存转到内存; 可能有以下几种情况 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;就绪挂起到就绪 : 没有就绪进程或挂起就绪进程优先级高于就绪进程时, 会进行这种转换.&lt;/li&gt;
&lt;li&gt;阻塞挂起到阻塞 : 当一个进程释放足够内存时, 系统会把一个高优先级阻塞挂起(系统认为会很快出现所等待的事件)进程转换为阻塞进程.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;抛出一个问题 : OS怎么通过PCB和定义的进程状态来管理PCB, 帮助完成进程的调度过程?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;状态队列&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;由操作系统来维护一组队列, 用来表示系统当中所有进程的当前状态;&lt;/li&gt;
&lt;li&gt;不同的状态分别用不同的队列来表示(就绪队列, 各种类型的阻塞队列);&lt;/li&gt;
&lt;li&gt;每个进程的PCB都根据它的状态加入到相应的队列当中, 当一个进程的状态发生变化时, 它的PCB从一个状态中脱离出来, 加入到另外一个队列.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;线程(thread)&lt;/h2&gt;
&lt;h3&gt;为什么使用线程?&lt;/h3&gt;
&lt;p&gt;实例 : 编写一个MP3播放软件.&lt;/p&gt;
&lt;p&gt;核心功能 : (1)从MP3音频文件中读取数据; (2)对数据进行解压缩; (3)把解压缩后的音频数据播放出来.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//单进程方式
while(1){
	Read();
	Decompress();
	Play();
}
//问题: 播放出来的声音能否连贯? 各个函数之间不是并发执行, 影响资源的使用效率.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;//多进程
//进程1
while(1){
	Read();
}
//进程2
while(1){
	Decompress();
}
//进程3
while(1){
	Play();
}
//问题: 进程之间如何通信,共享数据?另外,维护进程的系统开销较大:
//创建进程时,分配资源,建立PCB;撤销进程时,回收资源,撤销PCB;进程切换时,保存当前进程的状态信息
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此需要提出一种新的实体, 满足以下特征:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;实体之间可以并发执行;&lt;/li&gt;
&lt;li&gt;实体之间共享相同的地址空间.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这实体就是线程.&lt;/p&gt;
&lt;h3&gt;什么是线程&lt;/h3&gt;
&lt;p&gt;线程是进程当中的一条执行流程.&lt;/p&gt;
&lt;p&gt;从两个方面重新理解进程:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从资源组合的角度: 进程把一组相关的资源组合起来,构成了一个资源平台(环境),包括地址空间(代码段,数据段),打开的文件等各种资源;&lt;/li&gt;
&lt;li&gt;从运行的角度: 代码在这个资源平台上的一条执行流程(线程).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;线程 = 进程 - 共享资源&lt;/p&gt;
&lt;h3&gt;线程的优缺点&lt;/h3&gt;
&lt;p&gt;线程的优点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个进程中可以同时存在多个线程;&lt;/li&gt;
&lt;li&gt;各个线程之间可以并发地执行;&lt;/li&gt;
&lt;li&gt;各个线程之间可以共享地址空间和文件等资源.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;线程的缺点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;一个线程崩溃, 会导致其所属进程的所有线程崩溃.(给它了&quot;权限&quot;就得有更高的&quot;责任&quot;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线程所需的资源&lt;/p&gt;
&lt;p&gt;不同的线程需要独立的寄存器和堆栈, 共享代码,数据和文件等.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;线程和进程的比较&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;进程是资源分配单位, 线程是CPU调度单位;&lt;/li&gt;
&lt;li&gt;进程拥有一个完整的资源平台, 而线程只独享必不可少的资源, 如寄存器和栈;&lt;/li&gt;
&lt;li&gt;线程同样具有就绪,阻塞和执行三种基本状态,同样具有状态之间的转换关系;&lt;/li&gt;
&lt;li&gt;线程能减少并发执行的时间和空间开销:
&lt;ul&gt;
&lt;li&gt;线程的创建时间比进程短;(直接利用所属进程的一些状态信息)&lt;/li&gt;
&lt;li&gt;线程的终止时间比进程短;(不需要考虑把这些状态信息给释放)&lt;/li&gt;
&lt;li&gt;同一进程内的线程切换时间比进程短;(同一进程不同线程的切换不需要切换页表)&lt;/li&gt;
&lt;li&gt;由于同一进程的各线程之间共享内存和文件资源, 可直接进行不通过内核的通信.(直接通过内存地址读写资源)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;线程的实现&lt;/h3&gt;
&lt;p&gt;主要有三种线程的实现方式:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户线程  : 在用户空间实现; POSIX Pthreads, Mach C-threads, Solaris threads&lt;/li&gt;
&lt;li&gt;内核线程  : 在内核中实现; Windows, Solaris, Linux&lt;/li&gt;
&lt;li&gt;轻量级进程: 在内核中实现,支持用户线程; Solaris&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;用户线程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;操作系统只能看到进程, 看不到线程, 线程的TCB在线程库中实现;&lt;/p&gt;
&lt;p&gt;在用户空间实现的线程机制, 它不依赖于操作系统的内核, 由一组用户级的线程库来完成线程的管理, 包括进程的创建,终止,同步和调度等.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;由于用户线程的维护由相应的进程来完成(通过线程库函数),不需要操作系统内核了解用户进程的存在,可用于不支持线程技术的多进程操作系统;&lt;/li&gt;
&lt;li&gt;每个进程都需要它自己私有的线程控制块(TCB)列表,用来跟踪记录它的各个线程的状态信息(PC,栈指针,寄存器),TCB由线程库函数来维护;&lt;/li&gt;
&lt;li&gt;用户线程的切换也是由线程库函数来完成,无需用户态/核心态切换,所以速度特别快;&lt;/li&gt;
&lt;li&gt;允许每个进程拥有自定义的线程调度算法.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户线程的缺点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;阻塞性的系统调用如何实现?如果一个线程发起系统调用而阻塞,则整个进程在等待;&lt;/li&gt;
&lt;li&gt;当一个线程开始运行时,除非它主动地交出CPU的使用权,否则它所在的进程当中的其他线程将无法运行;&lt;/li&gt;
&lt;li&gt;由于时间片分配给进程,所以与其他进程比,在多线程执行时,每个线程得到的时间片较少,执行会较慢.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;内核线程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;操作系统能够看到进程也可能看到线程,线程在内核中实现;&lt;/p&gt;
&lt;p&gt;内核线程是在操作系统的内核当中实现的一种线程机制,由操作系统的内核来完成线程的创建,终止和管理.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在支持内核线程的操作系统中,由内核来维护进程和线程的上下文信息(PCB和TCB);&lt;/li&gt;
&lt;li&gt;线程的创建,终止和切换都是通过系统调用,内核函数的方式来进行,由内核来完成,因此系统开销较大;&lt;/li&gt;
&lt;li&gt;在一个进程当中,如果某个内核线程发起系统调用而被阻塞,并不会影响其他内核线程的运行;&lt;/li&gt;
&lt;li&gt;时间片分配给线程,多线程的进程获得更多CPU时间;&lt;/li&gt;
&lt;li&gt;Windows NT 和 Windows 2000/XP 支持内核线程.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;轻量级进程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它是内核支持的用户线程.一个进程可以有一个或多个轻量化进程,每个量级进程由一个单独的内核线程来支持.(Solaris,Linux)&lt;/p&gt;
&lt;h2&gt;上下文切换&lt;/h2&gt;
&lt;p&gt;停止当前运行进程(从运行状态变成其他状态),并且调度其他进程(转变为运行状态)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必须在切换之前存储许多部分的进程上下文&lt;/li&gt;
&lt;li&gt;必须能够在之后恢复他们,所以进程不能显示它曾经被暂停过&lt;/li&gt;
&lt;li&gt;必须快速(上下文切换时非常频繁)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要存储什么上下文?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;寄存器(PC,SP...),CPU状态等信息&lt;/li&gt;
&lt;li&gt;一些时候可能会费时,所以我们应该尽可能避免&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;操作系统为活跃进程准备了进程控制块&lt;/p&gt;
&lt;p&gt;操作系统将进程控制块放置在一个合适的队列中&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;就绪队列&lt;/li&gt;
&lt;li&gt;等待IO队列(每个设备的队列)&lt;/li&gt;
&lt;li&gt;僵尸队列&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;进程控制&lt;/h2&gt;
&lt;h3&gt;创建进程&lt;/h3&gt;
&lt;p&gt;fork()的简单实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对子进程分配内存&lt;/li&gt;
&lt;li&gt;复制父进程的内存和CPU寄存器到子进程&lt;/li&gt;
&lt;li&gt;开销昂贵&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在99%的情况下,我们在调用fork()之后调用exec()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在fork()操作中内存复制是没有作用的&lt;/li&gt;
&lt;li&gt;子进程将可能关闭打开的文件和连接&lt;/li&gt;
&lt;li&gt;开销因此是最高的&lt;/li&gt;
&lt;li&gt;为什么不能结合它们在一个调用中(OS/2, windows)?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;vfork()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个创建进程的系统调用,不需要创建一个同样的内存映像&lt;/li&gt;
&lt;li&gt;一些时候称为轻量级fork()&lt;/li&gt;
&lt;li&gt;子进程应该几乎立即调用exec()&lt;/li&gt;
&lt;li&gt;现在不再使用如果我们使用 copy on write 技术&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;加载和执行进程&lt;/h3&gt;
&lt;p&gt;系统调用exec()加载程序取代当前运行的进程&lt;/p&gt;
&lt;p&gt;exec()调用允许一个进程&quot;加载&quot;一个不同的程序并且在main开始执行(事实上 _start)&lt;/p&gt;
&lt;p&gt;它允许一个进程指定参数的数量(argc)和它字符串参数数组(argv)&lt;/p&gt;
&lt;p&gt;如果调用成功(相同的进程,不同的程序)&lt;/p&gt;
&lt;p&gt;代码,stack,heap重写&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int pid = fork(); //创建子进程
if(pid == 0) {    //子进程
	exec_status = exec(&quot;calc&quot;, argc, argv0,argv1,...);
	printf(&quot;Why would I execute?&quot;);
} else if(pid &amp;gt; 0) { //父进程
	printf(&quot;Whose your daddy?&quot;);
	...
	child_status = wait(pid);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;等待和终止进程&lt;/h3&gt;
&lt;p&gt;wait()系统调用是被父进程用来等待子进程的结束&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个子进程向父进程返回一个值,所以父进程必须接受这个值并处理&lt;/li&gt;
&lt;li&gt;wait()系统调用担任这个要求
&lt;ul&gt;
&lt;li&gt;它使父进程去睡眠来等待子进程的结束&lt;/li&gt;
&lt;li&gt;当一个子进程调用exit()的时候,操作系统解锁父进程,并且将通过exit()传递得到的返回值作为wait调用的一个结果(连同子进程的pid一起)如果这里没有子进程存活,wait()立刻返回&lt;/li&gt;
&lt;li&gt;当然,如果这里有为父进程的僵尸等待,wait()立即返回其中一个值(并且解除僵尸状态)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程结束执行之后,它调用exit()&lt;/li&gt;
&lt;li&gt;这个系统调用:
&lt;ul&gt;
&lt;li&gt;将这程序的&quot;结果&quot;作为一个参数&lt;/li&gt;
&lt;li&gt;关闭所有打开的文件,连接等等&lt;/li&gt;
&lt;li&gt;释放内存&lt;/li&gt;
&lt;li&gt;释放大部分支持进程的操作系统结构&lt;/li&gt;
&lt;li&gt;检查是否父进程是存活着的:
&lt;ul&gt;
&lt;li&gt;如果是的话,它保留结果的值直到父进程需要它;在这种情况里,进程没有真正死亡,但是它进入了僵尸状态&lt;/li&gt;
&lt;li&gt;如果没有,它释放所有的数据结构,这个进程死亡&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;清理所有等待的僵尸进程&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程终止是最终的垃圾收集(资源回收)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>博客评论区接入步骤</title><link>https://github.com/posts/comments/</link><guid isPermaLink="true">https://github.com/posts/comments/</guid><description>笔记站和博客站新增评论区！欢迎大家留言~</description><pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;博客评论区接入步骤&lt;/h1&gt;
&lt;p&gt;本次教程详细记录 Giscus 评论区的完整接入流程，从仓库准备到项目集成，每一步都附具体操作和注意事项，即使是新手也能轻松上手，同时解决集成过程中可能遇到的重复配置、主题适配、部署报错等问题。&lt;/p&gt;
&lt;h2&gt;一、GitHub 仓库准备&lt;/h2&gt;
&lt;p&gt;Giscus 评论区的核心原理是将评论数据存储在 GitHub 仓库的 Discussions（讨论）中，因此第一步必须先为博客仓库开启 Discussions 功能并创建对应分类。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开你的博客 GitHub 仓库，点击仓库顶部的「Settings」（设置）按钮，进入仓库设置页面。&lt;/li&gt;
&lt;li&gt;在左侧导航栏找到「Features」（功能）选项，找到「Discussions」（讨论）功能，勾选前方的复选框，开启该功能（勾选后会自动刷新页面，生成 Discussions 标签页）。&lt;/li&gt;
&lt;li&gt;切换到仓库顶部的「Discussions」标签页，点击右上角「New discussion」（新建讨论），选择「Start a new category」（创建新分类）。&lt;/li&gt;
&lt;li&gt;配置分类信息：分类名称推荐填写「General」（通用），分类描述可填写「博客评论存储区」，分类类型选择「Announcements」（公告）或「General」均可，点击「Create category」完成创建。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;注意：分类名称和类型请牢记，后续在 Giscus 配置页需要对应选择，否则评论无法正常关联存储。&lt;/p&gt;
&lt;h2&gt;二、安装并授权 Giscus 应用&lt;/h2&gt;
&lt;p&gt;Giscus 需要通过 GitHub App 授权，才能访问你的仓库 Discussions，实现评论的读取和提交功能，授权过程全程在 GitHub 官方页面完成，安全无风险。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;访问 &lt;a href=&quot;https://github.com/apps/giscus&quot;&gt;Giscus GitHub App&lt;/a&gt; 页面，点击页面中间的「Install」（安装）按钮。&lt;/li&gt;
&lt;li&gt;在授权页面，选择「Only select repositories」（仅选择指定仓库），从下拉列表中找到你的博客仓库，勾选该仓库。&lt;/li&gt;
&lt;li&gt;点击页面底部的「Install &amp;amp; Authorize」（安装并授权），完成授权后会自动跳转回 Giscus 配置页（若未跳转，可手动打开 &lt;a href=&quot;https://giscus.app/zh-CN&quot;&gt;Giscus 配置页&lt;/a&gt;）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;提示：授权成功后，Giscus 会自动获取你仓库的 Discussions 权限，无需额外配置，后续可在 GitHub 仓库的「Settings → Applications → Installed GitHub Apps」中管理 Giscus 授权。&lt;/p&gt;
&lt;h2&gt;三、生成 Giscus 评论区核心代码&lt;/h2&gt;
&lt;p&gt;在 Giscus 配置页完成各项参数设置，生成适配你博客的评论区代码，这一步的配置直接决定评论区能否正常显示和使用，务必仔细核对。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开 &lt;a href=&quot;https://giscus.app/zh-CN&quot;&gt;Giscus 配置页&lt;/a&gt;，在「仓库」输入框中，填写你的博客仓库完整名称（格式：用户名/仓库名，如 &lt;code&gt;Ryan-1125/Notes-Papership&lt;/code&gt;），输入完成后会自动验证仓库是否可用。&lt;/li&gt;
&lt;li&gt;「讨论分类」下拉列表中，选择第一步创建的分类（如「General」），选择后会自动加载该分类的ID。&lt;/li&gt;
&lt;li&gt;「映射方式」选择「按标题匹配」（或根据需求选择「按路径名匹配」，推荐按标题匹配，确保每篇文章对应独立评论区）。&lt;/li&gt;
&lt;li&gt;配置完成后，页面底部会自动生成一段&amp;lt;script&amp;gt;` 代码，后续会用到。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;注意：复制代码时，不要遗漏任何字符，尤其是 data-repo、data-repo-id 等关键属性，这些属性是评论区关联你仓库的核心。&lt;/p&gt;
&lt;h2&gt;四、集成到博客项目&lt;/h2&gt;
&lt;p&gt;这是整个接入流程中最关键的一步，也是最容易出错的环节。&lt;/p&gt;
&lt;h3&gt;4.1 新建 Comment 组件&lt;/h3&gt;
&lt;p&gt;首先在你的 Astro 项目中，新建评论区组件，所有评论相关的代码都集中在这个组件中，便于后续维护和修改。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开你的博客项目，进入 &lt;code&gt;src/components&lt;/code&gt; 目录。&lt;/li&gt;
&lt;li&gt;在 components 目录下，新建一个文件，命名为 &lt;code&gt;Comment.astro&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.2 编写组件代码&lt;/h3&gt;
&lt;p&gt;不建议直接粘贴 Giscus 生成的原始代码，而是将配置抽离到项目的 config.ts 文件中，避免组件代码重复，后续修改配置无需改动组件，这也是专业开发的常用方式。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先打开项目的 &lt;code&gt;src/config.ts&lt;/code&gt; 文件，在文件中添加评论区配置（复制下面代码，替换成你自己的配置，配置值&amp;lt;script&amp;gt;` 代码中提取）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// src/config.ts
// 其他配置（如 siteConfig、profileConfig 等）...

// Giscus 评论区配置（从 Giscus 配置页生成的代码中提取）
export const commentConfig = {
  enable: true, // 控制评论区是否显示（true 显示，false 隐藏）
  repo: &quot;Ryan-1125/Notes-Papership&quot;, // 你的博客仓库名
  repoId: &quot;xxx&quot;, //data-repo-id 的值
  category: &quot;General&quot;, // 第一步创建的 Discussions 分类名
  categoryId: &quot;xxx&quot;, //data-category-id 的值
  mapping: &quot;title&quot;, // 映射方式
  strict: &quot;0&quot;, // 严格匹配
  reactionsEnabled: &quot;0&quot;, // 表情反应（1启用，2禁用）
  emitMetadata: &quot;0&quot;, // 发送元数据
  inputPosition: &quot;top&quot;, // 输入框位置
  lang: &quot;zh-CN&quot;, // 语言
  loading: &quot;lazy&quot;, // 加载方式
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;回到 &lt;code&gt;src/components/Comment.astro&lt;/code&gt; 文件，编写组件代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ---
 // 1. 从项目配置文件中导入评论相关配置
 // 所有 repo、id、分类等信息都存在 config.ts 里
 import { commentConfig } from &quot;../config&quot;;
 ---

 &amp;lt;!-- 2. 评论区外层容器：控制上下间距 + 居中样式 --&amp;gt;
 &amp;lt;div class=&quot;mt-8 giscus-container&quot;&amp;gt;
   &amp;lt;!-- giscus 挂载点：脚本会自动把评论区渲染到这里 --&amp;gt;
   &amp;lt;section class=&quot;giscus mx-auto w-full&quot;&amp;gt;&amp;lt;/section&amp;gt;
 &amp;lt;/div&amp;gt;

 &amp;lt;!-- 3. Giscus 官方核心脚本 --&amp;gt;
 &amp;lt;script 
   is:inline          
   data-astro-rerun   
   src=&quot;https://giscus.app/client.js&quot;  
   
   
   data-repo={commentConfig.repo}
   data-repo-id={commentConfig.repoId}
   data-category={commentConfig.category}
   data-category-id={commentConfig.categoryId}
   data-mapping={commentConfig.mapping}
   data-strict={commentConfig.strict}
   data-reactions-enabled={commentConfig.reactionsEnabled}
   data-emit-metadata={commentConfig.emitMetadata}
   data-input-position={commentConfig.inputPosition}
   data-lang={commentConfig.lang}
   data-loading={commentConfig.loading}

   crossorigin=&quot;anonymous&quot;
   async  
 &amp;gt;
 &amp;lt;/script&amp;gt;

 &amp;lt;!-- 4. 主题自动切换核心逻辑 --&amp;gt;
 &amp;lt;script is:inline&amp;gt;
   // 更新 Giscus 主题：亮/暗色自动切换
   function updateGiscusTheme() {
     // 判断当前网站是否是暗色模式
     const theme = document.documentElement.classList.contains(&apos;dark&apos;) 
       ? &apos;dark&apos;   // 暗色 → 用 dark 主题
       : &apos;light&apos;; // 亮色 → 用 light 主题

     // 获取 Giscus 渲染的 iframe
     const iframe = document.querySelector(&apos;iframe.giscus-frame&apos;)
     
     // 如果 iframe 存在，发送消息切换主题
     if (iframe) {
       iframe.contentWindow.postMessage(
         { giscus: { setConfig: { theme: theme } } },
         &apos;https://giscus.app&apos;
       )
     }
   }

   // 监听网站主题变化
   const observer = new MutationObserver(updateGiscusTheme)
   observer.observe(document.documentElement, { 
     attributes: true, 
     attributeFilter: [&apos;class&apos;] 
   })

   // 页面初次加载完成时，自动设置一次主题
   window.addEventListener(&apos;message&apos;, (event) =&amp;gt; {
     if (event.origin !== &apos;https://giscus.app&apos;) return
     if (!(typeof event.data === &apos;object&apos; &amp;amp;&amp;amp; event.data.giscus)) return
     updateGiscusTheme()
   })
 &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;代码说明：这段代码包含三个核心部分——外层容器（控制样式）、Giscus 核心脚本（实现评论功能）、主题切换脚本（实现亮暗色同步），同时通过导入 config.ts 的配置，避免了硬编码重复，后续修改评论区配置（如仓库名、分类），只需修改 config.ts 即可，无需改动组件。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.3 引入组件到文章页面&lt;/h3&gt;
&lt;p&gt;组件创建完成后，需要将其引入到博客的文章详情页，让每篇文章底部都能显示评论区，同时调整评论区的位置，使其符合常规博客布局。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找到你的博客文章详情页文件，通常路径为 &lt;code&gt;src/pages/posts/[...slug].astro&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在该文件的顶部导入 Comment 组件，添加导入语句（注意导入路径要正确，根据你的文件层级调整）：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;  // 导入 Comment 评论组件
  import Comment from &apos;../../components/Comment.astro&apos;;
  // 导入 config.ts 中的 commentConfig，控制评论区显示/隐藏
  import { commentConfig } from &apos;../../config&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;调整评论区位置，将 Comment 组件插入到「文章内容」和「前后文导航」之间，具体代码位置如下：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;article class=&quot;post-content&quot;&amp;gt;
  &amp;lt;/article&amp;gt;

{commentConfig.enable &amp;amp;&amp;amp; (
  &amp;lt;div class=&quot;onload-animation&quot;&amp;gt;
    &amp;lt;/div&amp;gt;
)}

&amp;lt;nav class=&quot;post-nav&quot;&amp;gt;
  &amp;lt;/nav&amp;gt;

&amp;lt;/MainGridLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;路径核对：若你的 &lt;code&gt;[...slug].astro&lt;/code&gt; 文件在 &lt;code&gt;src/pages/posts&lt;/code&gt; 目录，Comment 组件在 &lt;code&gt;src/components&lt;/code&gt; 目录，导入路径 &lt;code&gt;../../components/Comment.astro&lt;/code&gt; 是正确的；若层级不同，调整 &lt;code&gt;../&lt;/code&gt; 的数量即可（比如目录层级更深，可改为 &lt;code&gt;../../../components/Comment.astro&lt;/code&gt;）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.4 本地验证&lt;/h3&gt;
&lt;p&gt;集成完成后，先在本地运行项目，验证评论区是否正常显示、主题是否能同步切换，避免直接部署后出现报错。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开终端，进入项目根目录，运行命令 &lt;code&gt;pnpm dev&lt;/code&gt;（若你使用的是 npm，运行 &lt;code&gt;npm run dev&lt;/code&gt;），启动本地开发服务。&lt;/li&gt;
&lt;li&gt;服务启动后，访问任意一篇文章页面，查看评论区是否正常加载（若显示「登录 GitHub 评论」，说明集成成功）。&lt;/li&gt;
&lt;li&gt;切换博客的亮暗色主题，验证评论区是否能同步切换主题（亮色模式显示 light 主题，暗色模式显示 dark 主题）。&lt;/li&gt;
&lt;li&gt;若评论区不显示，检查以下几点：① Comment 组件导入路径是否正确；② config.ts 中的 commentConfig 配置是否正确；③ Giscus 授权是否成功；④ 浏览器控制台是否有报错（按 F12 打开控制台，查看报错信息）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;五、部署上线&lt;/h2&gt;
&lt;p&gt;本地验证无误后，即可提交代码并部署，完成评论区的最终接入。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开GitHub Desktop，执行 &lt;code&gt;commit&lt;/code&gt;提交所有修改，提交备注。&lt;/li&gt;
&lt;li&gt;执行 &lt;code&gt;push&lt;/code&gt;，将代码推送到 GitHub 仓库。&lt;/li&gt;
&lt;li&gt;进入 GitHub 仓库，点击顶部的「Actions」标签页，查看部署工作流是否正常运行（若显示绿色对勾，说明部署成功；若显示红色叉号，点击进入查看报错信息，根据报错修改后重新推送）。&lt;/li&gt;
&lt;li&gt;部署完成后，访问你的博客网站，打开任意一篇文章，验证评论区是否正常显示、能否正常登录评论、表情反应是否可用。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;六、常见问题排查&lt;/h2&gt;
&lt;p&gt;集成过程中可能会遇到一些问题，这里整理了最常见的 4 个问题及解决方案，帮你快速避坑。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;问题1：评论区不显示，控制台报错「commentConfig is not defined」→ 解决方案：检查 &lt;code&gt;[...slug].astro&lt;/code&gt; 和 &lt;code&gt;Comment.astro&lt;/code&gt; 中是否导入了 &lt;code&gt;commentConfig&lt;/code&gt;，导入路径是否正确。&lt;/li&gt;
&lt;li&gt;问题2：主题切换时，评论区不跟随变化 → 解决方案：检查 Comment.astro 中的主题切换脚本是否完整，确保没有遗漏代码，同时确认博客的暗色模式是通过给 html 标签添加 dark 类实现的。&lt;/li&gt;
&lt;li&gt;问题3：部署后评论区不显示，本地正常 → 解决方案：检查 GitHub Actions 部署日志，确认是否有报错；同时检查 Giscus 授权是否成功，仓库 Discussions 功能是否开启。&lt;/li&gt;
&lt;li&gt;问题4：评论无法提交，提示「权限不足」→ 解决方案：重新授权 Giscus 应用，确保授权的仓库是你的博客仓库，且 Giscus 拥有 Discussions 的读写权限。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;七、最终效果与功能说明&lt;/h2&gt;
&lt;p&gt;成功接入后，你的博客评论区将具备以下功能，完全适配博客整体风格，体验流畅：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每篇文章对应独立的评论区，评论数据存储在 GitHub 仓库的 Discussions 中，完全可控，不会丢失。&lt;/li&gt;
&lt;li&gt;支持 GitHub 账号一键登录评论，无需额外注册，操作便捷。&lt;/li&gt;
&lt;li&gt;支持评论回复、表情反应（点赞、笑脸等），提升互动性；支持 Markdown 语法，可在评论中插入代码、链接、列表等。&lt;/li&gt;
&lt;li&gt;评论区主题自动跟随博客亮暗色切换，视觉统一，提升用户体验。&lt;/li&gt;
&lt;li&gt;可通过 config.ts 中的 &lt;code&gt;commentConfig.enable&lt;/code&gt; 一键开关评论区，灵活控制。&lt;/li&gt;
&lt;li&gt;懒加载机制，不影响博客页面加载速度，提升页面性能。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;至此，Giscus 评论区接入全部完成！欢迎大家在评论区留言互动，一起交流学习~&lt;/p&gt;
</content:encoded></item><item><title>深入理解操作系统 第6章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-6/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-6/</guid><description>操作系统的虚拟内存管理技术中的页面置换算法</description><pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第六章的主要内容是：操作系统的虚拟内存管理技术中的页面置换算法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;功能与目标&lt;/h2&gt;
&lt;p&gt;功能 : 当缺页中断发生, 需要调入新的页面而内存已满时, 选择内存当中哪个物理页面被置换.&lt;/p&gt;
&lt;p&gt;目标 : 尽可能地减少页面的换进换出次数(即缺页中断的次数). 具体来说, 把未来不再使用的或短期内较少使用的页面换出, 通常只能在局部性原理指导下依据过去的统计数据来进行预测.&lt;/p&gt;
&lt;p&gt;页面锁定 : 用于描述必须常驻内存的操作系统的关键部分或时间关键的应用进程. 实现的方式是 : 在页表中添加锁定标记位(lock bit).&lt;/p&gt;
&lt;h2&gt;实验设置与评价方法&lt;/h2&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;p&gt;记录一个进程对页访问的一个轨迹&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;举例 : 虚拟地址跟踪(页号, 偏移)...
&lt;ul&gt;
&lt;li&gt;(3,0) (1,9) (4,1) (2,1) (5,3) (2,0) ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;生成的页面轨迹
&lt;ul&gt;
&lt;li&gt;3, 1, 4, 2, 5, 2, 1, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;模拟一个页面置换的行为并且记录产生页缺失数的数量&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;更少的缺失, 更好的性能&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;局部页面置换算法&lt;/h2&gt;
&lt;h3&gt;最优页面置换算法&lt;/h3&gt;
&lt;p&gt;基本思路 : 当一个缺页中断发生时, 对于保存在内存当中的每一个逻辑页面, 计算在它的下一次访问之前, 还需等待多长时间, 从中选择等待时间最长的那个, 作为被置换的页面.&lt;/p&gt;
&lt;p&gt;这是一种理想情况, 在实际系统中是无法实现的, 因为操作系统无法知道每一个页面要等待多长时间以后才会再次被访问.&lt;/p&gt;
&lt;p&gt;可用作其他算法的性能评价的依据.(在一个模拟器上运行某个程序, 并记录每一次的页面访问情况, 在第二遍运行时即可使用最优算法)&lt;/p&gt;
&lt;h3&gt;先进先出算法&lt;/h3&gt;
&lt;p&gt;基本思路 : 选择在内存中驻留时间最长的页面淘汰. 具体来说, 系统维护着一个链表, 记录了所有位于内存当中的逻辑页面. 从链表的排列顺序来看, 链首页面的驻留时间最长, 链尾页面的驻留时间最短. 当发生一个缺页中断时, 把链首页面淘汰出去, 并把新的页面添加到链表的末尾.&lt;/p&gt;
&lt;p&gt;性能较差, 调出的页面有可能是经常要访问的页面. 并且有 belady现象. FIFO算法很少单独使用.&lt;/p&gt;
&lt;h3&gt;最近最久未使用算法&lt;/h3&gt;
&lt;p&gt;LRU(Least Recently Used)&lt;/p&gt;
&lt;p&gt;基本思路 : 当一个缺页中断发生时, 选择最久未使用的那个页面, 并淘汰.&lt;/p&gt;
&lt;p&gt;它是对最优页面置换算法的一个近似, 其依据是程序的局部性原理, 即在最近一小段时间(最近几条指令)内, 如果某些页面被频繁地访问, 那么再将来的一小段时间内, 他们还可能会再一次被频繁地访问. 反过来说, 如果过去某些页面长时间未被访问, 那么在将来它们还可能会长时间地得不到访问.&lt;/p&gt;
&lt;p&gt;LRU算法需要记录各个页面使用时间的先后顺序, 开销比较大.&lt;/p&gt;
&lt;p&gt;两种可能的实现方法是 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统维护一个页面链表, 最近刚刚使用过的页面作为首节点, 最久未使用的作为尾结点. 再一次访问内存时, 找出相应的页面, 把它从链表中摘下来, 再移动到链表首. 每次缺页中断发生时, 淘汰链表末尾的页面.&lt;/li&gt;
&lt;li&gt;设置一个活动页面栈, 当访问某页时, 将此页号压入栈顶, 然后, 考察栈内是否有与此页面相同的页号, 若有则抽出. 当需要淘汰一个页面时, 总是选择栈底的页面, 它就是最久未使用的.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;时钟页面置换算法&lt;/h3&gt;
&lt;p&gt;基本思路 :&lt;/p&gt;
&lt;p&gt;需要用到页表项的访问位, 当一个页面被装入内存时, 把该位初始化为0. 然后如果这个页面被访问, 则把该位置设为1;&lt;/p&gt;
&lt;p&gt;把各个页面组织成环形链表(类似钟表面), 把指针指向最老的页面(最先进来);&lt;/p&gt;
&lt;p&gt;当发生一个缺页中断时, 考察指针所指向的最老页面, 若它的访问位为0, 立即淘汰; 若访问位为0, 然后指针往下移动一格. 如此下去, 直到找到被淘汰的页面, 然后把指针移动到下一格.&lt;/p&gt;
&lt;p&gt;流程 :&lt;/p&gt;
&lt;p&gt;如果访问页在物理内存中, 访问位置1.&lt;/p&gt;
&lt;p&gt;如果不在物理页, 从指针当前指向的物理页开始, 如果访问位0, 替换当前页, 指针指向下一个物理页; 如果访问位为1, 置零以后访问下一个物理页再进行判断. 如果所有物理页的访问位都被清零了, 又回到了第一次指针所指向的物理页进行替换.&lt;/p&gt;
&lt;h3&gt;二次机会算法&lt;/h3&gt;
&lt;p&gt;因为考虑到时钟页面置换算法, 有时候会把一些 dirty bit 为1(有过写操作)的页面进行置换, 这样的话, 代价会比较大. 因此, 可以结合访问位和脏位一起来决定应该置换哪一页.&lt;/p&gt;
&lt;p&gt;used   dirty     →  used   dirty&lt;/p&gt;
&lt;p&gt;0         0                  replace&lt;/p&gt;
&lt;p&gt;0         1                  0         0&lt;/p&gt;
&lt;p&gt;1          0                 0         0&lt;/p&gt;
&lt;p&gt;1          1                 0         1&lt;/p&gt;
&lt;p&gt;相当于说, 替换的优先级, 没有读写也没写过, 那么直接走, 如果写过或者访问过, 那么给你一次机会, 如果又写过, 又访问过, 那么久给你两次机会.&lt;/p&gt;
&lt;h3&gt;最不常用算法&lt;/h3&gt;
&lt;p&gt;Least Frequently used, LFU&lt;/p&gt;
&lt;p&gt;基本思路 : 当一个缺页中断发生时, 选择访问次数最少的那个页面, 并淘汰.&lt;/p&gt;
&lt;p&gt;实现方法 : 对每一个页面设置一个访问计数器, 每当一个页面被访问时, 该页面的访问计数器加1. 当发生缺页中断时, 淘汰计数值最小的那个页面.&lt;/p&gt;
&lt;p&gt;LRU和LFU的对比 : LRU考察的是多久未访问, 时间越短越好. 而LFU考察的是访问的次数和频度, 访问次数越多越好.&lt;/p&gt;
&lt;h3&gt;Belady现象(科学家名字)&lt;/h3&gt;
&lt;p&gt;在采用FIFO算法时, 有时会出现分配的物理页面数增加, 缺页率反而提高的异常现象;&lt;/p&gt;
&lt;p&gt;出现原因 : FIFO算法的置换特征与进程访问内存的动态特征是矛盾的, 与置换算法的目标是不一致的(即替换较少使用的页面), 因此, 被他置换出去的页面不一定是进程不会访问的.&lt;/p&gt;
&lt;h3&gt;LRU / FIFO 和 Clock 的比较&lt;/h3&gt;
&lt;p&gt;LRU和FIFO都是先进先出的思路, 只不过LRU是针对页面最近访问时间来进行排序, 所以需要在每一次页面访问的时候动态地调整各个页面之间的先后顺序(有一个页面的最近访问时间变了). 而FIFO是针对页面进入内存的时间来进行排序, 这个时间是固定不变的, 所以各个页面之间的先后顺序是固定的. 如果一个页面在进入内存后没有被访问, 那么它的最近访问时间就是它进入内存的时间. 换句话说, 如果内存当中的所有页面都未曾访问过, 那么LRU算法就退化为了FIFO算法.&lt;/p&gt;
&lt;p&gt;例如 : 给进程分配3个物理页面, 逻辑页面的访问顺序是 : 1,2,3,4,5,6,1,2,3 ...&lt;/p&gt;
&lt;h2&gt;全局页面置换算法&lt;/h2&gt;
&lt;h3&gt;工作集模型&lt;/h3&gt;
&lt;p&gt;前面介绍的各种页面置换算法, 都是基于一个前提, 即程序的局部性原理. 但是此原理是否成立?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果局部性原理不成立, 那么各种页面置换算法就没有说明分别, 也没有什么意义. 例如 : 假设进程对逻辑页面的访问顺序是1,2,3,4,5,6,6,7,8,9..., 即单调递增, 那么在物理页面数有限的前提下, 不管采用何种置换算法, 每次的页面访问都必然导致缺页中断.&lt;/li&gt;
&lt;li&gt;如果局部性原理是成立的, 那么如何来证明它的存在, 如何来对它进行定量地分析? 这就是工作集模型.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;工作集&lt;/h3&gt;
&lt;p&gt;工作集 : 一个进程当前正在使用的逻辑页面集合.&lt;/p&gt;
&lt;p&gt;可以使用一个二元函数 W(t, delta) 来表示 :&lt;/p&gt;
&lt;p&gt;t 是当前的执行时刻;&lt;/p&gt;
&lt;p&gt;delta 称为工作集窗口, 即一个定长的页面访问的时间窗口;&lt;/p&gt;
&lt;p&gt;W(t, delta) = 在当前时刻 t 之前的 delta 时间窗口当中的所有页面所组成的集合(随着 t 的变化, 该集合也在不断的变化)&lt;/p&gt;
&lt;p&gt;|W(t, delta)| 是工作集的大小, 即逻辑页的数量.&lt;/p&gt;
&lt;p&gt;工作集大小的变化 : 进程开始执行后, 随着访问新页面逐步建立较稳定的工作集. 当内存访问的局部性区域的位置大致稳定时, 工作集大小也大致稳定; 局部性区域的位置改变时, 工作集快速扩张和收缩过渡到下一个稳定值.&lt;/p&gt;
&lt;h3&gt;常驻集&lt;/h3&gt;
&lt;p&gt;常驻集是指在当前时刻, 进程实际驻留在内存当中的页面集合.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工作集是进程在运行过程中固有的性质, 而常驻集取决于系统分配给进程的物理页面数目, 以及所采用的页面置换算法;&lt;/li&gt;
&lt;li&gt;如果一个进程的整个工作集都在内存当中, 即常驻集 包含 工作集, 那么进程将很顺利地运行, 而不会造成太多的缺页中断(直到工作集发生剧烈变动, 从而过渡到另一个状态);&lt;/li&gt;
&lt;li&gt;当进程常驻集的大小达到某个数目之后, 再给它分配更多的物理页面, 缺页率也不会明显下降.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;工作集页置换算法&lt;/h3&gt;
&lt;p&gt;当工作集窗口在滑动过程中, 如果页面不在集合中, 那么就会直接丢失这个不在窗口中页面, 而不会等待缺页中断再丢弃.&lt;/p&gt;
&lt;h3&gt;缺页率置换算法&lt;/h3&gt;
&lt;p&gt;可变分配策略 : 常驻集大小可变. 例如 : 每个进程在刚开始运行的时候, 先根据程序大小给它分配一定数目的物理页面, 然后在进程运行过程中, 再动态地调整常驻集的大小.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可采用全局页面置换的方式, 当发生一个缺页中断时, 被置换的页面可以是在其他进程当中, 各个并发进程竞争地使用物理页面.&lt;/li&gt;
&lt;li&gt;优缺点 : 性能较好, 但增加了系统开销.&lt;/li&gt;
&lt;li&gt;具体实现 : 可以使用缺页率算法来动态调整常驻集的大小.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;缺页率 : 表示 &quot;缺页次数 / 内存访问次数&quot;&lt;/p&gt;
&lt;p&gt;影响因素 : 页面置换算法, 分配给进程的物理页面数目, 页面本身的大小, 程序的编写方法.&lt;/p&gt;
&lt;h3&gt;抖动问题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;如果分配给一个进程的物理页面太少, 不能包含整个的工作集, 即常驻集 属于 工作集, 那么进程将会造成很多的缺页中断, 需要频繁的在内存与外存之间替换页面, 从而使进程的运行速度变得很慢, 我们把这种状态称为 &quot;抖动&quot;.&lt;/li&gt;
&lt;li&gt;产生抖动的原因 : 随着驻留内存的进程数目增加, 分配给每个进程的物理页面数不断就减小, 缺页率不断上升. 所以OS要选择一个适当的进程数目和进程需要的帧数, 以便在并发水平和缺页率之间达到一个平衡.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>数据结构笔记（南油）</title><link>https://github.com/posts/%E5%8D%97%E9%82%AE%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%9F%A5%E8%AF%86%E7%82%B9/</link><guid isPermaLink="true">https://github.com/posts/%E5%8D%97%E9%82%AE%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%9F%A5%E8%AF%86%E7%82%B9/</guid><description>数据结构笔记（南油）</description><pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;习题知识点&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;下边的知识点是在做南京邮电大学考研历年真题时遇到自己不会的题时整理出来的。第九部分是mooc课后习题整理出来的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1.线性表&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;设链表非空，采用&lt;strong&gt;不带表头结点的单循环链表，一个链表指针指向表的第一个结点&lt;/strong&gt; 这种链表可以在O(1)时间内在表尾插入一个新结点。&lt;/p&gt;
&lt;p&gt;解释：可以在头结点后插入一个和头结点一样的结点，然后修改头结点的值为新结点的值，将插入的结点指为新的头结点，时间复杂度为O(1)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在末尾插入和删除节点，带头结点的双循环链表  最节省时间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在单链表中设置头结点的作用是 ①对第一个结点的操作和对其他结点的操作统一。②空表和非空表的操作也统一。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;取&lt;/strong&gt; 第i个元素及其前趋元素 利用顺序表&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;存取&lt;/strong&gt; 第i个元素及其前趋元素 利用顺序表。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;存取&lt;/strong&gt; 第i个元素及其前后元素 利用顺序表。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线性表采用链式存储时，即存储了数据本身，又存储了数据间的关系。顺序存储是通过数组下标来表示数据之间的关系。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对线性表进行二分搜索时，线性表应为有序顺序表，且以表的中点为划分点。 错误&lt;/p&gt;
&lt;p&gt;解释： 如果是单数的话就是中间 如果是双数的话 一定不是中间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合并两个长度分别为 m 和 n 的有序表，最坏情况下需要比较 m+n-1次。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;链式存储设计时，节点内的存储单元地址==一定连续==&lt;/p&gt;
&lt;p&gt;不同节点的存储单元地址可以不连续，节点内的存储单元地址必须连续&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;需要分配较大的空间，插入和删除不需要移动元素的线性表，其存储结构为 静态链表。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线性表的两种存储结构各有哪些优缺点？&lt;/p&gt;
&lt;p&gt;链式存储：优点：插入和删除不需要移动大量元素。 缺点：大量访问操作时不如顺序存储结构。&lt;/p&gt;
&lt;p&gt;顺序存储：可随机存储表中任意元素，方便灵活，效率高。 缺点：插入和删除需大量移动元素。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;头指针，头结点，表头结点的区别&lt;/p&gt;
&lt;p&gt;不管有没有头结点，头指针始终指向链表的第一个结点（头结点是带头结点的链表中的第一个结点）；在表头结点之前设置的结点为头结点；表头结点为链表中第一个存储数据元素的结点。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数组作为一种线性结构，因此可以与线性表一样进行数据元素的插入删除运算    错误&lt;/p&gt;
&lt;p&gt;解释：数组的空间是提前分配好的，不能直接进行插入操作&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2.栈和队列&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;将中缀表达式转换为后缀表达式，进入堆栈的元素为表达式中的 运算符 ，计算后缀表达式中进入堆栈的元素为 操作数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;循环队列是一种顺序存储结构&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;消除递归不一定要用栈来实现。比如，对于斐波那契数列，可以使用递推的方式来实现； 但是使用栈，一定可以将递归消去。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;栈和队列是逻辑结构，不是存储结构。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;阐述栈和队列在非线性数据结构中的应用&lt;/p&gt;
&lt;p&gt;栈和队列可以在二叉树和图的非递归遍历中应用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;页面替换算法用到了队列&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用两个栈模拟队列&lt;/p&gt;
&lt;p&gt;对S2的出栈模拟出队，若S2为空，则先将S1中的所有元素送入S2。对S1的入栈模拟入队，==若S1满，必须先保证S2为空，才能将S1的元素插入S2==。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;什么是队列的上溢现象？&lt;/p&gt;
&lt;p&gt;在队列的顺序存储结构中，设队头指针为front，队尾指针为rear，队列的容量为maxnum。当有元素要加入队列时，若rear = maxnum，则会发生队列的上溢现象，此时不能将该元素加入队列。“假溢出”现象：队列中余有足够的空间，但元素却不能入队。可以用循环队列解决。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.数组&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;根据==三对角==对阵压缩方法，
将A[1..n] [1..n]压缩至B[0..3n-3]时，aij与k的对应关系为：k=2i+j-3；
将A[1..n] [1..n]压缩至B[1..3n-2]时，aij与k的对应关系为：k=2i+j-2；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;稀疏矩阵压缩存储后，必定失去随机存取功能。&lt;/p&gt;
&lt;p&gt;解释：压缩后，使用三元组进行存储，已经不是简单的线性结构，所以不可以随机存取。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==若用邻接矩阵存储有向图，矩阵中主对角线以下的元素均为零，该图拓扑排序存在，可能不唯一。加上条件A[i] [i+1]全为1才是唯一的==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有一个100*90的稀疏矩阵,非0元素有10个,设每个整型数占2字节,则用三元组表示该矩阵时,所需的字节数是 66&lt;/p&gt;
&lt;p&gt;解释：每个非零元素占3 * 2=6个字节，共10个非零元素，需6 * 10 = 60 个字节； 此外，还一般要用三个整数来存储矩阵的行数、列数和总元素个数，又需要3*2 = 6个字节； 总共：60 + 6 = 66 个字节。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;m*n的稀疏矩阵中有t个非零元素，==快速转置的时间复杂度为O(n+t)==&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;4.树和二叉树&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;扩充二叉树&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;扩充二叉树：在二叉树出现空子树的位置增加空树叶所形成的二叉树&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;外部结点：空树叶结点&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内部结点：非空结点&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;外路径长度：扩充二叉树中所有外部结点到根结点的路径长度之和&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内路径长度：扩充二叉树中所有内部结点到根节点的路径长度之和&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://typora-dzsq.oss-cn-hangzhou.aliyuncs.com/img/image-20220928101407515.png&quot; alt=&quot;image-20220928101407515&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://typora-dzsq.oss-cn-hangzhou.aliyuncs.com/img/image-20220928102035994.png&quot; alt=&quot;image-20220928102035994&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;B树一般用于文件索引系统。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线索二叉树的好处&lt;/p&gt;
&lt;p&gt;容易确定树中任何一个节点在某种遍历顺序下的前驱或者后继&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从堆中删除一个元素的时间复杂度为O(log2n)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;平衡二叉树每次调整的对象都是&lt;strong&gt;最小不平衡子树&lt;/strong&gt;，即插入路径上离插入结点最近的平衡因子的绝对值大于1的结点作为根的子树。（P21 简答题第一题）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;哈夫曼编码是不等长编码，ASCII为等长编码。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为什么采用哈夫曼编码？（P22）&lt;/p&gt;
&lt;p&gt;对出现频率高的字符使用较少的码位编码，对于出现频率低的字符使用较多的码位编码，减少总体文本长度。所以采用哈夫曼编码。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个索引文件，如果经常需要插入和删除元素，宜采用 B+树 做索引&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果一个有向图代表一个工程之间的领先关系，当算法执行出现拓扑排序不能包括图中全部顶点的情况时，应当检查 ==是否是反自反的==。（P25 填空第四题）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;存储B树中每个结点的存储块的大小是否相同，为什么?&lt;/p&gt;
&lt;p&gt;相同的，因为每个结点都是顺序表的形式，不需要在添加元素时动态分配它的内存空间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引入B树的根本原因是 为了减少外搜索的磁盘访问次数，为修改过程提供简单的平衡算法。在有 n 个元素的 m阶B树中，搜索一个元素的算法需要访问外存的次数至多为 log[m/2]向上取整 [(n+1)/2]+1。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;中序遍历的时间复杂度为O(n)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;森林中有N个非叶结点，则对应的二叉树中右指针域为空的结点有N+1个。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;B树只能随机存取，B+树可以随机和顺序存取。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==F,C是二叉树的两个结点，使用&lt;strong&gt;先序和后序遍历&lt;/strong&gt;可以判断F是C的祖先。==&lt;/p&gt;
&lt;p&gt;原因：①如果F是C的祖先，先序遍历时，F在C的前边；后序遍历时，F在C的后边，相对位置改变。②如果F，C是兄弟结点，那么先序遍历和后序遍历时相对位置不变。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最小生成树：普利姆算法适合求解边稠密的图，克鲁斯卡尔算法适用于求解 边稀疏的图。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在关键字==随机分布==的情况下，用二叉树排序树的方法进行查找，其查找长度与 折半查找 量级相当。&lt;/p&gt;
&lt;p&gt;原因：在随机的情况下，二叉排序树的平均查找长度的数据量级为O(log2n)，与折半查找同数量级。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设高度（根节点高度为1）为h的二叉树只有度为0和度为2的结点，则此类二叉树结点数至少为 2h-1&lt;/p&gt;
&lt;p&gt;解释：只有度为0和2两种情况,即每个结点要么没有子节点,要么有两个子节点(概念),这样一来除了根节点,每层至少两个结点,共h层,应有2(h-1)+1(除根节点外每层都有2个,最后加上1个根节点),化简得2h-1个&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;求AVL每个结点的平衡因子时使用后序遍历。 2011年最后一题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;任何无向图都可以转换成生成树。 错误&lt;/p&gt;
&lt;p&gt;解释：只有无向连通图才可以转换为生成树。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==图采用邻接表存储时最小生成树Prim算法时间复杂度为O(n^2)==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线索二叉树是一种物理结构&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;n个结点线索二叉树含有的 n+1 个线索&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;二叉判定树的树形取决于 表中元素个数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优先权队列就是构造小根堆的过程&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;树的路径长度是从树根到每个节点的路径长度的总和&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在二叉树中，m是n的祖先，使用==后序遍历==可以找到 m 到 n的路径&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;二叉树在线索化后，仍不能有效求解后序线索二叉树求后序后继&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;后序线索树遍历仍需要 栈 的支持&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;树和二叉树的区别 ①二叉树的度至多为2，树无此限制  ②二叉树有左右子树之分，树无此限制  ③二叉树允许为空，树一般不允许为空&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==非空二叉树先序和后序相反是什么形态？   二叉树高度和结点个数相同==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==层序遍历和中序遍历能唯一确定一颗二叉树==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完全二叉树必定是二叉平衡树   错误          因为二叉平衡树是有序的&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;5.散列表&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;一个好的散列函数满足以下条件：①能快速计算 ②具有均匀性&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线性探测法的缺点：容易产生聚集现象，从而导致搜索时间增加&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于除留余数法的模M，一般如何选择&lt;/p&gt;
&lt;p&gt;一般取不大于表长的最大素数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;删除散列表可行且有效的方法？说明理由&lt;/p&gt;
&lt;p&gt;在拉链法情况下可以物理清除。在开放定址法情况下，不能做物理删除，只能做标记删除。&lt;/p&gt;
&lt;p&gt;删除时只将其标记为NeverUsed，而不实际删除。这样可以跳过该元素继续查找，插入时可在该位置插入元素。P(22)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;哈希表的结点中只包含数据元素自身的信息，不包含任何指针。 ×&lt;/p&gt;
&lt;p&gt;散列法存储的思想是由关键字值决定数据的存储地址         √&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==拉链法又称开散列法，开地址法又称为闭散列法==     （易错）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;散列查找中的平均查找长度（查找效率）和 散列函数，装填因子，处理冲突的方法 有关。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用哈希（散列）方法处理冲突（碰撞）时可能出现堆积（聚集）现象，下列选项中，会受堆积现象直接影响的是 （D）&lt;/p&gt;
&lt;p&gt;解释：存储是基于查找的，所以不能算作直接影响。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;存储效率
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;数列函数
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;装填（装载）因子
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;平均查找长度
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;开地址法散列到同一个地址而引起的“堆积问题”是由于 ==同义词或非同义词之间发生冲突==。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;6.图&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;拓扑排序：无有向回路（无环）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;迪斯杰特拉算法按==路径长度递增==次数逐一产生最短路径。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有n个顶点的有向强连通图最多有 n(n-1) 条边。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设有向图用邻接表表示，计算第 i 个顶点的入度方法是 扫描邻接表中的所有边结点，统计 adjvex = i 的边结点个数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;判断一个图中是否有环的方法：拓扑排序，dfs（思路：==如果dfs遍历到的点之前已经被遍历过，说明有环==），求关键路径&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果具有n个顶点的图是一个环，则它有 n 棵生成树。&lt;/p&gt;
&lt;p&gt;解释：因为n个顶点构成的环共有n条边，去掉其中任意一条便是一棵生成树，所以共有n种情况。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==Prime算法思想==：从某一个顶点出发，选择与它关联的最小权值的边，将其顶点加入到顶点集s中，之后就从一个顶点在s集中，另一个顶点不在s集中，选择出权值最小的边，把对应的顶点加入到s集中，直到所有顶点都加到s集中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;图采用邻接表时最小生成树Prim算法的时间复杂度为O(n^2)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;只要无向连通图中没有权值相同的边，则其最小生成树唯一&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最小生成树代价唯一（形状可能不唯一）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若一个有向图具有==有序==的拓扑排序序列，则它的邻接矩阵必定为 ==三角==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;图的DFS遍历思想相当于二叉树的先序遍历&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若一个有向图的顶点不能排在一个拓扑序列中，则可判定该有向图 ==含有顶点数目大于1的强连通分量==。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个有n个顶点和n条边的无向图一定是 有环的。&lt;/p&gt;
&lt;p&gt;解释：不一定是连通图。比如构成两个环的时候。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于有 n 个顶点的图，普利姆算法的时间复杂度是O(n^2)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;7.排序算法&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;快排中，每次划分后，将划分的两个长度不等的子表分别排序。为提高排序效率，应对其中哪个子表先排序，为什么？&lt;/p&gt;
&lt;p&gt;先对长度较小的排序，为了减少栈空间的大小。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;快速排序最好情况，分割元素将序列分割成两个大小一样的子序列。（例题：P13）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;是否在多有情况下快排都优于直接插入排序，为什么？
快排适用于数据量大（上百万）的情形，插入排序适合数据量小的情形。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果用三个元素中间值法，可以避免将待排序元素划分在分割元素一边的情形发生，快速排序的优越性得以体现。（P22）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;排序的时间复杂度只需记住三个即可。直接插入和冒泡排序的最好时间复杂度为O(n)。快速排序的最坏时间复杂度为O(n^2)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;采用什么措施可改善快速排序的最坏情况的时间性能？&lt;/p&gt;
&lt;p&gt;①选取随机数作为枢轴&lt;/p&gt;
&lt;p&gt;②使用左端，右端和中心的中值作为枢轴&lt;/p&gt;
&lt;p&gt;③每次选取数据集的中位数作为枢轴&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;倒排文件是对次关键字建立索引的。（不知道考不考）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将两个长度均为n的有序表合成一个有序表，关键字比较的最多次数和最少次数为 2n-1 和 n。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;顺序查找n个元素的顺序表，当使用哨兵结点时，若查找失败，则比较关键字的次数为 n+1 次。&lt;/p&gt;
&lt;p&gt;解释：n个元素都要比较一次，但都不成功，最后监视哨也要比较一次，比较成功，一共比较n+1次。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;直接插入排序一趟不能确定一个元素的最终位置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;以下数据结构查找效率最低的是  C&lt;/p&gt;
&lt;p&gt;A.二叉搜索树            B.有序顺序表          C.堆                D.二叉平衡树&lt;/p&gt;
&lt;p&gt;解释：堆是用来排序的，在查找时是无序的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对任意n个关键字排序的==比较次数==至少为 ==log2(n!) 向上取整==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每趟排序结束后都至少能够确定一个元素最终位置的方法是 简单选择排序，冒泡排序，快排，堆排&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;排序趟数与序列初始状态无关的排序算法是  直接插入，简单选择，基数排序，归并排序&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对任意7个关键字进行基于比较的排序，至少进行 13 次关键字之间的两两比较。&lt;/p&gt;
&lt;p&gt;解释：对任意 n 个关键字排序的比较次数至少为  &lt;code&gt;log2(n!)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;快速排序在==被排序的数据完全无序==情况下最易发挥长处&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;8.其他&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;算法的输入可以有0个或多个，输出有1个或多个&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数据结构是带有结构的数据元素的集合。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;算法的健壮性是指当输入不合法数据时，程序会采取紧急措施。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件结构是外存数据的组织方式，常见的四种文件结构包括 顺序文件，索引文件，链式文件，索引顺序文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;四种逻辑结构，分别为集合，线性，图，树形结构&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==逻辑结构，存储结构和运算之间的关系？==&lt;/p&gt;
&lt;p&gt;数据的逻辑结构反应数据元素之间的逻辑关系，与存储结构无关。数据的存储结构是数据在计算机中的表示。数据的运算是对数据定义的一组操作，运算是定义在逻辑结构上的，与存储结构无关，而运算的实现依赖于存储结构。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若逻辑结构相同但存储结构不同，则为不同的数据结构。&lt;/p&gt;
&lt;p&gt;例1： 如线性表的顺序表和链表，二者的逻辑结构相同而存储结构不同，为不同的数据结构。&lt;/p&gt;
&lt;p&gt;例2： 二叉树的顺序存储和链式存储&lt;/p&gt;
&lt;p&gt;若存储结构相同但逻辑结构不同，则为不同的数据结构。&lt;/p&gt;
&lt;p&gt;例：链式存储的树和图&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;度量一个程序执行时间有哪些方法？优缺点是什么？&lt;/p&gt;
&lt;p&gt;①事后统计。缺点：1.必须先实际运行 2. 执行时间依赖于计算机硬件，软件等环境因素，有时容易掩盖算法本身的优势。&lt;/p&gt;
&lt;p&gt;②事前分析估算。解决了事后统计的缺点。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;影响时间复杂度O(n)的因素有 问题规模。（计算机软硬件不能选）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;O渐近上界，Ω渐近下界，θ渐进确界&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;计算机中有顺序存储，链式存储，索引存储，哈希存储四种存储方式&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以用==抽象数据类型==定义一个完整的数据结构&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;算法原地工作是指==算法所需的辅助空间是常量==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;折半查找和二叉排序树的时间性能有时不相同&lt;/p&gt;
&lt;p&gt;二叉排序树的查找性能和数据的输入顺序有关，最坏情况形成单支树，查找长度为O(n)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==对n个元素的有序顺序表和无序顺序表进行顺序查找，判断下边三种情况下平均查找长度是否相同==&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查找失败&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不相同，对有序顺序表，当查找的关键字值大于要查找的元素时，就查找失败，不必查找到表尾。而对于无序顺序表，必须查找到表尾才能确定查找失败。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查找成功，且表中只有一个关键字等于给定K的元素&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;相同。两者找到表中关键字值等于给定值时就停止查找。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查找成功，且表中有若干个关键字等于给定K的元素&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不相同。有序顺序表关键字相同的元素排列在一起，只要找到第一个就可以找到其他关键字相同的元素。而无序顺序表必须查找表中全部元素才能确定相同关键字元素全部找出来了，所以所需时间不同。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当执行函数时，其局部变量的存储一般采用 栈结构 进行存储。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;9. MOOC课件习题&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;算法和程序==最显著的区别==？&lt;/p&gt;
&lt;p&gt;算法是有穷的，而程序不一定满足有穷性。如操作系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==算法和程序的区别和联系？==&lt;/p&gt;
&lt;p&gt;联系：程序是计算机指令的有序集合，是算法用某种设计语言的表述，是算法在计算机上的实现。&lt;/p&gt;
&lt;p&gt;区别：语言描述上不同。程序必须用规定的程序设计语言，而算法可以用自然语言，伪代码等等。算法是有穷的，而程序不一定满足有穷性。如操作系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;数据是可被计算机识别并处理加工的对象&lt;/p&gt;
&lt;p&gt;数据元素是由数据组成的具有一定意义的基本单位&lt;/p&gt;
&lt;p&gt;数据项是数据元素中不可分割的最小单位&lt;/p&gt;
&lt;p&gt;数据结构是由某一数据对象及该对象中所有数据元素之间的关系组成的。&lt;/p&gt;
&lt;p&gt;数据的逻辑结构是数据元素之间的逻辑关系&lt;/p&gt;
&lt;p&gt;数据的存储结构是数据元素之间的关系在计算机内的表示形式&lt;/p&gt;
&lt;p&gt;数据的运算是数据被使用的方式&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://typora-dzsq.oss-cn-hangzhou.aliyuncs.com/img/image-20221208102625160.png&quot; alt=&quot;image-20221208102625160&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://typora-dzsq.oss-cn-hangzhou.aliyuncs.com/img/image-20221208103120546.png&quot; alt=&quot;image-20221208103120546&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://typora-dzsq.oss-cn-hangzhou.aliyuncs.com/img/image-20221208114058519.png&quot; alt=&quot;image-20221208114058519&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;行三元组表 行优先&lt;/p&gt;
&lt;p&gt;列三元组表 列优先&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;无序表查找成功的平均查找长度 (n+1)/2&lt;/p&gt;
&lt;p&gt;​            查找失败的平均查找长度 n&lt;/p&gt;
&lt;p&gt;有序表查找成功 (n+1)/2&lt;/p&gt;
&lt;p&gt;​            ==查找失败 2+n/2==&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;​        &lt;img src=&quot;https://typora-dzsq.oss-cn-hangzhou.aliyuncs.com/img/image-20221209095514534.png&quot; alt=&quot;image-20221209095514534&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;邻接表的优缺点&lt;/p&gt;
&lt;p&gt;优点：便于统计边的数目；空间效率高&lt;/p&gt;
&lt;p&gt;缺点：不便于判断顶点之间是否有边；不便于计算有向图各个顶点的度&lt;/p&gt;
&lt;p&gt;邻接矩阵的优缺点：&lt;/p&gt;
&lt;p&gt;优点：便于判断两个顶点是否有边；便于计算各个顶点的度&lt;/p&gt;
&lt;p&gt;缺点：不便于统计边的数目；空间复杂度高&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以存在多个关键路径&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;拉链法散列在同一个链表的结点称为 同义词&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>深入理解操作系统 第5章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-5/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-5/</guid><description>操作系统的虚拟内存管理技术</description><pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第五章的主要内容是：操作系统的虚拟内存管理技术&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;虚拟内存的起因&lt;/h2&gt;
&lt;p&gt;使用硬盘/磁盘使更多的程序在有限的内存中运行&lt;/p&gt;
&lt;p&gt;理想的存储器 : 更大更快更便宜和非易失性的存储区&lt;/p&gt;
&lt;h2&gt;覆盖技术&lt;/h2&gt;
&lt;p&gt;如果是程序太大, 超出了内存的容量, 可以采用手动的概率(overlay)技术, 只把需要的指令和数据保存在内存当中&lt;/p&gt;
&lt;p&gt;目的 : 是在较小的可用内存中运行较大的程序, 常用于多道程序系统, 与分区存储管理配合使用.&lt;/p&gt;
&lt;p&gt;原理 :&lt;/p&gt;
&lt;p&gt;把程序按照其自身逻辑结构, 划分为若干个功能上相互独立的程序模块, 那些不会同时执行的模块共享同一块内存区域, 按时间先后来运行.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必要部分(常用功能)的代码和数据常驻内存;&lt;/li&gt;
&lt;li&gt;可选部分(不常用功能)在其他程序模块中实现, 平时存放在外存中, 在需要用到时才装入内存;&lt;/li&gt;
&lt;li&gt;不存在调用关系的模块不必同时装入到内存, 从而可以相互覆盖, 即这些模块共用一个分区.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;也就是说,程序松耦合的部分可以按需装入内存,不需要的时候放在外存中,多个不常用部分共用一个分区.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;p&gt;A(20k) &lt;strong&gt;&lt;strong&gt;B(50k) ____ D(30k)
| ____ C(30k) ____ E(20k)
|&lt;/strong&gt;&lt;/strong&gt; F(40k)&lt;/p&gt;
&lt;p&gt;因此不需要将整个程序190k的数据全部放入内存中, 而是划分为 常驻区(20k) 覆盖区0(50k) 覆盖区1(40k) 压缩至了110k的内存空间使用&lt;/p&gt;
&lt;p&gt;缺点 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;由程序员来把一个大的程序划分为若干个小的功能模块, 并确定各个模块之间的覆盖关系, 费时费力, 增加了编程的复杂度;&lt;/li&gt;
&lt;li&gt;覆盖模块并从外存装入内存, 实际上是以时间延长来换取空间节省.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;交换技术&lt;/h2&gt;
&lt;p&gt;如果是程序太多, 超过了内存的容量, 可以采用自动的交换(swapping)技术, 把暂时不能执行的程序送到外存中&lt;/p&gt;
&lt;p&gt;目的 : 多道程序在内存时, 让正在运行的程序或需要运行的程序获得更多的内存资源&lt;/p&gt;
&lt;p&gt;原理 :&lt;/p&gt;
&lt;p&gt;可将暂时不能运行的程序送到外存, 从而获得空闲内存空间. 操作系统把一个进程的整个地址空间的内容保存到外存中(换出 swap out), 而将外存中的某个进程的地址空间读入到内存中(换入 swap in). 换入换出内容的大小为整个程序的地址空间.&lt;/p&gt;
&lt;p&gt;存在问题 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;交换时机的确定 : 何时需要发生交换? 只当内存空间不够或有不够的危险时换出;&lt;/li&gt;
&lt;li&gt;交换区的大小 : 必须足够大以存放所有用户进程的所有内存映像的拷贝, 必须能够对这些内存映像进行直接存取&lt;/li&gt;
&lt;li&gt;程序换入时的重定位 : 换出后再换入的内存位置一定要在原来的位置上嘛?(可能出现寻址问题) 最好采用动态地址映射的方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;覆盖技术和交换技术的对比&lt;/h2&gt;
&lt;p&gt;特点 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;覆盖只能发生在那些相互之间没有调用关系的程序模块之间, 因此程序员必须给出程序内的各个模块之间的逻辑覆盖结构.&lt;/li&gt;
&lt;li&gt;交换技术是以在内存中的程序大小为单位进行的, 它不需要程序员给出各个模块之间的逻辑覆盖结构.&lt;/li&gt;
&lt;li&gt;换言之, 交换发生在内存中程序与管理程序或操作系统之间, 而覆盖则发生在运行程序的内部.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在内存不够用的情形下, 可以采用覆盖技术和交换技术, 但是 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;覆盖技术 : 需要程序要自己把整个程序划分为若干个小的功能模块, 并确定各个模块之间的覆盖关系, 增加了程序员的负担.&lt;/li&gt;
&lt;li&gt;交换技术 : 以进程作为交换的单位, 需要把进程的整个地址空间都换入换出, 增加了处理器的开销.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;虚拟内存管理技术&lt;/h2&gt;
&lt;p&gt;如果想要在有限容量的内存中, 以更小的页粒度为单位装入更多更大的程序, 可以采用自动的虚拟存储技术&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;目标&lt;/p&gt;
&lt;p&gt;像覆盖技术那样, 不是把程序的所有内容都放在内存中, 因而能够运行比当前的空闲内存空间还要大的程序. 但做的更好, 由操作系统自动来完成, 无需程序员的干涉.&lt;/p&gt;
&lt;p&gt;像交换技术那样, 能够实现进程在内存与外存之间的交换, 因而获得更多的空闲内存空间. 但做的更好, 只对进程的部分内容在内存和外存之间进行交换.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;程序局部性原理&lt;/p&gt;
&lt;p&gt;程序的局部性原理(principle of locality) : 指程序在执行过程中的一个较短时期, 所执行的指令地址和指令的操作数地址, 分别局限于一定的区域.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;时间局部性 : 一条指令的一次执行和下次执行, 一个数据的一次访问和下次访问都集中在一个较短时期内 ;&lt;/li&gt;
&lt;li&gt;空间局部性 : 当前指令和邻近的几条指令, 当前访问的数据和邻近的几个数据都集中在一个较小区域内.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;程序的局部性原理表明, 从理论上来说, 虚拟存储技术是能够实现的. 而且在实现了以后应该是能够取得一个满意的效果.&lt;/p&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;题目描述 :
页面大小为4k, 分配给每个进程的物理页面是1. 
在一个进程中, 定义了如下的二维数组 int A[1024][1024]. 该数组按行存放在内存, 每一行放在一个页面中.
考虑一下程序的编写方法对缺页率的影响?

程序编写方法1 : (发生了1024*1024次缺页中断)
for(j = 0; j &amp;lt; 1024; j++)
		for(i = 0; i &amp;lt; 1024; i++)
				A[i][j] = 0;

程序编写方法2 : (发生了1024次缺页中断)
for(i = 0; i &amp;lt; 1024; i++)
		for(j = 0; j &amp;lt; 1024; j++)
				A[i][j] = 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本概念&lt;/p&gt;
&lt;p&gt;可以在页式或段式内存管理的基础上实现&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在装入程序时, 不必将其全部装入内存, 而只需将当前需要执行的部分页面或段装入到内存中, 就可以让程序开始执行;&lt;/li&gt;
&lt;li&gt;在程序执行过程中, 如果需执行的指令或访问的数据尚未在内存中(称为缺页或缺段), 则由处理器通知操作系统将相应的页面或段调入到内存, 然后继续执行程序;&lt;/li&gt;
&lt;li&gt;另一方面, 操作系统将内存中暂时不使用的页面或段调出保存在外存上, 从而腾出更多空闲内存空间存放将要装入的程序以及将要调入的页面或段.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本特征&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大的用户空间 : 通过把物理内存和外存相结合, 提供给用户的虚拟内存空间通常大于实际的物理内存, 即实现了这两者的分离. 如32位的虚拟地址理论上可以访问4GB, 而可能计算机上仅有256M的物理内存, 但硬盘容量大于4GB.&lt;/li&gt;
&lt;li&gt;部分交换 : 与交换技术相比较, 虚拟存储的调入和调出是对部分虚拟地址空间进行的;&lt;/li&gt;
&lt;li&gt;不连续性 : 物理内存分配的不连续性, 虚拟地址空间使用的不连续性.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟页式内存管理&lt;/p&gt;
&lt;p&gt;页式内存管理&lt;/p&gt;
&lt;p&gt;页表 : 完成逻辑页到物理页帧的映射&lt;/p&gt;
&lt;p&gt;根据页号去页表中寻找索引, 先查看 resident bit 是否为0, 0表示不存在, 1表示映射关系存在, 获得帧号加上原本的偏移, 获得了物理地址.&lt;/p&gt;
&lt;p&gt;虚拟页式内存管理&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;大部分虚拟存储系统都采用虚拟页式存储管理技术, 即在页式存储管理的基础上, 增加请求调页和页面置换功能.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本思路&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当一个用户程序要调入内存运行时, 不是将该程序的所有页面都装入内存, 而是只装入部分的页面, 就可启动程序运行.&lt;/li&gt;
&lt;li&gt;在运行的过程中, 如果发现要运行的程序或要访问的数据不再内存, 则向系统发出缺页的中断请求, 系统在处理这个中断时, 将外存中相应的页面调入内存, 使得该程序能够继续运行.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;页表表项&lt;/p&gt;
&lt;p&gt;逻辑页号 | 访问位 | 修改位 | 保护位 | 驻留位 | 物理页帧号&lt;/p&gt;
&lt;p&gt;驻留位 : 表示该页是在内存中还是在外存.&lt;/p&gt;
&lt;p&gt;保护位 : 表示允许对该页做何种类型的访问, 如只读, 可读写, 可执行等&lt;/p&gt;
&lt;p&gt;修改位 : 表示此页在内存中是否被修改过. 当系统回收该物理页面时, 根据此位来决定是否把它的内容写回外存&lt;/p&gt;
&lt;p&gt;访问位 : 如果该页被访问过(包括读写操作), 则设置此位. 用于页面置换算法.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺页中断处理过程 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果在内存中有空闲的物理页面, 则分配一物理页帧f, 然后转第4步; 否则转到第2步;&lt;/li&gt;
&lt;li&gt;采用某种页面置换算法, 选择一个将被替换的物理页帧f, 它所对应的逻辑页为q, 如果该页在内存期间被修改过, 则需要把它写回外存;&lt;/li&gt;
&lt;li&gt;对q所对应的页表项修改, 把驻留位置为0;&lt;/li&gt;
&lt;li&gt;将需要访问的页p装入到物理页面f当中;&lt;/li&gt;
&lt;li&gt;修改p所对应的页表项的内容, 把驻留位置为1, 把物理页帧号置为f;&lt;/li&gt;
&lt;li&gt;重新运行被中断是指令.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;在何处保存未被映射的页?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;能够简单地识别在二级存储器中的页&lt;/li&gt;
&lt;li&gt;交换空间(磁盘或者文件) : 特殊格式, 用于存储未被映射的页面&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;后备存储(二级存储) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个虚拟地址空间的页面可以被映射到一个文件(在二级存储中)的某个位置&lt;/li&gt;
&lt;li&gt;代码段 : 映射到可执行二进制文件&lt;/li&gt;
&lt;li&gt;动态加载的共享库程序段 : 映射到动态调用的库文件&lt;/li&gt;
&lt;li&gt;其他段 : 可能被映射到交换文件(swap file)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟内存性能&lt;/p&gt;
&lt;p&gt;为了便于理解分页的开销, 使用有效存储器访问时间 effective memory access time (EAT)&lt;/p&gt;
&lt;p&gt;EAT = 访存时间 * 页表命中几率 + page fault处理时间 * page fault几率&lt;/p&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;p&gt;访存时间 : 10 ns&lt;/p&gt;
&lt;p&gt;磁盘访问时间 : 5 ms&lt;/p&gt;
&lt;p&gt;参数 p  = page fault 几率&lt;/p&gt;
&lt;p&gt;参数 q = dirty page 几率(对页面写操作)&lt;/p&gt;
&lt;p&gt;EAT = 10*(1-p) + 5000000*p*(1+q)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>计算机组成原理笔记</title><link>https://github.com/posts/%E8%AE%A1%E7%BB%84%E8%80%83%E7%A0%94%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://github.com/posts/%E8%AE%A1%E7%BB%84%E8%80%83%E7%A0%94%E7%AC%94%E8%AE%B0/</guid><description>计算机组成原理笔记</description><pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;计算机组成原理&lt;/h1&gt;
&lt;h2&gt;1. 计算机系统概述&lt;/h2&gt;
&lt;h3&gt;1. 计算机系统层次结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;冯诺依曼机的基本工作方式：==控制流驱动方式==&lt;/li&gt;
&lt;li&gt;在运算器中，不包含==地址寄存器==&lt;/li&gt;
&lt;li&gt;把汇编语言源程序转变为机器语言程序的过程是 ==汇编==&lt;/li&gt;
&lt;li&gt;==数据库系统==不是 系统软件&lt;/li&gt;
&lt;li&gt;==相联存储器==既可以==按地址寻址也可按内容寻址==&lt;/li&gt;
&lt;li&gt;指令和数据都以二进制形式存储在存储器中，CPU区分它们的依据是：==指令周期的不同阶段==&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解析：在取值阶段取的是指令，执行阶段取的是数据。容易选成 指令操作码的译码结果，需要注意的是，CPU只有在确定取出的是指令时，才会将其操作码送去译码。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;计算机硬件能够直接执行的是 ==机器语言==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 计算机的性能指标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在CPU的寄存器中，==指令寄存器== 对用户是完全透明的。&lt;/li&gt;
&lt;li&gt;CPU 的 CPI 与==时钟频率==无关，与==系统结构，计算机组织，指令集==有关&lt;/li&gt;
&lt;li&gt;==ALU和通用寄存器==的位数一定与机器字长相同&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;各种关系的转换&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;MIPS = 时钟频率/CPI&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;程序执行时间 = CPI * 时钟周期 * 指令数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;指令周期 = 1 / 指令执行速度&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==微机逻辑电路相同，指的是 CPI 相同， 时钟周期不一定相同==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 数据的表示和运算&lt;/h2&gt;
&lt;h3&gt;1. 数制和编码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;如果没有特殊说明，BCD码指的是 ==8421码==&lt;/li&gt;
&lt;li&gt;真值0 表示惟一的机器数是 ==补码和移码==&lt;/li&gt;
&lt;li&gt;若定点整数为64位，含一位符号位，则用补码表示的绝对值最大的负数为 ==-2^63==&lt;/li&gt;
&lt;li&gt;一般用移码表示浮点数的阶，而补码表示定点整数&lt;/li&gt;
&lt;li&gt;==补码符号位取反获得移码==&lt;/li&gt;
&lt;li&gt;补码进行运算时带上符号位&lt;/li&gt;
&lt;li&gt;二进制转八进制，十六进制时，小数部分也是从右往左算&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://typora-dzsq.oss-cn-hangzhou.aliyuncs.com/picgoimages-master/img/image-20220124172619709.png&quot; alt=&quot;image-20220124172619709&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2. 运算方法和运算电路&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ALU属于==组合逻辑电路==&lt;/li&gt;
&lt;li&gt;在串行进位的并行加法器中，影响加法器运算速度的关键因素是 ==进位传递延迟==&lt;/li&gt;
&lt;li&gt;加法器中每位的 进位信号为 ==XY==，进位传递信号 ==X⊕Y==&lt;/li&gt;
&lt;li&gt;模4补码（即双符号位）存储时只需要==一个符号位== ，因为一个正确的数值，符号位是相同的&lt;/li&gt;
&lt;li&gt;实现 N位（不包含符号位） 补码一位乘时，乘积为==2N+1==位&lt;/li&gt;
&lt;li&gt;在算术移位的情况下，补码左移的前提条件是 ==其原最高有效位和原符号位要相同==&lt;/li&gt;
&lt;li&gt;C语言中变量都以补码形式存储，int 四字节，short 两字节，char 一字节&lt;/li&gt;
&lt;li&gt;加法器的 低位进位输入信息 ：==如果是减法为1，加法为 0==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 浮点数的表示和运算&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基数（小数点前边的那个数）越大，表示的范围越大，精度越低&lt;/li&gt;
&lt;li&gt;关于规格化&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;原码正数： 0.1……        原码负数：1.1……

补码正数： 0.1……        补码负数：1.0……
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在对阶操作中，==不存在阶码减小，位数左移的情况==。因为阶码小的要向阶码大的对齐。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;采用规格化浮点数主要是为了==增加数据的表示精度==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;8421码是十进制数的编码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==原码表示的规格化小数是小数点后两位（基数为4，用两位表示）不全为0的小数==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;定点数没有舍入的概念，只有浮点数才有&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==浮点数舍入的情况有两种：1. 对阶      2. 右规格化==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;舍入不一定产生误差&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;float类型（IEEE754 单精度浮点数格式）能表示的最大正整数是 ==2^128 - 2^104==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尾数溢出时，可能只产生误差，结果不一定溢出&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对阶操作不会引起阶码上溢或下溢&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3.存储器系统&lt;/h2&gt;
&lt;h3&gt;1. 存储器概述&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;磁盘属于==直接存取存储器（DAM）==&lt;/li&gt;
&lt;li&gt;存储器的存取周期是指 ==存储器进行连续存或写操作所允许的最短时间间隔==&lt;/li&gt;
&lt;li&gt;相联存储器是按 ==内容指定方式和地址指定方式相结合==进行寻址的存储器&lt;/li&gt;
&lt;li&gt;多级存储系统是为了==降低存储成本==    &lt;strong&gt;易错&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;虚拟存储器中主存和辅存之间的数据调动对 ==应用级程序员== 是透明的&lt;/li&gt;
&lt;li&gt;cache / 主存系统的效率 e = 访问Cache的时间 / 平均访存时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;公式  = 1/ [H + （1-H）*r]   H为命中率，r 为主存访问时间除以 Cache访问时间&lt;/p&gt;
&lt;h3&gt;2. 主存储器&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;芯片的引脚数除了数据线和地址线，还有 ==一根片选线和读写控制线（一根和两根都有可能）==&lt;/li&gt;
&lt;li&gt;DRAM的刷新是以==行==为单位的&lt;/li&gt;
&lt;li&gt;DRAM在==分散刷新==时，不存在死时间&lt;/li&gt;
&lt;li&gt;DRAM和SRAM 对比&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;==DRAM采用地址复用技术，因此地址线只有一半==&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://typora-dzsq.oss-cn-hangzhou.aliyuncs.com/picgoimages-master/img/image-20220130154140182.png&quot; alt=&quot;image-20220130154140182&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;==采用地址复用技术时，数据线减半，但会多增加行通选线和列通选线（片选线用行通选代替）==&lt;/li&gt;
&lt;li&gt;==Cache是易失性存储器==，falsh存储器不是易失性存储器&lt;/li&gt;
&lt;li&gt;U盘属于==只读存储器==&lt;/li&gt;
&lt;li&gt;RAM和ROM都是采用==随机存取方式==进行信息访问&lt;/li&gt;
&lt;li&gt;闪存：读速度比写快；是一种半导体存储器；非易失性存储器；随机访问方式&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 外部存储器&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;RAID技术==不可以==提高磁盘的磁记录密度&lt;/li&gt;
&lt;li&gt;固态硬盘（SSD）==容易磨损==&lt;/li&gt;
&lt;li&gt;磁盘平均存取时间：访道时间 + 访问扇区时间 + 扫描扇区时间&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 虚拟存储器&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;虚拟存储管理系统应该==具有较好的局部性==，此理论的基本含义是==在程序的执行过程中，程序对主存的访问是不均匀的==&lt;/li&gt;
&lt;li&gt;==段页式==和页式交换的单位都是==页==&lt;/li&gt;
&lt;li&gt;虚拟存储器对应用程序员透明，对系统程序员不透明&lt;/li&gt;
&lt;li&gt;快表（TLB）存放在寄存器中，慢表存放在 主存中&lt;/li&gt;
&lt;li&gt;TLB 存放 Page（页表）的一部分，Cache 存放 主存的一部分。&lt;/li&gt;
&lt;li&gt;Cache使用直写方式 需要把数据同时写入 cache 和 主存&lt;/li&gt;
&lt;li&gt;==缺页处理完成后回到发生缺页的指令继续执行==&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 指令系统&lt;/h2&gt;
&lt;h3&gt;1. 指令格式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指令系统是计算机软/硬件的界面&lt;/li&gt;
&lt;li&gt;指令系统和机器语言是相关的&lt;/li&gt;
&lt;li&gt;程序控制类指令的功能是==改变程序执行的顺序==&lt;/li&gt;
&lt;li&gt;堆栈计算机中，零地址指令的操作数来自 ==堆栈的栈顶和次栈顶单元==&lt;/li&gt;
&lt;li&gt;单字长指令可加快取指令的速度&lt;/li&gt;
&lt;li&gt;计算机按字节编制，指令的位数必须是8的倍数&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 指令的寻址方式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指令系统中采用不同的寻址方式的目的是==缩短指令字长，扩大寻址空间，提高编程的灵活性==&lt;/li&gt;
&lt;li&gt;无条件转移指令（是指程序转移到新的地址后继续执行）的功能是将指令中的地址码送入==程序计数器（PC）==&lt;/li&gt;
&lt;li&gt;为了缩短指令中某个地址段的位数，最有效的方法是采取==寄存器寻址==&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为寄存器的数量较小，使用较少的位数即可全部表示&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;==基址寻址有利于多道程序设计和编制浮动程序，变址寻址有利于处理数组问题和编制循环程序==&lt;/li&gt;
&lt;li&gt;堆栈寻址方式中，进栈时：先放入数据，SP（指向栈顶单元的指针）再减一   出栈时：SP先加一，在弹出数据&lt;/li&gt;
&lt;li&gt;相对寻址中，相对地址实际上是以==下条指令==在内存中的首地址为基准位置的偏移量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为在执行本条指令时，PC已完成加一操作&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;==采用跳跃寻址时，可以实现程序的无条件浮动和条件浮动==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;指令寄存器寻址和指令寄存器间接寻址所需的指令码长度最短，立即寻址，直接寻址，间接寻址方式的指令码长度最长&lt;/p&gt;
&lt;p&gt;寄存器寻址方式执行速度最快，间接寻址方式的执行速度最慢&lt;/p&gt;
&lt;p&gt;若指令系统采用定长指令码格式，则立即寻址方式执行速度最快。若采用变长指令码格式时，寄存器寻址方式执行速度最快&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.程序的机器级代码表示&lt;/h3&gt;
&lt;p&gt;两数相加寄存器变化：&lt;/p&gt;
&lt;p&gt;OF（溢出标志位，over flag）： 没有溢出为0&lt;/p&gt;
&lt;p&gt;SF（符号标志位，symbol flag）：看符号位&lt;/p&gt;
&lt;p&gt;CF（进位标志位，carry flag）：sub⊕C      （做减法时sub为1，C作为进位输出）&lt;/p&gt;
&lt;p&gt;ZF（零标志位，zero flag）相关指令执行后结果为0那么  ZF=1,结果不为0则 ZF=0；&lt;/p&gt;
&lt;h2&gt;5. 中央处理器&lt;/h2&gt;
&lt;h3&gt;1. CPU的功能和基本结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CPU分为运算器和控制器，控制器包括==IR（指令寄存器），MAR，MDR，PC，指令译码器，时序电路，微操作信号发生器==&lt;/li&gt;
&lt;li&gt;条件转移指令执行时所依据的条件来自==标志寄存器==&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要对标志寄存器的内容进行测试，看是否满足转移条件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在CPU的寄存器中，==指令寄存器==对用户是透明的。汇编语言程序员可见的是==PC==&lt;/li&gt;
&lt;li&gt;在一条无条件跳转指令的指令周期内，PC的值被修改 ==2== 次。&lt;/li&gt;
&lt;li&gt;指令寄存器的位数取决于 ==指令字长==&lt;/li&gt;
&lt;li&gt;CPU中通用寄存器的位数取决于==机器字长（计算机能直接处理的二进制位数，决定了计算机的运算精度）==，可以存放数据和地址，但不能存放指令。&lt;/li&gt;
&lt;li&gt;指令译码是对 ==指令的操作码字段== 进行译码&lt;/li&gt;
&lt;li&gt;间址周期（取操作数的有效地址）结束时，MDR的内容为 ==操作数地址==&lt;/li&gt;
&lt;li&gt;CPU中的专用寄存器：IR，PC，MAR，MDR，PSW（程序状态字寄存器）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 指令执行过程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;指令==总是根据程序计数器==从主存中取出，即使有转移指令，也是从PC中取出&lt;/li&gt;
&lt;li&gt;取指令操作是控制器自动运行的&lt;/li&gt;
&lt;li&gt;不同指令周期所包含的机器周期数可能不同&lt;/li&gt;
&lt;li&gt;只有出现中断请求时才会进入中断周期，每个指令周期不一定都包含一个中断周期&lt;/li&gt;
&lt;li&gt;==不同长度的指令，取值操作不同。如果指令长度相同，取址操作相同。==&lt;/li&gt;
&lt;li&gt;==指令字长和机器字长的长度没有任何关系==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 数据通路的功能和基本结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;采用CPU内部总线方式的数据通路的特点：==结构简单，容易实现，性能较低，存在较多的冲突现象==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.控制器的功能和工作原理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在微程序控制器中，形成微程序入口地址的是==机器指令的操作码地段==&lt;/li&gt;
&lt;li&gt;微程序控制器的速度比硬布线控制器慢，主要是因为==增加了从控制存储器读取微指令的时间==&lt;/li&gt;
&lt;li&gt;与硬布线控制器相比，==微程序控制器的时序系统比较简单==&lt;/li&gt;
&lt;li&gt;水平型微指令位数较多，较复杂；垂直型微指令位数较少，较简单，一次只能完成一次基本操作&lt;/li&gt;
&lt;li&gt;==水平型微指令能充分利用数据通路的并行结构==&lt;/li&gt;
&lt;li&gt;若指令系统中具有n种机器指令，则控制存储器中的微程序数至少是 ==n+2==个，一个是公共的取指令，另一个对应中断的微程序&lt;/li&gt;
&lt;li&gt;微机的CPU都是微处理器，和采用哪种控制器没有关系&lt;/li&gt;
&lt;li&gt;控制存储器采用==ROM==实现，按地址访问，容量是2的幂次方&lt;/li&gt;
&lt;li&gt;除控制部件（控制器）之外的部件都是执行部件&lt;/li&gt;
&lt;li&gt;==水平型微指令由操作控制字段，判别测试字段和下地址字段组成==。例题是大题的第四，第五题。（判别测试字段还不知道怎么求）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.异常和中断机制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;主存故障引起的中断是==硬件中断==&lt;/li&gt;
&lt;li&gt;异常事件在当前指令执行过程中进行检测，中断请求在当前指令执行后进行检测&lt;/li&gt;
&lt;li&gt;自陷是通过陷阱指令预先设定的一类内部中断事件&lt;/li&gt;
&lt;li&gt;中断是指来自CPU执行指令以外事件，异常也称内中断&lt;/li&gt;
&lt;li&gt;==访存时缺页属于异常==（易错）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 指令流水线&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;流水线的冒险&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;结构冒险：资源冲突&lt;/li&gt;
&lt;li&gt;数据冒险：数据冲突（RAW，WAR，WAW）&lt;/li&gt;
&lt;li&gt;控制冒险：改变指令执行顺序&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;==流水线的吞吐率==：n/(k+n-1)*时钟周期&lt;/li&gt;
&lt;li&gt;==流水线的加速比==：kn/(k+n-1)&lt;/li&gt;
&lt;li&gt;超标量流水线的实质是==以空间换时间==，不是指运算操作并行&lt;/li&gt;
&lt;li&gt;采用==旁路技术==可解决数据相关问题&lt;/li&gt;
&lt;li&gt;==流水线数据通路由控制部件控制，但不包含控制部件==&lt;/li&gt;
&lt;li&gt;==理想情况下，单周期CPU，基本流水线CPU的CPI为1，多周期CPU的CPI&amp;gt;1,超标量流水线CPU的CPI&amp;lt;1.==&lt;/li&gt;
&lt;li&gt;流水线采用==“按序发射，按序完成”==方式，==下一条指令的IF（取指操作）必须和上一条指令的ID（译码）并行==，以免因上一条指令发生冲突而导致下一条指令先执行完。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7. 多处理器的基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;单指令单数据流结构（SISD）；多指令多数据流结构（MIMD）&lt;/li&gt;
&lt;li&gt;==多处理机属于MIMD==&lt;/li&gt;
&lt;li&gt;双核是指在CPU上集成两个运算核心&lt;/li&gt;
&lt;li&gt;NUMA构架比UMA架构的运算扩展性要强&lt;/li&gt;
&lt;li&gt;UMA架构需要解决的重要问题是Cache的一致性&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6.总线&lt;/h2&gt;
&lt;h3&gt;6.1 总线概述&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;总线上的部件，==可以分时向总线发送数据，但可同时从总线接受数据==&lt;/li&gt;
&lt;li&gt;在总线上，==同一时刻只能有一个主设备控制总线传输操作==&lt;/li&gt;
&lt;li&gt;使用总线结构便于增减外设，==同时减少信息传输线的条数==&lt;/li&gt;
&lt;li&gt;系统总线中地址线的功能是 ==指定主存和 I/O设备接口电路的地址==&lt;/li&gt;
&lt;li&gt;不同信号在同一条信号线上分时传输的方式称作 ==总线复用方式==&lt;/li&gt;
&lt;li&gt;采用局部总线技术的作用是 ==节省系统的总带宽==&lt;/li&gt;
&lt;li&gt;==突发式（猝发式）发送可以连续传送地址连续的数据==&lt;/li&gt;
&lt;li&gt;USB是串行传输，不能同时传输2位数据； 可通过级联方式连接多台外设&lt;/li&gt;
&lt;li&gt;用于设备和设备控制器（I/O接口）之间互联的接口标准是 USB&lt;/li&gt;
&lt;li&gt;多总线结构中，总线之间通过==桥接器==连接&lt;/li&gt;
&lt;li&gt;PCI—Express采用串行数据报方式传送数据&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 总线事务和定时&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;==同步通信比异步通信的传输效率高==&lt;/li&gt;
&lt;li&gt;在异步总线中，传送操作==按需分配时间==、&lt;/li&gt;
&lt;li&gt;同步总线由统一的时序信号控制，异步总线由握手信号定时&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. 输入输出系统&lt;/h2&gt;
&lt;h3&gt;1. I/O系统基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;==I/O指令是CPU系统指令的一部分==，格式和其他通用指令相比有所不同&lt;/li&gt;
&lt;li&gt;==通道程序放在主存中，由通道执行==&lt;/li&gt;
&lt;li&gt;键盘，鼠标等输入设备一般都采用==中断方式==来实现&lt;/li&gt;
&lt;li&gt;打印机从打字原理的角度来分析，可分为击打式和非击打式；==按能打出汉字来分，可分为点阵式打印机和活字式打印机==&lt;/li&gt;
&lt;li&gt;计算机中一个汉字内码在主存中占用 ==2B==， 输出的字型码 16*16点阵在缓冲存储区中占用 ==32B==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. I/O接口&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在统一编制方式下，==区分存储单元和I/O设备是靠不同的地址码==，进行输入/输出的操作指令是==访存指令==&lt;/li&gt;
&lt;li&gt;I/O接口的功能也包括==I/O过程中的错误和状态检测==&lt;/li&gt;
&lt;li&gt;磁盘驱动器向盘片磁道记录数据时采用==串行方式==写入&lt;/li&gt;
&lt;li&gt;I/O指令实现的数据传送通常发生在==通用寄存器和I/O端口之间==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. I/O方式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在总线仲裁方式中，==独立请求方式响应时间最快，是以增加控制线数为代价的==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==中断向量是中断服务程序入口地址，中断向量地址是中断服务程序入口地址的地址==（重要）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以提出中断的有 ==外部事件，虚拟存储器失效，浮点数运算上溢==， 浮点数运算下溢不能引起中断&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DMA的优先级比程序中断的优先级高&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;能产生DMA请求的总线部件是==具有DMA接口的设备==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;中断优先级从高到低：访管-&amp;gt;程序性-&amp;gt; 重新启动&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==检测有无DMA请求，一般在存取周期结束后，而不是在指令周期结束后==（易错）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置中断屏蔽标志可以改变==多个中断服务程序执行完的次序==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==中断方式的特点是 CPU与外设并行工作，传送与主程序串行工作==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CPU响应DMA请求的条件是当前==机器周期==执行完&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;中断发生时，程序计数器内容的保护和更新是由==硬件自动==完成的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在DMA方式下，数据从内存传送到外设经过的路径是==内存-&amp;gt;数据总线-&amp;gt;DMAC（DMA的数据缓冲寄存器）-&amp;gt;外设==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DMA传送前由设备驱动程序设置传送参数，==传送结束后的处理由中断服务程序完成==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>计算机网络笔记</title><link>https://github.com/posts/%E8%AE%A1%E7%BD%91%E8%80%83%E7%A0%94%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://github.com/posts/%E8%AE%A1%E7%BD%91%E8%80%83%E7%A0%94%E7%AC%94%E8%AE%B0/</guid><description>计算机网络笔记</description><pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;计算机网络&lt;/h1&gt;
&lt;h2&gt;1.计算机网络体系结构&lt;/h2&gt;
&lt;h3&gt;1. 计算机网络概述&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;计算机网络最基本的功能是==数据通信==&lt;/li&gt;
&lt;li&gt;局域网和广域网的主要差异不仅在于范围，还有==所使用的协议==&lt;/li&gt;
&lt;li&gt;局域网和广域网的互联是通过==路由器==实现的&lt;/li&gt;
&lt;li&gt;计算机网络拓扑结构主要取决于它的==通信子网==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.计算机网络体系结构与参考模型&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;提供标准语言，定义标准界面，增强功能之间的独立性是对网络模型进行分层的目标，定义功能执行的方法则不是&lt;/li&gt;
&lt;li&gt;协议是控制两个（或多个）==对等实体==进行通信的规则的集合，==是水平的==。&lt;/li&gt;
&lt;li&gt;协议是指==不同节点对等实体==之间进行通信的规则或约定。&lt;/li&gt;
&lt;li&gt;服务，接口，协议是OSI参考模型的三个主要概念&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;传输单位&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;th&gt;协议&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;应用层&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;表示层&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;格式转换及压缩&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;会话层&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;校验点，进程之间的对话&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;传输层&lt;/td&gt;
&lt;td&gt;数据报/报文段&lt;/td&gt;
&lt;td&gt;分组排序，差错控制，流量控制，端到端连接，可靠传输&lt;/td&gt;
&lt;td&gt;TCP，UDP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;网络层&lt;/td&gt;
&lt;td&gt;分组&lt;/td&gt;
&lt;td&gt;路由选择，流量控制，差错控制，拥塞控制&lt;/td&gt;
&lt;td&gt;IP，ICMP，ARP，RARP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据链路层&lt;/td&gt;
&lt;td&gt;帧&lt;/td&gt;
&lt;td&gt;差错控制，流量控制 ，访问控制，帧定界功能&lt;/td&gt;
&lt;td&gt;PPP，HDLC，SLIP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;物理层&lt;/td&gt;
&lt;td&gt;比特&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;（常考点）==ISO/OSI参考模型在网络层支持无连接和面向连接的通信，但在传输层仅支持面向连接的通信；TCP/IP模型在网络层仅有无连接的通信，在传输层支持无连接和面向连接的通信。==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（易错点）在传输层将数据分割为==报文==后送入会话层。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;只有传输层及以上各层的通信才能称为端到端。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;因特网采用的核心技术是==TCP/IP==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;应用层的服务访问点也称==用户界面==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（重要）考研中层数一般是从下往上数的。物理层为第一层，应用层为第七层。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==路由器：网络层，交换机：数据链路层，集线器：物理层==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;协议与服务有何区别，有何联系？&lt;/p&gt;
&lt;p&gt;协议是控制两个对等实体之间通信的规则的集合。在协议的控制下，两个对等实体间的通信使得本层能够向上一层提供服务，而要实现本层协议，还需要使用下一层提供的服务。&lt;/p&gt;
&lt;p&gt;协议和服务概念上的区分：&lt;/p&gt;
&lt;p&gt;（1）协议的实现保证了能够向上一层提供服务。本层的服务用户只能看见本层的服务而无法看见下边的协议，即下边的协议对上层的服务用户是透明的。&lt;/p&gt;
&lt;p&gt;（2）协议是“水平”的，即协议是控制两个对等实体之间的通信的规则。但服务是“垂直”的，即服务是由下层通过层间接口向上层提供的。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2.物理层&lt;/h2&gt;
&lt;h3&gt;1. 通信基础&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;曼彻斯特编码最适合传输==二进制数字信号====。位中间的跳变作为时钟信号，每个码元的电平作为数据信号。因此，时钟和数据取值都包含在信号中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;QAM是一种用==模拟信号传输数字数据==的编码方式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把语音信号数字化的技术是==脉冲编码调制==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;波特率表示信号每秒变化的次数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==以太网采用曼彻斯特编码，分组交换技术==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;由于==报文交换== 数据经过网络的传输延迟长而且不固定，因此不能用于 语音数据传输。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（重要）==传输时延最小，电路交换==；为保证数据无差错的传送，不应该用电路交换；分组交换对报文交换的主要改进是==传输单位更小且有固定的最大长度==；在==出错率很高的传输系统中，选用数据报方式更合适==。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==虚电路是面向连接的，它提供的是一种可靠的服务，==分组中携带虚电路标识，保证分组有序到达。有建立连接，数据传输和连接拆除三个阶段。由网络负责差错控制和流量控制。不需要为每条虚电路预分配带宽。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==数据报服务中，网络为每个分组独立地选择路由，传输不保证可靠性，不保证分组的按序到达。==每个分组在传输过程中都必须携带源地址和目标地址。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NRZ一般指不归零码。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;比较分组交换和报文交换，并说明分组交换的原因？（P45）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 传输介质&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;双绞线绞合的目的是减少两根导线相互的电磁干扰。&lt;/li&gt;
&lt;li&gt;同轴电缆比双绞线的传输速率更快，得益于==同轴电缆具有更高的屏蔽性，同时有更好的抗噪声性。==&lt;/li&gt;
&lt;li&gt;如果单模光纤的直径减少到只有光的一个波长大小，那么光沿直线传播。&lt;/li&gt;
&lt;li&gt;==电气特性规定了信号的电压高低，传输距离等。功能特性指明某条线上出现的某一电平的电压表示何种含义。==&lt;/li&gt;
&lt;li&gt;出现的传输介质有 ==双绞线，同轴电缆，光纤==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 物理层设备&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;中继器的主要作用是将信号整形并放大再转发出去，其==原理是信号再生==（不是放大）。端口只作用于信号的电气部分。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;放大器和中继器的区别：放大器放大的是模拟信号，原理是将衰减的信号放大。中继器放大的是数字信号，原理是再生。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==“5-4-3规则”==：在采用粗同轴电缆的10BASE5以太网规范中，互相串联的中继器的个数不超过4个，而且4个中继器串联的5段通信介质中只有3段可以挂接计算机。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==集线器实质上是一个多端口的中继器。==连接的网络在拓扑结构上属于星形。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;转发器的作用是放大信号。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;两个网段在==物理层进行互联时要求数据传输速率要相同，但数据链路层协议可以不相同。==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用集线器连接的工作站集合同属一个冲突域，也同属一个广播域。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3.数据链路层&lt;/h2&gt;
&lt;h3&gt;1. 数据链路层的功能&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;数据链路层的功能包括==组帧（定义数据格式）==，提供结点之间的可靠传输，控制对物理传输介质的访问。不包括为终端结点隐藏物理传输的细节。&lt;/li&gt;
&lt;li&gt;为了避免传输过程中帧的丢失，数据链路层采用==计时器超时重发==&lt;/li&gt;
&lt;li&gt;数据链路层提供==无确认无连接，有确认无连接，有确认面向连接的服务。==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 差错控制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;提高信噪比可以减弱其影响的差错是==随机差错==&lt;/li&gt;
&lt;li&gt;奇偶校验码只能检测出奇数个比特错误，且不能纠错。&lt;/li&gt;
&lt;li&gt;CRC校验码能检测出所有单比特错误，不能纠错。&lt;/li&gt;
&lt;li&gt;==海明码纠错d位，需要码距为2d+1的编码方案，检错d位，需要码距 d+1。==&lt;/li&gt;
&lt;li&gt;==CRC检验码的位数等于生成多项式G（x）的最高次数。==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 流量控制与可靠传输机制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;后退N帧协议（GBN）一般采用累积确认方式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在连续ARQ（自动重传请求）协议中，发送窗口的大小&amp;lt;= 窗口总数-1.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;停止等待协议：发送窗口=接收窗口=1 ； 后退N帧协议：发送窗口&amp;gt;1,接收窗口=1 ；选择重传协议：发送窗口&amp;gt;1, 接收窗口 &amp;gt; 1&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==信道利用率 = 帧发送时间/发送周期==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 介质访问控制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;位速率又叫比特率，是指在单位时间内可以传输多少数据。&lt;/li&gt;
&lt;li&gt;TDM（时分多路复用）所用传输介质的性质是介质的位速率大于单个信号的位速率。&lt;/li&gt;
&lt;li&gt;TDM可用于数字传输而FDM（频分多路复用）不行。&lt;/li&gt;
&lt;li&gt;统计时分多路复用具有动态分配时隙的功能。&lt;/li&gt;
&lt;li&gt;在CSMA/CD协议的定义中，==‘’争议期‘’指的是信号在最远两个端点之间往返传输的时间==&lt;/li&gt;
&lt;li&gt;介质访问冲突，使用二进制回退法的理由是==这种算法考虑了网络负载对冲突的影响==&lt;/li&gt;
&lt;li&gt;与CSMA/CD网络相比，令牌环网更适合的环境是==负载重==&lt;/li&gt;
&lt;li&gt;多路复用器的主要功能是==结合来自两条或多条线路的传输，以充分利用信道==&lt;/li&gt;
&lt;li&gt;==对正确接收到的数据帧进行确认的MAC协议是 CSMA/CA==&lt;/li&gt;
&lt;li&gt;CSMA/CD适用于有线网络，CSMA/CA适用于无线局域网&lt;/li&gt;
&lt;li&gt;CSMA/CA进行信道预约的方法是==交换RTS与CTS帧。==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6. 局域网&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;以太网是局域网的一种&lt;/li&gt;
&lt;li&gt;以太网参考模型一般只包括物理层和数据链路层&lt;/li&gt;
&lt;li&gt;网卡实现的主要功能在==物理层和数据链路层==&lt;/li&gt;
&lt;li&gt;同一局域网中两个设备具有相同的静态MAC地址，会发生==在网络上这两个设备都不能正确通信==&lt;/li&gt;
&lt;li&gt;同轴电缆作为传输机制，无中继情况下，传输介质的最大长度不超过500m&lt;/li&gt;
&lt;li&gt;==10吉比特以太网只能工作在全双工模式下==&lt;/li&gt;
&lt;li&gt;实现&quot;给帧加序号&quot;功能的层次是==逻辑链路控制子层（LLC）==&lt;/li&gt;
&lt;li&gt;==无线局域网不需要在发送过程中进行冲突检测==&lt;/li&gt;
&lt;li&gt;在一个以太网中，有A,B,C,D四台主机，若A向B发送数据，则四台主机都能接收到数据。&lt;/li&gt;
&lt;li&gt;吉比特以太网同时支持全双工模式和半双工模式&lt;/li&gt;
&lt;li&gt;==虚拟局域网中的计算机可以处于不同的局域网中==&lt;/li&gt;
&lt;li&gt;提高网络安全性是虚拟局域网（VLAN）的优点，链路聚合则不是&lt;/li&gt;
&lt;li&gt;以太网的MAC协议提供的是无连接不可靠的服务&lt;/li&gt;
&lt;li&gt;10BaseT使用的传输介质是双绞线&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7.广域网&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;广域网所采用的传输方式是==存储转发式==&lt;/li&gt;
&lt;li&gt;为实现透明传输，PPP使用的填充方法是==字符填充==&lt;/li&gt;
&lt;li&gt;==PPP协议具有差错控制能力，支持动态分配IP地址，支持身份验证==等功能&lt;/li&gt;
&lt;li&gt;PPP协议提供的功能有一种成帧方式，链路控制协议，网络控制协议&lt;/li&gt;
&lt;li&gt;PPP协议中LCP帧的作用是==在建立状态阶段协商数据链路协议的选项==&lt;/li&gt;
&lt;li&gt;PPP是面向字节的协议，HDLC是面向比特的协议&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8. 数据链路层设备&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;网桥和以太网交换机工作在数据链路层&lt;/li&gt;
&lt;li&gt;VLAN不仅可以隔离冲突域，也可以隔离广播域&lt;/li&gt;
&lt;li&gt;减少冲突域的范围是网桥分割网络所带来的好处，缩小了广域网的范围则不是&lt;/li&gt;
&lt;li&gt;路由器的传播延时相较于集线器，网桥，交换机最大。&lt;/li&gt;
&lt;li&gt;交换机比集线器提供更好的网络性能的原因是==交换机支持多用户同时通信==&lt;/li&gt;
&lt;li&gt;通过交换机连接的一组工作站==组成一个广播域，但不是一个冲突域==&lt;/li&gt;
&lt;li&gt;交换机的多个端口可以并行传输，根据MAC地址进行转发&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 网络层&lt;/h2&gt;
&lt;h3&gt;1. 网络层的功能&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;网络层的主要目的是==在任意节点间进行数据报传输==&lt;/li&gt;
&lt;li&gt;路由器连接的异构网络是指==数据链路层和物理层均不同==&lt;/li&gt;
&lt;li&gt;网络中发生了拥塞，其根据是==随着通信子网负载的增加，吞吐量反而降低==&lt;/li&gt;
&lt;li&gt;在路由器互联的多个局域网的结构中，要求==每个局域网物理层，数据链路层，网络层的协议可以不同，而网络层以上的高层协议必须相同==&lt;/li&gt;
&lt;li&gt;在因特网中，一个路由器中的路由表，通常包含==目的网络和到达该目的网络路径上的下一个路由器的IP地址==&lt;/li&gt;
&lt;li&gt;路由器转发分组的根据是报文的==IP地址==&lt;/li&gt;
&lt;li&gt;属于网络层协议的是IP和ICMP协议&lt;/li&gt;
&lt;li&gt;软件定义网络（SDN）由远程控制器计算出最佳路由，路由器之间不再相互交换路由信息&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 路由算法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在链路状态路由算法中，发送的链路状态信息，包括该路由器的相邻路由器及所有相邻链路的状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在距离向量路由协议中，好消息传的快，而坏消息传的慢。就导致了当路由信息发生变化时，该变化未能及时的被所有路由器知道。而仍然可能在路由器之间进行传递，这就是慢收敛现象。==慢收敛是导致发生路由回路的根本原因。==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. IPV4&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;协议字段表示使用IP的上层协议，如值为六，表示tcp，值为17表示udp。版本字段表示IP的版本，值为4表示ipv4，值为6表示ipv6。&lt;/li&gt;
&lt;li&gt;IP分组中的检验字段检查范围是==仅检查分组首部==。&lt;/li&gt;
&lt;li&gt;当数据报到达目的网络后，传送到目的主机需要知道==IP地址对应的物理地址==。&lt;/li&gt;
&lt;li&gt;在IP首部的字段中，与分片和重组无关的字段是总长度。&lt;/li&gt;
&lt;li&gt;==IP分组头中与分片与组装相关的字段是标识标志与片偏移==&lt;/li&gt;
&lt;li&gt;片偏移的单位是8B&lt;/li&gt;
&lt;li&gt;==组播的地址是保留的D类地址从224.0.0.0—239.255.255.255==&lt;/li&gt;
&lt;li&gt;==主机号不全为0或者1就是单播地址==&lt;/li&gt;
&lt;li&gt;IP数据报从源结点到目的节点可能需要经过多个网络和路由器，在整个传输过程中，==IP数据报头部中原地址和目的目的地址都不会发生变化，MAC地址会随着信息被发往不同的网络而改变==&lt;/li&gt;
&lt;li&gt;把IP网络划分成子网，这样做的好处是减少广播域的大小。&lt;/li&gt;
&lt;li&gt;如果一台主机有两个或两个以上的IP地址，那么说明这台主机属于两个或两个以上的逻辑网络。&lt;/li&gt;
&lt;li&gt;CIDR网络的作用是把小的网络汇聚成大的超网。&lt;/li&gt;
&lt;li&gt;IP分组可以被路径中的路由器分片，并在目的主机进行重组。&lt;/li&gt;
&lt;li&gt;==路由器在转发IP数据报时重新封装源硬件地址和目的硬件地址。==&lt;/li&gt;
&lt;li&gt;可以动态为主机配置IP地址的协议是DHCP。&lt;/li&gt;
&lt;li&gt;PING使用了ICMP的询问报文中的回送请求和回答报文。&lt;/li&gt;
&lt;li&gt;ICMP差错报告报文有五种：终点不可达，原点抑制，时间超过，参数问题，改变路由（重定向）。==其中原点抑制是指在路由器或主机由于拥塞而丢弃数据报时，让源点发送原点抑制报文，使源点知道应当把数据报的发送速率放慢。==&lt;/li&gt;
&lt;li&gt;ARP的功能是根据IP地址查询mac地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. IPV6&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;与IPV4相比，IPV6减少头部字段，没有提供检验和字段。&lt;/li&gt;
&lt;li&gt;IPV6不允许分片。&lt;/li&gt;
&lt;li&gt;如果一个路由器收到的IPV6数据报因太大而不能转发到链路上，那么路由器将把该数据报==丢弃==。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 路由协议&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;采用分层次划分区域的方法，虽然使==交换信息的种类增多==了，同时也使==OSPF协议更加复杂==了，但这样做却能使每个区域内部==交换路由信息的通信量大大减少==。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在计算机网络中，路由选择协议的功能==不包括发现线下一跳的物理地址==。发现下一跳的物理地址一般是通过其他方式（如ARP）实现的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;所谓==收敛是指当路由环境发生变化后，各路由器调整自己的路由表以适应网络拓扑结构的变化，最终达到稳定状态（路由表与网络拓扑结构保持一致）==。收敛越快，路由器就能越快适应网络拓扑结构的变化。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==RIP是应用层协议，它使用UDP发送数据。OSPF是网络层协议。==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OSPF协议使用==hello分组==来保持与其邻居的连接。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BGP交换的网络可达性信息是==到达某个网络所经过的路径==。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==直接封装RIP，OSPF，BGP报文的协议分别是UDP， IP，TCP==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每经过一个路由器，IP分组的TTL值减1。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.IP组播&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;用多个单播仿真一个组播时，路由器的时延将很大，而处理一个组播分组的时延很小。&lt;/li&gt;
&lt;li&gt;在设计组播路由时，为了避免路由环路，==构造组播转发树==&lt;/li&gt;
&lt;li&gt;因特网的组播是靠路由器来实现的，这些路由器必须增加一些能够识别组播的软件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7. 移动IP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;移动IP的基本工作过程可以分为==代理发现，注册，分组路由与注销==四个阶段。&lt;/li&gt;
&lt;li&gt;节点在使用移动IP进行通信时，归属代理和外部代理之间需要建立一条隧道。&lt;/li&gt;
&lt;li&gt;移动IP为移动主机设置了两个IP地址，主地址和辅地址，==主地址固定，辅地址动态改变。==&lt;/li&gt;
&lt;li&gt;一台主机移动到另一个不属于它的子网的网络中，它将==既不可以直接接收分组，也不可以直接发送分组。==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8. 网络层设备&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;要控制网络上的广播风暴，可以采用的方法是==用路由器将网络分段==&lt;/li&gt;
&lt;li&gt;==路由器主要实现物理层，数据链路层与网络层的功能。==&lt;/li&gt;
&lt;li&gt;路由表中的分组转发部分由==交换结构，输入端口，输出端口==组成。&lt;/li&gt;
&lt;li&gt;路由器中计算路由信息的是路由选择处理机。&lt;/li&gt;
&lt;li&gt;==IP路由器检测到拥塞时，合理丢弃IP分组。==&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 传输层&lt;/h2&gt;
&lt;h3&gt;1. 传输层提供的服务&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;物理层，数据链路层，网络层属于通信子网&lt;/li&gt;
&lt;li&gt;传输层为==进程==之间提供逻辑通信&lt;/li&gt;
&lt;li&gt;传输层的面向连接服务的特性是==保证可靠和顺序交付==&lt;/li&gt;
&lt;li&gt;可靠传输协议中的可靠指的是==使用确认机制来保证传输的数据不丢失==&lt;/li&gt;
&lt;li&gt;==TCP和UDP分别拥有自己的端口号，他们互不干扰，可以共存于同一台主机==&lt;/li&gt;
&lt;li&gt;TCP报文的首部长度和IP数据报的首部长度都为20B&lt;/li&gt;
&lt;li&gt;若用户程序使用udp进行数据传输，则==应用层协议==必须承担可靠性方面的全部工作&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. UDP协议&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;UDP数据报中的长度字段包括==首部和数据部分的长度==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UDP数据报比IP数据报多提供了==端口功能==服务&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Udp的校验和功能不是必须的，可以不使用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==如果udp校验和计算结果为0，那么在校验和字段填充1==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==UDP数据报的伪首部包含了IP地址信息==，目的是通过数据校验，保证udp数据报正确的到达目的主机。该伪首部由源原和目的主机仅在校验和计算期间建立，==并不发送==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;UDP协议实现分用时，所依据的头部字段是==目的端口号==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. TCP协议&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;TCP报头长度为20~60B，其中固定部分为20B&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TCP检验和==伪首部==中IP分组头的协议字段为==6==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TCP的==确认号是指明接收方下一次希望收到的报文段的数据部分第1个字节的编号==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==报文段的序号是其数据部分第1个字节的编号==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为保证数据传输的可靠性，TCP采用了对==报文段==确认的机制&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TCP的滑动窗口协议中规定==重传分组的数量最多可以等于滑动窗口的大小==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（重要）在B发给A的报文中，seq值应该和A发向B的报文中的ack值相同&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当客户机收到服务器发送的FIN段，并向服务器发送ack段时，客户机的TCP状态变为==time_wait==，此时TCP连接还未释放，必须经过时间等待计数器设置的时间2MSL后，客户机才进入closed的状态。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7.应用层&lt;/h2&gt;
&lt;h3&gt;1. 网络应用模型&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;客户端是请求方，一旦连接建立，服务器就能响应客户端请求的内容，==服务器也能主动发送数据给客户端==，用于一些消息的通知，如一些错误的通知&lt;/li&gt;
&lt;li&gt;==客户机是面向用户的，服务器是面向任务的==&lt;/li&gt;
&lt;li&gt;P2P网络是指在互联网中由对等节点组成的一种覆盖网络，==是一种动态的逻辑网络==&lt;/li&gt;
&lt;li&gt;P2P通信模式是指==P2P网络中对等节点之间的直接通信能力==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 域名系统（DNS）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;（重要）域名与IP地址不具有一一对应的关系&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DNS是基于C/S模型的分布式系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==授权域名服务器==可以将其管辖的主机名转换为主机的IP地址&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以==使用传输层无连接服务的是DNS==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 文件传输协议FTP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;文件传输协议（FTP）的一个主要特征是==允许客户指明文件的类型和格式==&lt;/li&gt;
&lt;li&gt;在服务器端，==控制连接使用TCP的21号端口，数据连接使用TCP的20号端口==，而在客户端，==控制连接和数据连接的TCP端口号都是由客户端系统自动分配的==&lt;/li&gt;
&lt;li&gt;控制信息是带外传送的协议是FTP&lt;/li&gt;
&lt;li&gt;匿名FTP访问通常使用==anonymous==作为用户名&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 电子邮件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SMTP，POP3都是基于传输层的==TCP协议==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用户代理使用==POP3协议接收邮件==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;POP3协议在传输层是使用明文来传送密码的，并不对密码进行加密&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SMTP是一种&quot;推&quot;协议，用于==发送方用户代理与发送方服务器之间以及发送方服务器与接收方服务器之间==，不能用于接收方用户从服务器上读取邮件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SMAP只支持传输7比特ASCII码内容&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 万维网&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;HTTP采用TCP作为运输层协议，但==HTTP协议本身是无连接的==(通信双方在交换HTTP报文之前不需要先建立HTTP连接)。&lt;/li&gt;
&lt;li&gt;DNS采用UDP来传送数据，UDP是一种面向无连接的协议&lt;/li&gt;
&lt;li&gt;HTTP1.0只支持非持久连接，所以每选求一个对象需要建立一次TCP连接&lt;/li&gt;
&lt;li&gt;HTTP1.1 默认为持续连接&lt;/li&gt;
&lt;li&gt;HTTP请求报文中， Connection：连接方式，==close表示为非持续连接方式，keep-alive表示持续连接方式==&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>深入理解操作系统 第4章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-4/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-4/</guid><description>操作系统的非连续内存分配</description><pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第四章的主要内容是：操作系统的非连续内存分配&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第三章介绍的是连续内存管理, 即 : 操作系统加载到内存以及程序加载到内存中时, 分配一块连续的空闲(内存)块. 但是容易出现碎片问题, 这一章介绍的非连续内存分配可以有效的减少碎片的出现.&lt;/p&gt;
&lt;h2&gt;非连续内存分配的必要性&lt;/h2&gt;
&lt;h3&gt;连续内存分配的缺点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;分配给一个程序的物理内存是连续的&lt;/li&gt;
&lt;li&gt;内存利用率低&lt;/li&gt;
&lt;li&gt;有外碎片 / 内碎片的问题&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;非连续内存分配的优点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;一个程序的物理地址空间是非连续的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;更好的内存利用和管理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;允许共享代码与数据(共享库等...)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持动态加载和动态链接&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;非连续内存分配的缺点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;建立虚拟地址和物理地址的转换难度大&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;软件方案&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;硬件方案(采用硬件方案) : 分段 / 分页&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;非连续内存分配&lt;/h2&gt;
&lt;h3&gt;分段(Segmentation)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;段 :&lt;/strong&gt; 在程序中会有来自不同文件的函数 ; 在程序执行时, 不同的数据也有不同的字段, 比如 : 堆 / 栈 / .bss / .data 等&lt;/p&gt;
&lt;p&gt;**分段 : **更好的分离和共享&lt;/p&gt;
&lt;p&gt;程序的分段地址空间如下图所示 :&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;https://i.loli.net/2020/11/11/NAnzHKj5tEM416h.jpg&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分段寻址方案&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;逻辑地址空间连续，但是物理地址空间不连续，使用映射机制进行关联.&lt;/p&gt;
&lt;p&gt;一个段 : 一个内存&quot;块&quot;&lt;/p&gt;
&lt;p&gt;程序访问内存地址需要 : 一个二维的二元组(s, addr) → (段号, 地址)&lt;/p&gt;
&lt;p&gt;操作系统维护一张段表, 存储(段号, 物理地址中的起始地址, 长度限制)&lt;/p&gt;
&lt;p&gt;物理地址 : 段表中的起始地址 + 二元组中的偏移地址&lt;/p&gt;
&lt;h3&gt;分页(Paging)&lt;/h3&gt;
&lt;h4&gt;分页地址空间&lt;/h4&gt;
&lt;p&gt;划分物理内存至固定大小的帧(Frame)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大小是2的幂, 512 / 4096 / 8192&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;划分逻辑地址空间至相同大小的页(Page)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大小是2的幂, 512 / 4096 / 8192&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建立方案 → 转换逻辑地址为物理地址(pages to frames)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;页表&lt;/li&gt;
&lt;li&gt;MMU / TLB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;帧(Frame)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;物理内存被分割为大小相等的帧. 一个内存物理地址是一个二元组(f, o) → (帧号, 帧内偏移)&lt;/p&gt;
&lt;p&gt;帧号 : F位, 共有2^F个帧&lt;/p&gt;
&lt;p&gt;帧内偏移 : S位, 每帧有2^S个字节&lt;/p&gt;
&lt;p&gt;物理地址 = 2^S * f + o&lt;/p&gt;
&lt;p&gt;(例子 : 16-bit地址空间, 9-bit(512 byte) 大小的页帧 物理地址 = (3,6) 物理地址 = 2^9 * 3 + 6 = 1542)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;分页和分段的最大区别 : 这里的 S 是一个固定的数, 而分段中的长度限制不定&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;页(Page)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个程序的逻辑地址空间被划分为大小相等的页. 页内偏移的大小 = 帧内偏移的大小 页号大小 &amp;lt;&amp;gt; 帧号大小&lt;/p&gt;
&lt;p&gt;一个逻辑地址是一个二元组(p, o) → (页号, 页内偏移)&lt;/p&gt;
&lt;p&gt;页号 : P位, 共有2^P个页&lt;/p&gt;
&lt;p&gt;页内偏移 : S位, 每页有2^S个字节&lt;/p&gt;
&lt;p&gt;虚拟地址  = 2^S * p + o&lt;/p&gt;
&lt;h4&gt;页寻址方案&lt;/h4&gt;
&lt;p&gt;操作系统维护一张页表, 页表保存了逻辑地址——物理地址之间的映射关系&lt;/p&gt;
&lt;p&gt;存储 : (页号, 帧号)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;逻辑地址空间应当大于物理内存空间&lt;/li&gt;
&lt;li&gt;页映射到帧&lt;/li&gt;
&lt;li&gt;页是连续的虚拟内存&lt;/li&gt;
&lt;li&gt;帧是非连续的物理内存(有助于减少碎片的产生)&lt;/li&gt;
&lt;li&gt;不是所有的页都有对应的帧&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;页表(Page Table)&lt;/h3&gt;
&lt;h4&gt;页表概述&lt;/h4&gt;
&lt;p&gt;每一个运行的程序都有一个页表&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;属于程序运行状态, 会动态变化&lt;/li&gt;
&lt;li&gt;PTBR : 页表基址寄存器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;转换流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;CPU根据程序的page的页号的若干位, 计算出索引值index, 在页表中搜索这个index, 得到的是帧号, 帧号和原本的offset组成物理地址.&lt;/p&gt;
&lt;p&gt;页表中还有一些特殊标志位&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dirty bit,&lt;/li&gt;
&lt;li&gt;resident bit, (0 : 对应的物理页帧在内存中不存在 ; 1 : 存在)&lt;/li&gt;
&lt;li&gt;clock / reference bit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;转换实例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;16位地址的系统&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;32KB的物理内存&lt;/li&gt;
&lt;li&gt;每页的 1024 byte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;逻辑地址空间 : (4, 0) ... (3, 1023)&lt;/p&gt;
&lt;p&gt;页表 :&lt;/p&gt;
&lt;p&gt;Flags |  Frame nums&lt;/p&gt;
&lt;p&gt;1 0 1    0 0 0 0 0          → 内存访问异常(可能要杀死程序)&lt;/p&gt;
&lt;p&gt;0 1 1    0 0 1 0 0           → 页帧是4 偏移是 1023 → 物理地址 (4, 1023)&lt;/p&gt;
&lt;h4&gt;分页机制的性能问题&lt;/h4&gt;
&lt;p&gt;访问一个内存单元需要2次内存访问&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一次用于获取页表项&lt;/li&gt;
&lt;li&gt;一次用于访问数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;页表可能非常大&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;64位机器如果每页1024字节, 那么一个页表的大小会是多少？(2^64 / 2^10 = 2^54 存放不下)&lt;/li&gt;
&lt;li&gt;每一个运行的程序都需要有一个页表&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如何处理？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓存(Caching)&lt;/li&gt;
&lt;li&gt;间接(Indirection)访问&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;转换后备缓冲区(TLB)&lt;/h4&gt;
&lt;p&gt;缓解时间问题&lt;/p&gt;
&lt;p&gt;Translation Look-aside Buffer(TLB) 是一个缓冲区. CPU中有快表TLB(可以将经常访问的页表存放在这边)&lt;/p&gt;
&lt;p&gt;缓存近期访问的页帧转换表项&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TLB使用关联内存实现, 具备快速访问性能&lt;/li&gt;
&lt;li&gt;如果TLB命中, 物理页号可以很快被获取&lt;/li&gt;
&lt;li&gt;如果TLB未命中, 对应的表项被更新到TLB中(x86的CPU由硬件实现, 其他的可能是由操作系统实现)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;二级/多级页表&lt;/h4&gt;
&lt;p&gt;时间换空间&lt;/p&gt;
&lt;p&gt;二级页表&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将页号分为两个部分, 页表分为两个, 一级页号对应一级页表, 二级页号对应二级页表.&lt;/li&gt;
&lt;li&gt;一级页号查表获得在二级页表的起始地址, 地址加上二级页号的值, 在二级页表中获得帧号&lt;/li&gt;
&lt;li&gt;节约了一定的空间, 在一级页表中如果resident bit = 0, 可以使得在二级页表中不存储相关index,而只有一张页表的话, 这一些index都需要保留&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多级页表&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过把页号分为k个部分, 来实现多级间接页表, 建立一棵页表&quot;树&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;反向页表&lt;/h4&gt;
&lt;p&gt;解决大地址空间问题&lt;/p&gt;
&lt;p&gt;目的 : 根据帧号获得页号&lt;/p&gt;
&lt;p&gt;反向页表只需要存在一张即可&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有大地址空间(64-bits), 前向映射页表变得繁琐. 比如 : 使用了5级页表&lt;/li&gt;
&lt;li&gt;不是让页表与逻辑地址空间的大小相对应, 而是当页表与物理地址空间的大小相对应. 逻辑地址空间增长速度快于物理地址空间&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;基于页寄存器(Page Registers)的方案&lt;/h5&gt;
&lt;p&gt;存储 (帧号, 页号) 使得表大小与物理内存大小相关, 而与逻辑内存关联减小.&lt;/p&gt;
&lt;p&gt;每一个帧和一个寄存器关联, 寄存器内容包括 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;resident bit : 此帧是否被占用&lt;/li&gt;
&lt;li&gt;occupier : 对应的页号 p&lt;/li&gt;
&lt;li&gt;protection bits : 保护位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实例 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;物理内存大小是 : 4096 * 4096 = 4K * 4KB = 16 MB&lt;/li&gt;
&lt;li&gt;页面大小是 : 4096 bytes = 4 KB&lt;/li&gt;
&lt;li&gt;页帧数 : 4096 = 4 K&lt;/li&gt;
&lt;li&gt;页寄存器使用的空间(假设8 bytes / register) : 8 * 4096 = 32 Kbytes&lt;/li&gt;
&lt;li&gt;页寄存器带来的额外开销 : 32K / 16M = 0.2%&lt;/li&gt;
&lt;li&gt;虚拟内存大小 : 任意&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优势 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;转换表的大小相对于物理内存来说很小&lt;/li&gt;
&lt;li&gt;转换表的大小跟逻辑地址空间的大小无关&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;劣势 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要的信息对调了, 即根据帧号可以找到页号&lt;/li&gt;
&lt;li&gt;如何转换回来? (如何根据页号找到帧号)&lt;/li&gt;
&lt;li&gt;在需要在反向页表中搜索想要的页号&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;基于关联内存(associative memory)的方案&lt;/h5&gt;
&lt;p&gt;硬件设计复杂, 容量不大, 需要放置在CPU中&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果帧数较少, 页寄存器可以被放置在关联内存中&lt;/li&gt;
&lt;li&gt;在关联内存中查找逻辑页号
&lt;ul&gt;
&lt;li&gt;成功 : 帧号被提取&lt;/li&gt;
&lt;li&gt;失败 : 页错误异常 (page fault)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;限制因素:
&lt;ul&gt;
&lt;li&gt;大量的关联内存非常昂贵(难以在单个时钟周期内完成 ; 耗电)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;基于哈希(hash)的方案&lt;/h5&gt;
&lt;p&gt;哈希函数 : h(PID, p) 从 PID 标号获得页号&lt;/p&gt;
&lt;p&gt;在反向页表中通过哈希算法来搜索一个页对应的帧号&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对页号做哈希计算, 为了在帧表中获取对应的帧号&lt;/li&gt;
&lt;li&gt;页 i 被放置在表 f(i) 位置, 其中 f 是设定的哈希函数&lt;/li&gt;
&lt;li&gt;为了查找页 i , 执行下列操作 :
&lt;ul&gt;
&lt;li&gt;计算哈希函数 f(i) 并且使用它作为页寄存器表的索引, 获取对应的页寄存器&lt;/li&gt;
&lt;li&gt;检查寄存器标签是否包含 i, 如果包含, 则代表成功, 否则失败&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>第0章 计算机网络序言</title><link>https://github.com/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C-0/</link><guid isPermaLink="true">https://github.com/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C-0/</guid><pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;该笔记来自&lt;a href=&quot;https://github.com/OneChirpZ/Computer-Networking-Study-Notes&quot;&gt;OneChirpZ&lt;/a&gt;的笔记仓库：&lt;/h2&gt;
&lt;p&gt;::github{repo=&quot;OneChirpZ/Computer-Networking-Study-Notes&quot;}&lt;/p&gt;
&lt;h2&gt;课程资料&lt;a href=&quot;https://www.jianguoyun.com/p/DbLpY-4QgImKDhis8p0GIAA&quot;&gt;坚果云链接&lt;/a&gt;&lt;/h2&gt;
</content:encoded></item><item><title>深入理解操作系统 第3章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-3/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-3/</guid><description>操作系统的物理内存管理</description><pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第三章的主要内容是：操作系统的物理内存管理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;计算机体系结构&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;CPU&lt;/li&gt;
&lt;li&gt;内存&lt;/li&gt;
&lt;li&gt;I/O&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;内存分层体系&lt;/h2&gt;
&lt;h3&gt;内存分层体系&lt;/h3&gt;
&lt;p&gt;运行内存(主存) / 磁盘(虚拟内存). 主存是在运行程序时所需要保存的数据空间，而磁盘是用于持久化数据保存的数据空间.&lt;/p&gt;
&lt;p&gt;CPU可以访问的内存包括两大类 : 寄存器 / cache(L1缓存 / L2缓存)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;层次&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;微处理器(CPU访问)&lt;/p&gt;
&lt;p&gt;|___CPU寄存器 / L1缓存&lt;/p&gt;
&lt;p&gt;|___L2缓存&lt;/p&gt;
&lt;p&gt;主存(程序访问)&lt;/p&gt;
&lt;p&gt;磁盘(程序访问)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从CPU寄存器到磁盘，读写速度不断降低，单位成本不断降低，大小不断增大。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;内存管理目标&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;抽象：逻辑地址空间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保护：独立地址空间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;共享：访问相同内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟：更多的地址空间&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;内存管理方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;程序重定位&lt;/li&gt;
&lt;li&gt;分段&lt;/li&gt;
&lt;li&gt;分页&lt;/li&gt;
&lt;li&gt;虚拟内存&lt;/li&gt;
&lt;li&gt;按需分页虚拟内存&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;实现高度依赖于硬件, 其中内存管理单元(MMU)负责处理CPU的内存访问请求&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;地址空间&lt;/h2&gt;
&lt;p&gt;地址空间的定义&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;物理地址空间 —— 硬件支持的地址空间( address : [0, Max_sys] )&lt;/li&gt;
&lt;li&gt;逻辑地址空间 —— 一个运行在程序所拥有的的内存范围( address : [0, Max_prog] )&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;连续内存分配&lt;/h2&gt;
&lt;h3&gt;内存碎片问题&lt;/h3&gt;
&lt;p&gt;内存碎片问题指的是空闲的内存无法被利用&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;外部碎片 : 分配单元间的未使用内存&lt;/li&gt;
&lt;li&gt;内部碎片 : 分配单元内的未使用内存&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;分区的动态分配&lt;/h3&gt;
&lt;p&gt;分区的动态分配方式有以下三种 :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一匹配分配 : 在内存中找到第一个比需求大的空闲块, 分配给应用程序&lt;/li&gt;
&lt;li&gt;最优适配分配 : 在内存中找到最小的空闲块, 分配给应用程序&lt;/li&gt;
&lt;li&gt;最差适配分配 : 在内存中找到最大的空闲块, 分配给应用程序&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;分配方式的区别&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;分配方式&lt;/th&gt;
&lt;th&gt;第一匹配分配&lt;/th&gt;
&lt;th&gt;最优适配分配&lt;/th&gt;
&lt;th&gt;最差适配分配&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;分配方式实现需求&lt;/td&gt;
&lt;td&gt;1. 按地址排序的空闲块列表&amp;lt;br /&amp;gt;2. 分配需要寻找一个合适的分区&amp;lt;br /&amp;gt;3. 重分配需要检查是否可以合并相邻空闲分区&lt;/td&gt;
&lt;td&gt;1. 按尺寸排序的空闲块列表&amp;lt;br /&amp;gt;2. 分配需要寻找一个合适的分区&amp;lt;br /&amp;gt;3. 重分配需要检查是否可以合并相邻空闲分区&lt;/td&gt;
&lt;td&gt;1. 按尺寸排序的空闲块列表&amp;lt;br /&amp;gt;2. 分配最大的分区&amp;lt;br /&amp;gt;3. 重分配需要检查是否可以合并相邻空闲分区&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;优势&lt;/td&gt;
&lt;td&gt;简单 / 易于产生更大空闲块&lt;/td&gt;
&lt;td&gt;比较简单 / 大部分分配是小尺寸时高效&lt;/td&gt;
&lt;td&gt;分配很快 / 大部分分配是中尺寸时高效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;劣势&lt;/td&gt;
&lt;td&gt;产生外部碎片 / 不确定性&lt;/td&gt;
&lt;td&gt;产生外部碎片 / 重分配慢 / 产生很多没用的微小碎片&lt;/td&gt;
&lt;td&gt;产生外部碎片 / 重分配慢 / 易于破碎大的空闲块以致大分区无法被分配&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;三种分配方式并无优劣之分，因为我们无法判断内存请求的大小&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;碎片整理方法&lt;/h2&gt;
&lt;p&gt;可以看到的是，三种分区动态分配的方式都会产生外部碎片，因此我们可以对碎片进行一定的整理来解决碎片问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;压缩式碎片整理&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;重置程序以合并碎片&lt;/li&gt;
&lt;li&gt;要求所有程序是动态可重置的&lt;/li&gt;
&lt;li&gt;问题 :
&lt;ul&gt;
&lt;li&gt;何时重置 ? (在程序处于等待状态时才可以重置)&lt;/li&gt;
&lt;li&gt;需要考虑内存拷贝的开销&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;交换式碎片整理&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;运行程序需要更多的内存时，抢占等待的程序并且回收它们的内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;问题 :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;哪些程序应该被回收 ?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;情况 :&lt;/p&gt;
&lt;p&gt;运行中 : P3&lt;/p&gt;
&lt;p&gt;等待中 : P1 P2 P4&lt;/p&gt;
&lt;p&gt;内存分布 -&amp;gt; 主存 : OS / P1 / P3 / P2 / P4 磁盘 : 空&lt;/p&gt;
&lt;p&gt;当P3程序需要更大的内存时 -&amp;gt;&lt;/p&gt;
&lt;p&gt;内存分布 -&amp;gt; 主存 : OS / P1 / P3 / P2         磁盘 : P4&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>深入理解操作系统 第2章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-2/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-2/</guid><description>操作系统的异常/中断/系统调用</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第二章的主要内容是：操作系统的异常/中断/系统调用&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;计算机探究计算机启动过程&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;作用解析&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Disk : 存放OS和Bootloader&lt;/p&gt;
&lt;p&gt;BIOS : 基于I/O处理系统&lt;/p&gt;
&lt;p&gt;Bootloader : 加载OS，将OS放入内存&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结构&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Disk&lt;/p&gt;
&lt;p&gt;|___ OS&lt;/p&gt;
&lt;p&gt;|___ Bootloader&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;开机流程&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;BIOS&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开机后，寻找显卡和执行BIOS (此时, &lt;code&gt;CS : IP = 0xF000 : 0xFFF0&lt;/code&gt;,  CS/IP 两个寄存器)&lt;/li&gt;
&lt;li&gt;将Bootloader从磁盘的引导扇区加载到0x7C00 (Bootloader一共占用512M字节的内存)&lt;/li&gt;
&lt;li&gt;跳转到 &lt;code&gt;CS : IP = 0x0000 : 0x7C00&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bootloader&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将操作系统的代码和数据从硬盘加载到内存中&lt;/li&gt;
&lt;li&gt;跳转到操作系统的起始地址&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;中断、异常、系统调用&lt;/h2&gt;
&lt;h3&gt;中断&lt;/h3&gt;
&lt;p&gt;中断来源于外设，来自不同的硬件设备的计时器和网络的中断。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;中断流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;硬件：设置中断标记（CPU初始化）&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将内部、外部事件设置中断标记&lt;/li&gt;
&lt;li&gt;中断事件的ID&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;软件&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;保存当前处理状态（寄存器之类的一些数据）&lt;/li&gt;
&lt;li&gt;根据中断事件的ID跳转到中断服务程序，中断服务程序处理&lt;/li&gt;
&lt;li&gt;清楚中断标记&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;异常&lt;/h3&gt;
&lt;p&gt;异常来源于不良的应用程序，非法指令或者其他坏的处理状态（如：内存出错）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;异常处理流程&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;保存现场&lt;/li&gt;
&lt;li&gt;异常处理
&lt;ol&gt;
&lt;li&gt;杀死了产生异常的程序&lt;/li&gt;
&lt;li&gt;重新执行异常指令&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;恢复现场&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;系统调用&lt;/h3&gt;
&lt;p&gt;系统调用来源于应用程序，应用程序主动向操作系统发出服务请求。程序访问主要是通过高层次的API，而不是直接调用系统调用函数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;APIs&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Win32 API 用于Windows&lt;/li&gt;
&lt;li&gt;POSIX API 用于 POSIX-based systems（包括UNIX，LINUX，Mac OS X）&lt;/li&gt;
&lt;li&gt;Java API 用于JAVA虚拟机&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;特点&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通常情况下，每个系统调用有对应的序号
&lt;ul&gt;
&lt;li&gt;系统调用接口根据这些序号来维护表的索引&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;系统调用接口调用内核态中预期的系统调用
&lt;ul&gt;
&lt;li&gt;并返回系统调用的状态和其他任何返回值&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用户不需要知道系统调用是如何实现的
&lt;ul&gt;
&lt;li&gt;只需要获取API和了解操作系统将什么作为返回结果&lt;/li&gt;
&lt;li&gt;操作系统接口的细节大部分都隐藏在API中&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;用户态&lt;/strong&gt;：操作系统运行中，CPU处于的特权级别，不能直接执行特权指令&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;内核态&lt;/strong&gt;：操作系统运行中，CPU处于的特权级别，可以执行任何一条指令&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;系统调用&lt;/strong&gt;：触发CPU从用户态到内核态的转换，切换程序和内核的堆栈，需要一定的开销&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;跨越操作系统边界的开销（值得的且必须的，保证了操作系统的安全性）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在执行时间上的开销超过程序调用&lt;/li&gt;
&lt;li&gt;开销：
&lt;ul&gt;
&lt;li&gt;建立中断、异常、系统调用号与对应服务例程映射关系的初始化开销&lt;/li&gt;
&lt;li&gt;建立内核堆栈&lt;/li&gt;
&lt;li&gt;验证参数&lt;/li&gt;
&lt;li&gt;内核态映射到用户态的地址空间（更新页面映射权限）&lt;/li&gt;
&lt;li&gt;内核态独立地址空间（TLB）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;区别&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;源头&lt;/th&gt;
&lt;th&gt;处理时间&lt;/th&gt;
&lt;th&gt;响应&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;中断&lt;/td&gt;
&lt;td&gt;外设&lt;/td&gt;
&lt;td&gt;异步&lt;/td&gt;
&lt;td&gt;持续，对应用程序透明&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;异常&lt;/td&gt;
&lt;td&gt;应用程序意向不到的行为&lt;/td&gt;
&lt;td&gt;同步&lt;/td&gt;
&lt;td&gt;杀死或重新执行指令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;系统调用&lt;/td&gt;
&lt;td&gt;应用程序请求系统提供服务&lt;/td&gt;
&lt;td&gt;同步或异步&lt;/td&gt;
&lt;td&gt;等待和持续&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;异步：应用程序不知道什么时候会发生中断&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;同步：执行到某一条指令一定会发生该事件&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;为什么应用程序不能直接访问硬件？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在计算机运行时，内核是被信任的第三方&lt;/li&gt;
&lt;li&gt;只有内核可以执行特权指令&lt;/li&gt;
&lt;li&gt;为了方便应用程序&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>深入理解操作系统 第1章</title><link>https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-1/</link><guid isPermaLink="true">https://github.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-1/</guid><description>操作系统的一些知识</description><pubDate>Tue, 03 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;第一章的主要内容是：操作系统的一些知识&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;操作系统是什么？&lt;/h2&gt;
&lt;p&gt;用户角度：操作系统是一个控制软件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;管理应用程序&lt;/li&gt;
&lt;li&gt;为应用程序提供服务&lt;/li&gt;
&lt;li&gt;杀死应用程序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;程序角度：操作系统是资源管理器&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;管理外设、分配资源&lt;/li&gt;
&lt;li&gt;抽象
&lt;ul&gt;
&lt;li&gt;将CPU抽象成进程&lt;/li&gt;
&lt;li&gt;将磁盘抽象成文件&lt;/li&gt;
&lt;li&gt;将内存抽象成地址空间&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;操作系统层次&lt;/h2&gt;
&lt;p&gt;位于硬件之上，应用程序之下。&lt;/p&gt;
&lt;h2&gt;操作系统的界面和内核&lt;/h2&gt;
&lt;p&gt;Linux Windows Android 的界面属于外壳(Shell) ，而不是内核(kernel)。操作系统研究的是内核，处于Shell之下。&lt;/p&gt;
&lt;h2&gt;操作系统内部组件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CPU调度器&lt;/li&gt;
&lt;li&gt;物理内存管理&lt;/li&gt;
&lt;li&gt;虚拟内存管理&lt;/li&gt;
&lt;li&gt;文件系统管理&lt;/li&gt;
&lt;li&gt;中断处理与设备驱动&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;操作系统特征&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;并发
&lt;ul&gt;
&lt;li&gt;一段时间内运行多个进程（并行 : 一个时间点运行多个进程，一般要求有多个CPU)&lt;/li&gt;
&lt;li&gt;需要OS管理和调度&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;共享
&lt;ul&gt;
&lt;li&gt;“同时”共享&lt;/li&gt;
&lt;li&gt;互斥共享&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;虚拟
&lt;ul&gt;
&lt;li&gt;让每一个用户觉得的有一个计算机专门为他服务&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;异步
&lt;ul&gt;
&lt;li&gt;程序是走走停停，而不是一直运行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>操作系统笔记</title><link>https://github.com/posts/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%80%83%E7%A0%94%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://github.com/posts/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E8%80%83%E7%A0%94%E7%AC%94%E8%AE%B0/</guid><description>操作系统笔记</description><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;操作系统&lt;/h1&gt;
&lt;h2&gt;1.计算机系统概述&lt;/h2&gt;
&lt;h3&gt;1.操作系统的基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;系统调用是由操作系统提供给用户的，它==只能通过用户程序间接使用==&lt;/li&gt;
&lt;li&gt;==制约性，间断性，共享性属于多道程序设计的基本特征==，顺序性不是&lt;/li&gt;
&lt;li&gt;单处理机系统中，进程与进程不可并行处理&lt;/li&gt;
&lt;li&gt;计算机开机后，操作系统最终被加载到RAM中&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 操作系统发展历程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;实时操作系统必须在==被控制对象规定时间内==处理来自外部的事件&lt;/li&gt;
&lt;li&gt;分时系统追求的目标是==比较快速响应用户==&lt;/li&gt;
&lt;li&gt;允许多个用户以交互的方式使用计算机的操作系统称为==分时操作系统==；允许多个用户将若干作业提交给计算机系统集中处理的操作系统称为==批处理系统==；在==实时操作系统==的控制下，计算机系统能及时处理由过程控制反馈的数据并及时作出响应；在IBM-PC中，操作系统称为==微型计算机操作系统==&lt;/li&gt;
&lt;li&gt;==中断技术使得多道批处理系统的I/O设备可与CPU并行工作==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 操作系统运行环境&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入输出指令必须在核心态下执行&lt;/li&gt;
&lt;li&gt;操作系统中，==通道技术是一种硬件技术==&lt;/li&gt;
&lt;li&gt;==用户程序在用户态下要使用特权指令引起的中断属于访管中断==&lt;/li&gt;
&lt;li&gt;进程调度不需要硬件的支持&lt;/li&gt;
&lt;li&gt;从核心态到用户态的转换是由==操作系统程序==执行后完成的，而用户态到核心态的转换则是由==硬件==完成的&lt;/li&gt;
&lt;li&gt;==访管指令仅在用户态下使用，执行访管指令，将用户态转变为核心态==&lt;/li&gt;
&lt;li&gt;广义指令即为系统调用指令&lt;/li&gt;
&lt;li&gt;命令解释程序在用户态执行&lt;/li&gt;
&lt;li&gt;子程序调用只需保存程序断点，即该指令的下一条指令的地址；==中断处理不仅要保存断点（PC的内容），还要保存程序状态字寄存器（PSW）的内容==。&lt;/li&gt;
&lt;li&gt;处理外部中断时，应该由操作系统保存的是==通用寄存器的内容==&lt;/li&gt;
&lt;li&gt;异常是指令执行过程中在处理器内部发生的特殊事件，中断是来自处理器外部的请求事件。==访存时缺页属于异常==&lt;/li&gt;
&lt;li&gt;执行系统调用的过程包括如下主要操作：①传递系统调用参数 ②执行陷入（trap）指令 ③执行相应的服务程序 ④返回用户态&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.操作系统结构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;用==分层式架构设计==的操作系统结构清晰且便于调试&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分层式结构具有非常灵活的依赖关系是错误的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;微内核OS的主要缺点是==性能问题==，因为需要频繁地在核心态和用户态之间进行切换，因而切换开销偏大&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以从4个方面来描述微内核OS，①内核足够小 ②基于客户服务器模式 ③应用“机制与策略分离”原理 ④采用面向对象技术&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==添加系统服务时，不必修改内核是微内核的优点==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一般来说==内核的服务越少内核越稳定==，所以微内核结构比单一内核稳定&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Windows是宏内核操作系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==计算机操作系统的引导程序位于硬盘中==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;引导程序分为两种：一种是位于ROM中的自举程序（BIOS的组成部分），用于启动具体的设备；另一种是位于装有操作系统硬盘的活动分区的引导扇区中的引导程序（称为启动管理器），用于引导操作系统&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BIOS将控制权交给排在首位的启动设备后，CPU将该设备主引导扇区的内容（主引导记录MBR）加载到内存中，然后由MBR检查分区表，查找活动分区，并将该分区的引导扇区的内容（分区引导记录PBR）加载到内存加以执行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 线程与进程&lt;/h2&gt;
&lt;h3&gt;1. 进程与线程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;线程包含CPU现场，可以独立执行程序&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==线程没有自己独立的地址空间，它共享其所属的进程的空间==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程获得处理器运行是通过调度得到的&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;并发进程失去封闭性是指并发进程共享变量，其执行结果与速度有关&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不管系统中是否有线程，进程都是拥有资源的独立单位&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内核级线程是处理机调度和分派的基本单位&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;C语言编写的程序在使用内存时一般分为三个段，二进制代码和常量存放在正文段，动态分配的存储区在数据堆栈，临时使用的变量在数据栈段。==函数调用实参传递值在栈段==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==同一进程或不同进程内的线程都可以并发执行==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;整个系统只有一个键盘，而且键盘输入是人的操作，速度比较慢，完全可以使用一个线程来处理整个系统的键盘输入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==用户登录成功，启动程序执行可以创建新进程，设备分配不能创建新进程==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==进程对管道进行读操作和写操作都可能被阻塞==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 处理机调度&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;==时间片轮转调度算法是为了多个用户能及时干预系统==&lt;/li&gt;
&lt;li&gt;CPU繁忙型作业是指该类作业需要大量的CPU时间进行计算，而很少请求I/O操作。因此先来先服务调度算法（有利于长作业）有利于CPU繁忙型的作业，而不利于I/O繁忙型的作业&lt;/li&gt;
&lt;li&gt;==时间片轮转调度算法是绝对可抢占的==&lt;/li&gt;
&lt;li&gt;作业是用户提交的，进程是由系统自动生成的，除此之外两者的区别是==前者以用户任务为单位，后者以操作系统控制为单位==&lt;/li&gt;
&lt;li&gt;分时操作系统通常采用==时间片轮转调度算法==来为用户服务&lt;/li&gt;
&lt;li&gt;假设系统中所有进程同时到达，则使进程平均周转时间最短的是==短进程优先调度算法==&lt;/li&gt;
&lt;li&gt;Unix属于分时操作系统&lt;/li&gt;
&lt;li&gt;中断向量本身是用于存放中断服务例行程序的入口地址，因此中断向量地址就是该入口地址的地址&lt;/li&gt;
&lt;li&gt;中断由硬件保护并完成，==主要是为了保证系统运行可靠，正确==，提高处理速度也是一个好处，但不是主要目的&lt;/li&gt;
&lt;li&gt;==创建新进程后能进行处理器调度；在进程处于临界区时能进行处理机调度==&lt;/li&gt;
&lt;li&gt;一般来说，==I/O型作业的优先级高于计算型作业的优先级==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 同步与互斥&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;一个正在访问临界资源的进程，由于申请等待I/O操作而被中断时，它==允许其他进程抢占处理器，但不得进入该进程的临界区==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;临界区是指进程中用于访问临界资源的那段代码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==临界资源是互斥共享资源，非共享数据不属于临界资源==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==管程==定义了共享数据结构和各种进程在该数据结构上的全部操作&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==用PV操作实现进程同步，信号量的初始值为由用户确定==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可以被多个进程在任意时刻共享的代码，必须是==不允许任何修改的代码==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个进程映像中的==共享程序段必须用可重复编码编写==，因为共享程序段可能同时被多个进程使用，所以必须可重入编码，否则无法实现共享的功能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==管程中signal操作的作用和信号量机制中的V操作不同==，管程中的signal操作是针对某个条件变量的，若不存在因该条件而阻塞的进程，则signal不会产生任何影响&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若系统有N个进程，则==就绪队列中进程的个数最多有N-1个==；==阻塞队列中进程的个数最多有N个==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;信箱通信是一种==间接==通信方式&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;临界区互斥机制必须遵循忙着等待，空闲让进，有限等待准则，==让权等待准则不一定非得实现==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 死锁&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在操作系统中，死锁出现是指若干进程因竞争资源而无限等待其他进程释放已占有的资源&lt;/li&gt;
&lt;li&gt;一次分配所有资源的方法可以预防死锁的发生，它破坏死锁4个必要条件中的==占有并请求==&lt;/li&gt;
&lt;li&gt;==资源有序分配策略破坏了循环等待条件==&lt;/li&gt;
&lt;li&gt;资源分配图是一个有向图，用于表示某时刻系统资源与进程之间的状态&lt;/li&gt;
&lt;li&gt;死锁的4个必要条件中，无法破坏的是互斥使用资源&lt;/li&gt;
&lt;li&gt;死锁检测时 检查的是资源有向图&lt;/li&gt;
&lt;li&gt;==每种资源只有一个并出现环路，说明一定发生死锁==&lt;/li&gt;
&lt;li&gt;==死锁定理适用于检测死锁方法==&lt;/li&gt;
&lt;li&gt;银行家算法是避免死锁的方法，不是预防死锁的方法&lt;/li&gt;
&lt;li&gt;当系统处于安全状态时，系统中一定无死锁进程&lt;/li&gt;
&lt;li&gt;限制用户申请资源的顺序属于死锁预防，属于破坏循环等待条件&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 内存管理&lt;/h2&gt;
&lt;h3&gt;1. 内存管理概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在虚拟内存管理中，地址变换机构将逻辑地址变换为物理地址，形成该逻辑地址的阶段是==链接==&lt;/li&gt;
&lt;li&gt;实现虚拟内存管理必须有相应硬件的支持&lt;/li&gt;
&lt;li&gt;动态重定位是在==作业的执行过程中==进行的&lt;/li&gt;
&lt;li&gt;对重定位存储管理方式，应在==整个系统中设置一个重定位寄存器==&lt;/li&gt;
&lt;li&gt;分段存储管理方法有利于程序的动态连接&lt;/li&gt;
&lt;li&gt;可重入程序是通过==减少对换数量==方法来改善系统性能的&lt;/li&gt;
&lt;li&gt;引入段式存储管理方式，主要是为了满足用户的下列要求：==方便编程，分段共享，分段保护，动态链接和动态增长==&lt;/li&gt;
&lt;li&gt;存储管理的目的是方便用户和提高内存利用率&lt;/li&gt;
&lt;li&gt;对主存储器的访问以字节或字为单位&lt;/li&gt;
&lt;li&gt;==分页存储管理方式提供一维地址结构；分段存储管理方式，作业地址空间是二维的==&lt;/li&gt;
&lt;li&gt;在分段存储管理系统中，用共享段表描述所有被共享的段，若进程P1和P2共享段S，==在物理内存中仅保存一份段S的内容，段S对于进程P1，P2来说，使用位置可能不同，所以在不同的进程中的逻辑段号可能不同==&lt;/li&gt;
&lt;li&gt;最容易产生内存碎片的是最佳适应算法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 虚拟内存管理&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;请求分页存储管理中，若把页面尺寸增大一倍而且可容纳的最大页数不变，则在程序顺序执行中缺页中断次数会减少。 这是因为==由于页面尺寸增大，存放程序所需要的页帧数就会减少，因此缺页中断的次数也会减少==。&lt;/li&gt;
&lt;li&gt;导致LRU算法实现起来耗费高的原因是==需要对所有的页进行排序==&lt;/li&gt;
&lt;li&gt;在虚拟存储器系统的页表项中，==决定是否会发生页故障的是合法位==。&lt;/li&gt;
&lt;li&gt;==产生内存抖动的主要原因是页面置换算法不合理==。&lt;/li&gt;
&lt;li&gt;在虚拟分页存储管理系统中，若进程访问的页面不在主存中，且主存中没有可用的空闲帧时，系统正确的处理顺序为：==缺页中断—&amp;gt;决定淘汰页—&amp;gt;页面调出—&amp;gt;页面调入==。&lt;/li&gt;
&lt;li&gt;==虚拟存储只能基于非连续分配技术==&lt;/li&gt;
&lt;li&gt;系统调用是用户进程发起的请求操作系统的服务。==通过系统调用完成的操作是创建新进程==。页置换和进程调度是由操作系统完成的，没有使用进程调度。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 文件管理&lt;/h2&gt;
&lt;h3&gt;1.文件系统基础&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;打开文件操作的主要工作是==把指定文件的目录复制到内存指定区域==&lt;/li&gt;
&lt;li&gt;有些操作系统中将文件描述信息从目录项中分离出来，这样做的好处是减少查找文件时的I/O信息量。&lt;/li&gt;
&lt;li&gt;操作系统为保证未经用户拥有者授权，任何其他用户不能使用该文件，所提供的解决方法是文件保护。&lt;/li&gt;
&lt;li&gt;在文件系统中，属于文件保护的方法是==口令，用户权限表，存取控制==&lt;/li&gt;
&lt;li&gt;加密保护和访问控制两种机制相比，==访问控制机制必须由系统实现==&lt;/li&gt;
&lt;li&gt;为了对文件系统中的文件进行安全管理，任何一个用户在进入系统时都必须进行注册，这一级安全管理是==系统级==。&lt;/li&gt;
&lt;li&gt;==流式文件属于文件的逻辑结构的范畴==。&lt;/li&gt;
&lt;li&gt;文件的逻辑结构是为了方便==用户==而设计的。&lt;/li&gt;
&lt;li&gt;用磁带做文件存储介质时，文件只能组织成==顺序文件==。&lt;/li&gt;
&lt;li&gt;不适合直接存取的外存分配方式是==链接分配==&lt;/li&gt;
&lt;li&gt;在磁盘上最容易导致存储碎片发生的物理文件结构是==顺序存放==&lt;/li&gt;
&lt;li&gt;顺序文件进行检索时，首先从FTP中读出文件的第1个盘块号；而对索引文件进行检索时，应先从FCB中读出文件索引块的开始地址。&lt;/li&gt;
&lt;li&gt;==适合随机访问且易于文件扩展的是索引结构==&lt;/li&gt;
&lt;li&gt;read系统调用只需要文件描述符作为参数，不需要文件名作为参数&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.目录&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在利用顺序检索法时，只要路径名的一个分量名未找到就应停止查找。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对文件的访问控制常由==用户访问权限和文件属性共同限制==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设文件F1的当前引用计数值为1，先建立文件F1的符号连接（软连接）文件F2，再建立文件F1的硬连接文件F3，然后删除文件F1，此时文件F2和文件F3的引用计数值分别为1，1。&lt;/p&gt;
&lt;p&gt;解释：==建立符号链接时，引用计数值直接复制；建立硬链接时，引用计数值加1。==删除文件时，删除操作对于符号链接是不可见的，这并不影响文件系统，当以后再通过符号链接访问时，发现文件不存在，直接删除符号链接；但对于硬链接则不可直接删除，引用计数值减1，若值不为0，则不能删除此文件，因为还有其他硬链接指向此文件。&lt;/p&gt;
&lt;p&gt;当建立F2时，F1和F2的引用计数值都为1。当再建立F3时，F1和F3的引用计数值就都变成了2。当后来删除F1时，F3的引用计数值为2-1=1，F2的引用计数值不变。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若文件F1的硬连接为F2，两个进程分别打开F1和F2，获得对应的文件描述符为fd1和fd2，则==F1和F2共享同一个内存索引节点，fd1和fd2分别指向各自的用户打开文件表中的一项==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若多个进程共享同一个文件F，则在系统打开文件表中仅有一个表项包含F的属性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 文件系统&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;从用户的观点来看，操作系统引入文件系统的目的是==实现对文件的按名存取==&lt;/li&gt;
&lt;li&gt;Unix操作系统中文件的索引结构放在索引结点中&lt;/li&gt;
&lt;li&gt;==文件的存储空间管理实质上是对外存空闲区的组织和管理==。&lt;/li&gt;
&lt;li&gt;可用于文件系统管理空闲磁盘块的数据结构是==位图，空闲磁盘块链，文件分配表（FAT）==&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 输入输出（I/O）管理&lt;/h2&gt;
&lt;h3&gt;1.I/O管理概述&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;可寻址是块设备（如U盘，硬盘）的基本特征。&lt;/li&gt;
&lt;li&gt;分配共享设备是不会引起进程死锁的。&lt;/li&gt;
&lt;li&gt;共享设备必须是可寻址和可随机访问的设备。&lt;/li&gt;
&lt;li&gt;为了便于上层软件的编制，设备控制器通常需要提供==控制寄存器，状态寄存器和控制命令==。&lt;/li&gt;
&lt;li&gt;在设备控制器（IO控制器）中用于实现设备控制功能的是==IO逻辑==。&lt;/li&gt;
&lt;li&gt;在操作系统中==通道技术==指的是一种硬件机制。&lt;/li&gt;
&lt;li&gt;==字节多路通道==用作连接大量的低速或中速I/O设备。&lt;/li&gt;
&lt;li&gt;==及时性==不是设备分配中应该考虑的问题。&lt;/li&gt;
&lt;li&gt;将系统中的每台设备按某种原则统一进行编号，该编号称为设备的==绝对号==。&lt;/li&gt;
&lt;li&gt;==通道控制设备控制器，设备控制器控制设备工作==。&lt;/li&gt;
&lt;li&gt;每==类==设备只需一个设备驱动程序，不是每一个设备都需要设备驱动程序。如2台绘图机和3台打印机只需要两个设备驱动程序。&lt;/li&gt;
&lt;li&gt;将系统调用参数翻译成设备操作命令的工作由==设备无关的操作系统软件==完成。&lt;/li&gt;
&lt;li&gt;用户程序发出磁盘io请求后，系统的正确处理流程是==用户程序—&amp;gt;系统调用处理程序—&amp;gt;设备驱动程序—&amp;gt;中断处理程序==。&lt;/li&gt;
&lt;li&gt;用户程序发出磁盘io请求后，计算数据所在磁盘的柱面号，磁头号，扇区号的程序是==设备驱动程序==。&lt;/li&gt;
&lt;li&gt;系统将数据从磁盘读到内存的过程包含以下操作：①初始化DMA控制器并启动磁盘  ②从磁盘传输一块数据到内存缓冲区 ③DMA控制器发出中断请求 ④执行“DMA结束”中断服务程序&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 设备独立性软件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;设备的独立性是指==用户编程时使用的设备与实际使用的设备无关==。&lt;/li&gt;
&lt;li&gt;缓冲区管理着重要考虑的问题是==实现进程访问缓冲区的同步==，因为==缓冲区是一种临界资源==，所以在使用缓冲区时都有一个申请和释放的问题（即互斥问题）需要考虑&lt;/li&gt;
&lt;li&gt;磁盘是共享设备，打印机和磁带机不是共享设备&lt;/li&gt;
&lt;li&gt;提高单机资源利用率的关键技术是==多道程序设计技术==&lt;/li&gt;
&lt;li&gt;在采用SPOOLing技术的系统中==用户的打印结果首先被送到输出井中，输出井在磁盘中==&lt;/li&gt;
&lt;li&gt;采用SPOOLing技术的计算机系统，外围计算机需要0台&lt;/li&gt;
&lt;li&gt;SPOOLing系统由==预输入程序，井管理程序和缓输出程序组成==&lt;/li&gt;
&lt;li&gt;SPOOLing系统中的用户程序可以随时将输出数据输送到输出井中，待输出设备空闲时，再由SPOOLing系统完成数据的输出操作&lt;/li&gt;
&lt;li&gt;SPOOLing技术是操作系统中采用的以空间换取时间的技术&lt;/li&gt;
&lt;li&gt;不能改善磁盘设备IO性能的是==在一个磁盘上设置多个分区==&lt;/li&gt;
&lt;li&gt;在系统内存中设置磁盘缓冲区的主要目的是==减少磁盘I/O次数==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 磁盘和固态硬盘&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;既可以随机访问，又可以顺序访问的有光盘，U盘，磁盘，磁带不行&lt;/li&gt;
&lt;li&gt;磁盘的操作系统引导扇区产生在==对硬盘进行高级格式化时==&lt;/li&gt;
&lt;li&gt;扇区数据的处理时间主要影响传输时间，与旋转延迟没什么关系&lt;/li&gt;
&lt;li&gt;旋转延迟的大小取决于磁盘空闲空间的分配程序，以及与文件的物理结构有关&lt;/li&gt;
&lt;li&gt;==固态硬盘（SSD）基于闪存的存储技术，随机读写性能明显高于磁盘，随机写比较慢，它的缺点是易磨损==&lt;/li&gt;
&lt;li&gt;相比于常规硬盘，固态硬盘优势主要体现在==随机存取==的速度&lt;/li&gt;
&lt;li&gt;静态磨损均衡算法比动态磨损均衡算法的表现更优秀&lt;/li&gt;
&lt;li&gt;磁盘逻辑格式化程序所做的工作是==建立文件系统的根目录，对保存空闲磁盘块信息的数据结构进行初始化==&lt;/li&gt;
&lt;li&gt;==绝大多数操作系统为改善磁盘访问时间，以簇为单位进行空间分配==&lt;/li&gt;
&lt;li&gt;系统总是访问磁盘的某个磁道而不响应对其他磁道的访问请求，这种现象称为磁壁黏着。==先来先服务算法不会导致磁壁黏着==。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>数据结构笔记</title><link>https://github.com/posts/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E8%80%83%E7%A0%94%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://github.com/posts/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E8%80%83%E7%A0%94%E7%AC%94%E8%AE%B0/</guid><description>数据结构笔记</description><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;数据结构&lt;/h1&gt;
&lt;h2&gt;1.绪论&lt;/h2&gt;
&lt;h3&gt;1. 数据结构的基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;可以用==抽象数据类型==定义一个完整的数据结构&lt;/li&gt;
&lt;li&gt;与数据存储结构无关的术语是==栈==，    ==循环队列==是用顺序表表示的队列，是一种数据结构&lt;/li&gt;
&lt;li&gt;==数据的逻辑结构独立于其存储结构==&lt;/li&gt;
&lt;li&gt;在存储数据时，不仅要存储数据元素的值，还要存储==数据元素之间的关系==&lt;/li&gt;
&lt;li&gt;链式存储设计时，节点内的存储单元地址==一定连续==&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不同节点的存储单元地址可以不连续，节点内的存储单元地址必须连续&lt;/p&gt;
&lt;h3&gt;2. 算法和算法评价&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;一个算法应该是==问题求解的步骤==&lt;/li&gt;
&lt;li&gt;算法原地工作是指==算法所需的辅助空间是常量==&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 线性表&lt;/h2&gt;
&lt;h3&gt;1. 线性表的顺序表示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;==存储密度大==是顺序存储结构的优点&lt;/li&gt;
&lt;li&gt;线性表的顺序存储结构是一种==随机存取的存储结构==&lt;/li&gt;
&lt;li&gt;==线性表的序号是从一开始==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 线性表的链式表示&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;==链式存储结构比顺序存储结构能更方便的表示各种逻辑结构==&lt;/li&gt;
&lt;li&gt;==静态链表需要分配较大的连续空间，插入和删除不需要移动元素==&lt;/li&gt;
&lt;li&gt;单链表中，增加一个头结点的目的是方便运算的实现&lt;/li&gt;
&lt;li&gt;==单链表中，删除最后一个元素与链表长度有关，其他操作均无关==&lt;/li&gt;
&lt;li&gt;==在尾结点插入和删除数据，带头结点的双循环链表最节省时间==&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 栈，队列和数组&lt;/h2&gt;
&lt;h3&gt;1. 栈&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;栈和队列具有相同的==逻辑结构==&lt;/li&gt;
&lt;li&gt;向一个栈顶指针为top的链栈（不带头结点）中插入一个X节点，则执行==x-&amp;gt;next=top;top=x==&lt;/li&gt;
&lt;li&gt;采用共享栈的好处是==节省存储空间，降低发生上溢的可能==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 栈和队列的应用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;栈在==括号应用，表达式求值，递归，进制转换，迷宫求解==等中有应用&lt;/li&gt;
&lt;li&gt;队列在==层序遍历，bfs，缓冲区，页面替换算法等==中有应用&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 串&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;==简单的模式匹配算法时间复杂度为O(mn)，KMP算法的时间复杂度为O(m+n)==&lt;/li&gt;
&lt;li&gt;KMP算法求next数组（重点），视频P36&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 树与二叉树&lt;/h2&gt;
&lt;h3&gt;1. 树的基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;树的路径长度是==从树根到每个节点的路径长度的总和==&lt;/li&gt;
&lt;li&gt;树中所有节点的度数之和 = 树的所有分支 = 树的节点数目 - 1&lt;/li&gt;
&lt;li&gt;设树中度为 i 的节点数为  ni&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;节点数 = 各个度的节点数之和 = 1 + 分支数 
n=n0+n1+n2+n3 = 1+n1+2n2+3n3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 二叉树的概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;非空二叉树上的叶子节点数等于度为2的节点数加1，即 ==n0= n2+1==&lt;/li&gt;
&lt;li&gt;非空二叉树第k层上至多有 ==2^(k-1)== 个节点&lt;/li&gt;
&lt;li&gt;高度为h的二叉树至多有 ==2^h -1== 个节点&lt;/li&gt;
&lt;li&gt;在含有n个节点的二叉链表中，含有 ==n+1== 个空链域&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 二叉树的遍历与线索二叉树&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在二叉树中，m是n的祖先，使用==后序遍历==可以找到 m 到 n的路径&lt;/li&gt;
&lt;li&gt;在二叉树的前序，中序，后序遍历中，所有叶子节点的先后顺序==完全相同==&lt;/li&gt;
&lt;li&gt;二叉树的先序和后序完全相反，二叉树一定满足==只有一个叶子节点==&lt;/li&gt;
&lt;li&gt;==唯一不能确定一颗二叉树的是 先序遍历和后序遍历==&lt;/li&gt;
&lt;li&gt;线索二叉树是一种==物理结构==，==tag为0时指向孩子节点，为1时指向线索节点==&lt;/li&gt;
&lt;li&gt;==二叉树在线索化后，仍不能有效求解后序线索二叉树求后序后继==&lt;/li&gt;
&lt;li&gt;==后序线索树==遍历仍需要 栈 的支持&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 树，森林&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;将树转变成二叉树：==左孩子右兄弟==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将森林F转换为对应的二叉树T，F中叶节点的个数等于 ==T中左孩子指针为空的节点个数==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在一颗二叉树中，如果某个节点的左指针为NULL，就说明这个节点在原来的森林中没有孩子，是叶子节点&lt;/p&gt;
&lt;h3&gt;4. 树与二叉树的应用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;若没有编码是另一个编码的前缀，则称这样的编码为==前缀编码==&lt;/li&gt;
&lt;li&gt;==在哈夫曼树中只有叶子结点才能作为字符编码==&lt;/li&gt;
&lt;li&gt;对应一组权值构造出的哈夫曼树不是惟一的&lt;/li&gt;
&lt;li&gt;哈夫曼树的度只有0和2，没有1&lt;/li&gt;
&lt;li&gt;并查集的结构是一种 ==双亲表示法存储的树==&lt;/li&gt;
&lt;li&gt;==并查集查找操作的时间复杂度为 O(n)==&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6.图&lt;/h2&gt;
&lt;h3&gt;1. 图的基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;图中有关路径的定义：==由顶点和相邻顶点序偶构成的边所形成的序列==&lt;/li&gt;
&lt;li&gt;==无向图的全部顶点的度的和等于边数的两倍==&lt;/li&gt;
&lt;li&gt;==强连通有向图至少有 n 条边==（构成环）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 图的存储及基本操作&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;无向图的度为==邻接矩阵中第i行或第i列非零元素之和==&lt;/li&gt;
&lt;li&gt;一个图的邻接矩阵表示唯一，邻接表表示不唯一&lt;/li&gt;
&lt;li&gt;在有向图的邻接表存储结构中，顶点 v 在边表中出现的次数为 ==顶点v的入度==&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解释：这里的边表不包含顶点表（即出度）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设有n个顶点，e条边的有向表用邻接表表示，则删除与某个顶点v相关的所有边的时间复杂度为==O(n+e)==&lt;/li&gt;
&lt;li&gt;==十字链表是有向图的链式存储结构==&lt;/li&gt;
&lt;li&gt;==邻接多重表是无向图的链式存储结构==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 图的遍历&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;当各边的权值相等时，广度优先算法可以解决==单源最短路径问题==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==图的广搜使用队列，深搜使用栈==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;==图的深搜相当于树的 先序遍历==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;判断有向图中是否存在回路，除了利用拓扑排序外，还可以利用 ==深度优先遍历，求关键路径==（求最短路径不行）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用DFS算法递归的遍历一个有环无向图，在退出递归时输出相应顶点，这样得到的顶点序列是==逆拓扑有序==&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 图的应用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;==只要无向连通图中没有权值相同的边，则其最小生成树唯一==、&lt;/li&gt;
&lt;li&gt;==最短路径一定是简单路径==&lt;/li&gt;
&lt;li&gt;若一个有向图的顶点不能排成一个拓扑序列，则判定该有向图==含有顶点数大于1的强连通分量==&lt;/li&gt;
&lt;li&gt;若一个有向图具有==有序==的拓扑排序序列，则它的邻接矩阵必定为 ==三角==&lt;/li&gt;
&lt;li&gt;最小生成树代价唯一（形状可能不唯一）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7.查找&lt;/h2&gt;
&lt;h3&gt;1. 顺序查找和折半查找&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;折半查找过程所对应的判定树是一棵==平衡二叉树==&lt;/li&gt;
&lt;li&gt;折半查找和二叉排序树的时间性能==有时不相同==&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;二叉排序树的查找性能和数据的输入顺序有关，最坏情况形成单支树，查找长度为O(n)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对表长为n的有序表进行折半查找，判定树的高度为 ==log2(n+1)向上取整==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 树形查找&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;平衡二叉树（AVL）左子树与右子树的高度差称为平衡因子（-1,0,1）&lt;/li&gt;
&lt;li&gt;==节点数最少的平衡二叉树节点数的递推公式（重要）==&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;n1=1  n2=2
n3=n1+n2+1
...
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;AVL中所有非叶子节点的平衡因子均为1，说明它的叶子节点数最少&lt;/li&gt;
&lt;li&gt;平衡树的查询效率一般优于红黑树&lt;/li&gt;
&lt;li&gt;一棵含有n个节点的红黑树的高度至多为 ==2log(n+1)==&lt;/li&gt;
&lt;li&gt;==红黑树任意节点的左右子树的高度之差不超过两倍==&lt;/li&gt;
&lt;li&gt;==如果红黑树的所有节点都是黑色的，那么它一定是一棵满二叉树==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. B树，B+树&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;B+树不同于B树的特点之一是==能支持顺序查找==&lt;/li&gt;
&lt;li&gt;B树和B+树都可以用于文件索引结构&lt;/li&gt;
&lt;li&gt;B+树更加适用于实际应用中的==操作系统中的文件索引和数据库索引==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 散列表&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;==散列表查找成功的平均查找长度与散列因子有关，与表长无关==&lt;/li&gt;
&lt;li&gt;若在散列表中删除一个元素，不能简单地将该元素删除（在删除地方做删除标记）&lt;/li&gt;
&lt;li&gt;==采用再散列法处理冲突时不易产生聚集==&lt;/li&gt;
&lt;li&gt;==使用链地址法不会引起聚集现象==&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;8. 排序&lt;/h2&gt;
&lt;h3&gt;1. 排序的基本概念&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;==排序算法的稳定性是指经过排序后，能使关键字相同的元素保持原顺序中的相对位置不变==&lt;/li&gt;
&lt;li&gt;拓扑排序不属于内部排序方法&lt;/li&gt;
&lt;li&gt;==使用链表也可以进行排序，只不过有些排序算法不在适用==&lt;/li&gt;
&lt;li&gt;==对同一线性表使用不同的排序方法进行排序，得到的排序结果可能不同==&lt;/li&gt;
&lt;li&gt;对任意n个关键字排序的比较次数至少为 ==log2(n!) 向上取整==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 插入排序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;插入排序：直接插入排序，折半插入排序，希尔排序&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对n个元素的顺序表进行直接插入排序算法，==最坏情况下所需的比较次数是n(n-1)/2 , 最好情况下是（n-1）==&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;与直接插入排序相比，折半插入排序减少了比较元素的次数，元素的移动次数并未改变&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.交换排序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;交换排序：冒泡排序，快速排序&lt;/li&gt;
&lt;li&gt;==快速排序：当每次枢轴都把表等分位长度相近的两个子表时，速度是最快的；当表本身已经有序或者逆序时，速度最慢==&lt;/li&gt;
&lt;li&gt;递归次数与每次划分后得到的分区的处理顺序无关&lt;/li&gt;
&lt;li&gt;==快速排序的阶段性特点是：第 i 趟完成时，会有i个以上的数出现在它最终将要出现的位置，即它左边的数都比它小，右边的数都比它大==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 选择排序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;选择排序：简单选择排序，堆排序&lt;/li&gt;
&lt;li&gt;==简单选择排序的比较次数和移动次数分别为O(n^2)，O(n)==&lt;/li&gt;
&lt;li&gt;==通常，取一大堆数据中的K个最大（最小）元素时，都优先采用堆排序==&lt;/li&gt;
&lt;li&gt;向具有n个元素的堆中插入一个元素的时间复杂度为 O(logn),删除一个元素的时间复杂度为 O(logn)&lt;/li&gt;
&lt;li&gt;构建 n 个记录的初始堆，时间复杂度为 O(n) , 进行堆排序，最坏情况下，时间复杂度为 O(nlogn)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 归并排序和基数排序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基数排序不需要进行关键字的比较&lt;/li&gt;
&lt;li&gt;平均情况下空间复杂度为O(n)的是==归并排序==，最坏情况下空间复杂度为O(n)的是 ==归并排序，快速排序==&lt;/li&gt;
&lt;li&gt;对10TB的数据文件进行排序，应使用的方法是==归并排序==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.各个排序算法比较&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;排序算法名称&lt;/th&gt;
&lt;th&gt;时间复杂度&lt;/th&gt;
&lt;th&gt;空间复杂度&lt;/th&gt;
&lt;th&gt;稳定性&lt;/th&gt;
&lt;th&gt;适用性&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;直接插入排序&lt;/td&gt;
&lt;td&gt;O(n^2)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;稳定&lt;/td&gt;
&lt;td&gt;顺序存储和链式存储&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;折半插入排序&lt;/td&gt;
&lt;td&gt;O(n^2)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;稳定&lt;/td&gt;
&lt;td&gt;顺序存储&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;希尔排序&lt;/td&gt;
&lt;td&gt;O(n^2)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;不稳定&lt;/td&gt;
&lt;td&gt;顺序存储&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;冒泡排序&lt;/td&gt;
&lt;td&gt;O(n^2)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;稳定&lt;/td&gt;
&lt;td&gt;顺序存储&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;快速排序&lt;/td&gt;
&lt;td&gt;O(nlogn)&lt;/td&gt;
&lt;td&gt;O(logn)&lt;/td&gt;
&lt;td&gt;不稳定&lt;/td&gt;
&lt;td&gt;顺序存储&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;简单选择排序&lt;/td&gt;
&lt;td&gt;O(n^2)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;不稳定&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;堆排序&lt;/td&gt;
&lt;td&gt;O(nlogn)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;不稳定&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;归并排序&lt;/td&gt;
&lt;td&gt;O(nlogn)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;稳定&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;基数排序&lt;/td&gt;
&lt;td&gt;O(d（n+r）)&lt;/td&gt;
&lt;td&gt;O(r)&lt;/td&gt;
&lt;td&gt;稳定&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;排序趟数与序列初始状态无关的排序算法是 ==直接插入，简单选择，基数排序，归并排序==&lt;/li&gt;
&lt;li&gt;每趟排序结束后都至少能够确定一个元素最终位置的方法是==简单选择，冒泡排序，快速，堆排序==&lt;/li&gt;
&lt;li&gt;元素的移动次数与初始排列次序无关的是==基数排序==&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7. 外部排序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在做m路平衡归并排序的过程中，为实现==输入/内部归并/输出的并行处理==，需要设置 ==2m个输入缓冲区，2个输出缓冲区==&lt;/li&gt;
&lt;li&gt;如何判定添加虚段的数目？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;设度为0的节点有N0个，度为k的节点有Nk个，则对严格的k叉树有 N0 = (k-1)Nk+1 ，由此得 ==NK = （N0-1）/(k-1)==  (Nk必须为整数)&lt;/p&gt;
</content:encoded></item><item><title>C++的二分法</title><link>https://github.com/posts/c%E7%9A%84%E4%BA%8C%E5%88%86%E6%B3%95/</link><guid isPermaLink="true">https://github.com/posts/c%E7%9A%84%E4%BA%8C%E5%88%86%E6%B3%95/</guid><description>一些C++笔记</description><pubDate>Wed, 09 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;!--来自代码随想录--&amp;gt;&lt;/p&gt;
&lt;h3&gt;二分法第一种写法&lt;/h3&gt;
&lt;p&gt;第一种写法，我们定义 target 是在一个在左闭右闭的区间里，&lt;strong&gt;也就是[left, right] （这个很重要非常重要）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;区间的定义这就决定了二分法的代码应该如何写，&lt;strong&gt;因为定义target在[left, right]区间，所以有如下两点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;while (left &amp;lt;= right) 要使用 &amp;lt;= ，因为left == right是有意义的，所以使用 &amp;lt;=&lt;/li&gt;
&lt;li&gt;if (nums[middle] &amp;gt; target) right 要赋值为 middle - 1，因为当前这个nums[middle]一定不是target，那么接下来要查找的左区间结束下标位置就是 middle - 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如在数组：1,2,3,4,7,9,10中查找元素2，如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;代码如下：（详细注释）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 版本一
class Solution {
public:
    int search(vector&amp;lt;int&amp;gt;&amp;amp; nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里，[left, right]
        while (left &amp;lt;= right) { // 当left==right，区间[left, right]依然有效，所以用 &amp;lt;=
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] &amp;gt; target) {
                right = middle - 1; // target 在左区间，所以[left, middle - 1]
            } else if (nums[middle] &amp;lt; target) {
                left = middle + 1; // target 在右区间，所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值，直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(log n)&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;二分法第二种写法&lt;/h3&gt;
&lt;p&gt;如果说定义 target 是在一个在左闭右开的区间里，也就是[left, right) ，那么二分法的边界处理方式则截然不同。&lt;/p&gt;
&lt;p&gt;有如下两点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;while (left &amp;lt; right)，这里使用 &amp;lt; ,因为left == right在区间[left, right)是没有意义的&lt;/li&gt;
&lt;li&gt;if (nums[middle] &amp;gt; target) right 更新为 middle，因为当前nums[middle]不等于target，去左区间继续寻找，而寻找区间是左闭右开区间，所以right更新为middle，即：下一个查询区间不会去比较nums[middle]&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在数组：1,2,3,4,7,9,10中查找元素2，如图所示：（&lt;strong&gt;注意和方法一的区别&lt;/strong&gt;）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;image-1.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;p&gt;代码如下：（详细注释）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 版本二
class Solution {
public:
    int search(vector&amp;lt;int&amp;gt;&amp;amp; nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里，即：[left, right)
        while (left &amp;lt; right) { // 因为left == right的时候，在[left, right)是无效的空间，所以使用 &amp;lt;
            int middle = left + ((right - left) &amp;gt;&amp;gt; 1);
            if (nums[middle] &amp;gt; target) {
                right = middle; // target 在左区间，在[left, middle)中
            } else if (nums[middle] &amp;lt; target) {
                left = middle + 1; // target 在右区间，在[middle + 1, right)中
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值，直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;时间复杂度：O(log n)&lt;/li&gt;
&lt;li&gt;空间复杂度：O(1)&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>C++有关素数</title><link>https://github.com/posts/c%E6%9C%89%E5%85%B3%E7%B4%A0%E6%95%B0/</link><guid isPermaLink="true">https://github.com/posts/c%E6%9C%89%E5%85%B3%E7%B4%A0%E6%95%B0/</guid><description>一些C++笔记</description><pubDate>Tue, 08 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;1. 判断一个数是不是素数（函数）&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

bool isPrime(int x) {
    // 小于 2 的数一定不是素数
    if (x &amp;lt; 2) return false;

    // 从 2 遍历到 sqrt(x)，只要能整除，就不是素数
    for (int i = 2; i * i &amp;lt;= x; i++) {
        if (x % i == 0) {
            return false;
        }
    }
    // 都不能整除 → 是素数
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;怎么用？&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int main() {
    int x;
    cin &amp;gt;&amp;gt; x;
    if (isPrime(x)) {
        cout &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &quot; 是素数&quot;;
    } else {
        cout &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &quot; 不是素数&quot;;
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h1&gt;2. 求区间 [L, R] 内素数个数（暴力法）&lt;/h1&gt;
&lt;p&gt;直接循环每个数，调用上面的函数即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
int main() {
    int L, R;
    cin &amp;gt;&amp;gt; L &amp;gt;&amp;gt; R;

    int cnt = 0; // 统计素数个数

    // 遍历 L ~ R 每个数
    for (int i = L; i &amp;lt;= R; i++) {
        if (isPrime(i)) {
            cnt++;
        }
    }

    cout &amp;lt;&amp;lt; cnt &amp;lt;&amp;lt; endl;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h1&gt;3. 埃氏筛（区间素数统计）&lt;/h1&gt;
&lt;p&gt;如果 &lt;strong&gt;数字范围很大（比如 1~1e6）&lt;/strong&gt;，暴力会超时，用&lt;strong&gt;埃氏筛&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

const int MAX = 1000005;
bool isPrime[MAX];

// 埃氏筛：把 1~maxn 的素数全部筛出来
void sieve(int maxn) {
    // 先假设所有数都是素数
    for (int i = 0; i &amp;lt;= maxn; i++) isPrime[i] = true;
    isPrime[0] = isPrime[1] = false;

    for (int i = 2; i * i &amp;lt;= maxn; i++) {
        if (isPrime[i]) {
            // 把 i 的倍数全部标记为非素数
            for (int j = i * i; j &amp;lt;= maxn; j += i) {
                isPrime[j] = false;
            }
        }
    }
}

int main() {
    int L, R;
    cin &amp;gt;&amp;gt; L &amp;gt;&amp;gt; R;

    sieve(R); // 筛到 R 即可

    int cnt = 0;
    for (int i = L; i &amp;lt;= R; i++) {
        if (isPrime[i]) cnt++;
    }

    cout &amp;lt;&amp;lt; cnt;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h1&gt;关键&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;素数：大于 1，只能被 1 和自己整除&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;判断单个素数：循环到 sqrt(x)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;求区间素数：暴力小数据，筛法大数据&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>C++的输入输出</title><link>https://github.com/posts/c%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA/</link><guid isPermaLink="true">https://github.com/posts/c%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA/</guid><description>一些C++笔记</description><pubDate>Mon, 07 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;控制小数输出位数：&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iomanip&amp;gt;
cout &amp;lt;&amp;lt; fixed &amp;lt;&amp;lt; setprecision(位数) &amp;lt;&amp;lt; result &amp;lt;&amp;lt; endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;输入优化：&lt;/h1&gt;
&lt;p&gt;我们都知道，getchar 是用来读入一个字符的数据并将其转换为 char 类型的函数，且速度很快，故可以用“读入字符-转换为整型”来代替缓慢的读入
每个整数由两部分组成——符号和数字&lt;/p&gt;
&lt;p&gt;整数的 &apos;+&apos; 通常是省略的，且不会对后面数字所代表的值产生影响，而 &apos;-&apos; 不可省略，因此要进行判定&lt;/p&gt;
&lt;p&gt;10 进制整数中是不含空格或除 0~9 和正负号外的其他字符的，因此在读入不应存在于整数中的字符（通常为空格）时，就可以判定已经读入结束&lt;/p&gt;
&lt;p&gt;C 和 C++ 语言分别在 ctype.h 和 cctype 头文件中，提供了函数 isdigit, 这个函数会检查传入的参数是否为十进制数字字符，是则返回 true，否则返回 false。对应的，在下面的代码中，可以使用 isdigit(ch) 代替 ch &amp;gt;= &apos;0&apos; &amp;amp;&amp;amp; ch &amp;lt;= &apos;9&apos;，而可以使用 !isdigit(ch) 代替 ch &amp;lt;&apos;0&apos; || ch&amp;gt; &apos;9&apos;&lt;/p&gt;
&lt;p&gt;代码实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int read() {
  int x = 0, w = 1;
  char ch = 0;
  while (ch &amp;lt; &apos;0&apos; || ch &amp;gt; &apos;9&apos;) // ch 不是数字时
  {  
        if (ch == &apos;-&apos;) w = -1;        // 判断是否为负
        ch = getchar();               // 继续读入
  }
  while (ch &amp;gt;= &apos;0&apos; &amp;amp;&amp;amp; ch &amp;lt;= &apos;9&apos;) // ch 是数字时
  {  
        x = x * 10 + (ch - &apos;0&apos;);  // 将新读入的数字’加’在 x 的后面
        // x 是 int 类型，char 类型的 ch 和 ’0’ 会被自动转为其对应的
        // ASCII 码，相当于将 ch 转化为对应数字
        // 此处也可以使用 (x&amp;lt;&amp;lt;3)+(x&amp;lt;&amp;lt;1) 的写法来代替 x*10
        ch = getchar();  // 继续读入
  }
  return x * w;  // 数字 * 正负号 = 实际数值
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出优化：&lt;/p&gt;
&lt;p&gt;同样，putchar 是用来输出单个字符的函数，因此将数字的每一位转化为字符输出以加速。要注意的是，负号要单独判断输出，并且每次 %（mod）取出的是数字末位，因此要倒序输出&lt;/p&gt;
&lt;p&gt;代码实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int write(int x) {
  if (x &amp;lt; 0) 
  {  // 判负 + 输出负号 + 变原数为正数
        x = -x;
        putchar(&apos;-&apos;);
  }
  if (x &amp;gt; 9) 
         write(x / 10);  // 递归，将除最后一位外的其他部分放到递归中输出
  putchar(x % 10 + &apos;0&apos;);  // 已经输出（递归）完 x 末位前的所有数字，输出末位
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;如何优化输入输出？&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;取消缓冲区同步语句  std::ios::sync_with_stdio(false)
C++中cin，cout效率比较低，是因为先把要输出的东西存入缓冲区与C语言中的stdio同步后，再输出，导致效率降低，而这个语句的作用是取消缓冲区同步，直接使用，由此可节省时间，使效率与scanf与printf相差无几。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但需要注意的一点是，因为取消与stdio的同步之后，就不建议再使用 printf 与 scanf了，否则实际输出效率可能还会与预期不符。只能用cin 与 cout，以使得cout和cin的效率得到提高&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用tie函数减少IO负担
首先我们需要了解一个可能不知道的事实，那就是C++中的cout和cin并不是独立的，即在使用cout的时候，cin也会执行，反之亦然。正是由于这种情况，当我们遇到数据集超大造成 cin 超时（TLE）的时候,我们可能会认为这是cin的效率不及scanf的原因。其实是输入缓冲区，flush缓冲区，占用了时间。
tie 是将两个 stream 绑定的函数，空参数的话返回当前的输出流指针。如代码：
&lt;code&gt;std::cin.tie(0)&lt;/code&gt;
在默认的情况下 std::cin 绑定的是 std::cout，每次执行 &amp;lt;&amp;lt; 操作符的时候都要调用 flush() 来清理缓冲区（stream buffer），这样会增加 IO 负担。可以通过这句代码std::cin.tie(0)来解除 std::cin 与 std::cout 的绑定，进一步加快执行效率。一个完整且简单的例子如下：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;    using namespace std;
    int main()
    {
        int i;
        cin.tie(&amp;amp;cout);
        cout&amp;lt;&amp;lt;&quot;Enter a number.&quot;;
        cin&amp;gt;&amp;gt;i;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用户输入5然后敲回车后&lt;/p&gt;
&lt;p&gt;代码执行的过程是，直接将“Enter a number.&quot;输出到控制台上，然后直接将用户输入的5读入到i中,中间不经过缓冲区。&lt;/p&gt;
&lt;p&gt;因此当我们要大量读取数据的时候可以tie函数解绑，来加快数据读取。&lt;/p&gt;
</content:encoded></item><item><title>Markdown 扩展功能</title><link>https://github.com/posts/markdown-extended/</link><guid isPermaLink="true">https://github.com/posts/markdown-extended/</guid><description>Read more about Markdown features in Fuwari</description><pubDate>Wed, 01 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;GitHub Repository Cards&lt;/h2&gt;
&lt;p&gt;You can add dynamic cards that link to GitHub repositories, on page load, the repository information is pulled from the GitHub API.&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;Fabrizz/MMM-OnSpotify&quot;}&lt;/p&gt;
&lt;p&gt;Create a GitHub repository card with the code &lt;code&gt;::github{repo=&quot;&amp;lt;owner&amp;gt;/&amp;lt;repo&amp;gt;&quot;}&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;::github{repo=&quot;saicaca/fuwari&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Admonitions&lt;/h2&gt;
&lt;p&gt;Following types of admonitions are supported: &lt;code&gt;note&lt;/code&gt; &lt;code&gt;tip&lt;/code&gt; &lt;code&gt;important&lt;/code&gt; &lt;code&gt;warning&lt;/code&gt; &lt;code&gt;caution&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;:::note
Highlights information that users should take into account, even when skimming.
:::&lt;/p&gt;
&lt;p&gt;:::tip
Optional information to help a user be more successful.
:::&lt;/p&gt;
&lt;p&gt;:::important
Crucial information necessary for users to succeed.
:::&lt;/p&gt;
&lt;p&gt;:::warning
Critical content demanding immediate user attention due to potential risks.
:::&lt;/p&gt;
&lt;p&gt;:::caution
Negative potential consequences of an action.
:::&lt;/p&gt;
&lt;h3&gt;Basic Syntax&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;:::note
Highlights information that users should take into account, even when skimming.
:::

:::tip
Optional information to help a user be more successful.
:::
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Custom Titles&lt;/h3&gt;
&lt;p&gt;The title of the admonition can be customized.&lt;/p&gt;
&lt;p&gt;:::note[MY CUSTOM TITLE]
This is a note with a custom title.
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::note[MY CUSTOM TITLE]
This is a note with a custom title.
:::
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;GitHub Syntax&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;a href=&quot;https://github.com/orgs/community/discussions/16925&quot;&gt;The GitHub syntax&lt;/a&gt; is also supported.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; [!NOTE]
&amp;gt; The GitHub syntax is also supported.

&amp;gt; [!TIP]
&amp;gt; The GitHub syntax is also supported.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Spoiler&lt;/h3&gt;
&lt;p&gt;You can add spoilers to your text. The text also supports &lt;strong&gt;Markdown&lt;/strong&gt; syntax.&lt;/p&gt;
&lt;p&gt;The content :spoiler[is hidden &lt;strong&gt;ayyy&lt;/strong&gt;]!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The content :spoiler[is hidden **ayyy**]!

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>富有表现力的代码示例</title><link>https://github.com/posts/expressive-code/</link><guid isPermaLink="true">https://github.com/posts/expressive-code/</guid><description>How code blocks look in Markdown using Expressive Code.</description><pubDate>Wed, 10 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here, we&apos;ll explore how code blocks look using &lt;a href=&quot;https://expressive-code.com/&quot;&gt;Expressive Code&lt;/a&gt;. The provided examples are based on the official documentation, which you can refer to for further details.&lt;/p&gt;
&lt;h2&gt;Expressive Code&lt;/h2&gt;
&lt;h3&gt;Syntax Highlighting&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/syntax-highlighting/&quot;&gt;Syntax Highlighting&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Regular syntax highlighting&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;This code is syntax highlighted!&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Rendering ANSI escape sequences&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ANSI colors:
- Regular: [31mRed[0m [32mGreen[0m [33mYellow[0m [34mBlue[0m [35mMagenta[0m [36mCyan[0m
- Bold:    [1;31mRed[0m [1;32mGreen[0m [1;33mYellow[0m [1;34mBlue[0m [1;35mMagenta[0m [1;36mCyan[0m
- Dimmed:  [2;31mRed[0m [2;32mGreen[0m [2;33mYellow[0m [2;34mBlue[0m [2;35mMagenta[0m [2;36mCyan[0m

256 colors (showing colors 160-177):
[38;5;160m160 [38;5;161m161 [38;5;162m162 [38;5;163m163 [38;5;164m164 [38;5;165m165[0m
[38;5;166m166 [38;5;167m167 [38;5;168m168 [38;5;169m169 [38;5;170m170 [38;5;171m171[0m
[38;5;172m172 [38;5;173m173 [38;5;174m174 [38;5;175m175 [38;5;176m176 [38;5;177m177[0m

Full RGB colors:
[38;2;34;139;34mForestGreen - RGB(34, 139, 34)[0m

Text formatting: [1mBold[0m [2mDimmed[0m [3mItalic[0m [4mUnderline[0m
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Editor &amp;amp; Terminal Frames&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/frames/&quot;&gt;Editor &amp;amp; Terminal Frames&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Code editor frames&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;Title attribute example&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- src/content/index.html --&amp;gt;
&amp;lt;div&amp;gt;File name comment example&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Terminal frames&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;This terminal frame has no title&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;Write-Output &quot;This one has a title!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Overriding frame types&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Look ma, no frame!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;# Without overriding, this would be a terminal frame
function Watch-Tail { Get-Content -Tail 20 -Wait $args }
New-Alias tail Watch-Tail
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Text &amp;amp; Line Markers&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/text-markers/&quot;&gt;Text &amp;amp; Line Markers&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Marking full lines &amp;amp; line ranges&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Line 1 - targeted by line number
// Line 2
// Line 3
// Line 4 - targeted by line number
// Line 5
// Line 6
// Line 7 - targeted by range &quot;7-8&quot;
// Line 8 - targeted by range &quot;7-8&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Selecting line marker types (mark, ins, del)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  console.log(&apos;this line is marked as deleted&apos;)
  // This line and the next one are marked as inserted
  console.log(&apos;this is the second inserted line&apos;)

  return &apos;this line uses the neutral default marker type&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Adding labels to line markers&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// labeled-line-markers.jsx
&amp;lt;button
  role=&quot;button&quot;
  {...props}
  value={value}
  className={buttonClassName}
  disabled={disabled}
  active={active}
&amp;gt;
  {children &amp;amp;&amp;amp;
    !active &amp;amp;&amp;amp;
    (typeof children === &apos;string&apos; ? &amp;lt;span&amp;gt;{children}&amp;lt;/span&amp;gt; : children)}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Adding long labels on their own lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// labeled-line-markers.jsx
&amp;lt;button
  role=&quot;button&quot;
  {...props}

  value={value}
  className={buttonClassName}

  disabled={disabled}
  active={active}
&amp;gt;

  {children &amp;amp;&amp;amp;
    !active &amp;amp;&amp;amp;
    (typeof children === &apos;string&apos; ? &amp;lt;span&amp;gt;{children}&amp;lt;/span&amp;gt; : children)}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Using diff-like syntax&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;+this line will be marked as inserted
-this line will be marked as deleted
this is a regular line
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+this is an actual diff file
-all contents will remain unmodified
 no whitespace will be removed either
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Combining syntax highlighting with diff-like syntax&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  function thisIsJavaScript() {
    // This entire block gets highlighted as JavaScript,
    // and we can still add diff markers to it!
-   console.log(&apos;Old code to be removed&apos;)
+   console.log(&apos;New and shiny code!&apos;)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Marking individual text inside lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  // Mark any given text inside lines
  return &apos;Multiple matches of the given text are supported&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Regular expressions&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;The words yes and yep will be marked.&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Escaping forward slashes&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Test&quot; &amp;gt; /home/test.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Selecting inline marker types (mark, ins, del)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  console.log(&apos;These are inserted and deleted marker types&apos;);
  // The return statement uses the default marker type
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Word Wrap&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/word-wrap/&quot;&gt;Word Wrap&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Configuring word wrap per block&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Example with wrap
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Example with wrap=false
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Configuring indentation of wrapped lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Example with preserveIndent (enabled by default)
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Example with preserveIndent=false
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Collapsible Sections&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/plugins/collapsible-sections/&quot;&gt;Collapsible Sections&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// All this boilerplate setup code will be collapsed
import { someBoilerplateEngine } from &apos;@example/some-boilerplate&apos;
import { evenMoreBoilerplate } from &apos;@example/even-more-boilerplate&apos;

const engine = someBoilerplateEngine(evenMoreBoilerplate())

// This part of the code will be visible by default
engine.doSomething(1, 2, 3, calcFn)

function calcFn() {
  // You can have multiple collapsed sections
  const a = 1
  const b = 2
  const c = a + b

  // This will remain visible
  console.log(`Calculation result: ${a} + ${b} = ${c}`)
  return c
}

// All this code until the end of the block will be collapsed again
engine.closeConnection()
engine.freeMemory()
engine.shutdown({ reason: &apos;End of example boilerplate code&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Line Numbers&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/plugins/line-numbers/&quot;&gt;Line Numbers&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Displaying line numbers per block&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// This code block will show line numbers
console.log(&apos;Greetings from line 2!&apos;)
console.log(&apos;I am on line 3&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Line numbers are disabled for this block
console.log(&apos;Hello?&apos;)
console.log(&apos;Sorry, do you know what line I am on?&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Changing the starting line number&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;Greetings from line 5!&apos;)
console.log(&apos;I am on line 6&apos;)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Fuwari 简易指南</title><link>https://github.com/posts/guide/</link><guid isPermaLink="true">https://github.com/posts/guide/</guid><description>How to use this blog template.</description><pubDate>Mon, 01 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover image source: &lt;a href=&quot;https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/208fc754-890d-4adb-9753-2c963332675d/width=2048/01651-1456859105-(colour_1.5),girl,_Blue,yellow,green,cyan,purple,red,pink,_best,8k,UHD,masterpiece,male%20focus,%201boy,gloves,%20ponytail,%20long%20hair,.jpeg&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This blog template is built with &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;. For the things that are not mentioned in this guide, you may find the answers in the &lt;a href=&quot;https://docs.astro.build/&quot;&gt;Astro Docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Front-matter of Posts&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;---
title: My First Blog Post
published: 2023-09-09
description: This is the first post of my new Astro blog.
image: ./cover.jpg
tags: [Foo, Bar]
category: Front-end
draft: false
---
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The title of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;published&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The date the post was published.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A short description of the post. Displayed on index page.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The cover image path of the post.&amp;lt;br/&amp;gt;1. Start with &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;: Use web image&amp;lt;br/&amp;gt;2. Start with &lt;code&gt;/&lt;/code&gt;: For image in &lt;code&gt;public&lt;/code&gt; dir&amp;lt;br/&amp;gt;3. With none of the prefixes: Relative to the markdown file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The tags of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The category of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;draft&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If this post is still a draft, which won&apos;t be displayed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Where to Place the Post Files&lt;/h2&gt;
&lt;p&gt;Your post files should be placed in &lt;code&gt;src/content/posts/&lt;/code&gt; directory. You can also create sub-directories to better organize your posts and assets.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/content/posts/
├── post-1.md
└── post-2/
    ├── cover.png
    └── index.md
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Markdown 样例</title><link>https://github.com/posts/markdown/</link><guid isPermaLink="true">https://github.com/posts/markdown/</guid><description>A simple example of a Markdown blog post.</description><pubDate>Sun, 01 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;An h1 header&lt;/h1&gt;
&lt;p&gt;Paragraphs are separated by a blank line.&lt;/p&gt;
&lt;p&gt;2nd paragraph. &lt;em&gt;Italic&lt;/em&gt;, &lt;strong&gt;bold&lt;/strong&gt;, and &lt;code&gt;monospace&lt;/code&gt;. Itemized lists
look like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this one&lt;/li&gt;
&lt;li&gt;that one&lt;/li&gt;
&lt;li&gt;the other one&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that --- not considering the asterisk --- the actual text
content starts at 4-columns in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Block quotes are
written like so.&lt;/p&gt;
&lt;p&gt;They can span multiple paragraphs,
if you like.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., &quot;it&apos;s all
in chapters 12--14&quot;). Three dots ... will be converted to an ellipsis.
Unicode is supported. ☺&lt;/p&gt;
&lt;h2&gt;An h2 header&lt;/h2&gt;
&lt;p&gt;Here&apos;s a numbered list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;first item&lt;/li&gt;
&lt;li&gt;second item&lt;/li&gt;
&lt;li&gt;third item&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note again how the actual text starts at 4 columns in (4 characters
from the left side). Here&apos;s a code sample:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Let me re-iterate ...
for i in 1 .. 10 { do-something(i) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you probably guessed, indented 4 spaces. By the way, instead of
indenting the block, you can use delimited blocks, if you like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;define foobar() {
    print &quot;Welcome to flavor country!&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(which makes copying &amp;amp; pasting easier). You can optionally mark the
delimited block for Pandoc to syntax highlight it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import time
# Quick, count to ten!
for i in range(10):
    # (but not *too* quick)
    time.sleep(0.5)
    print i
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;An h3 header&lt;/h3&gt;
&lt;p&gt;Now a nested list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, get these ingredients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;carrots&lt;/li&gt;
&lt;li&gt;celery&lt;/li&gt;
&lt;li&gt;lentils&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Boil some water.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dump everything in the pot and follow
this algorithm:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; find wooden spoon
 uncover pot
 stir
 cover pot
 balance wooden spoon precariously on pot handle
 wait 10 minutes
 goto first step (or shut off burner when done)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do not bump wooden spoon or it will fall.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice again how text always lines up on 4-space indents (including
that last line which continues item 3 above).&lt;/p&gt;
&lt;p&gt;Here&apos;s a link to &lt;a href=&quot;http://foo.bar&quot;&gt;a website&lt;/a&gt;, to a &lt;a href=&quot;local-doc.html&quot;&gt;local
doc&lt;/a&gt;, and to a &lt;a href=&quot;#an-h2-header&quot;&gt;section heading in the current
doc&lt;/a&gt;. Here&apos;s a footnote [^1].&lt;/p&gt;
&lt;p&gt;[^1]: Footnote text goes here.&lt;/p&gt;
&lt;p&gt;Tables can look like this:&lt;/p&gt;
&lt;p&gt;size material color&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;9 leather brown
10 hemp canvas natural
11 glass transparent&lt;/p&gt;
&lt;p&gt;Table: Shoes, their sizes, and what they&apos;re made of&lt;/p&gt;
&lt;p&gt;(The above is the caption for the table.) Pandoc also supports
multi-line tables:&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;keyword text&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;red Sunsets, apples, and
other red or reddish
things.&lt;/p&gt;
&lt;p&gt;green Leaves, grass, frogs
and other things it&apos;s
not easy being.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;A horizontal rule follows.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Here&apos;s a definition list:&lt;/p&gt;
&lt;p&gt;apples
: Good for making applesauce.
oranges
: Citrus!
tomatoes
: There&apos;s no &quot;e&quot; in tomatoe.&lt;/p&gt;
&lt;p&gt;Again, text is indented 4 spaces. (Put a blank line between each
term/definition pair to spread things out more.)&lt;/p&gt;
&lt;p&gt;Here&apos;s a &quot;line block&quot;:&lt;/p&gt;
&lt;p&gt;| Line one
| Line too
| Line tree&lt;/p&gt;
&lt;p&gt;and images can be specified like so:&lt;/p&gt;
&lt;p&gt;Inline math equations go in like so: $\omega = d\phi / dt$. Display
math should get its own line and be put in in double-dollarsigns:&lt;/p&gt;
&lt;p&gt;$$I = \int \rho R^{2} dV$$&lt;/p&gt;
&lt;p&gt;$$
\begin{equation*}
\pi
=3.1415926535
;8979323846;2643383279;5028841971;6939937510;5820974944
;5923078164;0628620899;8628034825;3421170679;\ldots
\end{equation*}
$$&lt;/p&gt;
&lt;p&gt;And note that you can backslash-escape any punctuation characters
which you wish to be displayed literally, ex.: `foo`, *bar*, etc.&lt;/p&gt;
</content:encoded></item><item><title>在博客里导入视频</title><link>https://github.com/posts/video/</link><guid isPermaLink="true">https://github.com/posts/video/</guid><description>This post demonstrates how to include embedded video in a blog post.</description><pubDate>Tue, 01 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just copy the embed code from YouTube or other platforms, and paste it in the markdown file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: Include Video in the Post
published: 2023-10-19
// ...
---

&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;YouTube&lt;/h2&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;h2&gt;Bilibili&lt;/h2&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;//player.bilibili.com/player.html?bvid=BV1fK4y1s7Qf&amp;amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&amp;gt; &amp;lt;/iframe&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Draft 样例</title><link>https://github.com/posts/draft/</link><guid isPermaLink="true">https://github.com/posts/draft/</guid><pubDate>Fri, 01 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;This Article is a Draft&lt;/h1&gt;
&lt;p&gt;This article is currently in a draft state and is not published. Therefore, it will not be visible to the general audience. The content is still a work in progress and may require further editing and review.&lt;/p&gt;
&lt;p&gt;When the article is ready for publication, you can update the &quot;draft&quot; field to &quot;false&quot; in the Frontmatter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: Draft Example
published: 2024-01-11T04:40:26.381Z
tags: [Markdown, Blogging, Demo]
category: Examples
draft: false
---&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>