<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:h="http://www.w3.org/TR/html4/"><channel><title>Yutong&apos;s Site</title><description>Yutong Liang is a robotics researcher at UC San Diego working on dexterous manipulation, human demonstrations, and physics-based learning.</description><link>https://www.lyt0112.com</link><item><title>Human Demonstration</title><link>https://www.lyt0112.com/blog/human_demo-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/human_demo-en</guid><description>What is the role of human demonstration in robotics learning?</description><pubDate>Sun, 22 Mar 2026 23:00:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;Clarification&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Clarification&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;The “human demonstration” mentioned here mainly refers to human motion capture data, such as keypoints, skeletons, or mesh data, rather than human video egocentric data.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;for-whole-body-control&quot;&gt;For Whole Body Control&lt;a class=&quot;anchor&quot; href=&quot;#for-whole-body-control&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For WBC (whole body control), human demonstration is the end goal of humanoid motion itself. If a humanoid does not need to move like a human, there is not much reason to build it in a human form in the first place.&lt;/p&gt;
&lt;p&gt;Tracking human demonstration is also already supported by many mature methods. Starting from DeepMimic &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and moving through more recent work like GMT &lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, BeyondMimic &lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;, OmniRetarget &lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, and SONIC &lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, the field has gone from tracking single trajectories to learning from large-scale motion data. Humanoid locomotion has become richer and more robust, so it is not unusual now to hear people say that humanoids are already solved. Others would still disagree, but compared with manipulation, especially contact-rich manipulation, locomotion is clearly far ahead.&lt;/p&gt;
&lt;p&gt;So this post will focus on dexterous hand manipulation and ask a simpler question: is human demonstration actually useful there, and if so, what is it useful for?&lt;/p&gt;
&lt;h2 id=&quot;for-manipulation-why-trackers-wont-work&quot;&gt;For Manipulation, Why Trackers Won’t Work?&lt;a class=&quot;anchor&quot; href=&quot;#for-manipulation-why-trackers-wont-work&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;contact-is-heavy&quot;&gt;Contact Is Heavy&lt;a class=&quot;anchor&quot; href=&quot;#contact-is-heavy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is worth first asking why learning from humans is relatively simple for locomotion. The answer is straightforward: the contacts are simple.&lt;/p&gt;
&lt;p&gt;Most of the time, locomotion only involves the feet touching the ground. That contact structure is relatively easy to model in simulation, and it also leads to a small sim-to-real gap. Once contact with external objects enters the picture, the gap grows quickly. Contact in a physics simulator is still a heavy simplification of the real world, and even the physical parameters that remain are often far from reality. A learned policy (few trajectories tracker as policy) can therefore go OOD very easily and fail in complex real-world contact settings.&lt;/p&gt;
&lt;p&gt;This is not a difficulty unique to learning from humans. It is built into any sim-to-real problem. For example, one can use RL with reward shaping to train a dexterous hand to pick up a hammer and drive a nail. That setting does not use human demonstration, but once the policy is deployed in the real world, success is still hard because the contacts are hard.&lt;/p&gt;
&lt;h3 id=&quot;scale-is-huge&quot;&gt;Scale Is Huge&lt;a class=&quot;anchor&quot; href=&quot;#scale-is-huge&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So, is it like WBC, where if we could train a manipulation tracker that can track tens or even hundreds of thousands of trajectories (a policy as a tracker of millions of trajectories) to solve the OOD problem, maybe it would work? Actually, no, because this scope is much larger than WBC. There are infinitely many objects, so it’s impossible to collect demonstration data with enough coverage, and so far there still hasn’t been an algorithm design that powerful.&lt;/p&gt;
&lt;h3 id=&quot;human-demonstration-can-backfire&quot;&gt;Human Demonstration Can Backfire&lt;a class=&quot;anchor&quot; href=&quot;#human-demonstration-can-backfire&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Also there is a caveat here. For fast, contact-rich, or very delicate tasks, the embodiment gap can stop being a small error and become the problem itself. Picking up an apple may still put the human hand and the robot hand in roughly the same basin. In-place rotation of a cylinder is different. The successful behavior may not be a slight deformation of a human trajectory, but a different contact sequence that only makes sense for that particular robot hand.&lt;/p&gt;
&lt;p&gt;What is really constrained here is the set of object-rotation trajectories that the hand can support. This feasible set is tightly coupled to the hand’s mechanical structure, joint topology, and fingertip geometry. Different hands have different feasible regions. So when the human hand and the robot hand are far enough apart, direct retargeting can be a poor in-hand manipulation prior, or even a negative one. It may pull the policy toward a motion that looks human-like, but is simply not what this robot hand is good at.&lt;/p&gt;
&lt;p&gt;This makes me think that the usefulness of human demonstration has a task-dependent bar. Below some dexterity level, it is usually still helpful, for example in pick and place, grasping, and many tool-use settings where the important decisions are still coarse contact decisions. Past that level, especially in fast in-hand manipulation where the policy must keep rearranging contacts, the human prior may become the wrong attractor. I do not think this bar is low. My guess is that even Rubik’s Cube solving is still below it, and human demonstration is probably still a net positive there. The cases where it starts to backfire may be the more delicate in-hand manipulation tasks whose solution has to exploit the robot hand’s own structure.&lt;/p&gt;
&lt;p&gt;But this is exactly what makes the question interesting. If simple motion tracking becomes extremely difficult in contact-rich manipulation, is human demonstration useless there, or should it be used in a different way?&lt;/p&gt;
&lt;h2 id=&quot;what-can-human-demonstration-do&quot;&gt;What Can Human Demonstration Do?&lt;a class=&quot;anchor&quot; href=&quot;#what-can-human-demonstration-do&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here, I think human demonstration can be extended in roughly two directions.&lt;/p&gt;
&lt;p&gt;The first direction is very straightforward. Human demonstration can provide high-level semantic information.&lt;/p&gt;
&lt;p&gt;For example, a human demonstration can tell us that opening a door means pushing the handle rather than kicking the door, or that picking up a teapot means grasping the handle rather than the spout. A large share of work that uses human demonstration at scale follows exactly this line of thought. ObjDex &lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;, for instance, does not directly use finger-level information from human demonstrations. It only uses the wrist as coarse guidance for the robot hand, which makes it fairly clear that what matters there is the high-level semantic signal. EgoScale &lt;sup&gt;&lt;a href=&quot;#user-content-fn-7&quot; id=&quot;user-content-fnref-7&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; follows a similar pattern. Large-scale pretraining on human video first gives the model a rough semantic prior, and then a small amount of expensive real-robot data aligns that prior with actual robot actions.&lt;/p&gt;
&lt;p&gt;The second direction is that human demonstration may also help at the low level of control.&lt;/p&gt;
&lt;p&gt;Human motion carries rich signal at the fine-control level too, especially tactile signal. Those signals can substantially shrink the exploration space for any policy that outputs robot actions. They can point the policy in roughly the right direction and keep it from wandering blindly in the action space. Whether in simulation or in real-world RL, human demonstration may help a policy converge faster to solutions that both complete the task and look natural, or at least human-like. In simulation, this part seems true already.&lt;/p&gt;
&lt;p&gt;To summarize, at the most basic level, human demonstration probably provides strong real-world physical priors at both the high and low levels. In more concrete algorithmic terms, it provides a coarse motion prior. Since robot hands and human hands have similar morphology, it seems plausible that only a small amount of exploration around human demonstration is needed for a policy to complete the task with a fairly natural posture.&lt;/p&gt;
&lt;h2 id=&quot;one-more-thing&quot;&gt;One More Thing&lt;a class=&quot;anchor&quot; href=&quot;#one-more-thing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Does real-world RL maybe follow a similar philosophy?&lt;/p&gt;
&lt;p&gt;If a pretrained VLA is also treated as a coarse motion prior, then exploration in the real world, part of real-world RL, is basically correcting that prior with real dynamics. Recent RLT &lt;sup&gt;&lt;a href=&quot;#user-content-fn-8&quot; id=&quot;user-content-fnref-8&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt; can be understood in exactly this way: it performs real-world exploration and human-feedback-based correction around the actions produced by the VLA, but only on the hardest short segment of a trajectory. That leads to a nearby question. Could this real-world exploration process also be guided by human demonstration? After all, human demonstration is also collected in the real world, and it has strong scale-up potential.&lt;/p&gt;
&lt;hr/&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor&quot; href=&quot;#footnote-label&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;DeepMimic: Example-Guided Deep Reinforcement Learning of Physics-Based Character Skills. &lt;a href=&quot;https://xbpeng.github.io/projects/DeepMimic/index.html&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://xbpeng.github.io/projects/DeepMimic/index.html&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;GMT: General Motion Tracking for Humanoid Whole-Body Control. &lt;a href=&quot;https://gmt-humanoid.github.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://gmt-humanoid.github.io/&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;BeyondMimic: From Motion Tracking to Versatile Humanoid Control via Guided Diffusion. &lt;a href=&quot;https://beyondmimic.github.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://beyondmimic.github.io/&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;OmniRetarget: Interaction-Preserving Data Generation for Humanoid Whole-Body Loco-Manipulation and Scene Interaction. &lt;a href=&quot;https://omniretarget.github.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://omniretarget.github.io/&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;SONIC: Supersizing Motion Tracking for Natural Humanoid Whole-Body Control. &lt;a href=&quot;https://nvlabs.github.io/GEAR-SONIC/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://nvlabs.github.io/GEAR-SONIC/&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;Object-Centric Dexterous Manipulation from Human Motion Data. &lt;a href=&quot;https://sites.google.com/view/obj-dex&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://sites.google.com/view/obj-dex&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-6&quot; data-footnote-backref aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-7&quot;&gt;
&lt;p&gt;EgoScale: Scaling Dexterous Manipulation with Diverse Egocentric Human Data. &lt;a href=&quot;https://research.nvidia.com/labs/gear/egoscale/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://research.nvidia.com/labs/gear/egoscale/&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-7&quot; data-footnote-backref aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-8&quot;&gt;
&lt;p&gt;Precise Manipulation with Efficient Online RL. &lt;a href=&quot;https://www.pi.website/research/rlt&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.pi.website/research/rlt&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-8&quot; data-footnote-backref aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/thumbnail-test-1.Cnyp7B7p.jpg"/></item><item><title>Rubik&apos;s Cube Blindfold</title><link>https://www.lyt0112.com/blog/blindfold-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/blindfold-en</guid><description>A practical guide to 3x3 blindfold solving with Speffz lettering, three-cycles, parity, and setup-based execution.</description><pubDate>Sat, 28 Jun 2025 09:25:48 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;a class=&quot;anchor&quot; href=&quot;#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Blindfold solving feels very different from ordinary speedsolving. In speedsolving you watch the cube, react, and keep adjusting as you go. Blindfold solving is the opposite. Once the cube is covered, everything depends on the trace you built in advance and the moves you can still trust yourself to execute from memory.&lt;/p&gt;
&lt;p&gt;The first real hurdle is turning a scramble into something memorable. A common 3-cycle workflow is to trace corners and edges into two letter sequences, each usually around twenty letters long. After that, you solve the cube pair by pair with three-cycles. When both sequences are finished, the cube is finished too.&lt;/p&gt;
&lt;p&gt;Compared with speedsolving, learning 3x3 blindfold usually comes down to four things, and the learning order is basically the same:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A different solving workflow.&lt;/li&gt;
&lt;li&gt;A letter scheme for every sticker on the cube.&lt;/li&gt;
&lt;li&gt;Three-cycle algorithms that can be applied to letter pairs while looking at the cube:
&lt;ol&gt;
&lt;li&gt;In the basic version, you only need the cases between the buffer and two non-diagonal targets on the top layer. That means 8 corner algorithms and 18 edge algorithms.&lt;/li&gt;
&lt;li&gt;In the full version, you cover any two targets with the buffer. That expands to 440 corner algorithms and 378 edge algorithms, for 818 in total.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;A way to memorize two letter sequences of roughly twenty letters:
&lt;ol&gt;
&lt;li&gt;Turn each pair into a word or image.&lt;/li&gt;
&lt;li&gt;Read the pairs phonetically.&lt;/li&gt;
&lt;li&gt;Read the whole sequence straight through, like a phone number or a license plate.&lt;/li&gt;
&lt;li&gt;For beginners, image-based memo is still the safest place to start. Once the memo drops below about two minutes, many solvers prefer a hybrid approach: use richer imagery for the shorter corner memo and a simpler spoken rhythm for the longer edge memo.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;letter-scheme&quot;&gt;Letter Scheme&lt;a class=&quot;anchor&quot; href=&quot;#letter-scheme&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This article uses Speffz throughout.&lt;/p&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;img src=&quot;https://cdn.lyt0112.com/2025/06/93cb7c17daccec7da4c751540882c3a9.png&quot; alt style=&quot;width: 300px; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;&lt;p style=&quot;font-style: italic; color: gray; margin-top: 8px;&quot;&gt;Speffz lettering&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;memo-workflow&quot;&gt;Memo Workflow&lt;a class=&quot;anchor&quot; href=&quot;#memo-workflow&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The basic idea is simple enough once you see it a few times. You trace the edge and corner targets from the buffer, write them down as letters, then solve those letters in pairs.&lt;/p&gt;
&lt;h3 id=&quot;sequential-lettering&quot;&gt;Sequential Lettering&lt;a class=&quot;anchor&quot; href=&quot;#sequential-lettering&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;aside aria-label=&quot;What a letter means&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;What a letter means&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;With A-Z lettering, it helps to separate an “A sticker” from the “A position”. A letter refers to one sticker on one piece, not the whole piece. As the cube turns, the A sticker moves, but the A position stays fixed. After a &lt;code&gt;U&lt;/code&gt; turn, for example, the A sticker moves to the B position, while the A position is still the sticker on &lt;code&gt;UBL&lt;/code&gt;. At that moment that position is occupied by the &lt;code&gt;D&lt;/code&gt; sticker.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You start from the sticker above the buffer piece. For edges, that is the &lt;code&gt;U&lt;/code&gt; sticker of &lt;code&gt;UF&lt;/code&gt;. For corners, it is the &lt;code&gt;U&lt;/code&gt; sticker of &lt;code&gt;UFR&lt;/code&gt;. During memo, many people remember corners first because the corner string is shorter. During execution, edges often come first so the longer edge memo is less likely to fade. I will use edges first here.&lt;/p&gt;
&lt;p&gt;Suppose the sticker currently sitting in the buffer is the &lt;code&gt;U&lt;/code&gt; sticker of &lt;code&gt;UL&lt;/code&gt;. Then the first letter is &lt;code&gt;D&lt;/code&gt;, because in Speffz that sticker belongs to the &lt;code&gt;D&lt;/code&gt; position. From there you jump to the &lt;code&gt;D&lt;/code&gt; position and check where that sticker should go next. If it belongs at &lt;code&gt;B&lt;/code&gt;, the second letter is &lt;code&gt;B&lt;/code&gt;. Keep following the trail until every edge has been traced.&lt;/p&gt;
&lt;p&gt;So what you are really recording is the identity of the sticker at the current position, then the identity of the sticker at the next position. If the piece at position AA belongs to BB, and the piece at BB belongs to CC, the memo is BB CC.&lt;/p&gt;
&lt;h3 id=&quot;twists&quot;&gt;Twists&lt;a class=&quot;anchor&quot; href=&quot;#twists&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If an edge or corner is already in the correct location but flipped or twisted, treat that as a twist case.&lt;/p&gt;
&lt;p&gt;If the number of twisted pieces is even, twist those pieces together. If the number is odd, twist them together with the buffer by adding one extra algorithm.&lt;/p&gt;
&lt;h3 id=&quot;cycle-breaks&quot;&gt;Cycle Breaks&lt;a class=&quot;anchor&quot; href=&quot;#cycle-breaks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;During tracing, one annoying thing comes up all the time. A target may return to the buffer before the rest of the pieces have been covered. When that happens, the trace ends early and you get a cycle break.&lt;/p&gt;
&lt;p&gt;To continue, choose an unsolved piece as a new starting point and keep tracing from there until everything has been lettered. The case study below shows this much more clearly than a dry definition ever could.&lt;/p&gt;
&lt;h3 id=&quot;parity-check&quot;&gt;Parity Check&lt;a class=&quot;anchor&quot; href=&quot;#parity-check&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When the memo length is odd, and it will be odd for both edges and corners at the same time, you have parity.&lt;/p&gt;
&lt;p&gt;The reason is structural. A quarter turn creates a 4-cycle of edges and a 4-cycle of corners, while the basic blindfold toolbox is built around 3-cycles. A single &lt;code&gt;U&lt;/code&gt; turn from the solved state is already a parity state. From there, any further quarter turn flips the cube between parity and non-parity.&lt;/p&gt;
&lt;p&gt;The usual fix is simple: append &lt;code&gt;B&lt;/code&gt; to both the edge memo and the corner memo, then finish the solve with one &lt;code&gt;PLL Jb Perm: R U R&amp;#39; F&amp;#39; R U R&amp;#39; U&amp;#39; R&amp;#39; F R2 U&amp;#39; R&amp;#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is one special case. If the last letter and the appended &lt;code&gt;B&lt;/code&gt; are stickers on the same edge or the same corner, they do not form a legal 3-cycle target pair. In that case, append a different letter such as &lt;code&gt;A&lt;/code&gt;, and change the final PLL accordingly.&lt;/p&gt;
&lt;p&gt;Example: with yellow on top and blue in front, consider the scramble &lt;code&gt;B&amp;#39; L&amp;#39; F2 L B R&amp;#39; D2 R B2 L2 F2 L&amp;#39; U2 L&amp;#39; B2 R&amp;#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The corner memo is &lt;code&gt;BO N&lt;/code&gt;. If you still append &lt;code&gt;B&lt;/code&gt;, the last pair becomes &lt;code&gt;NB&lt;/code&gt;. Those two letters sit on the same corner, so they cannot be used as a legal target pair. In that case, append &lt;code&gt;A&lt;/code&gt; instead. The corner memo becomes &lt;code&gt;BO NA&lt;/code&gt;, and you finish with &lt;code&gt;PLL V Perm: U2 (R&amp;#39; U R U&amp;#39; R&amp;#39; f&amp;#39; U&amp;#39; R U2 R&amp;#39; U&amp;#39; R U&amp;#39; R&amp;#39; f R) U2&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;solving-workflow&quot;&gt;Solving Workflow&lt;a class=&quot;anchor&quot; href=&quot;#solving-workflow&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The execution order is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Edges&lt;/li&gt;
&lt;li&gt;Corners&lt;/li&gt;
&lt;li&gt;Parity&lt;/li&gt;
&lt;li&gt;Edge flips&lt;/li&gt;
&lt;li&gt;Corner twists&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;applying-algorithms-to-letter-pairs&quot;&gt;Applying Algorithms to Letter Pairs&lt;a class=&quot;anchor&quot; href=&quot;#applying-algorithms-to-letter-pairs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once the memo is done, you can start solving.&lt;/p&gt;
&lt;p&gt;For example, if the next pair is &lt;code&gt;DB&lt;/code&gt;, you can solve it directly with &lt;code&gt;M2 U&amp;#39; M U2 M&amp;#39; U&amp;#39; M2&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;set-up-and-reverse&quot;&gt;Set Up and Reverse&lt;a class=&quot;anchor&quot; href=&quot;#set-up-and-reverse&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you have not memorized all 818 algorithms, the usual shortcut is to learn only the basic top-layer cases and handle the rest with Set Up and Reverse. The idea is straightforward. First move the two target pieces into somewhere you know the algorithms with setup moves. Apply the algorithm you already know. Then undo the setup in reverse order. It feels a bit like pushing something onto a stack and popping it back off afterward.&lt;/p&gt;
&lt;p&gt;But note that during the Set Up process, you cannot move the buffer pieces, otherwise the formulas will no longer apply to the current buffer pieces (unless you clearly know the new position of the buffer pieces and can apply the corresponding formulas). Also, when you are just starting to learn, you must establish fixed steps for set up. If you have to think through the set up each time, it is very easy to make mistakes.&lt;/p&gt;
&lt;h2 id=&quot;algorithms&quot;&gt;Algorithms&lt;a class=&quot;anchor&quot; href=&quot;#algorithms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a compact summary of the three-cycle algorithms. A three-cycle swaps any three target pieces, edge or corner, without disturbing the rest of the cube.&lt;/p&gt;
&lt;p&gt;The list here contains 24+18 algorithms:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Edge three-cycles between the edge buffer and any two of the other three top-layer targets. All 24 top-layer cases are included because they make setup more flexible.&lt;/li&gt;
&lt;li&gt;Corner three-cycles between the corner buffer and &lt;code&gt;UBL&lt;/code&gt; / &lt;code&gt;DBL&lt;/code&gt;. Those two helper positions are especially convenient because every target can be reached in two setup moves.&lt;/li&gt;
&lt;/ol&gt;
&lt;aside aria-label=&quot;Turn Directions for M, E, S and x, y, z&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Turn Directions for M, E, S and x, y, z&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;M layer&lt;/strong&gt;: the middle layer between &lt;code&gt;R&lt;/code&gt; and &lt;code&gt;L&lt;/code&gt;, turning in the same direction as &lt;code&gt;L&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;E layer&lt;/strong&gt;: the middle layer between &lt;code&gt;U&lt;/code&gt; and &lt;code&gt;D&lt;/code&gt;, turning in the same direction as &lt;code&gt;D&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;S layer&lt;/strong&gt;: the middle layer between &lt;code&gt;F&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;, turning in the same direction as &lt;code&gt;F&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;x rotation&lt;/strong&gt;: rotate the whole cube 90 degrees in the &lt;code&gt;R&lt;/code&gt; direction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;y rotation&lt;/strong&gt;: rotate the whole cube 90 degrees in the &lt;code&gt;U&lt;/code&gt; direction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;z rotation&lt;/strong&gt;: rotate the whole cube 90 degrees in the &lt;code&gt;F&lt;/code&gt; direction.&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;aside aria-label=&quot;Definition of the Commutator&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Definition of the Commutator&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A:B means execute A first, then B, and finally the inverse of A, i.e. &lt;code&gt;A B A&amp;#39;&lt;/code&gt;. For example, &lt;code&gt;R:U&lt;/code&gt; represents &lt;code&gt;R U R&amp;#39;&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[A, B] means execute A first, then B, and finally the inverses of A and B, i.e. &lt;code&gt;A B A&amp;#39; B&amp;#39;&lt;/code&gt;. For example, &lt;code&gt;[R, U]&lt;/code&gt; represents &lt;code&gt;R U R&amp;#39; U&amp;#39;&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;edge-algorithms&quot;&gt;Edge Algorithms&lt;a class=&quot;anchor&quot; href=&quot;#edge-algorithms&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;

















































































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:center&quot;&gt;Letter Pair&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;Commutator&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;Algorithm&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;Left / Right&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;BD&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;M2 U:[M,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;M2 U M U2 M&amp;#39; U M2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;DB&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;M2 U&amp;#39;:[M,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;M2 U&amp;#39; M U2 M&amp;#39; U&amp;#39; M2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;BE&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;[S&amp;#39;,L F&amp;#39; L&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;S&amp;#39; L F&amp;#39; L&amp;#39; S L F L&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;EB&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;[L F&amp;#39; L&amp;#39;,S&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L F&amp;#39; L&amp;#39; S&amp;#39; L F L&amp;#39; S&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;MD&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;[R&amp;#39; F R,S]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R&amp;#39; F R S R&amp;#39; F&amp;#39; R S&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;DM&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;[S,R&amp;#39; F R]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;S R&amp;#39; F R S&amp;#39; R&amp;#39; F&amp;#39; R&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;ME&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;M U&amp;#39;:[M&amp;#39;,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;M U&amp;#39; M&amp;#39; U2 M U&amp;#39; M&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;EM&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;M U:[M&amp;#39;,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;M U M&amp;#39; U2 M U M&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;Right / Back&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;BA&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 U&amp;#39;:[R2,S]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 U&amp;#39; R2 S R2 S&amp;#39; U R2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;AB&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 U&amp;#39;:[S,R2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 U&amp;#39; S R2 S&amp;#39; R2 U R2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;BQ&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;r&amp;#39;:[U&amp;#39; R U,M&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;r&amp;#39; U&amp;#39; R U M&amp;#39; U&amp;#39; R&amp;#39; U R&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;QB&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R&amp;#39;:[U&amp;#39; R U,M]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R&amp;#39; U&amp;#39; R U M U&amp;#39; R&amp;#39; U r&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;MA&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;S U&amp;#39;:[R2,S]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;S U&amp;#39; R2 S R2 S&amp;#39; U S&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;AM&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;S U&amp;#39;:[S,R2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;S U&amp;#39; S R2 S&amp;#39; R2 U S&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;MQ&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R:[U R&amp;#39; U&amp;#39;,M&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R U R&amp;#39; U&amp;#39; M&amp;#39; U R U&amp;#39; r&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;QM&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;r:[U R&amp;#39; U&amp;#39;,M]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;r U R&amp;#39; U&amp;#39; M U R U&amp;#39; R&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;Left / Back&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;AD&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L2 U:[S&amp;#39;,L2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L2 U S&amp;#39; L2 S L2 U&amp;#39; L2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;DA&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L2 U:[L2,S&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L2 U L2 S&amp;#39; L2 S U&amp;#39; L2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;AE&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; M U&amp;#39;:[M&amp;#39;,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; M U&amp;#39; M&amp;#39; U2 M U&amp;#39; M&amp;#39; U&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;EA&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; M U:[M&amp;#39;,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; M U M&amp;#39; U2 M U M&amp;#39; U&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;QD&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L:[U L&amp;#39; U&amp;#39;,M]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L U L&amp;#39; U&amp;#39; M U L U&amp;#39; l&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;DQ&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;l:[U L&amp;#39; U&amp;#39;,M&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;l U L&amp;#39; U&amp;#39; M&amp;#39; U L U&amp;#39; L&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;QE&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;l&amp;#39;:[U&amp;#39; L U,M]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;l&amp;#39; U&amp;#39; L U M U&amp;#39; L&amp;#39; U L&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;EQ&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L&amp;#39;:[U&amp;#39; L U,M&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;L&amp;#39; U&amp;#39; L U M&amp;#39; U&amp;#39; L&amp;#39; U l&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Edge flip algorithm: &lt;code&gt;M&amp;#39; U M&amp;#39; U M&amp;#39; U2 M U M U M U2&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;corner-algorithms&quot;&gt;Corner Algorithms&lt;a class=&quot;anchor&quot; href=&quot;#corner-algorithms&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;




































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:center&quot;&gt;Letter Pair&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;Commutator&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;Algorithm&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;AX&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R&amp;#39; U&amp;#39; D&amp;#39; R:[R U&amp;#39; R&amp;#39;,D]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R&amp;#39; U&amp;#39; D&amp;#39; R2 U&amp;#39; R&amp;#39; D R U R&amp;#39; D&amp;#39; R&amp;#39; U D R&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;XA&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R&amp;#39; U&amp;#39; D&amp;#39; R:[D,R U&amp;#39; R&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R&amp;#39; U&amp;#39; D&amp;#39; R D R U&amp;#39; R&amp;#39; D&amp;#39; R U R2 U D R&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;AS&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; D&amp;#39;:[R D R&amp;#39;,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; D&amp;#39; R D R&amp;#39; U2 R D&amp;#39; R&amp;#39; U&amp;#39; D&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;SA&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U D&amp;#39;:[R D R&amp;#39;,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U D&amp;#39; R D R&amp;#39; U2 R D&amp;#39; R&amp;#39; U D&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;AH&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39;:[R D&amp;#39; R&amp;#39;,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; R D&amp;#39; R&amp;#39; U2 R D R&amp;#39; U&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;HA&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U:[R D&amp;#39; R&amp;#39;,U2]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U R D&amp;#39; R&amp;#39; U2 R D R&amp;#39; U&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;EX&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R:[R D&amp;#39; R&amp;#39;,U]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 D&amp;#39; R&amp;#39; U R D R&amp;#39; U&amp;#39; R&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;XE&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R:[U,R D&amp;#39; R&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R U R D&amp;#39; R&amp;#39; U&amp;#39; R D R2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;ES&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U D R&amp;#39;:[U&amp;#39;,R&amp;#39; D R]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U D R&amp;#39; U&amp;#39; R&amp;#39; D R U R&amp;#39; D&amp;#39; R2 U&amp;#39; D&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;SE&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U D R&amp;#39;:[R&amp;#39; D R,U&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U D R2 D R U&amp;#39; R&amp;#39; D&amp;#39; R U R U&amp;#39; D&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;EH&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R D&amp;#39;:[R D R&amp;#39;,U]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R D&amp;#39; R D R&amp;#39; U R D&amp;#39; R&amp;#39; U&amp;#39; D R&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;HE&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R D&amp;#39;:[U,R D R&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R U D&amp;#39; R D R&amp;#39; U&amp;#39; R D&amp;#39; R&amp;#39; D R&amp;#39;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;RX&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 U:[D&amp;#39;,R&amp;#39; U&amp;#39; R]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 U D&amp;#39; R&amp;#39; U&amp;#39; R D R&amp;#39; U R U&amp;#39; R2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;XR&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 U:[R&amp;#39; U&amp;#39; R,D&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;R2 U R&amp;#39; U&amp;#39; R D&amp;#39; R&amp;#39; U R U&amp;#39; D R2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;RS&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; R&amp;#39; U&amp;#39;:[R D&amp;#39; R&amp;#39;,U&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; R&amp;#39; U&amp;#39; R D&amp;#39; R&amp;#39; U&amp;#39; R D R&amp;#39; U2 R U&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;SR&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; R&amp;#39; U&amp;#39;:[U&amp;#39;,R D&amp;#39; R&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U&amp;#39; R&amp;#39; U2 R D&amp;#39; R&amp;#39; U R D R&amp;#39; U R U&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;RH&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U2 R:[U,R D&amp;#39; R&amp;#39;]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U2 R U R D&amp;#39; R&amp;#39; U&amp;#39; R D R2 U2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:center&quot;&gt;HR&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U2 R:[R D&amp;#39; R&amp;#39;,U]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;&lt;code&gt;U2 R2 D&amp;#39; R&amp;#39; U R D R&amp;#39; U&amp;#39; R&amp;#39; U2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Corner twists can be handled with commutators such as &lt;code&gt;(R U R&amp;#39; U&amp;#39;) D (U R U&amp;#39; R&amp;#39;) D&amp;#39;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;parity&quot;&gt;Parity&lt;a class=&quot;anchor&quot; href=&quot;#parity&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When both the edge memo and the corner memo have odd length, you need one extra parity algorithm at the end. Following the above description of adding a B at the end of the encoding, you need to additionally execute the &lt;code&gt;PLL Jb Perm: R U R&amp;#39; F&amp;#39; R U R&amp;#39; U&amp;#39; R&amp;#39; F R2 U&amp;#39; R&amp;#39;&lt;/code&gt; algorithm for parity correction. This algorithm swaps the corner-edge buffer piece and the two B pieces at the same time, thereby changing the parity of the encoding.&lt;/p&gt;
&lt;p&gt;Of course, if, as mentioned in the special case above, the last encoded piece and the added B fall on the same piece, then you need to add a different letter instead, such as A, and at the same time replace the final PLL with the corresponding algorithm, for example &lt;code&gt;PLL V Perm: U2 (R&amp;#39; U R U&amp;#39; R&amp;#39; f&amp;#39; U&amp;#39; R U2 R&amp;#39; U&amp;#39; R U&amp;#39; R&amp;#39; f R) U2&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;case-study-a-full-memo-example&quot;&gt;Case Study: A Full Memo Example&lt;a class=&quot;anchor&quot; href=&quot;#case-study-a-full-memo-example&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Consider the following scramble with white on top and green in front, and yellow on top blue in front as memo coordinates: &lt;code&gt;D L&amp;#39; F2 R B2 D&amp;#39; B2 R&amp;#39; U&amp;#39; F&amp;#39; U&amp;#39; R2 D&amp;#39; B2 U&amp;#39; L U L F&amp;#39; R&amp;#39; D R2 D&amp;#39; B2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This one includes nearly everything at once: sequential lettering, twists, parity, and cycle breaks. Walking through it with Set Up and Reverse and three-cycles makes the whole blindfold process much easier to picture.&lt;/p&gt;
&lt;h3 id=&quot;corner-memo&quot;&gt;Corner Memo&lt;a class=&quot;anchor&quot; href=&quot;#corner-memo&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Start from the buffer. The piece at the &lt;code&gt;C&lt;/code&gt; position is &lt;code&gt;I&lt;/code&gt;, and the piece at &lt;code&gt;I&lt;/code&gt; is &lt;code&gt;C&lt;/code&gt;, so the trace returns to the buffer and creates a cycle break. In this situation you only record &lt;code&gt;I&lt;/code&gt;. There is no need to write &lt;code&gt;C&lt;/code&gt; as well, because &lt;code&gt;C&lt;/code&gt; is the buffer. If every other piece goes home, the buffer goes home with them. You can think of this tiny cycle as a two-piece cube: one letter is enough to solve it. That gives the segment &lt;code&gt;I&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Choose an unlettered piece as a new starting point. If you choose &lt;code&gt;A&lt;/code&gt;, the piece at &lt;code&gt;A&lt;/code&gt; is &lt;code&gt;W&lt;/code&gt;, the piece at &lt;code&gt;W&lt;/code&gt; is &lt;code&gt;R&lt;/code&gt;, and the piece at &lt;code&gt;R&lt;/code&gt; sends you back to the start of that small cycle. The cycle memo is now &lt;code&gt;AW R&lt;/code&gt;, so you still need another cycle break to finish the trace.&lt;/p&gt;
&lt;p&gt;Now choose &lt;code&gt;L&lt;/code&gt; as the next starting point. The piece at &lt;code&gt;L&lt;/code&gt; is &lt;code&gt;P&lt;/code&gt;, the piece at &lt;code&gt;P&lt;/code&gt; is &lt;code&gt;G&lt;/code&gt;, and the piece at &lt;code&gt;G&lt;/code&gt; closes the cycle. This segment is &lt;code&gt;LP G&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With the traced cycles, the already solved &lt;code&gt;DLB&lt;/code&gt; corner, the in-place twisted &lt;code&gt;URB&lt;/code&gt; corner, and the buffer corner, all eight corners are now accounted for.&lt;/p&gt;
&lt;p&gt;Now handle the special cases. The memo length is odd, so parity requires one extra &lt;code&gt;B&lt;/code&gt; at the end. There is also an odd number of in-place twisted corners, so the buffer and &lt;code&gt;B&lt;/code&gt; need to perform the corner twist algorithm together. Since the twisted &lt;code&gt;B&lt;/code&gt; corner gets moved to the &lt;code&gt;C&lt;/code&gt; position, the buffer corner and the &lt;code&gt;URB&lt;/code&gt; corner end up twisting clockwise and counterclockwise respectively.&lt;/p&gt;
&lt;p&gt;So the final corner memo is:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;I(A WR)(LP G)B&lt;/code&gt;, twist cycle &lt;code&gt;QB&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Each pair of parentheses marks a cycle break. The final &lt;code&gt;B&lt;/code&gt; is the extra letter added for parity. The last cycle can be read as moving &lt;code&gt;C&lt;/code&gt; to &lt;code&gt;Q&lt;/code&gt;, then &lt;code&gt;Q&lt;/code&gt; to &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;edge-memo&quot;&gt;Edge Memo&lt;a class=&quot;anchor&quot; href=&quot;#edge-memo&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Again start from the buffer. The piece at the &lt;code&gt;C&lt;/code&gt; position is &lt;code&gt;P&lt;/code&gt;, the piece at &lt;code&gt;P&lt;/code&gt; is &lt;code&gt;R&lt;/code&gt;, the piece at &lt;code&gt;R&lt;/code&gt; is &lt;code&gt;F&lt;/code&gt;, the piece at &lt;code&gt;F&lt;/code&gt; is &lt;code&gt;Q&lt;/code&gt;, the piece at &lt;code&gt;Q&lt;/code&gt; is &lt;code&gt;G&lt;/code&gt;, the piece at &lt;code&gt;G&lt;/code&gt; is &lt;code&gt;T&lt;/code&gt;, and the piece at &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;B&lt;/code&gt;, which returns to the buffer. This creates a cycle break. The segment is &lt;code&gt;PR FQ GT B&lt;/code&gt;, and it restores seven pieces.&lt;/p&gt;
&lt;p&gt;Now the &lt;code&gt;C&lt;/code&gt; position holds &lt;code&gt;C&lt;/code&gt; itself. That is a U/D-colored sticker, so we break the cycle at another U/D-colored position, say &lt;code&gt;D&lt;/code&gt;, and continue. The piece at &lt;code&gt;D&lt;/code&gt; is &lt;code&gt;W&lt;/code&gt;, the piece at &lt;code&gt;W&lt;/code&gt; is &lt;code&gt;K&lt;/code&gt;, and the piece at &lt;code&gt;K&lt;/code&gt; is &lt;code&gt;E&lt;/code&gt;, which closes the cycle. This segment is &lt;code&gt;DW KE&lt;/code&gt;, restoring three pieces.&lt;/p&gt;
&lt;p&gt;Now ten edges are restored. Add the in-place flipped &lt;code&gt;DR&lt;/code&gt; edge and the buffer edge, and all twelve edges are covered.&lt;/p&gt;
&lt;p&gt;The memo length is odd, so append one more &lt;code&gt;B&lt;/code&gt; for parity. The in-place flipped &lt;code&gt;DR&lt;/code&gt; edge is recorded as &lt;code&gt;VO&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So the final edge memo is:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;PR FQ GT B(D WK E)B&lt;/code&gt;, flip cycle &lt;code&gt;VO&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;full-memo&quot;&gt;Full Memo&lt;a class=&quot;anchor&quot; href=&quot;#full-memo&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Edge memo: &lt;code&gt;PR FQ GT BD WK EB&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Corner memo: &lt;code&gt;IA WR LP GB&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Parity: &lt;code&gt;BB&lt;/code&gt; (the first letter tells where the edge buffer goes, the second tells where the corner buffer goes)&lt;/li&gt;
&lt;li&gt;Edge flips: &lt;code&gt;VO&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Corner twists: &lt;code&gt;QB&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OR, If more parity check formulas are remembered:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Edge memo: &lt;code&gt;PR FQ GT BD WK EV&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Corner memo: &lt;code&gt;IA WR LP GQ&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Parity: &lt;code&gt;OB&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Edge flips: None&lt;/li&gt;
&lt;li&gt;Corner twists: None&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;execution&quot;&gt;Execution&lt;a class=&quot;anchor&quot; href=&quot;#execution&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Edges: solve the pairs in &lt;code&gt;PR FQ GT BD WK EB&lt;/code&gt; one by one.&lt;/p&gt;
&lt;p&gt;Corners: solve the pairs in &lt;code&gt;IA WR LP GB&lt;/code&gt; one by one.&lt;/p&gt;
&lt;p&gt;Parity: execute &lt;code&gt;PLL Jb Perm: R U R&amp;#39; F&amp;#39; R U R&amp;#39; U&amp;#39; R&amp;#39; F R2 U&amp;#39; R&amp;#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Edge flips: flip the in-place &lt;code&gt;DR&lt;/code&gt; edge together with the buffer edge.&lt;/p&gt;
&lt;p&gt;Corner twists: twist the in-place &lt;code&gt;URB&lt;/code&gt; corner together with the buffer corner.&lt;/p&gt;
&lt;h2 id=&quot;memo&quot;&gt;Memo&lt;a class=&quot;anchor&quot; href=&quot;#memo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For memo, a short personal dictionary is usually enough. Turn each letter pair into a stable word, sound, or image and keep reusing it. &lt;code&gt;PR&lt;/code&gt; might become “pear”, &lt;code&gt;WK&lt;/code&gt; might become “wake”, and &lt;code&gt;QB&lt;/code&gt; might just be a familiar visual cue you can recall quickly. The clever part matters less than consistency. If the image comes back instantly under pressure, it is doing its job.&lt;/p&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/thumbnail-test-7.oZ40J_Us.jpg"/></item><item><title>Hand Motion Retargeting</title><link>https://www.lyt0112.com/blog/retargeting-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/retargeting-en</guid><description>Interaction-aware hand motion retargeting spanning geometry, force, and self-supervision.</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;background-and-definition&quot;&gt;Background and Definition&lt;a class=&quot;anchor&quot; href=&quot;#background-and-definition&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In character animation and robotics, Motion Retargeting converts one individual’s joint state (qpos) into another individual’s joint state.&lt;/p&gt;
&lt;p&gt;For hands, it means taking a human-hand motion (or one robot hand’s motion) and turning it into commands for a different hand, while keeping the object interaction intact. If you record a human picking up a block, good retargeting should produce robot joint commands that pick up that same block. This shows up everywhere in teleoperation, imitation learning, and data augmentation, because it lets you reuse demonstrations across embodiments.&lt;/p&gt;
&lt;p&gt;If the degrees of freedom are identical, you can often get away with copying joint angles. Once the hands differ in DOF, link lengths, or limits, that naive trick breaks and contacts drift. Contacts are what make this hard: small pose errors can turn into big interaction errors. That is why a lot of recent work goes beyond geometric matching and folds in object shape, force/tactile cues, and action intent, often with self-supervision and unpaired data.&lt;/p&gt;
&lt;h2 id=&quot;geometric-retargeting&quot;&gt;Geometric Retargeting&lt;a class=&quot;anchor&quot; href=&quot;#geometric-retargeting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Early approaches focused on geometric consistency: align keypoints, scale trajectories, and absorb residuals via optimization. AnyTeleop &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; includes the vector error from wrist to fingertip in the objective and applies smooth regularization. DexH2R &lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; scales human-hand trajectories and then solves a nonlinear optimization to produce a joint sequence for the robot hand. This path provides straightforward geometric intuition but lacks modeling of object semantics, so it often becomes unstable when the task or contact surface changes.&lt;/p&gt;
&lt;h2 id=&quot;object-conditioned-retargeting&quot;&gt;Object-Conditioned Retargeting&lt;a class=&quot;anchor&quot; href=&quot;#object-conditioned-retargeting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the hand interacts with objects of different shapes, joint angles and contact distributions rearrange systematically. If we continue to hard-map human poses to a robot hand, contact points will misalign, grip forces will become unbalanced, and the resulting pose will look unnatural. So recent work uses object geometry as an input: align the object, then infer a hand pose that matches the intended interaction.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FunGrasp (2024) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: A three-stage pipeline: estimate a functional human-hand pose from a single RGB-D image; retarget in the object frame by aligning link directions and optimizing contacts; then train a vision-and-touch DRL policy to adapt to shape variation and unseen objects, with privileged learning and system ID for sim-to-real.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DexFlow (2025) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: Builds a hierarchical optimization pipeline. It performs a global pose search to match human and robot hands, then locally optimizes contacts with an energy function so the robot hand naturally conforms to the object surface. It also extracts stable contacts via dual-threshold detection with temporal smoothing, and releases a cross-hand-topology dataset containing 292k grasp frames to support this pipeline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kinematic Motion Retargeting for Contact-Rich Manipulations (2024) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: Treats retargeting as a non-isometric shape matching problem. Using surface contact regions and marker data, it incrementally estimates and optimizes target-hand trajectories via inverse kinematics. The core contributions are a local shape-matching algorithm and a multi-stage optimization pipeline that maintains consistent contact distributions over full manipulation sequences, and supports object replacement and cross-hand generalization.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning Cross-hand Policies of High-DOF Reaching and Grasping (2024) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: Proposes a hand-shape-agnostic state-action representation and a two-stage framework. A unified policy predicts displacements of grasp keypoints, then hand-specific adapters convert them to each hand’s joint controls, enabling cross-hand transfer of high-DOF grasping. Inputs are semantic keypoints and the interaction bisector surface (IBS); a Transformer learns relations among fingers, yielding generalization over different hands and objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;force-conditioned-retargeting&quot;&gt;Force-Conditioned Retargeting&lt;a class=&quot;anchor&quot; href=&quot;#force-conditioned-retargeting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Force placement often decides whether a grasp holds. Even for the same object, changing the force profile can change the target pose, so it helps to treat force as an explicit condition.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Feel the Force: Contact-Driven Learning from Humans (2025) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-7&quot; id=&quot;user-content-fnref-7&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: Uses a tactile glove to record human contact forces and keypoint coordinates, predicts robot trajectories and desired grasp forces, and at execution time adjusts the gripper with PD control to track tactile demonstrations. However, the pipeline involves many hand-tuned components and has limited transferability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DexMachina (2025) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-8&quot; id=&quot;user-content-fnref-8&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: Introduces a fading virtual-object controller during RL and adds contact and task rewards, but this should be considered RL tracking rather than true retargeting.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cross-embodiment-and-self-supervision&quot;&gt;Cross-Embodiment and Self-Supervision&lt;a class=&quot;anchor&quot; href=&quot;#cross-embodiment-and-self-supervision&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The appeal here is to stop relying on manually paired data and instead learn cross-hand mappings from action principles.&lt;/p&gt;
&lt;p&gt;Personally, I like the versions that learn mappings from rules rather than curated paired demonstrations. In XL-VLA, we explore this idea via CrossLatent: a shared latent action space trained with differentiable kinematic constraints and random joint sampling, then plugged into VLA models as a unified action interface.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Geometric Retargeting (2025) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-12&quot; id=&quot;user-content-fnref-12&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: Uses action principles such as fingertip-velocity consistency as self-supervised signals to learn unpaired, cross-embodiment mappings that preserve contact semantics and motion stability despite scale and joint differences, and has been integrated as a geometric prior into Dexterity Gen &lt;sup&gt;&lt;a href=&quot;#user-content-fn-13&quot; id=&quot;user-content-fnref-13&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;XL-VLA / CrossLatent (2026) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-14&quot; id=&quot;user-content-fnref-14&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: Pretrains a shared latent action space with a multi-headed VAE across heterogeneous hands using reconstruction, differentiable-FK fingertip retargeting, and a smooth latent prior; the frozen encoders/decoders turn hand-specific joint chunks into a unified token interface for VLA models.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning to Transfer Human Hand Skills for Robot Manipulations (2025) &lt;sup&gt;&lt;a href=&quot;#user-content-fn-10&quot; id=&quot;user-content-fnref-10&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/strong&gt;: Fits a shared manifold of human-hand motion, robot actions, and object motion; trains on synthetic paired triplets to avoid the high cost of real human-robot pairs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;a class=&quot;anchor&quot; href=&quot;#conclusions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Retargeting alone rarely survives contact-rich manipulation, so recent work leans on visual and tactile cues to get cleaner contacts and better generalization. But beyond simple pick-and-place, transfer is still brittle. Two failure modes show up again and again: object understanding and force consistency. Change the object’s shape or function and the “same” human motion should map to a different robot configuration. Keep the object fixed but change the force distribution and the target qpos should move too. That is why many methods explicitly condition on object geometry/functionality, contact, or force targets.&lt;/p&gt;
&lt;p&gt;You could also imagine a future where RL gives us native dexterous policies that are strong enough that retargeting becomes an extra input alignment problem. That controller does not really exist yet. For now, object- and force-conditioned retargeting is where most of the practical wins are.&lt;/p&gt;
&lt;hr/&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor&quot; href=&quot;#footnote-label&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;AnyTeleop: A General Vision-Based Dexterous Robot Arm-Hand Teleoperation System. &lt;a href=&quot;https://arxiv.org/abs/2307.04577v3&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2307.04577v3&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;DexH2R. &lt;a href=&quot;https://arxiv.org/abs/2411.04428.pdf&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2411.04428.pdf&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;FunGrasp: Functional Grasping for Diverse Dexterous Hands. &lt;a href=&quot;https://arxiv.org/abs/2411.16755v1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2411.16755v1&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;DexFlow: A Unified Approach for Dexterous Hand Pose Retargeting and Interaction. &lt;a href=&quot;https://arxiv.org/abs/2505.01083v1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2505.01083v1&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;Kinematic Motion Retargeting for Contact-Rich Anthropomorphic Manipulations. &lt;a href=&quot;https://arxiv.org/abs/2402.04820.pdf&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2402.04820.pdf&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;Learning Cross-hand Policies of High-DOF Reaching and Grasping. &lt;a href=&quot;https://arxiv.org/abs/2404.09150&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2404.09150&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-6&quot; data-footnote-backref aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-7&quot;&gt;
&lt;p&gt;Feel the Force: Contact-Driven Learning from Humans. &lt;a href=&quot;https://arxiv.org/abs/2506.01944.pdf&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2506.01944.pdf&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-7&quot; data-footnote-backref aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-8&quot;&gt;
&lt;p&gt;DexMachina. &lt;a href=&quot;https://arxiv.org/abs/2505.24853.pdf&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2505.24853.pdf&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-8&quot; data-footnote-backref aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-12&quot;&gt;
&lt;p&gt;Geometric Retargeting. &lt;a href=&quot;https://arxiv.org/abs/2503.07541&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2503.07541&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-12&quot; data-footnote-backref aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-13&quot;&gt;
&lt;p&gt;Dexterity Gen. &lt;a href=&quot;https://zhaohengyin.github.io/dexteritygen/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://zhaohengyin.github.io/dexteritygen/&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-13&quot; data-footnote-backref aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-14&quot;&gt;
&lt;p&gt;XL-VLA / CrossLatent: Cross-Hand Latent Representation for Vision-Language-Action Models. &lt;a href=&quot;https://xl-vla.github.io&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://xl-vla.github.io&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-14&quot; data-footnote-backref aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-10&quot;&gt;
&lt;p&gt;Learning to Transfer Human Hand Skills for Robot Manipulations. &lt;a href=&quot;https://arxiv.org/abs/2501.04169v1&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2501.04169v1&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-10&quot; data-footnote-backref aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/retargeting.gquxNKKl.png"/></item><item><title>Running Into Doctor Who in Balboa Park</title><link>https://www.lyt0112.com/blog/dw_worlds_of_wonder-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/dw_worlds_of_wonder-en</guid><description>The plan was just to visit the San Diego Museum of Art, then a glance up while passing the Comic-Con Museum changed the day.</description><pubDate>Mon, 16 Feb 2026 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;This turned into a much better day than expected.&lt;/p&gt;
&lt;p&gt;The plan was just to spend an afternoon at the San Diego Museum of Art. The sky was gray, the paths were still wet, and Balboa Park felt unusually quiet. Then, while walking past the Comic-Con Museum, I looked up and saw a row of very familiar blue boxes with &lt;code&gt;Doctor Who Worlds of Wonder&lt;/code&gt; across them.&lt;/p&gt;
&lt;p&gt;I had no idea this exhibition was there. It was not the reason for the trip, and I definitely had not planned around it. I just happened to walk by at the right moment.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2393.jpeg&quot; alt=&quot;Comic-Con Museum at Balboa Park&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;The Doctor Who banner outside Comic-Con Museum&lt;/p&gt;
&lt;p&gt;I walked over just to make sure it was real.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2394.jpeg&quot; alt=&quot;Doctor Who Worlds of Wonder flyer&quot; style=&quot;width: 70%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Exhibition flyer&lt;/p&gt;
&lt;p&gt;Tickets were about &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;30&lt;/mn&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;30 for adults and &lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8889em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1076em&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0197em&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;20 for students. At the time, signing up on the official site also got a $15 discounted ticket.&lt;/p&gt;
&lt;h2 id=&quot;that-blue-door&quot;&gt;That Blue Door&lt;a class=&quot;anchor&quot; href=&quot;#that-blue-door&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The entrance itself was pretty plain, but the second that blue door came into view, the whole place snapped into Doctor Who mode.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2398.jpeg&quot; alt=&quot;Exhibition entrance shaped like the TARDIS&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Exhibition entrance&lt;/p&gt;
&lt;p&gt;Right inside was a wall of costumes.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2400.jpeg&quot; alt=&quot;Doctor costumes from modern eras&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Modern Doctor costumes&lt;/p&gt;
&lt;p&gt;Nine, Ten, Eleven, Twelve, Thirteen. If you have been with the show for years, seeing them lined up still does something to you. Twelve’s outfit hit especially hard. It pulled me straight back to 2014, when I first started watching.&lt;/p&gt;
&lt;h2 id=&quot;props&quot;&gt;Props&lt;a class=&quot;anchor&quot; href=&quot;#props&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Further in were the console and the sonic screwdrivers&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. The console was close enough to actually put a hand on, which I obviously did.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2406.jpeg&quot; alt=&quot;TARDIS console&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;TARDIS console&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2408.jpeg&quot; alt=&quot;A display of sonic screwdrivers&quot; style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;sonic screwdrivers&lt;/p&gt;
&lt;p&gt;One display case held smaller props that do not show up very often, including Twelve’s sonic shades&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and the little device used to identify the Zygon Queen Elizabeth in the 50th anniversary special&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2410.jpeg&quot; alt=&quot;Props and gadgets from Doctor Who&quot; style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Small props&lt;/p&gt;
&lt;p&gt;There was also Handles&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;, the old Cyberman head that stayed with Eleven through the end. Nice to see him there.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2411.jpeg&quot; alt=&quot;Handles from The Time of the Doctor&quot; style=&quot;width: 55%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Handles&lt;/p&gt;
&lt;p&gt;Past the outfit section was a row of three spacesuits. The one on the left is River Song’s Apollo-style suit from the &lt;code&gt;S06&lt;/code&gt; arc&lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;. The middle one is from &lt;code&gt;S04E08-S04E09&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-7&quot; id=&quot;user-content-fnref-7&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-8&quot; id=&quot;user-content-fnref-8&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;, and seeing it immediately brought back &lt;code&gt;Hey, who turned out the lights?&lt;/code&gt;. The orange suit on the right is the classic one that keeps showing up all over the series, including Clara’s in &lt;code&gt;S08E07 Kill the Moon&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-9&quot; id=&quot;user-content-fnref-9&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2416.jpeg&quot; alt=&quot;Space suits in the exhibition&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Three spacesuits&lt;/p&gt;
&lt;p&gt;The Weeping Angel on the left is from &lt;code&gt;S03E10&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-10&quot; id=&quot;user-content-fnref-10&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;. The fluffy Meep on the right is from the 2023 60th anniversary &lt;code&gt;Special 1&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-11&quot; id=&quot;user-content-fnref-11&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2419.jpeg&quot; alt=&quot;Weeping Angel and the Meep&quot; style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Weeping Angel and The Meep&lt;/p&gt;
&lt;p&gt;Then came the villains&lt;sup&gt;&lt;a href=&quot;#user-content-fn-12&quot; id=&quot;user-content-fnref-12&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-13&quot; id=&quot;user-content-fnref-13&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-14&quot; id=&quot;user-content-fnref-14&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2422.jpeg&quot; alt=&quot;Dalek display&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Dalek&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2423.jpeg&quot; alt=&quot;Cyberman display&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Cybermen&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2424.jpeg&quot; alt=&quot;Sontaran costume&quot; style=&quot;width: 70%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Sontaran&lt;/p&gt;
&lt;p&gt;Up close, the Dalek eyestalk is still a little unnerving.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2428.jpeg&quot; alt=&quot;Close-up of a Dalek eyestalk&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Dalek eyestalk&lt;/p&gt;
&lt;p&gt;It was the first time I had ever seen the text on the TARDIS door from that close.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2431.jpeg&quot; alt=&quot;Close-up of the TARDIS door&quot; style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Text on the TARDIS door&lt;/p&gt;
&lt;p&gt;There was also the Teller from &lt;code&gt;S08E05 Time Heist&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-15&quot; id=&quot;user-content-fnref-15&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;15&lt;/a&gt;&lt;/sup&gt;, also known as &lt;code&gt;Brain Soup&lt;/code&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2435.jpeg&quot; alt=&quot;Alien creatures on display&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;The Teller&lt;/p&gt;
&lt;p&gt;Face of Boe / Captain Jack Harkness&lt;sup&gt;&lt;a href=&quot;#user-content-fn-16&quot; id=&quot;user-content-fnref-16&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;16&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-17&quot; id=&quot;user-content-fnref-17&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;17&lt;/a&gt;&lt;/sup&gt;. &lt;code&gt;Torchwood&lt;/code&gt; still holds up, honestly&lt;sup&gt;&lt;a href=&quot;#user-content-fn-18&quot; id=&quot;user-content-fnref-18&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;18&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2438.jpeg&quot; alt=&quot;The Face of Boe&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Face of Boe&lt;/p&gt;
&lt;p&gt;Nearby was the cat nun from &lt;code&gt;S02E01&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-19&quot; id=&quot;user-content-fnref-19&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;19&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2443.jpeg&quot; alt=&quot;Sister of Plenitude costume&quot; style=&quot;width: 65%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Sisters of Plenitude&lt;/p&gt;
&lt;p&gt;Silence will fall!&lt;sup&gt;&lt;a href=&quot;#user-content-fn-20&quot; id=&quot;user-content-fnref-20&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;20&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2448.jpeg&quot; alt=&quot;The Silence hanging from the ceiling&quot; style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Silence&lt;/p&gt;
&lt;h2 id=&quot;whiteboard-and-screening-area&quot;&gt;Whiteboard and Screening Area&lt;a class=&quot;anchor&quot; href=&quot;#whiteboard-and-screening-area&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I left a quick note on the whiteboard.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2456.jpeg&quot; alt=&quot;The whiteboard message left at the exhibition&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Message on the whiteboard&lt;/p&gt;
&lt;p&gt;There was a screening area next to it. When I was there, it was playing an introduction to the 2023 60th anniversary &lt;code&gt;Special 1&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-11&quot; id=&quot;user-content-fnref-11-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2460.jpeg&quot; alt=&quot;The Star Beast screening poster&quot; style=&quot;width: 70%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Screening area&lt;/p&gt;
&lt;p&gt;This timeline wall runs from 1963 to the present. Doctor Who has been going for a long time.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2497.jpeg&quot; alt=&quot;A timeline wall of different Doctors&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Doctor timeline&lt;/p&gt;
&lt;h2 id=&quot;gift-shop&quot;&gt;Gift Shop&lt;a class=&quot;anchor&quot; href=&quot;#gift-shop&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;T-shirts, tote bags, Dalek-patterned bags, the usual danger zone.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2500.jpeg&quot; alt=&quot;Doctor Who merchandise including T-shirts and bags&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Gift shop&lt;/p&gt;
&lt;p&gt;Novels, comics, spin-offs, all of it.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2504.jpeg&quot; alt=&quot;Doctor Who books and comics in the gift shop&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Novels and comics&lt;/p&gt;
&lt;h2 id=&quot;after-leaving&quot;&gt;After Leaving&lt;a class=&quot;anchor&quot; href=&quot;#after-leaving&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I walked out, it still was not dark over Balboa Park. The ground was wet, but the light was starting to come back.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2508.jpeg&quot; alt=&quot;Comic-Con Museum exterior after the visit&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;One last look on the way out&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2511.jpeg&quot; alt=&quot;Balboa Park amphitheater&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Balboa Park after the rain&lt;/p&gt;
&lt;p&gt;After that I wandered into the Japanese garden nearby. Daleks and Cybermen first, then water and stones. It was a pretty good shift in mood.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2518.jpeg&quot; alt=&quot;Japanese garden at Balboa Park&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Japanese garden&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2520.jpeg&quot; alt=&quot;Wooden structure in the Japanese garden&quot; style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Wooden structure in the garden&lt;/p&gt;
&lt;p&gt;By the end there was a sunset after the rain.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2523.jpeg&quot; alt=&quot;Balboa Park after rain&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Light after the rain&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://cdn.lyt0112.com/Posts/dw_worlds_of_wonder/IMG_2524.jpeg&quot; alt=&quot;Sunset at Balboa Park&quot; style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;The last sunset&lt;/p&gt;
&lt;p&gt;My favorite kind of surprise is still the kind I never planned for.&lt;/p&gt;
&lt;hr/&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor&quot; href=&quot;#footnote-label&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;What is the TARDIS? Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/news-and-features/what-is-the-tardis&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/news-and-features/what-is-the-tardis&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;Sonic shades. Tardis Wiki. &lt;a href=&quot;https://tardis.wiki/wiki/Sonic_shades&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tardis.wiki/wiki/Sonic_shades&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;The Day of the Doctor. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/the-day-of-the-doctor&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/the-day-of-the-doctor&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;Handles. Tardis Wiki. &lt;a href=&quot;https://tardis.wiki/wiki/Handles&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tardis.wiki/wiki/Handles&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;The Time of the Doctor. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/the-time-of-the-doctor&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/the-time-of-the-doctor&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;River Song. Tardis Wiki. &lt;a href=&quot;https://tardis.wiki/wiki/River_Song&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tardis.wiki/wiki/River_Song&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-6&quot; data-footnote-backref aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-7&quot;&gt;
&lt;p&gt;Silence in the Library. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/silence-in-the-library&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/silence-in-the-library&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-7&quot; data-footnote-backref aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-8&quot;&gt;
&lt;p&gt;Forest of the Dead. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/forest-of-the-dead&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/forest-of-the-dead&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-8&quot; data-footnote-backref aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-9&quot;&gt;
&lt;p&gt;Kill the Moon. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/kill-the-moon&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/kill-the-moon&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-9&quot; data-footnote-backref aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-10&quot;&gt;
&lt;p&gt;Blink. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/blink&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/blink&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-10&quot; data-footnote-backref aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-11&quot;&gt;
&lt;p&gt;The Star Beast. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/the-star-beast&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/the-star-beast&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-11&quot; data-footnote-backref aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-11-2&quot; data-footnote-backref aria-label=&quot;Back to reference 11-2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-12&quot;&gt;
&lt;p&gt;Daleks. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/characters/daleks&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/characters/daleks&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-12&quot; data-footnote-backref aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-13&quot;&gt;
&lt;p&gt;Cybermen. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/characters/cybermen&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/characters/cybermen&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-13&quot; data-footnote-backref aria-label=&quot;Back to reference 13&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-14&quot;&gt;
&lt;p&gt;Sontarans. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/characters/sontarans&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/characters/sontarans&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-14&quot; data-footnote-backref aria-label=&quot;Back to reference 14&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-15&quot;&gt;
&lt;p&gt;Time Heist. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/time-heist&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/time-heist&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-15&quot; data-footnote-backref aria-label=&quot;Back to reference 15&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-16&quot;&gt;
&lt;p&gt;Face of Boe. Tardis Wiki. &lt;a href=&quot;https://tardis.wiki/wiki/Face_of_Boe&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tardis.wiki/wiki/Face_of_Boe&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-16&quot; data-footnote-backref aria-label=&quot;Back to reference 16&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-17&quot;&gt;
&lt;p&gt;Captain Jack Harkness. Tardis Wiki. &lt;a href=&quot;https://tardis.wiki/wiki/Captain_Jack_Harkness&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tardis.wiki/wiki/Captain_Jack_Harkness&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-17&quot; data-footnote-backref aria-label=&quot;Back to reference 17&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-18&quot;&gt;
&lt;p&gt;Torchwood (TV series). Tardis Wiki. &lt;a href=&quot;https://tardis.wiki/wiki/Torchwood_(TV_series)&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tardis.wiki/wiki/Torchwood_(TV_series)&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-18&quot; data-footnote-backref aria-label=&quot;Back to reference 18&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-19&quot;&gt;
&lt;p&gt;New Earth. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/stories/new-earth&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/stories/new-earth&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-19&quot; data-footnote-backref aria-label=&quot;Back to reference 19&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-20&quot;&gt;
&lt;p&gt;The Silence. Doctor Who. &lt;a href=&quot;https://www.doctorwho.tv/characters/the-silence&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.doctorwho.tv/characters/the-silence&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-20&quot; data-footnote-backref aria-label=&quot;Back to reference 20&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/dw_worlds_of_wonder.Do0a2RVI.jpeg"/></item><item><title>Course Review for PKU Students</title><link>https://www.lyt0112.com/blog/course_review-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/course_review-zh</guid><description>Helpful Course Information for PKU Students</description><pubDate>Sat, 19 Apr 2025 23:56:52 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;Guide&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-green-500 bg-green-50 dark:bg-green-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-green-700 dark:text-green-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Guide&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;欢迎来到博主的课程评测页面!&lt;/p&gt;&lt;p&gt;使用电脑等宽屏设备浏览, 可以在右侧看到详细目录; 使用手机等移动设备, 点击右下角按钮同样可以方便地展开目录.&lt;/p&gt;&lt;p&gt;每门课程的资料链接都放在对应的介绍后面, 欢迎根据需要查阅.&lt;/p&gt;&lt;p&gt;如果这些内容对你有所帮助, 非常欢迎在下方评论区留下你的反馈和建议, 这不仅能帮助到我, 也能帮助到更多同学. 同时也欢迎给我的 GitHub 仓库点个 star 🌟, 你的支持是我持续更新的动力!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;祝大家选课顺利, 学业进步, 收获满满!&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;aside aria-label=&quot;Update&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Update&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Update on 2025.12.29:&lt;/p&gt;&lt;p&gt;又到了一年一度的 ICS 期末季, 选择这门课的同学们真的辛苦了. 想在这里分享一些感想.&lt;/p&gt;&lt;p&gt;从现在的视角来看, 很多课程资料, 尤其是 Lab 代码, 已经不再具有强的指导意义, 因为各种 coding agent 已能高效完成定义明确的课程 Lab 编程任务. 比如, &lt;a href=&quot;https://github.com/ICUlizhi/VibePkuMinic&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Vibe Coding: PKU Compiler Principles Lab&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 就展示了, 即使是难度最高的编译原理 Lab, 也可以完全交给 AI 来完成. 这与我在科研中的实践也相互印证.&lt;/p&gt;&lt;p&gt;因此, 最简单的, &lt;strong&gt;一定要学会使用 coding agent 写代码&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;其次, 在这样的环境下, 我们如何保持竞争力？这并不是几句话能说清的. 但正如那篇文章的作者所言：&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;当 Implementation 的边际成本趋近于零, Engineering 的价值便会从 “How to build” 向 “What to build” 剧烈迁移.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;希望大家能在这样的环境中找到自己真正喜欢的事情, 并全身心投入. 这才是大学阶段最值得去做的事.&lt;/p&gt;&lt;p&gt;Update on 2025.07.03:&lt;/p&gt;&lt;p&gt;我在 &lt;a href=&quot;https://www.lyt0112.com/blog/this_is_pku-zh&quot;&gt;这篇文章&lt;/a&gt; 中分享了整理课程资料的心路历程，欢迎感兴趣的同学阅读！&lt;/p&gt;&lt;p&gt;Update on 2025.06.22:&lt;/p&gt;&lt;p&gt;最后一科分析性英语写作 (一门非常好的不用上课的 C 级英语课) 卡着毕业生出分 DDL 出分了, 本科 150 学分课程全部完成, 毕业快乐!&lt;/p&gt;&lt;p&gt;Update on 2025.04.18:&lt;/p&gt;&lt;p&gt;这里有一个北大同学开发的课程资料网站: &lt;a href=&quot;https://pkuhub.cn&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PKUHUB&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;, 在开发团队的邀请下我上传了部分资料, 也可以看到别的同学上传的资料, 非常好的项目!&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;春秋学期均开设的课程&quot;&gt;春秋学期均开设的课程&lt;a class=&quot;anchor&quot; href=&quot;#春秋学期均开设的课程&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;编译原理&quot;&gt;编译原理&lt;a class=&quot;anchor&quot; href=&quot;#编译原理&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;刘先华, 王迪, 张路 2024秋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;大四了就去过三次课, 但是我感觉讲的还挺清楚的, 但是如果只看 slides 可能会很迷惑, 尤其是语法分析和 SDT 部分, 所以建议要么看书作为补充, 要么看看下面资料中的笔记.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;十五次作业, 九个 lab. 有的时候一周两次作业, 有的时候一周没有作业.&lt;/p&gt;
&lt;p&gt;建议在没有绩点压力的大四选这门课, 就可以不写 Lv9 了, 因为 Lv9 非常浪费时间并且占所有 Lab 的 27% 的分数, 也就是总评的 8 分, 如果在绩点压力下完成所有 Lab 会浪费很多时间.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;最后一节课有三十分钟的 Lab 小测, 开卷可以查阅任何资料, 我们的小测问题是:&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;SysY 文法是否有二义性问题, 你的编译器是如何解决的.&lt;/li&gt;
&lt;li&gt;简述 KoopalR (或你所使用的 IR) 的主要特点.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;期中期末都有考试, 期中之前好好学习了, 每周看 slides ; 期中之后没看过, 期末考试前两天速成, 对往年题做 few-shot learning 可以做到期中 90 分, 期末也差不多, 同样写写往年题就行了.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Compiler_Principles-2024Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;编译原理资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Compiler_Principles-2024Fall-PKU&quot;&gt;EmptyBlueBox/Compiler_Principles-2024Fall-PKU&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Compiler_Principles_Lab-2024Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;编译原理 Lab 代码&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Compiler_Principles_Lab-2024Fall-PKU&quot;&gt;EmptyBlueBox/Compiler_Principles_Lab-2024Fall-PKU&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/compiler_principles_lab_note-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;编译原理 Lab 笔记&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://arthals.ink/tags/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;课程知识笔记和 Lab 注意事项 from Arthals&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2025.01.26: 81&lt;/p&gt;
&lt;p&gt;大四满足了 🎊&lt;/p&gt;
&lt;h3 id=&quot;操作系统&quot;&gt;操作系统&lt;a class=&quot;anchor&quot; href=&quot;#操作系统&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;陈向群 2025春&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;去了 0 次 + 最后一节课的 10 分钟.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;xv6 实验&lt;/li&gt;
&lt;li&gt;四次书面作业&lt;/li&gt;
&lt;li&gt;二十几次上课小测&lt;/li&gt;
&lt;li&gt;一次期中考试 (半开卷, 可以带五张正反面 A4 纸)&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;/p&gt;
&lt;p&gt;考到第六章虚拟内存技术.&lt;/p&gt;
&lt;p&gt;非常抽象的事情是考试方式阴晴不定, 我了解到的信息 (大概是这样):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2023年春季学期没有期中考试&lt;/li&gt;
&lt;li&gt;2023年秋季学期期中考试闭卷&lt;/li&gt;
&lt;li&gt;2024年春季学期期中考试闭卷&lt;/li&gt;
&lt;li&gt;2024年秋季学期期中考试完全开卷, 可以带任意多资料&lt;/li&gt;
&lt;li&gt;2025年春季学期期中考试部分开卷, 可以带 5 张 A4 纸&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;期末考试:&lt;/p&gt;
&lt;p&gt;这两年期末老师提到必考&lt;a href=&quot;https://github.com/EmptyBlueBox/Operating_Systems-2025Spring-PKU/blob/main/Resource/%E6%80%9D%E8%80%83%E9%A2%98.md#02-unix%E5%92%8Cfat16%E5%B8%83%E5%B1%80%E6%A8%A1%E6%8B%9F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;UNIX和FAT16布局模拟&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;, 上课也仔细讲了这道题, 如果没时间可以看看最后两次课的内容, &lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_10-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;经典的同步问题&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 和 &lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_11-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;死锁&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 的内容.&lt;/p&gt;
&lt;p&gt;期末考试题目回忆:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;举一个使用了 &lt;code&gt;mmap&lt;/code&gt; 的例子, 并解释这样做有什么好处, 使用了什么数据结构&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Operating_Systems-2025Spring-PKU/blob/main/Resource/%E8%AF%BE%E5%A0%82%E5%B0%8F%E6%B5%8B-%E6%9C%9F%E4%B8%AD%E5%89%8D.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;期中前课堂小测&lt;span&gt; ↗&lt;/span&gt;&lt;/a&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;/li&gt;
&lt;li&gt;就是上面这个 UNIX 文件系统布局模拟, 但是用了两级目录, 然后提问某个文件读取其中一段访问了几个数据块&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;code&gt;Ctrl+C&lt;/code&gt; 后发生了什么, 给了大约20个选项排序, 包括陷入内核的时候要并发调度到别的进程&lt;/li&gt;
&lt;li&gt;同步问题
&lt;ol&gt;
&lt;li&gt;用信号量解决三峡大坝船闸问题&lt;/li&gt;
&lt;li&gt;用管程解决生产者-消费者问题, 并说明你用的是 Hoare 管程还是 MESA 管程&lt;/li&gt;
&lt;li&gt;用信号量管理共享资源, 询问 “I’ll Do it for You” 是否是正确解法, 然后给 “Pass the Baton” 解法填空&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;xv6 代码题 (主要是跟作业相关的)
&lt;ol&gt;
&lt;li&gt;解释 &lt;code&gt;uvmalloc&lt;/code&gt; 怎么实现的, 主要是解释其中的 &lt;code&gt;for&lt;/code&gt; 循环在做什么&lt;/li&gt;
&lt;li&gt;解释为什么要获取 &lt;code&gt;bcache&lt;/code&gt; 的锁, 然后解释磁盘块高速缓存怎么实现 &lt;code&gt;LRU&lt;/code&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;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;课程笔记:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_01-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;01. 操作系统概述&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_02-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;02. 中断异常机制&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_03-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;03. 进程线程模型&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_04-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;04. 进程线程调度&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_05-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;05. 内存管理概述&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_06-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;06. 虚拟内存技术&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_07-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;07. 文件系统 1&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_08-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;08. 文件系统 2&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_09-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;09. 并发机制&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_10-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;10. 经典的同步问题&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/operating_systems_note_11-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;11. 死锁&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Operating_Systems-2025Spring-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;操作系统资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Operating_Systems-2025Spring-PKU&quot;&gt;EmptyBlueBox/Operating_Systems-2025Spring-PKU&lt;/a&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;Lab 代码 (下面这个仓库作为 submodule)&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;a href=&quot;https://github.com/EmptyBlueBox/Operating_Systems_Lab-2025Spring-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;操作系统 Lab 代码仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Operating_Systems_Lab-2025Spring-PKU&quot;&gt;EmptyBlueBox/Operating_Systems_Lab-2025Spring-PKU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个仓库包括:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lab 代码&lt;/li&gt;
&lt;li&gt;Lab 实验文档&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;80&lt;/p&gt;
&lt;p&gt;终究还是给了 80 而不是 80- 吗, 哈基cnn, 你这家伙…&lt;/p&gt;
&lt;h3 id=&quot;射箭&quot;&gt;射箭&lt;a class=&quot;anchor&quot; href=&quot;#射箭&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;张冰 2024秋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;超难选超好玩给分超好体育课.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/055f0043a11fe58770a68267dced1984.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 这个博主射箭超厉害 &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/3e98c1e86518aedeafc3f6f7be2d84fd.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 最后一节课的有趣射箭小游戏, 第一名有奖牌 &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;只有期末理论方面的一篇手写论文, 和射箭相关写多少字都行, 我就写了两段话半张 A4 纸.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;期末会有两节课的射箭排位赛, 具体方式就是每节课每人射 8 组箭, 前两组用作热身不算成绩, 每组箭 6 枝, 两节课总共算 72 枝箭的成绩, 最后全班按照总环数排名.&lt;/p&gt;
&lt;p&gt;基本上 650 环可以拿到第一名, 博主 610 环第四名.&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;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2024.12.30: 97&lt;/p&gt;
&lt;p&gt;疑似体测扣了 3 分, 射箭本体满分…🌚…&lt;/p&gt;
&lt;h2 id=&quot;秋季学期课程&quot;&gt;秋季学期课程&lt;a class=&quot;anchor&quot; href=&quot;#秋季学期课程&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;信息安全引论&quot;&gt;信息安全引论&lt;a class=&quot;anchor&quot; href=&quot;#信息安全引论&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;王昭 2024秋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;大四专业选修凑学分课, 疑似没听过. Slides 也非常非常极尽迷惑之事, 根本看不明白, 感觉不如看课本, 但是课本太多了, 所以建议前几节课学学后面的就不用学了, 尤其是期中之后的知识考试涉及不多.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;三次书面作业, 六次小测, 两次作业小测, 四次课堂小测, 但是课堂小测可以下来问同学密码是多少然后补测.&lt;/p&gt;
&lt;p&gt;一次写代码的 Lab, 若干次在一个在线平台上的 Lab, 第一个 Lab 需要费点时间, 后面的 Lab 比较简单, 一个小时就能做一个.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;没有期中考试, 期末考试 60% 为作业题, 大四同学好好复习作业题和小测题即可, 在仓库里有详细解析的作业题和小测题自取~&lt;/p&gt;
&lt;p&gt;期末题型为填空, 判断, 选择, 简答, 大题, 体感来讲如果好好复习了作业题和小测题确实至少能拿 60 分, 博主体感正经写了 80 分左右的题目, 剩下 20 分就瞎写了.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Information_Security-2024Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;信息安全引论资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Information_Security-2024Fall-PKU&quot;&gt;EmptyBlueBox/Introduction_to_Information_Security-2024Fall-PKU&lt;/a&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;Lab 解答&lt;/li&gt;
&lt;li&gt;小测题, 不过已经总结进期末复习选择题总结里了&lt;/li&gt;
&lt;li&gt;课本&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2025.01.16: 84&lt;/p&gt;
&lt;p&gt;大四满足了 🎊&lt;/p&gt;
&lt;h3 id=&quot;强化学习&quot;&gt;强化学习&lt;a class=&quot;anchor&quot; href=&quot;#强化学习&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;李文新 2024秋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;大四就去过两次课, 感觉不如看 slides, 但是上课回答问题可以加分, 最高加三分. 同时时不时就有传纸条签到的环节, 但是完全可以让同学帮忙写姓名学号.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;八次作业, 一次大作业, 一次课程大作业 Presentation.&lt;/p&gt;
&lt;p&gt;每次小作业差不多花一两个小时, 就是复现一些经典的 RL 算法, 有的调参还挺烦的. 其中最后一个作业是在开悟平台上的控制 agent 走迷宫, 只需要调整 reward 然后续训就能达到 1430 左右的分数.&lt;/p&gt;
&lt;p&gt;大作业是在四个项目里选择, 分别是开悟平台王者1v1对战, 国标麻将, 双升, 和掼蛋的强化学习模型训练, 每个大作业按照跟同学的对战胜负排名天梯给分. 建议选择王者1v1对战, 因为可以复用最后一次小作业的经验, 并且不用修改代码就能跑起来, 最后只需要调整 reward 和模型架构即可.&lt;/p&gt;
&lt;p&gt;大作业 Presentation 就是每组上台汇报自己的 RL agent 训练经验, 汇报持续两周, 第一周汇报的同学因为训练时间更少所以有一分加分.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;没有期中考试, 期末考试 60 道选择题. 拟合小测题和往年题即可.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Reinforcement_Learning-2024Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;强化学习资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Reinforcement_Learning-2024Fall-PKU&quot;&gt;EmptyBlueBox/Reinforcement_Learning-2024Fall-PKU&lt;/a&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;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;89&lt;/p&gt;
&lt;p&gt;给分吹爆, 没有任何额外加分都能给大四这么高, 优秀率 60%.&lt;/p&gt;
&lt;p&gt;来自树洞 7073734:&lt;/p&gt;
&lt;p&gt;学长学长, 你的地概确实很强, 但还是太吃优秀率限制了, 有没有什么更加简单又好拿分的课程推荐一下吗？&lt;/p&gt;
&lt;p&gt;有的兄弟！有的！像这么好的课程北大当然是不止一个了, 一共有九个, 当时当前学期t0.5的优质课程, 随便选上一个都能够轻松彩虹, 如果九门都选上那你的院系排名将会冲顶, 如果九门课都像学长一样拿到满分的水平, 那我可以说你的成绩可以直接冲五四奖学金都是没有问题的了.&lt;/p&gt;
&lt;p&gt;2025年, zkc教员宣告水课地概已不复存在, 考试重点不再勾画, 优秀率也不再给满, 新形势下, 老九门课程讲何去何从？我们还有没有机会重铸彩虹荣光？&lt;/p&gt;
&lt;p&gt;有的兄弟！有的！我推荐你来选嘻嘻科学技术学院课程：强化学习. 本课程由李文新教授讲授, 课堂幽默风趣互动良多愉快有加分, 且加分有上限无内卷, 作业两周一个轻松容易, 不考勤有慕课复习无压力, 考核内容有趣不紧张, 助教贴心全程陪学有问必答答必解惑, lab框架清晰同学交流密切, 更有前沿讲座各界大佬分享技术交流思想, 考题不超纲全是单选且与教学内容绝对平行不正交, 有往年题提供开源自信从不隐瞒, 大作业选题宽泛麻将掼蛋王者荣耀应有尽有, 即使你对强化学习算法一窍不通也可以简单多次训练拿到很好的表现加强对强化学习的了解与认知.&lt;/p&gt;
&lt;p&gt;选择强化学习, 重铸彩虹荣光！&lt;/p&gt;
&lt;p&gt;兄弟！有没有心动！&lt;/p&gt;
&lt;p&gt;兄弟！有的！&lt;/p&gt;
&lt;h3 id=&quot;智能机器人概论&quot;&gt;智能机器人概论&lt;a class=&quot;anchor&quot; href=&quot;#智能机器人概论&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;赵卉菁 2023秋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;听感一般, 老师讲课很认真, 但是三教的教室实在是太温暖了, 让人不得不睡觉…&lt;/p&gt;
&lt;p&gt;最后来的人越来越少, 老师就会以前一节课的 slides 内容作为小测内容, 在下一节课小测, 但是准确率不重要, 是用作签到的.&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;/p&gt;
&lt;p&gt;附上我们组的期末大作业视频以供参考: &lt;a href=&quot;https://b23.tv/gBXGwpG&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Bilibili&lt;span&gt; ↗&lt;/span&gt;&lt;/a&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;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Intelligent_Robots-zhj-2023Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;智能机器人概论资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Intelligent_Robots-zhj-2023Fall-PKU&quot;&gt;EmptyBlueBox/Introduction_to_Intelligent_Robots-zhj-2023Fall-PKU&lt;/a&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;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;93&lt;/p&gt;
&lt;p&gt;感觉是大作业的 idea 让老师感觉很有趣.&lt;/p&gt;
&lt;h3 id=&quot;计算机网络&quot;&gt;计算机网络&lt;a class=&quot;anchor&quot; href=&quot;#计算机网络&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;黄群 2023秋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;黄群老师讲课是自顶向下的, 和同一个学期的计网实验班是相反的顺序.&lt;/p&gt;
&lt;p&gt;老师讲得挺好的, 可以把 slides 和口述部分连接起来形成逻辑链条, 课程值得一听.&lt;/p&gt;
&lt;p&gt;讲课的时候老师还喜欢加入长者的梗, +1s&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;没有平时作业.&lt;/p&gt;
&lt;p&gt;四个 lab 选三个最高分, 我选择的是 1, 2, 4, 首先第四个 lab 半个小时就能写完, 一定要选; 第一个 lab 也不难, 而且是刚开学时间充裕, 建议选; 最后我还建议选第二个 lab, 因为第三个 lab 可能和期末考试时间有冲突, 难度也不低.&lt;/p&gt;
&lt;p&gt;MOOC 水过, ddl 是期末之后, 及格就行.&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;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Computer_Networks-hq-2023Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;计算机网络资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Computer_Networks-hq-2023Fall-PKU&quot;&gt;EmptyBlueBox/Computer_Networks-hq-2023Fall-PKU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个仓库包括:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;我的期末复习笔记.&lt;/li&gt;
&lt;li&gt;Labs , 但是第三个 Router Lab 仍然有 bug , 但是由于只需要完成四个 lab 中的三个即可, 所以这个 lab 就没有彻底完成.&lt;/li&gt;
&lt;li&gt;一些相关资源, 包括一些往年题.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;88&lt;/p&gt;
&lt;p&gt;疑似期末考得一般.&lt;/p&gt;
&lt;h3 id=&quot;博弈论&quot;&gt;博弈论&lt;a class=&quot;anchor&quot; href=&quot;#博弈论&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;刘霖 2023秋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是政府学院的刘霖教授开设的三学分博弈论课程, 课程分为四个部分介绍了不同种类的博弈论, 知识内容还是比较丰富的, 老师上课也会积极与同学互动, 叫同学上台回答问题.&lt;/p&gt;
&lt;p&gt;但是刘霖教授的讲课方式注定了这门课需要下课自己好好思考学习. 因为老师的 slides 只有大纲, 具体的知识点都是口述, 所以上课是必要的, 但是又由于老师讲课有的时候比较模糊, 所以需要下课复习回放. 在复习回放的基础上, 没有必要看其他的补充教材.&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;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;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Game_Theory-ll-2023Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;博弈论资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Game_Theory-ll-2023Fall-PKU&quot;&gt;EmptyBlueBox/Game_Theory-ll-2023Fall-PKU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个仓库包括:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;课件&lt;/li&gt;
&lt;li&gt;带有笔记的课件 (我的主要贡献是给老师较为简陋的 slides 上记录了非常详尽的笔记, 基本上都是根据老师口述所整理的, 感觉对这门课的学习会有非常大的帮助)&lt;/li&gt;
&lt;li&gt;课程作业&lt;/li&gt;
&lt;li&gt;一些相关资源 (包括往年的考试和往年带有答案的作业题)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;100&lt;/p&gt;
&lt;h3 id=&quot;魅力化学&quot;&gt;魅力化学&lt;a class=&quot;anchor&quot; href=&quot;#魅力化学&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;黄建滨 2022秋&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;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;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.lyt0112.com/blog/crystal-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;我的魅力化学课程论文&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;97&lt;/p&gt;
&lt;p&gt;唯一一个上 90 的论文课… 想必是论文写出了真情实感吧🥹&lt;/p&gt;
&lt;h3 id=&quot;计算机系统导论&quot;&gt;计算机系统导论&lt;a class=&quot;anchor&quot; href=&quot;#计算机系统导论&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;管雪涛 2022秋&lt;/p&gt;
&lt;p&gt;2024.09.05 Update: 兹喜闻zzy同学当选为24届27班ICS助教, 特此表示热烈祝贺!&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;a href=&quot;https://arthals.ink/blog/data-lab&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;更适合北大宝宝体质的 XXX Lab 踩坑记&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Computer_System_ICS-gxt-2022Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;计算机系统导论资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Computer_System_ICS-gxt-2022Fall-PKU&quot;&gt;EmptyBlueBox/Introduction_to_Computer_System_ICS-gxt-2022Fall-PKU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个仓库包括:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;所有lab的满分解答 (针对2022年版本的labs, 由于包含面向样例优化, 之后的年份可能由于测试样例的改变导致性能急剧下降, 比如下降到80分左右)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;94&lt;/p&gt;
&lt;p&gt;历史成绩不具备现实意义.&lt;/p&gt;
&lt;h3 id=&quot;数据结构与算法-a&quot;&gt;数据结构与算法 (A)&lt;a class=&quot;anchor&quot; href=&quot;#数据结构与算法-a&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;邹磊 2022秋&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;中规中矩, 感觉看看 slides 就可以了. 对于学习巩固算法知识还是有用的.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;中规中矩, 每一章有一些算法题和几道书面算法题, 尽量熟练使用 STL 可以提高编程效率.&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;/p&gt;
&lt;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Data_Structures_And_Algorithms_A-zl-2022Fall-PKU/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;数据结构与算法资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Data_Structures_And_Algorithms_A-zl-2022Fall-PKU/&quot;&gt;EmptyBlueBox/Data_Structures_And_Algorithms_A-zl-2022Fall-PKU/&lt;/a&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;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;91&lt;/p&gt;
&lt;p&gt;历史成绩不具备现实意义.&lt;/p&gt;
&lt;h3 id=&quot;中级微观经济学-legacy&quot;&gt;中级微观经济学 (Legacy)&lt;a class=&quot;anchor&quot; href=&quot;#中级微观经济学-legacy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;秦晋 2023秋&lt;/p&gt;
&lt;p&gt;Update 2024.09.25: 看老师在朋友圈发了一暑假照片哈哈哈&lt;/p&gt;
&lt;p&gt;Update 2024.06.06: 听说老师要走了, 真的很可惜, 虽然在考试和难度方面有争议, 但是老师讲的还是很认真的, 也对教书这件事情很热情. 希望老师能找到可以实现理想的地方.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;非常不错, Dr.J 讲课条理清晰, 用填空的形式带领补全思维链条, 微观经济学本身就有清晰的理论依据, 这更使得这门课听起来简洁明了. 如果学过吴泽南的经济学原理, 那么这门课的内容大部分是在经济学原理中已经讲授过的, 所以给我一种在复习经济学原理的错觉(x&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作业不难, 可能偶尔有一两道比较tricky的问题&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;出分之后在树洞上引起了不小的讨论, 主要是在讨论这样给分是否合理. 由于调分力度很大, 树洞里大量的彩虹和 84.5 样本说明成绩分布有极端化的趋势, 我觉得在调分的时候适当引入一些梯度可以更好评价每一位同学的付出程度. 当然无论如何调分力度都很大, 期末是助教出题, 题目较难, 但是最后的成绩很满意!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Intermediate_Microeconomics-qj-2023Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;中级微观经济学资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Intermediate_Microeconomics-qj-2023Fall-PKU&quot;&gt;EmptyBlueBox/Intermediate_Microeconomics-qj-2023Fall-PKU&lt;/a&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;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;100&lt;/p&gt;
&lt;p&gt;历史成绩不具备现实意义.&lt;/p&gt;
&lt;h2 id=&quot;春季学期课程&quot;&gt;春季学期课程&lt;a class=&quot;anchor&quot; href=&quot;#春季学期课程&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;计算机视觉导论&quot;&gt;计算机视觉导论&lt;a class=&quot;anchor&quot; href=&quot;#计算机视觉导论&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;王鹤 2024春&lt;/p&gt;
&lt;p&gt;2024.07.12 Update: 出分哩, 大三结束!&lt;/p&gt;
&lt;p&gt;2024.06.18 Update: PDF 格式的笔记在 GitHub 上作为 &lt;code&gt;Releases&lt;/code&gt; 发布&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Github 仓库所有笔记更新完毕, 完结撒花!!!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2024.06.08 Update: Embodied AI 相关笔记&lt;/p&gt;
&lt;p&gt;2024.06.01 Update: Generative Model 相关笔记&lt;/p&gt;
&lt;p&gt;2024.05.25 Update: Detection and Instance Segmentation 相关笔记&lt;/p&gt;
&lt;p&gt;2024.05.18 Update: Transformer 相关笔记&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;(以下是在课程笔记前写的前言)&lt;/p&gt;
&lt;p&gt;作为北京大学信息科学技术学院的学生, 长期以来饱受糟糕课程质量, 糟糕课程作业, 糟糕考试难度的折磨.  比如算法设计与分析的等课程的教学质量极低, 教考分离, ICS 考试一面黑板的考试错误题目订正等等.  在这样的环境下,  遇到了王鹤老师开设的计算机视觉导论课程, 这门课程的内容丰富, 作业质量高, 考试难度适中, 称得上是精品课程 (与算分这种国家精品课程相区别) .&lt;/p&gt;
&lt;p&gt;王鹤老师将计算机视觉的发展脉络呈现给大家, 在这个深度学习时代, 老师并没有完全忽视传统 CV 的方法, 而是挑选了其中具有代表性的工作, 这些工作为深度学习时代的 CV 打下了良好的基础, 提供了许多基础工具和数据集的构建方式. 同时老师也更加注重深度学习的基础知识, 如 BatchNorm 的特性和与其他 Norm 的区别, 许多人仅仅只是会 PyTorch 的积木搭建, 但是对于这些基础知识的原理和性质却不甚了解, 导致在实际使用中遇到问题时无法解决, 王老师在这方面往往提出 intuitive 的问题, 引人深思.&lt;/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;作业是四次 lab , 没有其它作业, 对我来讲周更笔记算是比较有趣的事情, 一边看回放一边记记笔记需要两三个小时, 预计总时长会在25小时左右 (更新完之后发现总时长在35小时左右) .&lt;/p&gt;
&lt;p&gt;Lab1&lt;/p&gt;
&lt;p&gt;都不能使用 for 循环&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;实现卷积, 包括 pedding , 使用 Toeplitz Matrix 实现卷积, 使用 Sliding Window 实现卷积&lt;/li&gt;
&lt;li&gt;实现 Canny Edge Detector, 包括包括 NMS 和 Edge Linking with Hysteresis Threshold&lt;/li&gt;
&lt;li&gt;实现 Harris Corner Detector&lt;/li&gt;
&lt;li&gt;使用 RANSAC 进行平面 fitting&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lab2&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;手工实现反向传播 (不是很要求矩阵求导, 考试也不考察矩阵求导)&lt;/li&gt;
&lt;li&gt;手工实现 Batch Norm&lt;/li&gt;
&lt;li&gt;使用前两问的函数在 Cifar-10 上训练一个 CNN&lt;/li&gt;
&lt;li&gt;使用 PyTorch 实现一个 CNN&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lab3&lt;/p&gt;
&lt;p&gt;除了 Marching Cube 都不能使用 for 循环&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;实现相机校准&lt;/li&gt;
&lt;li&gt;从 depth image 重建点云&lt;/li&gt;
&lt;li&gt;从 mesh sample 点云, 使用两种 metric 计算点云距离&lt;/li&gt;
&lt;li&gt;使用 Marching Cube 从 SDF 重建 mesh&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lab4&lt;/p&gt;
&lt;p&gt;可以使用 for 循环&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;实现 Point Net&lt;/li&gt;
&lt;li&gt;Mask RCNN&lt;/li&gt;
&lt;li&gt;RNN&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;时间: 3-4个小时&lt;/p&gt;
&lt;p&gt;lab质量较前三个有所下降, 大概是因为这几个网络复杂度提高了太多了, 想要好好写任务量太大了.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;期中考试&lt;/p&gt;
&lt;p&gt;还是有一些难度的, cheatsheet实际上用处不大, 记录一些公式或者课上老师口述的例子就够了, 我考试的时候就看了两三次, 还是确认一下我记的名词是不是对的&lt;/p&gt;
&lt;p&gt;需要对CV的知识和老师上课讲的 insight 比较熟悉才能拿到90+的分数&lt;/p&gt;
&lt;p&gt;期末考试&lt;/p&gt;
&lt;p&gt;几个记忆比较深刻的地方&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对于 RCNN, Fast RCNN, Faster RCNN 的算法流程要熟悉, 比如具体为什么 Fast RCNN 比 RCNN 快, 快在哪里了&lt;/li&gt;
&lt;li&gt;Embodied AI 和一些相关知识点我的笔记都有提到&lt;/li&gt;
&lt;li&gt;SDF 哪边是正数, 因为我有个同学记错了🙂‍↔️&lt;/li&gt;
&lt;li&gt;Object detection 的 IoU, PR曲线, AP计算模拟&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不过大三大四应该是不调分的, 我的原始成绩98.15, 最后还向下取整了🥹&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Computer_Vision-wh-2024Spring-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;计算机视觉导论资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Computer_Vision-wh-2024Spring-PKU&quot;&gt;EmptyBlueBox/Introduction_to_Computer_Vision-wh-2024Spring-PKU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个仓库包括:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Latex 格式的课程笔记, 编译好的 PDF 格式笔记可以在 &lt;code&gt;Releases&lt;/code&gt; 中下载&lt;/li&gt;
&lt;li&gt;Lab 及其解答&lt;/li&gt;
&lt;li&gt;一些相关 Cheatsheet&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;98&lt;/p&gt;
&lt;h3 id=&quot;计算机组织与体系结构&quot;&gt;计算机组织与体系结构&lt;a class=&quot;anchor&quot; href=&quot;#计算机组织与体系结构&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;陆俊林 2024春&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;老师讲的挺好的, 讲课水平算信科中上等, 并且3学分的课按理来说有一天是奇偶周上课, 但是这一天不用上课, 老师安排自己看MOOC, 所以上起来就像两学分的课.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一次 Report on the EDVAC&lt;/li&gt;
&lt;li&gt;每周MOOC课后题&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;考试考的是老师上课和MOOC的并集, 所以既需要上课也需要看MOOC, 很抽象.&lt;/p&gt;
&lt;p&gt;期中考试比较简单, 拟合往年题.&lt;/p&gt;
&lt;p&gt;期末考试: 我在写这段话的时候刚考完期末, 15道不定项选择题.&lt;/p&gt;
&lt;p&gt;大题:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;读一段小汇编代码&lt;/li&gt;
&lt;li&gt;流水线分析, 数加入气泡之后总共几个周期, 有点像 ICS 的考试题&lt;/li&gt;
&lt;li&gt;外部中断的流程填空, 串行和并行数据传输的优缺点, MSI的优缺点&lt;/li&gt;
&lt;li&gt;经典Burst传输图, 背slides上的图就可以了&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;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Computer_Architectures-ljl-2024Spring-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;计算机组织与体系结构资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Computer_Architectures-ljl-2024Spring-PKU&quot;&gt;EmptyBlueBox/Computer_Architectures-ljl-2024Spring-PKU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个仓库包括:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;课程讲义 (not available, copyright belongs to Prof. Junlin Lu)&lt;/li&gt;
&lt;li&gt;MOOC笔记&lt;/li&gt;
&lt;li&gt;课程作业&lt;/li&gt;
&lt;li&gt;一些其它资料 (往年考题和考试提纲)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;85&lt;/p&gt;
&lt;h3 id=&quot;数据库概论&quot;&gt;数据库概论&lt;a class=&quot;anchor&quot; href=&quot;#数据库概论&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;陈立军 2024春&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;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;六次作业, 五次实习, 虽然每次都借助 ChatGPT 完成, 用不了半天时间, 但是东西还挺多的, 主要是 ChatGPT 面对这种问题答案往往是不对的.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;没有期中考试, 这是好的.&lt;/p&gt;
&lt;p&gt;期末考试听说会考很多 sides 上面的东西, 过拟合一下可怜的一点点往年题好了.&lt;/p&gt;
&lt;p&gt;考完之后发现需要好好拟合往年题, 不论是期中还是期末.&lt;/p&gt;
&lt;p&gt;首先是15道填空, 一个一分, 考的很细致, 比如如何使用关系代数判定函数依赖和多值依赖&lt;/p&gt;
&lt;p&gt;大题:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;五个简答题, 元组关系演算, 期中往年题计算一个SQL查询的结果, 另外的记不清了&lt;/li&gt;
&lt;li&gt;期中往年题, 关系代数计算, 比如左外链接, 计算最小值, 基础关系运算表示除法, 四个小题还有一个忘了&lt;/li&gt;
&lt;li&gt;期中往年题, 股票SQL查询&lt;/li&gt;
&lt;li&gt;第一小题是计算一个比较复杂的函数依赖的候选码, 判断是哪个范式; 第二小题是期末往年题, 给定关系模式 R 和函数依赖集 F, 画出合适的 E-R 图 (提示: 先给出保持函数依赖的分解, 再观察各关系主外码的关系)&lt;/li&gt;
&lt;li&gt;期中往年题, 篮球比赛ER图&lt;/li&gt;
&lt;li&gt;视图可串行化+三个并发协议, 看看 slides 上面的例题即可&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;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Database_Systems-clj-2024Spring-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;数据库概论资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Database_Systems-clj-2024Spring-PKU&quot;&gt;EmptyBlueBox/Introduction_to_Database_Systems-clj-2024Spring-PKU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个仓库包括:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;课程讲义&lt;/li&gt;
&lt;li&gt;笔记 (基本上包含了老师上课口述的问题和知识, 从 &lt;code&gt;Releases&lt;/code&gt; 下载)&lt;/li&gt;
&lt;li&gt;课程作业 (包含六次作业和五次实习)&lt;/li&gt;
&lt;li&gt;一些其它资料 (往年考题和考试提纲)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;89.5&lt;/p&gt;
&lt;h3 id=&quot;经济学原理-ⅱ&quot;&gt;经济学原理 (Ⅱ)&lt;a class=&quot;anchor&quot; href=&quot;#经济学原理-ⅱ&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;庄晨 2023春&lt;/p&gt;
&lt;p&gt;Update June 5th, 2024: 祝愿带了我两个学期的经济学原理的李亦丁助教学长在美国的博士研究顺利!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这门课程是庄晨教授在北大第一次授课, 课程听起来难以通过 slides 和老师的口述建立起连贯的知识体系, 或许这也是宏观经济学本身的问题, 不像微观经济学那样有明确的唯一的数学模型.&lt;/p&gt;
&lt;p&gt;感觉课程难度偏难, 这是因为老师强调经济学直觉, 对于一些问题没有拿出数学工具进行分析, 这就导致在一些宏观问题上有些迷惑, 一知半解.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;较少, 作业比课程 slides 清楚&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;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Principle_of_Economics_II-zc-2023Spring-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;经济学原理 (Ⅱ) 资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Principle_of_Economics_II-zc-2023Spring-PKU&quot;&gt;EmptyBlueBox/Principle_of_Economics_II-zc-2023Spring-PKU&lt;/a&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;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;96&lt;/p&gt;
&lt;h3 id=&quot;ai引论&quot;&gt;AI引论&lt;a class=&quot;anchor&quot; href=&quot;#ai引论&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;刘利斌 2023春&lt;/p&gt;
&lt;p&gt;Update 2025.02.20: 我来当助教啦&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这门课程是在2023年课程改革后的第一年, 比之前的AI引论好了不少 (之前的AI引论纯 flybitch), 在课程结构上更加符合认知规律了. 但是对于比较重要的 deep learning 部分, 和隔壁的 AI 基础相比讲授还是偏少, 期中之前的搜索讲的太多太细致了.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作业不多, 但是lab想要满分还是需要一些时间的&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;考试&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;期中期末可以携带一张手写的 cheatsheet, 并且 cheatsheet 会被收走 (猜想可能是为了不让祖传cheatsheet) , 有难度, cheatsheet也用不上. 在此感慨隔壁 AI 基础考试半个小时拿到满分的 &lt;a href=&quot;https://arthals.ink&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;卓宝&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 同学真是太强了…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Artificial_Intelligence-llb-2023Spring-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;AI引论资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Introduction_to_Artificial_Intelligence-llb-2023Spring-PKU&quot;&gt;EmptyBlueBox/Introduction_to_Artificial_Intelligence-llb-2023Spring-PKU&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个仓库包括:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自己总结的课程笔记 (较为粗糙, 仅供参考)&lt;/li&gt;
&lt;li&gt;课程lab解答&lt;/li&gt;
&lt;li&gt;一些python练习 (许多同学没有python基础)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;94&lt;/p&gt;
&lt;h3 id=&quot;程序设计实习&quot;&gt;程序设计实习&lt;a class=&quot;anchor&quot; href=&quot;#程序设计实习&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;刘家瑛 2022春&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;课程听感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这门课程的教学质量算是信科中上水平的, 尤其是刘家瑛老师讲课还是很有趣可爱的, 有的时候还会讲一些关于保研出国的建议.&lt;/p&gt;
&lt;p&gt;这门课程前半学期是 c++ 类和对象的语法部分, 跟着 openjudge 上面的题目和课件正常学习就好了, 后半学期是算法部分, 对于非竞赛生可能需要多努力一些, 并且学着多使用 STL 会提高不少编程效率.&lt;/p&gt;
&lt;p&gt;因为疫情原因, 数学考试延期至下学期开学进行, 所以我有充足的时间练习算法题, 这就是仓库里为什么会有那么多算法题的解答.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作业/任务量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作业就是每周写语法, 算法题, 平时任务量不大, 但是著名的魔兽世界还是需要一些时间完成的, 笔者当时是五一之前上午期中考完试, 下午写的终极版魔兽世界, 对上一版魔兽世界做了非常多的修改, 之后又零零碎碎花了一些时间对拍 debug , 魔兽世界对拍 debug 真是非常好的方法.&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;strong&gt;相关资料&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Practice_of_Programming_in_C_and_CPP_cxsjsx-ljy-2022Spring-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;程序设计实习资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Practice_of_Programming_in_C_and_CPP_cxsjsx-ljy-2022Spring-PKU&quot;&gt;EmptyBlueBox/Practice_of_Programming_in_C_and_CPP_cxsjsx-ljy-2022Spring-PKU&lt;/a&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;li&gt;算法模板整理&lt;/li&gt;
&lt;li&gt;Qt 大作业&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;成绩&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;91&lt;/p&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/course_review_blog.B1jpJWuy.png"/></item><item><title>Real-World RL</title><link>https://www.lyt0112.com/blog/real_world_rl-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/real_world_rl-en</guid><description>How real-world RL pulls robots out of virtual worlds and lets them learn in the only environment that matters.</description><pubDate>Mon, 17 Nov 2025 23:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Physical Intelligence recently released the technical report for &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;sup&gt;&lt;a href=&quot;#user-content-fn-7&quot; id=&quot;user-content-fnref-7&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. One thing that caught my eye is that they run real-world RL directly on physical robots. Real-world RL has been quietly piling up results over the past few years, so this feels like a good moment to look back at how the line of work has evolved and where it might go next.&lt;/p&gt;
&lt;h2 id=&quot;background-what-is-real-world-rl&quot;&gt;Background: What Is Real-World RL?&lt;a class=&quot;anchor&quot; href=&quot;#background-what-is-real-world-rl&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a long time, robot RL happened almost entirely inside a simulator. MuJoCo, Isaac Gym, Isaac Lab, ManiSkill are really training grounds. You drop a virtual robot in, let it run hundreds of millions of actions, and eventually get a policy that maximizes return.&lt;/p&gt;
&lt;p&gt;Once the policy stabilizes in sim, you try to move it onto a real robot. That step usually needs extra tricks like domain randomization, sim-to-real transfer, or residual actions, all aimed at closing the dynamics mismatch between simulation and the real world. This mismatch is the familiar &lt;code&gt;Sim2Real Gap&lt;/code&gt;. The more dynamic and long-horizon the task, the more obvious the gap becomes.&lt;/p&gt;
&lt;p&gt;Real-World RL wants to take another route: let real-robot data carry most of the learning, use simulation only as a rough warm-up if at all, and improve the policy through repeated real interaction. The direct payoff is that training and deployment happen under the same dynamics, so the Sim2Real Gap is simply not there.&lt;/p&gt;
&lt;p&gt;The cost is also concrete. Hardware breaks, objects fall off the table, environments are messy, and humans cannot sit next to a robot forever.&lt;/p&gt;
&lt;h2 id=&quot;first-stage-from-learning-in-simulation-to-learning-in-reality&quot;&gt;First Stage: From Learning in Simulation to Learning in Reality&lt;a class=&quot;anchor&quot; href=&quot;#first-stage-from-learning-in-simulation-to-learning-in-reality&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you only look at the titles, DayDreamer and A Walk in the Park look like two completely separate paths, one from the world-model side, the other from the model-free side.&lt;/p&gt;
&lt;p&gt;Stretched out over a longer timeline, though, they are really doing the same thing: showing that RL in the real world is actually doable.&lt;/p&gt;
&lt;h3 id=&quot;daydreamer-not-in-world-in-world-model&quot;&gt;DayDreamer: Not in World, in World Model&lt;a class=&quot;anchor&quot; href=&quot;#daydreamer-not-in-world-in-world-model&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What DayDreamer does, put bluntly, is move Dreamer onto real robots and see whether it can still learn. It runs on several platforms, learning to navigate, learning balance, learning locomotion, all without simulation or human teleoperation, with data collection and training running online in parallel. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The world model matters here. It is effectively an internal simulator. The policy can train inside the learned model first and only then execute in the real world, which keeps the number of real interactions manageable.&lt;/p&gt;
&lt;p&gt;That single trick is what makes online RL on a real robot actually feasible.&lt;/p&gt;
&lt;h3 id=&quot;a-walk-in-the-park-algorithm-or-engineering&quot;&gt;A Walk in the Park: Algorithm or Engineering?&lt;a class=&quot;anchor&quot; href=&quot;#a-walk-in-the-park-algorithm-or-engineering&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A year later, A Walk in the Park gave a much blunter result. No world model. No new algorithm. Mostly just SAC.&lt;/p&gt;
&lt;p&gt;But with a carefully designed control stack, state representation, and training loop, an A1 learns to walk stably outdoors in roughly 20 minutes.&lt;/p&gt;
&lt;p&gt;The real takeaway from this paper is that the win came from system engineering, not from a new loss function.&lt;/p&gt;
&lt;h3 id=&quot;robocat-self-generated-data&quot;&gt;RoboCat: Self-Generated Data&lt;a class=&quot;anchor&quot; href=&quot;#robocat-self-generated-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In 2023, DeepMind’s RoboCat went in a somewhat different direction. It wasn’t designed for Real-World RL per se, but it plays like a rehearsal for what real-world RL eventually needs. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;RoboCat is built on a Gato-style visual decision Transformer. It first trains a generalist agent on demonstration data covering many robots and many tasks, and then enters a self-improvement loop: humans give 100~1000 demos for a new task, the model fine-tunes, practices in sim or on real robots about ten thousand times, and the generated trajectories feed back into the training set to produce a newer, stronger version.&lt;/p&gt;
&lt;p&gt;It doesn’t lean on online RL the way DayDreamer or A Walk in the Park does, but it plants another important idea:&lt;/p&gt;
&lt;p&gt;A general robot policy can keep getting stronger by generating its own data and improving itself.&lt;/p&gt;
&lt;p&gt;This idea comes back as a core theme later in &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2 id=&quot;second-stage-from-can-learn-to-learns-well&quot;&gt;Second Stage: From Can Learn to Learns Well&lt;a class=&quot;anchor&quot; href=&quot;#second-stage-from-can-learn-to-learns-well&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first stage answered a basic question: can real-world RL learn behaviors at all.&lt;/p&gt;
&lt;p&gt;Once you try to deploy any of these systems, you start caring about two other things: whether the robot can run for a long time without going off the rails, and whether the success rate can climb close to 100%.&lt;/p&gt;
&lt;p&gt;A lot of the work between 2023 and 2025 is essentially an answer to those two questions.&lt;/p&gt;
&lt;h3 id=&quot;hil-serl-data--human-corrections--rl&quot;&gt;HIL-SERL: Data + Human Corrections + RL&lt;a class=&quot;anchor&quot; href=&quot;#hil-serl-data--human-corrections--rl&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;HIL-SERL (Human-in-the-Loop Sample-Efficient RL) comes from Berkeley RAIL, and it appeared in Science Robotics 2025. The tasks it targets are much harder than learning to walk: dynamic shaking to pull out a Jenga block, precise assembly, dual-arm coordination, pan tossing while cooking, real manipulation. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The training recipe is simple but works. Collect good and bad trajectories by teleoperation. Train a binary reward model that decides success or failure. Use a small number of demos to initialize the policy. Then run online RL on the real robot with humans stepping in at key moments to correct the behavior, and let the policy improve on those corrections together with the learned reward.&lt;/p&gt;
&lt;p&gt;The results are direct. On a set of complex manipulation tasks, HIL-SERL pushes the success rate of vision-based policies close to 100% after only 1~2.5 hours of interaction, and the final execution is actually faster than human teleoperation.&lt;/p&gt;
&lt;p&gt;Two points from this paper had a strong influence on later work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Real-world RL shouldn’t start from random exploration, it should stand on top of demonstrations&lt;/li&gt;
&lt;li&gt;Human intervention isn’t noise, it’s the component that makes RL both safe and efficient&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can read it as an upgrade of what DayDreamer and A Walk in the Park did: from can learn to learns well, and learns fast.&lt;/p&gt;
&lt;h3 id=&quot;rl-100-systematizing-the-pipeline&quot;&gt;RL-100: Systematizing the Pipeline&lt;a class=&quot;anchor&quot; href=&quot;#rl-100-systematizing-the-pipeline&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If HIL-SERL is still a method, RL-100 has already grown into an engineering system.&lt;/p&gt;
&lt;p&gt;RL-100 proposes a three-stage pipeline: imitation learning to inject human experience into a diffusion policy, offline RL with OPE (offline policy evaluation) for conservative policy improvement, and finally a short online RL phase on the real robot to clean up the remaining failure modes. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;They validated it on seven real-robot tasks, including cloth folding, pouring fluids and granular materials, dynamic pushing, dexterous nut tightening, and multi-stage orange juicing. Across 900 evaluations they report 900/900 successes, with some tasks running 250 times in a row without a single failure.&lt;/p&gt;
&lt;p&gt;Technically, RL-100 and HIL-SERL share the same spirit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Both rely on demos and offline data to keep the starting point reasonable&lt;/li&gt;
&lt;li&gt;All exploration stays inside safety boundaries monitored by OPE or humans&lt;/li&gt;
&lt;li&gt;RL’s job is to patch long-tail failures, not to invent motions from scratch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What RL-100 adds is the thing that actually matters: it turns the full pipeline into a framework that is relatively agnostic to tasks, robot platforms, and sensing modalities. That is a step from paper demo toward a reusable system.&lt;/p&gt;
&lt;h3 id=&quot;contact-rich-sim-to-real-a-compromise&quot;&gt;Contact-Rich Sim-to-Real: A Compromise&lt;a class=&quot;anchor&quot; href=&quot;#contact-rich-sim-to-real-a-compromise&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For assembly and tight insertion, where contact mechanics are very sensitive, learning entirely in the real world is still too risky. Work from Tomizuka’s group proposes a hybrid route: learn trajectories and compliance parameters with RL in simulation, and in the real world only fine-tune a small admittance residual online. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Methods like this are less flashy than HIL-SERL or RL-100, but in industrial settings they are very practical. Most of the risk gets handled in sim, and real-world RL only applies a small residual update.&lt;/p&gt;
&lt;p&gt;It’s probably fair to see this as an important side branch in the second stage: Real-World RL doesn’t always play the leading role, it can also sit as the final adaptation layer in sim-to-real.&lt;/p&gt;
&lt;h2 id=&quot;third-stage-from-task-specific-to-general-policies&quot;&gt;Third Stage: From Task-Specific to General Policies&lt;a class=&quot;anchor&quot; href=&quot;#third-stage-from-task-specific-to-general-policies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the work so far, the main character is still the robot learning a single task.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; does something slightly counter-intuitive: it changes the training target of RL from a specific task to a general policy.&lt;/p&gt;
&lt;h3 id=&quot;a-good-enough-general-vla&quot;&gt;A Good-Enough General VLA&lt;a class=&quot;anchor&quot; href=&quot;#a-good-enough-general-vla&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Physical Intelligence released &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5806em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; in 2024. This model is essentially a vision-language-action (VLA) foundation model: internet-scale vision-language pretraining plus large-scale robot data, trained so that a single model can generalize across robots and tasks in a zero-shot and few-shot fashion. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-8&quot; id=&quot;user-content-fnref-8&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.5&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.5}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5806em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5806em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; then added model scale, data, and architectural capacity on top, yielding a general policy that can basically get the job done on many household and simple industrial tasks. But, as with all the systems above, it hits the familiar ceiling: success rates are passable but still not quite good enough for real deployment.&lt;/p&gt;
&lt;p&gt;That is the backdrop &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; lands in.&lt;/p&gt;
&lt;h3 id=&quot;rl-with-experience--corrections&quot;&gt;RL with Experience &amp;amp; Corrections&lt;a class=&quot;anchor&quot; href=&quot;#rl-with-experience--corrections&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; report describes a staged recipe: offline pretraining, supervised fine-tuning, and online correction-driven RL. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-7&quot; id=&quot;user-content-fnref-7-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Look closely at the learning signal, though, and Recap (RL with Experience &amp;amp; Corrections via Advantage-conditioned Policies) is closer to preference-conditioned supervised regression than to textbook RL. The value function regresses onto returns on the collected trajectories. The policy regresses onto actions in the dataset, but conditioned on advantage, so the model learns to bias toward higher-value choices. The crucial new piece compared with plain imitation is that &lt;strong&gt;failures are no longer thrown away as noise&lt;/strong&gt;. They get labelled explicitly as negative, and the model learns what to actively avoid. &lt;sup&gt;&lt;a href=&quot;#user-content-fn-9&quot; id=&quot;user-content-fnref-9&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Concretely, the pipeline looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run offline RL on &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5806em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; to let the model tell good actions from bad on offline data. That means training a value function on the model’s own trajectories, regressing it onto sparse returns, computing an advantage signal that scores actions as better or worse than average, and feeding that advantage into the VLA as a condition&lt;/li&gt;
&lt;li&gt;For each specific task, fine-tune on human demos so the model has a decent starting point&lt;/li&gt;
&lt;li&gt;Deploy the model on the real robot and let it run the task on its own. Humans only step in on clear mistakes, and those corrections are logged as supervision in the failure states, feeding back into the RL loop above&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The role of RL here is mostly to expose errors and turn failures into training signal, while the advantage-conditioned policy spreads those fixes to nearby states.&lt;/p&gt;
&lt;p&gt;Does it work? The report gives specific numbers and cases. On complex tasks like making espresso, assembling cardboard boxes, and folding different kinds of clothes, adding Recap roughly doubles the throughput (successful tasks per unit time), and cuts the failure rate to half or less. They run robots from 5:30 AM to 11:30 PM making coffee, or fold 50 unseen garments in a stranger’s house, or assemble 59 real packaging boxes on a factory line, all without model errors ending the run early.&lt;/p&gt;
&lt;p&gt;Zoom out, and &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; stands very naturally on the shoulders of the earlier work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Like HIL-SERL, it uses the demos + human corrections + RL trio to attack long-tail failures&lt;/li&gt;
&lt;li&gt;Like RL-100, it treats RL as a final repair layer that takes you from sometimes wrong to rarely wrong&lt;/li&gt;
&lt;li&gt;But it goes further: it isn’t optimizing a policy for one task, it’s fine-tuning a general-purpose large model&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, Real-World RL’s role has shifted from a skill-learning algorithm to a last-mile training tool for a general policy.&lt;/p&gt;
&lt;h2 id=&quot;summary-and-outlook&quot;&gt;Summary and Outlook&lt;a class=&quot;anchor&quot; href=&quot;#summary-and-outlook&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you compress the whole trajectory, it reads something like this.&lt;/p&gt;
&lt;p&gt;Early work showed that real-world RL is possible at all. Later systems started solving stability and efficiency. And models like &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; have folded Real-World RL into a general robot training pipeline.&lt;/p&gt;
&lt;p&gt;The research paradigm has been shifting quietly alongside. Early on, the discussion was about new RL algorithms. Then people realized that most of the problems are actually about system engineering. Later still, RL is increasingly used to patch the long-tail errors a large model makes in the real world.&lt;/p&gt;
&lt;p&gt;Simulation still has value, but its role is different. It looks more like a pre-training stage than the final learning ground.&lt;/p&gt;
&lt;p&gt;Some directions that look interesting from here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Larger-scale real-world data&lt;/li&gt;
&lt;li&gt;More automated human intervention and safety mechanisms&lt;/li&gt;
&lt;li&gt;More complex manipulation tasks, especially dexterous manipulation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From a system-design angle, Real-World RL is essentially answering two questions right now. Which capabilities should be handled by demos or offline training, so the model is already reasonably competent. And which problems genuinely need RL to touch the real world.&lt;/p&gt;
&lt;p&gt;The answer &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; gives is pretty plain: pretraining and demos get the system to a point where it can finish tasks, and Real-World RL takes care of the failure cases, slowly closing whatever gap is left.&lt;/p&gt;
&lt;hr/&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor&quot; href=&quot;#footnote-label&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-7&quot;&gt;
&lt;p&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0.6&lt;/mn&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0.6}^*&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9368em;vertical-align:-0.2481em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6887em&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0.6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;: A VLA that Learns from Experience. Physical Intelligence Blog, 2025-11-17. &lt;a href=&quot;https://www.pi.website/blog/pistar06&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.pi.website/blog/pistar06&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-7&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-7-2&quot; data-footnote-backref aria-label=&quot;Back to reference 1-2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;DayDreamer: World Models for Physical Robot Learning. CoRL 2022. &lt;a href=&quot;https://danijar.com/project/daydreamer/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://danijar.com/project/daydreamer/&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;RoboCat: A Self-Improving Generalist Agent for Robotic Manipulation. DeepMind, 2023. &lt;a href=&quot;https://arxiv.org/abs/2306.11706&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2306.11706&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-6&quot; data-footnote-backref aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;HIL-SERL: Precise and Dexterous Robotic Manipulation via Human-in-the-Loop Sample-Efficient Robotic Reinforcement Learning. Science Robotics, 2025. &lt;a href=&quot;https://hil-serl.github.io/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://hil-serl.github.io/&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;Kun Lei et al. RL-100: Performant Robotic Manipulation with Real-World Reinforcement Learning. arXiv:2510.14830, 2025. &lt;a href=&quot;https://arxiv.org/abs/2510.14830&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2510.14830&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;Xiang Zhang et al. Efficient Sim-to-real Transfer of Contact-Rich Manipulation Skills with Online Admittance Residual Learning. CoRL 2023. &lt;a href=&quot;https://arxiv.org/abs/2310.10509&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://arxiv.org/abs/2310.10509&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-8&quot;&gt;
&lt;p&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;π&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pi_{0}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5806em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;π&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;: A Vision-Language-Action Flow Model for General Robot Control. Physical Intelligence Blog, 2024-10-31. &lt;a href=&quot;https://www.physicalintelligence.company/blog/pi0&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.physicalintelligence.company/blog/pi0&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-8&quot; data-footnote-backref aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-9&quot;&gt;
&lt;p&gt;Pi 0.6 : 披着Reinforcement Learning 外衣的 Supervised Learning, 2026-01-13. &lt;a href=&quot;https://mp.weixin.qq.com/s/O7QOFeyjMDlg8Y5xDVbJNA&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://mp.weixin.qq.com/s/O7QOFeyjMDlg8Y5xDVbJNA&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-9&quot; data-footnote-backref aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/real_world_rl.BPc2AD6a.png"/></item><item><title>An Unfinished Story</title><link>https://www.lyt0112.com/blog/this_is_pku-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/this_is_pku-en</guid><description>So we beat on, boats against the current, borne back ceaselessly into the past.</description><pubDate>Wed, 02 Jul 2025 12:43:24 GMT</pubDate><content:encoded>&lt;h2 id=&quot;an-unfinished-story&quot;&gt;An Unfinished Story&lt;a class=&quot;anchor&quot; href=&quot;#an-unfinished-story&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My last day as an undergraduate at Peking University felt just like the morning I first arrived four years ago. The air then was cool and filled with a vague but passionate hope for the future; today, the warmth is tinged with the sadness of parting.&lt;/p&gt;
&lt;p&gt;As I start to write, I realize the torrent of thoughts that once surged within me has crumbled into fragments. Graduation, it turns out, isn’t a grand finale but a quiet exit. I never found a single label for these four years. They feel like an untitled manuscript, a hurried ending to a quest for meaning that was just beginning.&lt;/p&gt;
&lt;p&gt;Four years was long enough to eat my weight in braised chicken from the Shaoyuan canteen. Yet, it was short enough that I never managed to try breakfast anywhere but the Jiayuan and Xueyi canteens, or find time to just sit in the old library and nap between study sessions. I always thought I had all the time in the world, only to watch it slip away. These small, unfinished tasks, like un-dog-eared pages in a book, have become part of the story, reminding me that life is a journey with its share of blank spaces.&lt;/p&gt;
&lt;p&gt;On my first day, I met two upperclassmen on the winding path in front of the Qiu Deba Sports Hall. Their simple “Welcome to PKU” became my first real connection to this campus. The path sloped upwards, much like my own winding but ever-forward journey. Before I even started, I told myself to accept my own ordinariness and make peace with my expectations. I tried to listen to my inner voice, but was ultimately consumed by the pressure of my GPA. Peking University is vast enough to hold countless dreams; grades are just one measure of success, and by no means the only one. A person’s worth should never be defined by a transcript. What matters more is finding what makes your eyes light up, what allows your voice to be heard and your presence to be felt. Making your voice heard and your efforts count is far more meaningful than exhausting yourself on a crowded track.&lt;/p&gt;
&lt;p&gt;As a small experiment in my sophomore year, I began organizing my &lt;a href=&quot;https://www.lyt0112.com/blog/course_review-zh&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;course materials&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;. I wasn’t trying to build a monument, just leave a trail of footprints—a small light for those who would come after me. If it helped someone avoid a few detours on their own path, that would be the best farewell I could imagine. Maybe one day I’ll fade from everyone’s memory. But if, late one night, a student looks at my notes and softly reads the title, in that moment, my youth will be with them. My thoughts will have found a small piece of eternity on this campus. This endeavor was also inspired by my dear friend &lt;a href=&quot;https://arthals.ink&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Arthals&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;, who showed me that sharing knowledge is an act of creation in itself.&lt;/p&gt;
&lt;p&gt;The time to say goodbye has come. We turn, packing the seasons of Peking University into our bags, and head toward a future without walls. Most of us will fade from the grand narratives and become ordinary people, our faces perhaps indistinct in a crowd, but our lives incredibly real.&lt;/p&gt;
&lt;p&gt;But this isn’t the end of the story. Remember the young man who once gazed at the stars from Building 45B. Hold on to that stubborn refusal to compromise with the world. That is the most precious treasure in our bags. It will remind us in the storms to come that even on an ordinary path, we must create our own echo.&lt;/p&gt;
&lt;h2 id=&quot;epilogue&quot;&gt;Epilogue&lt;a class=&quot;anchor&quot; href=&quot;#epilogue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Destiny, it seems, has a habit of leaving clues where you least expect them. As one of the first students from the School of Information Science and Technology to check in, I was the first to step into Building 45B. After watching several floor managers come and go, by a strange twist of fate, I inherited the “mantle” of being the WeChat group admin for the third and fourth floors. This absurd little detail became an unexpected chapter in my story—perhaps time’s own quirky way of commemorating that first morning my journey began.&lt;/p&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/qdb.CVzIg79R.jpeg"/></item><item><title>Operating Systems Notes 11: Deadlock</title><link>https://www.lyt0112.com/blog/operating_systems_note_11-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_11-zh</guid><description>Operating Systems Notes 11: Deadlock</description><pubDate>Fri, 06 Jun 2025 15:40:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-核心概念&quot;&gt;1. 核心概念&lt;a class=&quot;anchor&quot; href=&quot;#1-核心概念&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;我们首先要对死锁以及与它相关的几个概念有一个清晰的认识。&lt;/p&gt;
&lt;h3 id=&quot;11-并发编程的问题&quot;&gt;1.1 并发编程的问题&lt;a class=&quot;anchor&quot; href=&quot;#11-并发编程的问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在复杂的并发程序 (如数据库MySQL、Web服务器Apache等) 中，开发者会遇到各种各样的缺陷。研究表明，这些缺陷主要分为两大类：死锁缺陷和非死锁缺陷 (如原子性违背、顺序错误等) 。其中，非死锁的缺陷占了绝大多数 (约97%) ，但这并不意味着死锁不重要。一旦发生死锁，其后果往往是灾难性的，可能导致整个系统挂起。&lt;/p&gt;
&lt;h3 id=&quot;12-死锁-deadlock&quot;&gt;1.2 死锁 (Deadlock)&lt;a class=&quot;anchor&quot; href=&quot;#12-死锁-deadlock&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;121-定义&quot;&gt;1.2.1 定义&lt;a class=&quot;anchor&quot; href=&quot;#121-定义&quot;&gt;#&lt;/a&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;/ol&gt;
&lt;p&gt;简单来说，就是两个或多个进程形成了一个”僵局”，比如进程A等着进程B手里的资源，而进程B又恰好在等进程A的资源，谁也不肯先放手，就这么永远地等下去了。&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;h4 id=&quot;122-为什么会发生死锁&quot;&gt;1.2.2 为什么会发生死锁？&lt;a class=&quot;anchor&quot; href=&quot;#122-为什么会发生死锁&quot;&gt;#&lt;/a&gt;&lt;/h4&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;可重用资源 (Reusable Resource): 可被多个进程多次使用，不会被消耗掉。例如：CPU、内存、磁盘、文件、数据库、信号量。&lt;/li&gt;
&lt;li&gt;可消耗资源 (Consumable Resource): 只能使用一次，可以被创建和销毁。例如：信号、中断、消息。&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;复杂的依赖关系: 在大型软件中，不同模块间的调用关系复杂，可能在不经意间形成一个锁的循环依赖，导致死锁。&lt;/li&gt;
&lt;li&gt;封装的副作用: 模块化编程隐藏了实现细节，这本是好事。但调用者可能不知道一个看似简单的接口内部会去获取某个锁，当多个这样的接口被组合调用时，就可能意外地触发死锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;13-活锁-livelock&quot;&gt;1.3 活锁 (Livelock)&lt;a class=&quot;anchor&quot; href=&quot;#13-活锁-livelock&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;活锁很有意思，它和死锁很像，但又有所不同。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义: 进程没有被阻塞 (Block) ，它们的状态在持续改变，看起来很”活跃”，但却无法向前推进任何有效的工作。&lt;/li&gt;
&lt;li&gt;例子: 想象两个人 (进程) 在一条狭窄的走廊相遇，都想给对方让路。A向左让，B也向左让，结果又堵住了；A向右让，B也向右让，又堵住了。两者都在不停地”尝试”和”让步” (状态在改变) ，但就是过不去 (没有进展) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在代码中，活锁通常表现为进程在循环中不断尝试获取锁，但因为某些条件 (例如礼让策略) 总是失败。&lt;/p&gt;
&lt;h3 id=&quot;14-饥饿-starvation&quot;&gt;1.4 饥饿 (Starvation)&lt;a class=&quot;anchor&quot; href=&quot;#14-饥饿-starvation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定义: 一个或多个进程由于资源分配策略不公，导致长时间甚至永远无法获得所需的资源，从而无法向前推进。&lt;/li&gt;
&lt;li&gt;例子: 在一个优先级调度系统中，如果总有高优先级的任务到来，那么低优先级的任务可能永远也得不到CPU时间，这就”饿”死了。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;15-死锁活锁与饥饿的区别&quot;&gt;1.5 死锁、活锁与饥饿的区别&lt;a class=&quot;anchor&quot; href=&quot;#15-死锁活锁与饥饿的区别&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;特征&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;死锁 (Deadlock)&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;活锁 (Livelock)&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;饥饿 (Starvation)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;进程状态&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;阻塞 (Waiting/Blocked)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;运行 (Running/Active)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;阻塞或就绪 (Blocked/Ready)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;系统进展&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;无进展&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;无进展&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能有其他进程在进展&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;原因&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;循环等待资源且不释放&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;错误的重试/礼让逻辑&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;不公平的资源分配策略&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;2-死锁的产生&quot;&gt;2. 死锁的产生&lt;a class=&quot;anchor&quot; href=&quot;#2-死锁的产生&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;一个系统中要发生死锁，必须同时满足以下四个必要条件。只要能破坏其中任意一个，死锁就不会发生。&lt;/p&gt;
&lt;h3 id=&quot;21-四大必要条件&quot;&gt;2.1 四大必要条件&lt;a class=&quot;anchor&quot; href=&quot;#21-四大必要条件&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;互斥使用 (Mutual Exclusion)&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;占有且等待 (Hold and Wait)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;描述: 进程至少占有一个资源，并且在等待获取其他进程占有的额外资源。&lt;/li&gt;
&lt;li&gt;例子: 进程P1占有了资源R1，然后又去请求资源R2，但R2被P2占有。此时P1就处于”占有R1且等待R2”的状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不可抢占 (No Preemption)&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;循环等待 (Circular Wait)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;描述: 存在一个进程-资源的循环链，P1在等P2的资源，P2在等P3的资源，…，Pn在等P1的资源，形成一个闭环。&lt;/li&gt;
&lt;li&gt;数学表示: 存在一个进程集合 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;{&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;.&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;.&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;.&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;}&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\{P_0, P_1, ..., P_n\}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1514em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，使得 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 等待 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 持有的资源，&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 等待 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 持有的资源，…，&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1514em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 等待 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 持有的资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-资源分配图-resource-allocation-graph---rag&quot;&gt;3. 资源分配图 (Resource Allocation Graph - RAG)&lt;a class=&quot;anchor&quot; href=&quot;#3-资源分配图-resource-allocation-graph---rag&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;为了更直观地描述和分析死锁，我们引入了资源分配图。&lt;/p&gt;
&lt;h3 id=&quot;31-rag的构成&quot;&gt;3.1 RAG的构成&lt;a class=&quot;anchor&quot; href=&quot;#31-rag的构成&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RAG是一个有向图 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;G&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;G=(V, E)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.2222em&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0576em&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，包含两类节点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程节点 (P): 用圆形表示，如 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_1, P_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8778em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;。&lt;/li&gt;
&lt;li&gt;资源节点 (R): 用方形表示。方框内的点表示该类资源的实例数量。例如，一个方框代表”打印机”这类资源，里面的两个点代表有两台打印机。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;图中包含两类有向边：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;申请边 (Request Edge): 从进程指向资源类，表示该进程正在申请该类资源的一个实例。&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i \rightarrow R_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0077em&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0077em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;。&lt;/li&gt;
&lt;li&gt;分配边 (Assignment Edge): 从资源实例指向进程，表示该资源实例已经被分配给了这个进程。&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;R_j \rightarrow P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0077em&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0077em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-rag-图例&quot;&gt;3.2 RAG 图例&lt;a class=&quot;anchor&quot; href=&quot;#32-rag-图例&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;下面是一些RAG的例子，我们可以用Mermaid流程图来表示它们。&lt;/p&gt;
&lt;h4 id=&quot;321-交通路口死锁&quot;&gt;3.2.1 交通路口死锁&lt;a class=&quot;anchor&quot; href=&quot;#321-交通路口死锁&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;十字路口的四个区域可以看作四个资源，四辆车看作四个进程。每辆车都占用了自己当前的路口，同时又想进入前方的路口。&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    P1(P1) --&amp;gt; Ra[Ra]
    P2(P2) --&amp;gt; Rb[Rb]
    P3(P3) --&amp;gt; Rc[Rc]
    P4(P4) --&amp;gt; Rd[Rd]
    Ra --&amp;gt; P2
    Rb --&amp;gt; P3
    Rc --&amp;gt; P4
    Rd --&amp;gt; P1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这就形成了一个 &lt;code&gt;P1 -&amp;gt; R_a -&amp;gt; P2 -&amp;gt; R_b -&amp;gt; P3 -&amp;gt; R_c -&amp;gt; P4 -&amp;gt; R_d -&amp;gt; P1&lt;/code&gt; 的循环等待，导致死锁。&lt;/p&gt;
&lt;h3 id=&quot;33-死锁定理&quot;&gt;3.3 死锁定理&lt;a class=&quot;anchor&quot; href=&quot;#33-死锁定理&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RAG和死锁之间存在一个重要的关系，我们称之为死锁定理：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果RAG中没有环路，则系统中一定没有死锁。&lt;/li&gt;
&lt;li&gt;如果RAG中存在环路，则系统中可能存在死锁。
&lt;ul&gt;
&lt;li&gt;特殊情况：如果每个资源类都只有一个实例，那么环路是死锁存在的充分必要条件 (即有环必死锁) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;331-有环但无死锁的例子&quot;&gt;3.3.1 有环但无死锁的例子&lt;a class=&quot;anchor&quot; href=&quot;#331-有环但无死锁的例子&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;为什么有环路却”可能”没有死锁呢？这通常用来阐明在每类资源拥有多个实例的情况下，环路是死锁的必要不充分条件。看下面这个例子。&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    R1[&amp;quot;R1 (2)&amp;quot;]
    R2[&amp;quot;R2 (2)&amp;quot;]
    P1(P1)
    P2(P2)
    P3(P3)
    P4(P4)

    R2 --&amp;quot;持有&amp;quot;--&amp;gt; P1
    R1 --&amp;quot;持有&amp;quot;--&amp;gt; P2
    R1 --&amp;quot;持有&amp;quot;--&amp;gt; P3
    R2 --&amp;quot;持有&amp;quot;--&amp;gt; P4

    P1 --&amp;quot;请求&amp;quot;--&amp;gt; R1
    P3 --&amp;quot;请求&amp;quot;--&amp;gt; R2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在这个图中，存在环路 &lt;code&gt;P1 → R1 → P3 → R2 → P1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;虽然图中存在一个 &lt;code&gt;P1 → R1 → P3 → R2 → P1&lt;/code&gt; 的循环等待，但我们可以通过一个简单的推演 (这其实就是资源图化简法的思路) 发现，系统仍然可以找到一个让所有进程都完成的执行序列，因此没有死锁：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;我们观察到进程 &lt;code&gt;P2&lt;/code&gt; 和 &lt;code&gt;P4&lt;/code&gt;。它们虽然持有资源，但没有请求任何新资源 (即没有从它们出发的指向资源框的请求边) 。这意味着它们不处于等待状态，可以顺利执行完毕并释放资源。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;P2 (或 P4) 先完成：假设 &lt;code&gt;P2&lt;/code&gt; 先执行完毕，它会释放它持有的1个 &lt;code&gt;R1&lt;/code&gt; 资源实例。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;此时，系统可用的 &lt;code&gt;R1&lt;/code&gt; 资源变为1个 (&lt;code&gt;Available R1 = 1&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;P1的请求被满足：&lt;code&gt;P1&lt;/code&gt; 一直在等待 &lt;code&gt;R1&lt;/code&gt;。现在系统有可用的 &lt;code&gt;R1&lt;/code&gt; 了，于是就可以把这个 &lt;code&gt;R1&lt;/code&gt; 实例分配给 &lt;code&gt;P1&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;P1&lt;/code&gt; 获得了所需的全部资源 (它原来持有的&lt;code&gt;R2&lt;/code&gt;和新获得的&lt;code&gt;R1&lt;/code&gt;) ，可以继续执行直到完成。僵局被打破。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;P1完成并释放资源：&lt;code&gt;P1&lt;/code&gt; 完成后，会释放它持有的所有资源：1个 &lt;code&gt;R1&lt;/code&gt; 实例和1个 &lt;code&gt;R2&lt;/code&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;code&gt;P1&lt;/code&gt; 释放的 &lt;code&gt;R2&lt;/code&gt; 可以满足 &lt;code&gt;P3&lt;/code&gt; 的请求，&lt;code&gt;P3&lt;/code&gt; 也能完成。&lt;code&gt;P4&lt;/code&gt; 本来就可以自己完成。最终，所有进程都能顺利结束。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因为存在一个可以让所有进程都完成的执行序列 (例如 &lt;code&gt;&amp;lt;P2, P1, P4, P3&amp;gt;&lt;/code&gt;) ，所以系统没有发生死锁。这个例子说明了：当每个资源类有多个实例时，资源分配图中存在环路是死锁的必要条件，但不是充分条件。&lt;/p&gt;
&lt;h3 id=&quot;34-资源分配图化简&quot;&gt;3.4 资源分配图化简&lt;a class=&quot;anchor&quot; href=&quot;#34-资源分配图化简&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我们可以通过一种称为”图化简”的方法来检测死锁。基本思想是：模拟系统资源的分配和释放过程，看看是否能满足所有进程的需求。&lt;/p&gt;
&lt;p&gt;化简步骤:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在图中寻找一个既不阻塞 (没有申请边) 也非孤立的进程节点 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;。&lt;/li&gt;
&lt;li&gt;释放该进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 所占有的所有资源，抹去所有与它相连的分配边，使其变为孤立节点。这些释放的资源现在可用于满足其他进程的请求。&lt;/li&gt;
&lt;li&gt;检查这些新释放的资源是否能满足某个正在等待的进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 的请求。如果可以，将 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 的申请边变为分配边。现在 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 获得了所需资源，可以继续运行，未来它也会释放所有资源。我们可以把它也看作一个可以被”消去”的节点。&lt;/li&gt;
&lt;li&gt;重复以上步骤，直到无法再找到可化简的进程。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;结论: 如果最终图中所有的进程都被化简掉 (成为孤立节点) ，则称该图是可完全化简的，系统没有死锁。反之，如果最后还剩下无法化简的进程节点，则系统存在死锁。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;4-解决死锁的方法&quot;&gt;4. 解决死锁的方法&lt;a class=&quot;anchor&quot; href=&quot;#4-解决死锁的方法&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;面对死锁，主要有四大类处理策略：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;忽略该问题 (鸵鸟算法) : 就像鸵鸟把头埋进沙子里一样，假装问题不存在。适用于发生概率极低，且处理代价远高于其危害的系统。很多主流操作系统 (如Windows, UNIX) 都采用此策略，它们假设死锁不会发生，将处理死锁的责任留给了程序员。&lt;/li&gt;
&lt;li&gt;死锁预防 (Deadlock Prevention): 在系统设计时，通过施加某些限制，从根本上破坏死锁产生的四个必要条件之一。这是一种静态策略。&lt;/li&gt;
&lt;li&gt;死锁避免 (Deadlock Avoidance): 在系统运行时，动态地跟踪资源分配，每当有资源请求时，先判断分配后是否会导致系统进入”不安全状态”。如果会，则拒绝分配。这是一种动态策略。&lt;/li&gt;
&lt;li&gt;死锁检测与解除 (Deadlock Detection and Recovery): 允许死锁发生，但系统有能力检测到它，并采取措施 (如剥夺资源、终止进程) 来解除死锁。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;41-死锁预防&quot;&gt;4.1 死锁预防&lt;a class=&quot;anchor&quot; href=&quot;#41-死锁预防&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是最直接的思路，即破坏四个必要条件中的一个。&lt;/p&gt;
&lt;h4 id=&quot;411-破坏互斥使用&quot;&gt;4.1.1 破坏”互斥使用”&lt;a class=&quot;anchor&quot; href=&quot;#411-破坏互斥使用&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;思路: 让资源变得可共享，而不是独占。&lt;/li&gt;
&lt;li&gt;方法: 资源转换技术。一个典型的例子是 SPOOLing 技术 (Simultaneous Peripheral Operations On-Line)。以打印机为例，它本身是独占设备。但系统可以设立一个打印机守护进程 (daemon) ，所有进程不直接访问打印机，而是将打印内容发送给这个 daemon 进程，由 daemon 统一管理打印队列。这样，对于进程来说，它们访问的”打印服务”是共享的，不会被阻塞。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;412-破坏占有且等待&quot;&gt;4.1.2 破坏”占有且等待”&lt;a class=&quot;anchor&quot; href=&quot;#412-破坏占有且等待&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;思路: 不允许进程在占有资源的同时去等待新资源。&lt;/li&gt;
&lt;li&gt;方案1: 一次性申请。进程在开始运行前，必须一次性地申请它在整个运行过程中所需要的所有资源。只有系统能满足其全部请求时，才分配给它。
&lt;ul&gt;
&lt;li&gt;缺点: 严重降低资源利用率 (一个进程可能很早就申请了某个资源，但很晚才用) ；可能导致”饥饿” (一个需要很多资源的进程可能永远也等不到所有资源都空闲的时刻) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;方案2: 释放已有资源。允许进程动态申请资源，但当它申请的新资源无法被满足时，它必须释放掉已经占有的所有资源。等以后需要时再重新申请。
&lt;ul&gt;
&lt;li&gt;缺点: 大大增加系统开销，进程可能要反复申请和释放资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;413-破坏不可抢占&quot;&gt;4.1.3 破坏”不可抢占”&lt;a class=&quot;anchor&quot; href=&quot;#413-破坏不可抢占&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;思路: 允许系统强行剥夺已被占有的资源。&lt;/li&gt;
&lt;li&gt;方法: 当进程申请新资源失败时，系统可以要求它释放已经占有的资源，或者从其他进程手中抢占可恢复状态的资源。&lt;/li&gt;
&lt;li&gt;局限性: 这种方法只适用于那些状态容易保存和恢复的资源，如CPU、内存。对于打印机、文件写入这类外部状态已经发生变化的资源，强行抢占通常不可行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;414-破坏循环等待&quot;&gt;4.1.4 破坏”循环等待”&lt;a class=&quot;anchor&quot; href=&quot;#414-破坏循环等待&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;思路: 避免形成等待环路。&lt;/li&gt;
&lt;li&gt;方法: 资源有序分配法 (Resource Ordering)。将系统中所有的资源类型进行线性排序 (例如，R1=1, R2=2, R3=3…) 。然后强制规定：任何进程在申请资源时，都必须严格按照资源编号的递增顺序进行申请。&lt;/li&gt;
&lt;li&gt;为什么能行？: 假设一个进程已经申请了资源 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;R_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0077em&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0077em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，它接下来只能申请编号大于 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 的资源 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;R_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0077em&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0077em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; (&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mo&gt;&amp;gt;&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;j &amp;gt; i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.854em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;)。这样就不可能出现一个进程占有 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;R_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0077em&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0077em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 反过来去申请 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;R_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0077em&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0077em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 的情况，从而保证了资源分配图不会出现环路。&lt;/li&gt;
&lt;li&gt;缺点: 资源编号需要精心设计，可能会给编程带来不便。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;42-死锁避免&quot;&gt;4.2 死锁避免&lt;a class=&quot;anchor&quot; href=&quot;#42-死锁避免&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;死锁避免不像预防那样施加严格的限制，而是更加灵活。它允许前三个条件的存在，但在分配资源时会小心翼翼，确保不会踏入死胡同。这里的核心概念是安全状态。&lt;/p&gt;
&lt;h4 id=&quot;421-安全状态-safe-state&quot;&gt;4.2.1 安全状态 (Safe State)&lt;a class=&quot;anchor&quot; href=&quot;#421-安全状态-safe-state&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;安全状态: 如果系统能找到一个安全序列，那么系统就处于安全状态。&lt;/li&gt;
&lt;li&gt;安全序列: 一个进程序列 &lt;code&gt;$&amp;lt;P_1, P_2, ..., P_n&amp;gt;$&lt;/code&gt; 是安全的，指的是对于序列中的每一个进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，它还需要的资源量，可以被系统当前剩余的资源加上所有排在它前面的进程 (&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mo&gt;&amp;lt;&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j, j&amp;lt;i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    A[系统状态] --&amp;gt; B(安全状态)
    A --&amp;gt; C(不安全状态)
    C --&amp;gt; D(死锁状态)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;422-银行家算法-bankers-algorithm&quot;&gt;4.2.2 银行家算法 (Banker’s Algorithm)&lt;a class=&quot;anchor&quot; href=&quot;#422-银行家算法-bankers-algorithm&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;它的安全性检查和资源分配图化简法思路相近，同时额外使用进程声明的最大需求。&lt;/p&gt;
&lt;p&gt;这是由Dijkstra提出的最著名的死锁避免算法。它模拟银行家审批贷款的过程：银行家 (操作系统) 在批出贷款 (分配资源) 前，要确保即使这笔钱放出去了，自己手头的资金 (剩余资源) 依然能满足其他客户 (进程) 后续的最大需求，从而保证银行不会倒闭 (系统不会死锁) 。&lt;/p&gt;
&lt;p&gt;算法所需的数据结构:
假设有n个进程和m类资源。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Available[m]&lt;/code&gt;: 一个向量，表示每类资源当前还可用的实例数量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Max[n][m]&lt;/code&gt;: 一个矩阵，表示每个进程对每类资源最大的需求量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Allocation[n][m]&lt;/code&gt;: 一个矩阵，表示每个进程当前已分配到的每类资源的实例数量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Need[n][m]&lt;/code&gt;: 一个矩阵，表示每个进程还需要的每类资源的实例数量。
&lt;ul&gt;
&lt;li&gt;这个矩阵是推算出来的：&lt;code&gt;Need[i][j] = Max[i][j] - Allocation[i][j]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;算法核心:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;资源请求: 当进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 请求资源 &lt;code&gt;Request[i]&lt;/code&gt; 时：
a. 检查请求是否合法：&lt;code&gt;Request[i] &amp;lt;= Need[i]&lt;/code&gt;？如果一个进程请求的超过了它声明的最大需求，则为非法请求。
b. 检查系统是否有足够资源：&lt;code&gt;Request[i] &amp;lt;= Available&lt;/code&gt;？如果没有，进程必须等待。
c. 假装分配: 如果前两步都通过，系统暂时将资源分配给它，并更新数据结构：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Available = Available - Request[i]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Allocation[i] = Allocation[i] + Request[i]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Need[i] = Need[i] - Request[i]&lt;/code&gt;
d. 安全检查: 调用安全性算法，检查分配后的新状态是否安全。&lt;/li&gt;
&lt;li&gt;如果安全，则正式完成分配。&lt;/li&gt;
&lt;li&gt;如果不安全，则撤销刚才的”假装分配”，恢复到原来的状态，让进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 等待。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;安全性算法:
a. 初始化工作向量 &lt;code&gt;Work = Available&lt;/code&gt;，和完成标记 &lt;code&gt;Finish[n]&lt;/code&gt; (所有元素为&lt;code&gt;false&lt;/code&gt;) 。
b. 从进程中寻找一个满足以下两个条件的进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Finish[i] == false&lt;/code&gt; (还没执行完)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Need[i] &amp;lt;= Work&lt;/code&gt; (它需要的资源小于等于系统当前可用的资源)
c. 如果找不到这样的进程，直接跳到第 e 步。
d. 如果找到了 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，则假设它执行完毕并释放所有资源。更新 &lt;code&gt;Work&lt;/code&gt; 和 &lt;code&gt;Finish&lt;/code&gt;：&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Work = Work + Allocation[i]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish[i] = true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;返回第 b 步，继续寻找下一个可以满足的进程。
e. 检查 &lt;code&gt;Finish&lt;/code&gt; 数组，如果所有元素都为 &lt;code&gt;true&lt;/code&gt;，则说明系统处于安全状态。否则，为不安全状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;423-银行家算法应用示例1&quot;&gt;4.2.3 银行家算法应用示例1&lt;a class=&quot;anchor&quot; href=&quot;#423-银行家算法应用示例1&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;状态: 1类资源，总量12。P1, P2, P3。&lt;/li&gt;
&lt;/ul&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;进程&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;目前占有量&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;最大需求量&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;尚需要量 (Need)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P1&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;4&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P2&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;4&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;6&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P3&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;5&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;8&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;3&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;系统剩余量 (Available): &lt;code&gt;12 - (1+4+5) = 2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安全性检查:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Work = 2&lt;/code&gt;, &lt;code&gt;Finish = [F, F, F]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; s.t. &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;[&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;]&lt;/mo&gt;&lt;mo&gt;≤&lt;/mo&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;Need[i] \leq Work&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.109em&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;ee&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≤&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;W&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0315em&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;:
&lt;ul&gt;
&lt;li&gt;P1: &lt;code&gt;Need=3 &amp;gt; Work=2&lt;/code&gt; (不行)&lt;/li&gt;
&lt;li&gt;P2: &lt;code&gt;Need=2 &amp;lt;= Work=2&lt;/code&gt; (可以！)&lt;/li&gt;
&lt;li&gt;P3: &lt;code&gt;Need=3 &amp;gt; Work=2&lt;/code&gt; (不行)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模拟P2执行:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = Work + Allocation[P2] = 2 + 4 = 6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [F, T, F]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;回到第2步，用新的Work=6找:
&lt;ul&gt;
&lt;li&gt;P1: &lt;code&gt;Need=3 &amp;lt;= Work=6&lt;/code&gt; (可以！)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模拟P1执行:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = Work + Allocation[P1] = 6 + 1 = 7&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [T, T, F]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;回到第2步，用新的Work=7找:
&lt;ul&gt;
&lt;li&gt;P3: &lt;code&gt;Need=3 &amp;lt;= Work=7&lt;/code&gt; (可以！)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模拟P3执行:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = Work + Allocation[P3] = 7 + 5 = 12&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [T, T, T]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;结论: 由于所有进程的 &lt;code&gt;Finish&lt;/code&gt; 标记都为 &lt;code&gt;true&lt;/code&gt;，所以当前状态是安全的。一个安全序列是 &lt;code&gt;&amp;lt;P2, P1, P3&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id=&quot;424-银行家算法应用示例2&quot;&gt;4.2.4 银行家算法应用示例2&lt;a class=&quot;anchor&quot; href=&quot;#424-银行家算法应用示例2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;状态: 5个进程 (P1-P5)，3类资源 (A,B,C)。&lt;/li&gt;
&lt;/ul&gt;















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;已分配 (Allocation)&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;最大需求 (Max)&lt;/th&gt;&lt;th style=&quot;text-align:center&quot;&gt;尚需要 (Need)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;A B C&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;A B C&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;A B C&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P1&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;0 1 0&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;7 5 3&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;7 4 3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P2&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;2 0 0&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;3 2 2&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;1 2 2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P3&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;3 0 2&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;9 0 2&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;6 0 0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P4&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;2 1 1&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;2 2 2&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;0 1 1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P5&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;0 0 2&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;4 3 3&lt;/td&gt;&lt;td style=&quot;text-align:center&quot;&gt;4 3 1&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;剩余资源 (Available): A=3, B=3, C=2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;问题1：此状态是否安全？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Work = [3, 3, 2]&lt;/code&gt;, &lt;code&gt;Finish = [F, F, F, F, F]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; s.t. &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;[&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;]&lt;/mo&gt;&lt;mo&gt;≤&lt;/mo&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;Need[i] \leq Work&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.109em&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;ee&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≤&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;W&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em&quot;&gt;or&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0315em&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;:
&lt;ul&gt;
&lt;li&gt;P1: &lt;code&gt;[7,4,3] &amp;gt; [3,3,2]&lt;/code&gt; (不行)&lt;/li&gt;
&lt;li&gt;P2: &lt;code&gt;[1,2,2] &amp;lt;= [3,3,2]&lt;/code&gt; (可以！) -&amp;gt; 找到 P2。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模拟P2执行:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = [3,3,2] + [2,0,0] = [5,3,2]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [F, T, F, F, F]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用新的Work=[5,3,2]找:
&lt;ul&gt;
&lt;li&gt;P1: &lt;code&gt;[7,4,3] &amp;gt; [5,3,2]&lt;/code&gt; (不行)&lt;/li&gt;
&lt;li&gt;P3: &lt;code&gt;[6,0,0] &amp;gt; [5,3,2]&lt;/code&gt; (不行)&lt;/li&gt;
&lt;li&gt;P4: &lt;code&gt;[0,1,1] &amp;lt;= [5,3,2]&lt;/code&gt; (可以！) -&amp;gt; 找到 P4。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模拟P4执行:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = [5,3,2] + [2,1,1] = [7,4,3]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [F, T, F, T, F]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用新的Work=[7,4,3]找:
&lt;ul&gt;
&lt;li&gt;P1: &lt;code&gt;[7,4,3] &amp;lt;= [7,4,3]&lt;/code&gt; (可以！) -&amp;gt; 找到 P1。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模拟P1执行:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = [7,4,3] + [0,1,0] = [7,5,3]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [T, T, F, T, F]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用新的Work=[7,5,3]找:
&lt;ul&gt;
&lt;li&gt;P3: &lt;code&gt;[6,0,0] &amp;lt;= [7,5,3]&lt;/code&gt; (可以！) -&amp;gt; 找到 P3。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模拟P3执行:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = [7,5,3] + [3,0,2] = [10,5,5]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [T, T, T, T, F]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用新的Work=[10,5,5]找:
&lt;ul&gt;
&lt;li&gt;P5: &lt;code&gt;[4,3,1] &amp;lt;= [10,5,5]&lt;/code&gt; (可以！) -&amp;gt; 找到 P5。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;模拟P5执行:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = [10,5,5] + [0,0,2] = [10,5,7]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [T, T, T, T, T]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;结论: 是安全状态。一个安全序列为 &lt;code&gt;&amp;lt;P2, P4, P1, P3, P5&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;问题2：P1申请(0,2,0)能否分配？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;检查请求:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Request_P1 = [0,2,0] &amp;lt;= Need_P1 = [7,4,3]&lt;/code&gt; (合法)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Request_P1 = [0,2,0] &amp;lt;= Available = [3,3,2]&lt;/code&gt; (有资源)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;假装分配:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Available = [3,3,2] - [0,2,0] = [3,1,2]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Allocation_P1 = [0,1,0] + [0,2,0] = [0,3,0]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Need_P1 = [7,4,3] - [0,2,0] = [7,2,3]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用新状态进行安全检查:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Work = [3,1,2]&lt;/code&gt;, &lt;code&gt;Finish = [F,F,F,F,F]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;P1: &lt;code&gt;[7,2,3] &amp;gt; [3,1,2]&lt;/code&gt; (不行)&lt;/li&gt;
&lt;li&gt;P2: &lt;code&gt;[1,2,2] &amp;gt; [3,1,2]&lt;/code&gt; (不行)&lt;/li&gt;
&lt;li&gt;P3: &lt;code&gt;[6,0,0] &amp;gt; [3,1,2]&lt;/code&gt; (不行)&lt;/li&gt;
&lt;li&gt;P4: &lt;code&gt;[0,1,1] &amp;lt;= [3,1,2]&lt;/code&gt; (可以！) -&amp;gt; 找到 P4&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Work = [3,1,2] + [2,1,1] = [5,2,3]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [F,F,F,T,F]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;用新的Work=[5,2,3]找:&lt;/li&gt;
&lt;li&gt;P1: &lt;code&gt;[7,2,3] &amp;gt; [5,2,3]&lt;/code&gt; (不行)&lt;/li&gt;
&lt;li&gt;P2: &lt;code&gt;[1,2,2] &amp;lt;= [5,2,3]&lt;/code&gt; (可以！) -&amp;gt; 找到 P2&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Work = [5,2,3] + [2,0,0] = [7,2,3]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Finish = [F,T,F,T,F]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;用新的Work=[7,2,3]找:&lt;/li&gt;
&lt;li&gt;P1: &lt;code&gt;[7,2,3] &amp;lt;= [7,2,3]&lt;/code&gt; (可以！) -&amp;gt; 找到 P1&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Work = [7,2,3] + [0,3,0] = [7,5,3]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;此后 P3 和 P5 都可以完成。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;结论: 可以分配。一个安全序列是 &lt;code&gt;&amp;lt;P4, P2, P1, P3, P5&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&quot;43-死锁检测与解除&quot;&gt;4.3 死锁检测与解除&lt;a class=&quot;anchor&quot; href=&quot;#43-死锁检测与解除&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这种策略不预防也不避免，而是采取”亡羊补牢”的方式。&lt;/p&gt;
&lt;h4 id=&quot;431-死锁检测&quot;&gt;4.3.1 死锁检测&lt;a class=&quot;anchor&quot; href=&quot;#431-死锁检测&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&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;/li&gt;
&lt;li&gt;检测算法:
&lt;ul&gt;
&lt;li&gt;如果每类资源只有一个实例，最常用的方法是维护一个等待图 (Wait-for Graph)，它是资源分配图的简化版，只包含进程节点。&lt;/li&gt;
&lt;li&gt;如果 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 正在等待 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 持有的资源，就在图中画一条从 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 到 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&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;h4 id=&quot;432-死锁解除-恢复&quot;&gt;4.3.2 死锁解除 (恢复)&lt;a class=&quot;anchor&quot; href=&quot;#432-死锁解除-恢复&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;一旦检测到死锁，就需要采取措施打破僵局。代价越小越好。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;撤销所有死锁进程: 最简单粗暴，但代价巨大，所有进程的工作成果都丢失了。&lt;/li&gt;
&lt;li&gt;进程回退 (Rollback): 利用系统的检查点 (Checkpoint) 机制，让死锁的进程回退到某个未发生死锁的先前状态，然后重新执行。&lt;/li&gt;
&lt;li&gt;逐一撤销死锁进程: 按照某种原则 (如进程优先级、已执行时间、占有资源多少等) 选择一个”牺牲品”进程并终止它，释放其资源。然后再次检测死锁，如果还存在，再撤销下一个，直到死锁解除。&lt;/li&gt;
&lt;li&gt;逐一抢占资源: 同样按某种原则，从一个死锁进程中抢占部分资源分配给其他进程，以打破环路。被抢占资源的进程必须回退到安全状态。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;5-经典问题哲学家就餐问题&quot;&gt;5. 经典问题：哲学家就餐问题&lt;a class=&quot;anchor&quot; href=&quot;#5-经典问题哲学家就餐问题&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这个问题是Dijkstra提出的一个经典的并发同步问题，用以说明死锁。&lt;/p&gt;
&lt;h3 id=&quot;51-问题描述&quot;&gt;5.1 问题描述&lt;a class=&quot;anchor&quot; href=&quot;#51-问题描述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;五个哲学家围坐在一张圆桌旁。&lt;/li&gt;
&lt;li&gt;每人面前一盘意面，每两人之间放着一只筷子，共五只。&lt;/li&gt;
&lt;li&gt;哲学家的动作是：思考 -&amp;gt; 感到饥饿 -&amp;gt; 拿筷子吃饭 -&amp;gt; 放下筷子 -&amp;gt; 继续思考。&lt;/li&gt;
&lt;li&gt;规则: 必须同时拿到左右两边的两只筷子才能吃饭。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;52-解决方案分析&quot;&gt;5.2 解决方案分析&lt;a class=&quot;anchor&quot; href=&quot;#52-解决方案分析&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;方案1朴素的信号量解法&quot;&gt;方案1：朴素的信号量解法&lt;a class=&quot;anchor&quot; href=&quot;#方案1朴素的信号量解法&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;每个筷子是一个信号量。哲学家&lt;code&gt;i&lt;/code&gt;先拿左边的筷子&lt;code&gt;fork[i]&lt;/code&gt;，再拿右边的&lt;code&gt;fork[(i+1)%5]&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 伪代码
void philosopher(int i) {
    while(true) {
        think();
        P(fork[i]);              // 拿左筷子
        P(fork[(i+1) % 5]);      // 拿右筷子
        eat();
        V(fork[(i+1) % 5]);      // 放右筷子
        V(fork[i]);              // 放左筷子
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;问题: 会发生死锁。如果五个哲学家同时感到饥饿，并同时拿起自己左手边的筷子，那么他们每个人都将永远等待自己右手边的筷子，而那只筷子正被邻座拿着。这就形成了循环等待。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;方案2使用管程-monitor&quot;&gt;方案2：使用管程 (Monitor)&lt;a class=&quot;anchor&quot; href=&quot;#方案2使用管程-monitor&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;将获取和释放筷子的操作封装在管程内部。但幻灯片中给出的&lt;code&gt;get_forks&lt;/code&gt;实现是有问题的，它依然是顺序地申请左右筷子，如果获取左筷子后在等待右筷子时被阻塞，同样会造成死锁，并没有解决根本问题。一个正确的管程实现需要能原子地检查并获取两只筷子。&lt;/p&gt;
&lt;h4 id=&quot;方案3状态数组法-推荐&quot;&gt;方案3：状态数组法 (推荐)&lt;a class=&quot;anchor&quot; href=&quot;#方案3状态数组法-推荐&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;这是 Tanenbaum 提出的一个经典解法，可以避免死锁。在信号量等待队列和调度器公平的前提下，它也可以避免饥饿。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;核心思想: 引入&lt;code&gt;state&lt;/code&gt;数组记录每个哲学家的状态 (思考、饥饿、进食) 。一个哲学家只有在他左右两边的邻居都没有在进食时，才允许他从”饥饿”状态转为”进食”状态。这个检查和状态转换是原子的 (通过&lt;code&gt;mutex&lt;/code&gt;互斥锁保护) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 伪代码
#define N 5
#define THINKING 0
#define HUNGRY 1
#define EATING 2
int state[N];
semaphore mutex = 1; // 保护对state数组的访问
semaphore s[N] = {0}; // 每个哲学家一个信号量，用于阻塞

void philosopher(int i) {
    while(true) {
        think();
        take_forks(i); // 核心逻辑
        eat();
        put_forks(i);  // 核心逻辑
    }
}

void take_forks(int i) {
    P(mutex); // 进入临界区
    state[i] = HUNGRY;
    test(i); // 尝试获取筷子
    V(mutex); // 退出临界区
    P(s[i]); // 如果不能吃，则在此阻塞
}

void put_forks(int i) {
    P(mutex); // 进入临界区
    state[i] = THINKING;
    test((i+N-1) % N); // 检查左邻居能否开吃
    test((i+1) % N); // 检查右邻居能否开吃
    V(mutex); // 退出临界区
}

void test(int i) {
    // 如果哲学家i饿了，并且左右邻居都没在吃
    if (state[i] == HUNGRY &amp;amp;&amp;amp;
        state[(i+N-1) % N] != EATING &amp;amp;&amp;amp;
        state[(i+1) % N] != EATING)
    {
        state[i] = EATING;
        V(s[i]); // 唤醒哲学家i (如果他被阻塞了)
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;优点: 这个方案不会死锁，因为获取筷子的条件是两只都可用。同时，通过&lt;code&gt;test&lt;/code&gt;邻居，它能在公平调度下减少长期等待。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;方案4限制就餐人数-破坏循环等待的变种&quot;&gt;方案4：限制就餐人数 (破坏循环等待的变种)&lt;a class=&quot;anchor&quot; href=&quot;#方案4限制就餐人数-破坏循环等待的变种&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;思想: 最多只允许 N-1 (即4个) 哲学家同时进入餐厅 (尝试拿筷子) 。&lt;/li&gt;
&lt;li&gt;实现: 引入一个计数信号量 &lt;code&gt;room&lt;/code&gt;，并初始化为4。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 伪代码
semaphore room = 4; // 餐厅最多坐4人
void philosopher(int i) {
    while(true) {
        think();
        P(room); // 进入餐厅
        P(fork[i]);
        P(fork[(i+1) % 5]);
        eat();
        V(fork[(i+1) % 5]);
        V(fork[i]);
        V(room); // 离开餐厅
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;为什么能行？: 因为最多只有4个哲学家会尝试拿筷子。即使这4个人都先拿到一只筷子，桌上仍有1只筷子空闲，至少有一个哲学家可以拿到第二只筷子并完成就餐。这个限制破坏了循环等待。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;6-思考题解答&quot;&gt;6. 思考题解答&lt;a class=&quot;anchor&quot; href=&quot;#6-思考题解答&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;思考题1画出5个进程陷入死锁的所有非同构模型&quot;&gt;思考题1：画出5个进程陷入死锁的所有非同构模型&lt;a class=&quot;anchor&quot; href=&quot;#思考题1画出5个进程陷入死锁的所有非同构模型&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这个题目必须先明确模型。把它直接理解成”5个节点的所有强连通有向图”会得到大量图。操作系统课里通常讨论的是等待图模型，并且每个阻塞进程等待一个由另一个进程持有的资源。&lt;/p&gt;
&lt;p&gt;如果题目要求5个进程都直接参与循环等待，并且每个进程只有一条等待边，那么只有两类非同构模型。&lt;/p&gt;
&lt;h4 id=&quot;模型1一个5环&quot;&gt;模型1：一个5环&lt;a class=&quot;anchor&quot; href=&quot;#模型1一个5环&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    P1 --&amp;gt; P2 --&amp;gt; P3 --&amp;gt; P4 --&amp;gt; P5 --&amp;gt; P1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;模型2一个2环加一个3环&quot;&gt;模型2：一个2环加一个3环&lt;a class=&quot;anchor&quot; href=&quot;#模型2一个2环加一个3环&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    P1 --&amp;gt; P2 --&amp;gt; P1
    P3 --&amp;gt; P4 --&amp;gt; P5 --&amp;gt; P3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果只要求5个进程都会永久阻塞，情况会更多。例如4环外接一个等待环内进程的节点、3环外接一条长度为2的等待链，也会让所有5个进程无法继续执行。若还允许一个进程等待多个资源，等待图可以继续加边，非同构情况会大幅增加。此时题目需要给出边数、每个进程最多等待的资源数、资源实例数等限制，才有可能列出”所有”模型。&lt;/p&gt;
&lt;h3 id=&quot;思考题2学生用超时方法解决死锁该给多少分&quot;&gt;思考题2：学生用”超时”方法解决死锁，该给多少分？&lt;a class=&quot;anchor&quot; href=&quot;#思考题2学生用超时方法解决死锁该给多少分&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;问题: 进程请求资源时启动计时器。如果因阻塞超时，就”释放”该进程并让它重新执行。&lt;/p&gt;
&lt;p&gt;我的评价:
作为老师，我会给这位同学一个及格分，但不会给高分。比如 60-70分。&lt;/p&gt;
&lt;p&gt;优点 (为什么给分):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;抓住了问题: 学生意识到了死锁是进程无限等待，并试图通过打破”无限”这个条件来解决问题，思路是对的。&lt;/li&gt;
&lt;li&gt;提出了一种可行的恢复策略: 这本质上是一种死锁解除机制，通过强制终止 (或重启) 进程来打破等待。在某些实时系统或健壮性要求高的系统中，类似”看门狗”(Watchdog Timer)的机制确实在被使用，防止任务卡死。&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;ul&gt;
&lt;li&gt;如果时间设得太短，一个正常的、只是计算时间或I/O时间稍长的进程可能会被无辜”杀死”。&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;/ol&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 10: Classic Synchronization Problems</title><link>https://www.lyt0112.com/blog/operating_systems_note_10-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_10-zh</guid><description>Operating Systems Notes 10: Classic Synchronization Problems</description><pubDate>Fri, 06 Jun 2025 15:39:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;在多道程序环境下, 多个进程并发执行, 它们之间或多或少会存在一些依赖关系. 为了协调这些进程的执行, 确保程序的正确性, 我们需要一套机制来让它们进行通信和同步, 这就是所谓的IPC (Inter-Process Communication) . 本讲聚焦于几种经典的同步问题, 并要求使用信号量 (Semaphore) 的P、V操作或管程 (Monitor) 来解决.&lt;/p&gt;
&lt;p&gt;本课程我们主要关注以下几个问题：另类PV操作问题、食品供应问题、三峡大坝问题、狒狒过峡谷问题、睡眠理发师问题, 以及最后的一个资源管理问题.&lt;/p&gt;
&lt;h2 id=&quot;1-解决同步问题的系统方法论&quot;&gt;1 解决同步问题的系统方法论&lt;a class=&quot;anchor&quot; href=&quot;#1-解决同步问题的系统方法论&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;无论是用信号量还是管程, 解决问题的第一步都是相同的：分析问题本身.&lt;/p&gt;
&lt;h3 id=&quot;11-分析模型-与工具无关&quot;&gt;1.1 分析模型 (与工具无关)&lt;a class=&quot;anchor&quot; href=&quot;#11-分析模型-与工具无关&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在写任何一行代码之前, 先用自然语言清晰地定义以下三个要素：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;识别”角色”(Actors) 和”资源”(Resources)&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;定义”规则”, 即约束条件 (Constraints)
这是最关键的一步. 规则分为两类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;互斥 (Mutual Exclusion): “在任何时候, 只有一个角色能做某件事”. 这通常是为了保护共享资源的完整性.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;例子&lt;/em&gt;: 任何时候只能有一个理发师在修改 &lt;code&gt;waiting_chairs&lt;/code&gt; 计数器. 任何时候只能有一艘船在使用某一级船闸 &lt;code&gt;gate[i]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;同步 (Synchronization / Condition Synchronization): “一个角色必须等待某个条件为真才能继续”. 这是角色之间的协作和时序关系.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;例子&lt;/em&gt;: 消费者必须等待缓冲区里有产品. 理发师必须等待有顾客到来. 船只要想上行, 必须等待航向没有被下行船只占用.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一旦用自然语言把所有”互斥”和”同步”的规则都列出来, 问题就解决了一半. 下一步才是选择工具, 将这些规则”翻译”成代码.&lt;/p&gt;
&lt;h3 id=&quot;12-一个重要的问题&quot;&gt;1.2 一个重要的问题&lt;a class=&quot;anchor&quot; href=&quot;#12-一个重要的问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;读者可能会提出两个相关问题&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在睡眠理发师问题中, &lt;code&gt;customers&lt;/code&gt;  (现在等待的顾客数) 不也是一个共享变量吗？为什么它被直接实现为一个信号量, 而不是一个受&lt;code&gt;mutex&lt;/code&gt;保护的整数？&lt;/li&gt;
&lt;li&gt;为什么在读者-写者问题中, &lt;code&gt;read_count&lt;/code&gt; (正在读取的读者数) 被实现为一个普通整型变量, 而不是一个信号量？&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;这说明信号量同时封装了”计数”和”等待”.&lt;/p&gt;
&lt;p&gt;用信号量直接实现&lt;code&gt;customers&lt;/code&gt;, 是因为理发师的同步规则 (“若无顾客则等待”) 与信号量的P操作匹配. &lt;code&gt;P(customers)&lt;/code&gt; 这一个动作就完成了”检查顾客数, 如果为零则睡眠”的全部逻辑.&lt;/p&gt;
&lt;p&gt;这是一种设计上的抽象和优化. 我们本可以这样做：&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 复杂的、非必要的实现
int customers_count = 0;
semaphore mutex = 1;
semaphore barber_is_sleeping = 0; // 单独的等待工具

// 理发师
P(mutex);
if (customers_count == 0) {
    V(mutex);
    P(barber_is_sleeping); // 在独立的信号量上等待
} else { ... }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这种实现更繁琐, 引入了更多的变量和复杂性. 直接使用 &lt;code&gt;semaphore customers = 0;&lt;/code&gt; 更清晰.&lt;/p&gt;
&lt;p&gt;更精确的指导原则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当同步规则是简单的 “等待, 直到某个资源的计数 &amp;gt; 0” 时, 直接用一个信号量代表这个资源. 例如：等待顾客(&lt;code&gt;customers&lt;/code&gt;)、等待产品(&lt;code&gt;full&lt;/code&gt;)、等待空位(&lt;code&gt;empty&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;当你的同步规则更复杂, 需要读取一个计数值并基于它的值执行不同的逻辑分支时 (例如读者-写者问题中的&lt;code&gt;read_count&lt;/code&gt;, 你需要在第一个读者进来的时候锁住写者不让他进来, 而这个操作信号量是不支持的) , 你应该使用一个普通整型变量, 并用一个互斥锁来保护它的读写操作.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;13-选择工具并进行翻译&quot;&gt;1.3 选择工具并进行”翻译”&lt;a class=&quot;anchor&quot; href=&quot;#13-选择工具并进行翻译&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;131-方法论一使用信号量-pv-操作&quot;&gt;1.3.1 方法论一：使用信号量 (P/V 操作)&lt;a class=&quot;anchor&quot; href=&quot;#131-方法论一使用信号量-pv-操作&quot;&gt;#&lt;/a&gt;&lt;/h4&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;方法: 为每一个需要互斥访问的共享资源 (或代码段) 定义一个二元信号量 (mutex), 初值为 1.&lt;/li&gt;
&lt;li&gt;模式: 在临界区代码前 &lt;code&gt;P(mutex)&lt;/code&gt;, 在之后 &lt;code&gt;V(mutex)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;思考: “这个信号量代表了进入临界区的’许可’, 初始时有1个许可. ”&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;ul&gt;
&lt;li&gt;这个计数代表了角色正在等待的”资源”或”事件”的数量.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;确定初值: 信号量的初值等于系统启动时, 它所计数的资源的初始数量.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;例子 (睡眠理发师)&lt;/em&gt;: 顾客等待”空闲的理发师”. 系统开始时, 理发师都在睡觉 (不空闲) , 所以 &lt;code&gt;barbers&lt;/code&gt; 信号量初值为 0. 理发师等待”已到店的顾客”. 系统开始时, 没有顾客, 所以 &lt;code&gt;customers&lt;/code&gt; 信号量初值为 0.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;例子 (食品供应)&lt;/em&gt;: 供应者A等待”A食品的空货架”. 初始时货架全空, 所以 &lt;code&gt;emptyA&lt;/code&gt; 信号量初值为 m.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;安排 P/V 操作:
&lt;ul&gt;
&lt;li&gt;P 操作: 当一个角色需要/消耗一个资源时, 它就执行 &lt;code&gt;P&lt;/code&gt; 操作. 如果资源数 (信号量的值) 为0, 它就自动等待.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;例子&lt;/em&gt;: 理发师需要一个顾客, 所以他 &lt;code&gt;P(customers)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;V 操作: 当一个角色生产/释放一个资源时, 它就执行 &lt;code&gt;V&lt;/code&gt; 操作, 通知等待者资源增加了.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;例子&lt;/em&gt;: 顾客到店了, 他”生产”了一个”理发请求”, 所以他 &lt;code&gt;V(customers)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;132-方法论二使用管程-monitor&quot;&gt;1.3.2 方法论二：使用管程 (Monitor)&lt;a class=&quot;anchor&quot; href=&quot;#132-方法论二使用管程-monitor&quot;&gt;#&lt;/a&gt;&lt;/h4&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;code&gt;countA&lt;/code&gt;, &lt;code&gt;up_count&lt;/code&gt;, &lt;code&gt;writer_active&lt;/code&gt; 等) 全部封装在一个管程对象内部.&lt;/li&gt;
&lt;li&gt;思考: 管程自动保证了所有调用其方法 (过程) 的线程都是互斥的. 你不再需要手动创建 &lt;code&gt;mutex&lt;/code&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;方法: 为每一个”等待原因”定义一个条件变量 (Condition Variable).
&lt;ul&gt;
&lt;li&gt;命名技巧: 给条件变量起一个能反映”等待者期望什么变为真”的名字. 例如, &lt;code&gt;can_go_east&lt;/code&gt; (东行狒狒等待这个条件为真), &lt;code&gt;barber_available&lt;/code&gt; (理发师等待这个条件为真).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;实现管程过程 (经典模式: &lt;code&gt;while-wait-signal&lt;/code&gt;):
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;while (condition_is_not_met)&lt;/code&gt;: 在管程方法内部, 永远使用 &lt;code&gt;while&lt;/code&gt; 循环来检查等待条件, 而不是 &lt;code&gt;if&lt;/code&gt;. 这是因为当你被唤醒时, 你期望的条件可能已经被另一个刚被唤醒的进程改变了. &lt;code&gt;while&lt;/code&gt; 确保你继续执行时, 条件一定是满足的.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wait(condition_variable)&lt;/code&gt;: 在 &lt;code&gt;while&lt;/code&gt; 循环中, 如果条件不满足, 就调用 &lt;code&gt;wait&lt;/code&gt;. 这会原子地做两件事：1) 释放管程的锁；2) 在这个条件变量上睡眠.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;signal(c)&lt;/code&gt; / &lt;code&gt;broadcast(c)&lt;/code&gt;: 当你修改了共享状态后, 问自己：“我的修改是否使得某个等待队列的条件成真了？” 如果是, 就调用 &lt;code&gt;signal&lt;/code&gt; (唤醒一个) 或 &lt;code&gt;broadcast&lt;/code&gt; (唤醒所有) 来通知等待者.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;例子 (狒狒过峡谷)&lt;/em&gt;: 当最后一个东行狒狒离开后 (&lt;code&gt;east_count--&lt;/code&gt; 变为 0), 它使得 &lt;code&gt;east_count &amp;gt; 0&lt;/code&gt; 这个等待条件对西行狒狒不再成立, 所以它应该 &lt;code&gt;broadcast(can_go_west)&lt;/code&gt; 来唤醒所有等待的西行狒狒, 让它们重新检查.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;14-总结与建议&quot;&gt;1.4 总结与建议&lt;a class=&quot;anchor&quot; href=&quot;#14-总结与建议&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;特性&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;信号量 (P/V)&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;管程 (Monitor)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;核心思想&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;计数资源/事件&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;封装状态, 等待条件&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;互斥实现&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;手动 (使用&lt;code&gt;mutex&lt;/code&gt;信号量)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;自动 (由管程结构保证)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;同步实现&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;隐式 (通过信号量的值)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;显式 (通过条件变量&lt;code&gt;wait/signal&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;易错点&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;忘记&lt;code&gt;P/V&lt;/code&gt;操作, 死锁, 信号量初值设错&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;wait&lt;/code&gt;条件用&lt;code&gt;if&lt;/code&gt;不用&lt;code&gt;while&lt;/code&gt;, 忘记&lt;code&gt;signal&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;抽象层级&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;较低, 更接近硬件&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;较高, 更结构化, 易于管理复杂状态&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;2-纯同步问题&quot;&gt;2 纯同步问题&lt;a class=&quot;anchor&quot; href=&quot;#2-纯同步问题&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;纯同步问题是指进程之间仅仅存在执行顺序上的依赖关系, 而没有互斥访问共享资源的需求. 这种依赖关系通常可以用前驱图 (Precedence Graph) 来描述.&lt;/p&gt;
&lt;h3 id=&quot;21-例子前驱图&quot;&gt;2.1 例子：前驱图&lt;a class=&quot;anchor&quot; href=&quot;#21-例子前驱图&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;下面是前驱图的例子, 箭头代表了进程 (或代码段) 执行的先后顺序. 例如, 从 S 指向 p1 的箭头表示 p1 必须在 S 完成后才能开始.&lt;/p&gt;
&lt;p&gt;通用前驱图 (General precedence)&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    S --&amp;gt; p1
    p1 --&amp;gt; p2
    p1 --&amp;gt; p3
    p2 --&amp;gt; p4
    p3 --&amp;gt; p4
    p3 --&amp;gt; p5
    p4 --&amp;gt; p6
    p5 --&amp;gt; p6
    p6 --&amp;gt; p7
    p6 --&amp;gt; p8
    p7 --&amp;gt; F
    p8 --&amp;gt; F&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;解法思路:
解决这类问题的通用方法是, 为每一个前驱关系 (图中的每一条边) 设置一个信号量.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;信号量设置: 对于从进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 指向 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 的每一条边, 都设置一个初始值为0的信号量 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;S_{ij}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0576em&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0576em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;ij&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;: 在 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 执行完毕后, 对所有从 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 发出的边所对应的信号量执行 &lt;code&gt;V&lt;/code&gt; 操作. 例如, 如果 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 是 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 和 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_k&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0315em&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 的前驱, 则 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 结尾处需要执行 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;V(S_{ij})&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0361em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.2222em&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0576em&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0576em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;ij&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 和 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;V(S_{ik})&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.2222em&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0576em&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0576em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0315em&quot;&gt;ik&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;: 在 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 开始执行前, 对所有指向 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 的边所对应的信号量执行 &lt;code&gt;P&lt;/code&gt; 操作. 例如, 如果 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 的前驱是 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 和 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_k&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0315em&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, 则 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 开始处需要执行 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P(S_{ij})&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0361em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0576em&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0576em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;ij&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 和 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P(S_{kj})&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0361em;vertical-align:-0.2861em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0576em&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0576em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0315em&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过这种方式, 可以保证图中所定义的所有前驱关系都能得到满足.&lt;/p&gt;
&lt;h2 id=&quot;3-另类pv操作问题&quot;&gt;3 另类P、V操作问题&lt;a class=&quot;anchor&quot; href=&quot;#3-另类pv操作问题&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-问题描述&quot;&gt;3.1 问题描述&lt;a class=&quot;anchor&quot; href=&quot;#31-问题描述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;有一个系统, 对P、V操作的定义如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;P(s):
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;s.count --;
if (s.count &amp;lt; 0) {
    将本进程插入相应队列末尾等待;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;V(s):
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;s.count ++;
if (s.count &amp;lt;= 0) {
    从相应等待队列队尾唤醒一个进程, 将其插入就绪队列;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;思考并回答:
a. 这样定义P、V操作是否有问题？
b. 用这样的P、V操作实现N个进程竞争使用某一共享变量的互斥机制.
c. 对于b的解法, 有无效率更高的方法. 如有, 试问降低了多少复杂性？&lt;/p&gt;
&lt;h3 id=&quot;32-使用pv操作的问题解答&quot;&gt;3.2 使用P、V操作的问题解答&lt;a class=&quot;anchor&quot; href=&quot;#32-使用pv操作的问题解答&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;a. 这样定义P、V操作是否有问题？&lt;/p&gt;
&lt;p&gt;答: 有问题. 关键在于&lt;code&gt;V(s)&lt;/code&gt;操作. 标准的&lt;code&gt;V&lt;/code&gt;操作在唤醒进程时, 是从等待队列的队头唤醒一个进程, 这遵循了”先来先服务” (FCFS) 的原则, 可以保证等待的公平性, 避免某些进程饥饿 (长时间得不到执行) . 而题目中定义的&lt;code&gt;V&lt;/code&gt;操作是从队尾唤醒进程, 这是一种”后来先服务” (LIFS) 的策略. 这会导致等待时间最长的进程可能一直得不到唤醒, 从而产生饥饿现象. 因此, 这种定义是不公平的, 在实际系统中可能会导致严重问题.&lt;/p&gt;
&lt;p&gt;b. 用这样的P、V操作实现N个进程竞争共享变量的互斥&lt;/p&gt;
&lt;p&gt;答: 尽管存在公平性问题, 但我们仍然可以用它来实现互斥. 实现互斥的经典方法是使用一个初值为1的信号量.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 信号量定义
semaphore mutex;
mutex.count = 1;

// N个进程的执行代码
Process_i() {
    while(true) {
        P(mutex);
        // --- 临界区开始 ---
        // 访问共享变量
        // --- 临界区结束 ---
        V(mutex);

        // 非临界区代码
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;工作原理分析:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;mutex&lt;/code&gt; 信号量的初值为1, 表示允许一个进程进入临界区.&lt;/li&gt;
&lt;li&gt;第一个进程调用 &lt;code&gt;P(mutex)&lt;/code&gt;, &lt;code&gt;mutex.count&lt;/code&gt; 变为0, 进程进入临界区.&lt;/li&gt;
&lt;li&gt;此时若有第二个进程调用 &lt;code&gt;P(mutex)&lt;/code&gt;, &lt;code&gt;mutex.count&lt;/code&gt; 变为-1, 该进程被阻塞, 并加入等待队列. 之后再来的进程也同样被阻塞.&lt;/li&gt;
&lt;li&gt;第一个进程完成临界区操作后, 调用 &lt;code&gt;V(mutex)&lt;/code&gt;, &lt;code&gt;mutex.count&lt;/code&gt; 变为0 (&lt;code&gt;-1+1=0&lt;/code&gt;). 因为 &lt;code&gt;mutex.count &amp;lt;= 0&lt;/code&gt;, 所以会从等待队列的队尾唤醒一个进程.&lt;/li&gt;
&lt;li&gt;被唤醒的进程进入临界区, 其他进程继续等待. 这样就保证了任意时刻只有一个进程在临界区内, 实现了互斥.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;c. 效率和复杂性分析&lt;/p&gt;
&lt;p&gt;答: 这里的”效率”可以从两个角度理解：一是算法的执行效率, 二是系统的公平性.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;执行效率: 对于单个P或V操作来说, 无论是从队头还是队尾操作, 其时间复杂度通常都是 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;O(1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. 所以单次操作的执行效率没有太大差别.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;公平性与系统整体效率: 标准的队头唤醒 (FIFO) 方式, 其公平性好. 而题目中的队尾唤醒 (LIFO) 方式, 公平性差, 可能导致饥饿. 从系统整体来看, 饥饿现象会降低系统的效率和响应性.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;是否有更高效率的方法？
如果只要求互斥, 初值为1的信号量已经足够. 如果还要求有限等待, 这个修改版&lt;code&gt;V&lt;/code&gt;操作会破坏公平性, 单靠一个互斥信号量不够, 需要额外的排队机制, 或者把&lt;code&gt;V&lt;/code&gt;操作改回从队头唤醒.&lt;/p&gt;
&lt;p&gt;降低了多少复杂性？
队头和队尾唤醒在常见队列实现中都可以做到 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;O(1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, 单次操作没有数量级差异. 真正变化的是公平性分析. FIFO唤醒可以直接给出有限等待结论, LIFO唤醒需要额外机制处理饥饿问题.&lt;/p&gt;
&lt;h2 id=&quot;4-食品供货问题&quot;&gt;4 食品供货问题&lt;a class=&quot;anchor&quot; href=&quot;#4-食品供货问题&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;41-问题描述&quot;&gt;4.1 问题描述&lt;a class=&quot;anchor&quot; href=&quot;#41-问题描述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;某商店有两种食品A和B, 最大容量各为 &lt;code&gt;m&lt;/code&gt; 个. 商店将A、B两种食品搭配出售 (每次各取一个) . 为保证新鲜, 遵循”先到食品先出售”原则. 有两个食品公司分别不断地供应A和B. 为保证正常销售, 当某种食品的数量比另一种的数量超过 &lt;code&gt;k&lt;/code&gt; (&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mo&gt;&amp;lt;&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;k &amp;lt; m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7335em;vertical-align:-0.0391em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0315em&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;) 个时, 暂停对数量大的食品进货. 试用P、V操作解决同步和互斥关系.&lt;/p&gt;
&lt;h3 id=&quot;42-使用pv操作的问题解答&quot;&gt;4.2 使用P、V操作的问题解答&lt;a class=&quot;anchor&quot; href=&quot;#42-使用pv操作的问题解答&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;关系分析&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;互斥关系: 商店的库存 (食品A和B的数量) 是共享变量, 所有进程 (供应A、供应B、销售) 在修改库存数量时都必须互斥. 我们可以用一个信号量 &lt;code&gt;mutex&lt;/code&gt; 来实现.&lt;/li&gt;
&lt;li&gt;同步关系:
&lt;ul&gt;
&lt;li&gt;销售员: 必须等到商店里同时有食品A和食品B时才能进行销售.&lt;/li&gt;
&lt;li&gt;供应A: 当A的数量已满(&lt;code&gt;m&lt;/code&gt;个)时, 或当A比B多&lt;code&gt;k&lt;/code&gt;个时, 需要暂停供应.&lt;/li&gt;
&lt;li&gt;供应B: 当B的数量已满(&lt;code&gt;m&lt;/code&gt;个)时, 或当B比A多&lt;code&gt;k&lt;/code&gt;个时, 需要暂停供应.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;信号量设置&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mutex&lt;/code&gt;: 互斥信号量, 保护对库存数量的访问, 初值为 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;emptyA&lt;/code&gt;, &lt;code&gt;emptyB&lt;/code&gt;: 表示A和B的空闲货架数, 初值均为 &lt;code&gt;m&lt;/code&gt;. 用于控制库存上限.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;foodA&lt;/code&gt;, &lt;code&gt;foodB&lt;/code&gt;: 表示已有的A和B食品数量, 初值均为 0. 用于销售员判断是否可以销售.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;limitA&lt;/code&gt;: 表示A相对于B还能增加的额度. 当A比B多&lt;code&gt;k&lt;/code&gt;个时, A的供应者需要等待. 初值为 &lt;code&gt;k&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;limitB&lt;/code&gt;: 表示B相对于A还能增加的额度. 当B比A多&lt;code&gt;k&lt;/code&gt;个时, B的供应者需要等待. 初值为 &lt;code&gt;k&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 共享变量
int countA = 0, countB = 0; // 食品数量

// 信号量
semaphore mutex = 1;
semaphore emptyA = m, emptyB = m;
semaphore foodA = 0, foodB = 0;
semaphore limitA = k, limitB = k; // A相对B, B相对A的可增加额度

// 供应A进程
ProviderA() {
    while(true) {
        P(emptyA);       // 是否还有空货架放A?
        P(limitA);       // A比B的数量是否已经多了k个？
                         // P(limitA)意味着消耗一个A相对B的可增加额度
        P(mutex);
        // 生产一个A并放入
        countA++;
        V(mutex);

        V(limitB);       // 增加一个B相对A的可增加额度
        V(foodA);        // 增加一个食品A
    }
}

// 供应B进程
ProviderB() {
    while(true) {
        P(emptyB);       // 是否还有空货架放B?
        P(limitB);       // B比A的数量是否已经多了k个？
                         // P(limitB)意味着消耗一个B相对A的可增加额度
        P(mutex);
        // 生产一个B并放入
        countB++;
        V(mutex);

        V(limitA);       // 增加一个A相对B的可增加额度
        V(foodB);        // 增加一个食品B
    }
}

// 销售员进程
Seller() {
    while(true) {
        P(foodA); // 是否有食品A?
        P(foodB); // 是否有食品B?

        P(mutex);
        // 取走A和B
        countA--;
        countB--;
        V(mutex);

        V(emptyA); // 货架空出
        V(emptyB); // 货架空出
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;43-使用管程的解决方案&quot;&gt;4.3 使用管程的解决方案&lt;a class=&quot;anchor&quot; href=&quot;#43-使用管程的解决方案&quot;&gt;#&lt;/a&gt;&lt;/h3&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;共享变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;countA&lt;/code&gt;, &lt;code&gt;countB&lt;/code&gt;: 两种食品的当前数量.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;条件变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;canSupplyA&lt;/code&gt;: 当A食品无法供应时 (已满或比B多k个), 供应者A在此等待.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;canSupplyB&lt;/code&gt;: 当B食品无法供应时, 供应者B在此等待.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;canSell&lt;/code&gt;: 当A或B食品缺货时, 销售员在此等待.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管程过程:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SupplyA(item)&lt;/code&gt;: 供货商A调用.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SupplyB(item)&lt;/code&gt;: 供货商B调用.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Sell()&lt;/code&gt;: 销售员调用.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;class FoodStore {
    int countA = 0, countB = 0;
    condition canSupplyA, canSupplyB, canSell;

    // 供应A食品
    public void SupplyA() {
        while (countA &amp;gt;= m || countA - countB &amp;gt;= k) {
            wait(canSupplyA);
        }
        // 放入食品A
        countA++;
        // 唤醒可能在等待的销售员和供应者B
        notify(canSell);
        if (countB - countA &amp;lt; k) { // 检查是否可以解除对B的限制
             broadcast(canSupplyB); // 使用broadcast更安全, 唤醒所有等待的B供应者
        }
    }

    // 供应B食品
    public void SupplyB() {
        while (countB &amp;gt;= m || countB - countA &amp;gt;= k) {
            wait(canSupplyB);
        }
        // 放入食品B
        countB++;
        // 唤醒可能在等待的销售员和供应者A
        notify(canSell);
        if (countA - countB &amp;lt; k) { // 检查是否可以解除对A的限制
            broadcast(canSupplyA);
        }
    }

    // 销售
    public void Sell() {
        while (countA &amp;lt; 1 || countB &amp;lt; 1) {
            wait(canSell);
        }
        // 取走食品
        countA--;
        countB--;
        // 唤醒可能在等待的供应者
        broadcast(canSupplyA);
        broadcast(canSupplyB);
    }
}

// 进程调用
ProviderA() { while(true) { FoodStore.SupplyA(); } }
ProviderB() { while(true) { FoodStore.SupplyB(); } }
Seller() { while(true) { FoodStore.Sell(); } }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;说明&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;封装与互斥: 管程 &lt;code&gt;FoodStore&lt;/code&gt; 封装了所有共享状态 (&lt;code&gt;countA&lt;/code&gt;, &lt;code&gt;countB&lt;/code&gt;), 并自动保证了对这些状态的所有操作 (&lt;code&gt;SupplyA&lt;/code&gt;, &lt;code&gt;SupplyB&lt;/code&gt;, &lt;code&gt;Sell&lt;/code&gt;) 都是互斥的, 无需手动使用 &lt;code&gt;mutex&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;条件等待: 每个进程在进入管程后, 使用 &lt;code&gt;while&lt;/code&gt; 循环检查其执行条件. 如果条件不满足, 则调用 &lt;code&gt;wait()&lt;/code&gt; 在相应的条件变量上等待, 这会自动释放管程的锁.&lt;/li&gt;
&lt;li&gt;唤醒: 当一个进程改变了系统状态后 (如增加或减少了食品), 它会调用 &lt;code&gt;notify()&lt;/code&gt; 或 &lt;code&gt;broadcast()&lt;/code&gt; 来唤醒可能在等待的其它进程. 例如, &lt;code&gt;Sell&lt;/code&gt; 过程完成后, 货架空出, 两种食品的数量关系也可能改变, 因此它 &lt;code&gt;broadcast&lt;/code&gt; 唤醒所有可能在等待的供应者, 让它们重新检查条件. 使用 &lt;code&gt;while&lt;/code&gt; 而不是 &lt;code&gt;if&lt;/code&gt; 来检查等待条件, 确保了被唤醒的进程在继续执行前, 其条件一定是满足的, 这是一种健壮的并发编程模式.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;5-三峡大坝船闸调度问题&quot;&gt;5 三峡大坝船闸调度问题&lt;a class=&quot;anchor&quot; href=&quot;#5-三峡大坝船闸调度问题&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;51-问题描述&quot;&gt;5.1 问题描述&lt;a class=&quot;anchor&quot; href=&quot;#51-问题描述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;三峡大坝有五级船闸 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;∼&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;T_1 \sim T_5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∼&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. 上游船只依次通过 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;4&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;T_1 \to T_2 \to T_3 \to T_4 \to T_5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 到达下游, 下游船只依次通过 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;4&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;T_5 \to T_4 \to T_3 \to T_2 \to T_1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 到达上游. 假设船闸为单向通行, 即在同一时间段内, 所有五级船闸要么都给上游船只使用, 要么都给下游船只使用. 试用P、V操作解决该调度问题.&lt;/p&gt;
&lt;h3 id=&quot;52-使用pv操作的问题解答&quot;&gt;5.2 使用P、V操作的问题解答&lt;a class=&quot;anchor&quot; href=&quot;#52-使用pv操作的问题解答&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;关系分析&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;互斥关系:
&lt;ol&gt;
&lt;li&gt;船闸互斥: 每一级船闸 (&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;T_1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 到 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;T_5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;) 在同一时刻只能被一艘船使用.&lt;/li&gt;
&lt;li&gt;方向互斥: 整个船闸系统在同一时间只能有一个通行方向. 这是一个典型的”读者-写者问题”的变体. 我们可以将上行和下行的船队看作两类”读者”, 但这两类”读者”是互斥的.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;信号量设置&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gate[5]&lt;/code&gt;: 一个信号量数组, &lt;code&gt;gate[i]&lt;/code&gt; 代表第 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i+1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7429em;vertical-align:-0.0833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 级船闸, 初值均为 1, 用于保证每级船闸的互斥使用.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;up_mutex&lt;/code&gt;, &lt;code&gt;down_mutex&lt;/code&gt;: 两个互斥信号量, 初值为 1, 用于保护上行船只计数器 &lt;code&gt;up_count&lt;/code&gt; 和下行船只计数器 &lt;code&gt;down_count&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;up_count&lt;/code&gt;, &lt;code&gt;down_count&lt;/code&gt;: 两个整型变量, 初值为 0, 记录正在船闸系统中航行的上行和下行船只数量.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;direction_mutex&lt;/code&gt;: 关键的”方向”信号量, 初值为 1. 用于保证同一时间只有一个方向的船只可以通过. 第一个进入系统的船 (无论是上行还是下行) 将持有这个信号量, 最后一个离开系统的船将释放它.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 信号量和计数器
semaphore gate[5] = {1, 1, 1, 1, 1}; // 5个船闸
semaphore up_mutex = 1, down_mutex = 1;
semaphore direction_mutex = 1;
int up_count = 0, down_count = 0;

// 上行船只进程 (下游 -&amp;gt; 上游: T5 -&amp;gt; T1)
Upward_Ship() {
    // ---- 进入船闸系统 ----
    P(up_mutex);
    if (up_count == 0) { // 如果是第一艘上行船
        P(direction_mutex); // 尝试获取航行方向控制权
    }
    up_count++;
    V(up_mutex);

    // ---- 依次通过船闸 ----
    P(gate[4]); // 过 T5
    // 正在过 T5 ...
    V(gate[4]);

    P(gate[3]); // 过 T4
    // 正在过 T4 ...
    V(gate[3]);

    P(gate[2]); // 过 T3
    // 正在过 T3 ...
    V(gate[2]);

    P(gate[1]); // 过 T2
    // 正在过 T2 ...
    V(gate[1]);

    P(gate[0]); // 过 T1
    // 正在过 T1 ...
    V(gate[0]);

    // ---- 离开船闸系统 ----
    P(up_mutex);
    up_count--;
    if (up_count == 0) { // 如果是最后一艘上行船
        V(direction_mutex); // 释放航行方向控制权
    }
    V(up_mutex);
}

// 下行船只进程 (上游 -&amp;gt; 下游: T1 -&amp;gt; T5)
Downward_Ship() {
    // ---- 进入船闸系统 ----
    P(down_mutex);
    if (down_count == 0) { // 如果是第一艘下行船
        P(direction_mutex); // 尝试获取航行方向控制权
    }
    down_count++;
    V(down_mutex);

    // ---- 依次通过船闸 ----
    P(gate[0]); // 过 T1
    // 正在过 T1 ...
    V(gate[0]);

    P(gate[1]); // 过 T2
    // 正在过 T2 ...
    V(gate[1]);

    P(gate[2]); // 过 T3
    // 正在过 T3 ...
    V(gate[2]);

    P(gate[3]); // 过 T4
    // 正在过 T4 ...
    V(gate[3]);

    P(gate[4]); // 过 T5
    // 正在过 T5 ...
    V(gate[4]);

    // ---- 离开船闸系统 ----
    P(down_mutex);
    down_count--;
    if (down_count == 0) { // 如果是最后一艘下行船
        V(direction_mutex); // 释放航行方向控制权
    }
    V(down_mutex);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;说明:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gate[i]&lt;/code&gt; 保证了每个船闸的互斥使用.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;direction_mutex&lt;/code&gt; 是解决问题的核心. 当一个方向 (例如上行) 的第一艘船到达时, 它会执行 &lt;code&gt;P(direction_mutex)&lt;/code&gt;, 从而锁定了航行方向. 在此期间, 任何试图从相反方向 (下行) 进入的第一艘船都会因为 &lt;code&gt;P(direction_mutex)&lt;/code&gt; 而被阻塞.&lt;/li&gt;
&lt;li&gt;只有当该方向的所有船只都通过并离开后 (&lt;code&gt;up_count&lt;/code&gt; 减为 0) , 最后一艘船才会执行 &lt;code&gt;V(direction_mutex)&lt;/code&gt;, 从而允许相反方向的船只开始通行. 这实现了”单向通行”的要求.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;53-使用管程的解决方案&quot;&gt;5.3 使用管程的解决方案&lt;a class=&quot;anchor&quot; href=&quot;#53-使用管程的解决方案&quot;&gt;#&lt;/a&gt;&lt;/h3&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;共享变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;up_count&lt;/code&gt;, &lt;code&gt;down_count&lt;/code&gt;: 记录正在系统中的上行和下行船只数.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;direction&lt;/code&gt;: 表示当前航行方向 (&lt;code&gt;UP&lt;/code&gt;, &lt;code&gt;DOWN&lt;/code&gt;, 或 &lt;code&gt;NONE&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;条件变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;up_q&lt;/code&gt;: 上行船只在此等待.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;down_q&lt;/code&gt;: 下行船只在此等待.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管程过程:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RequestUpstreamPass()&lt;/code&gt;: 上行船只请求进入.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FinishUpstreamPass()&lt;/code&gt;: 上行船只完成通过.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RequestDownstreamPass()&lt;/code&gt;: 下行船只请求进入.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FinishDownstreamPass()&lt;/code&gt;: 下行船只完成通过.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;class DamLockControl {
    int up_count = 0, down_count = 0;
    // 使用一个枚举或整数表示方向: 0-NONE, 1-UP, 2-DOWN
    int current_direction = 0;
    condition up_q, down_q;

    // 上行船只请求进入
    public void RequestUpstreamPass() {
        // 如果当前是下行方向, 则等待
        while (current_direction == 2) {
            wait(up_q);
        }
        up_count++;
        current_direction = 1; // 占定方向为上行
    }

    // 上行船只完成通过
    public void FinishUpstreamPass() {
        up_count--;
        if (up_count == 0) {
            current_direction = 0; // 释放方向
            broadcast(down_q);     // 通知所有等待的下行船只
        }
    }

    // 下行船只请求进入
    public void RequestDownstreamPass() {
        // 如果当前是上行方向, 则等待
        while (current_direction == 1) {
            wait(down_q);
        }
        down_count++;
        current_direction = 2; // 占定方向为下行
    }

    // 下行船只完成通过
    public void FinishDownstreamPass() {
        down_count--;
        if (down_count == 0) {
            current_direction = 0; // 释放方向
            broadcast(up_q);       // 通知所有等待的上行船只
        }
    }
}

// 船只进程
Upward_Ship() {
    DamLockControl.RequestUpstreamPass();
    // 依次通过T5 -&amp;gt; T1... (这部分互斥可以用信号量或更细粒度的管程)
    DamLockControl.FinishUpstreamPass();
}

Downward_Ship() {
    DamLockControl.RequestDownstreamPass();
    // 依次通过T1 -&amp;gt; T5...
    DamLockControl.FinishDownstreamPass();
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;说明&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;方向控制: 管程通过 &lt;code&gt;current_direction&lt;/code&gt; 变量显式地管理航行方向. 当一个方向的船只请求进入时, 它会检查 &lt;code&gt;current_direction&lt;/code&gt;. 如果方向冲突, 它就在相应的条件变量上等待.&lt;/li&gt;
&lt;li&gt;进入与离开: 第一个进入的船只 (任一方向) 会设定 &lt;code&gt;current_direction&lt;/code&gt;, 从而阻止相反方向的船只. 最后一个离开的船只负责重置 &lt;code&gt;current_direction&lt;/code&gt; 并用 &lt;code&gt;broadcast&lt;/code&gt; 唤醒所有等待的对向船只, 把通行权交给它们.&lt;/li&gt;
&lt;li&gt;简化: 此管程方案解决核心的”方向互斥”问题. 对于”每级船闸只能有一艘船”的次级互斥问题, 可以使用单独的信号量数组 (如原解法) , 或者为每级船闸设计一个简单的互斥管程. 将两者结合即可得到完整解法.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;6-狒狒过峡谷问题&quot;&gt;6 狒狒过峡谷问题&lt;a class=&quot;anchor&quot; href=&quot;#6-狒狒过峡谷问题&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这个问题实际上是”读者-写者问题”的一个形象的变体.&lt;/p&gt;
&lt;h3 id=&quot;61-问题概述&quot;&gt;6.1 问题概述&lt;a class=&quot;anchor&quot; href=&quot;#61-问题概述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;一根绳索横跨峡谷, 狒狒可以沿绳索过峡谷. 只要方向相同, 可以有多只狒狒同时在绳索上. 但如果不同方向的狒狒同时上绳, 就会在中间相遇, 产生死锁. 编写一个避免死锁的程序.&lt;/p&gt;
&lt;p&gt;不考虑饥饿问题或者考虑饥饿问题.&lt;/p&gt;
&lt;h3 id=&quot;62-使用pv操作的解答-不考虑饥饿问题&quot;&gt;6.2 使用P、V操作的解答 (不考虑饥饿问题)&lt;a class=&quot;anchor&quot; href=&quot;#62-使用pv操作的解答-不考虑饥饿问题&quot;&gt;#&lt;/a&gt;&lt;/h3&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;&lt;code&gt;mutex_e&lt;/code&gt;, &lt;code&gt;mutex_w&lt;/code&gt;: 分别保护 &lt;code&gt;east_count&lt;/code&gt; 和 &lt;code&gt;west_count&lt;/code&gt;, 初值为 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;east_count&lt;/code&gt;, &lt;code&gt;west_count&lt;/code&gt;: 计数器, 记录正在绳索上向东和向西的狒狒数量, 初值为 0.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rope&lt;/code&gt;: 代表绳索使用权的信号量, 初值为 1. 第一个要上绳的狒狒群体 (无论是东行还是西行) 获取它, 最后一个离开的狒狒释放它.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 信号量和计数器
semaphore mutex_e = 1, mutex_w = 1;
semaphore rope = 1;
int east_count = 0, west_count = 0; // 可以合并为一个方向计数器和一个方向变量

// 向东走的狒狒
Eastward_Baboon() {
    P(mutex_e);
    east_count++;
    if (east_count == 1) { // 如果是第一只向东的狒狒
        P(rope);           // 尝试获取绳索
    }
    V(mutex_e);

    // --- 过峡谷 ---
    // crossing the rope...
    // --- 过完峡谷 ---

    P(mutex_e);
    east_count--;
    if (east_count == 0) { // 如果是最后一只向东的狒狒
        V(rope);           // 释放绳索
    }
    V(mutex_e);
}

// 向西走的狒狒
Westward_Baboon() {
    P(mutex_w);
    west_count++;
    if (west_count == 1) { // 如果是第一只向西的狒狒
        P(rope);           // 尝试获取绳索
    }
    V(mutex_w);

    // --- 过峡谷 ---
    // crossing the rope...
    // --- 过完峡谷 ---

    P(mutex_w);
    west_count--;
    if (west_count == 0) { // 如果是最后一只向西的狒狒
        V(rope);           // 释放绳索
    }
    V(mutex_w);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;说明:
这个解法是”读者优先”的变体. 例如, 如果有一批狒狒正在向东走, &lt;code&gt;rope&lt;/code&gt; 信号量被持有. 此时, 任何想向西走的狒狒都会在 &lt;code&gt;P(rope)&lt;/code&gt; 处被阻塞. 而新来的向东的狒狒则可以不受阻碍地直接上绳. 这可能导致西行的狒狒饥饿.&lt;/p&gt;
&lt;h3 id=&quot;63-使用管程的解答-不考虑饥饿问题&quot;&gt;6.3 使用管程的解答 (不考虑饥饿问题)&lt;a class=&quot;anchor&quot; href=&quot;#63-使用管程的解答-不考虑饥饿问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;管程设计&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个管程 &lt;code&gt;RopeBridge&lt;/code&gt; 将封装所有共享状态和同步逻辑.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;共享变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;east_count&lt;/code&gt;: 记录正在向东走的狒狒数量.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;west_count&lt;/code&gt;: 记录正在向西走的狒狒数量.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;条件变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;can_go_east&lt;/code&gt;: 准备向东走的狒狒在此条件上等待.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;can_go_west&lt;/code&gt;: 准备向西走的狒狒在此条件上等待.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管程过程 (Procedures):
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ArriveEast()&lt;/code&gt; / &lt;code&gt;LeaveEast()&lt;/code&gt;: 东行狒狒调用的进入和离开方法.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ArriveWest()&lt;/code&gt; / &lt;code&gt;LeaveWest()&lt;/code&gt;: 西行狒狒调用的进入和离开方法.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;class RopeBridge {
    int east_count = 0;
    int west_count = 0;
    condition can_go_east, can_go_west;

    // 狒狒准备向东走时调用
    public void ArriveEast() {
        // 如果有任何狒狒正在向西走, 则必须等待
        while (west_count &amp;gt; 0) {
            wait(can_go_east);
        }
        // 允许通行, 东行计数加一
        east_count++;
    }

    // 狒狒向东走完后调用
    public void LeaveEast() {
        // 东行计数减一
        east_count--;
        // 如果我是最后一个东行的狒狒, 就唤醒所有等待西行的狒狒
        if (east_count == 0) {
            broadcast(can_go_west);
        }
    }

    // 狒狒准备向西走时调用
    public void ArriveWest() {
        // 如果有任何狒狒正在向东走, 则必须等待
        while (east_count &amp;gt; 0) {
            wait(can_go_west);
        }
        // 允许通行, 西行计数加一
        west_count++;
    }

    // 狒狒向西走完后调用
    public void LeaveWest() {
        // 西行计数减一
        west_count--;
        // 如果我是最后一个西行的狒狒, 就唤醒所有等待东行的狒狒
        if (west_count == 0) {
            broadcast(can_go_east);
        }
    }
}

// 外部调用逻辑
// 向东走的狒狒
Eastward_Baboon() {
    RopeBridge.ArriveEast();
    // --- 正在过峡谷 ---
    RopeBridge.LeaveEast();
}

// 向西走的狒狒
Westward_Baboon() {
    RopeBridge.ArriveWest();
    // --- 正在过峡谷 ---
    RopeBridge.LeaveWest();
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;说明
这个解法是”读者优先”模式的管程实现. 例如, 当有一批狒狒正在向东走时 (&lt;code&gt;east_count &amp;gt; 0&lt;/code&gt;), 任何新到达的、想向东走的狒狒都可以立即通过 &lt;code&gt;ArriveEast&lt;/code&gt; 方法并上绳. 然而, 所有想向西走的狒狒都会在 &lt;code&gt;ArriveWest&lt;/code&gt; 中的 &lt;code&gt;wait(can_go_west)&lt;/code&gt; 处被阻塞. 只有当最后一个东行狒狒调用 &lt;code&gt;LeaveEast&lt;/code&gt; 并执行 &lt;code&gt;broadcast(can_go_west)&lt;/code&gt; 后, 西行狒狒才可能被唤醒.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这种策略的缺点是, 如果东行狒狒源源不断地到来, 那么西行狒狒可能会永远等待下去, 导致饥饿.&lt;/p&gt;
&lt;h3 id=&quot;64-使用pv操作的解答-考虑饥饿问题&quot;&gt;6.4 使用P、V操作的解答 (考虑饥饿问题)&lt;a class=&quot;anchor&quot; href=&quot;#64-使用pv操作的解答-考虑饥饿问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在6.2节的解法中, 一个方向的狒狒流可能会导致另一个方向的狒狒永久等待, 产生饥饿. 为了解决这个问题, 我们可以增加一个”旋转门” (turnstile) 信号量, 来保证对绳索的请求是公平的, 实现先到先服务 (FCFS).&lt;/p&gt;
&lt;aside aria-label=&quot;Note&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Note&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;我觉得这是一个很巧妙的算法!&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;ol&gt;
&lt;li&gt;信号量设置&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mutex_e&lt;/code&gt;, &lt;code&gt;mutex_w&lt;/code&gt;: 分别保护 &lt;code&gt;east_count&lt;/code&gt; 和 &lt;code&gt;west_count&lt;/code&gt; 的互斥锁, 初值为 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;east_count&lt;/code&gt;, &lt;code&gt;west_count&lt;/code&gt;: 计数器, 记录正在绳索上的狒狒数量, 初值为 0.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rope&lt;/code&gt;: 代表绳索的使用权, 保证方向的互斥, 初值为 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;turnstile&lt;/code&gt;: 新增的”旋转门”或”门卫”信号量, 所有狒狒在尝试获取绳索前都必须先通过它. 这确保了对&lt;code&gt;rope&lt;/code&gt;信号量的竞争是公平的. 初值为 1.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 信号量和计数器
semaphore mutex_e = 1, mutex_w = 1;
semaphore rope = 1;
semaphore turnstile = 1; // 保证公平的旋转门
int east_count = 0, west_count = 0;

// 向东走的狒狒
Eastward_Baboon() {
    P(turnstile); // 在旋转门前排队
    P(mutex_e);
    east_count++;
    if (east_count == 1) { // 如果是第一只东行狒狒
        P(rope);           // 尝试为东行方向获取绳索
    }
    V(mutex_e);
    V(turnstile); // 通过旋转门, 让下一个人/狒狒可以排队

    // --- 过峡谷 ---

    P(mutex_e);
    east_count--;
    if (east_count == 0) { // 如果是最后一只东行狒狒
        V(rope);           // 释放绳索
    }
    V(mutex_e);
}

// 向西走的狒狒
Westward_Baboon() {
    P(turnstile); // 在旋转门前排队
    P(mutex_w);
    west_count++;
    if (west_count == 1) { // 如果是第一只西行狒狒
        P(rope);           // 尝试为西行方向获取绳索
    }
    V(mutex_w);
    V(turnstile); // 通过旋转门

    // --- 过峡谷 ---

    P(mutex_w);
    west_count--;
    if (west_count == 0) { // 如果是最后一只西行狒狒
        V(rope);           // 释放绳索
    }
    V(mutex_w);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;说明&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;公平性保证: &lt;code&gt;turnstile&lt;/code&gt; 是解决饥饿问题的关键. 假设有一队东行狒狒正在过桥, &lt;code&gt;rope&lt;/code&gt; 信号量被东行者持有. 此时, 一只西行狒狒 &lt;code&gt;W1&lt;/code&gt; 到达, 它会通过 &lt;code&gt;turnstile&lt;/code&gt; 并阻塞在 &lt;code&gt;P(rope)&lt;/code&gt; 上. 紧接着, 一只新的东行狒狒 &lt;code&gt;E_new&lt;/code&gt; 到达. &lt;code&gt;E_new&lt;/code&gt; 会被 &lt;code&gt;P(turnstile)&lt;/code&gt; 阻塞, 因为 &lt;code&gt;W1&lt;/code&gt; 还没有执行 &lt;code&gt;V(turnstile)&lt;/code&gt;. 这样, &lt;code&gt;E_new&lt;/code&gt; 就必须排在 &lt;code&gt;W1&lt;/code&gt; 之后. 当东行队伍全部过完并释放 &lt;code&gt;rope&lt;/code&gt; 时, &lt;code&gt;W1&lt;/code&gt; 就能获得绳索, &lt;code&gt;E_new&lt;/code&gt; 则需等待 &lt;code&gt;W1&lt;/code&gt; 的队伍过完. 这就避免了西行狒狒的饥饿.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;65-使用管程的解答-考虑饥饿问题&quot;&gt;6.5 使用管程的解答 (考虑饥饿问题)&lt;a class=&quot;anchor&quot; href=&quot;#65-使用管程的解答-考虑饥饿问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;管程条件变量本身不保证先到先服务. 如果只把所有狒狒放到同一个条件变量上并 &lt;code&gt;broadcast&lt;/code&gt;, 被唤醒者仍会重新竞争管程锁, 对向等待者仍可能被后来同向者反复插队. 因此需要在管程状态里显式记录等待方向, 当发现对向已经等待时关闭当前方向的新入口.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;管程设计&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;共享变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;east_count&lt;/code&gt;, &lt;code&gt;west_count&lt;/code&gt;: 记录正在绳索上的狒狒数量.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;east_waiting&lt;/code&gt;, &lt;code&gt;west_waiting&lt;/code&gt;: 记录两个方向正在等待的数量.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;turn&lt;/code&gt;: 记录下一批优先放行的方向, &lt;code&gt;0&lt;/code&gt; 表示当前没有指定方向.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;条件变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;can_go_east&lt;/code&gt;, &lt;code&gt;can_go_west&lt;/code&gt;: 两个方向各自等待的条件变量.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管程过程:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Arrive(direction)&lt;/code&gt;: 狒狒到达时调用, 传入其方向.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Leave(direction)&lt;/code&gt;: 狒狒离开时调用.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;class FairRopeBridge {
    int east_count = 0;
    int west_count = 0;
    int east_waiting = 0;
    int west_waiting = 0;
    int turn = 0; // 0-NONE, 1-EAST, 2-WEST
    condition can_go_east, can_go_west;

    // 狒狒到达, 准备上绳
    public void Arrive(direction) {
        if (direction == EAST) {
            east_waiting++;
            if (west_count &amp;gt; 0) {
                turn = 1;
            }
            while (west_count &amp;gt; 0 || (turn == 2 &amp;amp;&amp;amp; west_waiting &amp;gt; 0)) {
                wait(can_go_east);
            }
            east_waiting--;
            east_count++;
        } else { // direction == WEST
            west_waiting++;
            if (east_count &amp;gt; 0) {
                turn = 2;
            }
            while (east_count &amp;gt; 0 || (turn == 1 &amp;amp;&amp;amp; east_waiting &amp;gt; 0)) {
                wait(can_go_west);
            }
            west_waiting--;
            west_count++;
        }
    }

    // 狒狒离开
    public void Leave(direction) {
        if (direction == EAST) {
            east_count--;
            if (east_count == 0) {
                if (west_waiting &amp;gt; 0) {
                    turn = 2;
                    broadcast(can_go_west);
                } else {
                    turn = 0;
                    broadcast(can_go_east);
                }
            }
        } else { // direction == WEST
            west_count--;
            if (west_count == 0) {
                if (east_waiting &amp;gt; 0) {
                    turn = 1;
                    broadcast(can_go_east);
                } else {
                    turn = 0;
                    broadcast(can_go_west);
                }
            }
        }
    }
}

// 外部调用逻辑
Eastward_Baboon() {
    FairRopeBridge.Arrive(EAST);
    // --- 正在过峡谷 ---
    FairRopeBridge.Leave(EAST);
}

Westward_Baboon() {
    FairRopeBridge.Arrive(WEST);
    // --- 正在过峡谷 ---
    FairRopeBridge.Leave(WEST);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;说明&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;批次切换: 当某个方向正在通过且对向开始等待时, &lt;code&gt;turn&lt;/code&gt; 会被设置为对向. 之后新到达的当前方向狒狒会等待, 已经在绳索上的狒狒可以继续完成通过.&lt;/li&gt;
&lt;li&gt;避免饥饿: 最后一只当前方向狒狒离开后, 如果对向有人等待, 管程会唤醒对向队列. 这样一批同向者可以并发通过, 对向等待者也能在当前批次结束后获得机会.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while&lt;/code&gt; 检查: 被唤醒的狒狒仍然用 &lt;code&gt;while&lt;/code&gt; 重新检查条件, 适配 Mesa 管程语义和虚假唤醒.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;7-睡眠理发师问题&quot;&gt;7 睡眠理发师问题&lt;a class=&quot;anchor&quot; href=&quot;#7-睡眠理发师问题&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;71-问题描述&quot;&gt;7.1 问题描述&lt;a class=&quot;anchor&quot; href=&quot;#71-问题描述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;理发店有一位理发师、一把理发椅和N把供顾客等候的椅子.&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 id=&quot;72-使用pv操作的问题解答-一位理发师&quot;&gt;7.2 使用P、V操作的问题解答 (一位理发师)&lt;a class=&quot;anchor&quot; href=&quot;#72-使用pv操作的问题解答-一位理发师&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;关系分析
这是一个生产者-消费者模型. 顾客是”生产者” (生产理发请求) , 理发师是”消费者” (消费理发请求) , 椅子是缓冲区. 但是和生产者-消费者模型不同的是, 不能在顾客坐下 (生产理发请求) 之后立刻结束, 必须等理发师理发结束 (消费理发请求) 之后才能结束.&lt;/li&gt;
&lt;/ol&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;互斥关系: 对等候椅数量 &lt;code&gt;waiting_chairs&lt;/code&gt; 的修改需要互斥.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;信号量设置&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;customers&lt;/code&gt;: 信号量, 表示等待理发的顾客数量. 理发师通过 &lt;code&gt;P(customers)&lt;/code&gt; 来检查是否有顾客, 如果没有就会睡眠. 初值为 0.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;barbers&lt;/code&gt;: 信号量, 表示空闲的理发师数量. 顾客通过 &lt;code&gt;P(barbers)&lt;/code&gt; 来等待理发师空闲. 初值为 0 (或 1, 取决于初始状态理发师是否在睡觉). 我们设为0, 表示初始没有空闲理发师, 需要顾客唤醒.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mutex&lt;/code&gt;: 互斥信号量, 用于保护对 &lt;code&gt;waiting_chairs&lt;/code&gt; 计数器的访问, 初值为 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;waiting_chairs&lt;/code&gt;: 一个整型变量, 记录可用的等候椅数量, 初值为 N.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 常量和变量
#define N 5 // 假设有5把椅子
int waiting_chairs = N; // 可用的等候椅数量, 就是生产者-消费者模型中的 empty 信号量

// 信号量
semaphore customers = 0; // 等待的顾客数, 就是生产者-消费者模型中的 full 信号量
semaphore barbers = 0;   // 空闲的理发师数, 加入的新信号量, 因为必须理完发才能走, 所以必须找到一个空闲的理发师
semaphore mutex = 1;     // 保护 waiting_chairs, 就是生产者-消费者模型中的 mutex 信号量

// 顾客进程-生产者
Customer() {
    P(mutex);
    if (waiting_chairs &amp;gt; 0) {
        waiting_chairs--; // 坐下一把椅子
        V(mutex);         // 释放锁
        V(customers);     // 通知理发师, 我来了

        // 如果是生产者-消费者模型, 这里就可以结束了, 因为进入就可以结束了, 但是理发坐下之后还得等

        P(barbers);       // 等待理发师准备好 (即理发师被叫醒给自己理发)

        // --- 正在理发 ---
        // get_haircut();
    } else {      // 没有空椅子了
        V(mutex); // 必须释放锁再离开

        // --- 顾客离开 ---
        // leave_shop();
    }
}

// 理发师进程-消费者
Barber() {
    while(true) {
        P(customers); // 等待顾客到来. 如果没有顾客, 则在椅子上睡眠.

        // 下述三行其实就是生产者-消费者模型中的 V(empty) 操作, 因为顾客坐下之后, 等待用的椅子就多了一把, 所以 empty 信号量加一
        P(mutex);
        waiting_chairs++; // 一个顾客从等待区坐到理发椅上, 空出一把椅子
        V(mutex);

        // 如果是生产者-消费者模型, 这里就可以结束了, 因为使用完物品就可以结束了

        V(barbers); // 我这个理发师被叫醒了, 可以开始理发了

        // --- 理发进行中 ---
        // cut_hair();
        // --- 理发结束 ---
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这个解法保证互斥和同步, 但不单独保证顾客按到达顺序理发. 如果信号量等待队列是FIFO, 先等待的顾客会先被唤醒. 如果信号量不保证公平, 后到顾客可能先获得 &lt;code&gt;barbers&lt;/code&gt;. 严格FIFO服务需要维护顾客队列或为每个顾客设置单独的等待信号量.&lt;/p&gt;
&lt;h3 id=&quot;73-使用管程的解决方案-一位理发师&quot;&gt;7.3 使用管程的解决方案 (一位理发师)&lt;a class=&quot;anchor&quot; href=&quot;#73-使用管程的解决方案-一位理发师&quot;&gt;#&lt;/a&gt;&lt;/h3&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;共享变量: &lt;code&gt;waiting_count&lt;/code&gt;, 记录在等候椅上的人数.&lt;/li&gt;
&lt;li&gt;条件变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;customer_is_waiting&lt;/code&gt;: 理发师在此等待顾客来.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;barber_is_ready&lt;/code&gt;: 顾客在此等待理发师睡醒.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管程过程:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GetHaircut()&lt;/code&gt;: 由顾客调用.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NextCustomer()&lt;/code&gt;: 由理发师调用.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;class BarberShop {
    int waiting_count = 0;
    final int N = 5; // 椅子数量
    condition customer_is_waiting, barber_is_ready;

    // 顾客调用的过程
    public function GetHaircut() returns bool {
        if (waiting_count == N) {
            return false; // 没有椅子, 理发失败
        }
        waiting_count++;
        // 唤醒理发师
        notify(customer_is_waiting);

        // 等待理发师为我服务, 如果是生产者-消费者模型, 这里就可以结束了
        wait(barber_is_ready);
        return true; // 理发成功
    }

    // 理发师调用的过程
    public void NextCustomer() {
        // 如果没有等待的顾客, 就睡觉
        while (waiting_count == 0) {
            wait(customer_is_waiting);
        }
        waiting_count--;

        // 叫醒一个顾客, 如果是生产者-消费者模型, 这里就可以结束了
        notify(barber_is_ready);
    }
}

// 进程
Customer() {
    if (BarberShop.GetHaircut()) {
        // 正在理发...
    } else {
        // 离开...
    }
}

Barber() {
    while(true) {
        BarberShop.NextCustomer();
        // cut_hair()...
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;说明
顾客调用 &lt;code&gt;GetHaircut&lt;/code&gt;, 如果成功进入等待队列, 就 &lt;code&gt;notify&lt;/code&gt; 理发师并 &lt;code&gt;wait&lt;/code&gt; 在自己的条件上. 理发师调用 &lt;code&gt;NextCustomer&lt;/code&gt;,  &lt;code&gt;wait&lt;/code&gt; 等待顾客, 被唤醒后 &lt;code&gt;notify&lt;/code&gt; 一个顾客来理发. 这种客户和理发师之间的”握手”清晰.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;74-问题解答-m个理发师&quot;&gt;7.4 问题解答 (M个理发师)&lt;a class=&quot;anchor&quot; href=&quot;#74-问题解答-m个理发师&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如果理发店有 M 个理发师, 如何修改？&lt;/p&gt;
&lt;p&gt;如果有多个消费者, 不需要修改代码, 因为本身生产者-消费者模型中, 消费者是多个的, 所以不需要修改.&lt;/p&gt;
&lt;h2 id=&quot;8-第二类读者写者问题-写者优先&quot;&gt;8 第二类读者写者问题 (写者优先)&lt;a class=&quot;anchor&quot; href=&quot;#8-第二类读者写者问题-写者优先&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;81-问题描述&quot;&gt;8.1 问题描述&lt;a class=&quot;anchor&quot; href=&quot;#81-问题描述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;要求：
a. 多个读者可以同时进行读.
b. 写者必须互斥 (一次只有一个写者, 且读写不能同时) .
c. 写者优先于读者 (一旦有写者在等待, 后续到达的读者必须等待, 直到所有等待的写者都完成后才能读) .&lt;/p&gt;
&lt;h3 id=&quot;82-使用pv操作的问题解答&quot;&gt;8.2 使用P、V操作的问题解答&lt;a class=&quot;anchor&quot; href=&quot;#82-使用pv操作的问题解答&quot;&gt;#&lt;/a&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;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;read_count&lt;/code&gt;, &lt;code&gt;write_count&lt;/code&gt;: 读者和写者的计数器, 初值为 0.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mutex_r&lt;/code&gt;, &lt;code&gt;mutex_w&lt;/code&gt;: 分别保护 &lt;code&gt;read_count&lt;/code&gt; 和 &lt;code&gt;write_count&lt;/code&gt; 的互斥锁, 初值为 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;r_gate&lt;/code&gt;: 一个”读者大门”信号量. 当有写者在等待时, 此门关闭, 阻止新读者进入. 初值为 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;w_resource&lt;/code&gt;: 代表共享资源的”写者锁”, 也用于实现读者和写者的互斥. 初值为 1.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;int read_count = 0, write_count = 0;
semaphore mutex_r = 1, mutex_w = 1; // 保护计数器的锁
semaphore r_gate = 1;               // 读者进入的大门
semaphore w_resource = 1;           // 资源本身的锁, 也是写者锁

// 读者进程
Reader() {
    P(r_gate);        // 尝试通过读者大门
    P(mutex_r);
    read_count++;
    if (read_count == 1) { // 如果是第一个读者
        P(w_resource); // 锁住资源, 阻止写者
    }
    V(mutex_r);
    V(r_gate);        // 通过后, 立刻让其他读者也通过大门

    // --- 读操作 ---
    // reading...

    P(mutex_r);
    read_count--;
    if (read_count == 0) { // 如果是最后一个读者
        V(w_resource); // 释放资源, 允许写者进入
    }
    V(mutex_r);
}

// 写者进程
Writer() {
    P(mutex_w);
    write_count++;
    if (write_count == 1) { // 如果是第一个写者
        P(r_gate);        // 关上读者大门, 阻止新读者进入
    }
    V(mutex_w);

    P(w_resource);      // 获取资源的独占访问权 (写者锁)

    // --- 写操作 ---
    // writing...

    V(w_resource);      // 释放资源

    P(mutex_w);
    write_count--;
    if (write_count == 0) { // 如果是最后一个写者
        V(r_gate);        // 打开读者大门, 允许读者进入
    }
    V(mutex_w);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;说明:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;写者如何优先: 当第一个写者到达时 (&lt;code&gt;write_count&lt;/code&gt; 变为 1), 它会立即 &lt;code&gt;P(r_gate)&lt;/code&gt;. 这会导致所有后续到来的读者全部被阻塞在 &lt;code&gt;P(r_gate)&lt;/code&gt; 处. 它们甚至没有机会去修改 &lt;code&gt;read_count&lt;/code&gt; 或尝试获取 &lt;code&gt;w_resource&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;写者执行: 写者进程会继续执行 &lt;code&gt;P(w_resource)&lt;/code&gt;. 如果当前有读者正在读, 它会等待这些读者全部结束 (最后一个读者会 &lt;code&gt;V(w_resource)&lt;/code&gt;). 一旦 &lt;code&gt;w_resource&lt;/code&gt; 被释放, 等待的写者就能立刻获得它, 开始写入.&lt;/li&gt;
&lt;li&gt;写者离开: 当最后一个等待的写者完成工作后 (&lt;code&gt;write_count&lt;/code&gt; 变为 0), 它会 &lt;code&gt;V(r_gate)&lt;/code&gt;, 把读者大门打开, 此时在门外等候的读者们才能进入.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种机制确保了只要有写者在等待, 就不会有新的读者开始读取, 体现了写者优先.&lt;/p&gt;
&lt;h3 id=&quot;83-使用管程的解决方案&quot;&gt;8.3 使用管程的解决方案&lt;a class=&quot;anchor&quot; href=&quot;#83-使用管程的解决方案&quot;&gt;#&lt;/a&gt;&lt;/h3&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;共享变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;read_count&lt;/code&gt;: 正在读的读者数.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write_waiting_count&lt;/code&gt;: 正在等待的写者数.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;writer_active&lt;/code&gt;: 是否有写者正在写.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;条件变量: &lt;code&gt;can_read&lt;/code&gt;, &lt;code&gt;can_write&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;管程过程: &lt;code&gt;StartRead&lt;/code&gt;, &lt;code&gt;EndRead&lt;/code&gt;, &lt;code&gt;StartWrite&lt;/code&gt;, &lt;code&gt;EndWrite&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;class WriterPriorityRW {
    int read_count = 0;
    int write_waiting_count = 0;
    bool writer_active = false;
    condition can_read, can_write;

    public void StartWrite() {
        write_waiting_count++;
        // 如果有读者在读, 或者有写者在写, 则等待
        while (read_count &amp;gt; 0 || writer_active) {
            wait(can_write);
        }
        write_waiting_count--;
        writer_active = true;
    }

    public void EndWrite() {
        writer_active = false;
        // 优先唤醒等待的写者
        if (write_waiting_count &amp;gt; 0) {
            notify(can_write);
        } else { // 否则唤醒所有等待的读者
            broadcast(can_read);
        }
    }

    public void StartRead() {
        // 如果有写者在写, 或者有写者在等待, 则等待
        while (writer_active || write_waiting_count &amp;gt; 0) {
            wait(can_read);
        }
        read_count++;
    }

    public void EndRead() {
        read_count--;
        // 如果是最后一个读者, 则唤醒一个写者
        if (read_count == 0) {
            notify(can_write);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;说明&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;写者优先的实现: 关键在于 &lt;code&gt;StartRead&lt;/code&gt; 和 &lt;code&gt;StartWrite&lt;/code&gt; 中的 &lt;code&gt;write_waiting_count&lt;/code&gt; 计数器.
&lt;ul&gt;
&lt;li&gt;一个写者到达时, 先将 &lt;code&gt;write_waiting_count&lt;/code&gt; 加一, 表明”有写者驾到”.&lt;/li&gt;
&lt;li&gt;一个读者准备读之前, 必须检查 &lt;code&gt;writer_active&lt;/code&gt; 和 &lt;code&gt;write_waiting_count&lt;/code&gt;. 只要 &lt;code&gt;write_waiting_count &amp;gt; 0&lt;/code&gt;, 即使当前没有写者在写, 读者也必须等待. 这就赋予了写者优先权.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;唤醒逻辑: 在 &lt;code&gt;EndWrite&lt;/code&gt; 中, 优先检查并唤醒其他等待的写者. 只有在没有写者等待时, 才会唤醒读者. 在 &lt;code&gt;EndRead&lt;/code&gt; 中, 最后一个读者离开时, 需要检查是否有写者在等待, 并负责唤醒它们.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;9-利用信号量管理共享资源&quot;&gt;9 利用信号量管理共享资源&lt;a class=&quot;anchor&quot; href=&quot;#9-利用信号量管理共享资源&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这是一个复杂的问题, 通过分析一个有问题的代码, 逐步引出两种经典的并发编程模式.&lt;/p&gt;
&lt;h3 id=&quot;91-问题描述&quot;&gt;9.1 问题描述&lt;a class=&quot;anchor&quot; href=&quot;#91-问题描述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;一个共享资源有如下特性：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当使用者少于 3 个时, 新进程可以立刻获得资源.&lt;/li&gt;
&lt;li&gt;当 3 个资源都被占用后, 新进程必须等待. 直到当前使用资源的 3 个进程都释放完资源后, 其他等待的进程才能获得资源 (一批一批地进入) .&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;92-初始的错误程序分析&quot;&gt;9.2 初始的错误程序分析&lt;a class=&quot;anchor&quot; href=&quot;#92-初始的错误程序分析&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;程序代码 (简化版):&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 申请资源
P(mutex);
if (must_wait) { // must_wait is (active == 3)
    waiting++;
    V(mutex);
    P(block);
    P(mutex); // 再次获取锁, 修改waiting
    waiting--;
}
active++;
must_wait = (active == 3);
V(mutex);

// 释放资源
P(mutex);
active--;
if (active == 0) {
    int n = min(waiting, 3);
    for (i=0; i&amp;lt;n; i++) {
        V(block);
    }
    must_wait = false;
}
V(mutex);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;a. 解释其出错的位置&lt;/p&gt;
&lt;p&gt;答: 错误发生在被 &lt;code&gt;P(block)&lt;/code&gt; 唤醒之后.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;假设进程&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;4&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_4&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;6&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_6&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;… 都在 &lt;code&gt;P(block)&lt;/code&gt; 处等待.&lt;/li&gt;
&lt;li&gt;当 &lt;code&gt;active&lt;/code&gt; 变为 0 时, 最后一个离开的进程 (比如&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_3&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;) 会执行 &lt;code&gt;V(block)&lt;/code&gt; 三次, 唤醒 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;4&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_4&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;6&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_6&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;这三个进程被唤醒后, 会依次尝试获取 &lt;code&gt;mutex&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;假设 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;4&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_4&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 第一个获取 &lt;code&gt;mutex&lt;/code&gt;. 它执行 &lt;code&gt;waiting--&lt;/code&gt;, 然后 &lt;code&gt;active++&lt;/code&gt; (此时 &lt;code&gt;active&lt;/code&gt;=1), &lt;code&gt;must_wait&lt;/code&gt; 仍然是 &lt;code&gt;false&lt;/code&gt;, 然后释放 &lt;code&gt;mutex&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;现在轮到 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. 它获取 &lt;code&gt;mutex&lt;/code&gt;, 执行 &lt;code&gt;waiting--&lt;/code&gt;, &lt;code&gt;active++&lt;/code&gt; (此时 &lt;code&gt;active&lt;/code&gt;=2), &lt;code&gt;must_wait&lt;/code&gt; 仍然是 &lt;code&gt;false&lt;/code&gt;, 释放 &lt;code&gt;mutex&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;问题来了: 此时, 如果一个新进程 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_{10}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 到达, 它执行申请资源的代码. 它会发现 &lt;code&gt;must_wait&lt;/code&gt; 是 &lt;code&gt;false&lt;/code&gt;！于是它不会去 &lt;code&gt;P(block)&lt;/code&gt; 等待, 而是直接 &lt;code&gt;active++&lt;/code&gt; (此时 &lt;code&gt;active&lt;/code&gt;=3), 并将 &lt;code&gt;must_wait&lt;/code&gt; 设置为 &lt;code&gt;true&lt;/code&gt;. &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_{10}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;“插队”成功了.&lt;/li&gt;
&lt;li&gt;之后, 之前被唤醒的 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;mn&gt;6&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;P_6&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.1389em&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 才能获取 &lt;code&gt;mutex&lt;/code&gt;, 它执行时 &lt;code&gt;active&lt;/code&gt; 已经变成3, &lt;code&gt;must_wait&lt;/code&gt; 已经为 &lt;code&gt;true&lt;/code&gt;, 但它还是会执行 &lt;code&gt;active++&lt;/code&gt;, 导致 &lt;code&gt;active&lt;/code&gt; 变成4, 这违反了最多3个进程使用的规则.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;根本原因: 进程被唤醒后, 它所处的”环境” (即 &lt;code&gt;must_wait&lt;/code&gt; 的状态) 可能已经被其他进程改变了.&lt;/p&gt;
&lt;p&gt;b. &lt;code&gt;if&lt;/code&gt; 换成 &lt;code&gt;while&lt;/code&gt; 是否解决问题？&lt;/p&gt;
&lt;p&gt;答: 将 &lt;code&gt;if(must_wait)&lt;/code&gt; 换成 &lt;code&gt;while(must_wait)&lt;/code&gt; 可以避免 &lt;code&gt;active&lt;/code&gt; 超出3, 但仍然不能作为最终解法.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如何解决越界: 如果P10插队并设置了 &lt;code&gt;must_wait = true&lt;/code&gt;, 那么当P6被唤醒并重新检查 &lt;code&gt;while(must_wait)&lt;/code&gt; 条件时, 它会再次进入等待状态. 这避免了 &lt;code&gt;active &amp;gt; 3&lt;/code&gt; 的情况.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;剩余问题: 这个改法会浪费唤醒信号, 也没有解决被唤醒进程和新到达进程重新竞争 &lt;code&gt;mutex&lt;/code&gt; 的问题. P6重新等待后会把 &lt;code&gt;waiting&lt;/code&gt; 加回去, 等当前3个使用者都释放后还能再被唤醒, 所以这里不会形成永久死锁. 更好的写法是后面的 “I’ll Do It for You” 或 “Pass the Baton”, 它们分别通过预先替等待者修改状态、直接传递互斥权来消除这个竞争窗口.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;93-正确解法一-ill-do-it-for-you-模式&quot;&gt;9.3 正确解法一: “I’ll Do It for You” 模式&lt;a class=&quot;anchor&quot; href=&quot;#93-正确解法一-ill-do-it-for-you-模式&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;程序代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 申请资源 (Acquire)
P(mutex);
if (must_wait) {
    waiting++;
    V(mutex);
    P(block); // 等待被别人处理
} else {
    active++;
    must_wait = (active == 3);
    V(mutex);
}

// 释放资源 (Release)
P(mutex);
active--;
if (active == 0) {
    int n = min(waiting, 3);
    waiting -= n;
    active = n;
    for (i=0; i&amp;lt;n; i++) {
        V(block);
    }
    must_wait = (active == 3);
}
V(mutex);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;a. 解释工作方式和正确性
这种模式被称为 “I’ll Do It for You” (我来帮你做).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;核心思想: 申请资源的进程如果需要等待, 它什么都不做, 只是排队 (&lt;code&gt;P(block)&lt;/code&gt;). 唤醒它的进程 (即释放资源的进程) 会替它完成所有状态变量的修改.&lt;/li&gt;
&lt;li&gt;工作流程:
&lt;ol&gt;
&lt;li&gt;申请: 进程检查 &lt;code&gt;must_wait&lt;/code&gt;. 如果为 &lt;code&gt;true&lt;/code&gt;, 则 &lt;code&gt;waiting++&lt;/code&gt; 然后就去 &lt;code&gt;P(block)&lt;/code&gt; 睡觉. 它不自己去 &lt;code&gt;active++&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;释放: 当最后一个使用者 (&lt;code&gt;active&lt;/code&gt;变为0) 释放资源时, 它扮演了”组织者”的角色. 它查看有多少进程在等待 (&lt;code&gt;waiting&lt;/code&gt;), 决定唤醒 &lt;code&gt;n&lt;/code&gt; 个 (最多3个) . 关键在于：它在唤醒它们之前, 就预先把 &lt;code&gt;active&lt;/code&gt; 设置为 &lt;code&gt;n&lt;/code&gt;, 把 &lt;code&gt;waiting&lt;/code&gt; 减去 &lt;code&gt;n&lt;/code&gt;, 并更新好 &lt;code&gt;must_wait&lt;/code&gt; 状态.&lt;/li&gt;
&lt;li&gt;唤醒后: 被唤醒的进程从 &lt;code&gt;P(block)&lt;/code&gt; 返回后, 什么也不用做, 它已经被”记作”是 &lt;code&gt;active&lt;/code&gt; 状态了, 可以直接去使用资源.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正确性: 这种方法是正确的, 因为所有状态变量的修改都在一个单一的临界区内 (释放者的临界区) 完成. 这避免了多个被唤醒进程之间以及新来进程之间的竞争和状态不一致问题.&lt;/p&gt;
&lt;p&gt;b. 是否会出现新进程插队
在这段代码中, 新到达的进程不会插到已经等待的进程前面获得资源. 关键原因是释放者在唤醒等待者之前, 已经在同一个临界区内完成了 &lt;code&gt;waiting -= n&lt;/code&gt;, &lt;code&gt;active = n&lt;/code&gt;, &lt;code&gt;must_wait = (active == 3)&lt;/code&gt; 这些状态更新.&lt;/p&gt;
&lt;p&gt;如果等待者不少于3个, &lt;code&gt;active&lt;/code&gt; 会被预先设为3, &lt;code&gt;must_wait&lt;/code&gt; 保持为 &lt;code&gt;true&lt;/code&gt;. 之后即使新进程先抢到 &lt;code&gt;mutex&lt;/code&gt;, 它也会看到 &lt;code&gt;must_wait == true&lt;/code&gt; 并进入 &lt;code&gt;P(block)&lt;/code&gt; 等待. 如果等待者少于3个, 释放者会把全部等待者都预留进下一批, 此时已经没有旧等待者可被插队.&lt;/p&gt;
&lt;p&gt;c. 解释 “I’ll Do It for You” 模式
该模式的核心是, 当一个进程需要等待某个条件满足时, 它不主动在唤醒后重新检查条件或修改状态. 它仅仅是作为一个被动的等待者. 而负责满足这个条件的进程 (通常是释放资源的进程) , 会主动地、代替那些等待者完成所有的状态转换. 就像一个秘书, 把所有事情都安排好了, 然后通知老板们：“你们现在可以开始工作了”, 老板们 (被唤醒的进程) 无需再关心任何准备工作.&lt;/p&gt;
&lt;h3 id=&quot;94-正确解法二-pass-the-baton-模式&quot;&gt;9.4 正确解法二: “Pass the Baton” 模式&lt;a class=&quot;anchor&quot; href=&quot;#94-正确解法二-pass-the-baton-模式&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;程序代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;semaphore mutex = 1, block = 0;
int active = 0, waiting = 0;
bool must_wait = false;

void Acquire() {
    // 申请资源 (Acquire)
    P(mutex);
    if (must_wait) {
        waiting++;
        V(mutex);
        P(block); // 等待接力棒, 第四个进程开始需要等候, 因为 block 初始为 0
        waiting--;
        // 唤醒后, 我获得了mutex的所有权 (接力棒)
    }
    active++;
    must_wait = (active == 3);
    if (waiting &amp;gt; 0 &amp;amp;&amp;amp; !must_wait) {
        V(block); // 传递接力棒
    } else {
        V(mutex); // 释放锁
    }
}

void Release() {
    // 释放资源 (Release)
    P(mutex);
    active--;
    if (active == 0) {
        must_wait = false;
    }
    if (waiting &amp;gt; 0 &amp;amp;&amp;amp; !must_wait) {
        V(block); // 开始传递接力棒
    } else {
        V(mutex); // 释放锁
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;a. 解释工作方式和正确性
该模式被称为 “Pass the Baton” (传递接力棒).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;核心思想: 互斥锁 &lt;code&gt;mutex&lt;/code&gt; 在被唤醒的进程间直接传递, 就像接力赛中的”接力棒”. 一个进程完成自己的临界区后, 不直接释放锁给公众, 而是直接将”锁的所有权”传递给下一个需要进入的、正在等待的进程.&lt;/li&gt;
&lt;li&gt;工作流程:
&lt;ol&gt;
&lt;li&gt;申请: 如果需要等待 (&lt;code&gt;must_wait&lt;/code&gt; 为 &lt;code&gt;true&lt;/code&gt;), 进程释放 &lt;code&gt;mutex&lt;/code&gt; 并 &lt;code&gt;P(block)&lt;/code&gt; 等待.&lt;/li&gt;
&lt;li&gt;释放/传递: 当一个进程 (无论是释放者还是刚进入的申请者) 完成操作后, 它会检查是否还有进程在等待 (&lt;code&gt;waiting &amp;gt; 0&lt;/code&gt;) 并且进入条件满足 (&lt;code&gt;!must_wait&lt;/code&gt;). 如果满足, 它不执行 &lt;code&gt;V(mutex)&lt;/code&gt;, 而是执行 &lt;code&gt;V(block)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;接力棒传递: &lt;code&gt;V(block)&lt;/code&gt; 唤醒一个在 &lt;code&gt;P(block)&lt;/code&gt; 处等待的进程. 这个被唤醒的进程在返回时, 它就拥有了互斥锁 &lt;code&gt;mutex&lt;/code&gt; 的所有权, 因为它没有通过 &lt;code&gt;P(mutex)&lt;/code&gt; 来获得, 而是由上一个进程”递给”它的. 然后它继续执行临界区代码 (&lt;code&gt;active++&lt;/code&gt; 等) .&lt;/li&gt;
&lt;li&gt;最后一人: 当最后一个需要被唤醒的进程完成操作后, 它检查条件, 发现不再需要唤醒别人了, 于是它执行 &lt;code&gt;V(mutex)&lt;/code&gt;, 将锁”放回”架子上, 供所有新来的进程竞争.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正确性: 这种方式保证了被唤醒的进程可以立即执行, 无需再次竞争 &lt;code&gt;mutex&lt;/code&gt;, 从而避免了”唤醒后发现条件又变了”的问题. 它形成了一个”唤醒链”, 效率很高.&lt;/p&gt;
&lt;p&gt;b. 与上一解法在唤醒进程个数上的不同&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“I’ll Do It for You”: 是一次性唤醒一批进程. 释放者在一个循环里执行 &lt;code&gt;V(block)&lt;/code&gt; &lt;code&gt;n&lt;/code&gt; 次, 同时唤醒 &lt;code&gt;n&lt;/code&gt; 个进程. 这 &lt;code&gt;n&lt;/code&gt; 个进程随后会并发执行.&lt;/li&gt;
&lt;li&gt;“Pass the Baton”: 是一次只唤醒一个进程. 一个进程 &lt;code&gt;V(block)&lt;/code&gt; 唤醒下一个, 下一个再唤醒下下一个, 形成一个串行的唤醒链. 每次只有一个进程在临界区内活动.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;c. 解释 “Pass the Baton” 模式
这个模式的关键在于将锁的所有权和唤醒信号绑定. 执行 &lt;code&gt;V(block)&lt;/code&gt; 的进程不仅是在唤醒一个等待者, 更是在对它说：“我把临界区的钥匙 (mutex) 现在直接交给你了, 你继续”. 这避免了被唤醒的进程再去和外面那些新来的进程一起竞争锁, 保证了等待队列的公平性和执行的有序性.&lt;/p&gt;
&lt;h3 id=&quot;95-使用管程的解法&quot;&gt;9.5 使用管程的解法&lt;a class=&quot;anchor&quot; href=&quot;#95-使用管程的解法&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;管程过程自动互斥, 所以 &lt;code&gt;active_count&lt;/code&gt; 和 &lt;code&gt;must_wait&lt;/code&gt; 都在管程锁保护下访问.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;管程设计&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;共享变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;active_count&lt;/code&gt;: 当前正在使用资源的进程数.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;must_wait&lt;/code&gt;: 一个布尔标志, 当资源满员后设置为 &lt;code&gt;true&lt;/code&gt;, 阻止新进程进入, 直到下一批开始.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;条件变量:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;next_batch_q&lt;/code&gt;: 等待下一批的进程在此睡眠.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管程过程: &lt;code&gt;Acquire()&lt;/code&gt; 和 &lt;code&gt;Release()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;伪代码实现&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;class ResourceManager {
    int active_count = 0;
    bool must_wait = false;
    condition next_batch_q;

    // 申请资源
    public void Acquire() {
        // 如果上一批已满员, 则必须等待
        while (must_wait) {
            wait(next_batch_q);
        }

        active_count++;
        // 如果我是本批的第3个, 则关上大门
        if (active_count == 3) {
            must_wait = true;
        }
    }

    // 释放资源
    public void Release() {
        active_count--;
        // 如果我是本批最后一个释放资源的
        if (active_count == 0) {
            // 打开大门, 并通知所有等待者
            must_wait = false;
            broadcast(next_batch_q);
        }
    }
}

// 进程代码
Process_i() {
    while(true) {
        ResourceManager.Acquire();
        // --- 使用资源 ---
        ResourceManager.Release();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;说明&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;批次控制: &lt;code&gt;must_wait&lt;/code&gt; 标志是实现”批处理”的关键. 当第三个进程获得资源时, 它将 &lt;code&gt;must_wait&lt;/code&gt; 设置为 &lt;code&gt;true&lt;/code&gt;, 这就像关上了一扇大门, 所有后续到来的进程都会在 &lt;code&gt;wait(next_batch_q)&lt;/code&gt; 处被阻塞.&lt;/li&gt;
&lt;li&gt;批次切换: 只有当 &lt;code&gt;active_count&lt;/code&gt; 降为0时 (即上一批的所有3个进程都已释放资源), 最后一个离开的进程才会将 &lt;code&gt;must_wait&lt;/code&gt; 设回 &lt;code&gt;false&lt;/code&gt; 并 &lt;code&gt;broadcast&lt;/code&gt; 唤醒所有等待的进程.&lt;/li&gt;
&lt;li&gt;条件重查: 被 &lt;code&gt;broadcast&lt;/code&gt; 唤醒的进程会重新检查 &lt;code&gt;while (must_wait)&lt;/code&gt;. 虽然所有等待者都被唤醒, 但只有前3个能够成功通过循环并获得资源. 第4个及以后的进程会发现 &lt;code&gt;active_count&lt;/code&gt; 又变成了3, &lt;code&gt;must_wait&lt;/code&gt; 再次变为 &lt;code&gt;true&lt;/code&gt;, 于是它们会继续 &lt;code&gt;wait&lt;/code&gt;, 等待再下一批.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 09: Concurrency Mechanism</title><link>https://www.lyt0112.com/blog/operating_systems_note_09-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_09-zh</guid><description>Operating Systems Notes 09: Concurrency Mechanism</description><pubDate>Fri, 06 Jun 2025 15:38:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-并发的特征与挑战&quot;&gt;1. 并发的特征与挑战&lt;a class=&quot;anchor&quot; href=&quot;#1-并发的特征与挑战&quot;&gt;#&lt;/a&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;不确定性: 由于执行速度的不可预测性，程序的执行结果可能是不确定的，这会导致与时间有关的错误。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;11-案例分析与时间有关的错误&quot;&gt;1.1. 案例分析：与时间有关的错误&lt;a class=&quot;anchor&quot; href=&quot;#11-案例分析与时间有关的错误&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;111-银行账户取款问题&quot;&gt;1.1.1. 银行账户取款问题&lt;a class=&quot;anchor&quot; href=&quot;#111-银行账户取款问题&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;假设一个银行账户有5000元，两个ATM机 (T1和T2) 同时对该账户进行取款操作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;T1: 取款1000元&lt;/li&gt;
&lt;li&gt;T2: 取款2000元&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个可能的错误执行序列如下：&lt;/p&gt;






















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;步骤&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;ATM T1 操作&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;ATM T2 操作&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;账户余额 (x)&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;read(x)&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;5000&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;T1 读取余额 5000&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;2&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;read(x)&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;5000&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;T2 也读取余额 5000&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;3&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;x := x - 1000&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;4000&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;T1 计算新余额&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;4&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;write(x)&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;4000&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;T1 将 4000 写回&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;5&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;x := x - 2000&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;3000&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;T2 基于它旧的读取值计算&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;6&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;write(x)&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;3000&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;T2 将 3000 写回&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;最终结果: 账户余额为3000元，但正确结果应为 5000 - 1000 - 2000 = 2000元。银行损失了1000元。这个问题的根源在于T1和T2的操作发生了交叉，导致T2的操作覆盖了T1的操作结果。&lt;/p&gt;
&lt;h4 id=&quot;112-共享计数器问题-c代码及汇编&quot;&gt;1.1.2. 共享计数器问题 (C代码及汇编)&lt;a class=&quot;anchor&quot; href=&quot;#112-共享计数器问题-c代码及汇编&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;一个常见的并发问题是多个线程同时对一个共享变量进行自增操作。&lt;/p&gt;
&lt;p&gt;C 代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;for (i = 0; i &amp;lt; niters; i++)
    cnt++;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;对应的汇编代码 (以一个线程为例):&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;L3:
    movq cnt(%rip), %rdx   ; (Li) Load: 将 cnt 的值加载到寄存器 %rdx
    addq $1, %rdx          ; (Ui) Update: 将寄存器 %rdx 的值加 1
    movq %rdx, cnt(%rip)   ; (Si) Store: 将寄存器 %rdx 的值写回内存中的 cnt
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;L&lt;/code&gt;, &lt;code&gt;U&lt;/code&gt;, &lt;code&gt;S&lt;/code&gt; 这三个步骤合起来才能完成一次 &lt;code&gt;cnt++&lt;/code&gt;。在并发环境下，这三个步骤并非原子操作 (不可分割) ，可能被中断。&lt;/p&gt;
&lt;p&gt;错误的执行交错:
假设 &lt;code&gt;cnt&lt;/code&gt; 初始值为 0，两个线程都想执行 &lt;code&gt;cnt++&lt;/code&gt;。&lt;/p&gt;





























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;线程&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;指令&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;%rdx1&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;%rdx2&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;cnt&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;L1&lt;/code&gt; (Load)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;-&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;线程1加载 cnt=0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;2&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;L2&lt;/code&gt; (Load)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;线程2也加载 cnt=0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;U1&lt;/code&gt; (Update)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;线程1计算结果为 1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;2&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;U2&lt;/code&gt; (Update)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;线程2计算结果也为 1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;S1&lt;/code&gt; (Store)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;线程1将 1 写回&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;2&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;S2&lt;/code&gt; (Store)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;线程2将 1 写回&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;最终结果: &lt;code&gt;cnt&lt;/code&gt; 的值为1，而不是预期的2。这就是竞争条件 (Race Condition) 的典型例子。&lt;/p&gt;
&lt;p&gt;竞争条件 (Race Condition) 定义: 两个或多个进程/线程读写某些共享数据，而最终的结果取决于它们运⾏的精确时序。&lt;/p&gt;
&lt;h4 id=&quot;113-线程参数传递问题&quot;&gt;1.1.3. 线程参数传递问题&lt;a class=&quot;anchor&quot; href=&quot;#113-线程参数传递问题&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;在循环中创建线程并传递循环变量的地址是一个经典的错误。&lt;/p&gt;
&lt;p&gt;错误代码示例:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// Main Thread
void *thread(void *vargp);

int main() {
    pthread_t tid;
    int i;
    for (i = 0; i &amp;lt; 100; i++) {
        // 传递 i 的地址
        pthread_create(&amp;amp;tid, NULL, thread, &amp;amp;i);
    }
    ...
}

// Peer Thread
void *thread(void *vargp) {
    // 解引用指针获取值
    int i = *((int *)vargp);
    ...
    // 保存 i 的值用于后续分析
    save_value(i);
    return NULL;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;问题分析:
所有新创建的线程都共享同一个变量 &lt;code&gt;i&lt;/code&gt; 的内存地址。主线程的&lt;code&gt;for&lt;/code&gt;循环可能很快就执行完毕，&lt;code&gt;i&lt;/code&gt; 的值变成了100。而新创建的线程在这之后才开始执行，当它们去解引用&lt;code&gt;vargp&lt;/code&gt;时，访问到的都是主线程栈上那个已经变成100 (或某个中间值) 的&lt;code&gt;i&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;实验结果: 实验表明，在多核服务器上，很多线程获取到的 &lt;code&gt;i&lt;/code&gt; 值是重复的，甚至是混乱的，很少有线程能拿到从0到99的唯一值。这证明了竞争条件确实发生了。&lt;/p&gt;
&lt;p&gt;正确做法:
应该为每个线程传递一个独一无二的内存地址。通常的做法是在主线程中为每个线程动态分配内存 (&lt;code&gt;malloc&lt;/code&gt;) ，或者传递值本身 (如果值的大小不超过&lt;code&gt;void*&lt;/code&gt;) 。&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// 修正后的主线程循环
for (i = 0; i &amp;lt; 100; i++) {
    int *arg = malloc(sizeof(int));
    *arg = i;
    pthread_create(&amp;amp;tid, NULL, thread, arg);
}

// 修正后的子线程
void *thread(void *vargp) {
    int i = *((int *)vargp);
    free(vargp); // 释放内存
    ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;2-进程线程间的制约关系&quot;&gt;2. 进程/线程间的制约关系&lt;a class=&quot;anchor&quot; href=&quot;#2-进程线程间的制约关系&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在并发环境中，进程/线程之间主要存在两种制约关系。&lt;/p&gt;
&lt;h3 id=&quot;21-互斥-mutual-exclusion&quot;&gt;2.1. 互斥 (Mutual Exclusion)&lt;a class=&quot;anchor&quot; href=&quot;#21-互斥-mutual-exclusion&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;原因: 多个进程/线程需要排他性地使用某个共享资源 (如打印机、共享变量) 。&lt;/li&gt;
&lt;li&gt;定义: 当一个进程在使用共享资源时，其他试图使用该资源的进程必须等待。&lt;/li&gt;
&lt;li&gt;临界资源 (Critical Resource): 一次只允许一个进程/线程访问的资源。&lt;/li&gt;
&lt;li&gt;临界区 (Critical Section): 程序中访问临界资源的代码片段。&lt;/li&gt;
&lt;/ul&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;让权等待: 进程在等待进入临界区时，应放弃CPU，进入阻塞状态，避免”忙等待”。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;22-同步-synchronization&quot;&gt;2.2. 同步 (Synchronization)&lt;a class=&quot;anchor&quot; href=&quot;#22-同步-synchronization&quot;&gt;#&lt;/a&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;p&gt;用前驱图表示同步关系:
前驱图是一个有向无环图，用于描述进程间的执行顺序。箭头 &lt;code&gt;p1 -&amp;gt; p2&lt;/code&gt; 表示 &lt;code&gt;p1&lt;/code&gt; 必须在 &lt;code&gt;p2&lt;/code&gt; 之前完成。&lt;/p&gt;
&lt;p&gt;前驱图 (Precedence Graph)&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    S --&amp;gt; p1
    p1 --&amp;gt; p2
    p1 --&amp;gt; p5
    p2 --&amp;gt; p3
    p3 --&amp;gt; p4
    p4 --&amp;gt; p5
    p5 --&amp;gt; p6
    p3 --&amp;gt; p7
    p7 --&amp;gt; p6
    p6 --&amp;gt; p8
    p8 --&amp;gt; F&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-解决互斥问题的方案&quot;&gt;3. 解决互斥问题的方案&lt;a class=&quot;anchor&quot; href=&quot;#3-解决互斥问题的方案&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-软件方案&quot;&gt;3.1. 软件方案&lt;a class=&quot;anchor&quot; href=&quot;#31-软件方案&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;软件方案通过算法逻辑来确保互斥，但通常较为复杂且效率不高。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解法1 (单一锁变量法): 使用一个&lt;code&gt;bool locked&lt;/code&gt;变量。
&lt;ul&gt;
&lt;li&gt;问题: 在&lt;code&gt;while(locked);&lt;/code&gt;和&lt;code&gt;locked=true;&lt;/code&gt;之间可能发生上下文切换，两个进程都可能通过&lt;code&gt;while&lt;/code&gt;检查，然后都进入临界区。违反互斥。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;解法2 (轮转法): 使用一个&lt;code&gt;turn&lt;/code&gt;变量，&lt;code&gt;turn=true&lt;/code&gt;允许P进，&lt;code&gt;turn=false&lt;/code&gt;允许Q进。
&lt;ul&gt;
&lt;li&gt;问题: 必须严格轮流进入临界区。如果P不想进入，但&lt;code&gt;turn&lt;/code&gt;标志归它，Q也无法进入。违反”有空让进”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;解法3 (双标志法): 每个进程有自己的&lt;code&gt;pturn&lt;/code&gt;, &lt;code&gt;qturn&lt;/code&gt;标志，表示自己想进入。
&lt;ul&gt;
&lt;li&gt;问题: 两个进程可能同时设置自己的标志为&lt;code&gt;true&lt;/code&gt;，然后都在&lt;code&gt;while&lt;/code&gt;循环中等待对方，导致死锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dekker算法 (1965): 结合解法2和3，引入&lt;code&gt;turn&lt;/code&gt;变量来解决死锁。是第一个被证明正确的纯软件解法，但实现复杂。&lt;/li&gt;
&lt;li&gt;Peterson算法 (1981): 一个更简洁优美的软件解法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Peterson 算法详解&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;#define N 2 // 进程数量
int turn; // 冲突时由谁等待
int interested[N]; // 兴趣数组，初始为FALSE

void enter_region(int process) {
    int other = 1 - process; // 另一个进程

    interested[process] = TRUE; // 1. 表示自己感兴趣
    turn = process;             // 2. 冲突时让自己等待，把机会留给对方

    // 3. 当对方感兴趣且自己应等待时，循环等待
    while (turn == process &amp;amp;&amp;amp; interested[other] == TRUE) {
        // busy waiting
    }
}

void leave_region(int process) {
    interested[process] = FALSE; // 离开临界区，不再感兴趣
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;工作原理:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;互斥: 如果两个进程同时调用&lt;code&gt;enter_region&lt;/code&gt;，它们都会设置自己的&lt;code&gt;interested&lt;/code&gt;为&lt;code&gt;TRUE&lt;/code&gt;。但&lt;code&gt;turn&lt;/code&gt;变量的写入是原子性的，后写入的会覆盖先写入的。假设进程1后写，则&lt;code&gt;turn=1&lt;/code&gt;。此时进程0在&lt;code&gt;while&lt;/code&gt;中会发现&lt;code&gt;turn==0 &amp;amp;&amp;amp; interested[1]==TRUE&lt;/code&gt;不成立，跳出循环进入临界区。而进程1会发现&lt;code&gt;turn==1 &amp;amp;&amp;amp; interested[0]==TRUE&lt;/code&gt;成立，陷入等待。也就是说，冲突时后写&lt;code&gt;turn&lt;/code&gt;的一方等待，另一方进入临界区。&lt;/li&gt;
&lt;li&gt;有空让进: 如果进程1不感兴趣 (&lt;code&gt;interested[1]==FALSE&lt;/code&gt;)，进程0的&lt;code&gt;while&lt;/code&gt;条件不成立，直接进入。&lt;/li&gt;
&lt;li&gt;有限等待: 不会死锁。即使一个进程在&lt;code&gt;while&lt;/code&gt;中等待，一旦另一个进程离开临界区 (设置&lt;code&gt;interested&lt;/code&gt;为&lt;code&gt;FALSE&lt;/code&gt;) ，等待的进程就能立即进入。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;32-硬件方案&quot;&gt;3.2. 硬件方案&lt;a class=&quot;anchor&quot; href=&quot;#32-硬件方案&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;硬件方案利用特殊的CPU指令来提供原子操作，实现起来更简单高效。&lt;/p&gt;
&lt;h4 id=&quot;321-关闭中断-disable-interrupts&quot;&gt;3.2.1. 关闭中断 (Disable Interrupts)&lt;a class=&quot;anchor&quot; href=&quot;#321-关闭中断-disable-interrupts&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;while (true) {
    disable_interrupts();
    // critical section
    enable_interrupts();
    // remainder section
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;不适用于多处理器: 在多核CPU上，一个核心关闭中断并不能阻止其他核心并发访问共享内存。&lt;/li&gt;
&lt;li&gt;滥用风险: 如果把这个权力交给用户进程，一个恶意或有bug的程序可能会导致整个系统瘫痪。因此，它只适用于操作系统内核自身。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;思考题提问：单处理器下，关闭中断是否符合临界区的使用原则 (有空让进、无空等待、有限等待) ？
思考题解答：在单处理器系统中，关闭中断的方式符合”有空让进”和”无空等待”原则。因为只有一个处理器，关闭中断后不会被其他进程抢占，能保证互斥和不会出现空等。但它不一定符合”有限等待”原则：如果某个进程长时间占用临界区，其他进程会一直等待，甚至可能被饿死。此外，这种方式不符合”让权等待”原则，因为等待的进程无法主动让出CPU，整个系统调度被阻塞，属于一种特殊的忙等。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;322-测试并加锁-test-and-set-lock-tsl-指令&quot;&gt;3.2.2. 测试并加锁 (Test-and-Set Lock, TSL) 指令&lt;a class=&quot;anchor&quot; href=&quot;#322-测试并加锁-test-and-set-lock-tsl-指令&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;TSL是一条原子指令，它执行两个操作：读取一个内存位置 (锁变量) 的值到寄存器，然后将该内存位置设置为1。&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;enter_region:
    TSL REGISTER, LOCK   ; 原子操作: 将LOCK的值复制到REGISTER, 并将LOCK设为1
    CMP REGISTER, #0     ; 比较之前LOCK的值是否为0
    JNE enter_region     ; 如果不为0 (锁已被占用), 则循环等待
    RET                  ; 如果为0 (成功获取锁), 返回并进入临界区

leave_region:
    MOVE LOCK, #0        ; 将LOCK的值设为0, 释放锁
    RET&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;原理: &lt;code&gt;TSL&lt;/code&gt;指令的原子性保证了在检查锁和设置锁之间不会有上下文切换。&lt;/li&gt;
&lt;li&gt;忙等待 (Busy Waiting) / 自旋锁 (Spin Lock): 这种实现方式会让等待的进程在一个循环中持续消耗CPU时间，因此也称为自旋锁。&lt;/li&gt;
&lt;li&gt;优先级反转: 如果一个低优先级进程持有锁，而一个高优先级进程在自旋等待，就会发生优先级反转。高优先级进程无法执行，而低优先级进程可能得不到CPU时间来释放锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;思考题提问: TSL指令对多处理器系统有效吗？为什么？
思考题解答: TSL指令对多处理器系统有效。因为&lt;code&gt;TSL&lt;/code&gt;是原子读改写指令，硬件会通过总线锁定或缓存一致性协议保证同一个内存地址&lt;code&gt;LOCK&lt;/code&gt;上的操作跨核心原子执行。&lt;code&gt;XCHG&lt;/code&gt; (原子交换) 指令也能达到同样的效果。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;4-高级同步机制&quot;&gt;4. 高级同步机制&lt;a class=&quot;anchor&quot; href=&quot;#4-高级同步机制&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;软件和硬件方案都存在问题 (复杂性、忙等待等) 。为了解决这些问题，操作系统和编程语言提供了更高级、更易用的同步机制。&lt;/p&gt;
&lt;h3 id=&quot;41-信号量-semaphore-及-pv-操作&quot;&gt;4.1. 信号量 (Semaphore) 及 P/V 操作&lt;a class=&quot;anchor&quot; href=&quot;#41-信号量-semaphore-及-pv-操作&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;由Dijkstra在1965年提出，是一种功能强大且经典的同步工具。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义: 信号量是一个特殊的整数变量，只能通过两个原子操作来访问：&lt;code&gt;P&lt;/code&gt;和&lt;code&gt;V&lt;/code&gt;。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;P&lt;/code&gt; (Proberen, test): 检查并尝试减少信号量的值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V&lt;/code&gt; (Verhogen, increment): 增加信号量的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本课程定义的 P/V 操作:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;struct semaphore {
    int count;          // 计数值
    queueType queue;    // 等待队列
};

// P(s) 操作 - 申请资源
P(s) {
    s.count--;
    if (s.count &amp;lt; 0) {
        // 资源不足，阻塞当前进程
        add_to_queue(s.queue);
        block_process();
        reschedule();
    }
}

// V(s) 操作 - 释放资源
V(s) {
    s.count++;
    if (s.count &amp;lt;= 0) {
        // 有进程在等待，唤醒一个
        wakeup_process_from_queue(s.queue);
        move_to_ready_queue();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s.count&lt;/code&gt;的含义:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s.count &amp;gt;= 0&lt;/code&gt;: 表示可用资源的数量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s.count &amp;lt; 0&lt;/code&gt;: 其绝对值表示正在等待该资源的进程数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;P/V操作是原语 (Atomic Action): 它们的执行不可分割，通常由操作系统通过关闭中断或硬件指令来保证。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;问题解答: 信号量及PV操作既可以解决互斥问题，也可以解决同步问题。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解决互斥: 使用一个二元信号量 (也叫互斥锁 &lt;code&gt;mutex&lt;/code&gt;) ，初值为1。在临界区前后分别调用 &lt;code&gt;P(mutex)&lt;/code&gt; 和 &lt;code&gt;V(mutex)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;解决同步: 使用一个计数信号量，其初值表示可用资源的数量。例如，同步某个事件的发生，初值为0。等待事件的进程执行 &lt;code&gt;P&lt;/code&gt; 操作被阻塞，触发事件的进程执行 &lt;code&gt;V&lt;/code&gt; 操作来唤醒等待者。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;411-案例生产者-消费者问题&quot;&gt;4.1.1. 案例：生产者-消费者问题&lt;a class=&quot;anchor&quot; href=&quot;#411-案例生产者-消费者问题&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;这是同步问题的经典模型。&lt;/p&gt;
&lt;ul&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;li&gt;对缓冲区的访问是临界区，必须互斥。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;信号量解决方案:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;#define N 100 // 缓冲区大小
semaphore mutex = 1;      // 互斥信号量，保护缓冲区访问，初值为1
semaphore empty = N;      // 同步信号量，记录空闲缓冲区数量，初值为N
semaphore full = 0;       // 同步信号量，记录产品数量，初值为0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;生产者代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;void producer() {
    while (TRUE) {
        item = produce_item();
        P(&amp;amp;empty); // 1. 申请一个空缓冲区 (若无则等待)
        P(&amp;amp;mutex); // 2. 锁住缓冲区
        insert_item(item);
        V(&amp;amp;mutex); // 3. 解锁缓冲区
        V(&amp;amp;full);  // 4. 通知有一个产品可用
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;消费者代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;void consumer() {
    while (TRUE) {
        P(&amp;amp;full);  // 1. 申请一个产品 (若无则等待)
        P(&amp;amp;mutex); // 2. 锁住缓冲区
        item = remove_item();
        V(&amp;amp;mutex); // 3. 解锁缓冲区
        V(&amp;amp;empty); // 4. 通知有一个空缓冲区可用
        consume_item(item);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;思考题解答:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;颠倒两个P操作的顺序会怎样?
如果生产者代码变为 &lt;code&gt;P(&amp;amp;mutex); P(&amp;amp;empty);&lt;/code&gt;。当缓冲区满时 (&lt;code&gt;empty=0&lt;/code&gt;)，生产者会先成功执行&lt;code&gt;P(&amp;amp;mutex)&lt;/code&gt;获得互斥锁，然后执行&lt;code&gt;P(&amp;amp;empty)&lt;/code&gt;被阻塞。但因为它持有&lt;code&gt;mutex&lt;/code&gt;锁，消费者即使消费了产品想执行&lt;code&gt;V(&amp;amp;empty)&lt;/code&gt;，也无法进入临界区 (被&lt;code&gt;P(&amp;amp;mutex)&lt;/code&gt;阻塞) ，也就无法唤醒生产者。系统发生死锁。&lt;/li&gt;
&lt;li&gt;颠倒两个V操作的顺序会怎样?
&lt;code&gt;V(&amp;amp;full); V(&amp;amp;mutex);&lt;/code&gt; 颠倒为 &lt;code&gt;V(&amp;amp;mutex); V(&amp;amp;full);&lt;/code&gt;。这不会导致逻辑错误或死锁。释放互斥锁和通知产品可用是两个独立的操作，它们的顺序不影响正确性。但通常建议先&lt;code&gt;V(mutex)&lt;/code&gt;再&lt;code&gt;V(full)&lt;/code&gt;，这样可以尽快释放临界区，让其他进程进入，提高并发度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;412-案例第一类读者-写者问题&quot;&gt;4.1.2. 案例：第一类读者-写者问题&lt;a class=&quot;anchor&quot; href=&quot;#412-案例第一类读者-写者问题&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&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;li&gt;读者和写者不能同时访问。&lt;/li&gt;
&lt;/ol&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;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;semaphore w = 1;      // 控制写的互斥信号量
semaphore mutex = 1;  // 用于保护对 read_count 的修改
int read_count = 0;   // 记录当前正在读的读者数量&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;写者代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;void writer() {
    while (TRUE) {
        P(w); // 申请写权限
        // 写操作
        V(w); // 释放写权限
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;读者代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;void reader() {
    while (TRUE) {
        P(mutex); // 锁住 read_count
        read_count++;
        if (read_count == 1) { // 第一个读者
            P(w); // 阻止写者进入
        }
        V(mutex); // 解锁 read_count

        // 读操作

        P(mutex); // 锁住 read_count
        read_count--;
        if (read_count == 0) { // 最后一个读者
            V(w); // 允许写者进入
        }
        V(mutex); // 解锁 read_count
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;42-管程-monitor&quot;&gt;4.2. 管程 (Monitor)&lt;a class=&quot;anchor&quot; href=&quot;#42-管程-monitor&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;信号量虽然强大，但使用时容易出错 (比如P/V顺序写反导致死锁) 。管程是一种更高级的、语言层面的同步机制，旨在简化并发编程。&lt;/p&gt;
&lt;ul&gt;
&lt;li&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;/li&gt;
&lt;li&gt;核心特性:
&lt;ol&gt;
&lt;li&gt;自动互斥: 编译器保证在任何时刻，最多只有一个进程能在管程内部执行其过程。进程调用管程过程时，会自动获得互斥锁。&lt;/li&gt;
&lt;li&gt;条件变量 (Condition Variable): 用于解决同步问题。它不是一个值，而是一个等待队列。提供&lt;code&gt;wait&lt;/code&gt;和&lt;code&gt;signal&lt;/code&gt;操作。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;c.wait()&lt;/code&gt;: 调用此操作的进程会被阻塞，并释放管程的互斥锁，然后进入条件变量&lt;code&gt;c&lt;/code&gt;的等待队列。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c.signal()&lt;/code&gt;: 如果有进程在条件变量&lt;code&gt;c&lt;/code&gt;上等待，则唤醒其中一个。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;421-hoare-管程-vs-mesa-管程&quot;&gt;4.2.1. Hoare 管程 vs. Mesa 管程&lt;a class=&quot;anchor&quot; href=&quot;#421-hoare-管程-vs-mesa-管程&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;当一个在管程中的进程P执行 &lt;code&gt;c.signal()&lt;/code&gt; 唤醒了另一个等待的进程Q时，管程中就有了两个活跃进程P和Q。如何处理？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Hoare 管程 (P waits for Q):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;规则: P立刻挂起，Q马上执行。P进入一个”紧急等待队列”，优先级高于管程入口的等待队列。当Q执行完毕或再次&lt;code&gt;wait&lt;/code&gt;时，P才能恢复执行。&lt;/li&gt;
&lt;li&gt;优点: &lt;code&gt;signal&lt;/code&gt;之后，被唤醒的进程Q可以确定它等待的条件现在是满足的。可以用&lt;code&gt;if&lt;/code&gt;来判断条件。&lt;/li&gt;
&lt;li&gt;缺点: 实现复杂，需要多次上下文切换。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mesa 管程 (P continues, Q waits):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;规则: &lt;code&gt;signal&lt;/code&gt;操作 (在Mesa中称为&lt;code&gt;notify&lt;/code&gt;) 只是一个”提示”。P继续执行，Q从等待队列移到就绪队列，但不会立即执行。Q何时执行取决于调度器。&lt;/li&gt;
&lt;li&gt;优点: 实现简单，上下文切换少。&lt;/li&gt;
&lt;li&gt;缺点: 当Q最终被调度执行时，它等待的条件可能已经被其他进程改变了。因此，Q被唤醒后必须重新检查条件。这导致了著名的while循环范式。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hoare管程 - 生产者消费者&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;pascal&quot;&gt;&lt;pre&gt;&lt;code&gt;monitor ProducerConsumer
    condition full, empty;
    integer count;

    procedure insert(item: integer);
    begin
        // Hoare管程, signal后条件确定为真, 用if即可
        if count == N then wait(full);
        ...
        if count == 1 then signal(empty);
    end;
    ...
end;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Mesa管程 - 生产者消费者&lt;/p&gt;
&lt;p&gt;核心思想是，被唤醒的线程 (无论是生产者还是消费者) 必须用&lt;code&gt;while&lt;/code&gt;循环重新检查条件。这是因为从&lt;code&gt;c.notify&lt;/code&gt; (通知) 发生，到等待的线程被唤醒并重新获得管程锁的这段时间内，共享状态 (如缓冲区是满是空) 可能已经被其他线程改变了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;append&lt;/code&gt;和&lt;code&gt;take&lt;/code&gt;函数的实现如下：&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;// Producer Code
void append(char x) {
    // 在Mesa管程中，唤醒的线程必须重新检查条件，因为从notify()发生到线程重新获得管程锁的这段时间内，共享状态 (如缓冲区是满是空) 可能已经被其他线程改变了。
    // 这就是为什么必须使用while循环，而不是if语句。
    while (count == N) {
        c.wait(notfull); // 如果缓冲区满了，则等待
    }

    // 添加项目到缓冲区
    buffer[nextin] = x;
    nextin = (nextin + 1) % N;
    count++;

    c.notify(notempty);  // 通知任何等待的消费者。
}

// Consumer Code
// 注意：函数签名从 `take(char x)` 改为 `take(char *x)`
// 为了通过输出参数返回一个值，使其在C中有效。
void take(char *x) {
    while (count == 0) {
        c.wait(notempty); // 如果缓冲区为空，则等待
    }

    // 从缓冲区中移除项目
    *x = buffer[nextout];
    nextout = (nextout + 1) % N;
    count--;

    c.notify(notfull);    // 通知任何等待的生产者。
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Mesa管程的改进:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;broadcast&lt;/code&gt;: 唤醒在某个条件变量上等待的所有进程。当不确定哪个进程能满足条件时有用。&lt;/li&gt;
&lt;li&gt;超时 &lt;code&gt;wait&lt;/code&gt;: &lt;code&gt;wait&lt;/code&gt;操作可以带一个超时参数，让线程在等待过久后重新检查状态或执行恢复逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;422-java-中的管程&quot;&gt;4.2.2. Java 中的管程&lt;a class=&quot;anchor&quot; href=&quot;#422-java-中的管程&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Java的 &lt;code&gt;synchronized&lt;/code&gt; 关键字和 &lt;code&gt;Object&lt;/code&gt; 类的 &lt;code&gt;wait()&lt;/code&gt;, &lt;code&gt;notify()&lt;/code&gt;, &lt;code&gt;notifyAll()&lt;/code&gt; 方法是Mesa管程模型的一种实现。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;synchronized&lt;/code&gt;: 任何对象都可以作为锁。当一个线程进入一个对象的&lt;code&gt;synchronized&lt;/code&gt;方法或代码块时，它就获得了该对象的锁。这实现了自动互斥。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wait()&lt;/code&gt;, &lt;code&gt;notify()&lt;/code&gt;, &lt;code&gt;notifyAll()&lt;/code&gt;: 相当于Mesa管程的 &lt;code&gt;cwait&lt;/code&gt;, &lt;code&gt;cnotify&lt;/code&gt;, &lt;code&gt;cbroadcast&lt;/code&gt;。它们必须在&lt;code&gt;synchronized&lt;/code&gt;块中调用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Java 实现生产者消费者:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;pre&gt;&lt;code&gt;// our_monitor 类的 insert 方法
public synchronized void insert(int val) {
    // 必须使用 while 循环检查条件
    while (count == N) {
        try {
            wait(); // 释放锁并等待
        } catch (InterruptedException e) {}
    }
    // ... 插入数据 ...
    count++;
    // 唤醒一个等待的消费者
    notifyAll(); // 唤醒所有等待线程，避免notify唤醒到条件不匹配的线程
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;43-pthreads-同步机制&quot;&gt;4.3. Pthreads 同步机制&lt;a class=&quot;anchor&quot; href=&quot;#43-pthreads-同步机制&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pthreads (POSIX Threads) 是一套标准的线程API，它提供了两种主要的同步工具：互斥锁和条件变量。这套API接近Mesa管程模型。&lt;/p&gt;
&lt;h4 id=&quot;431-互斥锁-mutex&quot;&gt;4.3.1. 互斥锁 (Mutex)&lt;a class=&quot;anchor&quot; href=&quot;#431-互斥锁-mutex&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pthread_mutex_t&lt;/code&gt;: 互斥锁变量类型。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_mutex_init()&lt;/code&gt;: 初始化互斥锁。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_mutex_lock()&lt;/code&gt;: 加锁。如果锁已被占用，线程阻塞。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_mutex_unlock()&lt;/code&gt;: 解锁。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_mutex_destroy()&lt;/code&gt;: 销毁互斥锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;432-条件变量-condition-variable&quot;&gt;4.3.2. 条件变量 (Condition Variable)&lt;a class=&quot;anchor&quot; href=&quot;#432-条件变量-condition-variable&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pthread_cond_t&lt;/code&gt;: 条件变量类型。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_cond_init()&lt;/code&gt;: 初始化条件变量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_cond_wait(cond, mutex)&lt;/code&gt;:
&lt;ol&gt;
&lt;li&gt;原子地解锁 &lt;code&gt;mutex&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;使线程阻塞在 &lt;code&gt;cond&lt;/code&gt; 上。&lt;/li&gt;
&lt;li&gt;当被唤醒后，重新自动地锁上 &lt;code&gt;mutex&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_cond_signal(cond)&lt;/code&gt;: 唤醒至少一个在&lt;code&gt;cond&lt;/code&gt;上等待的线程 (类似&lt;code&gt;notify&lt;/code&gt;) 。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_cond_broadcast(cond)&lt;/code&gt;: 唤醒所有在&lt;code&gt;cond&lt;/code&gt;上等待的线程 (类似&lt;code&gt;broadcast&lt;/code&gt;) 。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_cond_destroy()&lt;/code&gt;: 销毁条件变量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;433-pthreads-解决生产者-消费者问题&quot;&gt;4.3.3. Pthreads 解决生产者-消费者问题&lt;a class=&quot;anchor&quot; href=&quot;#433-pthreads-解决生产者-消费者问题&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;代码框架:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;pthread_mutex_t the_mutex;
pthread_cond_t condc, condp; // c:消费者等待条件, p:生产者等待条件
int buffer = 0;

void *producer(void *ptr) {
    for (...) {
        pthread_mutex_lock(&amp;amp;the_mutex); // 加锁
        // 缓冲区不为空(已被填充), 生产者等待
        while (buffer != 0) {
            pthread_cond_wait(&amp;amp;condp, &amp;amp;the_mutex);
        }
        buffer = i; // 生产
        pthread_cond_signal(&amp;amp;condc); // 唤醒消费者
        pthread_mutex_unlock(&amp;amp;the_mutex); // 解锁
    }
}

void *consumer(void *ptr) {
    for (...) {
        pthread_mutex_lock(&amp;amp;the_mutex); // 加锁
        // 缓冲区为空, 消费者等待
        while (buffer == 0) {
            pthread_cond_wait(&amp;amp;condc, &amp;amp;the_mutex);
        }
        buffer = 0; // 消费
        pthread_cond_signal(&amp;amp;condp); // 唤醒生产者
        pthread_mutex_unlock(&amp;amp;the_mutex); // 解锁
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;重点讨论: 为什么 &lt;code&gt;pthread_cond_wait&lt;/code&gt; 必须用 &lt;code&gt;while&lt;/code&gt; 循环包裹？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;虚假唤醒 (Spurious Wakeup): 在某些系统实现中，线程可能在没有&lt;code&gt;signal&lt;/code&gt;或&lt;code&gt;broadcast&lt;/code&gt;的情况下被唤醒。&lt;/li&gt;
&lt;li&gt;Mesa模型特性: 正如之前讨论的，&lt;code&gt;signal&lt;/code&gt;只是一个提示。从线程被唤醒到它实际运行并重新获得锁之间，它等待的条件可能已经被其他线程改变了。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果使用 &lt;code&gt;if&lt;/code&gt;，线程被唤醒后会盲目地继续执行，可能会操作一个不满足条件的共享状态，导致错误。&lt;code&gt;while&lt;/code&gt;循环确保线程在继续执行前，必须重新验证条件是否为真。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;5-锁的实现与现代硬件&quot;&gt;5. 锁的实现与现代硬件&lt;a class=&quot;anchor&quot; href=&quot;#5-锁的实现与现代硬件&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;51-锁-mutex-的实现&quot;&gt;5.1. 锁 (Mutex) 的实现&lt;a class=&quot;anchor&quot; href=&quot;#51-锁-mutex-的实现&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;锁是实现其他高级同步机制的基础。&lt;/p&gt;
&lt;h4 id=&quot;511-基于开关中断的实现&quot;&gt;5.1.1. 基于开关中断的实现&lt;a class=&quot;anchor&quot; href=&quot;#511-基于开关中断的实现&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;这种方法有严重缺陷。
错误实现:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;lock() {
    disable_interrupts();
    while (value != FREE); // 忙等待，且中断被关闭
    value = BUSY;
    enable_interrupts();
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;问题: 如果锁被占用，&lt;code&gt;while&lt;/code&gt;循环会一直执行，而中断是关闭的，持有锁的线程永远无法被调度来释放锁，导致系统死锁。&lt;/p&gt;
&lt;p&gt;稍好的实现:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;lock() {
    disable_interrupts();
    while (value != FREE) {
        enable_interrupts();  // 循环时临时开中断
        // some delay...
        disable_interrupts(); // 再次检查前关中断
    }
    value = BUSY;
    enable_interrupts();
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;问题: 在 &lt;code&gt;enable_interrupts()&lt;/code&gt; 和下一次 &lt;code&gt;disable_interrupts()&lt;/code&gt; 之间，可能会一直被新的 &lt;code&gt;lock&lt;/code&gt; 调用阻塞导致饥饿。&lt;/p&gt;
&lt;p&gt;正确的无忙等待实现 (OS内核级):&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;lock() {
    disable_interrupts();
    if (value == FREE) {
        value = BUSY;
    } else {
        // (1)
        add_thread_to_queue;
        // (2)
        switch_to_next_thread;
        // (3)
    }
    enable_interrupts();
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;思考题解答:&lt;/p&gt;
&lt;p&gt;这段代码展示了操作系统内核在获取锁失败时，如何让线程休眠等待。为防止竞态条件，从关中断到线程安全地休眠或成功获取锁的整个过程，其原子性至关重要。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在位置 (1) 开中断（入队前）：不可行
&lt;ul&gt;
&lt;li&gt;风险: “丢失唤醒” (Lost Wakeup)。如果在此处开启中断，持有锁的线程可能会立即运行并释放锁。&lt;code&gt;unlock&lt;/code&gt; 操作会检查等待队列，但因为当前线程还未入队，所以它不会被唤醒。当中断返回后，当前线程会继续执行，将自己加入一个空队列并进入休眠，从而永久错过了已经发生的唤醒信号。&lt;/li&gt;
&lt;li&gt;结论: 检查锁的状态和将线程加入等待队列必须合并为一个原子操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;在位置 (2) 开中断（休眠前）：不可行
&lt;ul&gt;
&lt;li&gt;风险: 同样是”丢失唤醒”。线程已在队列中，但尚未调用 &lt;code&gt;switch_to_next_thread&lt;/code&gt; 进入休眠状态。如果此时被唤醒（例如，状态被置为 &lt;code&gt;READY&lt;/code&gt;），它紧接着会继续执行 &lt;code&gt;switch_to_next_thread&lt;/code&gt;，该操作会将其状态置为 &lt;code&gt;BLOCKED&lt;/code&gt; 并让出CPU。结果是，线程刚被唤醒，马上又睡着了，导致唤醒操作被无效化。&lt;/li&gt;
&lt;li&gt;结论: 将线程加入队列和使其休眠也必须是一个原子操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;在位置 (3) 开中断（唤醒后）：可行且必要
&lt;ul&gt;
&lt;li&gt;状态: 线程执行到这里，意味着它已经被其他线程（通过 &lt;code&gt;unlock&lt;/code&gt;）唤醒，并且调度器已选择它重新运行。此时，该线程已经”获得”了锁的所有权，但CPU仍处于之前 &lt;code&gt;lock&lt;/code&gt; 操作时关闭中断的状态。&lt;/li&gt;
&lt;li&gt;操作: 在 &lt;code&gt;lock()&lt;/code&gt; 函数返回、进入临界区代码之前，必须重新开启中断，以保证系统的正常响应。代码中，执行路径会自然地落到最后的 &lt;code&gt;enable_interrupts()&lt;/code&gt;，这在逻辑上是完全正确的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;512-基于-tsl-指令的实现-带让出cpu&quot;&gt;5.1.2. 基于 TSL 指令的实现 (带让出CPU)&lt;a class=&quot;anchor&quot; href=&quot;#512-基于-tsl-指令的实现-带让出cpu&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;为了避免纯粹的自旋消耗CPU，可以在尝试获取锁失败后，主动让出CPU。&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;mutex_lock:
    TSL REGISTER, MUTEX   ; 尝试获取锁
    CMP REGISTER, #0      ; 检查之前是否锁着
    JZE ok                ; 成功获取，跳转到ok
    CALL thread_yield     ; 失败，调用线程调度，让出CPU
    JMP mutex_lock        ; 被唤醒后，重新尝试
ok:
    RET&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这种方式结合了硬件原子指令和操作系统的调度，比纯自旋锁要好，但仍然有尝试-失败的开销。&lt;/p&gt;
&lt;h3 id=&quot;52-现代并发机制拓展&quot;&gt;5.2. 现代并发机制拓展&lt;a class=&quot;anchor&quot; href=&quot;#52-现代并发机制拓展&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;521-futex-fast-userspace-mutex&quot;&gt;5.2.1. Futex (Fast Userspace Mutex)&lt;a class=&quot;anchor&quot; href=&quot;#521-futex-fast-userspace-mutex&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Futex是Linux中一种高效的锁机制，它结合了用户空间和内核空间的优点。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;快速路径 (Fast Path): 在无竞争的情况下，锁的获取和释放完全在用户空间通过原子操作完成，无需进入内核，开销极小。&lt;/li&gt;
&lt;li&gt;慢速路径 (Slow Path): 当发生锁竞争时，线程通过系统调用进入内核，将自己置于等待队列并睡眠，由内核负责唤醒。&lt;/li&gt;
&lt;li&gt;两阶段锁: 这种思想可以演变为两阶段锁：第一阶段在用户态自旋一小段时间 (期望锁很快被释放) ，如果失败，第二阶段再进入内核休眠。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;522-现代硬件支持&quot;&gt;5.2.2. 现代硬件支持&lt;a class=&quot;anchor&quot; href=&quot;#522-现代硬件支持&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;指令重排 (Instruction Reordering): 为了优化性能，CPU和编译器可能会改变指令的执行顺序。这在单线程下不影响结果，但在多线程下可能破坏程序员预期的同步逻辑。需要使用内存屏障 (Memory Fence/Barrier) 来阻止重排。&lt;/li&gt;
&lt;li&gt;更多原子指令:
&lt;ul&gt;
&lt;li&gt;CAS (Compare-and-Swap): &lt;code&gt;CAS(addr, old, new)&lt;/code&gt; 原子地检查 &lt;code&gt;*addr&lt;/code&gt; 是否等于 &lt;code&gt;old&lt;/code&gt;，如果是，就把它更新为 &lt;code&gt;new&lt;/code&gt;。这是实现无锁数据结构 (lock-free) 和乐观锁的基础。&lt;/li&gt;
&lt;li&gt;Fetch-and-Add: 原子地读取一个值并给它加上一个数。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSP (Communicating Sequential Processes) 模型: 与通过共享内存和锁进行同步不同，CSP模型强调进程之间通过显式的通信通道 (Channel) 来传递消息和同步。Go语言的&lt;code&gt;channel&lt;/code&gt;就是基于此模型，它能减少共享内存竞争条件，但通道使用不当仍然可能发生死锁。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 08: File System 2</title><link>https://www.lyt0112.com/blog/operating_systems_note_08-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_08-zh</guid><description>Operating Systems Notes 08: File System 2</description><pubDate>Sun, 18 May 2025 19:17:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-文件系统的管理&quot;&gt;1. 文件系统的管理&lt;a class=&quot;anchor&quot; href=&quot;#1-文件系统的管理&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;文件系统的管理主要涵盖三个方面：可靠性、一致性和安全性。&lt;/p&gt;
&lt;h3 id=&quot;11-文件系统的可靠性&quot;&gt;1.1 文件系统的可靠性&lt;a class=&quot;anchor&quot; href=&quot;#11-文件系统的可靠性&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;可靠性指的是文件系统抵御和预防各种物理性或人为性破坏的能力。&lt;/p&gt;
&lt;h4 id=&quot;111-坏块问题&quot;&gt;1.1.1 坏块问题&lt;a class=&quot;anchor&quot; href=&quot;#111-坏块问题&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;磁盘上可能会出现物理损坏的块，称为坏块。文件系统需要能够识别、标记并隔离这些坏块，避免将数据写入其中。&lt;/p&gt;
&lt;h4 id=&quot;112-备份-backup&quot;&gt;1.1.2 备份 (Backup)&lt;a class=&quot;anchor&quot; href=&quot;#112-备份-backup&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;备份是通过转储操作，为文件或整个文件系统创建多个副本，以便在发生数据丢失或损坏时进行恢复。主要有以下几种备份方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全量转储 (Full Dump): 定期将所有文件完整地拷贝到备份存储器（如磁带、另一块硬盘）上。这种方式最简单，但如果数据量大且变化少，会耗时且浪费存储空间。&lt;/li&gt;
&lt;li&gt;增量转储 (Incremental Dump): 只转储自上次备份以来被修改过的文件。这种方式可以显著减少备份所需的时间和空间开销。&lt;/li&gt;
&lt;li&gt;物理转储 (Physical Dump): 从磁盘的第一个物理块（0号块）开始，按顺序将所有磁盘块的内容原封不动地输出到备份介质。这种方式不关心文件系统的逻辑结构，备份和恢复速度快，但不够灵活。&lt;/li&gt;
&lt;li&gt;逻辑转储 (Logical Dump): 从一个或多个指定的目录开始，递归地遍历文件树，只转储那些自某个给定日期之后发生过修改的文件和目录。这种方式更灵活，可以支持更复杂的需求，例如恢复单个文件或目录。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;12-文件系统的一致性&quot;&gt;1.2 文件系统的一致性&lt;a class=&quot;anchor&quot; href=&quot;#12-文件系统的一致性&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;121-问题来源&quot;&gt;1.2.1 问题来源&lt;a class=&quot;anchor&quot; href=&quot;#121-问题来源&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;文件系统的操作通常不是原子性的。例如，更新一个文件可能需要修改数据块、i-node（索引节点）和空闲块列表。这些修改通常先在内存的缓冲区中进行，然后再一次性或分批写回磁盘。如果在数据完全写回磁盘之前系统发生崩溃（例如突然断电），磁盘上的文件系统状态就可能处于一种中间状态，导致不一致。&lt;/p&gt;
&lt;p&gt;例如，一个数据块可能既被一个文件引用，又同时存在于空闲块列表中。&lt;/p&gt;
&lt;h4 id=&quot;122-解决方案&quot;&gt;1.2.2 解决方案&lt;a class=&quot;anchor&quot; href=&quot;#122-解决方案&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;h5 id=&quot;1221-文件系统检查工具-fsck&quot;&gt;1.2.2.1 文件系统检查工具 (fsck)&lt;a class=&quot;anchor&quot; href=&quot;#1221-文件系统检查工具-fsck&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;为了解决不一致问题，可以设计一个实用程序（在UNIX/Linux中通常是 &lt;code&gt;fsck&lt;/code&gt;），在系统启动时运行，检查并修复文件系统的逻辑结构。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;UNIX &lt;code&gt;fsck&lt;/code&gt; 的工作过程示例:
&lt;code&gt;fsck&lt;/code&gt; 会使用两张表来辅助检查，表的条目数与磁盘块数相同。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;表1: 用于记录每个磁盘块在文件中被引用的次数。&lt;/li&gt;
&lt;li&gt;表2: 用于记录每个磁盘块在空闲块列表中出现的次数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;fsck&lt;/code&gt; 会遍历整个文件系统的元数据（如i-node和间接块），填充这两张表。理想情况下，对于任何一个磁盘块，它要么在表1中的计数为1，表2中的计数为0（已被分配）；要么在表1中的计数为0，表2中的计数为1（是空闲的）。&lt;/p&gt;
&lt;p&gt;下面是几种不一致的情况及其修复方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;情况A：一个块在空闲列表中，但未被任何文件使用。 这是正常状态。&lt;/li&gt;
&lt;li&gt;情况B：一个块在空闲列表中，同时也被文件使用（表1计数&amp;gt;0，表2计数&amp;gt;0）。 这是严重错误。&lt;code&gt;fsck&lt;/code&gt; 会将其从空闲列表中移除来解决。&lt;/li&gt;
&lt;li&gt;情况C：一个块既不在空闲列表，也未被任何文件使用（表1计数=0，表2计数=0）。 这会导致块资源泄露。&lt;code&gt;fsck&lt;/code&gt; 会将其添加到空闲列表中。&lt;/li&gt;
&lt;li&gt;情况D：一个块被两个或多个文件同时使用（表1计数&amp;gt;1）。 这也是严重错误。&lt;code&gt;fsck&lt;/code&gt; 通常会复制该块，让每个文件拥有自己的副本。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    A[开始检查] --&amp;gt; B{遍历所有i-node};
    B --&amp;gt; C{填充&amp;#39;文件引用计数表&amp;#39;};
    A --&amp;gt; D{扫描空闲块列表};
    D --&amp;gt; E{填充&amp;#39;空闲块计数表&amp;#39;};
    F{分析两张表};
    C --&amp;gt; F;
    E --&amp;gt; F;
    F --&amp;gt; G{不一致?};
    G -- 是 --&amp;gt; H[根据规则修复];
    G -- 否 --&amp;gt; I[结束];
    H --&amp;gt; I;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;1222-文件系统写入方式&quot;&gt;1.2.2.2 文件系统写入方式&lt;a class=&quot;anchor&quot; href=&quot;#1222-文件系统写入方式&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;为了在性能和一致性之间取得平衡，发展出了不同的写入策略。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通写 (Write-through): 内存缓冲区的修改立即写回磁盘。这种方式能最大程度保证数据一致性，但因为频繁的磁盘I/O，性能差。FAT文件系统是典型的例子。&lt;/li&gt;
&lt;li&gt;延迟写 (Lazy-write): 修改只在内存缓冲区中进行，由操作系统决定何时将这些”脏”的缓冲区（write-back cache）写回磁盘。这种方式性能很好，但如果系统在写回前崩溃，数据丢失的风险很高，可恢复性差。&lt;/li&gt;
&lt;li&gt;可恢复写 (Journaling / Transaction Log): 这是在安全性和性能之间的一个优秀折中。在对文件系统的元数据进行实际修改之前，先把要做的操作步骤（事务日志，Journal/Log）记录下来并写入磁盘。如果系统在修改过程中崩溃，重启后只需读取日志，就能重做（redo）未完成的操作，或撤销（undo）不完整的操作，从而快速恢复到一致状态。NTFS和现代Linux文件系统（如ext4）都采用了这种技术。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;13-文件系统的安全性&quot;&gt;1.3 文件系统的安全性&lt;a class=&quot;anchor&quot; href=&quot;#13-文件系统的安全性&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;安全性旨在确保数据不会丢失或被未经授权的用户访问。&lt;/p&gt;
&lt;h4 id=&quot;131-威胁来源&quot;&gt;1.3.1 威胁来源&lt;a class=&quot;anchor&quot; href=&quot;#131-威胁来源&quot;&gt;#&lt;/a&gt;&lt;/h4&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;132-文件保护机制&quot;&gt;1.3.2 文件保护机制&lt;a class=&quot;anchor&quot; href=&quot;#132-文件保护机制&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;h5 id=&quot;1321-用户身份验证&quot;&gt;1.3.2.1 用户身份验证&lt;a class=&quot;anchor&quot; href=&quot;#1321-用户身份验证&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;这是保护的第一道防线，用于在用户登录时验证其身份。常见方法包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户知道什么: 口令（Password）、密码。&lt;/li&gt;
&lt;li&gt;用户拥有什么: 磁卡、智能卡、USB Key。&lt;/li&gt;
&lt;li&gt;用户是什么 (生物特征): 指纹、虹膜、人脸识别、声纹等。&lt;/li&gt;
&lt;li&gt;CAPTCHA测试: 用于区分人类和计算机程序。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;1322-访问控制&quot;&gt;1.3.2.2 访问控制&lt;a class=&quot;anchor&quot; href=&quot;#1322-访问控制&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;当用户身份被确认后，系统需要决定该用户能对哪些文件进行哪些操作。主要有两种实现模型：&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;特性&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;访问控制表 (ACL - Access Control List)&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;能力表 (Capability List)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;核心思想&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;以 资源 (文件) 为中心&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;以 用户 (主体) 为中心&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;数据结构&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;每个文件附加一个列表，记录(用户, 权限)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;每个用户持有一个列表，记录(文件, 权限)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;查询方式&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;”谁能访问我？&amp;quot;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&amp;quot;我能访问谁？“&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;存放位置&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;内核空间&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;内核空间&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;常见场景&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;文件系统（如Unix权限、NTFS ACL）&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;能力系统、不可伪造 token；OAuth bearer token 有类似能力凭证的用法&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;思考：这两种访问控制方式的优缺点是什么？
&lt;ul&gt;
&lt;li&gt;ACL的优点：管理特定资源的权限直观和方便。很容易回答”谁能访问这个文件？”。&lt;/li&gt;
&lt;li&gt;ACL的缺点：当需要查看某个用户的所有权限时，必须遍历系统中所有文件的ACL，效率低下。权限的撤销也很直接（从ACL中移除即可）。&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;h5 id=&quot;1323-拓展访问控制模型&quot;&gt;1.3.2.3 拓展访问控制模型&lt;a class=&quot;anchor&quot; href=&quot;#1323-拓展访问控制模型&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;RBAC (Role-Based Access Control, 基于角色的访问控制): 在用户和权限之间引入”角色”层。权限被赋予角色，而用户被分配角色。当需要修改一组用户的权限时，只需修改角色的权限即可，大大简化了管理。例如，公司里的”财务”角色拥有访问财务报表的权限，“人事”角色拥有访问员工档案的权限。&lt;/li&gt;
&lt;li&gt;ABAC (Attribute-Based Access Control, 基于属性的访问控制): 这是最灵活的模型。访问决策基于用户、资源、环境等多种属性的组合。例如，可以制定一个规则：“‘医生’角色的用户，在’工作时间’内，从’医院内网’访问其’自己病人’的’病历’，可以被允许”。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;1324-unix的文件保护机制&quot;&gt;1.3.2.4 UNIX的文件保护机制&lt;a class=&quot;anchor&quot; href=&quot;#1324-unix的文件保护机制&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;UNIX采用了一种简化的、两级访问控制机制。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;第一级：对访问者的识别&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文件主 (owner): 创建文件的用户。&lt;/li&gt;
&lt;li&gt;文件主的同组用户 (group): 与文件主在同一个用户组的用户。&lt;/li&gt;
&lt;li&gt;其他用户 (other): 系统中的所有其他用户。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第二级：对操作权限的识别&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;读操作 (r): read&lt;/li&gt;
&lt;li&gt;写操作 (w): write&lt;/li&gt;
&lt;li&gt;执行操作 (x): execute&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两种分类组合成了经典的 &lt;code&gt;rwxrwxrwx&lt;/code&gt; 9-bit 权限模式。例如：&lt;code&gt;rwx r-x r--&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rwx&lt;/code&gt; (二进制111，八进制7): 文件主拥有读、写、执行权限。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;r-x&lt;/code&gt; (二进制101，八进制5): 同组用户拥有读、执行权限。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;r--&lt;/code&gt; (二进制100，八进制4): 其他用户仅拥有读权限。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;chmod 755 file&lt;/code&gt; 命令就是将文件的权限设置为 &lt;code&gt;rwxr-xr-x&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&quot;14-数据恢复技术&quot;&gt;1.4 数据恢复技术&lt;a class=&quot;anchor&quot; href=&quot;#14-数据恢复技术&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;数据恢复的原理在于，当文件被删除或分区被格式化时，存储介质上的数据通常没有被立即清除，只是文件系统用于索引这些数据的元信息（如目录项、FAT表、i-node）被修改或清除了。只要这些数据没有被新的数据覆盖，就有可能被恢复。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;2-文件系统的性能&quot;&gt;2. 文件系统的性能&lt;a class=&quot;anchor&quot; href=&quot;#2-文件系统的性能&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;磁盘I/O是计算机系统中最慢的环节之一，因此提高文件系统性能的核心在于减少磁盘访问次数。&lt;/p&gt;
&lt;h3 id=&quot;21-磁盘高速缓存-disk-cache--buffer-cache&quot;&gt;2.1 磁盘高速缓存 (Disk Cache / Buffer Cache)&lt;a class=&quot;anchor&quot; href=&quot;#21-磁盘高速缓存-disk-cache--buffer-cache&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是最重要和最有效的性能提升手段。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原理: 在内存中开辟一块区域，用于缓存从磁盘读出的数据块的副本。&lt;/li&gt;
&lt;li&gt;工作流程:
&lt;ol&gt;
&lt;li&gt;当进程请求读取某个磁盘块时，系统首先检查该块是否已在缓存中。&lt;/li&gt;
&lt;li&gt;如果在（称为缓存命中，Cache Hit），则直接从内存中读取数据，无需访问磁盘。&lt;/li&gt;
&lt;li&gt;如果不在（称为缓存未命中，Cache Miss），系统会从磁盘读取该块，存入缓存，然后再提供给进程。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;局部性原理: 由于程序的访问具有时间局部性（一个数据被访问后，短时间内可能再次被访问）和空间局部性（一个数据被访问后，其附近的数据也可能很快被访问），缓存的命中率通常很高。&lt;/li&gt;
&lt;li&gt;缓存置换算法: 当缓存满了之后，需要选择一个块来替换出去。常用算法是 LRU (Least Recently Used, 最近最少使用)，即替换掉最长时间没有被访问过的块。&lt;/li&gt;
&lt;li&gt;一致性问题: 延迟写（write-back）策略会带来一致性问题。如果一个被修改的缓存块（脏块）在写回磁盘前系统崩溃，修改就会丢失。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    subgraph &amp;quot;操作系统内核 (Kernel Level)&amp;quot;
        A[文件子系统]
        B[缓冲区缓存 Buffer Cache]
        C[设备驱动]
        A &amp;lt;--&amp;gt; B
        B &amp;lt;--&amp;gt; C
    end
    subgraph &amp;quot;用户态 (User Level)&amp;quot;
        D[用户程序]
    end
    subgraph &amp;quot;硬件 (Hardware Level)&amp;quot;
        E[磁盘]
    end
    D -- &amp;quot;系统调用&amp;quot; --&amp;gt; A
    C &amp;lt;--&amp;gt; E&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;22-提前读取-prefetch--read-ahead&quot;&gt;2.2 提前读取 (Prefetch / Read-Ahead)&lt;a class=&quot;anchor&quot; href=&quot;#22-提前读取-prefetch--read-ahead&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;思路: 基于空间局部性原理，当系统读取一个块时，它会猜测程序接下来可能会访问相邻的块，于是”提前”将这些块也读入缓存。&lt;/li&gt;
&lt;li&gt;开销: 额外开销很小，因为磁盘寻道和旋转定位的时间已经付出，多读几个连续的块只增加了少量数据传输时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;23-合理分配磁盘空间&quot;&gt;2.3 合理分配磁盘空间&lt;a class=&quot;anchor&quot; href=&quot;#23-合理分配磁盘空间&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;思路: 将逻辑上连续的文件块，在物理上也存放在相邻的位置，最好是在同一个柱面上。&lt;/li&gt;
&lt;li&gt;目标: 减少磁盘臂的移动（寻道时间）。&lt;/li&gt;
&lt;li&gt;例子：柱面组 (Cylinder Group): 像Berkeley FFS这样的文件系统会将磁盘划分为多个柱面组。每个柱面组都有自己的i-node区和数据块区。当创建一个新文件时，系统会尽量将其i-node和数据块都分配在同一个柱面组内，从而将访问局部化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;24-磁盘调度&quot;&gt;2.4 磁盘调度&lt;a class=&quot;anchor&quot; href=&quot;#24-磁盘调度&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;当有多个磁盘I/O请求在队列中等待时，调度算法决定了为哪个请求服务的顺序。
一次访盘时间 = 寻道时间 + 旋转延迟时间 + 传输时间。
调度的主要目标是减少总的寻道时间。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;问题示例:
&lt;ul&gt;
&lt;li&gt;磁盘请求队列 (按到达顺序): 98, 183, 37, 122, 14, 124, 65, 67&lt;/li&gt;
&lt;li&gt;磁头初始位置: 53&lt;/li&gt;
&lt;li&gt;磁盘柱面范围: 0-199 (假设)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;241-先来先服务-fcfs---first-come-first-served&quot;&gt;2.4.1 先来先服务 (FCFS - First-Come, First-Served)&lt;a class=&quot;anchor&quot; href=&quot;#241-先来先服务-fcfs---first-come-first-served&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;策略: 按请求到达的顺序处理。&lt;/li&gt;
&lt;li&gt;服务序列: 53 -&amp;gt; 98 -&amp;gt; 183 -&amp;gt; 37 -&amp;gt; 122 -&amp;gt; 14 -&amp;gt; 124 -&amp;gt; 65 -&amp;gt; 67&lt;/li&gt;
&lt;li&gt;磁头移动距离: (98-53) + (183-98) + (183-37) + (122-37) + (122-14) + (124-14) + (124-65) + (67-65) = 45 + 85 + 146 + 85 + 108 + 110 + 59 + 2 = 640 个磁道。&lt;/li&gt;
&lt;li&gt;评价: 公平，但效率低下，磁头可能在磁盘两端来回移动。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;242-最短寻道时间优先-sstf---shortest-seek-time-first&quot;&gt;2.4.2 最短寻道时间优先 (SSTF - Shortest Seek Time First)&lt;a class=&quot;anchor&quot; href=&quot;#242-最短寻道时间优先-sstf---shortest-seek-time-first&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;策略: 优先服务与当前磁头位置最近的请求。&lt;/li&gt;
&lt;li&gt;服务序列: 53 -&amp;gt; 65 -&amp;gt; 67 -&amp;gt; 37 -&amp;gt; 14 -&amp;gt; 98 -&amp;gt; 122 -&amp;gt; 124 -&amp;gt; 183&lt;/li&gt;
&lt;li&gt;磁头移动距离: (65-53) + (67-65) + (67-37) + (37-14) + (98-14) + (122-98) + (124-122) + (183-124) = 12 + 2 + 30 + 23 + 84 + 24 + 2 + 59 = 236 个磁道。&lt;/li&gt;
&lt;li&gt;评价: 性能比FCFS好很多，但可能导致”饥饿”现象，即离磁头较远的请求可能长时间得不到服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;243-扫描算法-scan--elevator-algorithm&quot;&gt;2.4.3 扫描算法 (SCAN / Elevator Algorithm)&lt;a class=&quot;anchor&quot; href=&quot;#243-扫描算法-scan--elevator-algorithm&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;策略: 磁头在一个方向上移动，服务所有沿途的请求，直到到达磁盘末端，然后反转方向继续。&lt;/li&gt;
&lt;li&gt;服务序列 (假设初始向磁道号减小的方向移动): 53 -&amp;gt; 37 -&amp;gt; 14 -&amp;gt; (到达0) -&amp;gt; 65 -&amp;gt; 67 -&amp;gt; 98 -&amp;gt; 122 -&amp;gt; 124 -&amp;gt; 183&lt;/li&gt;
&lt;li&gt;磁头移动距离: (53-0) + (183-0) = 53 + 183 = 236 个磁道。&lt;/li&gt;
&lt;li&gt;评价: 性能较好，且避免了饥饿。但对两端的请求不公平，中间区域的请求被服务得更频繁。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;244-单向扫描算法-c-scan---circular-scan&quot;&gt;2.4.4 单向扫描算法 (C-SCAN - Circular SCAN)&lt;a class=&quot;anchor&quot; href=&quot;#244-单向扫描算法-c-scan---circular-scan&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;策略: 磁头只在一个方向上服务请求（例如从0到199）。到达末端后，立即返回到起始端（0号），不服务任何请求，然后开始下一轮扫描。&lt;/li&gt;
&lt;li&gt;服务序列 (假设初始向磁道号增加的方向移动): 53 -&amp;gt; 65 -&amp;gt; 67 -&amp;gt; 98 -&amp;gt; 122 -&amp;gt; 124 -&amp;gt; 183 -&amp;gt; (到199) -&amp;gt; (跳回0) -&amp;gt; 14 -&amp;gt; 37&lt;/li&gt;
&lt;li&gt;评价: 提供了更均匀的等待时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;245-n-step-scan-和-fscan&quot;&gt;2.4.5 N-Step-SCAN 和 FSCAN&lt;a class=&quot;anchor&quot; href=&quot;#245-n-step-scan-和-fscan&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;这两种是SCAN的变种，旨在解决”磁头臂粘性”问题（即在处理当前队列时，新来的请求如果恰好在磁头附近，会插队，导致磁头长时间停留在一个区域）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;N-Step-SCAN: 将请求队列分成长度为N的子队列，按顺序处理。在处理一个子队列时，新来的请求放入其他队列。&lt;/li&gt;
&lt;li&gt;FSCAN: 使用两个队列。扫描开始时，所有请求在一个队列，另一个为空。扫描期间，新请求放入空队列，直到当前队列处理完毕再切换。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;246-练习题解答&quot;&gt;2.4.6 练习题解答&lt;a class=&quot;anchor&quot; href=&quot;#246-练习题解答&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;练习1:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;请求列表 (柱面, 磁头, 扇区): (5,4,1), (5,1,5), (5,4,5), (5,2,8)&lt;/li&gt;
&lt;li&gt;分析: 所有请求都在同一个柱面（5号）。因此，寻道时间为0。我们需要优化旋转延迟。假设磁头从0扇区开始旋转，服务顺序应按扇区号从小到大排列。&lt;/li&gt;
&lt;li&gt;最优次序: (5,4,1) -&amp;gt; (5,1,5) -&amp;gt; (5,4,5) -&amp;gt; (5,2,8)。注意，(5,1,5)和(5,4,5)扇区号相同，可以在同一旋转位置被不同磁头服务，它们的先后顺序不影响时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;练习2:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;请求列表 (柱面, 磁头, 扇区): ①(9,6,3), ②(7,5,6), ③(15,20,6), ④(9,4,4), ⑤(20,9,5), ⑥(7,15,2)&lt;/li&gt;
&lt;li&gt;磁头初始位置: 8号柱面&lt;/li&gt;
&lt;li&gt;分析: 这是一个综合优化问题，需要结合寻道和旋转。我们采用SSTF策略来决定柱面访问顺序。
&lt;ol&gt;
&lt;li&gt;当前在柱面8。最近的请求是柱面7和9。&lt;/li&gt;
&lt;li&gt;访问柱面9的请求①和④。移动1个柱面。&lt;/li&gt;
&lt;li&gt;访问柱面7的请求②和⑥。移动2个柱面。&lt;/li&gt;
&lt;li&gt;访问柱面15的请求③。移动8个柱面。&lt;/li&gt;
&lt;li&gt;访问柱面20的请求⑤。移动5个柱面。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;最优柱面访问次序: 8 -&amp;gt; 9 -&amp;gt; 7 -&amp;gt; 15 -&amp;gt; 20。&lt;/li&gt;
&lt;li&gt;最终请求服务次序: 先后访问柱面9上的请求（①和④），再访问柱面7上的请求（⑥和②），然后是柱面15（③），最后是柱面20（⑤）。在同一柱面内，按扇区号优化。所以响应次序是 ① -&amp;gt; ④ -&amp;gt; ⑥ -&amp;gt; ② -&amp;gt; ③ -&amp;gt; ⑤ (假设磁头从0扇区开始转，先遇到3扇区再遇到4扇区，先遇到2扇区再遇到6扇区)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;25-信息的优化分布-optimized-information-layout-on-disk&quot;&gt;2.5 信息的优化分布 (Optimized Information Layout on Disk)&lt;a class=&quot;anchor&quot; href=&quot;#25-信息的优化分布-optimized-information-layout-on-disk&quot;&gt;#&lt;/a&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;ul&gt;
&lt;li&gt;一个处理程序要求按顺序处理文件中的8个逻辑记录。&lt;/li&gt;
&lt;li&gt;磁盘旋转一周为20毫秒 (假设每转处理完所有可见记录)。&lt;/li&gt;
&lt;li&gt;处理器处理一个记录需要5毫秒。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;图示分析 (Interleaving - 交叉因子):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;无交叉 (Sequential Layout): 逻辑记录1, 2, 3, … 存放在物理扇区 1, 2, 3, …
&lt;ul&gt;
&lt;li&gt;读取记录1 (假设1ms传输) -&amp;gt; 处理记录1 (5ms)。&lt;/li&gt;
&lt;li&gt;在处理记录1的5ms期间，磁盘继续旋转。当处理完毕准备读取记录2时，记录2所在的扇区可能已经转过磁头，需要等待几乎一整圈 (接近20ms) 才能再读到记录2。&lt;/li&gt;
&lt;li&gt;总时间 = 8 * (1ms读 + 5ms处理 + ~19ms等待) = 8 * 25ms = 200ms (估算)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;有交叉 (Interleaved Layout): 为了匹配处理速度和磁盘旋转速度，逻辑记录的存放可以跳过一些物理扇区。
&lt;ul&gt;
&lt;li&gt;例如，如果每处理一个记录磁盘转过N个扇区，那么逻辑记录i和逻辑记录i+1之间应该间隔N-1个物理扇区。&lt;/li&gt;
&lt;li&gt;假设5ms处理时间对应磁盘转过1/4圈 (5ms/20ms)。如果一圈有8个扇区 (简化) ，则转过2个扇区。那么交叉因子可以是2。&lt;/li&gt;
&lt;li&gt;记录1在扇区1。读1，处理1。此时磁盘可能到了扇区3附近。如果记录2放在扇区3，则可以立即读取。&lt;/li&gt;
&lt;li&gt;物理扇区应该如何安排:
&lt;ul&gt;
&lt;li&gt;逻辑记录: 1  2  3  4  5  6  7  8&lt;/li&gt;
&lt;li&gt;物理扇区: 1  4  7  2  5  8  3  6 (假设交叉因子为2，即跳过2个物理扇区)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;目标: 当CPU处理完当前记录后，下一个逻辑记录所在的物理扇区正好旋转到磁头下，从而避免或减少旋转等待时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;26-raid技术-redundant-arrays-of-independent-disks&quot;&gt;2.6 RAID技术 (Redundant Arrays of Independent Disks)&lt;a class=&quot;anchor&quot; href=&quot;#26-raid技术-redundant-arrays-of-independent-disks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RAID通过将多块独立的磁盘组合成一个逻辑单元，来提供比单个磁盘更高的性能、更大的容量和更好的容错能力。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;核心思想:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;数据分条 (Striping): 将数据分成小块（条带），并行地写入/读出多块磁盘，以提高吞吐率。&lt;/li&gt;
&lt;li&gt;冗余 (Redundancy): 通过镜像或校验码来提供容错能力。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;主要RAID级别:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RAID 0 (条带化): 纯粹的数据分条，没有冗余。读写性能最佳，但任何一块磁盘损坏都会导致所有数据丢失。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    D0[&amp;quot;Disk 0&amp;quot;]
    D1[&amp;quot;Disk 1&amp;quot;]
    D2[&amp;quot;Disk 2&amp;quot;]
    D3[&amp;quot;Disk 3&amp;quot;]
    subgraph &amp;quot;Logic Data&amp;quot;
        S0[Stripe 0] --&amp;gt; D0;
        S1[Stripe 1] --&amp;gt; D1;
        S2[Stripe 2] --&amp;gt; D2;
        S3[Stripe 3] --&amp;gt; D3;
        S4[Stripe 4] --&amp;gt; D0;
        S5[Stripe 5] --&amp;gt; D1;
    end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;RAID 1 (镜像): 将数据完全复制到另一块磁盘。提供最高的数据安全性，但磁盘利用率只有50%。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    D0[&amp;quot;Disk 0 (Primary)&amp;quot;]
    D1[&amp;quot;Disk 1 (Mirror)&amp;quot;]
    subgraph &amp;quot;Logic Data&amp;quot;
        S0[Stripe 0] --&amp;gt; D0;
        S0_M[Stripe 0] --&amp;gt; D1;
        S1[Stripe 1] --&amp;gt; D0;
        S1_M[Stripe 1] --&amp;gt; D1;
    end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;RAID 3 (字节级带奇偶校验的条带化): 数据以字节为单位分条，并使用一块专用磁盘存储奇偶校验码。&lt;/li&gt;
&lt;li&gt;RAID 4 (块级带奇偶校验的条带化): 与RAID 3类似，但以块为单位。校验盘成为写入瓶颈。&lt;/li&gt;
&lt;li&gt;RAID 5 (块级带分布式奇偶校验的条带化): 将校验块分布到阵列中的所有磁盘上，消除了RAID 4的校验盘瓶颈，是性能、成本和可靠性之间的一个良好平衡。&lt;/li&gt;
&lt;li&gt;RAID 6 (双重分布式奇偶校验): 在RAID 5的基础上增加了第二个独立的校验块。可以容忍两块磁盘同时损坏，可靠性更高，但写入性能和成本代价也更大。&lt;/li&gt;
&lt;li&gt;RAID 1+0 (或RAID 10): 先做镜像，再做条带化。性能和可靠性都很好，但成本高。&lt;/li&gt;
&lt;li&gt;RAID 0+1: 先做条带化，再做镜像。容错性不如RAID 10。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-文件系统的结构&quot;&gt;3. 文件系统的结构&lt;a class=&quot;anchor&quot; href=&quot;#3-文件系统的结构&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-文件系统通用层次模型&quot;&gt;3.1 文件系统通用层次模型&lt;a class=&quot;anchor&quot; href=&quot;#31-文件系统通用层次模型&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;一个典型的文件系统可以被看作是分层的结构。&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    A[应用程序] --&amp;gt; B[逻辑文件系统层];
    B --&amp;gt; C[文件组织模块层];
    C --&amp;gt; D[基本文件系统层];
    D --&amp;gt; E[基本I/O控制层];
    E --&amp;gt; F[物理磁盘];&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;/ul&gt;
&lt;h3 id=&quot;32-虚拟文件系统-vfs---virtual-file-system&quot;&gt;3.2 虚拟文件系统 (VFS - Virtual File System)&lt;a class=&quot;anchor&quot; href=&quot;#32-虚拟文件系统-vfs---virtual-file-system&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;VFS是内核中的一个抽象层，它为用户态程序提供了一个统一的文件系统接口，从而屏蔽了底层不同类型文件系统的差异。&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    A[&amp;quot;用户程序 (open, read, write)&amp;quot;] --&amp;gt; B[&amp;quot;系统调用接口 (sys_open, sys_read)&amp;quot;];
    subgraph &amp;quot;内核空间&amp;quot;
        B --&amp;gt; C[&amp;quot;虚拟文件系统 (VFS)&amp;quot;];
        C --&amp;gt; D[&amp;quot;ext4 模块&amp;quot;];
        C --&amp;gt; E[&amp;quot;NTFS 模块&amp;quot;];
        C --&amp;gt; F[&amp;quot;NFS 模块&amp;quot;];
    end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;有了VFS，你的程序无需关心它操作的文件是在ext4分区、NTFS分区还是在一个网络文件系统（NFS）上，可以使用同样的方式来访问。&lt;/p&gt;
&lt;h3 id=&quot;33-日志结构文件系统-lfs---log-structured-file-system&quot;&gt;3.3 日志结构文件系统 (LFS - Log-Structured File System)&lt;a class=&quot;anchor&quot; href=&quot;#33-日志结构文件系统-lfs---log-structured-file-system&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;核心思想: 为了将所有随机写操作转换为顺序写，从而极大地提高写入性能，LFS将整个磁盘看作一个巨大的、只追加的日志（Log）。&lt;/li&gt;
&lt;li&gt;工作方式: 任何修改（包括数据、i-node、目录等）都会被打包成一个段（segment），然后顺序地写入到日志的末尾。一个后台的清理线程会周期性地扫描日志，回收旧的、不再有效的空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;34-日志文件系统-journaling-file-system&quot;&gt;3.4 日志文件系统 (Journaling File System)&lt;a class=&quot;anchor&quot; href=&quot;#34-日志文件系统-journaling-file-system&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是从LFS借鉴了”日志”思想并加以简化和实用化的产物。它不把所有数据都写入日志，通常只记录元数据的变更。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;典型删除操作步骤:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;写日志: 向日志区写入一个条目，记录”我将要：1.从目录中删除文件X；2.释放i-node Y；3.归还数据块Z到空闲池”。&lt;/li&gt;
&lt;li&gt;提交日志: 确保日志条目已安全写入磁盘。&lt;/li&gt;
&lt;li&gt;执行操作: 实际地去修改目录、i-node和空闲块列表。&lt;/li&gt;
&lt;li&gt;清除日志: 操作完成后，标记日志条目为已完成（或直接擦除）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果在第3步时系统崩溃，重启后只需检查日志，发现有一个未完成的事务，然后重新执行这三步操作即可，保证了文件系统的一致性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;4-分布式文件系统&quot;&gt;4. 分布式文件系统&lt;a class=&quot;anchor&quot; href=&quot;#4-分布式文件系统&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;分布式文件系统允许多台计算机通过网络共享文件，并且对用户来说，远程文件仿佛就在本地一样（透明性）。&lt;/p&gt;
&lt;h3 id=&quot;41-hdfs-hadoop-distributed-file-system-概述&quot;&gt;4.1 HDFS (Hadoop Distributed File System) 概述&lt;a class=&quot;anchor&quot; href=&quot;#41-hdfs-hadoop-distributed-file-system-概述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;HDFS是专为在大规模廉价硬件集群上存储和处理海量数据而设计的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HDFS架构:
&lt;ul&gt;
&lt;li&gt;NameNode (主节点): 负责管理文件系统的命名空间（目录树）和所有文件的元数据（文件由哪些块组成，这些块存储在哪些DataNode上）。未启用 HA 时，它是系统的单点故障来源。&lt;/li&gt;
&lt;li&gt;DataNode (从节点): 负责存储实际的数据块，并响应来自客户端或NameNode的读写请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    Client_Read[客户端 Read]
    Client_Write[客户端 Write]

    NameNode

    subgraph &amp;quot;机架1&amp;quot;
        DN11[DataNode]
        DN12[DataNode]
    end

    subgraph &amp;quot;机架2&amp;quot;
        DN21[DataNode]
        DN22[DataNode]
    end

    Client_Read -- &amp;quot;1. 请求元数据&amp;quot; --&amp;gt; NameNode
    NameNode -- &amp;quot;2. 返回块位置&amp;quot; --&amp;gt; Client_Read
    Client_Read -- &amp;quot;3. 直接读取数据&amp;quot; --&amp;gt; DN11
    Client_Read -- &amp;quot;3. 直接读取数据&amp;quot; --&amp;gt; DN21

    Client_Write -- &amp;quot;1. 请求写入&amp;quot; --&amp;gt; NameNode
    NameNode -- &amp;quot;2. 分配块位置&amp;quot; --&amp;gt; Client_Write
    Client_Write -- &amp;quot;3. 写入数据管道&amp;quot; --&amp;gt; DN12
    DN12 -- &amp;quot;4. 复制&amp;quot; --&amp;gt; DN22
    DN22 -- &amp;quot;5. 复制&amp;quot; --&amp;gt; DN11&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;关键特性:
&lt;ul&gt;
&lt;li&gt;大文件和块存储: 文件被切分成巨大的块（默认128MB或256MB），以块为单位存储和复制。这减少了元数据的规模。&lt;/li&gt;
&lt;li&gt;一次写入，多次读取: 为流式数据访问优化，吞吐量高。支持文件追加，但不支持任意位置修改。&lt;/li&gt;
&lt;li&gt;数据冗余和机架感知: 每个块默认有3个副本。HDFS会尽量将副本分散到不同的机架上（例如，1个在本地机架，2个在远程机架），以防止整个机架掉电或网络故障导致数据不可用。&lt;/li&gt;
&lt;li&gt;高可用 (High Availability): 为了解决NameNode的单点故障问题，引入了Active-Standby NameNode模式。一个Active NameNode对外服务，一个Standby NameNode作为热备份，它们通过一个共享存储（如NFS或JournalNode集群）来同步元数据。如果Active节点宕机，Standby节点可以迅速接管。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside aria-label=&quot;期末考试练习题&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;期末考试练习题&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;这里有两道期末考试常考的练习题, 可以参考我写的&lt;a href=&quot;https://github.com/EmptyBlueBox/Operating_Systems-2025Spring-PKU/blob/main/Resource/%E6%80%9D%E8%80%83%E9%A2%98.md&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;思考题答案&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;中的解答.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 07: File System 1</title><link>https://www.lyt0112.com/blog/operating_systems_note_07-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_07-zh</guid><description>Operating Systems Notes 07: File System 1</description><pubDate>Wed, 23 Apr 2025 02:09:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-文件系统的基本概念&quot;&gt;1. 文件系统的基本概念&lt;a class=&quot;anchor&quot; href=&quot;#1-文件系统的基本概念&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;11-问题导引&quot;&gt;1.1 问题导引&lt;a class=&quot;anchor&quot; href=&quot;#11-问题导引&quot;&gt;#&lt;/a&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;文件系统与其他操作系统功能（如I/O系统）的接口是怎样的？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;带着这些问题，我们来逐一揭开文件系统的神秘面纱。&lt;/p&gt;
&lt;h3 id=&quot;12-文件-file&quot;&gt;1.2 文件 (File)&lt;a class=&quot;anchor&quot; href=&quot;#12-文件-file&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;121-什么是文件&quot;&gt;1.2.1 什么是文件？&lt;a class=&quot;anchor&quot; href=&quot;#121-什么是文件&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;“文件是对磁盘的抽象”。这是一个重要的概念。磁盘的物理结构是磁道、扇区，它只能通过低级的指令（如“读写第X磁道第Y扇区”）来访问，这不便。文件系统将这些底层细节隐藏起来，提供给我们一个更高层次、更易于理解和操作的视图。&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;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;pre&gt;&lt;code&gt;| 信息项0 | 信息项1 | ... | 信息项i | ... | 信息项n-1 |
                      ^
                      |
                    读写指针&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在UNIX/Linux系统中，这个抽象被推向了极致，也就是著名的“万物皆文件” (Everything is a file)。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通文件 (如 &lt;code&gt;my_document.txt&lt;/code&gt;) 是文件。&lt;/li&gt;
&lt;li&gt;硬件设备 (如磁盘分区 &lt;code&gt;/dev/sda2&lt;/code&gt;，终端 &lt;code&gt;/dev/tty2&lt;/code&gt;) 也是文件。&lt;/li&gt;
&lt;li&gt;甚至内核的各种数据结构也通过 &lt;code&gt;/proc&lt;/code&gt; 目录以文件的形式暴露出来。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这极大地简化了应用程序与系统各种资源的交互方式，因为它们都可以使用同一套文件操作API（&lt;code&gt;open&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;close&lt;/code&gt;）来访问。&lt;/p&gt;
&lt;h4 id=&quot;122-文件的分类&quot;&gt;1.2.2 文件的分类&lt;a class=&quot;anchor&quot; href=&quot;#122-文件的分类&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;根据文件的性质和用途，特别是在UNIX系统中，文件可以分为以下几类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通文件 (Regular File): 包含用户的信息，内容通常是ASCII文本或二进制数据。这是我们最常见的文件类型。&lt;/li&gt;
&lt;li&gt;目录文件 (Directory File): 用于管理文件系统的系统文件。它的内容是该目录下包含的文件和子目录的列表。&lt;/li&gt;
&lt;li&gt;特殊文件 (Special File): 用于I/O。
&lt;ul&gt;
&lt;li&gt;字符设备文件: 用于模拟串行I/O设备，如终端、打印机。数据以字符为单位进行传输。&lt;/li&gt;
&lt;li&gt;块设备文件: 用于模拟磁盘等块设备。数据以数据块为单位进行传输。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管道文件 (Pipe): 用于进程间通信。&lt;/li&gt;
&lt;li&gt;套接字 (Socket): 用于不同机器间的网络通信。&lt;/li&gt;
&lt;li&gt;符号链接文件 (Symbolic Link): 其内容是到另一个文件的路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;123-文件的逻辑结构&quot;&gt;1.2.3 文件的逻辑结构&lt;a class=&quot;anchor&quot; href=&quot;#123-文件的逻辑结构&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;文件的逻辑结构是指在用户看来，文件内部是如何组织的。这由用户的访问方式决定，主要有以下几种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;流式文件 (Stream File): 文件被看作一个无结构的字节序列（如UNIX中的文件）。这是最简单、最常用的一种结构。&lt;/li&gt;
&lt;li&gt;记录式文件 (Record File): 文件由一组定长或变长的记录构成。&lt;/li&gt;
&lt;li&gt;树形结构: 文件由记录的树状结构组织而成，便于按键值查找。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    subgraph &amp;quot;文件的逻辑结构&amp;quot;
        A[&amp;quot;字节序列 (流式文件)&amp;quot;]
        B[&amp;quot;记录序列 (记录式文件)&amp;quot;]
        C[&amp;quot;树形结构&amp;quot;]
    end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;此外，还有一些更复杂的结构，如索引结构、索引顺序结构、散列结构等，它们通常用于数据库系统中，以优化数据检索效率。&lt;/p&gt;
&lt;h4 id=&quot;124-文件的访问方式&quot;&gt;1.2.4 文件的访问方式&lt;a class=&quot;anchor&quot; href=&quot;#124-文件的访问方式&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;顺序存取 (Sequential Access): 从文件开头按字节或记录的顺序依次读写。这是最基本的方式。&lt;/li&gt;
&lt;li&gt;随机存取 (Random Access): 可以从文件中的任意位置开始读写。这需要操作系统提供&lt;code&gt;seek&lt;/code&gt;之类的操作来移动读写指针。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;13-文件属性与文件控制块-fcb&quot;&gt;1.3 文件属性与文件控制块 (FCB)&lt;a class=&quot;anchor&quot; href=&quot;#13-文件属性与文件控制块-fcb&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;操作系统为了管理文件，需要记录每个文件的相关信息。这些信息被称为文件属性 (File Attributes) 或 元数据 (Metadata)。它们存放在一个称为文件控制块 (File Control Block, FCB) 的数据结构中。&lt;/p&gt;
&lt;p&gt;一个FCB通常包含以下属性：&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;li&gt;其他: 共享计数、只读/隐藏/系统等标志位。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;14-文件操作&quot;&gt;1.4 文件操作&lt;a class=&quot;anchor&quot; href=&quot;#14-文件操作&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;操作系统提供了一系列系统调用来支持对文件的操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create&lt;/code&gt;: 创建一个新文件。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delete&lt;/code&gt;: 删除一个文件。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;open&lt;/code&gt;: 打开一个已存在的文件，为后续的读写操作做准备。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;close&lt;/code&gt;: 关闭一个已打开的文件，释放相关资源。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read&lt;/code&gt;: 从文件中读取数据。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write&lt;/code&gt;: 向文件中写入数据。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;append&lt;/code&gt;: 在文件末尾追加数据。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seek&lt;/code&gt;: 移动文件读写指针到指定位置（用于随机存取）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get attributes&lt;/code&gt; / &lt;code&gt;set attributes&lt;/code&gt;: 获取或设置文件属性。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rename&lt;/code&gt;: 重命名文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个典型的文件访问模式是：先 &lt;code&gt;open&lt;/code&gt;，然后进行若干次 &lt;code&gt;read&lt;/code&gt;/&lt;code&gt;write&lt;/code&gt;，最后 &lt;code&gt;close&lt;/code&gt;。
&lt;code&gt;f = open(name, flag);&lt;/code&gt;
&lt;code&gt;...&lt;/code&gt;
&lt;code&gt;read(f, ...);&lt;/code&gt;
&lt;code&gt;...&lt;/code&gt;
&lt;code&gt;close(f);&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;15-文件目录-directory&quot;&gt;1.5 文件目录 (Directory)&lt;a class=&quot;anchor&quot; href=&quot;#15-文件目录-directory&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;151-基本概念&quot;&gt;1.5.1 基本概念&lt;a class=&quot;anchor&quot; href=&quot;#151-基本概念&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;文件目录 (File Directory): 为了统一管理所有文件的FCB，我们将它们组织起来，形成的这个集合就是文件目录。所以，文件目录是文件控制块的有序集合。&lt;/li&gt;
&lt;li&gt;目录项 (Directory Entry): 构成文件目录的基本单元，每个目录项对应一个文件或子目录。在简单系统中，目录项就是FCB。在复杂系统中（如UNIX），目录项可能只包含文件名和指向一个更完整数据结构（如i-node）的指针。&lt;/li&gt;
&lt;li&gt;目录文件 (Directory File): 为了持久化存储，文件目录通常会以一种特殊文件的形式存放在磁盘上，这个文件就叫目录文件。其内容由一系列目录项组成。为保证文件系统的一致性，目录文件通常只允许内核进行修改，应用程序通过系统调用来访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;152-目录结构&quot;&gt;1.5.2 目录结构&lt;a class=&quot;anchor&quot; href=&quot;#152-目录结构&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;最常用的是树形目录结构 (Tree-structured Directory)。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;根目录 (Root Directory): 整个文件系统的起点。&lt;/li&gt;
&lt;li&gt;子目录 (Subdirectory): 允许用户将文件分组，形成层次化结构。&lt;/li&gt;
&lt;li&gt;路径名 (Pathname): 唯一标识一个文件或目录在树中的位置。
&lt;ul&gt;
&lt;li&gt;绝对路径名: 从根目录开始的路径，例如 &lt;code&gt;/User_B/Draw/ABC&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;相对路径名: 从当前目录开始的路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;当前目录 (Current/Working Directory): 每个进程都有一个当前目录。这使得用户可以使用更短的相对路径名来访问文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    Root(Root directory) --&amp;gt; A(User A)
    Root --&amp;gt; B(User B)
    Root --&amp;gt; C(User C)

    A --&amp;gt; A_file(File A)

    B --&amp;gt; B_subdir1(Dir B)
    B --&amp;gt; B_subdir2(Dir B)
    B_subdir1 --&amp;gt; B_file(File B)

    C --&amp;gt; C_subdir1(Dir C)
    C --&amp;gt; C_file1(File C)
    C_subdir1 --&amp;gt; C_subdir2(Dir C)
    C_subdir1 --&amp;gt; C_file2(File C)
    C_subdir2 --&amp;gt; C_file3(File C)
    C_subdir2 --&amp;gt; C_file4(File C)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;153-目录操作&quot;&gt;1.5.3 目录操作&lt;a class=&quot;anchor&quot; href=&quot;#153-目录操作&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create&lt;/code&gt; / &lt;code&gt;delete&lt;/code&gt;: 创建或删除一个目录。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opendir&lt;/code&gt; / &lt;code&gt;closedir&lt;/code&gt;: 打开和关闭目录以读取其内容。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;readdir&lt;/code&gt;: 读取目录中的下一个目录项。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rename&lt;/code&gt;: 重命名目录。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;link&lt;/code&gt; / &lt;code&gt;unlink&lt;/code&gt;: 创建或删除文件的链接（别名）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;154-思考题文件名解析&quot;&gt;1.5.4 思考题：文件名解析&lt;a class=&quot;anchor&quot; href=&quot;#154-思考题文件名解析&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;问题: 观察下图，试画出对应的树形目录结构，并给出解析文件名 &lt;code&gt;/User_B/Draw/ABC&lt;/code&gt; 的步骤。
图示: (根据幻灯片 Page 25 的图)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Master Directory (Root)
&lt;ul&gt;
&lt;li&gt;User_A (Dir) -&amp;gt; …&lt;/li&gt;
&lt;li&gt;User_B (Dir)
&lt;ul&gt;
&lt;li&gt;Word (Dir) -&amp;gt; …&lt;/li&gt;
&lt;li&gt;Draw (Dir) -&amp;gt; ABC (File)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;User_C (Dir) -&amp;gt; …&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;回答:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;树形结构:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    Root(&amp;quot;Master Directory&amp;quot;) --&amp;gt; UserA(&amp;quot;User_A&amp;quot;)
    Root --&amp;gt; UserB(&amp;quot;User_B&amp;quot;)
    Root --&amp;gt; UserC(&amp;quot;User_C&amp;quot;)

    UserB --&amp;gt; Word(&amp;quot;Word&amp;quot;)
    UserB --&amp;gt; Draw(&amp;quot;Draw&amp;quot;)

    Draw --&amp;gt; ABC(&amp;quot;File ABC&amp;quot;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件名解析步骤 (/User_B/Draw/ABC):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从根目录（Master Directory）开始查找。&lt;/li&gt;
&lt;li&gt;在根目录中，查找名为 &lt;code&gt;User_B&lt;/code&gt; 的目录项。找到后，获取该目录文件的位置信息。&lt;/li&gt;
&lt;li&gt;读取 &lt;code&gt;User_B&lt;/code&gt; 目录文件的内容，在其中查找名为 &lt;code&gt;Draw&lt;/code&gt; 的目录项。找到后，获取该目录文件的位置信息。&lt;/li&gt;
&lt;li&gt;读取 &lt;code&gt;Draw&lt;/code&gt; 目录文件的内容，在其中查找名为 &lt;code&gt;ABC&lt;/code&gt; 的目录项。&lt;/li&gt;
&lt;li&gt;找到 &lt;code&gt;ABC&lt;/code&gt; 的目录项，从中获取文件的FCB信息（如物理地址、大小、权限等），解析完成。此时，文件就可以被打开和访问了。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;16-文件系统-file-system&quot;&gt;1.6 文件系统 (File System)&lt;a class=&quot;anchor&quot; href=&quot;#16-文件系统-file-system&quot;&gt;#&lt;/a&gt;&lt;/h3&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;与I/O系统的统一接口: 为上层应用提供与具体设备无关的I/O操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2-文件系统的实现&quot;&gt;2. 文件系统的实现&lt;a class=&quot;anchor&quot; href=&quot;#2-文件系统的实现&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;现在我们从系统内部视角，看看文件系统是如何被设计和实现的。&lt;/p&gt;
&lt;h3 id=&quot;21-存储介质与物理块&quot;&gt;2.1 存储介质与物理块&lt;a class=&quot;anchor&quot; href=&quot;#21-存储介质与物理块&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;存储介质: 文件通常存储在持久性存储介质上，如磁盘（HDD）、固态硬盘（SSD）、U盘等。&lt;/li&gt;
&lt;li&gt;抽象: 操作系统将磁盘抽象为一维的逻辑块（Block）数组，从 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;n-1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;数据块 (Block): 存储设备被划分为大小相等的块，块是信息存储、传输和分配的基本单位。Windows中称为簇 (Cluster)。一个块通常由若干个连续的扇区组成。扇区是物理存储单元，而块是逻辑存储单元。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HDD vs SSD 寻址:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LBA (Logical Block Addressing): 操作系统使用的是逻辑块地址。&lt;/li&gt;
&lt;li&gt;PBA (Physical Block Address): 存储设备内部的实际物理地址。&lt;/li&gt;
&lt;li&gt;映射:
&lt;ul&gt;
&lt;li&gt;在HDD中，LBA到PBA的映射相对固定，由硬盘固件管理。物理地址通常由柱面号、磁头号、扇区号组成。&lt;/li&gt;
&lt;li&gt;在SSD中，由于闪存的写入限制（磨损均衡），LBA到PBA的映射是动态的，由FTL（Flash Translation Layer）管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一次典型的HDD访盘请求包括三个主要时间开销：&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;h3 id=&quot;22-文件系统布局&quot;&gt;2.2 文件系统布局&lt;a class=&quot;anchor&quot; href=&quot;#22-文件系统布局&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在实现文件系统时，首先要考虑的是它在磁盘上和内存中的内容布局。&lt;/p&gt;
&lt;h4 id=&quot;221-相关术语&quot;&gt;2.2.1 相关术语&lt;a class=&quot;anchor&quot; href=&quot;#221-相关术语&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;磁盘分区 (Partition): 把一个物理磁盘的存储空间划分为几个相互独立的部分。&lt;/li&gt;
&lt;li&gt;文件卷 (Volume): 一个逻辑分区，可以是一个分区，也可以跨越多个磁盘（如RAID）。同一个文件卷使用同一份管理数据（元数据）来进行文件分配和空间管理。&lt;/li&gt;
&lt;li&gt;格式化 (Format): 在一个文件卷上建立文件系统的过程。这包括创建和初始化用于文件分配和磁盘空间管理的元数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;222-磁盘上的内容&quot;&gt;2.2.2 磁盘上的内容&lt;a class=&quot;anchor&quot; href=&quot;#222-磁盘上的内容&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;一个格式化好的文件卷通常包含以下几个部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;引导区 (Boot Block / Boot Sector): 包含从该卷引导操作系统所需的信息。通常是分区的第一个扇区。&lt;/li&gt;
&lt;li&gt;卷管理信息 (Superblock): 描述整个文件卷的信息，如块大小、块总数、空闲块数量、i-node数量等。这是文件系统的“元数据中的元数据”。&lt;/li&gt;
&lt;li&gt;空闲空间管理区: 用于记录哪些磁盘块是空闲的数据结构（如位图或空闲块链表）。&lt;/li&gt;
&lt;li&gt;文件元数据区: 存放文件控制块（如UNIX中的i-node区）的区域。&lt;/li&gt;
&lt;li&gt;数据区: 存放实际的用户文件和目录文件内容的区域。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;UNIX文件系统布局 (分区内):&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;pre&gt;&lt;code&gt;| 引导记录 | 超级块 | 空闲区管理 | i-node区 | 根目录及数据区 |&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;FAT文件系统布局:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;pre&gt;&lt;code&gt;| 引导区 | 文件分配表1 (FAT1) | 文件分配表2 (FAT2) | 根目录 | 其他目录和文件 |&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;23-文件的物理结构&quot;&gt;2.3 文件的物理结构&lt;a class=&quot;anchor&quot; href=&quot;#23-文件的物理结构&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是文件系统实现的核心之一，决定了文件在物理介质上的存放方式。&lt;/p&gt;
&lt;h4 id=&quot;231-连续结构-contiguous-allocation&quot;&gt;2.3.1 连续结构 (Contiguous Allocation)&lt;a class=&quot;anchor&quot; href=&quot;#231-连续结构-contiguous-allocation&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;文件的信息存放在若干连续的物理块中。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FCB中如何记录? 只需记录起始块号和文件长度（总块数）。&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;/ul&gt;
&lt;h4 id=&quot;232-链接结构-linked-allocation&quot;&gt;2.3.2 链接结构 (Linked Allocation)&lt;a class=&quot;anchor&quot; href=&quot;#232-链接结构-linked-allocation&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;文件的信息存放在若干不连续的物理块中，各块之间通过指针连接。每个块中都包含指向下一个块的指针。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FCB中如何记录? 只需记录起始块号。&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;ul&gt;
&lt;li&gt;不支持高效的随机存取: 要访问第 N 块，必须从头开始沿着指针链顺序访问前 N-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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;变形：文件分配表 (File Allocation Table, FAT)
为了解决链接结构的缺点，FAT文件系统将所有块的链接指针集中存放在磁盘开头的一个特殊区域——文件分配表 (FAT) 中。FAT本身是一个大数组，数组的下标对应磁盘块号，数组元素的值表示下一个块的块号。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FCB/目录项中如何记录? 仍然是记录起始块号。&lt;/li&gt;
&lt;li&gt;优点: 整个FAT可以被读入内存，使得随机访问变得高效。要找第 N 块，只需在内存的FAT数组中查找 N 次即可，无需访问磁盘数据区。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;233-索引结构-indexed-allocation&quot;&gt;2.3.3 索引结构 (Indexed Allocation)&lt;a class=&quot;anchor&quot; href=&quot;#233-索引结构-indexed-allocation&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;为每个文件建立一个专用的索引表 (Index Block)，这个表中存放了文件所有数据块的块号。索引表本身也需要一个磁盘块来存储。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FCB中如何记录? 记录该文件的索引表的地址。&lt;/li&gt;
&lt;li&gt;优点:
&lt;ul&gt;
&lt;li&gt;保留了链接结构的优点（无外部碎片，动态增长）。&lt;/li&gt;
&lt;li&gt;支持高效的随机存取: 要访问第 N 块，只需先读入索引表，然后在索引表中直接找到第 N 项，就能得到目标块的地址。&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;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;UNIX 多级索引结构 (综合模式)
典型 UNIX/ext2 风格的 i-node 可包含一个多级索引数组。常见教材示例使用15个地址项：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前12项 (直接寻址): 直接指向文件的前12个数据块。&lt;/li&gt;
&lt;li&gt;第13项 (一级间接): 指向一个索引块，该索引块里存放了更多数据块的地址。&lt;/li&gt;
&lt;li&gt;第14项 (二级间接): 指向一个二级索引块，该块中的每一项都指向一个一级索引块。&lt;/li&gt;
&lt;li&gt;第15项 (三级间接): 指向一个三级索引块。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    Inode[&amp;quot;i-node&amp;quot;] --&amp;gt; D1(Direct Block 1)
    Inode --&amp;gt; D2(Direct Block 2)
    Inode --&amp;gt; D_etc(...)
    Inode --&amp;gt; D12(Direct Block 12)

    Inode -- &amp;quot;Single Indirect&amp;quot; --&amp;gt; S_Indirect(一级索引块)
    S_Indirect --&amp;gt; DataBlock1
    S_Indirect --&amp;gt; DataBlock2
    S_Indirect --&amp;gt; DataBlock_etc(...)

    Inode -- &amp;quot;Double Indirect&amp;quot; --&amp;gt; D_Indirect(二级索引块)
    D_Indirect --&amp;gt; S_Indirect_1(一级索引块)
    D_Indirect --&amp;gt; S_Indirect_2(一级索引块)
    S_Indirect_1 --&amp;gt; DataBlock_A
    S_Indirect_2 --&amp;gt; DataBlock_B

    Inode -- &amp;quot;Triple Indirect&amp;quot; --&amp;gt; T_Indirect(三级索引块)
    T_Indirect --&amp;gt; D_Indirect_A(二级索引块)
    T_Indirect --&amp;gt; D_Indirect_B(二级索引块)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;思考题: UNIX中采用了三级索引结构后, 文件最大可达到多少个物理块? 假设扇区大小为512字节, 物理块等于扇区块大小, 一级索引表可以存放256个物理块号(一个块号占2字节)。
回答:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接块: 12 块&lt;/li&gt;
&lt;li&gt;一级间接: 256 块&lt;/li&gt;
&lt;li&gt;二级间接: &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;256&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;256&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;65&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;536&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;256 \times 256 = 65,536&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8389em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;536&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 块&lt;/li&gt;
&lt;li&gt;三级间接: &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;256&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;256&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;256&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;777&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;216&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;256 \times 256 \times 256 = 16,777,216&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8389em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;777&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;216&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 块&lt;/li&gt;
&lt;li&gt;总计: &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;12&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;256&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;65&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;536&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;777&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;216&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;843&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;020&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;12 + 256 + 65,536 + 16,777,216 = 16,843,020&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8389em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;536&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8389em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;777&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;216&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8389em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;843&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;020&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 个物理块。&lt;/li&gt;
&lt;li&gt;最大文件大小: &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;843&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;020&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;512&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;16,843,020 \times 512&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8389em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;843&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;020&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;512&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 字节 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\approx&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4831em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 8 GB。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;24-目录文件的实现&quot;&gt;2.4 目录文件的实现&lt;a class=&quot;anchor&quot; href=&quot;#24-目录文件的实现&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;241-目录项组织方式&quot;&gt;2.4.1 目录项组织方式&lt;a class=&quot;anchor&quot; href=&quot;#241-目录项组织方式&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;顺序表: 最简单的方式，将目录项一个接一个地存放。查找文件时需要线性扫描，效率低。&lt;/li&gt;
&lt;li&gt;散列表 (Hash Table): 根据文件名计算散列值，快速定位到目录项。解决了查找速度问题，但需要处理冲突。&lt;/li&gt;
&lt;li&gt;B+树: 很多现代文件系统（如NTFS）使用B+树来组织目录项。B+树是一种平衡树，能保证目录的增、删、查操作都有稳定且高效的性能。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;242-目录检索的加速&quot;&gt;2.4.2 目录检索的加速&lt;a class=&quot;anchor&quot; href=&quot;#242-目录检索的加速&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;问题: 如何加快目录检索？一个很大的目录文件，如果线性查找所有FCB会很慢。&lt;/p&gt;
&lt;p&gt;解决方案：目录项分解法 (UNIX i-node)
将FCB拆分为两部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;目录项: 只包含文件名和指向文件元数据的文件号 (i-node number)。&lt;/li&gt;
&lt;li&gt;i-node (索引节点): 包含文件名之外的所有其他元数据（权限、大小、物理地址等）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所有i-node集中存放在磁盘的i-node区。目录文件变得更小，因为每个条目只占几个字节。&lt;/p&gt;
&lt;p&gt;思考题: 假设一个FCB有48字节，物理块大小512字节。一个目录文件有128个目录项。分解前，符号目录项占8字节（文件名6B，文件号2B），基本目录项占42字节。计算查找一个文件的平均访盘次数。
回答:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分解前:
&lt;ul&gt;
&lt;li&gt;一个块能放 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;512&lt;/mn&gt;&lt;mn&gt;48&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{512}{48} \approx 10&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;48&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;512&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 个FCB。&lt;/li&gt;
&lt;li&gt;128个目录项需要 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;128&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;13&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{128}{10} \approx 13&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;128&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;13&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 个块。&lt;/li&gt;
&lt;li&gt;假设目录文件是连续存放的，平均查找的期望为，&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;mn&gt;13&lt;/mn&gt;&lt;/msubsup&gt;&lt;mfrac&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mn&gt;13&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mn&gt;13&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;13&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;13&lt;/mn&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;7&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\sum_{i=1}^{13} \frac{i}{13} = \frac{13 \times (13 + 1)}{2 \times 13} = 7&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.299em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop op-symbol small-op&quot; style=&quot;position:relative;top:0em&quot;&gt;∑&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.954em&quot;&gt;&lt;span style=&quot;top:-2.4003em;margin-left:0em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mrel mtight&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.2029em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;13&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2997em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8557em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;13&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.4133em;vertical-align:-0.4033em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.01em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;mbin mtight&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;13&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.485em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;mbin mtight&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mopen mtight&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;13&lt;/span&gt;&lt;span class=&quot;mbin mtight&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose mtight&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.4033em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;7&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 次访盘。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;分解后:
&lt;ul&gt;
&lt;li&gt;目录文件只存符号目录项。一个块能放 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;512&lt;/mn&gt;&lt;mn&gt;8&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;64&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{512}{8} = 64&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;8&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;512&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;64&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 个符号目录项。&lt;/li&gt;
&lt;li&gt;128个目录项需要 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;128&lt;/mn&gt;&lt;mn&gt;64&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{128}{64} = 2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;64&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;128&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 个块。&lt;/li&gt;
&lt;li&gt;i-node区存放基本目录项。一个块能放 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;512&lt;/mn&gt;&lt;mn&gt;42&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;12&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{512}{42} \approx 12&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;42&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;512&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;12&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 个i-node。&lt;/li&gt;
&lt;li&gt;128个i-node需要 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;128&lt;/mn&gt;&lt;mn&gt;12&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;11&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{128}{12} \approx 11&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;12&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;128&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;11&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 个块。&lt;/li&gt;
&lt;li&gt;查找过程：
&lt;ol&gt;
&lt;li&gt;读目录文件，找到文件名对应的i-node号。平均需要 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1.5&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{1}{2}+\frac{2}{2}=1.5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em&quot;&gt;&lt;span style=&quot;top:-2.655em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 次访盘。&lt;/li&gt;
&lt;li&gt;根据i-node号去i-node区读取i-node信息。需要1次访盘。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;总平均访盘次数：&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1.5&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;2.5&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;1.5 + 1 = 2.5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2.5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 次。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;结论: 改进后，通过先在小型的目录文件中快速定位，再精确访问i-node，显著减少了磁盘访问次数，提高了检索速度。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;25-磁盘空间管理&quot;&gt;2.5 磁盘空间管理&lt;a class=&quot;anchor&quot; href=&quot;#25-磁盘空间管理&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;文件系统需要跟踪所有空闲的磁盘块。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;位图法 (Bitmap): 用一个二进制位串来表示所有磁盘块。&lt;code&gt;1&lt;/code&gt;代表空闲，&lt;code&gt;0&lt;/code&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;code&gt;b&lt;/code&gt;，字长&lt;code&gt;w&lt;/code&gt;：位图中的字号 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i = b / w&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0269em&quot;&gt;w&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;，位号 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mspace&gt;&lt;/mspace&gt;&lt;mspace width=&quot;0.4444em&quot;&gt;&lt;/mspace&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mrow&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;m&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;o&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;d&lt;/mi&gt;&lt;/mrow&gt;&lt;mspace width=&quot;0.3333em&quot;&gt;&lt;/mspace&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;j = b \pmod w&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.854em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0572em&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;mspace allowbreak&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.4444em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathrm&quot;&gt;mod&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.3333em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0269em&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;。&lt;/li&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;li&gt;缺点: 申请/归还多个块时效率低，因为要多次读写链表。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;成组链接法 (UNIX): 空闲链表的改进版。将空闲块分组，每组的第一个块记录了该组其他块的块号以及下一组的地址。
&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;/ul&gt;
&lt;h3 id=&quot;26-内存中的数据结构-unix&quot;&gt;2.6 内存中的数据结构 (UNIX)&lt;a class=&quot;anchor&quot; href=&quot;#26-内存中的数据结构-unix&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;为了提高效率，操作系统会在内存中维护一些与文件系统相关的数据结构。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;系统级打开文件表 (System-wide Open File Table):
&lt;ul&gt;
&lt;li&gt;整个系统只有一张。&lt;/li&gt;
&lt;li&gt;当任何进程第一次打开一个文件时，该文件的i-node被从磁盘读入内存，并在此表中创建一个条目。&lt;/li&gt;
&lt;li&gt;条目内容包括i-node的拷贝、引用计数（记录有多少个进程打开了此文件）等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程级打开文件表 (Per-process Open File Table):
&lt;ul&gt;
&lt;li&gt;每个进程都有一张。&lt;/li&gt;
&lt;li&gt;当进程打开一个文件时，会在自己的这张表中创建一个条目。这个条目被称为文件描述符 (File Descriptor)。&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;当一个进程&lt;code&gt;fork&lt;/code&gt;一个子进程时，子进程会继承父进程的打开文件表。&lt;/li&gt;
&lt;li&gt;父子进程的文件描述符将指向同一个系统级打开文件表条目。&lt;/li&gt;
&lt;li&gt;这意味着它们共享同一个读写指针。如果一个进程移动了指针，另一个进程也会看到变化。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I/O重定向 (&lt;code&gt;dup2&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dup2(oldfd, newfd)&lt;/code&gt;系统调用可以使&lt;code&gt;newfd&lt;/code&gt;指向&lt;code&gt;oldfd&lt;/code&gt;所指向的同一个系统级文件表条目。&lt;/li&gt;
&lt;li&gt;这使得我们可以将标准输出（&lt;code&gt;fd=1&lt;/code&gt;）重定向到一个文件，从而实现命令输出到文件的功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    direction LR
    subgraph &amp;quot;进程A&amp;quot;
        A_Tbl[&amp;quot;描述符表&amp;quot;]
        A_Tbl -- &amp;quot;fd 0 (stdin)&amp;quot; --&amp;gt; OF_Term
        A_Tbl -- &amp;quot;fd 1 (stdout)&amp;quot; --&amp;gt; OF_Term
        A_Tbl -- &amp;quot;fd 4&amp;quot; --&amp;gt; OF_FileB
    end

    subgraph &amp;quot;内核&amp;quot;
        OF_Term[&amp;quot;系统打开文件表项 (终端) \n pos, refcnt=2&amp;quot;]
        OF_FileB[&amp;quot;系统打开文件表项 (文件B) \n pos, refcnt=1&amp;quot;]

        OF_Term --&amp;gt; V_Term[&amp;quot;v-node表项 (终端信息)&amp;quot;]
        OF_FileB --&amp;gt; V_FileB[&amp;quot;v-node表项 (文件B信息)&amp;quot;]
    end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;27-文件操作的实现&quot;&gt;2.7 文件操作的实现&lt;a class=&quot;anchor&quot; href=&quot;#27-文件操作的实现&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create(文件名, ...)&lt;/code&gt;:
&lt;ol&gt;
&lt;li&gt;在目录中查找，确保没有重名文件。&lt;/li&gt;
&lt;li&gt;申请一个新的i-node。&lt;/li&gt;
&lt;li&gt;在目录文件中增加一个新条目（文件名, i-node号）。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;open(文件名, ...)&lt;/code&gt;:
&lt;ol&gt;
&lt;li&gt;解析路径名，找到文件的i-node号。&lt;/li&gt;
&lt;li&gt;检查系统打开文件表，看文件是否已被其他进程打开。
&lt;ul&gt;
&lt;li&gt;是：引用计数加1。&lt;/li&gt;
&lt;li&gt;否：从磁盘读入i-node到系统打开文件表的新条目中，引用计数置为1。&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;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read(fd, ...)&lt;/code&gt;:
&lt;ol&gt;
&lt;li&gt;通过&lt;code&gt;fd&lt;/code&gt;在进程和系统打开文件表中找到文件的i-node。&lt;/li&gt;
&lt;li&gt;根据文件描述符中的读写指针和i-node中的物理地址信息，计算出要读取的物理块号。&lt;/li&gt;
&lt;li&gt;启动磁盘I/O操作，将数据从磁盘读入内核缓冲区，再从内核缓冲区复制到用户指定的内存地址。&lt;/li&gt;
&lt;li&gt;更新读写指针。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seek(fd, ...)&lt;/code&gt;:
&lt;ol&gt;
&lt;li&gt;通过&lt;code&gt;fd&lt;/code&gt;找到进程打开文件表中的条目。&lt;/li&gt;
&lt;li&gt;修改其中的读写指针值。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;28-文件共享&quot;&gt;2.8 文件共享&lt;a class=&quot;anchor&quot; href=&quot;#28-文件共享&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;硬链接 (Hard Link):
&lt;ul&gt;
&lt;li&gt;多个目录项（可以在不同目录，有不同文件名）指向同一个i-node。&lt;/li&gt;
&lt;li&gt;i-node中有一个链接计数 (link count)，记录有多少个目录项指向它。&lt;/li&gt;
&lt;li&gt;创建链接时，计数加1。&lt;/li&gt;
&lt;li&gt;删除文件（即删除一个目录项）时，计数减1。&lt;/li&gt;
&lt;li&gt;只有当链接计数减为0时，操作系统才会真正删除i-node并回收数据块。&lt;/li&gt;
&lt;li&gt;所有硬链接是平等的，没有主次之分。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;软链接/符号链接 (Soft/Symbolic Link):
&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 id=&quot;3-文件系统实例&quot;&gt;3. 文件系统实例&lt;a class=&quot;anchor&quot; href=&quot;#3-文件系统实例&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-unix-文件系统&quot;&gt;3.1 UNIX 文件系统&lt;a class=&quot;anchor&quot; href=&quot;#31-unix-文件系统&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;核心: i-node。&lt;/li&gt;
&lt;li&gt;结构: 目录项（文件名+i-node号）和i-node（元数据）分离。&lt;/li&gt;
&lt;li&gt;查找示例 &lt;code&gt;/usr/ast/mbox&lt;/code&gt;:
&lt;ol&gt;
&lt;li&gt;内核从根目录（i-node号已知，通常是2）开始。&lt;/li&gt;
&lt;li&gt;读取根目录的数据块，查找名为 &lt;code&gt;usr&lt;/code&gt; 的条目，得到其i-node号（例如 7）。&lt;/li&gt;
&lt;li&gt;读取i-node 7，找到 &lt;code&gt;usr&lt;/code&gt; 目录的数据块地址。&lt;/li&gt;
&lt;li&gt;读取 &lt;code&gt;usr&lt;/code&gt; 目录的数据块，查找名为 &lt;code&gt;ast&lt;/code&gt; 的条目，得到其i-node号（例如 62）。&lt;/li&gt;
&lt;li&gt;读取i-node 62，找到 &lt;code&gt;ast&lt;/code&gt; 目录的数据块地址。&lt;/li&gt;
&lt;li&gt;读取 &lt;code&gt;ast&lt;/code&gt; 目录的数据块，查找名为 &lt;code&gt;mbox&lt;/code&gt; 的条目，得到其i-node号（例如 80）。&lt;/li&gt;
&lt;li&gt;现在内核有了 &lt;code&gt;mbox&lt;/code&gt; 文件的i-node 80，就可以进行读写等操作了。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-fat-file-allocation-table-文件系统&quot;&gt;3.2 FAT (File Allocation Table) 文件系统&lt;a class=&quot;anchor&quot; href=&quot;#32-fat-file-allocation-table-文件系统&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;核心: 文件分配表 (FAT)。&lt;/li&gt;
&lt;li&gt;磁盘布局:
&lt;ul&gt;
&lt;li&gt;DBR (DOS Boot Record): 引导扇区，包含BPB（BIOS参数块），描述了文件系统的各种参数（每扇区字节数、每簇扇区数等）。&lt;/li&gt;
&lt;li&gt;FAT表: 紧跟在DBR之后。通常有两份作为备份。它是一个大数组，记录了簇（块）的链接关系和状态（未使用、坏簇、文件结束标志等）。&lt;/li&gt;
&lt;li&gt;根目录区: 在FAT12/16中，根目录大小和位置是固定的。在FAT32中，根目录是可扩展的，像普通文件一样存储。&lt;/li&gt;
&lt;li&gt;数据区: 存放文件和子目录。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;目录项:
&lt;ul&gt;
&lt;li&gt;32字节定长结构。&lt;/li&gt;
&lt;li&gt;包含文件名（8.3格式）、属性、创建/修改时间、文件大小和起始簇号。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;文件读取:
&lt;ol&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;获取起始簇号: 从目录项中获取文件的起始簇号。目录项是32字节的定长结构，其中第26-27字节（低16位）和第20-21字节（高16位，仅FAT32）存储了文件的起始簇号。对于FAT12/16，只使用低16位；对于FAT32，需要组合高低16位得到完整的32位簇号。&lt;/li&gt;
&lt;li&gt;以该簇号为索引，在内存中的FAT表副本中查找，得到下一簇的簇号。&lt;/li&gt;
&lt;li&gt;重复此过程，直到遇到文件结束标志。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;长文件名 (FAT32):
&lt;ul&gt;
&lt;li&gt;对于长文件名，会创建多个特殊的长文件名目录项 (LFN entry)。&lt;/li&gt;
&lt;li&gt;每个LFN项存储长文件名的一部分（Unicode编码）。&lt;/li&gt;
&lt;li&gt;这些LFN项紧挨在真正的8.3格式短文件名目录项之前。&lt;/li&gt;
&lt;li&gt;不识别长文件名的旧系统会忽略这些LFN项（因为它们的属性被设为卷标、系统、隐藏、只读的组合，旧系统不认识），只看到短文件名项。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;33-ntfs-new-technology-file-system&quot;&gt;3.3 NTFS (New Technology File System)&lt;a class=&quot;anchor&quot; href=&quot;#33-ntfs-new-technology-file-system&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Windows NT/XP及之后版本使用的文件系统。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设计目标: 可靠性、高效性、安全性。&lt;/li&gt;
&lt;li&gt;核心: 主控文件表 (Master File Table, MFT)。&lt;/li&gt;
&lt;li&gt;MFT: MFT本身也是一个文件。它是一个由文件记录 (File Record) 组成的数组。卷上的每一个文件或目录都至少对应MFT中的一个记录。&lt;/li&gt;
&lt;li&gt;万物皆属性: 在NTFS中，所有数据都被看作是文件的属性 (Attribute)。我们通常所说的文件内容，只是一个未命名的“数据属性流”。文件名、时间戳、权限等都是属性。&lt;/li&gt;
&lt;li&gt;文件记录:
&lt;ul&gt;
&lt;li&gt;常驻属性 (Resident Attribute): 对于小文件或小属性，其内容直接存储在MFT记录中。&lt;/li&gt;
&lt;li&gt;驻属性 (Non-resident Attribute): 对于大文件或大属性，其内容存储在MFT之外的数据区。MFT记录中只存放指向这些数据块的指针，称为run或extent。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;目录: 目录也是一个文件，其“数据属性”是一个文件名索引。为了提高效率，这个索引通常用B+树来组织。&lt;/li&gt;
&lt;li&gt;可靠性: 通过日志记录 (Logging) 实现。对文件系统的所有修改操作，在实际写入磁盘前，会先在日志文件（&lt;code&gt;$LogFile&lt;/code&gt;）中记录。如果系统崩溃，重启后可以通过重做或撤销日志中的事务来恢复文件系统的一致性。&lt;/li&gt;
&lt;li&gt;元数据文件: MFT的前16个记录被保留给系统元数据文件使用，如&lt;code&gt;$Mft&lt;/code&gt;(MFT自身)、&lt;code&gt;$MftMirr&lt;/code&gt;(MFT镜像)、&lt;code&gt;$LogFile&lt;/code&gt;(日志文件)、&lt;code&gt;$Volume&lt;/code&gt;(卷信息)、&lt;code&gt;$.&lt;/code&gt;(根目录)等。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;4-其他文件系统和技术&quot;&gt;4. 其他文件系统和技术&lt;a class=&quot;anchor&quot; href=&quot;#4-其他文件系统和技术&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ext4: Linux默认的文件系统，是ext3的改进。使用extents（连续块区段）代替传统的块映射，减少大文件碎片；支持延迟分配，优化写入性能。&lt;/li&gt;
&lt;li&gt;VFS (Virtual File System): 一个内核抽象层，为用户程序提供统一的文件系统接口（如&lt;code&gt;open&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;）。VFS可以将这些通用请求转换为底层具体文件系统（如ext4, FAT, NTFS）的特定操作。这使得Linux可以同时支持多种不同的文件系统。&lt;/li&gt;
&lt;li&gt;NFS (Network File System): 一种分布式文件系统协议，允许客户端计算机像访问本地文件一样通过网络访问远程服务器上的文件。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 06: Virtual Memory Technology</title><link>https://www.lyt0112.com/blog/operating_systems_note_06-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_06-zh</guid><description>Operating Systems Notes 06: Virtual Memory Technology</description><pubDate>Fri, 11 Apr 2025 01:36:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-虚拟内存基础概念&quot;&gt;1. 虚拟内存基础概念&lt;a class=&quot;anchor&quot; href=&quot;#1-虚拟内存基础概念&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;11-虚拟地址空间-virtual-address-space&quot;&gt;1.1 虚拟地址空间 (Virtual Address Space)&lt;a class=&quot;anchor&quot; href=&quot;#11-虚拟地址空间-virtual-address-space&quot;&gt;#&lt;/a&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;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;提问：CPU取到的地址是什么地址？物理地址还是虚拟地址？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解答: CPU发出的地址通常是 虚拟地址 (Virtual Address)。这个虚拟地址随后会被 内存管理单元 (MMU) 转换为物理地址 (Physical Address)。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph LR
    CPU -- Virtual Address --&amp;gt; MMU;
    MMU -- Physical Address --&amp;gt; PhysicalMemory[物理内存];
    MMU -- Page Fault --&amp;gt; OS[操作系统];
    OS -- Data from Disk --&amp;gt; PhysicalMemory;
    PhysicalMemory &amp;lt;--&amp;gt; Disk[磁盘];&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;12-虚拟内存管理的目标&quot;&gt;1.2 虚拟内存管理的目标&lt;a class=&quot;anchor&quot; href=&quot;#12-虚拟内存管理的目标&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;透明性 (Transparency): 运行的程序不应感知到虚拟内存机制的存在。程序员可以像操作一个巨大的连续内存一样编程。&lt;/li&gt;
&lt;li&gt;效率 (Efficiency): 地址转换和页面调度应尽可能快，减少性能开销。需要 硬件支持 (如MMU, TLB)。&lt;/li&gt;
&lt;li&gt;保护 (Protection): 确保进程之间、进程与操作系统之间相互隔离，互不干扰。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;13-存储体系-memory-hierarchy&quot;&gt;1.3 存储体系 (Memory Hierarchy)&lt;a class=&quot;anchor&quot; href=&quot;#13-存储体系-memory-hierarchy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;结构: 寄存器 -&amp;gt; Cache -&amp;gt; 内存 (RAM) -&amp;gt; 磁盘 (Disk)&lt;/li&gt;
&lt;li&gt;操作系统角色: 协调各级存储器的使用。&lt;/li&gt;
&lt;li&gt;目标: 结合速度快但容量小的存储 (如Cache, RAM) 和速度慢但容量大的存储 (如磁盘) ，为用户提供一个既“快”又“大”的逻辑内存 (虚存) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;14-相关术语辨识&quot;&gt;1.4 相关术语辨识&lt;a class=&quot;anchor&quot; href=&quot;#14-相关术语辨识&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;虚拟内存 (Virtual Memory):
&lt;ul&gt;
&lt;li&gt;解释: 将物理内存与磁盘结合使用，为程序提供一个容量远大于物理内存的逻辑存储空间。&lt;/li&gt;
&lt;li&gt;关键: 程序引用的地址 (虚拟地址) 与物理内存地址不同，由系统自动转换。虚存大小受限于计算机寻址能力和可用磁盘空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;虚拟地址空间 (Virtual Address Space):
&lt;ul&gt;
&lt;li&gt;解释: 分配给一个进程的逻辑地址范围。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;虚拟地址 (Virtual Address):
&lt;ul&gt;
&lt;li&gt;解释: 虚拟地址空间中的某个地址。进程通过虚拟地址访问数据，仿佛它就在内存中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;虚拟存储技术 (Virtual Memory Technology):
&lt;ul&gt;
&lt;li&gt;解释: 一种内存管理技术。程序运行时，只将其一部分装入内存，其余部分留在磁盘。当需要访问不在内存中的部分时，操作系统自动将其从磁盘调入内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;2-虚拟页式存储管理-paged-virtual-memory&quot;&gt;2. 虚拟页式存储管理 (Paged Virtual Memory)&lt;a class=&quot;anchor&quot; href=&quot;#2-虚拟页式存储管理-paged-virtual-memory&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;21-基本思想&quot;&gt;2.1 基本思想&lt;a class=&quot;anchor&quot; href=&quot;#21-基本思想&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;按需加载: 装载程序时，只装入部分 (甚至零个) 页面到物理内存。&lt;/li&gt;
&lt;li&gt;动态调页: 当进程执行需要访问不在内存中的页面时，产生 页错误 (Page Fault)，操作系统负责将所需页面从磁盘动态调入内存。&lt;/li&gt;
&lt;li&gt;页面换出: 当内存不足时，将内存中暂时不用的页面交换 (写回) 到磁盘，以腾出空间。&lt;/li&gt;
&lt;li&gt;实现方式:
&lt;ul&gt;
&lt;li&gt;请求调页 (Demand Paging): 只有当页面被访问时才调入。 (最常用)&lt;/li&gt;
&lt;li&gt;预先调页 (Prepaging): 预测进程可能需要的页面并提前调入。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;本质: 资源转换技术，用CPU时间和磁盘空间换取 (看似无限的) 物理内存空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;22-核心策略-coffman--denning&quot;&gt;2.2 核心策略 (Coffman &amp;amp; Denning)&lt;a class=&quot;anchor&quot; href=&quot;#22-核心策略-coffman--denning&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;取页策略 (Fetch Policy): 决定何时将页面从磁盘调入内存。
&lt;ul&gt;
&lt;li&gt;请求调页: 发生缺页时才调入。&lt;/li&gt;
&lt;li&gt;预调页: 预测并提前调入。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;放置策略 (Placement Policy): 决定将调入的页面放置在物理内存的哪个 页框 (Page Frame) 中。
&lt;ul&gt;
&lt;li&gt;解释: 在分页系统中，任何空闲页框都可以存放任何页面，所以此策略相对简单。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;置换策略 (Replacement Policy): 当内存没有空闲页框时，决定选择哪个页框中的页面换出到磁盘。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;23-设计与实现问题&quot;&gt;2.3 设计与实现问题&lt;a class=&quot;anchor&quot; href=&quot;#23-设计与实现问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;页表表项 (PTE) 的设计。&lt;/li&gt;
&lt;li&gt;如何处理页表过大的问题 (如多级页表) 。&lt;/li&gt;
&lt;li&gt;地址重定位与快表 (TLB)。&lt;/li&gt;
&lt;li&gt;缺页异常 (Page Fault) 的处理机制。&lt;/li&gt;
&lt;li&gt;驻留集 (Resident Set) 管理。&lt;/li&gt;
&lt;li&gt;置换策略 (Replacement Algorithms)。&lt;/li&gt;
&lt;li&gt;清除策略 (Cleaning Policy)。&lt;/li&gt;
&lt;li&gt;加载控制 (Load Control)。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-硬件支持与核心机制&quot;&gt;3. 硬件支持与核心机制&lt;a class=&quot;anchor&quot; href=&quot;#3-硬件支持与核心机制&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-页表表项-page-table-entry---pte-设计&quot;&gt;3.1 页表表项 (Page Table Entry - PTE) 设计&lt;a class=&quot;anchor&quot; href=&quot;#31-页表表项-page-table-entry---pte-设计&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;关键字段:
&lt;ul&gt;
&lt;li&gt;页框号 (Page Frame Number - PFN): 该虚拟页对应的物理内存块号。&lt;/li&gt;
&lt;li&gt;有效位/驻留位 (Valid/Present Bit - P): 标记该页是否在物理内存中 (1=在内存, 0=不在内存/在磁盘)。&lt;/li&gt;
&lt;li&gt;访问位/引用位 (Accessed/Referenced Bit - A/R): 标记该页近期是否被访问过 (硬件在访问时设置，OS定期清零)。用于置换算法。&lt;/li&gt;
&lt;li&gt;修改位/脏位 (Dirty/Modified Bit - D/M): 标记该页在内存中是否被修改过 (硬件在写入时设置)。如果为1，换出时必须写回磁盘。&lt;/li&gt;
&lt;li&gt;保护位 (Protection Bits - R/W/X): 控制对该页的访问权限 (读/写/执行) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;i386 PDE/PTE 示例: (展示了具体位域)
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;P&lt;/code&gt; (Present), &lt;code&gt;A&lt;/code&gt; (Accessed), &lt;code&gt;D&lt;/code&gt; (Dirty), &lt;code&gt;R/W&lt;/code&gt; (Read/Write), &lt;code&gt;U/S&lt;/code&gt; (User/Supervisor), &lt;code&gt;PWT&lt;/code&gt; (Page Write Through), &lt;code&gt;PCD&lt;/code&gt; (Page Cache Disable), &lt;code&gt;PS&lt;/code&gt; (Page Size - for large pages).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-处理页表过大的问题&quot;&gt;3.2 处理页表过大的问题&lt;a class=&quot;anchor&quot; href=&quot;#32-处理页表过大的问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;问题:
&lt;ul&gt;
&lt;li&gt;32位地址空间 (4KB页面, 4B PTE): 页表本身占用内存 &lt;code&gt;2^20 个 PTE * 4B = 4MB = 2^22 = 1024 个 4KB 页面&lt;/code&gt; 的空间。&lt;/li&gt;
&lt;li&gt;若用户拥有 &lt;code&gt;2G = 2^31 = 2^19 个 4KB 页面&lt;/code&gt; 的物理空间，索引这块内存的有效 (有效位 P=1)的页表就占 512 页 (&lt;code&gt;2^19 * 4B / 4KB = 512&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;64位地址空间: 页表大小会变得极其巨大 (理论上 &lt;code&gt;2^52 * 8B&lt;/code&gt;，不可行)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;解决方案:
&lt;ol&gt;
&lt;li&gt;多级页表 (Multi-Level Page Tables):
&lt;ul&gt;
&lt;li&gt;思想: 将巨大的线性页表变成树形结构。外层页表 (页目录) 的条目指向内层页表。只有被用到的内层页表才需要分配内存。&lt;/li&gt;
&lt;li&gt;二级页表示例: 虚拟地址分为 &lt;code&gt;页目录偏移 | 页表偏移 | 页内偏移&lt;/code&gt;。CR3寄存器指向页目录基址 -&amp;gt; 查页目录得页表基址 -&amp;gt; 查页表得页框号 -&amp;gt; 拼接页内偏移得物理地址。&lt;/li&gt;
&lt;li&gt;Core i7 示例 (四级页表): 48位虚拟地址，分为 &lt;code&gt;9 | 9 | 9 | 9 | 12&lt;/code&gt; 位，对应四级页表的索引和页内偏移。&lt;/li&gt;
&lt;li&gt;优点: 节省空间，只有实际使用的页表部分才需载入内存。虽然理论上总页表项数量不变，但实际上大多数进程只使用地址空间的一小部分。&lt;/li&gt;
&lt;li&gt;空间节省示例:
&lt;ul&gt;
&lt;li&gt;在32位系统中 (4GB地址空间) ，使用4KB页面，线性页表需要1M个页表项 (32位虚拟地址空间都需要对应的页表项) 。&lt;/li&gt;
&lt;li&gt;假设一个进程只使用了4MB的连续内存 (位于0x80000000-0x80400000) ，在二级页表中：
&lt;ul&gt;
&lt;li&gt;需要1个完整的页目录 (1024项，4KB)&lt;/li&gt;
&lt;li&gt;只需要1个二级页表 (1024项，4KB，对应使用的4MB区域)&lt;/li&gt;
&lt;li&gt;其余1023个二级页表 (对应未使用的地址空间) 根本不需要创建&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;总计只需8KB内存，而不是线性页表的4MB，节省了约99.8%的空间&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;缺点: 每次地址翻译需要多次内存访问 (可通过TLB缓解) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;反转页表 (Inverted Page Table):
&lt;ul&gt;
&lt;li&gt;思想: 不再为每个进程维护一个页表，而是为整个物理内存建立一个全局页表。页表项 &lt;code&gt;i&lt;/code&gt; 对应物理页框 &lt;code&gt;i&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;内容: 每个页表项记录 &lt;code&gt;(进程ID, 虚拟页号)&lt;/code&gt;，表示哪个进程的哪个虚拟页映射到了这个物理页框。&lt;/li&gt;
&lt;li&gt;地址转换: 给定 &lt;code&gt;(进程ID, 虚拟页号)&lt;/code&gt;，需要搜索整个反转页表找到匹配项，得到其索引 (即物理页框号) 。&lt;/li&gt;
&lt;li&gt;优化: 使用哈希表 (Hash Table) 加速查找。将 &lt;code&gt;(进程ID, 虚拟页号)&lt;/code&gt; 哈希到一个索引，指向反转页表中的一个桶 (可能需要链表解决冲突) 。&lt;/li&gt;
&lt;li&gt;优点: 页表大小与物理内存大小成正比，与虚拟地址空间大小和进程数量无关。&lt;/li&gt;
&lt;li&gt;缺点: 查找可能较慢 (即使有哈希) ，实现共享比较困难。&lt;/li&gt;
&lt;li&gt;应用: PowerPC, UltraSPARC, IA-64 等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;33-内存管理单元-mmu&quot;&gt;3.3 内存管理单元 (MMU)&lt;a class=&quot;anchor&quot; href=&quot;#33-内存管理单元-mmu&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定义: CPU中的硬件单元，负责将虚拟地址转换为物理地址。&lt;/li&gt;
&lt;li&gt;过程: 接收CPU发出的虚拟地址，查询页表 (优先查TLB) ，生成物理地址或触发缺页异常。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;34-地址转换-address-translation&quot;&gt;3.4 地址转换 (Address Translation)&lt;a class=&quot;anchor&quot; href=&quot;#34-地址转换-address-translation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;硬件机制:
&lt;ol&gt;
&lt;li&gt;CPU发出虚拟地址。&lt;/li&gt;
&lt;li&gt;MMU 从虚拟地址中提取 虚拟页号 (VPN) 和 页内偏移 (Offset)。&lt;/li&gt;
&lt;li&gt;MMU 使用 VPN (可能结合多级页表结构) 查找页表 (先查TLB) 。&lt;/li&gt;
&lt;li&gt;检查 PTE:
&lt;ul&gt;
&lt;li&gt;Case 1: PTE 有效 (Valid/Present bit = 1) 且权限允许:
&lt;ul&gt;
&lt;li&gt;从 PTE 中获取 页框号 (PFN)。&lt;/li&gt;
&lt;li&gt;将 PFN 与页内偏移拼接，形成 物理地址。&lt;/li&gt;
&lt;li&gt;访问内存。&lt;/li&gt;
&lt;li&gt;硬件根据访问类型 (读/写) 可能设置 访问位 (A) 或 修改位 (D)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Case 2: PTE 无效 (Valid/Present bit = 0) 或权限不足:
&lt;ul&gt;
&lt;li&gt;MMU 产生 页错误 (Page Fault) 异常，将控制权交给操作系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;页错误处理 (OS): (详见 3.6)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;35-快表-translation-look-aside-buffer---tlb&quot;&gt;3.5 快表 (Translation Look-Aside Buffer - TLB)&lt;a class=&quot;anchor&quot; href=&quot;#35-快表-translation-look-aside-buffer---tlb&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;问题: 多级页表导致每次地址翻译需要多次内存访问，显著降低性能。&lt;/li&gt;
&lt;li&gt;原理: 利用 程序访问的局部性原理 (Locality of Reference)。最近访问过的页面很可能再次被访问。&lt;/li&gt;
&lt;li&gt;什么是TLB:
&lt;ul&gt;
&lt;li&gt;一种高速的、容量小的 相联存储器 (Associative Memory)。&lt;/li&gt;
&lt;li&gt;特点：按 内容 并行查找，速度极快。&lt;/li&gt;
&lt;li&gt;存储内容：缓存近期使用过的 虚拟页号 (VPN) 到 页框号 (PFN) 的映射 (即部分活跃的页表项)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;工作流程:
&lt;ol&gt;
&lt;li&gt;MMU 收到虚拟地址后，首先并行查找 TLB。&lt;/li&gt;
&lt;li&gt;TLB Hit (命中): 如果在 TLB 中找到匹配的 VPN，直接获取 PFN，快速完成地址转换。跳过页表查找。&lt;/li&gt;
&lt;li&gt;TLB Miss (未命中):
&lt;ul&gt;
&lt;li&gt;MMU 需要访问内存中的页表进行查找。&lt;/li&gt;
&lt;li&gt;找到 PFN 后，将 (VPN -&amp;gt; PFN) 的映射关系装入 TLB (可能需要替换掉 TLB 中的一个旧条目，使用LRU等策略)。&lt;/li&gt;
&lt;li&gt;完成地址转换。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;TLB 刷新问题:
&lt;ul&gt;
&lt;li&gt;问题: 进程切换时，原进程的 TLB 条目对新进程无效，需要刷新 TLB，导致新进程初期 TLB Miss 增多，性能下降。&lt;/li&gt;
&lt;li&gt;解决:
&lt;ul&gt;
&lt;li&gt;PCID (Process Context Identifier) / ASID (Address Space Identifier): 给 TLB 条目打上进程标识符。切换进程时，只需加载新进程的 PCID/ASID，TLB 中带有不同 ID 的条目不会被匹配，无需完全刷新。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;关键参数: TLB 的大小、位置 (通常集成在MMU或CPU核心内) 、替换策略。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;36-缺页异常-page-fault-处理&quot;&gt;3.6 缺页异常 (Page Fault) 处理&lt;a class=&quot;anchor&quot; href=&quot;#36-缺页异常-page-fault-处理&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;触发: 地址转换过程中，MMU 发现所需页面的 PTE 无效 (P=0) 或访问权限不足。&lt;/li&gt;
&lt;li&gt;本质: 一种硬件中断/异常，将控制权交给操作系统内核的 缺页异常处理程序 (Page Fault Handler)。&lt;/li&gt;
&lt;li&gt;处理流程 (典型情况 - 页面不在内存):
&lt;ol&gt;
&lt;li&gt;保存现场: 保存用户进程的状态 (PC, 寄存器等) 。&lt;/li&gt;
&lt;li&gt;确定原因: 操作系统分析是真缺页 (P=0) ，还是保护性错误 (权限不足) 。如果是后者，可能终止进程。&lt;/li&gt;
&lt;li&gt;定位磁盘地址: 如果是真缺页，查找该虚拟页在磁盘 (交换空间或文件) 上的位置。&lt;/li&gt;
&lt;li&gt;查找空闲页框: 在物理内存中寻找一个空闲的页框。&lt;/li&gt;
&lt;li&gt;处理无空闲页框:
&lt;ul&gt;
&lt;li&gt;若无空闲页框: 执行 页面置换算法，选择一个牺牲页框 (Victim Frame)。&lt;/li&gt;
&lt;li&gt;写回脏页: 如果牺牲页框中的页面是 “脏” 的 (D=1)，则需要将其内容 写回磁盘。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;调入页面: 启动磁盘 I/O 操作，将所需的页面从磁盘读入选定的 (空闲或牺牲) 页框。&lt;/li&gt;
&lt;li&gt;更新页表: 页面调入完成后，修改该虚拟页对应的 PTE (Page Table Entry，页表项)：设置 P=1，填入 PFN，清除 D 位，可能设置 A 位。&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;h3 id=&quot;37-驻留集管理-resident-set-management&quot;&gt;3.7 驻留集管理 (Resident Set Management)&lt;a class=&quot;anchor&quot; href=&quot;#37-驻留集管理-resident-set-management&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;驻留集 (Resident Set): 进程当前在物理内存中的页面集合。&lt;/li&gt;
&lt;li&gt;驻留集大小管理: 决定给每个进程分配多少页框。
&lt;ul&gt;
&lt;li&gt;固定分配策略 (Fixed Allocation):
&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;可变分配策略 (Variable Allocation):
&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;/li&gt;
&lt;li&gt;缺点: 实现复杂，需要监控缺页率，调整策略可能引入系统开销。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;hr/&gt;
&lt;h2 id=&quot;4-页面置换算法-page-replacement-algorithms&quot;&gt;4. 页面置换算法 (Page Replacement Algorithms)&lt;a class=&quot;anchor&quot; href=&quot;#4-页面置换算法-page-replacement-algorithms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;41-置换问题&quot;&gt;4.1 置换问题&lt;a class=&quot;anchor&quot; href=&quot;#41-置换问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;背景: 当发生缺页异常且没有空闲物理页框时，需要选择一个当前在内存中的页面换出，为新页面腾出空间。&lt;/li&gt;
&lt;li&gt;目标: 选择一个 最近最不可能被访问 的页面进行置换，以最小化未来的缺页次数。&lt;/li&gt;
&lt;li&gt;约束: 不能置换被 锁定 (Locked/Pinned) 的页框 (如内核代码、I/O 缓冲区等) 。
&lt;ul&gt;
&lt;li&gt;页框锁定: 通过 PTE 中的锁定位或特殊机制，防止 OS 将某些关键页面换出内存，避免 I/O 操作期间页面被换出导致错误，或保证实时任务的响应时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;42-置换范围&quot;&gt;4.2 置换范围&lt;a class=&quot;anchor&quot; href=&quot;#42-置换范围&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;局部置换策略 (Local Replacement): 仅在引发缺页的那个进程自己的 驻留集 (Resident Set) 中选择牺牲页。&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;全局置换策略 (Global Replacement): 可以在内存中所有未锁定的页框中选择牺牲页，无论它属于哪个进程。&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;ul&gt;
&lt;li&gt;固定分配通常配合局部置换。&lt;/li&gt;
&lt;li&gt;可变分配可以配合局部或全局置换。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;43-典型置换算法&quot;&gt;4.3 典型置换算法&lt;a class=&quot;anchor&quot; href=&quot;#43-典型置换算法&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;最优置换算法 (Optimal - OPT / MIN):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;思想: 置换 未来最长时间内不会被访问 的页面。&lt;/li&gt;
&lt;li&gt;实现: 无法实现，因为需要预知未来。&lt;/li&gt;
&lt;li&gt;作用: 作为性能比较的 基准 (Benchmark)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;先进先出算法 (First-In, First-Out - FIFO):&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;缺点: 性能较差，可能换出常用页面。存在 Belady 异常。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第二次机会算法 (Second Chance - SCR):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;思想: FIFO 的改进。检查队首页面的 访问位 (A/R)。&lt;/li&gt;
&lt;li&gt;流程:
&lt;ul&gt;
&lt;li&gt;检查队首页面 PTE 的 A 位。&lt;/li&gt;
&lt;li&gt;如果 A=0，置换该页。&lt;/li&gt;
&lt;li&gt;如果 A=1，给它 “第二次机会”：将 A 位清零，并将该页移到队尾，然后检查下一个队首页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优点: 比 FIFO 好，避免了立即换出刚被访问过的页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;时钟算法 (Clock):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;思想: Second Chance Replacement (SCR，第二次机会算法) 的高效实现，避免了频繁移动页面。&lt;/li&gt;
&lt;li&gt;实现: 将所有物理页框组织成一个 循环链表 (缓冲区)，用一个指针指向下一个要检查的候选页框。&lt;/li&gt;
&lt;li&gt;流程:
&lt;ul&gt;
&lt;li&gt;发生缺页时，从指针当前位置开始扫描。&lt;/li&gt;
&lt;li&gt;检查当前页框 PTE 的 A 位。&lt;/li&gt;
&lt;li&gt;如果 A=0，选择该页框进行置换，将新页面放入，指针前移。&lt;/li&gt;
&lt;li&gt;如果 A=1，将 A 位清零，指针前移，继续检查下一个页框。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优点: 实现相对简单，性能优于 FIFO，接近 LRU。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最近未使用算法 (Not Recently Used - NRU):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;思想: 优先淘汰近期 既未被访问 (A=0) 也未被修改 (D=0) 的页面。&lt;/li&gt;
&lt;li&gt;实现: 利用 PTE 中的 访问位 (A) 和 修改位 (D) 将页面分为四类：
&lt;ul&gt;
&lt;li&gt;第 0 类: (A=0, D=0) - 未访问，未修改&lt;/li&gt;
&lt;li&gt;第 1 类: (A=0, D=1) - 未访问，已修改&lt;/li&gt;
&lt;li&gt;第 2 类: (A=1, D=0) - 已访问，未修改&lt;/li&gt;
&lt;li&gt;第 3 类: (A=1, D=1) - 已访问，已修改&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;流程: OS 定期将所有页面的 A 位清零。发生缺页时，随机 从编号最小的非空类别中选择一个页面进行置换。&lt;/li&gt;
&lt;li&gt;优点: 实现简单，性能尚可。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NRU 的时钟实现: (一种变体)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;扫描 1: 找第一个 (A=0, D=0) 的页框，找到即置换。此过程不清 A 位。&lt;/li&gt;
&lt;li&gt;扫描 2 (若扫描 1 失败): 重新扫描，找第一个 (A=0, D=1) 的页框。此过程中，跳过的页框 (A=1) 的 A 位被清零。找到即置换。&lt;/li&gt;
&lt;li&gt;扫描 3 (若扫描 2 失败): 此时所有页框 A 位都为 0。重复扫描 1 (必然能找到 A=0, D=0 或 A=0, D=1) ，然后如有必要重复扫描 2。&lt;/li&gt;
&lt;li&gt;特点: 优先换出干净页 (D=0)，节省写回磁盘的时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最近最少使用算法 (Least Recently Used - LRU):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;思想: 置换 过去最长时间未被访问 的页面。基于局部性原理，认为最久未用的页面，近期也最不可能被使用。&lt;/li&gt;
&lt;li&gt;实现:
&lt;ul&gt;
&lt;li&gt;时间戳法: 每个 PTE 记录上次访问时间，置换时间戳最小的。 (硬件开销大)&lt;/li&gt;
&lt;li&gt;栈/链表法: 维护一个按访问时间排序的页面栈/链表，每次访问将页面移到栈顶/链表头，置换栈底/链表尾的页面。 (软件开销大)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优点: 性能好，接近 OPT。&lt;/li&gt;
&lt;li&gt;缺点: 实现开销大，纯硬件或纯软件实现都困难。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最不经常使用算法 (Not Frequently Used - NFU):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;思想: 置换 过去访问次数最少 的页面。LRU 的一种软件近似。&lt;/li&gt;
&lt;li&gt;实现: 每个 PTE 关联一个软件计数器，初值为 0。每次时钟中断，检查 A 位，若 A=1，则对应计数器加 1，并将 A 位清零。缺页时置换计数值最小的页面。&lt;/li&gt;
&lt;li&gt;缺点: 不能很好地区分早期频繁访问但近期不用的页面和近期才开始访问的页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;老化算法 (Aging):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;思想: 模拟 LRU。改进 NFU，使计数器能反映访问的时间远近。&lt;/li&gt;
&lt;li&gt;实现: 每个 PTE 关联一个多位计数器 (e.g., 8-bit)。每次时钟中断：
&lt;ol&gt;
&lt;li&gt;将每个计数器 右移 1 位 (模拟时间流逝，旧的访问权重降低)。&lt;/li&gt;
&lt;li&gt;将当前 PTE 的 A 位 加到计数器的 最左边 (最高位)。&lt;/li&gt;
&lt;li&gt;将 A 位清零。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;缺页时，置换计数值最小的页面。计数值小的页面表示近期访问较少或很久未访问。&lt;/li&gt;
&lt;li&gt;优点: 较好地模拟了 LRU，实现开销适中。&lt;/li&gt;
&lt;li&gt;与LRU的区别:
&lt;ul&gt;
&lt;li&gt;精确度: LRU 精确记录每次访问的时间顺序，而 Aging 只能近似反映访问频率和时间远近。&lt;/li&gt;
&lt;li&gt;实现开销: LRU 需要在每次内存访问时更新数据结构，开销大；Aging 只在时钟中断时更新计数器，开销小。&lt;/li&gt;
&lt;li&gt;历史长度: LRU 可以无限追溯历史访问记录；Aging 受计数器位数限制 (如8位只能记录最近8个时间窗口的访问情况) 。&lt;/li&gt;
&lt;li&gt;硬件支持: LRU 需要专门硬件支持才能高效实现；Aging 只需要访问位支持，更易于实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;44-算法示例与现象&quot;&gt;4.4 算法示例与现象&lt;a class=&quot;anchor&quot; href=&quot;#44-算法示例与现象&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;FIFO, LRU, OPT 缺页次数计算:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;例子: 页面访问序列 &lt;code&gt;2 3 2 1 5 2 4 5 3 2 5 2&lt;/code&gt;，分配 3 个页框。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;FIFO 算法过程:&lt;/p&gt;
















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;访问页面&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;页框1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框2&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框3&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;2&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;td&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;/td&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;p&gt;总计9次缺页&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LRU 算法过程:&lt;/p&gt;
















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;访问页面&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;页框1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框2&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框3&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&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;td&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;/td&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;p&gt;总计7次缺页&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OPT 算法过程:&lt;/p&gt;
















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;访问页面&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;页框1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框2&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框3&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&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;td&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;/td&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;p&gt;总计6次缺页, 可以发现重点是第五次访问要把 1 换出去, 因为我们知道未来信息 1 不再被用到了&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Belady 异常 (Belady’s Anomaly):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;现象: 对于某些置换算法 (如 FIFO) ，增加分配给进程的物理页框数，缺页次数 反而增加 的反常现象。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例子: 序列 &lt;code&gt;1 2 3 4 1 2 5 1 2 3 4 5&lt;/code&gt;，FIFO算法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;m=3 时，缺页过程:&lt;/p&gt;
















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;访问页面&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;页框1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框2&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框3&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&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;td&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;/td&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;p&gt;总计9次缺页&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;m=4 时，缺页过程:&lt;/p&gt;































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;访问页面&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;页框1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框2&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框3&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页框4&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&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;td&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;/td&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;p&gt;总计10次缺页&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;原因: FIFO 只考虑进来的时间，不考虑进来之后的访问情况。增加页框可能导致一个”坏”的页面 (未来会用到) 驻留更久，从而在后面挤掉了更有用的页面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LRU 和 OPT 不存在 Belady 异常: 因为它们满足 栈属性 (Stack Property)：即 &lt;code&gt;m&lt;/code&gt; 个页框时的内存内容总是 &lt;code&gt;m+1&lt;/code&gt; 个页框时内存内容的子集。增加页框只会包含更多有用的页，不会导致缺页增加。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;45-影响缺页次数的因素&quot;&gt;4.5 影响缺页次数的因素&lt;a class=&quot;anchor&quot; href=&quot;#45-影响缺页次数的因素&quot;&gt;#&lt;/a&gt;&lt;/h3&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;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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;小页面优缺点:
&lt;ul&gt;
&lt;li&gt;优点: 减少内部碎片，更适合程序局部性。&lt;/li&gt;
&lt;li&gt;缺点: 页表变大，TLB 效率可能降低，磁盘 I/O 效率低。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;大页面优缺点:
&lt;ul&gt;
&lt;li&gt;优点: 页表小，TLB 覆盖范围大，磁盘 I/O 效率高。&lt;/li&gt;
&lt;li&gt;缺点: 内部碎片增加，可能不适合小局部性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;最优页面大小: 理论上可以用公式 P = √(2se) 来计算，其中 &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;s&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 是页表项的大小 (表示页表开销) ，&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;e&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;e&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 是平均程序段大小 (表示内部碎片开销) 。这个公式平衡了页表大小和内部碎片之间的权衡。&lt;/li&gt;
&lt;li&gt;实际实现:
&lt;ul&gt;
&lt;li&gt;Intel 80x86/Pentium: 支持 4KB 或 4MB 页面大小。&lt;/li&gt;
&lt;li&gt;现代系统: 通常支持多种页面大小 (如 4KB, 2MB, 1GB) ，为有效使用TLB带来灵活性，但给操作系统带来复杂性。&lt;/li&gt;
&lt;li&gt;OS 和应用可根据需求灵活选择不同页面大小。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;程序的编制方法: 访问模式影响局部性。
&lt;ul&gt;
&lt;li&gt;例子: 只分配了一个 4KB页框，访问按行存储的二维数组 &lt;code&gt;A[1024][1024]&lt;/code&gt; (4KB页面)。
&lt;ul&gt;
&lt;li&gt;按行访问 (方法2): &lt;code&gt;for(i)... for(j)... A[i][j]&lt;/code&gt;。空间局部性好，每次访问都在同一页或下一页，缺页少 ( 1024 次，每行开始时缺页) 。&lt;/li&gt;
&lt;li&gt;按列访问 (方法1): &lt;code&gt;for(j)... for(i)... A[i][j]&lt;/code&gt;。空间局部性差，每次访问 &lt;code&gt;A[i][j]&lt;/code&gt; 和 &lt;code&gt;A[i+1][j]&lt;/code&gt; 会跨越多个页面 (1024*4 bytes ≈ 1 page) ，导致大量缺页 (1024 * 1024 次) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;颠簸/抖动 (Thrashing):
&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;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;5-高级内存管理策略&quot;&gt;5. 高级内存管理策略&lt;a class=&quot;anchor&quot; href=&quot;#5-高级内存管理策略&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;51-工作集模型-working-set-model&quot;&gt;5.1 工作集模型 (Working Set Model)&lt;a class=&quot;anchor&quot; href=&quot;#51-工作集模型-working-set-model&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;提出者: Denning (1968)&lt;/li&gt;
&lt;li&gt;基本思想: 基于 程序访问的局部性原理。一个进程在任何时刻都倾向于访问一个相对较小的页面集合，称为 活跃页面 (Active Pages)。如果能将这些活跃页面都保留在内存中，就能显著减少缺页。&lt;/li&gt;
&lt;li&gt;工作集 (Working Set) W(t, Δ): 在当前时间 &lt;code&gt;t&lt;/code&gt; 之前的 时间窗口 Δ 内，进程实际访问过的 虚拟页面 的集合。
&lt;ul&gt;
&lt;li&gt;Δ: 工作集窗口大小，是一个关键参数。&lt;/li&gt;
&lt;li&gt;工作集大小 &lt;code&gt;|W(t, Δ)|&lt;/code&gt; 随时间动态变化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;与驻留集的关系:
&lt;ul&gt;
&lt;li&gt;驻留集 (Resident Set): 当前时刻，进程 实际 驻留在物理内存中的页面集合。由 OS 分配策略和置换算法决定。&lt;/li&gt;
&lt;li&gt;理想状态: 进程的驻留集应包含其当前的工作集 (&lt;code&gt;Resident Set &amp;gt;= Working Set&lt;/code&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;加载控制: 只有当一个进程的工作集能够被完全调入内存时，才激活该进程运行，否则挂起。防止 Thrashing。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;52-工作集算法-实现工作集置换&quot;&gt;5.2 工作集算法 (实现工作集置换)&lt;a class=&quot;anchor&quot; href=&quot;#52-工作集算法-实现工作集置换&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基本思路: 识别并换出不在当前工作集 (W(t, Δ)) 中的页面。&lt;/li&gt;
&lt;li&gt;一种实现:
&lt;ul&gt;
&lt;li&gt;PTE 增强: 每个 PTE 增加一个字段，记录该页面的 最后访问时间 (Last Use Time)。&lt;/li&gt;
&lt;li&gt;参数: 设置一个时间阈值 &lt;code&gt;T&lt;/code&gt; (近似 Δ)。&lt;/li&gt;
&lt;li&gt;扫描过程 (类似时钟):
&lt;ol&gt;
&lt;li&gt;定期或缺页时扫描页框。&lt;/li&gt;
&lt;li&gt;检查 PTE 的 A 位：
&lt;ul&gt;
&lt;li&gt;若 A=1: 表示在当前时钟滴答内被访问。记录 当前虚拟时间 到 PTE 的 “最后访问时间” 字段，并将 A 位清零。&lt;/li&gt;
&lt;li&gt;若 A=0: 表示在当前滴答内未被访问。计算 页面年龄 (Age) = 当前虚拟时间 - 最后访问时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;判断与置换:
&lt;ul&gt;
&lt;li&gt;如果 Age &amp;gt; T (页面“老”，不在工作集内)：
&lt;ul&gt;
&lt;li&gt;如果页面是干净的 (D=0)，则该页面是 最佳 牺牲页，直接置换。&lt;/li&gt;
&lt;li&gt;如果页面是脏的 (D=1)，先记录下来，继续扫描，希望能找到一个干净的老页面。如果找不到干净的老页面，最后回来置换这个脏的老页面 (需要写回磁盘) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果 Age ≤ T (页面“年轻”，在工作集内)：保留该页面，继续扫描。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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;缺页率算法 (Page Fault Frequency - PFF): 一种近似方法。通过监控进程的缺页率来动态调整其驻留集大小。
&lt;ul&gt;
&lt;li&gt;设置缺页率上限和下限。&lt;/li&gt;
&lt;li&gt;缺页率 &amp;gt; 上限: 增加进程的页框数。&lt;/li&gt;
&lt;li&gt;缺页率 &amp;lt; 下限: 减少进程的页框数。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;53-清除策略-cleaning-policy&quot;&gt;5.3 清除策略 (Cleaning Policy)&lt;a class=&quot;anchor&quot; href=&quot;#53-清除策略-cleaning-policy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;问题: 当需要空闲页框时，如果选中的牺牲页是 “脏” 的，需要先写回磁盘，增加了缺页处理时间。&lt;/li&gt;
&lt;li&gt;目标: 保持一定数量的 干净 (Clean) 空闲页框可用。&lt;/li&gt;
&lt;li&gt;实现: 使用 分页守护进程 (Paging Daemon) (如 &lt;code&gt;kswapd&lt;/code&gt; in Linux)。
&lt;ul&gt;
&lt;li&gt;该进程周期性 (或在内存不足时) 被唤醒。&lt;/li&gt;
&lt;li&gt;检查内存状态，如果空闲页框低于某个阈值。&lt;/li&gt;
&lt;li&gt;使用页面置换算法 (如 Clock 或 LRU 近似) 选择一些页面。&lt;/li&gt;
&lt;li&gt;如果选中的页面是脏页，则启动 I/O 将其 提前写回 (Write Back) 磁盘，并将其标记为干净。&lt;/li&gt;
&lt;li&gt;这样，未来需要空闲页框时，可以直接使用这些已变干净的页框，或者快速换出它们。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;双指针时钟 (Two-Handed Clock):
&lt;ul&gt;
&lt;li&gt;前指针 (Cleaning Hand): 由分页守护进程控制。扫描页框，遇到脏页就启动写回，然后将其标记为干净；遇到干净页则跳过。前指针不断“清洁”页面。&lt;/li&gt;
&lt;li&gt;后指针 (Eviction Hand): 由缺页处理程序控制。用于实际选择牺牲页。由于前指针的工作，后指针更有可能遇到干净页面，从而加速缺页处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;54-页缓冲技术-page-buffering&quot;&gt;5.4 页缓冲技术 (Page Buffering)&lt;a class=&quot;anchor&quot; href=&quot;#54-页缓冲技术-page-buffering&quot;&gt;#&lt;/a&gt;&lt;/h3&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;li&gt;空闲页链表 (Free Page List): 存放被置换出的 干净 页面。&lt;/li&gt;
&lt;li&gt;修改页链表 (Modified Page List): 存放被置换出的 脏 页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;这些页面 暂时保留在内存中。&lt;/li&gt;
&lt;li&gt;优点:
&lt;ul&gt;
&lt;li&gt;快速回收 (Soft Fault): 如果进程很快又要访问刚被“置换”到这两个链表中的页面，可以直接将其重新链回进程的驻留集，无需磁盘 I/O。&lt;/li&gt;
&lt;li&gt;簇写回 (Cluster Write): 修改页链表中的脏页可以累积起来，成簇地 (in clusters) 写回磁盘，而不是一次只写一页，提高了磁盘 I/O 效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Page Cache (如 OSTEP 23章 提及): 现代 OS 中广泛使用的技术，用于缓存文件数据和匿名页 (包括上述缓冲的页面) 。
&lt;ul&gt;
&lt;li&gt;基本概念: 操作系统在物理内存中维护的一个缓存区域，用于存储最近访问的文件数据和元数据。&lt;/li&gt;
&lt;li&gt;工作原理:
&lt;ul&gt;
&lt;li&gt;当进程读取文件时，数据首先从磁盘加载到 page cache，然后再传递给进程。&lt;/li&gt;
&lt;li&gt;当进程写入文件时，数据先写入 page cache，标记为”脏”，稍后由后台进程 (如 pdflush/flush/kswapd) 异步写回磁盘。&lt;/li&gt;
&lt;li&gt;后续对相同文件数据的访问可直接从 page cache 获取，避免磁盘 I/O。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;管理策略:
&lt;ul&gt;
&lt;li&gt;使用近似 LRU 的替换策略决定哪些页面保留在缓存中。Linux 主要维护 active/inactive LRU 链表，思想上与 2Q 一类算法相近。&lt;/li&gt;
&lt;li&gt;通过 readahead 机制预读文件数据，提高顺序访问性能。&lt;/li&gt;
&lt;li&gt;支持 write-back (延迟写) 和 write-through (直接写) 两种写入策略。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优势:
&lt;ul&gt;
&lt;li&gt;减少磁盘 I/O: 大幅降低文件操作的延迟，提高系统整体性能。&lt;/li&gt;
&lt;li&gt;统一缓存: 在现代系统中，page cache 通常与 buffer cache 统一，形成统一缓存管理。&lt;/li&gt;
&lt;li&gt;内存利用: 未使用的物理内存自动用于缓存，提高内存利用率。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2Q 算法: 一种近似 LRU 的页面缓存替换算法，可作为 active/inactive 双队列思想的参考。
&lt;ul&gt;
&lt;li&gt;基本结构: 维护两个队列:
&lt;ul&gt;
&lt;li&gt;非活跃队列 (A1): 存放首次访问的页面。&lt;/li&gt;
&lt;li&gt;活跃队列 (Am): 存放多次访问的页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;工作流程:
&lt;ul&gt;
&lt;li&gt;当页面第一次被访问时，放入非活跃队列 A1 的头部。&lt;/li&gt;
&lt;li&gt;当页面在 A1 中再次被访问时，将其从 A1 移除并放入活跃队列 Am 的头部。&lt;/li&gt;
&lt;li&gt;当页面在 Am 中被访问时，将其移到 Am 的头部 (类似 LRU) 。&lt;/li&gt;
&lt;li&gt;需要置换页面时，总是从非活跃队列 A1 的尾部选择牺牲页。&lt;/li&gt;
&lt;li&gt;定期将活跃队列 Am 尾部的页面移回非活跃队列 A1，以保持整个缓存中约 2/3 的页面在活跃队列中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优势: 能有效应对扫描型工作负载 (如顺序读取大文件) 导致的频繁页面互换问题，这类场景下传统 LRU 表现不佳。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;55-加载控制-load-control&quot;&gt;5.5 加载控制 (Load Control)&lt;a class=&quot;anchor&quot; href=&quot;#55-加载控制-load-control&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;问题: 系统中并发运行的进程过多，总内存需求超过物理内存容量，导致 Thrashing。&lt;/li&gt;
&lt;li&gt;目标: 控制系统的 并发度 (Multiprogramming Level)，即同时驻留在内存中 (活跃) 的进程数量。&lt;/li&gt;
&lt;li&gt;解决方案:
&lt;ul&gt;
&lt;li&gt;进程挂起/交换 (Process Suspension/Swapping): 当系统负载过高 (如通过高缺页率或低 CPU 利用率检测到 Thrashing) 时，选择一个或多个进程，将其 所有页面 换出到磁盘 (交换区) ，并将其置于挂起状态。&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;hr/&gt;
&lt;h2 id=&quot;6-内存映射文件-memory-mapped-files&quot;&gt;6. 内存映射文件 (Memory-Mapped Files)&lt;a class=&quot;anchor&quot; href=&quot;#6-内存映射文件-memory-mapped-files&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;61-基本思想&quot;&gt;6.1 基本思想&lt;a class=&quot;anchor&quot; href=&quot;#61-基本思想&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;允许进程将一个 文件 或文件的一部分直接 映射 到其 虚拟地址空间 的一个区域。&lt;/li&gt;
&lt;li&gt;映射后，进程可以像访问普通内存 (如数组) 一样 通过内存读写指令 来访问文件内容，而无需使用 &lt;code&gt;read()&lt;/code&gt; / &lt;code&gt;write()&lt;/code&gt; 等系统调用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;62-工作机制&quot;&gt;6.2 工作机制&lt;a class=&quot;anchor&quot; href=&quot;#62-工作机制&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;系统调用: 如 POSIX 的 &lt;code&gt;mmap()&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;映射建立: &lt;code&gt;mmap()&lt;/code&gt; 调用并不立即读取文件内容。它只是在进程的虚拟地址空间中建立一个区域 (&lt;code&gt;vm_area_struct&lt;/code&gt; in Linux)，并设置相应的页表项指向 文件 作为后备存储 (Backing Store)。PTE 初始标记为无效。&lt;/li&gt;
&lt;li&gt;按需调页: 当进程 首次访问 映射区域中的某个地址时，会触发 缺页异常。&lt;/li&gt;
&lt;li&gt;缺页处理: OS 识别出这是一个映射文件的缺页，计算出该虚拟地址对应文件中的偏移量，然后从 磁盘文件 读取相应的 数据块 (页) 到一个物理页框，并更新 PTE 使其有效。&lt;/li&gt;
&lt;li&gt;写回:
&lt;ul&gt;
&lt;li&gt;如果映射是 共享的 (MAP_SHARED)，对内存区域的修改 最终会写回 磁盘上的原始文件 (通常在页面换出时、或调用 &lt;code&gt;msync()&lt;/code&gt;、或解除映射 &lt;code&gt;munmap()&lt;/code&gt; 时) 。其他映射同一文件的进程也能看到修改。&lt;/li&gt;
&lt;li&gt;如果映射是 私有的 (MAP_PRIVATE)，使用 写时复制 (Copy-on-Write)。首次写入时，会为该进程创建一个私有的页面副本，后续修改只影响此副本，不影响原始文件或其他进程。&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;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;Linux实现: 通过将所有进程的高地址部分映射到相同的内核物理页面实现&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;减轻页表增长压力的方式:
&lt;ul&gt;
&lt;li&gt;稀疏地址空间处理:
&lt;ul&gt;
&lt;li&gt;多级页表 (Multi-level Page Tables): 将页表分为多级，只为实际使用的地址区域分配页表项，避免为整个虚拟地址空间分配连续页表&lt;/li&gt;
&lt;li&gt;倒排页表 (Inverted Page Tables): 以物理页框为索引建立表项，每个表项记录映射到该物理页的虚拟页信息，页表大小与物理内存成正比而非虚拟地址空间&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;大页模式 (Huge Pages/Large Pages):
&lt;ul&gt;
&lt;li&gt;解决的问题:
&lt;ul&gt;
&lt;li&gt;减少TLB缺失: 使用大页可以增加TLB覆盖范围，单个TLB表项可以映射更大内存区域(如2MB或1GB而非4KB)&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;li&gt;页面换出复杂化: 换出一个大页需要更多I/O操作，可能增加延迟&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;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;63-mmap-函数-posix-示例&quot;&gt;6.3 &lt;code&gt;mmap()&lt;/code&gt; 函数 (POSIX 示例)&lt;a class=&quot;anchor&quot; href=&quot;#63-mmap-函数-posix-示例&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;start&lt;/code&gt;: 建议的映射起始虚拟地址 (通常设为 NULL，由内核选择)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;length&lt;/code&gt;: 映射的字节数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prot&lt;/code&gt;: 内存保护标志 (指定映射区域的访问权限)。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PROT_READ&lt;/code&gt;: 可读。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROT_WRITE&lt;/code&gt;: 可写。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROT_EXEC&lt;/code&gt;: 可执行。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PROT_NONE&lt;/code&gt;: 不可访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flags&lt;/code&gt;: 映射类型和选项。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MAP_SHARED&lt;/code&gt;: 共享映射，修改会写回文件。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MAP_PRIVATE&lt;/code&gt;: 私有写时复制映射。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MAP_ANONYMOUS&lt;/code&gt; (或 &lt;code&gt;MAP_ANON&lt;/code&gt;): 匿名映射，不关联任何文件，用于分配内存 (类似 &lt;code&gt;malloc&lt;/code&gt;) 。常与 &lt;code&gt;MAP_PRIVATE&lt;/code&gt; 结合用于进程的堆、栈、BSS段。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fd&lt;/code&gt;: 要映射的文件描述符。对于匿名映射，此参数忽略 (通常设为 -1) 。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;offset&lt;/code&gt;: 文件内的映射起始偏移量 (必须是页面大小的倍数) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;64-mmap-与-shm-共享内存-对比&quot;&gt;6.4 &lt;code&gt;mmap&lt;/code&gt; 与 &lt;code&gt;shm&lt;/code&gt; (共享内存) 对比&lt;a class=&quot;anchor&quot; href=&quot;#64-mmap-与-shm-共享内存-对比&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mmap&lt;/code&gt; (基于文件):
&lt;ul&gt;
&lt;li&gt;通信方式: 通过映射 同一个磁盘文件 到不同进程的地址空间实现共享。&lt;/li&gt;
&lt;li&gt;持久性: 共享内容与磁盘文件关联，可以是持久的。&lt;/li&gt;
&lt;li&gt;使用场景: 共享大文件，IPC，加载动态库，程序加载器。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shm&lt;/code&gt; (System V Shared Memory):
&lt;ul&gt;
&lt;li&gt;通信方式: 使用 &lt;code&gt;shmget()&lt;/code&gt; 创建一个内核管理的 纯内存 共享区域，然后用 &lt;code&gt;shmat()&lt;/code&gt; 将其附加到进程地址空间。&lt;/li&gt;
&lt;li&gt;持久性: 通常是临时的，与进程生命周期或显式删除 (&lt;code&gt;shmctl&lt;/code&gt; with &lt;code&gt;IPC_RMID&lt;/code&gt;) 相关，不直接关联磁盘文件。&lt;/li&gt;
&lt;li&gt;性能: 可能比基于文件的 &lt;code&gt;mmap&lt;/code&gt; 更快，因为它不涉及文件系统开销 (除非发生交换) 。&lt;/li&gt;
&lt;li&gt;大小限制: 受可用物理内存/交换空间限制。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;65-mmap-相关思考&quot;&gt;6.5 &lt;code&gt;mmap&lt;/code&gt; 相关思考&lt;a class=&quot;anchor&quot; href=&quot;#65-mmap-相关思考&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mmap&lt;/code&gt; 比物理内存+Swap空间大，是否有问题?
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;解答: &lt;code&gt;mmap&lt;/code&gt; 本身 可以 映射比物理内存+Swap 大得多的文件。&lt;code&gt;mmap&lt;/code&gt; 只是建立了虚拟地址到文件内容的 潜在 映射。只有当进程 实际访问 映射区域的页面时，才需要将其调入物理内存。这里需要区分两种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文件映射: 对于映射到文件的页面，文件本身就是这些页面的”后备存储”。当内存不足时，如果这些页面没有被修改过(非脏页)，可以直接丢弃，需要时再从文件重新读取；如果被修改过(脏页)，则需要先写回文件再释放。&lt;/li&gt;
&lt;li&gt;匿名映射: 没有关联文件的映射区域(如堆)，必须使用Swap空间作为后备存储。Swap是专门用于存储从内存中换出的页面的磁盘区域。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，文件映射可以超过物理内存+Swap的总大小，因为它使用原始文件作为后备存储。但如果工作集(实际访问的页面总数)超过了物理内存+Swap，系统会发生颠簸(Thrashing)，性能急剧下降，尽管映射本身是合法的。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;mmap&lt;/code&gt; 代替 &lt;code&gt;read/write&lt;/code&gt; 进行文件读写的优势?
&lt;ul&gt;
&lt;li&gt;解答:
&lt;ol&gt;
&lt;li&gt;减少数据拷贝: &lt;code&gt;read/write&lt;/code&gt; 通常涉及数据在内核缓冲区和用户缓冲区之间的拷贝。&lt;code&gt;mmap&lt;/code&gt; 允许进程直接访问内核的页缓存 (或直接从磁盘调页) ，避免了这次拷贝，提高效率，尤其对于大文件或频繁读写。&lt;/li&gt;
&lt;li&gt;简化随机访问: 对于需要频繁在文件中随机定位读写的场景，&lt;code&gt;mmap&lt;/code&gt; 将文件视为内存数组，可以通过指针运算直接访问任意位置，代码更简洁，无需管理文件指针和复杂的 &lt;code&gt;lseek&lt;/code&gt; 调用。&lt;/li&gt;
&lt;li&gt;内核优化: 内核可以更有效地管理 &lt;code&gt;mmap&lt;/code&gt; 区域的页面缓存和预读。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;66-内存映射文件应用示例&quot;&gt;6.6 内存映射文件应用示例&lt;a class=&quot;anchor&quot; href=&quot;#66-内存映射文件应用示例&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;程序加载: 加载可执行文件和动态链接库 (DLLs/SOs) 时，代码段和只读数据段通常通过私有映射 (&lt;code&gt;MAP_PRIVATE&lt;/code&gt;) 加载，数据段通过写时复制加载。&lt;/li&gt;
&lt;li&gt;进程间通信 (IPC): 通过共享映射 (&lt;code&gt;MAP_SHARED&lt;/code&gt;) 同一个文件 (或匿名映射) ，实现高效的数据共享。&lt;/li&gt;
&lt;li&gt;数据库: 像 LMDB (Lightning Memory-Mapped Database) 这样的内存映射数据库，将整个数据库文件映射到内存，利用 OS 的虚拟内存管理进行数据缓存和访问，简化了缓冲管理，提高了 I/O 性能。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;7-虚拟内存管理全貌-结合进程结构&quot;&gt;7. 虚拟内存管理全貌 (结合进程结构)&lt;a class=&quot;anchor&quot; href=&quot;#7-虚拟内存管理全貌-结合进程结构&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;进程控制块 (PCB / &lt;code&gt;task_struct&lt;/code&gt; in Linux): 包含指向内存描述符 (&lt;code&gt;mm_struct&lt;/code&gt;) 的指针。&lt;/li&gt;
&lt;li&gt;内存描述符 (&lt;code&gt;mm_struct&lt;/code&gt;): 描述进程的整个虚拟地址空间，包含指向页表的指针 (如 CR3 指向的页目录物理地址) 和指向虚拟内存区域链表/树 (&lt;code&gt;vm_area_struct&lt;/code&gt; list/tree) 的指针。&lt;/li&gt;
&lt;li&gt;虚拟内存区域 (&lt;code&gt;vm_area_struct&lt;/code&gt; - VMA): 描述进程地址空间中一段 连续 的、具有 相同属性 (如权限、映射文件) 的虚拟内存区域。例如，代码段、数据段、堆、栈、每个内存映射文件、每个共享库都对应一个或多个 VMA。
&lt;ul&gt;
&lt;li&gt;关键字段: 起始地址 (&lt;code&gt;vm_start&lt;/code&gt;), 结束地址 (&lt;code&gt;vm_end&lt;/code&gt;), 访问权限 (&lt;code&gt;vm_prot&lt;/code&gt;), 标志 (&lt;code&gt;vm_flags&lt;/code&gt;, 如 &lt;code&gt;VM_READ&lt;/code&gt;, &lt;code&gt;VM_WRITE&lt;/code&gt;, &lt;code&gt;VM_EXEC&lt;/code&gt;, &lt;code&gt;VM_SHARED&lt;/code&gt;), 指向映射文件信息的指针 (&lt;code&gt;vm_file&lt;/code&gt;) 等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;页表 (Page Tables): 将 VMA 内的虚拟页号映射到物理页框号或标记为不在内存。&lt;/li&gt;
&lt;li&gt;物理内存 (Page Frames): 实际存储数据的内存块。&lt;/li&gt;
&lt;li&gt;后备存储 (Backing Store): 磁盘上的文件 (可执行文件、库、数据文件) 或交换空间 (Swap Area)，用于存放不在物理内存中的页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;交互过程: 访问虚拟地址 -&amp;gt; 查找 VMA -&amp;gt; 查找页表 (TLB first) -&amp;gt; 访问物理内存 / Page Fault -&amp;gt; (缺页处理) -&amp;gt; 访问后备存储。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;8-写时复制-copy-on-write---cow&quot;&gt;8. 写时复制 (Copy-on-Write - COW)&lt;a class=&quot;anchor&quot; href=&quot;#8-写时复制-copy-on-write---cow&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;目的: 优化资源 (特别是内存) 的复制过程，推迟实际的物理复制，直到真正需要时才进行。&lt;/li&gt;
&lt;li&gt;应用场景:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fork()&lt;/code&gt; 系统调用创建子进程。&lt;/li&gt;
&lt;li&gt;私有内存映射 (&lt;code&gt;MAP_PRIVATE&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;机制:
&lt;ol&gt;
&lt;li&gt;共享初始副本: 当创建副本时 (如 &lt;code&gt;fork()&lt;/code&gt; 创建子进程) ，并不立即复制父进程的物理内存页面。而是让子进程的页表项指向与父进程 相同 的物理页框。&lt;/li&gt;
&lt;li&gt;标记为只读: 同时，将这些共享页框在 父子进程的页表项中都标记为只读 (Read-Only)，即使它们原本是可写的。&lt;/li&gt;
&lt;li&gt;写操作触发异常: 如果任何一个进程 (父或子) 尝试 写入 这些共享的页面，会触发一个 保护性页错误 (Protection Fault)。&lt;/li&gt;
&lt;li&gt;真正复制: 操作系统捕获此异常，识别出是 COW 机制。此时，内核会：
&lt;ul&gt;
&lt;li&gt;分配一个新的物理页框。&lt;/li&gt;
&lt;li&gt;将原始页框的内容 复制 到新页框。&lt;/li&gt;
&lt;li&gt;修改 触发写入操作的那个进程 的页表项，使其指向 新复制的页框，并将该页表项的权限 恢复为可写 (Read-Write)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;恢复执行: 进程继续执行写操作，现在写入的是它自己的私有副本。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;优点:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fork()&lt;/code&gt; 效率高: 如果子进程立刻调用 &lt;code&gt;exec()&lt;/code&gt; 加载新程序，那么之前的大部分复制就白费了。COW 避免了这种不必要的开销。&lt;/li&gt;
&lt;li&gt;节省内存: 只要页面不被修改，父子进程可以一直共享同一物理副本。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;9-windows-虚拟内存管理-概述&quot;&gt;9. Windows 虚拟内存管理 (概述)&lt;a class=&quot;anchor&quot; href=&quot;#9-windows-虚拟内存管理-概述&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;(注: 以下内容可能为对 Windows (可能较早版本如 NT/XP/7) 的描述) , 不重要&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;91-intel-x86-虚拟内存机制回顾&quot;&gt;9.1 Intel x86 虚拟内存机制回顾&lt;a class=&quot;anchor&quot; href=&quot;#91-intel-x86-虚拟内存机制回顾&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;保护模式寻址: 使用 段选择符 (Segment Selector) + 偏移量 (Offset) 形成逻辑地址。段选择符指向 段描述符 (Segment Descriptor) (在 GDT/LDT 中)，包含段基址、限长、权限等。逻辑地址 -&amp;gt; 线性地址 (Linear Address)。&lt;/li&gt;
&lt;li&gt;分页机制: 如果 CR0 寄存器的 PG 位开启，线性地址会被页式机制进一步转换为 物理地址 (Physical Address)。&lt;/li&gt;
&lt;li&gt;绕过分段: 可以设置段描述符使段基址为 0，限长为 4GB，从而让线性地址等于逻辑地址的偏移量部分，达到“平坦内存模型”的效果。&lt;/li&gt;
&lt;li&gt;页表结构: x86 支持多级页表 (早期 2 级，x86-64 支持 4 级)。CR3 寄存器指向最高级页表的物理基地址。PTE/PDE 结构包含 PFN, P, A, D, R/W, U/S, PCD, PWT 等位。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;92-windows-内存管理器-memory-manager&quot;&gt;9.2 Windows 内存管理器 (Memory Manager)&lt;a class=&quot;anchor&quot; href=&quot;#92-windows-内存管理器-memory-manager&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;位置: 位于内核执行体 (&lt;code&gt;Ntoskrnl.exe&lt;/code&gt;) 中。&lt;/li&gt;
&lt;li&gt;主要组成:
&lt;ul&gt;
&lt;li&gt;执行体系统服务: 提供 API (&lt;code&gt;VirtualAlloc&lt;/code&gt;, &lt;code&gt;MapViewOfFile&lt;/code&gt;, &lt;code&gt;HeapAlloc&lt;/code&gt; 等) 用于虚存分配、回收和管理。&lt;/li&gt;
&lt;li&gt;页面错误陷阱处理程序 (&lt;code&gt;MmAccessFault&lt;/code&gt;): 处理 MMU 检测到的内存管理异常 (缺页、权限错误)。&lt;/li&gt;
&lt;li&gt;后台线程 (关键组件):
&lt;ul&gt;
&lt;li&gt;工作集管理器 (&lt;code&gt;MmWorkingSetManager&lt;/code&gt;): 负责调整进程工作集大小 (修整) 、老化页面、启动脏页写出等。&lt;/li&gt;
&lt;li&gt;进程/栈交换器 (&lt;code&gt;KeSwapProcessOrStack&lt;/code&gt;): 负责整个进程或内核线程栈的换入换出 (用于挂起/恢复进程)。&lt;/li&gt;
&lt;li&gt;修改页面写出器 (&lt;code&gt;MiModifiedPageWriter&lt;/code&gt;): 将“脏”的匿名页面 (来自页文件) 写回到页文件。&lt;/li&gt;
&lt;li&gt;映射页面写出器 (&lt;code&gt;MiMappedPageWriter&lt;/code&gt;): 将内存映射文件中的“脏”页写回磁盘文件。&lt;/li&gt;
&lt;li&gt;零页线程 (&lt;code&gt;MmZeroPageThread&lt;/code&gt;): 将空闲页框清零，为按需零页 (Demand-Zero) 提供准备好的页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;93-windows-地址空间布局-32-bit-示例&quot;&gt;9.3 Windows 地址空间布局 (32-bit 示例)&lt;a class=&quot;anchor&quot; href=&quot;#93-windows-地址空间布局-32-bit-示例&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;总览: 4GB 虚拟地址空间。&lt;/li&gt;
&lt;li&gt;用户空间 (通常 0x00000000 - 0x7FFFFFFF, 2GB):
&lt;ul&gt;
&lt;li&gt;应用程序代码 (EXE)&lt;/li&gt;
&lt;li&gt;动态链接库 (DLL) 代码和数据&lt;/li&gt;
&lt;li&gt;进程堆 (Heap)&lt;/li&gt;
&lt;li&gt;线程栈 (Stack)&lt;/li&gt;
&lt;li&gt;进程环境块 (PEB), 线程环境块 (TEB)&lt;/li&gt;
&lt;li&gt;内存映射文件区域&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;系统空间 (通常 0x80000000 - 0xFFFFFFFF, 2GB):
&lt;ul&gt;
&lt;li&gt;内核代码 (&lt;code&gt;Ntoskrnl.exe&lt;/code&gt;), HAL (&lt;code&gt;hal.dll&lt;/code&gt;), 内核驱动&lt;/li&gt;
&lt;li&gt;页表本身 (自映射区域)&lt;/li&gt;
&lt;li&gt;系统缓存 (File Cache)&lt;/li&gt;
&lt;li&gt;分页缓冲池 (Paged Pool): 可被换出的内核内存&lt;/li&gt;
&lt;li&gt;非分页缓冲池 (Non-Paged Pool): 不能被换出的内核内存 (用于中断处理等)&lt;/li&gt;
&lt;li&gt;超空间 (Hyperspace): 临时映射进程页表等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;/3GB 启动选项: 可以修改用户/系统空间划分为 3GB/1GB，让用户进程获得更大地址空间。&lt;/li&gt;
&lt;li&gt;自映射机制: Windows 将当前进程的 页目录 和 所有页表 映射到系统空间的一个固定虚拟地址范围 (如 0xC0000000 开始)，使得内核可以通过虚拟地址方便地访问任何进程的 PTE/PDE，而无需切换 CR3 寄存器。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MiGetPdeAddress(va)&lt;/code&gt; 和 &lt;code&gt;MiGetPteAddress(va)&lt;/code&gt; 宏利用此机制快速计算给定虚拟地址 &lt;code&gt;va&lt;/code&gt; 对应的 PDE 和 PTE 的 虚拟地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;94-windows-缺页处理&quot;&gt;9.4 Windows 缺页处理&lt;a class=&quot;anchor&quot; href=&quot;#94-windows-缺页处理&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;流程:
&lt;ol&gt;
&lt;li&gt;CPU 访问无效 PTE 地址，触发 0xE 号中断 (Page Fault)。&lt;/li&gt;
&lt;li&gt;CPU 将出错的虚拟地址存入 CR2 寄存器。&lt;/li&gt;
&lt;li&gt;CPU 跳转到内核的缺页中断处理例程 &lt;code&gt;KiTrap0E&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KiTrap0E&lt;/code&gt; 调用核心处理函数 &lt;code&gt;MmAccessFault&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MmAccessFault&lt;/code&gt; 读取 CR2，找到对应的 PTE。&lt;/li&gt;
&lt;li&gt;分析 PTE 内容 (即使 P=0，其他位仍有 OS 定义的含义):
&lt;ul&gt;
&lt;li&gt;页面从未被提交 (Committed): 可能是非法访问。&lt;/li&gt;
&lt;li&gt;访问违反权限 (Protection Violation): 如写入只读页。&lt;/li&gt;
&lt;li&gt;写时复制 (Copy-on-Write): 执行 COW 操作。&lt;/li&gt;
&lt;li&gt;栈扩展 (Stack Expansion): 自动分配并映射新的栈页面。&lt;/li&gt;
&lt;li&gt;页面在转换状态 (Transition): 页面正在被 I/O (读入/写出) ，需等待 I/O 完成。&lt;/li&gt;
&lt;li&gt;页面在页文件/映射文件中 (Paged Out): 需要从磁盘调入。&lt;/li&gt;
&lt;li&gt;请求零页面 (Demand Zero): 分配一个已清零的物理页框。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;执行相应操作: 分配页框、启动磁盘 I/O、修改 PTE、完成 COW 等。&lt;/li&gt;
&lt;li&gt;返回用户模式，重新执行指令。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;95-windows-工作集-working-set&quot;&gt;9.5 Windows 工作集 (Working Set)&lt;a class=&quot;anchor&quot; href=&quot;#95-windows-工作集-working-set&quot;&gt;#&lt;/a&gt;&lt;/h3&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;工作集修整 (Trimming): 当系统物理内存紧张时，工作集管理器 会减少某些进程的工作集大小 (通常使用 Clock 类似算法移除“最老”的页面) ，将移除的页面放入 Standby 或 Modified 链表。&lt;/li&gt;
&lt;li&gt;自动增长: 进程发生缺页时，如果其工作集大小未达到最大值，且系统有空闲内存，则调入页面会使其工作集增长。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;96-用户空间内存分配方式&quot;&gt;9.6 用户空间内存分配方式&lt;a class=&quot;anchor&quot; href=&quot;#96-用户空间内存分配方式&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;以页为单位的虚拟内存分配 (&lt;code&gt;VirtualAlloc&lt;/code&gt;, &lt;code&gt;VirtualFree&lt;/code&gt;):
&lt;ul&gt;
&lt;li&gt;两阶段:
&lt;ul&gt;
&lt;li&gt;保留 (Reserve): 在进程虚拟地址空间预留一段范围，不分配物理内存或页文件空间。只是标记地址范围不可用。&lt;/li&gt;
&lt;li&gt;提交 (Commit): 为保留的地址空间实际分配物理内存 (或页文件支持) 。页面首次访问时才会真正调入物理内存 (Demand Zero 或从页文件加载) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;虚拟地址描述符 (VAD - Virtual Address Descriptor): 内核为每个进程维护一棵 自平衡二叉树 (VAD Tree)，每个节点描述一段连续的、属性相同的虚拟地址空间 (已保留或已提交) 。用于快速查找、分配、释放虚拟地址范围。&lt;code&gt;EPROCESS&lt;/code&gt; 结构包含指向 VAD 树根的指针。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;内存映射文件 (&lt;code&gt;CreateFileMapping&lt;/code&gt;, &lt;code&gt;MapViewOfFile&lt;/code&gt;):
&lt;ul&gt;
&lt;li&gt;使用 区域对象 (Section Object) (Win32 API 称之为 File Mapping Object) 实现。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CreateFileMapping&lt;/code&gt; 创建一个区域对象，可以基于磁盘文件或页文件 (用于匿名共享内存) 。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MapViewOfFile&lt;/code&gt; 将区域对象的一部分或全部映射到进程的虚拟地址空间，得到一个 视图 (View)。&lt;/li&gt;
&lt;li&gt;通过映射同一区域对象，实现进程间共享内存或文件访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;内存堆 (Heap) (&lt;code&gt;HeapCreate&lt;/code&gt;, &lt;code&gt;HeapAlloc&lt;/code&gt;, &lt;code&gt;HeapFree&lt;/code&gt;):
&lt;ul&gt;
&lt;li&gt;用途: 管理 大量、小块 的内存分配。&lt;/li&gt;
&lt;li&gt;机制: 进程首先用 &lt;code&gt;VirtualAlloc&lt;/code&gt; (通常在进程启动时由系统自动完成，创建默认堆) 分配一大块虚拟内存作为堆区域。然后，堆管理器 (用户模式库或内核函数集) 在这个区域内进一步细分和管理小块内存的分配与释放。&lt;/li&gt;
&lt;li&gt;类型: 每个进程有一个 默认进程堆 (&lt;code&gt;GetProcessHeap&lt;/code&gt;)。也可以创建额外的 私有堆 (&lt;code&gt;HeapCreate&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;97-windows-物理内存管理&quot;&gt;9.7 Windows 物理内存管理&lt;a class=&quot;anchor&quot; href=&quot;#97-windows-物理内存管理&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;页框号数据库 (PFN Database): 一个 全局数组 (&lt;code&gt;MmPfnDatabase&lt;/code&gt;)，数组的每个元素是一个 &lt;code&gt;MMPFN&lt;/code&gt; 结构，对应一个物理内存页框。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MMPFN&lt;/code&gt; 结构: 包含该物理页框的所有状态信息，如：
&lt;ul&gt;
&lt;li&gt;状态 (State): Active, Standby, Modified, Transition, Free, Zeroed, Bad。&lt;/li&gt;
&lt;li&gt;链接指针: 用于将处于相同状态的页框链接起来 (形成链表) 。&lt;/li&gt;
&lt;li&gt;指向 PTE 的指针 (如果页面是 Active/Standby/Modified)。&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;Active/Valid: 页面在某个工作集中，PTE有效。&lt;/li&gt;
&lt;li&gt;Transition: 页面正在进行 I/O。&lt;/li&gt;
&lt;li&gt;Standby: 页面刚被从工作集移除，内容 干净，PTE 无效但指向此 PFN。可被快速重用 (Soft Fault)。&lt;/li&gt;
&lt;li&gt;Modified: 页面刚被从工作集移除，内容 脏，PTE 无效但指向此 PFN。重用前需写回磁盘。&lt;/li&gt;
&lt;li&gt;Free: 页框空闲，内容无效。&lt;/li&gt;
&lt;li&gt;Zeroed: 页框空闲，且已清零。可立即用于 Demand Zero 页。&lt;/li&gt;
&lt;li&gt;Bad: 页框硬件损坏，不可用。&lt;/li&gt;
&lt;li&gt;链表: 内核维护 &lt;code&gt;FreePageList&lt;/code&gt;, &lt;code&gt;ZeroedPageList&lt;/code&gt;, &lt;code&gt;StandbyPageList&lt;/code&gt;, &lt;code&gt;ModifiedPageList&lt;/code&gt; 等链表，通过 &lt;code&gt;MMPFN&lt;/code&gt; 中的指针将对应状态的页框链接起来，方便快速查找和管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;状态转换图 (示意): 页面在不同链表和工作集之间根据缺页、写回、清零、置换等操作进行转换。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;10-重点小结&quot;&gt;10. 重点小结&lt;a class=&quot;anchor&quot; href=&quot;#10-重点小结&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;核心概念: 虚拟内存是对物理内存的抽象，提供更大、受保护的地址空间。通过页表和 MMU 实现虚拟到物理地址的转换。&lt;/li&gt;
&lt;li&gt;关键机制:
&lt;ul&gt;
&lt;li&gt;分页 (Paging): 将地址空间划分为固定大小的页/页框。&lt;/li&gt;
&lt;li&gt;页表 (Page Table): 存储 VPN 到 PFN 的映射及状态位 (P, A, D, Protection)。&lt;/li&gt;
&lt;li&gt;多级页表/反转页表: 解决页表过大问题。&lt;/li&gt;
&lt;li&gt;TLB (快表): 加速地址转换。&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;置换策略 (Replacement): FIFO, LRU, Clock, Working Set 等，决定牺牲哪个页面。&lt;/li&gt;
&lt;li&gt;驻留集管理: 控制进程占用多少物理内存 (Fixed vs. Variable, Working Set)。&lt;/li&gt;
&lt;li&gt;清除策略 (Cleaning): 提前写回脏页，保持空闲页框干净。&lt;/li&gt;
&lt;li&gt;加载控制: 防止 Thrashing。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;高级特性:
&lt;ul&gt;
&lt;li&gt;内存映射文件 (mmap): 高效文件 I/O 和 IPC 机制。&lt;/li&gt;
&lt;li&gt;写时复制 (COW): 优化 &lt;code&gt;fork()&lt;/code&gt; 和私有映射。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Windows 特点: VAD 树管理虚拟地址空间，PFN 数据库管理物理页框，多种页面状态链表，工作集模型，多种内存分配 API。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 05: Memory Management Overview</title><link>https://www.lyt0112.com/blog/operating_systems_note_05-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_05-zh</guid><description>Operating Systems Notes 05: Memory Management Overview</description><pubDate>Fri, 28 Mar 2025 16:14:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-重要概念&quot;&gt;1. 重要概念&lt;a class=&quot;anchor&quot; href=&quot;#1-重要概念&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;11-存储体系-memory-hierarchy&quot;&gt;1.1 存储体系 (Memory Hierarchy)&lt;a class=&quot;anchor&quot; href=&quot;#11-存储体系-memory-hierarchy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;计算机存储器按照速度、容量和成本排列成层次结构。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;寄存器 (Registers): 最快，容量最小，成本最高，直接由 CPU 访问。&lt;/li&gt;
&lt;li&gt;高速缓存 (Cache - L1, L2, L3): 速度快，容量较小，成本高，用于缓解 CPU 和主存之间的速度差异。&lt;/li&gt;
&lt;li&gt;内存 (Main Memory / RAM): 速度、容量和成本居中，是程序运行时代码和数据的主要存储区域。&lt;/li&gt;
&lt;li&gt;本地磁盘 (Local Disk / Secondary Storage): 速度慢，容量大，成本低，用于持久化存储，如硬盘 (HDD)、固态硬盘 (SSD)。&lt;/li&gt;
&lt;li&gt;远程磁盘 (Remote Storage): 通过网络访问，速度最慢，容量可以很大。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;内存管理主要关注的是主存 (内存) 的管理。&lt;/p&gt;
&lt;h3 id=&quot;12-地址空间-address-space&quot;&gt;1.2 地址空间 (Address Space)&lt;a class=&quot;anchor&quot; href=&quot;#12-地址空间-address-space&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定义: 操作系统为每个进程分配的一个独立的逻辑地址范围。进程所能“看到”和访问的地址集合。&lt;/li&gt;
&lt;li&gt;独立性: 每个进程拥有自己独立的地址空间，一个进程默认不能访问另一个进程的地址空间，这是实现存储保护的基础。&lt;/li&gt;
&lt;li&gt;管理: 操作系统需要管理地址空间的分配 (放置 placement) 、回收、分割与合并。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;13-逻辑地址-vs-物理地址&quot;&gt;1.3 逻辑地址 vs 物理地址&lt;a class=&quot;anchor&quot; href=&quot;#13-逻辑地址-vs-物理地址&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;逻辑地址 (Logical Address):
&lt;ul&gt;
&lt;li&gt;也称为相对地址 (Relative Address) 或 虚拟地址 (Virtual Address)。&lt;/li&gt;
&lt;li&gt;是用户程序 (或 CPU 发出) 使用的地址。&lt;/li&gt;
&lt;li&gt;通常从 0 开始编址，地址是相对于进程自身的地址空间。&lt;/li&gt;
&lt;li&gt;编译、汇编后生成的目标代码通常使用逻辑地址。&lt;/li&gt;
&lt;li&gt;不能直接用于在物理内存中寻址。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;物理地址 (Physical Address):
&lt;ul&gt;
&lt;li&gt;也称为绝对地址 (Absolute Address) 或 实地址 (Real Address)。&lt;/li&gt;
&lt;li&gt;是内存存储单元的实际地址，硬件内存总线可以直接访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;14-地址重定位-address-relocation&quot;&gt;1.4 地址重定位 (Address Relocation)&lt;a class=&quot;anchor&quot; href=&quot;#14-地址重定位-address-relocation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定义: 将用户程序中的逻辑地址转换为运行时可由机器直接寻址的物理地址的过程。&lt;/li&gt;
&lt;li&gt;别名: 也常被称为地址转换 (Address Translation)、地址变换 (Address Transformation) 或 地址映射 (Address Mapping)。这些术语基本同义，都指代从逻辑地址到物理地址的转换过程。&lt;/li&gt;
&lt;li&gt;目的: 保证 CPU 执行指令时能够正确访问到物理内存单元。&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;地址绑定时机 (Binding Time): 指令和数据绑定到内存地址的时间点：
&lt;ul&gt;
&lt;li&gt;编译时 (Compile Time): 如果编译时就知道程序将驻留在内存的哪个位置，编译器可以直接生成绝对代码。但如果加载位置改变，程序就必须重新编译。很少用。&lt;/li&gt;
&lt;li&gt;加载时 (Load Time): 如果编译时不知道加载位置，编译器生成可重定位代码 (Relocatable Code)。加载器 (Loader) 在将程序加载到内存时，根据实际加载的起始地址，一次性地将所有逻辑地址转换为物理地址。这称为静态地址重定位。&lt;/li&gt;
&lt;li&gt;运行时 (Run Time): 地址转换延迟到程序运行时才进行。CPU 每次访问内存 (取指或访存) 时，都会将逻辑地址转换为物理地址。这需要硬件支持 (如 MMU) ，称为动态地址重定位。现代操作系统普遍采用此方式，因为它提供了最大的灵活性 (如进程可以在内存中移动) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;静态地址重定位-static-relocation&quot;&gt;静态地址重定位 (Static Relocation)&lt;a class=&quot;anchor&quot; href=&quot;#静态地址重定位-static-relocation&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;过程: 在程序被加载进内存时，由加载器一次性完成逻辑地址到物理地址的转换。&lt;/li&gt;
&lt;li&gt;实现: 通常由软件 (加载器) 完成。&lt;/li&gt;
&lt;li&gt;示例:
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;pre&gt;&lt;code&gt;// 源代码         // 目标代码 (逻辑地址)        // 装载模块 (磁盘, 逻辑)   // 装载模块 (内存, 物理 @ 1000)
i = ...;        store 20;                 store 120;               store 1120;
f();            branch f;                 branch 100;              branch 1100; // 假设 f 在 100
...             ...                       ...                      ...
f: ...          f: ...                    f: ... (在 100)          f: ... (在 1100)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;缺点: 程序加载后不能在内存中移动；不灵活。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;动态地址重定位-dynamic-relocation&quot;&gt;动态地址重定位 (Dynamic Relocation)&lt;a class=&quot;anchor&quot; href=&quot;#动态地址重定位-dynamic-relocation&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;过程: 在进程执行过程中，每次访问内存地址时进行转换。&lt;/li&gt;
&lt;li&gt;实现: 需要硬件支持，通常是 内存管理单元 (MMU - Memory Management Unit)。
&lt;ul&gt;
&lt;li&gt;MMU: 一个硬件设备，负责将 CPU 发出的逻辑地址实时转换为物理地址。&lt;/li&gt;
&lt;li&gt;实现方式 (简单示例): 使用基址寄存器 (Base Register) 和重定位寄存器 (Relocation Register)。逻辑地址加上重定位寄存器的值得到物理地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优点: 进程可以在内存中移动 (例如，为了内存紧凑) ；支持更高级的内存管理技术 (如虚拟内存) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;15-存储保护-memory-protection&quot;&gt;1.5 存储保护 (Memory Protection)&lt;a class=&quot;anchor&quot; href=&quot;#15-存储保护-memory-protection&quot;&gt;#&lt;/a&gt;&lt;/h3&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;基地址寄存器 (Base Register): 存放进程在物理内存中的起始地址。&lt;/li&gt;
&lt;li&gt;界限寄存器 (Limit Register): 存放进程的逻辑地址空间的大小 (或最大合法逻辑地址) 。&lt;/li&gt;
&lt;li&gt;检查过程: CPU 产生的每个逻辑地址 &lt;code&gt;addr&lt;/code&gt; 必须满足 &lt;code&gt;0 &amp;lt;= addr &amp;lt; Limit Register&lt;/code&gt;。转换后的物理地址 &lt;code&gt;Base Register + addr&lt;/code&gt; 也必须在分配给该进程的物理内存范围内 (有时 limit check 可以直接在物理地址上做，&lt;code&gt;Base &amp;lt;= Physical Address &amp;lt; Base + Limit&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;加载: 基址和界限寄存器的值由操作系统通过特权指令 (Privileged Instructions) 加载，用户程序无法修改。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;其他机制: 页表/段表中的保护位 (读/写/执行权限) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;16-存储共享-memory-sharing&quot;&gt;1.6 存储共享 (Memory Sharing)&lt;a class=&quot;anchor&quot; href=&quot;#16-存储共享-memory-sharing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;允许多个进程安全地共享同一段物理内存区域。例如，共享库 (如 C 库) 的代码段通常可以在多个进程间共享，只需在物理内存中保留一份副本，节省内存。实现通常依赖于分页或分段机制。&lt;/p&gt;
&lt;h3 id=&quot;17-局部性原理-principle-of-locality&quot;&gt;1.7 局部性原理 (Principle of Locality)&lt;a class=&quot;anchor&quot; href=&quot;#17-局部性原理-principle-of-locality&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;程序在执行过程中的一个普遍倾向：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;时间局部性 (Temporal Locality): 如果一个内存位置被访问，那么它在不久的将来很可能再次被访问 (如循环中的指令、变量) 。&lt;/li&gt;
&lt;li&gt;空间局部性 (Spatial Locality): 如果一个内存位置被访问，那么它附近的内存位置也很可能在不久的将来被访问 (如顺序执行的代码、数组元素) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;意义: 存储体系 (特别是 Cache) 和虚拟内存管理 (如页面置换算法) 都依赖局部性原理来提高性能。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;2-内存管理的目标与功能&quot;&gt;2. 内存管理的目标与功能&lt;a class=&quot;anchor&quot; href=&quot;#2-内存管理的目标与功能&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;目标: 有效、安全地管理计算机主存资源，提升系统性能和易用性。主要追求：
&lt;ul&gt;
&lt;li&gt;透明性 (Transparency): 内存管理对用户程序应该是透明的，程序员不需要关心物理内存的细节。&lt;/li&gt;
&lt;li&gt;效率 (Efficiency): 最小化内存访问时间，高效利用内存资源 (减少浪费) ，降低管理开销。&lt;/li&gt;
&lt;li&gt;保护 (Protection): 确保进程间地址空间隔离，保护操作系统自身。&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;内存扩充: 通过覆盖、交换、虚拟内存等技术，在有限的物理内存上运行更大的程序或更多的进程。&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;需要支持地址重定位 (程序加载位置不确定) 。&lt;/li&gt;
&lt;li&gt;需要支持地址保护 (进程间隔离) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-进程地址空间&quot;&gt;3. 进程地址空间&lt;a class=&quot;anchor&quot; href=&quot;#3-进程地址空间&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-地址空间布局-典型-linux-布局&quot;&gt;3.1 地址空间布局 (典型 Linux 布局)&lt;a class=&quot;anchor&quot; href=&quot;#31-地址空间布局-典型-linux-布局&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;进程的逻辑地址空间通常划分为几个标准段：&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;pre&gt;&lt;code&gt;    高地址 0xFFFFFFFF  +-----------------------+
                      | 内核地址空间 (Kernel)    | --&amp;gt; 供操作系统使用，用户模式不可访问
          0xC0000000  +-----------------------+
                      | 用户栈 (Stack)          | --&amp;gt; 函数调用、局部变量 (向下增长)         |
                      | ----------------------- |
                      |                         |
                      | 内存映射区域            | --&amp;gt; 共享库、内存映射文件                  |
                      | (Memory Mapped Region)  |
                      |                         |
                      | ----------------------- |
                      | 堆 (Heap)               | --&amp;gt; 动态内存分配 (malloc, new) (向上增长) |
                      | ----------------------- |
                      | BSS 段 (Uninit. Data)   | --&amp;gt; 未初始化全局/静态变量                 |
                      | ----------------------- |
                      | 数据段 (Data Segment)   | --&amp;gt; 已初始化全局/静态变量                 |
                      | 0x08048000              | -----------------------                   |
                      | 代码段 (Text Segment)   | --&amp;gt; 程序指令 (只读)                       |
    低地址  0x00000000 +-----------------------+&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;加载来源: 代码段、数据段、BSS 段通常从可执行文件中加载。栈和堆是在运行时动态创建和增长的。共享库在运行时动态链接和映射。&lt;/li&gt;
&lt;li&gt;PC (Program Counter): 指向当前执行的指令 (在代码段内) 。&lt;/li&gt;
&lt;li&gt;SP (Stack Pointer): 指向栈顶。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-xv6-示例-地址空间&quot;&gt;3.2 XV6 示例 (地址空间)&lt;a class=&quot;anchor&quot; href=&quot;#32-xv6-示例-地址空间&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;XV6 是一个教学用的简单操作系统，其地址空间布局相对简单：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地址从低到高依次为：
&lt;ul&gt;
&lt;li&gt;代码 (text): 程序指令。&lt;/li&gt;
&lt;li&gt;数据 (data) &amp;amp; BSS: 初始化和未初始化的全局/静态变量。&lt;/li&gt;
&lt;li&gt;栈 (stack): 函数调用栈。&lt;/li&gt;
&lt;li&gt;堆 (heap): 动态分配区域。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;高地址处有 Trampoline 和 Trapframe 区域，用于用户态和内核态之间的切换，并且在两种模式下都映射。(&lt;code&gt;kernel/memlayout.h&lt;/code&gt; 中定义)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;33-相关概念解释-常用于虚拟内存&quot;&gt;3.3 相关概念解释 (常用于虚拟内存)&lt;a class=&quot;anchor&quot; href=&quot;#33-相关概念解释-常用于虚拟内存&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;活跃页面 (Active Page): 指当前正在被进程频繁访问的页面。&lt;/li&gt;
&lt;li&gt;工作集 (Working Set): 一个进程在最近一段时间 &lt;code&gt;Δ&lt;/code&gt; 内所访问到的页面集合。这是衡量进程当前运行所需内存大小的一个动态指标，反映了程序的局部性。如果一个进程的工作集能完全驻留在内存中，那么它就能高效运行，很少发生缺页中断。&lt;/li&gt;
&lt;li&gt;常驻集 (Resident Set): 指一个进程当前时刻实际驻留在物理内存中的页面集合。常驻集的大小受操作系统分配策略的影响。理想情况下，常驻集应该包含进程的工作集。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;4-物理内存管理-空闲空间管理&quot;&gt;4. 物理内存管理 (空闲空间管理)&lt;a class=&quot;anchor&quot; href=&quot;#4-物理内存管理-空闲空间管理&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;操作系统需要跟踪哪些物理内存是空闲的，哪些已被分配。&lt;/p&gt;
&lt;h3 id=&quot;41-管理数据结构&quot;&gt;4.1 管理数据结构&lt;a class=&quot;anchor&quot; href=&quot;#41-管理数据结构&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;位图 (Bitmap / Bit Vector):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将物理内存划分为固定大小的分配单元 (通常大小等于页框或几倍页框大小) 。&lt;/li&gt;
&lt;li&gt;位图中的每一位对应一个分配单元。&lt;/li&gt;
&lt;li&gt;位的值表示单元状态：&lt;code&gt;0&lt;/code&gt; 表示空闲，&lt;code&gt;1&lt;/code&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;空闲区表/链表 (Free List):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;维护一个包含所有空闲内存块 (区) 信息的数据结构。&lt;/li&gt;
&lt;li&gt;每个节点/表项记录一个空闲区的起始地址 (Start Address) 和 长度 (Length)。&lt;/li&gt;
&lt;li&gt;可以组织成表 (Array) 或链表 (Linked List)。&lt;/li&gt;
&lt;li&gt;链表类型:
&lt;ul&gt;
&lt;li&gt;隐式空闲链表 (Implicit Free List): 空闲块和已分配块都存储在内存中，通过块头部信息 (大小、是否空闲) 来遍历。分配和回收时需要查找。&lt;/li&gt;
&lt;li&gt;显式空闲链表 (Explicit Free List): 只将空闲块链接起来。头部包含指向下一个 (有时还有上一个) 空闲块的指针。查找空闲块更快，但维护链表指针有开销。&lt;/li&gt;
&lt;li&gt;分离空闲链表 (Segregated Free List): 维护多个空闲链表，每个链表负责特定大小范围的空闲块。分配时，根据请求大小直接去对应链表查找。可以加快分配速度，减少碎片。伙伴系统和 SLAB 分配器属于此类。&lt;/li&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;/ul&gt;
&lt;h3 id=&quot;42-衡量指标&quot;&gt;4.2 衡量指标&lt;a class=&quot;anchor&quot; href=&quot;#42-衡量指标&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;内存资源利用率: 目标是减少浪费。
&lt;ul&gt;
&lt;li&gt;内碎片 (Internal Fragmentation): 分配给进程的内存块大于进程实际请求的大小，块内部未被使用的部分。常见于固定分区、页式管理和固定大小分配策略 (如伙伴系统分配的块可能大于请求) 。&lt;/li&gt;
&lt;li&gt;外碎片 (External Fragmentation): 内存中存在足够多的空闲空间总和来满足一个请求，但这些空间不连续，而是散布在已分配块之间的小块。导致无法分配较大的连续内存块。常见于可变分区、段式管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;性能: 分配和回收内存操作的速度，以及这些操作占用的 CPU 时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;43-内存分配算法-针对空闲区表链表&quot;&gt;4.3 内存分配算法 (针对空闲区表/链表)&lt;a class=&quot;anchor&quot; href=&quot;#43-内存分配算法-针对空闲区表链表&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;当需要为进程分配长度为 &lt;code&gt;s&lt;/code&gt; 的内存时，如何在空闲区列表中选择一个合适的空闲块？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首次适配 (First Fit): 从链表/表的开头开始查找，选择第一个找到的大小 &lt;code&gt;&amp;gt;= s&lt;/code&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;下次适配 (Next Fit): 从上次分配操作结束的位置开始查找，选择第一个找到的大小 &lt;code&gt;&amp;gt;= s&lt;/code&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;最佳适配 (Best Fit): 遍历整个空闲链表/表，找到大小 &lt;code&gt;&amp;gt;= s&lt;/code&gt; 且最小的那个空闲块 (即差值 &lt;code&gt;size - s&lt;/code&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;最差适配 (Worst Fit): 遍历整个空闲链表/表，找到大小 &lt;code&gt;&amp;gt;= s&lt;/code&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;code&gt;Size&lt;/code&gt; 远大于请求大小 &lt;code&gt;s&lt;/code&gt;，通常会将其分割为两部分：一部分 (大小为 &lt;code&gt;s&lt;/code&gt;) 分配给进程，另一部分 (大小为 &lt;code&gt;Size - s&lt;/code&gt;) 变回一个新的、更小的空闲块。&lt;/p&gt;
&lt;p&gt;示例:
假设空闲区为 [15K, 23K], [48K, 20K], [80K, 30K]。进程 P5 请求 5K，进程 P6 请求 13K。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First Fit: P5 分配到 [15K, 5K]，剩余 [20K, 18K]。P6 分配到 [20K, 13K]，剩余 [33K, 5K]。空闲区变为 [33K, 5K], [48K, 20K], [80K, 30K]。&lt;/li&gt;
&lt;li&gt;Best Fit: P5 分配到 [48K, 5K]，剩余 [53K, 15K]。P6 分配到 [53K, 13K]，剩余 [66K, 2K]。空闲区变为 [15K, 23K], [66K, 2K], [80K, 30K]。&lt;/li&gt;
&lt;li&gt;Worst Fit: P5 分配到 [80K, 5K]，剩余 [85K, 25K]。P6 分配到 [85K, 13K]，剩余 [98K, 12K]。空闲区变为 [15K, 23K], [48K, 20K], [98K, 12K]。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;44-内存回收-针对空闲区表链表&quot;&gt;4.4 内存回收 (针对空闲区表/链表)&lt;a class=&quot;anchor&quot; href=&quot;#44-内存回收-针对空闲区表链表&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;当一个进程释放内存块时，需要将其归还给空闲列表。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;合并 (Coalescing): 为了减少碎片，回收时需要检查该块是否与物理上相邻的空闲块接壤。
&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;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;45-特定分配策略&quot;&gt;4.5 特定分配策略&lt;a class=&quot;anchor&quot; href=&quot;#45-特定分配策略&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;伙伴系统-buddy-system&quot;&gt;伙伴系统 (Buddy System)&lt;a class=&quot;anchor&quot; href=&quot;#伙伴系统-buddy-system&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;思想: 一种特殊的“分离适配”算法，用于管理大小为 2 的幂次的内存块。Linux 内核页分配器仍使用伙伴系统管理页框。&lt;/li&gt;
&lt;li&gt;结构: 将整个可用内存 (假设大小为 2&lt;sup&gt;U&lt;/sup&gt;) 看作一个块。维护 &lt;code&gt;U+1&lt;/code&gt; 个空闲链表，分别管理大小为 2&lt;sup&gt;0&lt;/sup&gt;, 2&lt;sup&gt;1&lt;/sup&gt;, …, 2&lt;sup&gt;U&lt;/sup&gt; 的空闲块。&lt;/li&gt;
&lt;li&gt;分配过程 (请求大小为 &lt;code&gt;s&lt;/code&gt;):
&lt;ol&gt;
&lt;li&gt;计算满足需求的最小 2 的幂次 &lt;code&gt;k&lt;/code&gt;，使得 2&lt;sup&gt;k-1&lt;/sup&gt; &amp;lt; &lt;code&gt;s&lt;/code&gt; ≤ 2&lt;sup&gt;k&lt;/sup&gt;。&lt;/li&gt;
&lt;li&gt;查找大小为 2&lt;sup&gt;k&lt;/sup&gt; 的空闲链表。&lt;/li&gt;
&lt;li&gt;如果找到，分配该块。&lt;/li&gt;
&lt;li&gt;如果没找到，则查找更大的块 (2&lt;sup&gt;k+1&lt;/sup&gt;)。找到后，将其分裂 (Split) 成两个大小相等的伙伴 (Buddies) (均为 2&lt;sup&gt;k&lt;/sup&gt;)。一个用于分配，另一个放入 2&lt;sup&gt;k&lt;/sup&gt; 的空闲链表。&lt;/li&gt;
&lt;li&gt;如果 2&lt;sup&gt;k+1&lt;/sup&gt; 也没有，继续向上查找并递归分裂，直到找到可分配的块。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;回收过程 (释放大小为 2&lt;sup&gt;k&lt;/sup&gt; 的块 &lt;code&gt;B&lt;/code&gt;):
&lt;ol&gt;
&lt;li&gt;查找块 &lt;code&gt;B&lt;/code&gt; 的伙伴 &lt;code&gt;B&amp;#39;&lt;/code&gt; (地址可以通过异或运算计算得到)。&lt;/li&gt;
&lt;li&gt;检查伙伴 &lt;code&gt;B&amp;#39;&lt;/code&gt; 是否也空闲且大小相同 (2&lt;sup&gt;k&lt;/sup&gt;)。&lt;/li&gt;
&lt;li&gt;如果是，则将 &lt;code&gt;B&lt;/code&gt; 和 &lt;code&gt;B&amp;#39;&lt;/code&gt; 合并 (Merge) 成一个更大的块 (2&lt;sup&gt;k+1&lt;/sup&gt;)，并递归尝试与新块的伙伴合并。&lt;/li&gt;
&lt;li&gt;如果否，则将 &lt;code&gt;B&lt;/code&gt; 加入 2&lt;sup&gt;k&lt;/sup&gt; 的空闲链表。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;优点: 分裂和合并相对高效 (伙伴地址计算快) ，能较好地控制外部碎片 (合并机制) 。&lt;/li&gt;
&lt;li&gt;缺点: 存在内碎片 (分配的块大小必须是 2 的幂，可能大于实际需求) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;伙伴系统示例 (1MB 内存):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;初始: Free list for 1M: [Block 1M]&lt;/li&gt;
&lt;li&gt;A 申请 100K: 需要 128K (2&lt;sup&gt;7&lt;/sup&gt;)。分裂 1M -&amp;gt; 512K + 512K; 分裂 512K -&amp;gt; 256K + 256K; 分裂 256K -&amp;gt; 128K + 128K。分配一个 128K 给 A。
&lt;ul&gt;
&lt;li&gt;Memory: [A=128K][Free 128K][Free 256K][Free 512K]&lt;/li&gt;
&lt;li&gt;Free lists: 128K:[1 block], 256K:[1 block], 512K:[1 block]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;B 申请 240K: 需要 256K (2&lt;sup&gt;8&lt;/sup&gt;)。分配链表中的 256K 给 B。
&lt;ul&gt;
&lt;li&gt;Memory: [A=128K][Free 128K][B=256K][Free 512K]&lt;/li&gt;
&lt;li&gt;Free lists: 128K:[1 block], 512K:[1 block]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;C 申请 64K: 需要 64K (2&lt;sup&gt;6&lt;/sup&gt;)。分裂 128K -&amp;gt; 64K + 64K。分配一个 64K 给 C。
&lt;ul&gt;
&lt;li&gt;Memory: [A=128K][C=64K][Free 64K][B=256K][Free 512K]&lt;/li&gt;
&lt;li&gt;Free lists: 64K:[1 block], 512K:[1 block]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;D 申请 256K: 需要 256K (2&lt;sup&gt;8&lt;/sup&gt;)。分裂 512K -&amp;gt; 256K + 256K。分配一个 256K 给 D。
&lt;ul&gt;
&lt;li&gt;Memory: [A=128K][C=64K][Free 64K][B=256K][D=256K][Free 256K]&lt;/li&gt;
&lt;li&gt;Free lists: 64K:[1 block], 256K:[1 block]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;释放 B (256K): 它的伙伴 D=256K 未释放。将 B 加入 256K 链表。
&lt;ul&gt;
&lt;li&gt;Memory: [A=128K][C=64K][Free 64K][Free 256K][D=256K][Free 256K]&lt;/li&gt;
&lt;li&gt;Free lists: 64K:[1 block], 256K:[2 blocks]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;释放 A (128K): 它的伙伴 C=64K+Free 64K 中的 Free 64K 是否是伙伴？需要看具体地址。假设 A 的伙伴是 [C=64K][Free 64K] 中的 Free 64K 之前的那个 128K 块，则 A 不能立即合并。将 A 加入 128K 链表。 (&lt;em&gt;原始图示似乎假设 A 的伙伴是 Free 128K，那释放 A 时应该合并成 256K&lt;/em&gt;。我们按原始图示逻辑继续)
&lt;ul&gt;
&lt;li&gt;按图示：释放 A(128K) -&amp;gt; 与其伙伴(Free 128K)合并 -&amp;gt; 256K。&lt;/li&gt;
&lt;li&gt;Memory: [Free 256K][C=64K][Free 64K][B=256K][D=256K][Free 256K] (B 已经释放) -&amp;gt; [Free 256K][C=64K][Free 64K][Free 256K][D=256K][Free 256K]&lt;/li&gt;
&lt;li&gt;Free lists: 64K:[1 block], 256K:[3 blocks]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;E 申请 75K: 需要 128K。从 Free 256K 中分裂 -&amp;gt; 128K+128K。分配一个 128K 给 E。
&lt;ul&gt;
&lt;li&gt;Memory: [E=128K][Free 128K][C=64K][Free 64K][Free 256K][D=256K][Free 256K]&lt;/li&gt;
&lt;li&gt;Free lists: 64K:[1 block], 128K:[1 block], 256K:[2 blocks]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;释放 C (64K): 伙伴是 Free 64K。合并 -&amp;gt; 128K。这个 128K 的伙伴是 E=128K (未释放)。将这个新 128K 加入链表。
&lt;ul&gt;
&lt;li&gt;Memory: [E=128K][Free 128K][Free 128K][Free 256K][D=256K][Free 256K]&lt;/li&gt;
&lt;li&gt;Free lists: 128K:[2 blocks], 256K:[2 blocks]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;释放 E (128K): 伙伴是 Free 128K (来自 C 的合并)。合并 -&amp;gt; 256K。这个 256K 的伙伴是另一个 Free 128K  (原始 A 分裂剩下的) ？地址决定。按图示，它与另一个 128K 合并成 256K，再与相邻的 Free 256K 合并成 512K。
&lt;ul&gt;
&lt;li&gt;Memory: [Free 512K][Free 256K][D=256K][Free 256K]&lt;/li&gt;
&lt;li&gt;Free lists: 256K:[2 blocks], 512K:[1 block]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;释放 D (256K): 伙伴是 Free 256K。合并 -&amp;gt; 512K。这个 512K 的伙伴是 Free 512K。合并 -&amp;gt; 1M。
&lt;ul&gt;
&lt;li&gt;Memory: [Free 1M]&lt;/li&gt;
&lt;li&gt;Free lists: 1M:[1 block]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;slabslubslob-分配器&quot;&gt;SLAB/SLUB/SLOB 分配器&lt;a class=&quot;anchor&quot; href=&quot;#slabslubslob-分配器&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;目的: 高效管理内核中频繁分配和释放的小内存对象 (如 inode、task_struct 等) 。伙伴系统分配的最小块可能仍太大，导致内碎片。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本思想:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将伙伴系统分配的大块内存 (称为 “slab”) 进一步细分成多个固定大小的小对象 (object)。&lt;/li&gt;
&lt;li&gt;为每种类型的对象维护一个或多个 slab 缓存 (cache)。&lt;/li&gt;
&lt;li&gt;分配对象时，从对应的 cache 中快速获取一个空闲对象。&lt;/li&gt;
&lt;li&gt;释放对象时，将其放回原 cache，通常无需立即归还给伙伴系统。&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;/li&gt;
&lt;li&gt;分配和释放速度快 (对象通常已初始化，且无需查找) 。&lt;/li&gt;
&lt;li&gt;利用缓存局部性 (对象在 L1/L2 cache 中可能仍然有效) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SLAB 分配器 (原始, Jeff Bonwick, Solaris -&amp;gt; Linux):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为每种对象类型维护一个 &lt;code&gt;kmem_cache&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;每个 cache 包含多个 slab (通常是 1 或多个物理页)。&lt;/li&gt;
&lt;li&gt;Slab 内包含对象和元数据。&lt;/li&gt;
&lt;li&gt;Slab 分为：全满 (full)、部分空闲 (partial)、全空 (empty) 三种链表。分配优先从 partial slab 获取。&lt;/li&gt;
&lt;li&gt;问题: 实现相对复杂，元数据管理开销较大，多核环境下锁竞争可能成为瓶颈。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SLUB 分配器 (改进, Pekka Enberg, Linux 2.6.22+):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目标: 简化设计，提高性能和可伸缩性。是当前 Linux 默认的分配器。&lt;/li&gt;
&lt;li&gt;简化: 去除了复杂的 slab 链表管理，主要将 page (物理页) 作为 slab 进行管理。元数据存储开销更小。&lt;/li&gt;
&lt;li&gt;性能: 减少锁竞争，对 NUMA (Non-Uniform Memory Access) 架构和多核系统优化更好 (利用 per-CPU 缓存) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SLOB 分配器 (简单):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目标: 极简、紧凑，适用于代码大小和内存开销受限的嵌入式系统。&lt;/li&gt;
&lt;li&gt;实现: 使用简单的首次适配算法在小的内存块 (slab) 内分配。不适合高性能、大内存系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看 Slab 信息 (Linux):&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;cat /proc/slabinfo
# 或者使用 slabtop 工具
slabtop&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(&lt;code&gt;slabinfo&lt;/code&gt; 提供详细的 cache 列表，包括对象大小、活动对象数、总对象数、slab 数等信息。)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;5-基本内存管理方案&quot;&gt;5. 基本内存管理方案&lt;a class=&quot;anchor&quot; href=&quot;#5-基本内存管理方案&quot;&gt;#&lt;/a&gt;&lt;/h2&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;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;不划分 (除 OS 外)&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;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;td&gt;基址+界限寄存器 (每个进程)&lt;/td&gt;&lt;td&gt;按需分配，灵活，但产生外碎片，需要压缩技术&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页式 (Paging)&lt;/td&gt;&lt;td&gt;页&lt;/td&gt;&lt;td&gt;固定大小的页框&lt;/td&gt;&lt;td&gt;内碎片 (最后一页)&lt;/td&gt;&lt;td&gt;页表 (MMU硬件查表)&lt;/td&gt;&lt;td&gt;消除外碎片，分配管理简单，不要求连续，常用&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;段式 (Segmentation)&lt;/td&gt;&lt;td&gt;段&lt;/td&gt;&lt;td&gt;动态按需划分的段&lt;/td&gt;&lt;td&gt;外碎片&lt;/td&gt;&lt;td&gt;段表 (MMU硬件查表)&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;td&gt;段表 + 页表 (MMU硬件查表)&lt;/td&gt;&lt;td&gt;结合二者优点，管理复杂，开销大&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr/&gt;
&lt;h3 id=&quot;51-单一连续区-single-contiguous-allocation&quot;&gt;5.1 单一连续区 (Single Contiguous Allocation)&lt;a class=&quot;anchor&quot; href=&quot;#51-单一连续区-single-contiguous-allocation&quot;&gt;#&lt;/a&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;/ul&gt;
&lt;h3 id=&quot;52-固定分区-fixed-partitioning&quot;&gt;5.2 固定分区 (Fixed Partitioning)&lt;a class=&quot;anchor&quot; href=&quot;#52-固定分区-fixed-partitioning&quot;&gt;#&lt;/a&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;ul&gt;
&lt;li&gt;内碎片: 分配给进程的分区可能大于进程实际需要的大小。&lt;/li&gt;
&lt;li&gt;不灵活: 分区大小固定，大进程可能无处容身，小进程占用大分区造成浪费。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;53-可变分区-variable-partitioning--dynamic-partitioning&quot;&gt;5.3 可变分区 (Variable Partitioning / Dynamic Partitioning)&lt;a class=&quot;anchor&quot; href=&quot;#53-可变分区-variable-partitioning--dynamic-partitioning&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;特点: 内存不预先划分。根据进程的实际需求，从空闲内存 (洞 Hole)  中动态地分割出一个分区分配给它。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分配: 使用 First Fit, Best Fit, Worst Fit 等算法在空闲区列表中查找并分配。&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;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;ul&gt;
&lt;li&gt;紧缩技术 (Compaction / Memory Compaction):
&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;54-页式管理-paging&quot;&gt;5.4 页式管理 (Paging)&lt;a class=&quot;anchor&quot; href=&quot;#54-页式管理-paging&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;核心思想:
&lt;ul&gt;
&lt;li&gt;逻辑地址空间: 划分为固定大小的块，称为 页 (Page)。&lt;/li&gt;
&lt;li&gt;物理内存空间: 划分为与页大小相同的块，称为 页框 (Page Frame) 或物理页面、内存块。&lt;/li&gt;
&lt;li&gt;分配: 以页为单位进行。进程需要的页可以加载到任意空闲的页框中。逻辑上相邻的页在物理上不必相邻。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;逻辑地址结构: &lt;code&gt;逻辑地址 = 页号 (Page Number) + 页内偏移 (Offset)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;例如，32位地址，页面大小 4KB (2&lt;sup&gt;12&lt;/sup&gt; B):
&lt;ul&gt;
&lt;li&gt;高 20位 (31-12) 是页号。&lt;/li&gt;
&lt;li&gt;低 12位 (11-0) 是页内偏移。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;数据结构:
&lt;ul&gt;
&lt;li&gt;页表 (Page Table): 每个进程都有一个页表。
&lt;ul&gt;
&lt;li&gt;功能: 记录逻辑页号到物理页框号的映射关系。&lt;/li&gt;
&lt;li&gt;页表项 (Page Table Entry - PTE): 至少包含 &lt;code&gt;页框号 (Frame Number)&lt;/code&gt;。通常还包含其他控制位：
&lt;ul&gt;
&lt;li&gt;有效位/驻留位 (Valid/Present Bit): 标记该页是否在物理内存中。&lt;/li&gt;
&lt;li&gt;保护位 (Protection Bits): 控制读/写/执行权限。&lt;/li&gt;
&lt;li&gt;修改位 (Modified/Dirty Bit): 标记该页加载到内存后是否被修改过。&lt;/li&gt;
&lt;li&gt;访问位 (Accessed/Referenced Bit): 标记该页是否被访问过。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;存储: 页表本身也存储在内存中。操作系统通过页表基址寄存器 (Page Table Base Register - PTBR) (如 x86 的 CR3 寄存器) 指向当前进程的页表起始地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;空闲页框列表: 操作系统需要维护一个数据结构 (如位图或链表) 来跟踪哪些物理页框是空闲的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;地址转换过程 (硬件 MMU):
&lt;ol&gt;
&lt;li&gt;CPU 发出逻辑地址。&lt;/li&gt;
&lt;li&gt;MMU 将逻辑地址分解为页号 &lt;code&gt;p&lt;/code&gt; 和页内偏移 &lt;code&gt;d&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;使用页号 &lt;code&gt;p&lt;/code&gt; 作为索引，访问当前进程的页表 (基址在 PTBR)。&lt;/li&gt;
&lt;li&gt;找到对应的页表项 (PTE)。&lt;/li&gt;
&lt;li&gt;检查 PTE 中的有效位和保护位。如果无效或权限不足，则产生缺页异常 (Page Fault) 或保护异常，陷入操作系统处理。&lt;/li&gt;
&lt;li&gt;如果有效且权限允许，从 PTE 中取出页框号 &lt;code&gt;f&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;将页框号 &lt;code&gt;f&lt;/code&gt; 与页内偏移 &lt;code&gt;d&lt;/code&gt; 拼接 (或 &lt;code&gt;f * PageSize + d&lt;/code&gt;) 得到最终的物理地址。&lt;/li&gt;
&lt;li&gt;访问物理内存。&lt;/li&gt;
&lt;/ol&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;li&gt;地址转换开销: 每次访存理论上需要两次内存访问 (一次查页表，一次访问数据) 。实际使用 TLB (Translation Lookaside Buffer)，一种页表项的高速缓存，来加速转换。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;55-段式管理-segmentation&quot;&gt;5.5 段式管理 (Segmentation)&lt;a class=&quot;anchor&quot; href=&quot;#55-段式管理-segmentation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;核心思想:
&lt;ul&gt;
&lt;li&gt;逻辑地址空间: 按照程序的逻辑结构划分为多个段 (Segment)，如代码段、数据段、栈段等。每个段有自己的名字 (通常用段号代替) 和长度。段的长度可以不同。&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;code&gt;逻辑地址 = 段号 (Segment Number) + 段内偏移 (Offset within Segment)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;数据结构:
&lt;ul&gt;
&lt;li&gt;段表 (Segment Table): 每个进程一个段表。
&lt;ul&gt;
&lt;li&gt;功能: 记录逻辑段号到物理内存信息的映射。&lt;/li&gt;
&lt;li&gt;段表项 (Segment Table Entry - STE): 通常包含：
&lt;ul&gt;
&lt;li&gt;段基址 (Segment Base): 该段在物理内存中的起始地址。&lt;/li&gt;
&lt;li&gt;段限长 (Segment Limit): 该段的长度。&lt;/li&gt;
&lt;li&gt;保护位 (Protection Bits): 如读/写/执行权限。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;存储: 段表本身也存储在内存中。操作系统通过段表基址寄存器 (Segment Table Base Register - STBR) 指向当前进程的段表。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;物理内存管理: 类似于可变分区管理，需要维护空闲区列表，使用 First Fit 等算法分配连续空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;地址转换过程 (硬件 MMU):
&lt;ol&gt;
&lt;li&gt;CPU 发出逻辑地址。&lt;/li&gt;
&lt;li&gt;MMU 将逻辑地址分解为段号 &lt;code&gt;s&lt;/code&gt; 和段内偏移 &lt;code&gt;d&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;使用段号 &lt;code&gt;s&lt;/code&gt; 作为索引，访问当前进程的段表 (基址在 STBR)。&lt;/li&gt;
&lt;li&gt;找到对应的段表项 (STE)。&lt;/li&gt;
&lt;li&gt;检查:
&lt;ul&gt;
&lt;li&gt;段号 &lt;code&gt;s&lt;/code&gt; 是否合法 (在段表范围内) 。&lt;/li&gt;
&lt;li&gt;段内偏移 &lt;code&gt;d&lt;/code&gt; 是否小于段限长 &lt;code&gt;Limit&lt;/code&gt; (&lt;code&gt;0 &amp;lt;= d &amp;lt; Limit&lt;/code&gt;)。如果超出，则产生地址越界异常。&lt;/li&gt;
&lt;li&gt;访问权限是否允许。如果不允许，则产生保护异常。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果检查通过，取出段基址 &lt;code&gt;Base&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;计算物理地址: 物理地址 = 段基址 &lt;code&gt;Base&lt;/code&gt; + 段内偏移 &lt;code&gt;d&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;访问物理内存。&lt;/li&gt;
&lt;/ol&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;56-段页式管理-segmented-paging&quot;&gt;5.6 段页式管理 (Segmented Paging)&lt;a class=&quot;anchor&quot; href=&quot;#56-段页式管理-segmented-paging&quot;&gt;#&lt;/a&gt;&lt;/h3&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;code&gt;逻辑地址 = 段号 s + 段内页号 p + 页内偏移 d&lt;/code&gt;
(或者看作 &lt;code&gt;段号 s + 段内偏移 offset&lt;/code&gt;，其中 &lt;code&gt;offset&lt;/code&gt; 再被解释为 &lt;code&gt;页号 p + 页内偏移 d&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;数据结构:
&lt;ul&gt;
&lt;li&gt;段表 (Segment Table): 每个进程一个。
&lt;ul&gt;
&lt;li&gt;段表项: 不再直接指向物理基址，而是指向该段对应的页表的基址，并包含页表的长度 (或段的页数) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;页表 (Page Table): 每个段拥有一个页表。
&lt;ul&gt;
&lt;li&gt;页表项: 记录段内的逻辑页号到物理页框号的映射。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;地址转换过程 (硬件 MMU):
&lt;ol&gt;
&lt;li&gt;CPU 发出逻辑地址 &lt;code&gt;(s, offset)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;用段号 &lt;code&gt;s&lt;/code&gt; 查段表，找到对应段的页表基址和段限长。&lt;/li&gt;
&lt;li&gt;检查段内偏移 &lt;code&gt;offset&lt;/code&gt; 是否小于段限长。如果超出，则地址越界。&lt;/li&gt;
&lt;li&gt;将段内偏移 &lt;code&gt;offset&lt;/code&gt; 分解为段内页号 &lt;code&gt;p&lt;/code&gt; 和页内偏移 &lt;code&gt;d&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;使用段内页号 &lt;code&gt;p&lt;/code&gt; 作为索引，访问该段的页表 (基址来自段表项) ，找到对应的页表项 (PTE)。&lt;/li&gt;
&lt;li&gt;检查 PTE 的有效位和保护位。&lt;/li&gt;
&lt;li&gt;从 PTE 中取出页框号 &lt;code&gt;f&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;计算物理地址: 物理地址 = 页框号 &lt;code&gt;f&lt;/code&gt; * PageSize + 页内偏移 &lt;code&gt;d&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;访问物理内存。&lt;/li&gt;
&lt;/ol&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;li&gt;地址转换更复杂: 需要多次内存访问 (查段表 -&amp;gt; 查页表 -&amp;gt; 访问数据) 。同样需要 TLB 来加速。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;6-内存-扩充-技术&quot;&gt;6. 内存 “扩充” 技术&lt;a class=&quot;anchor&quot; href=&quot;#6-内存-扩充-技术&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;在物理内存不足时，让系统能运行更大程序或更多进程的技术。&lt;/p&gt;
&lt;h3 id=&quot;61-覆盖技术-overlaying&quot;&gt;6.1 覆盖技术 (Overlaying)&lt;a class=&quot;anchor&quot; href=&quot;#61-覆盖技术-overlaying&quot;&gt;#&lt;/a&gt;&lt;/h3&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;程序员负责: 需要程序员手动划分程序模块，并指定它们之间的覆盖结构 (Overlay Structure)。&lt;/li&gt;
&lt;li&gt;操作系统提供加载覆盖模块的机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;示例: 程序 A 调用 B 或 C；B 调用 D 或 E；C 调用 F。
&lt;ul&gt;
&lt;li&gt;常驻区: A (8K)&lt;/li&gt;
&lt;li&gt;覆盖区 0: B (8K) 或 C (10K) -&amp;gt; 需要 10K&lt;/li&gt;
&lt;li&gt;覆盖区 1: D (12K) 或 E (4K) (当B在内存时) 或 F (10K) (当C在内存时) -&amp;gt; 需要 12K&lt;/li&gt;
&lt;li&gt;总需内存 = 常驻区 + Max(覆盖区0) + Max(覆盖区1) = 8K + 10K + 12K = 30K (远小于原始总和 54K)。&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;应用: 主要用于早期内存极其有限的操作系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;62-交换技术-swapping&quot;&gt;6.2 交换技术 (Swapping)&lt;a class=&quot;anchor&quot; href=&quot;#62-交换技术-swapping&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;目的: 提高内存利用率和系统吞吐量，允许运行的进程总大小超过物理内存。&lt;/li&gt;
&lt;li&gt;思想: 将暂时不运行的进程完整地从内存移动到外存 (交换区 Swap Space) ，称为换出 (Swap Out / Roll Out)。当需要再次运行时，再从外存将其换回 (Swap In / Roll In) 到内存中。&lt;/li&gt;
&lt;li&gt;实现:
&lt;ul&gt;
&lt;li&gt;Swapper (交换程序): 操作系统中负责执行交换操作的模块。&lt;/li&gt;
&lt;li&gt;交换区 (Swap Space): 通常是磁盘上一块连续或特殊管理的区域，用于快速读写整个进程映像。&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;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;换出进程选择: 考虑进程状态 (不应换出等待 I/O 的进程) 、优先级、在内存驻留时间等因素。&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;开销大: 整个进程映像的磁盘 I/O 耗时。&lt;/li&gt;
&lt;li&gt;可能导致抖动 (Thrashing): 如果内存严重不足，系统可能花费大量时间在换入换出进程上，而实际执行用户代码的时间很少。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;应用: 曾用于分时系统，现代系统中的虚拟内存可以看作是更精细化的交换技术 (以页为单位) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;63-虚拟内存技术-virtual-memory&quot;&gt;6.3 虚拟内存技术 (Virtual Memory)&lt;a class=&quot;anchor&quot; href=&quot;#63-虚拟内存技术-virtual-memory&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;(本讲义中提及，但未详细展开，通常是后续章节内容)
结合了请求调页 (Demand Paging) 或请求分段 (Demand Segmentation) 与交换技术的思想。允许程序只加载部分页面/段到内存即可运行，其余部分在需要时才从磁盘加载。这是现代操作系统普遍采用的核心内存管理技术。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;7-重点小结&quot;&gt;7. 重点小结&lt;a class=&quot;anchor&quot; href=&quot;#7-重点小结&quot;&gt;#&lt;/a&gt;&lt;/h2&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;特定策略：伙伴系统、SLAB/SLUB/SLOB 分配器。&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;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 04: Process and Thread Scheduling</title><link>https://www.lyt0112.com/blog/operating_systems_note_04-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_04-zh</guid><description>Operating Systems Notes 04: Process and Thread Scheduling</description><pubDate>Fri, 28 Mar 2025 04:15:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-进程线程调度问题分析&quot;&gt;1. 进程/线程调度问题分析&lt;a class=&quot;anchor&quot; href=&quot;#1-进程线程调度问题分析&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;11-when-and-how-调度时机与原因&quot;&gt;1.1 When and How (调度时机与原因)&lt;a class=&quot;anchor&quot; href=&quot;#11-when-and-how-调度时机与原因&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;调度这件事儿什么时候做做的理由有哪些&quot;&gt;调度这件事儿什么时候做？做的理由有哪些？&lt;a class=&quot;anchor&quot; href=&quot;#调度这件事儿什么时候做做的理由有哪些&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;调度时机 (When):&lt;/p&gt;
&lt;p&gt;操作系统需要在特定事件发生时决定哪个进程接下来应该占用CPU。这些时机主要包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;进程创建 (Process Creation): 当一个新进程被创建时 (例如，通过&lt;code&gt;fork()&lt;/code&gt;系统调用) ，需要决定是运行父进程还是子进程，或者其他进程。&lt;/li&gt;
&lt;li&gt;进程终止 (Process Termination): 当一个进程执行完毕或被终止时 (例如，调用&lt;code&gt;exit()&lt;/code&gt;) ，它占用的CPU必须分配给其他就绪的进程。&lt;/li&gt;
&lt;li&gt;进程阻塞 (Process Blocking): 当一个进程因等待某个事件 (如I/O操作完成、等待信号量、等待用户输入等) 而无法继续执行时，它会进入阻塞状态，此时调度器需要选择另一个进程来运行。&lt;/li&gt;
&lt;li&gt;I/O中断发生 (I/O Interrupt): 当一个I/O操作完成，产生中断时，原先等待该I/O的进程可能会从阻塞态变为就绪态。这时，调度器可能需要重新评估，决定是继续运行当前进程，还是切换到刚刚变为就绪的、可能优先级更高的进程。&lt;/li&gt;
&lt;li&gt;时钟中断发生 (Clock Interrupt): 在分时系统中，为了防止某个进程长时间独占CPU，操作系统会设置一个定时器。当定时器中断发生时，当前运行进程的时间片 (Time Slice/Quantum) 可能已用完，调度器会介入，决定是继续运行该进程 (如果时间片未用完或没有其他就绪进程) ，还是切换到另一个就绪进程 (抢占式调度) 。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;调度的理由 (Why):&lt;/p&gt;
&lt;p&gt;调度的根本目的是有效、公平地管理和分配有限的CPU资源给多个并发执行的进程。具体理由包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;提高CPU利用率: 尽量让CPU保持忙碌状态，减少空闲时间。当一个进程等待I/O时，可以让其他就绪进程使用CPU。&lt;/li&gt;
&lt;li&gt;提高系统吞吐量 (Throughput): 单位时间内完成的进程数量。好的调度算法可以在满足其他目标的同时，尽可能多地完成任务。&lt;/li&gt;
&lt;li&gt;减少周转时间 (Turnaround Time): 指一个进程从提交到完成所花费的总时间 (等待进入内存、在就绪队列等待、CPU执行、I/O执行的总和) 。&lt;/li&gt;
&lt;li&gt;减少等待时间 (Waiting Time): 指进程在就绪队列中等待CPU所花费的总时间。&lt;/li&gt;
&lt;li&gt;减少响应时间 (Response Time): 对于交互式系统尤其重要，指从用户发出请求到系统首次产生响应 (而非完成任务) 所花费的时间。&lt;/li&gt;
&lt;li&gt;确保公平性 (Fairness): 保证每个进程都能获得合理的CPU时间份额，防止某些进程被饿死 (Starvation) 。&lt;/li&gt;
&lt;li&gt;满足实时性要求 (Real-time Constraints): 对于实时系统，调度必须保证关键任务在它们的截止时间 (Deadline) 之前完成。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;如果没有可被调度的进程系统做什么呢&quot;&gt;如果没有可被调度的进程，系统做什么呢？&lt;a class=&quot;anchor&quot; href=&quot;#如果没有可被调度的进程系统做什么呢&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;如果当前没有用户进程或系统核心任务进程处于就绪状态 (Ready State) ，CPU不能完全停止。操作系统通常会执行一个特殊的空闲进程 (Idle Process) 或称为系统空闲任务 (System Idle Task)。&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;更重要的是，在许多架构上 (如x86) ，它可以执行一个特殊的指令 (如&lt;code&gt;HLT&lt;/code&gt; - Halt) ，使CPU进入低功耗状态，直到下一个中断 (如时钟中断、I/O中断) 唤醒CPU。这有助于节能和降低温度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;目的: 确保CPU总是有事可做 (即使是“等待”) ，并提供一个合法的状态供调度器切换，同时优化能源使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;上下文切换的过程有哪些开销&quot;&gt;上下文切换的过程？有哪些开销？&lt;a class=&quot;anchor&quot; href=&quot;#上下文切换的过程有哪些开销&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;上下文切换 (Context Switch) 是指操作系统保存当前正在运行进程的状态 (上下文) ，并加载另一个进程的状态，以便让后者开始或继续运行的过程。这是实现多任务处理的基础。&lt;/p&gt;
&lt;p&gt;过程:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;中断/系统调用触发: 调度发生 (如时间片用完、进程阻塞等) 。&lt;/li&gt;
&lt;li&gt;保存当前进程上下文:
&lt;ul&gt;
&lt;li&gt;保存程序计数器 (Program Counter, PC) 和其他CPU寄存器 (通用寄存器、状态寄存器等) 的值。这些值通常保存在该进程的进程控制块 (Process Control Block, PCB) 中。&lt;/li&gt;
&lt;li&gt;保存当前进程的栈指针。&lt;/li&gt;
&lt;li&gt;更新进程状态 (例如，从 “Running” 变为 “Ready” 或 “Blocked”) 。&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;从选定进程的PCB中恢复其状态。&lt;/li&gt;
&lt;li&gt;加载新进程的程序计数器和CPU寄存器。&lt;/li&gt;
&lt;li&gt;恢复新进程的栈指针。&lt;/li&gt;
&lt;li&gt;更新新进程的状态 (通常是从 “Ready” 变为 “Running”) 。&lt;/li&gt;
&lt;li&gt;恢复内存管理信息 (可能需要刷新TLB - Translation Lookaside Buffer) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;跳转执行: CPU跳转到新进程被中断时的下一条指令地址 (或其入口点，如果是首次运行) 开始执行。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;开销:&lt;/p&gt;
&lt;p&gt;上下文切换本身并不执行任何有用的用户工作，它是一种纯粹的开销 (Overhead)。开销主要包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;直接开销 (Direct Costs):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保存和加载寄存器: CPU需要时间来读写寄存器和PCB。&lt;/li&gt;
&lt;li&gt;执行调度器代码: 选择下一个进程也需要CPU时间。&lt;/li&gt;
&lt;li&gt;更新PCB和其他数据结构: 维护进程队列等操作需要时间。&lt;/li&gt;
&lt;li&gt;MMU操作: 可能需要加载新的页表基址，这可能导致TLB被刷新 (TLB flush) ，增加后续内存访问的延迟。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;间接开销 (Indirect Costs):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓存污染 (Cache Pollution): 当新进程开始运行时，CPU缓存 (L1, L2, L3 Cache) 中很可能包含的是前一个进程的数据和指令。新进程运行时会发生大量的缓存未命中 (Cache Miss) ，需要从内存中重新加载数据，这会显著降低执行速度，直到新进程的“工作集” (Working Set) 被加载到缓存中。这是上下文切换最主要的性能影响之一。&lt;/li&gt;
&lt;li&gt;CPU流水线冲刷: 切换可能导致CPU的指令流水线被清空和重建。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;建议: 频繁的上下文切换会显著降低系统整体性能。因此，调度算法和系统设计 (如时间片大小的选择) 需要在响应时间和系统吞吐量/效率之间找到平衡。&lt;/p&gt;
&lt;h4 id=&quot;关于调度算法我们都关心什么&quot;&gt;关于调度算法，我们都关心什么？&lt;a class=&quot;anchor&quot; href=&quot;#关于调度算法我们都关心什么&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;我们在评估和选择调度算法时，主要关心以下几个性能指标 (Performance Metrics) 或目标 (Goals)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU 利用率 (CPU Utilization): CPU处于忙碌状态的时间百分比。越高越好，但100%可能意味着没有冗余，响应性可能变差。&lt;/li&gt;
&lt;li&gt;系统吞吐量 (Throughput): 单位时间内完成的进程 (或作业) 数量。越高越好。&lt;/li&gt;
&lt;li&gt;周转时间 (Turnaround Time): 从进程提交到完成的总时间。越短越好 (平均周转时间、最差周转时间) 。&lt;/li&gt;
&lt;li&gt;等待时间 (Waiting Time): 进程在就绪队列中等待CPU的总时间。越短越好 (平均等待时间、最差等待时间) 。&lt;/li&gt;
&lt;li&gt;响应时间 (Response Time): 从提交请求到产生第一个响应的时间 (交互式系统关键) 。越短且越稳定越好。&lt;/li&gt;
&lt;li&gt;公平性 (Fairness): 每个进程获得合理的CPU份额，防止饿死。&lt;/li&gt;
&lt;li&gt;可预测性 (Predictability): 对于实时系统，执行时间的可预测性比平均性能更重要。&lt;/li&gt;
&lt;li&gt;满足截止时间 (Meeting Deadlines): 对于实时系统，这是硬性或软性要求。&lt;/li&gt;
&lt;li&gt;优先级处理 (Priority Handling): 系统能否有效处理不同优先级的进程。&lt;/li&gt;
&lt;li&gt;资源平衡 (Resource Balance): 尽量保持所有资源 (CPU, I/O设备) 都处于忙碌状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;不同类型的操作系统都适用同一种调度算法吗&quot;&gt;不同类型的操作系统都适用同一种调度算法吗？&lt;a class=&quot;anchor&quot; href=&quot;#不同类型的操作系统都适用同一种调度算法吗&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;不适用。 不同类型的操作系统有着不同的设计目标和用户需求，因此需要采用不同的调度策略。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;批处理系统 (Batch Systems):
&lt;ul&gt;
&lt;li&gt;目标：最大化吞吐量和CPU利用率，减少平均周转时间。用户通常不直接与系统交互。&lt;/li&gt;
&lt;li&gt;适用算法：先来先服务 (FCFS)、最短作业优先 (SJF)、最高响应比优先 (HRRN)。公平性和响应时间相对不重要。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;交互式系统 (Interactive Systems / Time-Sharing Systems):
&lt;ul&gt;
&lt;li&gt;目标：最小化响应时间，提供良好的用户体验，兼顾公平性。&lt;/li&gt;
&lt;li&gt;适用算法：轮转法 (Round Robin, RR)、优先级调度、多级队列调度 (Multi-level Queue)、多级反馈队列调度 (Multi-level Feedback Queue, MLFQ)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;实时系统 (Real-Time Systems, RTOS):
&lt;ul&gt;
&lt;li&gt;目标：满足任务的截止时间要求，可预测性至关重要。分为硬实时 (必须满足) 和软实时 (尽量满足) 。&lt;/li&gt;
&lt;li&gt;适用算法：速率单调调度 (Rate Monotonic Scheduling, RMS - 用于静态优先级)、最早截止时间优先 (Earliest Deadline First, EDF - 用于动态优先级)、优先级调度 (配合精确的优先级分配) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;对于一个调度算法应该追求什么样的目标&quot;&gt;对于一个调度算法，应该追求什么样的目标？&lt;a class=&quot;anchor&quot; href=&quot;#对于一个调度算法应该追求什么样的目标&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;一个调度算法应该追求的目标组合取决于具体的系统类型和应用场景。通常需要在多个 (有时是相互冲突的) 目标之间进行权衡 (Trade-off)。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通用目标 (All Systems):
&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;/li&gt;
&lt;li&gt;批处理系统目标:
&lt;ul&gt;
&lt;li&gt;高吞吐量&lt;/li&gt;
&lt;li&gt;低周转时间&lt;/li&gt;
&lt;li&gt;高CPU利用率&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;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;h4 id=&quot;选进程时都考虑了哪些点单一因素还是多因素&quot;&gt;选进程时都考虑了哪些点？单一因素还是多因素？&lt;a class=&quot;anchor&quot; href=&quot;#选进程时都考虑了哪些点单一因素还是多因素&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;选择下一个要运行的进程时，调度算法可能考虑单一因素或多个因素。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;单一因素:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FCFS: 只考虑进程到达就绪队列的时间。&lt;/li&gt;
&lt;li&gt;SJF (非抢占式): 只考虑预估的下一个CPU脉冲 (burst) 长度。&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;优先级调度 (带老化): 考虑静态优先级，但也考虑进程等待时间 (老化机制，aging) ，提高等待过久进程的优先级以防饿死。&lt;/li&gt;
&lt;li&gt;RR: 考虑到达时间和时间片轮转。&lt;/li&gt;
&lt;li&gt;MLFQ: 考虑优先级、进程行为 (CPU密集型 vs I/O密集型) 、等待时间等，动态调整进程在不同队列间的移动。&lt;/li&gt;
&lt;li&gt;HRRN: 考虑等待时间 (W) 和服务时间/脉冲长度 (S)，计算响应比 &lt;code&gt;(W+S)/S&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;Linux CFS: 考虑进程的虚拟运行时间 (virtual runtime)，旨在给每个进程公平的CPU时间比例。它间接考虑了进程的等待时间和已运行时间。&lt;/li&gt;
&lt;li&gt;Windows调度: 考虑基础优先级、动态优先级提升 (如完成I/O、处于前台窗口) 、时间片消耗情况等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结论: 简单算法可能只关注单一因素，但为了在复杂环境中平衡多个目标 (如响应时间、公平性、吞吐量) ，现代通用操作系统广泛使用多因素调度算法。&lt;/p&gt;
&lt;h3 id=&quot;12-how-调度算法详解&quot;&gt;1.2 How (调度算法详解)&lt;a class=&quot;anchor&quot; href=&quot;#12-how-调度算法详解&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;适用批处理系统的调度算法有哪些&quot;&gt;适用批处理系统的调度算法有哪些？&lt;a class=&quot;anchor&quot; href=&quot;#适用批处理系统的调度算法有哪些&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;主要目标是效率 (吞吐量、CPU利用率) 和整体完成时间 (周转时间) 。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先来先服务 (First-Come, First-Served, FCFS):
&lt;ul&gt;
&lt;li&gt;实现：按进程到达就绪队列的顺序进行调度。使用FIFO队列。&lt;/li&gt;
&lt;li&gt;优点：简单，易于实现，公平 (按到达顺序) 。&lt;/li&gt;
&lt;li&gt;缺点：平均等待时间可能很长，尤其当短进程排在长进程之后时 (护航效应 Convoy Effect) 。不适合交互式系统。是非抢占式的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;最短作业优先 (Shortest Job First, SJF):
&lt;ul&gt;
&lt;li&gt;实现：选择预计CPU执行时间 (下一个CPU burst) 最短的进程。可以是抢占式 (SRTF - Shortest Remaining Time First) 或非抢占式。&lt;/li&gt;
&lt;li&gt;优点：理论上具有最优的平均等待时间和平均周转时间。&lt;/li&gt;
&lt;li&gt;缺点：
&lt;ul&gt;
&lt;li&gt;需要预测下一个CPU burst长度，这很难精确做到 (通常基于历史数据估计) 。&lt;/li&gt;
&lt;li&gt;可能导致长作业饿死 (Starvation)，即长时间得不到CPU。&lt;/li&gt;
&lt;li&gt;非抢占式SJF不适合交互系统。抢占式SRTF开销较大。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;最高响应比优先 (Highest Response Ratio Next, HRRN):
&lt;ul&gt;
&lt;li&gt;实现：非抢占式。计算每个进程的响应比 &lt;code&gt;R = (等待时间 W + 服务时间 S) / 服务时间 S&lt;/code&gt;，选择R最高的进程。&lt;/li&gt;
&lt;li&gt;优点：结合了FCFS和SJF的优点。短作业容易被选中 (S小，R大) 。同时，等待时间长的进程其响应比也会增加，避免了饿死。&lt;/li&gt;
&lt;li&gt;缺点：仍需要预测服务时间S。计算响应比有额外开销。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;适用交互式系统的调度算法有哪些&quot;&gt;适用交互式系统的调度算法有哪些？&lt;a class=&quot;anchor&quot; href=&quot;#适用交互式系统的调度算法有哪些&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;主要目标是提供快速响应和用户满意度。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;轮转法 (Round Robin, RR):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实现：类似于FCFS，但增加了时间片和抢占。每个进程被分配一个固定的时间片 (Quantum) ，运行时间超出时间片后会被强制切换 (抢占) ，放回就绪队列尾部。&lt;/li&gt;
&lt;li&gt;优点：公平，响应时间相对较短 (特别是对于短请求) ，简单。&lt;/li&gt;
&lt;li&gt;缺点：
&lt;ul&gt;
&lt;li&gt;性能对时间片大小敏感。太小则上下文切换频繁，开销大；太大则退化为FCFS，响应时间变长。&lt;/li&gt;
&lt;li&gt;平均周转时间通常比SJF长。&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;p&gt;优先级调度 (Priority Scheduling):&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;优先级的确定可能是个问题 (静态 vs 动态) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;改进:
&lt;ul&gt;
&lt;li&gt;老化 (Aging): 随时间增加等待进程的优先级。&lt;/li&gt;
&lt;li&gt;动态优先级: 根据进程行为 (如I/O等待) 调整优先级。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;多级队列调度 (Multi-level Queue Scheduling):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实现：将就绪队列划分为多个独立的队列，每个队列有自己的优先级和调度算法 (如：前台交互队列用RR，后台批处理队列用FCFS) 。进程被永久分配到一个队列。&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;多级反馈队列调度 (Multi-level Feedback Queue, MLFQ):&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;I/O密集型进程 (经常阻塞放弃CPU) 通常会停留在较高优先级队列，保证响应性。CPU密集型进程会逐渐下降到较低优先级队列。&lt;/li&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;/ol&gt;
&lt;h4 id=&quot;适用实时系统的调度算法有哪些&quot;&gt;适用实时系统的调度算法有哪些？&lt;a class=&quot;anchor&quot; href=&quot;#适用实时系统的调度算法有哪些&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;主要目标是满足时间约束 (截止时间) 。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;速率单调调度 (Rate Monotonic Scheduling, RMS):
&lt;ul&gt;
&lt;li&gt;类型：静态优先级，抢占式。&lt;/li&gt;
&lt;li&gt;原理：周期性任务的优先级与其执行频率 (速率) 成正比。周期越短 (频率越高) ，优先级越高。&lt;/li&gt;
&lt;li&gt;优点：简单，易于实现，是最佳的静态优先级调度算法 (如果任务集可调度，RMS就能找到调度方案) 。可进行理论上的可调度性分析 (例如，利用率测试) 。&lt;/li&gt;
&lt;li&gt;缺点：只适用于周期性任务；对任务特性有较强假设；CPU利用率上限不如动态优先级算法高。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;最早截止时间优先 (Earliest Deadline First, EDF):
&lt;ul&gt;
&lt;li&gt;类型：动态优先级，抢占式。&lt;/li&gt;
&lt;li&gt;原理：当前就绪任务中，绝对截止时间最早的任务拥有最高优先级。&lt;/li&gt;
&lt;li&gt;优点：理论上是最优的动态优先级调度算法。只要任务集的总CPU利用率不超过100%，EDF就能找到调度方案 (对于可抢占、独立任务等理想情况) 。可以处理周期性和非周期性任务。&lt;/li&gt;
&lt;li&gt;缺点：实现比RMS复杂；可能出现多米诺骨牌效应 (一个任务错过截止时间可能导致后续任务也错过) ；动态优先级变化导致上下文切换可能更频繁。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;基于优先级的抢占式调度:
&lt;ul&gt;
&lt;li&gt;通用方法：给实时任务分配高优先级，使用标准优先级调度器。可以通过精心设置优先级来模拟RMS或EDF的行为。常用于软实时系统或硬实时系统中与其他任务共存的情况。&lt;/li&gt;
&lt;li&gt;需要确保高优先级任务能抢占低优先级任务，并且优先级反转 (Priority Inversion) 问题得到处理 (如使用优先级继承 Priority Inheritance 或优先级天花板 Priority Ceiling Protocol) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;怎样理解抢占式和非抢占式&quot;&gt;怎样理解抢占式和非抢占式？&lt;a class=&quot;anchor&quot; href=&quot;#怎样理解抢占式和非抢占式&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;这是调度器决定何时进行调度的两种基本模式。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;非抢占式调度 (Non-preemptive / Cooperative Scheduling):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义: 一旦CPU分配给某个进程，该进程将一直运行，直到它主动放弃CPU (完成任务、阻塞等待I/O、或显式调用yield) 。调度器不能在进程运行中途强制剥夺其CPU使用权。&lt;/li&gt;
&lt;li&gt;优点: 实现简单；上下文切换只在进程自愿放弃CPU时发生，开销相对较小；不会有并发访问内核数据结构的竞争问题 (在单处理器上) 。&lt;/li&gt;
&lt;li&gt;缺点: 一个长时间运行或行为不当的进程可以独占CPU，导致其他进程 (特别是需要快速响应的交互式进程) 长时间等待，响应性差；不适合分时和实时系统。&lt;/li&gt;
&lt;li&gt;例子: 早期的Windows (如Windows 3.1), 早期的Mac OS, 某些简单的嵌入式系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;抢占式调度 (Preemptive Scheduling):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义: 操作系统可以强制暂停当前正在运行的进程 (即使它并未主动放弃CPU) ，并将CPU分配给另一个进程。这种抢占通常发生在时钟中断 (时间片用完) 或更高优先级进程变为就绪时。&lt;/li&gt;
&lt;li&gt;优点: 能够保证CPU在进程间公平分配；提供更好的响应时间；可以有效处理优先级，防止低优先级任务阻塞高优先级任务 (前提是优先级设置合理) ；是现代多任务操作系统的标准做法。&lt;/li&gt;
&lt;li&gt;缺点: 实现更复杂；上下文切换更频繁，开销更大；需要处理内核数据结构的并发访问问题 (需要锁或其他同步机制) 。&lt;/li&gt;
&lt;li&gt;例子: Unix/Linux, Windows NT及后续版本, macOS, 大多数现代RTOS。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;从哪几方面对调度算法进行比较&quot;&gt;从哪几方面对调度算法进行比较？&lt;a class=&quot;anchor&quot; href=&quot;#从哪几方面对调度算法进行比较&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;主要从前面提到的性能指标/目标来进行比较和评估：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CPU利用率: 哪个算法更能让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;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;/ol&gt;
&lt;p&gt;比较方法:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确定性建模 (Deterministic Modeling): 给定一组特定的进程及其属性 (到达时间、CPU burst) ，模拟运行不同算法，计算性能指标。简单但只反映特定场景。&lt;/li&gt;
&lt;li&gt;排队论建模 (Queueing Models): 使用数学方法 (基于概率分布描述进程到达和CPU burst) 来分析平均性能。能提供理论洞察但模型可能简化现实。&lt;/li&gt;
&lt;li&gt;模拟 (Simulation): 编写程序模拟操作系统调度行为，使用随机生成的进程或真实系统负载的轨迹 (trace) 作为输入。灵活且能反映动态行为，是常用的评估方法。&lt;/li&gt;
&lt;li&gt;实际系统测量 (Implementation &amp;amp; Measurement): 在真实操作系统中实现算法，运行基准测试 (Benchmark) 或实际负载进行测量。最准确但成本最高。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;机制和策略分离的原则在调度算法中的应用&quot;&gt;机制和策略分离的原则在调度算法中的应用&lt;a class=&quot;anchor&quot; href=&quot;#机制和策略分离的原则在调度算法中的应用&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;机制与策略分离 (Separation of Mechanism and Policy) 是一个重要的操作系统设计原则，也适用于调度。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;机制 (Mechanism): 提供如何做 (How) 的基础能力或工具。在调度中，机制包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;上下文切换的代码 (保存/加载寄存器、切换页表) 。&lt;/li&gt;
&lt;li&gt;维护进程状态 (就绪、运行、阻塞) 和PCB的数据结构。&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;/li&gt;
&lt;li&gt;
&lt;p&gt;策略 (Policy): 决定做什么 (What) 或何时做 (When)。在调度中，策略是指具体的调度算法逻辑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如何选择下一个运行的进程 (FCFS规则？SJF规则？RR规则？优先级规则？) 。&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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;应用与好处:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;灵活性与可扩展性: 将调度算法 (策略) 与底层的上下文切换等 (机制) 分开，使得修改或更换调度策略更加容易，而无需改变底层的核心机制代码。例如，Linux内核允许通过&lt;code&gt;sched_setscheduler()&lt;/code&gt;系统调用为进程选择不同的调度策略 (如&lt;code&gt;SCHED_FIFO&lt;/code&gt;, &lt;code&gt;SCHED_RR&lt;/code&gt;, &lt;code&gt;SCHED_NORMAL/CFS&lt;/code&gt;) ，但它们都使用相同的底层上下文切换机制。&lt;/li&gt;
&lt;li&gt;模块化: 代码结构更清晰，职责分明。调度策略模块可以独立开发、测试和更新。&lt;/li&gt;
&lt;li&gt;可定制性: 用户或管理员可以更容易地根据特定需求调整调度策略参数，甚至在某些系统中插入自定义的调度模块。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例子:
调度器的主循环可能是一个通用的框架 (机制) ，它调用一个函数 (策略) 来选择下一个进程。不同的调度算法可以通过实现这个选择函数来插入。上下文切换函数是另一个独立的机制。优先级队列的实现 (如使用堆或链表) 是机制，而如何利用这个队列 (是按优先级取还是按FIFO取) 是策略。&lt;/p&gt;
&lt;h4 id=&quot;实例操作系统的调度算法都是什么&quot;&gt;实例操作系统的调度算法都是什么？&lt;a class=&quot;anchor&quot; href=&quot;#实例操作系统的调度算法都是什么&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;现代通用操作系统通常采用复杂且混合的调度策略，以平衡各种需求。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Linux:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通进程调度: Linux 2.6.23 起主要使用 CFS；Linux 6.6 以后公平调度路径开始转向 EEVDF。
&lt;ul&gt;
&lt;li&gt;目标：为所有运行中的任务提供尽可能公平的CPU时间份额。&lt;/li&gt;
&lt;li&gt;机制：不再基于固定时间片，而是维护每个任务的虚拟运行时间 (vruntime)。总是选择vruntime最小的任务来运行。任务运行会增加其vruntime。I/O等待的任务vruntime增长慢，因此返回时更容易被选中。&lt;/li&gt;
&lt;li&gt;实现：使用红黑树来高效地找到vruntime最小的任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;实时调度策略:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SCHED_FIFO&lt;/code&gt;: 静态优先级的先来先服务 (非时间片轮转) 。相同优先级的任务按到达顺序执行，直到阻塞、退出或被更高优先级抢占。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SCHED_RR&lt;/code&gt;: 静态优先级的轮转法。同&lt;code&gt;SCHED_FIFO&lt;/code&gt;，但增加了时间片，同一优先级任务轮流运行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优先级：实时任务优先级高于普通任务。普通任务也有优先级 (nice值) ，CFS/EEVDF 会用权重影响公平时间分配。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Windows (NT内核及以后):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;采用基于优先级的抢占式调度算法，结合了多级反馈队列的思想。&lt;/li&gt;
&lt;li&gt;优先级：分为32个优先级。0为系统空闲线程，1-15为可变优先级类 (Variable Priority Classes) ，16-31为实时优先级类 (Real-time Priority Classes) 。内核线程可能使用更高的内部优先级。&lt;/li&gt;
&lt;li&gt;动态调整：
&lt;ul&gt;
&lt;li&gt;对于可变优先级类，系统会动态提升线程的优先级 (Priority Boost) ，例如：
&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;/li&gt;
&lt;li&gt;长时间消耗CPU的线程其动态优先级会逐渐衰减回基础优先级。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;时间片：不同优先级的线程可能有不同的时间片长度。前台进程的时间片通常比后台进程长且可变。&lt;/li&gt;
&lt;li&gt;Quantum：Windows中称为Quantum，不是固定的，可以根据系统设置和前后台状态调整。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;macOS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基于XNU内核 (混合了Mach微内核和BSD Unix) 。&lt;/li&gt;
&lt;li&gt;调度也采用基于优先级的抢占式模型，具有多级反馈特性。&lt;/li&gt;
&lt;li&gt;线程优先级分为几个主要波段 (bands)：内核模式、系统高优先级、用户交互 (UI响应关键) 、用户启动、后台任务等。&lt;/li&gt;
&lt;li&gt;动态调整：系统会根据线程的行为 (如CPU使用情况、是否阻塞等待I/O、是否与用户界面交互) 动态调整其优先级，类似于Windows。&lt;/li&gt;
&lt;li&gt;也使用了类似Mach的线程调度原语和概念，例如时间片捐赠 (Time-sharing donation) 等机制来优化性能。&lt;/li&gt;
&lt;li&gt;近年来引入了服务质量 (Quality of Service, QoS) 类的概念，让开发者可以指定任务的意图 (如用户交互、后台数据处理、维护任务) ，系统据此进行更智能的资源 (包括CPU调度) 管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;总结: 现代主流操作系统都使用抢占式、基于优先级的调度框架，并结合动态优先级调整、多级队列/反馈机制，以及针对公平性 (如Linux CFS) 或服务质量 (如macOS QoS) 的特定优化，以适应通用计算环境中复杂多变的需求。&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;2-处理器调度的基本概念&quot;&gt;2. 处理器调度的基本概念&lt;a class=&quot;anchor&quot; href=&quot;#2-处理器调度的基本概念&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;21-调度的三个层次&quot;&gt;2.1 调度的三个层次&lt;a class=&quot;anchor&quot; href=&quot;#21-调度的三个层次&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;操作系统中的调度可以发生在不同层面，对应不同的资源管理和时间尺度：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;长程调度 (Long-term Scheduling / 作业调度):
&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;中程调度 (Medium-term Scheduling / 内存调度):
&lt;ul&gt;
&lt;li&gt;时机: 内存资源紧张或需要优化内存使用时。&lt;/li&gt;
&lt;li&gt;决策: 决定哪些进程的部分或全部从内存换出到外存 (挂起) ，以及何时将挂起的进程换回内存。&lt;/li&gt;
&lt;li&gt;目标: 提高内存利用率和系统吞吐量，通过交换 (Swapping) 技术实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;短程调度 (Short-term Scheduling / CPU调度 / 微观调度):
&lt;ul&gt;
&lt;li&gt;时机: 发生特定事件 (如中断、系统调用、进程阻塞/唤醒、时间片用完等) 后，需要选择下一个占用CPU的进程时。&lt;/li&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;/ol&gt;
&lt;p&gt;联系: 这三个层次的调度相互关联，共同管理进程从创建到完成的整个生命周期及其资源使用。&lt;/p&gt;
&lt;h3 id=&quot;22-处理器调度的定义与场景&quot;&gt;2.2 处理器调度的定义与场景&lt;a class=&quot;anchor&quot; href=&quot;#22-处理器调度的定义与场景&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定义: 控制和协调多个进程对CPU资源的竞争。&lt;/li&gt;
&lt;li&gt;场景: 系统中有 N 个进程处于就绪状态，等待在 M 个CPU (M ≥ 1) 上运行。&lt;/li&gt;
&lt;li&gt;任务: 调度程序 (内核函数) 根据特定的调度算法，从就绪队列中选择一个进程，并将CPU使用权交给它。&lt;/li&gt;
&lt;li&gt;Idle进程: 如果就绪队列为空 (没有可运行的用户或系统进程) ，系统会调度一个特殊的空闲进程 (idle process) 来运行，它通常执行一些低优先级任务 (如系统监控、节能) 或简单地循环等待中断。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;23-调度需要解决的核心问题&quot;&gt;2.3 调度需要解决的核心问题&lt;a class=&quot;anchor&quot; href=&quot;#23-调度需要解决的核心问题&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;调度时机 (When): 何时进行处理器分配决策。&lt;/li&gt;
&lt;li&gt;调度算法 (What): 依据何种原则挑选进程/线程。&lt;/li&gt;
&lt;li&gt;调度过程 (How): 如何完成CPU的分配，即上下文切换。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;24-cpu调度的时机&quot;&gt;2.4 CPU调度的时机&lt;a class=&quot;anchor&quot; href=&quot;#24-cpu调度的时机&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;调度通常在以下事件发生后，内核处理完相应事件并准备返回用户态之前的最后时刻进行：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程生命周期变化:
&lt;ul&gt;
&lt;li&gt;进程执行完毕并退出 (&lt;code&gt;exit()&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;进程因错误或异常而终止 (&lt;code&gt;abort&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;创建新进程 (&lt;code&gt;fork()&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程状态转换:
&lt;ul&gt;
&lt;li&gt;运行进程因等待I/O或资源而进入阻塞态 (&lt;code&gt;wait()&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;阻塞进程被唤醒，回到就绪态 (I/O完成中断)。&lt;/li&gt;
&lt;li&gt;运行进程用完分配的时间片，回到就绪态 (时钟中断)。&lt;/li&gt;
&lt;li&gt;运行进程主动放弃 CPU (&lt;code&gt;yield()&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;流程: 事件发生 → 暂停当前进程 → 硬件响应 → 进入内核处理事件 → 事件处理结束 (可能导致进程状态变化、就绪队列调整) → 执行进程调度 → 选择新进程运行。&lt;/p&gt;
&lt;h3 id=&quot;25-调度过程上下文切换-context-switching&quot;&gt;2.5 调度过程：上下文切换 (Context Switching)&lt;a class=&quot;anchor&quot; href=&quot;#25-调度过程上下文切换-context-switching&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;定义: 将CPU的控制权从一个进程 (或线程) 转移给另一个进程 (或线程) 的过程。这涉及到保存当前进程的状态并加载新进程的状态。&lt;/li&gt;
&lt;li&gt;上下文 (Context): 进程运行时，其执行状态 (硬件上下文) 保存在CPU的寄存器中 (如程序计数器PC, 程序状态字PSW, 栈指针SP, 通用寄存器等) 。进程不运行时，这些信息保存在其进程控制块 (PCB) 中。&lt;/li&gt;
&lt;li&gt;主要工作:
&lt;ol&gt;
&lt;li&gt;切换地址空间: 修改页目录寄存器 (如CR3 on x86) 以指向新进程的页表，加载新的虚拟地址空间。&lt;/li&gt;
&lt;li&gt;切换内核栈和硬件上下文:
&lt;ul&gt;
&lt;li&gt;保存当前进程的寄存器值到其PCB或内核栈。&lt;/li&gt;
&lt;li&gt;从新进程的PCB或内核栈中恢复其寄存器值到CPU。&lt;/li&gt;
&lt;li&gt;内核栈 (Kernel Stack):
&lt;ul&gt;
&lt;li&gt;每个进程都有自己的内核栈，用于在进程执行内核代码时存储函数调用、局部变量和上下文信息。&lt;/li&gt;
&lt;li&gt;当进程从用户态切换到内核态 (如系统调用、中断) 时，CPU会自动切换到该进程的内核栈。&lt;/li&gt;
&lt;li&gt;内核栈位于内核地址空间，对用户程序不可见，大小通常是固定的 (如 Linux 中为 8KB 或 16KB) 。&lt;/li&gt;
&lt;li&gt;内核栈的地址通常保存在进程的 PCB 中，在上下文切换时需要更新相关寄存器 (如栈指针) 指向新进程的内核栈。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;具体步骤 (进程A切换到进程B):
&lt;ol&gt;
&lt;li&gt;保存进程A的硬件上下文 (寄存器值) 。&lt;/li&gt;
&lt;li&gt;更新进程A的PCB (如状态改为就绪或阻塞，记录PC等) 。&lt;/li&gt;
&lt;li&gt;将进程A移入相应的队列 (就绪队列、等待队列) 。&lt;/li&gt;
&lt;li&gt;选择进程B作为下一个运行进程。&lt;/li&gt;
&lt;li&gt;更新进程B的PCB (状态改为运行) 。&lt;/li&gt;
&lt;li&gt;加载进程B的上下文 (恢复寄存器值，切换地址空间) 。&lt;/li&gt;
&lt;li&gt;开始执行进程B。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;XV6 Context Switch Example (&lt;code&gt;swtch.S&lt;/code&gt;): (此处展示汇编代码逻辑，具体代码略)
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;swtch&lt;/code&gt; 函数接受两个参数：旧进程上下文指针 (&lt;code&gt;old&lt;/code&gt;) 和新进程上下文指针 (&lt;code&gt;new&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;它负责保存 &lt;code&gt;old&lt;/code&gt; 进程的callee-saved寄存器到其上下文结构中。&lt;/li&gt;
&lt;li&gt;然后，从 &lt;code&gt;new&lt;/code&gt; 进程的上下文结构中恢复callee-saved寄存器。&lt;/li&gt;
&lt;li&gt;最后，通过 &lt;code&gt;ret&lt;/code&gt; 指令返回，此时CPU将跳转到 &lt;code&gt;new&lt;/code&gt; 进程之前保存的PC地址， effectively switching execution flow. The key is switching the stack pointer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;上下文切换开销 (Cost):
&lt;ul&gt;
&lt;li&gt;直接开销: 内核执行切换操作所花费的CPU时间。
&lt;ul&gt;
&lt;li&gt;保存和恢复寄存器。&lt;/li&gt;
&lt;li&gt;切换地址空间 (TLB Flush相关指令通常较昂贵) 。&lt;/li&gt;
&lt;li&gt;执行调度算法本身的代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;间接开销: 切换导致缓存性能下降。
&lt;ul&gt;
&lt;li&gt;CPU Cache 失效: 新进程的代码和数据不在缓存中，需要从内存加载。&lt;/li&gt;
&lt;li&gt;TLB (Translation Lookaside Buffer) 失效: 地址翻译缓存失效，需要重新查询页表。&lt;/li&gt;
&lt;li&gt;缓冲区缓存 (Buffer Cache) 可能失效: 文件系统相关的缓存可能对新进程无效。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-处理器调度算法的设计&quot;&gt;3. 处理器调度算法的设计&lt;a class=&quot;anchor&quot; href=&quot;#3-处理器调度算法的设计&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-不同操作系统类型的调度目标&quot;&gt;3.1 不同操作系统类型的调度目标&lt;a class=&quot;anchor&quot; href=&quot;#31-不同操作系统类型的调度目标&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;调度算法的选择与操作系统的主要应用场景和目标密切相关：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;批处理系统 (Batch Systems):
&lt;ul&gt;
&lt;li&gt;特点: 通常运行长任务，无需用户交互。&lt;/li&gt;
&lt;li&gt;目标:
&lt;ul&gt;
&lt;li&gt;高吞吐量 (Throughput): 单位时间内完成的作业数量最大化。&lt;/li&gt;
&lt;li&gt;短周转时间 (Turnaround Time): 作业从提交到完成的总时间最小化。&lt;/li&gt;
&lt;li&gt;高CPU利用率 (CPU Utilization): 让CPU尽可能处于忙碌状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;交互式系统 (Interactive Systems):
&lt;ul&gt;
&lt;li&gt;特点: 需要频繁与用户交互，用户等待输入。&lt;/li&gt;
&lt;li&gt;目标:
&lt;ul&gt;
&lt;li&gt;快速响应时间 (Response Time): 从用户输入命令到系统首次给出反馈的时间要短 (通常要求低于50-150ms) 。&lt;/li&gt;
&lt;li&gt;均衡性 (Proportionality): 用户感觉系统性能稳定，符合预期。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;实时系统 (Real-time Systems):
&lt;ul&gt;
&lt;li&gt;特点: 任务有严格的时间限制 (截止时间, Deadline) 。&lt;/li&gt;
&lt;li&gt;目标:
&lt;ul&gt;
&lt;li&gt;满足最后期限 (Meeting Deadlines): 关键任务必须在规定时间内完成 (硬实时) 或尽可能满足 (软实时) 。&lt;/li&gt;
&lt;li&gt;可预测性 (Predictability): 系统行为在时间上是确定的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-调度算法的设计考量&quot;&gt;3.2 调度算法的设计考量&lt;a class=&quot;anchor&quot; href=&quot;#32-调度算法的设计考量&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;设计调度算法时，需在多个目标之间进行权衡 (Trade-off) ：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户角度 (User-oriented):
&lt;ul&gt;
&lt;li&gt;周转时间 (Turnaround Time): T(completion) - T(arrival)。进程从进入系统到完成的总时间。目标：最小化平均周转时间。&lt;/li&gt;
&lt;li&gt;响应时间 (Response Time): 从请求发出到第一次产生响应的时间。目标：最小化响应时间 (对交互式系统尤为重要) 。&lt;/li&gt;
&lt;li&gt;最后期限 (Deadline): 实时任务必须在规定时间前完成。目标：确保满足所有 (硬实时) 或重要 (软实时) 的截止时间。&lt;/li&gt;
&lt;li&gt;可预测性 (Predictability): 任务运行时间稳定，尤其对实时系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;系统角度 (System-oriented):
&lt;ul&gt;
&lt;li&gt;吞吐量 (Throughput): 单位时间内完成的进程数量。目标：最大化吞吐量。&lt;/li&gt;
&lt;li&gt;CPU 利用率 (CPU Utilization): CPU忙于执行有效工作的时间百分比。目标：最大化CPU利用率。&lt;/li&gt;
&lt;li&gt;公平性 (Fairness): 各进程获得合理的CPU时间份额，防止饥饿。&lt;/li&gt;
&lt;li&gt;均衡性 (Balance): 系统资源 (CPU, I/O设备等) 应保持忙碌，充分利用。&lt;/li&gt;
&lt;li&gt;强制优先级 (Enforcing Priorities): 确保高优先级进程优先获得服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;33-调度算法的关键决策点&quot;&gt;3.3 调度算法的关键决策点&lt;a class=&quot;anchor&quot; href=&quot;#33-调度算法的关键决策点&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;设计或选择调度算法时，需要考虑以下几个方面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;进程优先级 (Priority):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优先数: 用于表示优先级的数值 (数值越大优先级越高或越低，取决于系统定义) 。&lt;/li&gt;
&lt;li&gt;静态优先级 (Static Priority): 进程创建时指定，运行期间不变。简单，但可能不适应进程行为变化。&lt;/li&gt;
&lt;li&gt;动态优先级 (Dynamic Priority): 进程优先级在运行过程中可以调整。例如，可以提升长时间等待的进程的优先级 (老化, Aging) ，或降低长时间占用CPU进程的优先级。更能适应系统变化。&lt;/li&gt;
&lt;li&gt;PCB记录: PCB中需要包含优先级信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;就绪队列组织 (Ready Queue Organization):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;单一队列: 所有就绪进程放在一个队列中，按某种顺序 (如FCFS、优先级) 排列。&lt;/li&gt;
&lt;li&gt;多级队列 (Multiple Queues): 按进程属性 (如优先级、类型) 划分多个队列。不同队列可采用不同调度策略。
&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;抢占 vs. 非抢占 (Preemptive vs. Non-preemptive):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非抢占式 (Non-preemptive / 不可剥夺): 一旦进程获得CPU，它将一直运行，直到它自愿放弃 (完成、阻塞、&lt;code&gt;yield&lt;/code&gt;) 。适用于批处理，简单，但响应性差。&lt;/li&gt;
&lt;li&gt;抢占式 (Preemptive / 可剥夺): 当前运行的进程可以被更高优先级的就绪进程或时钟中断强制中断，CPU被分配给新进程。适用于交互式和实时系统，响应性好，但有上下文切换开销。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I/O密集型 vs. CPU密集型进程 (I/O-bound vs. CPU-bound):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I/O密集型: 进程大部分时间在等待I/O操作完成，CPU计算时间短。&lt;/li&gt;
&lt;li&gt;CPU密集型 (计算密集型): 进程大部分时间在进行CPU计算，很少I/O操作。&lt;/li&gt;
&lt;li&gt;调度倾向: 现代系统通常倾向于优先调度I/O密集型进程，以保持I/O设备忙碌，提高系统整体吞吐量和响应性。让I/O进程尽快发出下一个I/O请求，然后在其等待时运行CPU密集型进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;时间片 (Time Slice / Quantum):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义: 在抢占式调度 (特别是轮转RR) 中，分配给进程一次连续运行的最大CPU时间。&lt;/li&gt;
&lt;li&gt;选择: 时间片大小的选择是一个重要的权衡：
&lt;ul&gt;
&lt;li&gt;太长: 接近非抢占，长任务会阻塞短任务，交互式响应变慢。退化为FCFS。&lt;/li&gt;
&lt;li&gt;太短: 频繁发生上下文切换，系统开销增大，有效工作比例下降。&lt;/li&gt;
&lt;li&gt;合适的大小: 通常需要在几十到几百毫秒之间，取决于系统负载、CPU速度、上下文切换开销和对响应时间的要求。应略大于典型的一次交互所需CPU时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;固定 vs. 可变: 时间片可以是固定的，也可以根据进程优先级或行为动态调整。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;4-典型的处理器调度算法&quot;&gt;4. 典型的处理器调度算法&lt;a class=&quot;anchor&quot; href=&quot;#4-典型的处理器调度算法&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;41-批处理系统调度算法&quot;&gt;4.1 批处理系统调度算法&lt;a class=&quot;anchor&quot; href=&quot;#41-批处理系统调度算法&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;主要目标：高吞吐量、低周转时间、高CPU利用率。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;先来先服务 (FCFS - First Come First Serve):&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;平均周转时间和平均等待时间可能很长，特别是当短进程排在长进程之后时 (护航效应, Convoy Effect) 。&lt;/li&gt;
&lt;li&gt;不利于I/O密集型进程 (长CPU进程运行时，I/O进程等待；I/O进程运行时，CPU空闲) 。&lt;/li&gt;
&lt;li&gt;对交互式用户不友好。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;例子: 进程P1(24s), P2(3s), P3(3s) 按 P1, P2, P3 顺序到达。
&lt;ul&gt;
&lt;li&gt;执行顺序: P1 -&amp;gt; P2 -&amp;gt; P3&lt;/li&gt;
&lt;li&gt;完成时间: P1(24), P2(27), P3(30)&lt;/li&gt;
&lt;li&gt;周转时间: P1(24), P2(27), P3(30) -&amp;gt; 平均 27s&lt;/li&gt;
&lt;li&gt;若按 P2, P3, P1 顺序调度:
&lt;ul&gt;
&lt;li&gt;执行顺序: P2 -&amp;gt; P3 -&amp;gt; P1&lt;/li&gt;
&lt;li&gt;完成时间: P2(3), P3(6), P1(30)&lt;/li&gt;
&lt;li&gt;周转时间: P2(3), P3(6), P1(30) -&amp;gt; 平均 13s (显著改善)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最短作业优先 (SJF - Shortest Job First):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;策略: 选择预计运行时间最短的进程投入运行。&lt;/li&gt;
&lt;li&gt;版本:
&lt;ul&gt;
&lt;li&gt;非抢占式 SJF: 当前进程一直运行直到结束或阻塞。&lt;/li&gt;
&lt;li&gt;抢占式 SJF (最短剩余时间优先, SRTN - Shortest Remaining Time Next): 当一个新进程到达，其预计总运行时间比当前进程的 &lt;em&gt;剩余&lt;/em&gt; 运行时间还短时，抢占当前进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优点: 理论上可证明，在所有进程同时到达时，SJF(非抢占)具有最低的平均周转时间。SRTN通常比非抢占SJF的平均周转时间更短。&lt;/li&gt;
&lt;li&gt;缺点:
&lt;ul&gt;
&lt;li&gt;需要预测未来: 如何准确知道进程的运行时间？通常基于历史数据进行估计，可能不准。&lt;/li&gt;
&lt;li&gt;饥饿 (Starvation): 长进程可能永远得不到CPU，如果总有短进程到来。&lt;/li&gt;
&lt;li&gt;不公平: 明显偏袒短进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;例子 (SRTN):






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;进程&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;到达时刻&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;运行时间&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P1&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;0&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;7&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P2&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;2&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P3&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;4&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;P4&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;5&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;0: P1 运行 (剩余 7)&lt;/li&gt;
&lt;li&gt;2: P2 到达 (剩余 4) &amp;lt; P1 (剩余 5)，P2 抢占 P1。P2 运行 (剩余 4)&lt;/li&gt;
&lt;li&gt;4: P3 到达 (剩余 1) &amp;lt; P2 (剩余 2)，P3 抢占 P2。P3 运行 (剩余 1)&lt;/li&gt;
&lt;li&gt;5: P3 完成。P4 到达 (剩余 4)。比较 P1(剩余 5), P2(剩余 2), P4(剩余 4)。P2 剩余时间最短，P2 运行 (剩余 2)&lt;/li&gt;
&lt;li&gt;7: P2 完成。比较 P1(剩余 5), P4(剩余 4)。P4 剩余时间最短，P4 运行 (剩余 4)&lt;/li&gt;
&lt;li&gt;11: P4 完成。只剩 P1，P1 运行 (剩余 5)&lt;/li&gt;
&lt;li&gt;16: P1 完成。&lt;/li&gt;
&lt;li&gt;执行序列: P1(0-2) -&amp;gt; P2(2-4) -&amp;gt; P3(4-5) -&amp;gt; P2(5-7) -&amp;gt; P4(7-11) -&amp;gt; P1(11-16)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最高响应比优先 (HRRN - Highest Response Ratio Next):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;策略: 综合考虑等待时间和运行时间，选择响应比最高的进程。非抢占式。&lt;/li&gt;
&lt;li&gt;响应比 R = (等待时间 + 预计运行时间) / 预计运行时间 = 1 + (等待时间 / 预计运行时间)&lt;/li&gt;
&lt;li&gt;优点:
&lt;ul&gt;
&lt;li&gt;试图在SJF和FCFS之间取得平衡。&lt;/li&gt;
&lt;li&gt;短进程：预计运行时间小，响应比增长快，容易被选中 (类似SJF) 。&lt;/li&gt;
&lt;li&gt;长进程：等待时间足够长后，响应比会提高，最终能获得CPU，避免了饥饿。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;缺点: 仍需预测运行时间。计算响应比有额外开销。&lt;/li&gt;
&lt;li&gt;抢占式HRRN? 理论上可以，但每次事件 (如新进程到达) 都需要重新计算所有就绪进程的响应比并排序，开销较大。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;42-交互式系统调度算法&quot;&gt;4.2 交互式系统调度算法&lt;a class=&quot;anchor&quot; href=&quot;#42-交互式系统调度算法&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;主要目标：快速响应时间、均衡性、公平性。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;时间片轮转 (RR - Round Robin):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;策略: 将所有就绪进程按FCFS排成队列。调度器选择队首进程，分配一个时间片 (quantum) 。进程用完时间片后，若未完成或阻塞，则移到队尾。抢占式。&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;对周转时间不一定最优。对于运行时间相近的进程，RR的平均周转时间可能比FCFS差。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;例子 (时间片 q=20): P1(53), P2(8), P3(68), P4(24)
&lt;ul&gt;
&lt;li&gt;执行序列: P1(0-20) -&amp;gt; P2(20-28) -&amp;gt; P3(28-48) -&amp;gt; P4(48-68) -&amp;gt; P1(68-88) -&amp;gt; P3(88-108) -&amp;gt; P4(108-112) -&amp;gt; P1(112-125) -&amp;gt; P3(125-145) -&amp;gt; P3(145-153)&lt;/li&gt;
&lt;li&gt;平均等待时间 = ( (68-20)+(112-88) + (20-0) + (28-0)+(88-48)+(125-108) + (48-0)+(108-68) ) / 4 = (72 + 20 + 85 + 88) / 4 = 66.25 ms (假设到达时间为0)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟轮转 (Virtual RR - VRR):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;动机: RR对I/O密集型进程可能不公平。I/O进程经常在时间片未用完时就阻塞，返回就绪队列时排在队尾，下次获得CPU可能要等很久。&lt;/li&gt;
&lt;li&gt;策略: 维护一个辅助就绪队列 (如AUX队列) 。当一个进程因I/O阻塞完成而返回时，不放入主RR队列尾部，而是放入AUX队列头部。调度器优先检查AUX队列，若非空则调度AUX队首进程，给其一个较短的时间片 (通常是其上次阻塞时剩余的时间片) ；若AUX队列为空，则按标准RR调度主队列。从AUX队列运行完时间片的进程回到主RR队列尾部。&lt;/li&gt;
&lt;li&gt;目标: 给I/O密集型进程更多机会运行，提高I/O设备利用率。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优先级调度 (Priority Scheduling):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;策略: 选择就绪队列中优先级最高的进程运行。可以是抢占式或非抢占式。&lt;/li&gt;
&lt;li&gt;优点: 实现简单，能满足不同进程的紧急程度需求 (如系统进程&amp;gt;用户进程，前台&amp;gt;后台，I/O型&amp;gt;CPU型) 。&lt;/li&gt;
&lt;li&gt;缺点:
&lt;ul&gt;
&lt;li&gt;饥饿: 低优先级进程可能永远无法运行。&lt;/li&gt;
&lt;li&gt;优先级反转 (Priority Inversion): 一个低优先级进程持有高优先级进程所需的资源 (如锁) ，导致高优先级进程被迫等待低优先级进程。更糟的是，如果此时有一个中等优先级的CPU密集型进程就绪，它会抢占低优先级进程，使得高优先级进程的等待时间变得更长甚至不可预测。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;优先级反转解决方案:
&lt;ul&gt;
&lt;li&gt;优先级继承 (Priority Inheritance): 当高优先级进程等待低优先级进程持有的资源时，暂时将低优先级进程的优先级提升到与高优先级进程相同，使其能尽快运行并释放资源。&lt;/li&gt;
&lt;li&gt;优先级天花板协议 (Priority Ceiling Protocol): 给每个资源预设一个优先级上限 (等于可能使用该资源的所有进程中的最高优先级) 。当一个进程获得资源时，将其优先级提升到该资源的优先级上限。这能预防死锁并限制阻塞时间。&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;多级队列调度 (Multilevel Queue Scheduling):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;策略: 将就绪队列划分为多个独立的队列，每个队列有自己的调度算法和优先级。例如：
&lt;ul&gt;
&lt;li&gt;系统进程队列 (最高优先级, RR 或 FCFS)&lt;/li&gt;
&lt;li&gt;交互式进程队列 (中优先级, RR)&lt;/li&gt;
&lt;li&gt;批处理进程队列 (最低优先级, FCFS)&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;/li&gt;
&lt;li&gt;
&lt;p&gt;多级反馈队列调度 (Multilevel Feedback Queue Scheduling - MFQ):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;策略: 结合了多级队列和动态优先级调整。进程可以在不同队列之间移动。&lt;/li&gt;
&lt;li&gt;典型实现:
&lt;ol&gt;
&lt;li&gt;设置多个优先级队列 (Q0, Q1, …, Qn)，优先级 Q0 &amp;gt; Q1 &amp;gt; … &amp;gt; Qn。&lt;/li&gt;
&lt;li&gt;不同队列分配不同的时间片长度，优先级越高的队列时间片越短 (如 Q0=q, Q1=2q, Q2=4q…) 。&lt;/li&gt;
&lt;li&gt;新进程进入最高优先级队列 Q0。&lt;/li&gt;
&lt;li&gt;调度器总是先运行最高非空队列中的进程，同队列内通常用RR。&lt;/li&gt;
&lt;li&gt;如果进程在一个队列中用完了其时间片但未完成，它会被 降级 到下一个较低优先级队列。&lt;/li&gt;
&lt;li&gt;如果进程在时间片未用完前因 阻塞 (如等待I/O) 而放弃CPU，当它再次就绪时，通常会回到 原来的 队列 (或有时提升一级) ，以优待I/O密集型进程。(讨论点： 回到原队列还是队首/队尾？提升吗？具体策略不同系统可能不同。)&lt;/li&gt;
&lt;li&gt;最低优先级队列通常采用FCFS或很长的时间片RR。&lt;/li&gt;
&lt;li&gt;(可选) 可以加入 老化 (Aging) 机制：在低优先级队列等待过久的进程可以被提升到较高优先级队列，防止饥饿。&lt;/li&gt;
&lt;/ol&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;公平共享调度 (Fair-share Scheduling): 不仅考虑单个进程，还考虑进程所属的用户或用户组，确保CPU时间在用户/组之间公平分配。&lt;/li&gt;
&lt;li&gt;保证调度 (Guaranteed Scheduling): 向用户承诺每个进程将获得 CPU 时间的 1/n (如果有n个进程) ，并跟踪进程实际获得的CPU时间，优先运行获得时间最少的进程。&lt;/li&gt;
&lt;li&gt;彩票调度 (Lottery Scheduling): 给每个进程分配一定数量的“彩票”，调度器随机抽取一张彩票，持有该彩票的进程获得CPU。进程持有的彩票越多，获得CPU的机会越大。优先级可以通过分配不同数量的彩票来体现。简单，易实现概率公平。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;43-实时系统调度算法&quot;&gt;4.3 实时系统调度算法&lt;a class=&quot;anchor&quot; href=&quot;#43-实时系统调度算法&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;主要目标：满足任务截止时间、可预测性。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;可调度性分析: 对于周期性实时任务，需要判断系统是否能在所有任务的截止时间内完成它们。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若有 m 个周期任务，任务 i 的周期为 Pi，每次执行需 Ci 的CPU时间，则一个简单的 (充分非必要) 可调度条件是：
Σ (Ci / Pi) ≤ 1 (CPU利用率不超过100%)&lt;/li&gt;
&lt;li&gt;更精确的条件取决于具体算法 (如RM, EDF) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;速率单调调度 (Rate-Monotonic Scheduling - RM):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;类型: 静态优先级，抢占式。&lt;/li&gt;
&lt;li&gt;策略: 任务的优先级根据其 周期 (Rate) 设定：周期越短 (频率越高) ，优先级越高。&lt;/li&gt;
&lt;li&gt;适用: 周期性实时任务。&lt;/li&gt;
&lt;li&gt;优点: 简单，理论成熟，可进行精确的可调度性分析 (Liu &amp;amp; Layland 条件：Σ(Ci/Pi) ≤ n(2^(1/n)-1))。&lt;/li&gt;
&lt;li&gt;缺点: 仅适用于周期任务，对任务集利用率上限有要求 (不是100%)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最早截止时间优先 (Earliest Deadline First - EDF):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;类型: 动态优先级，抢占式。&lt;/li&gt;
&lt;li&gt;策略: 调度器在每次调度时，选择就绪队列中 绝对截止时间 (Deadline) 最早的任务运行。&lt;/li&gt;
&lt;li&gt;适用: 周期性和非周期性实时任务。&lt;/li&gt;
&lt;li&gt;优点: 理论上是最优的动态优先级算法，只要系统总利用率 ≤ 1，EDF就能找到一个可行的调度 (如果存在的话) 。CPU利用率上限可达100%。&lt;/li&gt;
&lt;li&gt;缺点: 实现比RM复杂 (需要跟踪每个任务的截止时间) ，可能出现瞬时过载导致多米诺骨牌效应 (一个任务错过deadline可能导致后续任务都错过) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;44-各种调度算法比较总结&quot;&gt;4.4 各种调度算法比较总结&lt;a class=&quot;anchor&quot; href=&quot;#44-各种调度算法比较总结&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;











































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;调度算法&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;选择依据&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;决策模式&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;吞吐量&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;响应时间&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;开销&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;对进程影响&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;饥饿问题&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;FCFS&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;max[w]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;非抢占&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;不强调&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能很差 (长作业阻塞短作业)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;最小&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;对短进程/IO密集型不利&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;RR&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;固定时间片&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;抢占(时间片)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;q过小则低&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;短进程好&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;较小&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;公平&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;SJF&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;min[s]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;非抢占&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;高&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;短进程好&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能较高&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;对长进程不利&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;SRTN&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;min[s-e]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;抢占(到达时)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;高&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;好&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能较高&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;对长进程不利&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;HRRN&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;code&gt;max[(w+s)/s]&lt;/code&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;非抢占&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;高&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;较好&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能较高&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;平衡&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;Feedback&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;见算法思想&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;抢占(时间片)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;不强调&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;较好 (可调优)&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能较高&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可优待IO密集型，可能对某些进程不利&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;可能 (需老化)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;(表中 &lt;code&gt;w&lt;/code&gt;: 等待时间, &lt;code&gt;s&lt;/code&gt;: 总服务时间, &lt;code&gt;e&lt;/code&gt;: 已执行时间)&lt;/em&gt;&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;5-调度中的重要原则与实践&quot;&gt;5. 调度中的重要原则与实践&lt;a class=&quot;anchor&quot; href=&quot;#5-调度中的重要原则与实践&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;51-机制与策略分离-mechanism-vs-policy-separation&quot;&gt;5.1 机制与策略分离 (Mechanism vs. Policy Separation)&lt;a class=&quot;anchor&quot; href=&quot;#51-机制与策略分离-mechanism-vs-policy-separation&quot;&gt;#&lt;/a&gt;&lt;/h3&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;怎么做? 操作系统内核提供通用的调度框架 (机制，如优先级队列、上下文切换函数) ，而具体的调度算法 (策略) 作为可配置或可插拔的模块实现。例如，Linux 的 &lt;code&gt;sched_class&lt;/code&gt; 结构就体现了这种思想。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;52-线程调度-thread-scheduling&quot;&gt;5.2 线程调度 (Thread Scheduling)&lt;a class=&quot;anchor&quot; href=&quot;#52-线程调度-thread-scheduling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;背景: 现代操作系统多数支持内核级线程。调度单元从进程变为线程。&lt;/li&gt;
&lt;li&gt;用户级线程 vs. 内核级线程:
&lt;ul&gt;
&lt;li&gt;用户级线程: 调度由用户空间的线程库管理，内核只看到一个进程。切换快，但一个线程阻塞会导致整个进程阻塞。无法利用多核。&lt;/li&gt;
&lt;li&gt;内核级线程: 调度由内核管理，每个线程有自己的上下文。切换开销比用户级线程大，但比进程切换小。一个线程阻塞不影响其他线程。可以并发运行在多核上。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;调度对象: 内核调度器直接调度内核级线程。对于用户级线程，内核调度的是其所属的进程 (或承载用户线程的内核线程LWP) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;6-实例操作系统调度算法&quot;&gt;6. 实例：操作系统调度算法&lt;a class=&quot;anchor&quot; href=&quot;#6-实例操作系统调度算法&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;61-典型系统采用的算法概览&quot;&gt;6.1 典型系统采用的算法概览&lt;a class=&quot;anchor&quot; href=&quot;#61-典型系统采用的算法概览&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;UNIX (早期): 动态优先级，基于nice值和CPU使用情况调整。&lt;/li&gt;
&lt;li&gt;5.3BSD: 多级反馈队列算法。&lt;/li&gt;
&lt;li&gt;Windows: 基于优先级的抢占式多任务调度 (细节见下) 。&lt;/li&gt;
&lt;li&gt;Linux: 抢占式调度，普通进程使用公平调度类 (CFS/EEVDF 演进) ，实时进程使用实时调度策略。&lt;/li&gt;
&lt;li&gt;Solaris: 综合调度算法，支持多种调度类 (实时、分时、交互、系统等) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;62-windows-线程调度&quot;&gt;6.2 Windows 线程调度&lt;a class=&quot;anchor&quot; href=&quot;#62-windows-线程调度&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;调度单位: 线程 (内核级线程)。&lt;/li&gt;
&lt;li&gt;核心算法: 基于 动态优先级 的 抢占式 调度，结合 时间配额 (Quantum) 调整。&lt;/li&gt;
&lt;li&gt;就绪队列: 维护多个优先级队列 (0-31) 。系统总是选择当前最高非空优先级队列中的线程运行。&lt;/li&gt;
&lt;li&gt;同优先级调度: 同一优先级队列内部，线程按 时间片轮转 (RR) 方式调度。&lt;/li&gt;
&lt;li&gt;多处理器:允许多个线程在不同处理器上并行运行。&lt;/li&gt;
&lt;li&gt;调度触发条件:
&lt;ul&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;线程改变其处理器亲和性 (Affinity)。&lt;/li&gt;
&lt;li&gt;时间片用完。&lt;/li&gt;
&lt;li&gt;主动放弃 (&lt;code&gt;yield&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;线程优先级:
&lt;ul&gt;
&lt;li&gt;共32个优先级级别 (0-31)。&lt;/li&gt;
&lt;li&gt;实时优先级 (16-31): 优先级固定不变。用于需要紧急响应的任务。最高。&lt;/li&gt;
&lt;li&gt;可变优先级 (1-15): 线程有一个基本优先级 (Base Priority)，其当前优先级 (Current Priority) 可以在此基础上动态调整 (提升或降低) 。用于普通用户和系统线程。&lt;/li&gt;
&lt;li&gt;系统线程 (1-15中的一部分): 用于操作系统内部任务。&lt;/li&gt;
&lt;li&gt;零页线程 (0): 特殊线程，优先级最低，用于在系统空闲时将物理内存页清零。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;时间配额 (Quantum):
&lt;ul&gt;
&lt;li&gt;不是绝对时间值，而是以 配额单位 (quantum unit) 的整数表示。系统时钟中断时递减。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Quantum&lt;/code&gt; 和 &lt;code&gt;QuantumReset&lt;/code&gt; 记录在 &lt;code&gt;KTHREAD&lt;/code&gt; 结构中。&lt;/li&gt;
&lt;li&gt;当线程用完时间配额：
&lt;ul&gt;
&lt;li&gt;如果 没有 其他同优先级或更高优先级的线程就绪，Windows会 重新分配 一个新的时间配额给该线程，让它继续运行 (避免不必要的切换) 。&lt;/li&gt;
&lt;li&gt;如果 有 其他同优先级线程就绪，该线程移到其优先级队列的末尾，调度器选择下一个线程。&lt;/li&gt;
&lt;li&gt;如果用完时间配额 且 优先级被降低，则会被抢占。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;作用: 调整时间配额 (而非仅优先级) 可以影响进程获得CPU时间的比例，而不会完全饿死其他进程。例如，给前台游戏进程更大配额，使其运行更流畅，同时后台计算任务也能获得一些CPU时间。&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;Dispatcher Ready List: 包含32个就绪线程队列的数组。&lt;/li&gt;
&lt;li&gt;KiDispatcherReadyListHead: 指向就绪队列的指针数组。&lt;/li&gt;
&lt;li&gt;Ready Summary (就绪位图): 一个32位掩码，每一位对应一个优先级队列，指示该队列是否为空。调度器通过查找第一个置位的位 (Find First Set bit, FFS) 快速找到最高优先级的非空队列。&lt;/li&gt;
&lt;li&gt;Idle Summary (空闲位图): (多处理器) 位图，指示哪些处理器当前处于空闲状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;调度策略细节:
&lt;ul&gt;
&lt;li&gt;主动切换: 进程自愿放弃CPU (阻塞、Yield等) 。&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;可变优先级线程被抢占，下次运行时继续执行剩余时间配额。&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;/li&gt;
&lt;li&gt;线程优先级提升 (Priority Boost):
&lt;ul&gt;
&lt;li&gt;目的: 改善响应性、解决饥饿、提高吞吐量。仅针对可变优先级线程 (1-15)。&lt;/li&gt;
&lt;li&gt;触发情况:
&lt;ol&gt;
&lt;li&gt;I/O操作完成: 临时提升等待该I/O的线程优先级，幅度由设备驱动程序建议 (与设备响应要求相关) ，使其能快速处理数据。提升后时间配额会减1 (避免不公平利用I/O提升) 。&lt;/li&gt;
&lt;li&gt;等待事件或信号量结束: 线程优先级提升1级 (不超过15) ，以补偿其等待时间。完成提升后的运行后，优先级会逐渐衰减回基本优先级。时间配额减1。&lt;/li&gt;
&lt;li&gt;前台进程中的线程 完成等待操作。&lt;/li&gt;
&lt;li&gt;因 窗口消息 (GUI活动) 而唤醒的线程。&lt;/li&gt;
&lt;li&gt;反饥饿: 系统线程”平衡集管理器” (Balance Set Manager) 定期扫描，将等待过久 (如 &amp;gt; 300时钟中断) 的线程优先级提升到15，并给予4倍时间配额。用完后优先级立即恢复。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;空闲线程 (Idle Thread):
&lt;ul&gt;
&lt;li&gt;每个处理器核都有一个对应的空闲线程。优先级为0。&lt;/li&gt;
&lt;li&gt;当没有其他可运行线程时，调度器调度空闲线程。&lt;/li&gt;
&lt;li&gt;功能: 循环检测是否有工作要做：处理挂起的中断(DPCs)、检查是否有新就绪线程、调用HAL执行电源管理 (如让CPU进入低功耗状态) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;63-多处理器调度-multiprocessor-scheduling&quot;&gt;6.3 多处理器调度 (Multiprocessor Scheduling)&lt;a class=&quot;anchor&quot; href=&quot;#63-多处理器调度-multiprocessor-scheduling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;特点: 系统包含多个CPU (核) ，可共享负载。&lt;/li&gt;
&lt;li&gt;对称多处理 (SMP - Symmetric Multiprocessing):
&lt;ul&gt;
&lt;li&gt;所有CPU地位平等，都可以运行内核代码和用户进程。&lt;/li&gt;
&lt;li&gt;每个CPU通常有自己的调度器实例。&lt;/li&gt;
&lt;li&gt;调度器访问共享数据结构 (如就绪队列) 需要同步 (锁、原子操作) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;设计挑战:
&lt;ul&gt;
&lt;li&gt;进程/线程分配: 决定哪个任务在哪个CPU上运行。&lt;/li&gt;
&lt;li&gt;负载均衡 (Load Balancing): 使各CPU负载大致均匀，避免某些CPU过载而其他CPU空闲。&lt;/li&gt;
&lt;li&gt;处理器亲和性 (Processor Affinity): 尽量让一个进程/线程连续在同一个CPU上运行，以利用CPU缓存 (L1/L2 cache) 中已加载的数据和TLB条目，减少缓存失效带来的开销。&lt;/li&gt;
&lt;li&gt;缓存一致性 (Cache Coherence): 硬件机制 (如MESI协议) 确保多个CPU缓存中共享数据的副本是一致的。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程分配策略:
&lt;ul&gt;
&lt;li&gt;静态进程分配 (Static Assignment): 进程从创建到结束都绑定在某个特定CPU上。每个CPU有自己的私有就绪队列。调度开销小，易于维护亲和性，但可能导致负载不均。&lt;/li&gt;
&lt;li&gt;动态进程分配 (Dynamic Assignment): 进程可以在不同CPU之间迁移。通常有一个全局共享就绪队列，或各CPU有私有队列但允许任务迁移。负载均衡好，但调度开销大 (需要同步、迁移成本) 。&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;负载均衡: 需要策略在CPU间迁移任务。
&lt;ul&gt;
&lt;li&gt;缓存亲和性 vs. 负载均衡: 这是个权衡。过于强调亲和性可能导致负载失衡；过于频繁地迁移以追求负载均衡则会破坏缓存亲和性，增加开销。&lt;/li&gt;
&lt;li&gt;例子:
&lt;ul&gt;
&lt;li&gt;并行计算/渲染: 任务与数据绑定到核心可利用缓存，但计算量不均时需迁移任务以平衡负载，导致缓存失效。&lt;/li&gt;
&lt;li&gt;CDN: 内容按地理位置缓存 (亲和性) ，但负载高时请求可能路由到其他节点 (破坏亲和性) 以均衡负载。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;工作窃取 (Work Stealing): 一种常见的负载均衡策略。每个CPU维护一个本地任务队列 (通常是双端队列) 。CPU优先执行自己队列的任务。当一个CPU空闲时，它会随机选择另一个CPU，并从其任务队列的 尾部 “窃取” 一个任务来执行。 (被窃取的CPU从头部获取任务) 。
&lt;ul&gt;
&lt;li&gt;优点: 实现了负载均衡，同时本地任务优先执行保证了一定的缓存亲和性，分布式决策减少了中心瓶颈。&lt;/li&gt;
&lt;li&gt;缺点: 仍有通信和同步开销。&lt;/li&gt;
&lt;li&gt;实例: Go语言的GMP调度器，Java的ForkJoinPool，Hadoop YARN。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;64-实时调度-real-time-scheduling-回顾&quot;&gt;6.4 实时调度 (Real-Time Scheduling) (回顾)&lt;a class=&quot;anchor&quot; href=&quot;#64-实时调度-real-time-scheduling-回顾&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;目标: 满足时间约束 (截止时间) ，高可靠性，确定性。&lt;/li&gt;
&lt;li&gt;类型: 硬实时 (必须满足) vs. 软实时 (尽量满足)。周期性 vs. 偶发性 vs. 非周期性任务。&lt;/li&gt;
&lt;li&gt;关键参数: 时间 (周期、执行时间、截止时间) 。&lt;/li&gt;
&lt;li&gt;算法: RM (静态优先级, 周期短优先), EDF (动态优先级, 截止时间早优先)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;65-openeuler-多核调度技术-简述&quot;&gt;6.5 OpenEuler 多核调度技术 (简述)&lt;a class=&quot;anchor&quot; href=&quot;#65-openeuler-多核调度技术-简述&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;基础: CPU调度是为保证并发性，通过调度程序(Scheduler)按调度策略(Policy)选择进程占用CPU。&lt;/li&gt;
&lt;li&gt;算法: 结合使用 FIFO, RR, 优先级调度 (用于实时进程) 。&lt;/li&gt;
&lt;li&gt;普通进程: 主要使用公平调度类。传统 CFS 基于虚拟运行时间按权重分配 CPU 时间；Linux 6.6 以后开始转向 EEVDF。&lt;/li&gt;
&lt;li&gt;多核调度:
&lt;ul&gt;
&lt;li&gt;早期单队列问题: 所有CPU共享一个队列。
&lt;ul&gt;
&lt;li&gt;策略一 (简单RR) ：进程在CPU间频繁迁移，破坏缓存亲和性。&lt;/li&gt;
&lt;li&gt;策略二 (带亲和性) ：尽量让进程留在一个CPU，但可能牺牲某些进程 (如E) 的公平性或导致负载失衡。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;多队列调度: 每个CPU维护自己的就绪队列 (如Q0 for CPU0, Q1 for CPU1) 。
&lt;ul&gt;
&lt;li&gt;优点: 提高缓存亲和性，减少锁竞争。&lt;/li&gt;
&lt;li&gt;问题: 可能导致负载失衡 (如一个队列空了，另一个还很忙) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;迁移线程 (Migration Thread): OpenEuler 使用迁移线程解决负载不均衡。每个CPU有一个 &lt;code&gt;migration/CPUID&lt;/code&gt; 内核线程。当检测到负载不均时 (如CPU0空闲，CPU1忙) ，CPU0可以向CPU1的停机工作队列 (stop machine workqueue) 添加一个任务，唤醒CPU1的迁移线程。该线程优先级很高，会立即执行迁移任务 (如将进程D从CPU1迁移到CPU0) ，从而实现负载均衡。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;66-linux-进程调度&quot;&gt;6.6 Linux 进程调度&lt;a class=&quot;anchor&quot; href=&quot;#66-linux-进程调度&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;调度单位: 线程 (内核级线程，Linux中称为’进程’或’任务’) 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程分类与调度策略:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实时进程 (Real-time Processes):
&lt;ul&gt;
&lt;li&gt;要求：调度延迟最低，立即响应。&lt;/li&gt;
&lt;li&gt;策略：&lt;code&gt;SCHED_FIFO&lt;/code&gt; (静态优先级，无时间片轮转；会被更高优先级任务抢占)、&lt;code&gt;SCHED_RR&lt;/code&gt; (静态优先级，带时间片轮转)。优先级范围 1-99。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;普通进程 (Normal Processes):
&lt;ul&gt;
&lt;li&gt;包括交互式进程 (需要快速响应) 和批处理进程 (后台运行，容忍延迟) 。&lt;/li&gt;
&lt;li&gt;策略：&lt;code&gt;SCHED_NORMAL&lt;/code&gt; (也叫 &lt;code&gt;SCHED_OTHER&lt;/code&gt;), &lt;code&gt;SCHED_BATCH&lt;/code&gt;, &lt;code&gt;SCHED_IDLE&lt;/code&gt;。旧表述通常说主要由 CFS 管理；Linux 6.6 以后公平调度路径开始转向 EEVDF。优先级范围 100-139 (对应nice值 -20 到 +19)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Linux调度算法演化:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux 2.4: 简单 O(n) 调度器。基于优先级和时间片。遍历整个运行队列找最高优先级进程。所有进程时间片用完后统一重新计算。对交互式进程通过剩余时间片补偿来提升优先级。缺点: 扩展性差 (高负载时慢) ，交互性优化不完善，非抢占内核。&lt;/li&gt;
&lt;li&gt;Linux 2.6 (早期): O(1) 调度器 (by Ingo Molnar):
&lt;ul&gt;
&lt;li&gt;引入 active/expired 两个优先级数组队列。调度只需 O(1) 时间找到最高优先级非空队列。&lt;/li&gt;
&lt;li&gt;动态优先级基于静态优先级(nice值)和平均睡眠时间 bonus 计算，试图区分交互式/批处理。&lt;/li&gt;
&lt;li&gt;进程时间片用完后移入 expired 队列 (除非是特殊情况) 。active 队列空后，交换 active 和 expired 指针。&lt;/li&gt;
&lt;li&gt;缺点: 区分交互式的启发式规则复杂难懂且易失效，代码难维护。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Linux 2.6 (中期): SD (Staircase Scheduler by Con Kolivas) / RSDL (Rotating Staircase Deadline Scheduler):
&lt;ul&gt;
&lt;li&gt;追求公平，抛弃复杂动态优先级。&lt;/li&gt;
&lt;li&gt;SD: 进程用完时间片后优先级降低一级 (下楼梯) ，到底后回到较高层并获更多时间片。交互进程睡眠时停留在高层，唤醒后响应快。&lt;/li&gt;
&lt;li&gt;RSDL: 引入 group quota (Tg) 和 expired 数组。高优先级组用完 Tg 后整体降级 (minor rotation) ，保证低优先级任务的可预测等待时间。时间片用完进 expired 队列。active 队列空或到底后触发 major rotation (交换 active/expired)。&lt;/li&gt;
&lt;li&gt;影响: 启发了CFS的公平思想。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Linux 2.6.23 起: CFS (Completely Fair Scheduler by Ingo Molnar):
&lt;ul&gt;
&lt;li&gt;核心思想: 完全公平。理想情况下，每个进程获得 1/n 的CPU时间。不再区分交互式/批处理，不再使用固定时间片。&lt;/li&gt;
&lt;li&gt;虚拟运行时间 (vruntime): &lt;code&gt;vruntime&lt;/code&gt; 记录进程的加权运行时间。&lt;code&gt;vruntime&lt;/code&gt; 增长速度与实际运行时间成正比，与进程权重 (优先级) 成反比。
&lt;code&gt;vruntime ≈ 实际运行时间 * (NICE_0_LOAD / 进程权重)&lt;/code&gt;
(NICE_0_LOAD 是 nice=0 进程的权重)。&lt;/li&gt;
&lt;li&gt;调度决策: 总是选择就绪队列中 &lt;code&gt;vruntime&lt;/code&gt; 最小 的进程运行。&lt;/li&gt;
&lt;li&gt;数据结构: 使用 红黑树 (Red-Black Tree) 存储就绪进程，按 &lt;code&gt;vruntime&lt;/code&gt; 排序。插入、删除、查找最小节点都是 O(log n) 时间。调度器取最左节点运行。&lt;/li&gt;
&lt;li&gt;公平性实现: 优先级高的进程权重高，&lt;code&gt;vruntime&lt;/code&gt; 增长慢，更容易被选中；优先级低的进程权重低，&lt;code&gt;vruntime&lt;/code&gt; 增长快。最终达到按权重比例分配CPU时间的效果。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Linux 6.6+：EEVDF (Earliest Eligible Virtual Deadline First):
&lt;ul&gt;
&lt;li&gt;Linux 从 6.6 开始让公平调度路径转向 EEVDF。EEVDF 仍追求同优先级可运行任务之间的公平 CPU 时间分配，但会计算 lag 和虚拟截止时间，优先选择 eligible 且虚拟截止时间最早的任务，从而改善延迟敏感任务的响应。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CFS 调度器详解:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;task_struct&lt;/code&gt;: Linux 进程/任务描述符。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;sched_entity&lt;/code&gt;: 调度实体，嵌入 &lt;code&gt;task_struct&lt;/code&gt; 中，包含CFS调度所需信息 (如 &lt;code&gt;load_weight&lt;/code&gt; 权重, &lt;code&gt;rb_node&lt;/code&gt; 红黑树节点, &lt;code&gt;vruntime&lt;/code&gt; 等) 。一个 &lt;code&gt;sched_entity&lt;/code&gt; 可以代表一个任务或一个任务组 (用于组调度) 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;sched_class&lt;/code&gt;: 调度类结构体，定义了一套调度器操作函数接口 (如 &lt;code&gt;enqueue_task&lt;/code&gt;, &lt;code&gt;dequeue_task&lt;/code&gt;, &lt;code&gt;pick_next_task&lt;/code&gt;) 。CFS, RT(FIFO/RR), Idle 都有自己的 &lt;code&gt;sched_class&lt;/code&gt; 实现。内核按优先级顺序查询 &lt;code&gt;sched_class&lt;/code&gt; 来决定使用哪个调度器。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cfs_rq&lt;/code&gt;: CFS 运行队列，每个CPU有一个。包含红黑树 &lt;code&gt;tasks_timeline&lt;/code&gt; 和 &lt;code&gt;min_vruntime&lt;/code&gt; 等信息。&lt;code&gt;min_vruntime&lt;/code&gt; 记录该队列中所有进程的最小 &lt;code&gt;vruntime&lt;/code&gt;，作为新进程/唤醒进程 &lt;code&gt;vruntime&lt;/code&gt; 计算的基准。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;红黑树 (&lt;code&gt;rb_node&lt;/code&gt;, &lt;code&gt;rb_root tasks_timeline&lt;/code&gt;): 按 &lt;code&gt;vruntime&lt;/code&gt; 组织就绪的 &lt;code&gt;sched_entity&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CFS 关键情景:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;新进程创建 (&lt;code&gt;fork()&lt;/code&gt;):
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;vruntime&lt;/code&gt; 初始值通常设为当前 &lt;code&gt;cfs_rq-&amp;gt;min_vruntime&lt;/code&gt; (或略大) ，确保新进程不会立即获得过多优势。&lt;/li&gt;
&lt;li&gt;父子 &lt;code&gt;vruntime&lt;/code&gt; 交换? 如果设置了&lt;code&gt;sysctl_sched_child_runs_first&lt;/code&gt;，且父子在同CPU，父&lt;code&gt;vruntime&lt;/code&gt; &amp;lt; 子&lt;code&gt;vruntime&lt;/code&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;code&gt;wake_up_process()&lt;/code&gt;):
&lt;ul&gt;
&lt;li&gt;调整 &lt;code&gt;vruntime&lt;/code&gt;：通常设为 &lt;code&gt;max(waker-&amp;gt;vruntime, cfs_rq-&amp;gt;min_vruntime - delta)&lt;/code&gt;，其中 &lt;code&gt;delta&lt;/code&gt; 是一个小的补偿值。确保进程不会因睡眠获得不公平优势，但也给予一定补偿使其尽快运行。&lt;/li&gt;
&lt;li&gt;插入红黑树。&lt;/li&gt;
&lt;li&gt;检查是否需要抢占当前进程 (如果唤醒进程 &lt;code&gt;vruntime&lt;/code&gt; 足够小) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;时钟中断 (&lt;code&gt;scheduler_tick()&lt;/code&gt;):
&lt;ul&gt;
&lt;li&gt;更新当前运行进程的 &lt;code&gt;vruntime&lt;/code&gt; (&lt;code&gt;actual_runtime * NICE_0_LOAD / weight&lt;/code&gt;) 。&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;cfs_rq-&amp;gt;min_vruntime&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;检查当前进程是否已运行超过其“理想运行时间” (基于调度周期和权重计算得出) 。如果是，则设置抢占标记 (&lt;code&gt;TIF_NEED_RESCHED&lt;/code&gt;)，在中断返回前会调用 &lt;code&gt;schedule()&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;主动调度 (&lt;code&gt;schedule()&lt;/code&gt;):
&lt;ul&gt;
&lt;li&gt;当前进程阻塞、&lt;code&gt;yield&lt;/code&gt; 或被标记抢占时调用。&lt;/li&gt;
&lt;li&gt;更新当前进程 &lt;code&gt;vruntime&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果当前进程仍是就绪态，将其重新插入红黑树。&lt;/li&gt;
&lt;li&gt;调用 &lt;code&gt;pick_next_task()&lt;/code&gt; 选择下一个运行进程：
&lt;ul&gt;
&lt;li&gt;按优先级查询 &lt;code&gt;sched_class&lt;/code&gt; (RT -&amp;gt; CFS -&amp;gt; Idle)。&lt;/li&gt;
&lt;li&gt;CFS 中，通常选择红黑树最左节点 (&lt;code&gt;vruntime&lt;/code&gt; 最小者) 。&lt;/li&gt;
&lt;li&gt;特殊情况：考虑 &lt;code&gt;cfs_rq-&amp;gt;next&lt;/code&gt; (上次被抢占者) 和 &lt;code&gt;cfs_rq-&amp;gt;last&lt;/code&gt; (刚运行完者) 的缓存亲和性，可能优先选择它们。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;从红黑树中移除被选中进程的 &lt;code&gt;sched_entity&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;执行上下文切换 (&lt;code&gt;context_switch()&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CFS 与进程状态转换图示:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph TD
    New -- fork() --&amp;gt; Ready(Ready State: In Red-Black Tree);
    Ready -- schedule() selects --&amp;gt; Running(Running State);
    Running -- Block (I/O, wait) --&amp;gt; Blocked(Blocked State);
    Blocked -- Wakeup --&amp;gt; Ready;
    Running -- Timeslice Check in Tick / Preempted --&amp;gt; Ready;
    Running -- exit() --&amp;gt; Terminated(Terminated State);

    subgraph CFS Logic
        direction LR
        Ready -- select min vruntime --&amp;gt; Running;
        Running -- update vruntime &amp;amp; re-insert --&amp;gt; Ready;
    end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;7-重点小结&quot;&gt;7. 重点小结&lt;a class=&quot;anchor&quot; href=&quot;#7-重点小结&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;调度基本概念: 层次 (长/中/短程) 、时机、上下文切换 (过程、开销) 。&lt;/li&gt;
&lt;li&gt;进程行为: I/O密集型 vs. CPU密集型。&lt;/li&gt;
&lt;li&gt;设计目标: 吞吐量、周转时间、响应时间、公平性、实时性等，需权衡。&lt;/li&gt;
&lt;li&gt;典型算法:
&lt;ul&gt;
&lt;li&gt;批处理: FCFS, SJF/SRTN, HRRN。&lt;/li&gt;
&lt;li&gt;交互式: RR, Priority (含反转问题), 多级队列, 多级反馈队列。&lt;/li&gt;
&lt;li&gt;实时: RM, EDF。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;关键设计点: 优先级 (静/动) 、队列组织、抢占、时间片。&lt;/li&gt;
&lt;li&gt;核心原则: 机制与策略分离。&lt;/li&gt;
&lt;li&gt;实例分析: Windows 线程调度 (优先级、时间配额、提升机制) 、Linux 进程调度 (演化、CFS核心思想、vruntime、红黑树) 。&lt;/li&gt;
&lt;li&gt;多处理器调度: SMP、负载均衡、缓存亲和性、工作窃取。&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 03: Process and Thread Model</title><link>https://www.lyt0112.com/blog/operating_systems_note_03-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_03-zh</guid><description>Operating Systems Notes 03: Process and Thread Model</description><pubDate>Thu, 27 Mar 2025 00:13:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-核心问题解答&quot;&gt;1. 核心问题解答&lt;a class=&quot;anchor&quot; href=&quot;#1-核心问题解答&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;怎样理解“进程是对CPU的抽象”这句话？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解答： 物理CPU只有一个 (或有限个) ，但通过多道程序设计和操作系统的进程调度，可以让多个程序在宏观上“同时”运行。操作系统为每个运行的程序创建一个进程，并管理它们对CPU的使用 (分时复用) 。这使得每个进程都感觉自己仿佛独占了一个CPU (或一个虚拟CPU) 来执行自己的指令序列。因此，进程机制将一个或多个物理CPU虚拟化成了多个虚拟CPU，供多个程序并发执行，这是对CPU计算能力的抽象。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;何谓进程映像？进程有实体吗？在哪里？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解答： 进程映像 (Process Image) 是指进程在执行时的完整状态描述，是进程实体的静态体现。它包括：
&lt;ul&gt;
&lt;li&gt;程序代码 (Code Segment): 进程要执行的指令。&lt;/li&gt;
&lt;li&gt;程序数据 (Data Segment): 程序使用的全局变量、静态变量等。&lt;/li&gt;
&lt;li&gt;程序堆栈 (Stack): 用于函数调用、局部变量存储。&lt;/li&gt;
&lt;li&gt;堆 (Heap): 动态分配内存的区域。&lt;/li&gt;
&lt;li&gt;进程控制块 (PCB): 包含进程的所有管理信息 (状态、ID、寄存器值、资源列表等) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程是有实体的。它的实体就是进程映像所包含的内存区域 (代码、数据、堆栈、堆) 以及在操作系统内核中的数据结构 (PCB) 。这些实体主要存在于 内存 中 (代码、数据、堆栈、堆) 和 操作系统内核空间 (PCB及其相关数据结构) 。当进程被挂起时，部分映像可能被交换到 磁盘 (交换空间)  上。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;怎样描述进程？一个进程都有什么 (组成要素) ？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解答： 描述一个进程主要通过其 进程控制块 (PCB)。PCB是操作系统感知进程存在的唯一标志，包含了描述和控制进程运行所需的所有信息。&lt;/li&gt;
&lt;li&gt;一个进程的组成要素 (即进程映像) 包括：
&lt;ul&gt;
&lt;li&gt;程序代码&lt;/li&gt;
&lt;li&gt;数据集合 (全局变量、静态变量、动态分配的内存)&lt;/li&gt;
&lt;li&gt;执行上下文 (CPU寄存器值、程序计数器PC、程序状态字PSW、栈指针等)&lt;/li&gt;
&lt;li&gt;进程控制块 (PCB) (包含进程标识符、状态、优先级、资源列表等)&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;code&gt;fork()&lt;/code&gt; 或 &lt;code&gt;CreateProcess&lt;/code&gt;) 主要包括：
&lt;ul&gt;
&lt;li&gt;分配进程标识符 (PID): 给新进程一个唯一的ID。&lt;/li&gt;
&lt;li&gt;创建和初始化进程控制块 (PCB): 分配PCB结构，并填入初始信息 (如PID、父进程ID、初始状态设为New或Ready、优先级等) 。&lt;/li&gt;
&lt;li&gt;分配地址空间: 为进程分配独立的虚拟内存空间 (可能通过复制父进程空间或加载新程序) 。&lt;/li&gt;
&lt;li&gt;加载程序和数据: 将可执行文件的代码和数据加载到进程的地址空间中 (&lt;code&gt;exec&lt;/code&gt; 的工作) 。&lt;/li&gt;
&lt;li&gt;初始化执行上下文: 设置PC指向程序入口，初始化栈指针和寄存器。&lt;/li&gt;
&lt;li&gt;分配资源: 分配进程所需的其他资源 (如文件描述符，继承自父进程或新创建) 。&lt;/li&gt;
&lt;li&gt;状态设置与调度: 将进程状态设置为就绪态 (Ready)，并将其链入就绪队列，等待调度器分配CPU。&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;ul&gt;
&lt;li&gt;创建态 (New): 进程正在被创建。&lt;/li&gt;
&lt;li&gt;就绪态 (Ready): 具备运行条件，等待CPU。&lt;/li&gt;
&lt;li&gt;运行态 (Running): 正在CPU上执行。&lt;/li&gt;
&lt;li&gt;等待态/阻塞态 (Waiting/Blocked): 等待某个事件 (如I/O完成) 而暂停执行。&lt;/li&gt;
&lt;li&gt;终止态 (Terminated): 进程执行完毕或被终止，等待系统回收资源。&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;p&gt;进程有哪些状态？进程状态之间的转换 (条件？操作？)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解答：
&lt;ul&gt;
&lt;li&gt;基本状态： 运行态 (Running)、就绪态 (Ready)、等待态 (Waiting/Blocked)。&lt;/li&gt;
&lt;li&gt;其他状态： 创建态 (New)、终止态 (Terminated)。还可能引入挂起态 (Suspended Ready, Suspended Blocked)。&lt;/li&gt;
&lt;li&gt;常见转换及条件/操作：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;New -&amp;gt; Ready&lt;/code&gt;: OS完成进程创建的必要工作，资源基本到位，允许参与调度。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ready -&amp;gt; Running&lt;/code&gt;: 进程被调度器 (Scheduler) 选中，获得CPU使用权。操作： 上下文切换，恢复进程现场。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Running -&amp;gt; Ready&lt;/code&gt;: 时间片用完；或被更高优先级的进程抢占。操作： 上下文切换，保存进程现场。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Running -&amp;gt; Waiting&lt;/code&gt;: 进程请求I/O操作或等待某一资源/事件。操作： 进程主动调用阻塞原语 (e.g., &lt;code&gt;wait()&lt;/code&gt;)，保存现场，移入等待队列。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Waiting -&amp;gt; Ready&lt;/code&gt;: 进程等待的事件发生或资源可用 (如I/O完成) 。操作： 中断处理程序或相关内核线程执行唤醒原语 (e.g., &lt;code&gt;wakeup()&lt;/code&gt;)，将进程移入就绪队列。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Running -&amp;gt; Terminated&lt;/code&gt;: 进程正常执行完毕或出错退出。操作： 进程调用退出原语 (e.g., &lt;code&gt;exit()&lt;/code&gt;)，进入终止态。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Terminated -&amp;gt; Gone&lt;/code&gt;: OS回收进程所占资源 (PCB、内存等) 。&lt;/li&gt;
&lt;/ul&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;ul&gt;
&lt;li&gt;例如，一个进程从 Running -&amp;gt; Waiting，会释放CPU。如果就绪队列非空，调度器会选择另一个 Ready 进程运行，发生 Ready -&amp;gt; Running。&lt;/li&gt;
&lt;li&gt;一个进程从 Waiting -&amp;gt; Ready (如I/O完成)，它进入就绪队列；如果它优先级更高，也可能抢占当前运行进程。&lt;/li&gt;
&lt;li&gt;一个进程 Running -&amp;gt; Terminated，会释放它占有的资源；只有当其他进程正在等待这些资源时，才可能使其从 Waiting 变为 Ready。&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;解答： 操作系统提供给进程的地址空间是 虚拟地址空间 (Virtual Address Space)。&lt;/li&gt;
&lt;li&gt;原因：
&lt;ul&gt;
&lt;li&gt;隔离与保护: 每个进程拥有独立的虚拟地址空间，一个进程无法直接访问另一个进程的内存，提供了安全保护。&lt;/li&gt;
&lt;li&gt;地址空间扩展: 虚拟地址空间可以大于物理内存，借助内存管理单元(MMU)和磁盘交换空间，给进程提供更大的可用地址范围。&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;/li&gt;
&lt;li&gt;
&lt;p&gt;操作系统如何描述进程的地址空间？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解答： 操作系统内核通常使用特定的数据结构来描述进程的地址空间。例如：
&lt;ul&gt;
&lt;li&gt;在 Linux 中，使用 &lt;code&gt;mm_struct&lt;/code&gt; 结构来表示一个进程的整个地址空间。&lt;code&gt;mm_struct&lt;/code&gt; 内部包含一个 &lt;code&gt;vm_area_struct&lt;/code&gt; (VMA) 的链表或树，每个 VMA 描述了虚拟地址空间中的一个连续区域 (段) ，包括其起止地址、访问权限 (读/写/执行) 、映射的文件 (如果有) 等信息。&lt;/li&gt;
&lt;li&gt;通过 页表 (Page Tables) 或 段表 (Segment Tables) 将虚拟地址映射到物理地址。这些表由硬件 (MMU) 使用，操作系统负责维护。&lt;/li&gt;
&lt;li&gt;可以通过 &lt;code&gt;cat /proc/&amp;lt;PID&amp;gt;/maps&lt;/code&gt; 命令查看一个进程的虚拟地址空间布局和 VMA 信息。需要将 &lt;code&gt;&amp;lt;PID&amp;gt;&lt;/code&gt; 替换为实际的进程 ID (可通过 &lt;code&gt;ps&lt;/code&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;ul&gt;
&lt;li&gt;并发应用需求: 许多应用内部包含多个并发执行的任务 (如Web服务器处理多个请求，GUI程序响应用户输入同时后台处理) 。用多进程实现这些任务，开销较大且通信复杂。&lt;/li&gt;
&lt;li&gt;开销问题: 创建进程、撤销进程、以及在进程间切换 (上下文切换) 都需要较大的时间和系统资源开销。线程作为“轻量级进程”，创建、销毁和切换的开销小得多。&lt;/li&gt;
&lt;li&gt;通信效率: 同一进程内的线程共享地址空间和大部分资源，它们之间的通信 (通过共享内存) 高效，无需内核介入。进程间通信 (IPC) 通常需要内核的协调，更复杂且效率较低。&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;ul&gt;
&lt;li&gt;用户级线程 (User-Level Threads, ULT): 线程的管理 (创建、调度、同步) 完全在用户空间由一个线程库来完成。内核对线程无感知，只管理进程。
&lt;ul&gt;
&lt;li&gt;优点: 切换快 (不需内核模式) ，可自定义调度算法，可移植性好。&lt;/li&gt;
&lt;li&gt;缺点: 一个线程阻塞 (如系统调用) ，整个进程会阻塞；无法利用多核并行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;核心级线程 (Kernel-Level Threads, KLT): 线程的管理由操作系统内核完成。内核知道每个线程的存在，并进行调度。
&lt;ul&gt;
&lt;li&gt;优点: 一个线程阻塞不影响其他线程；可以利用多核并行。&lt;/li&gt;
&lt;li&gt;缺点: 线程创建、销毁、切换需要进入内核，开销比ULT大。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;混合实现 (Hybrid Implementation): 结合了ULT和KLT。内核管理KLT，用户空间线程库将多个ULT映射到一个或多个KLT上。
&lt;ul&gt;
&lt;li&gt;目标: 兼具两者的优点，但实现复杂。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;存在多种方式的原因： 是在性能、并发能力、实现复杂度、系统资源消耗之间进行权衡的结果。
&lt;ul&gt;
&lt;li&gt;ULT优先考虑低开销和灵活性。&lt;/li&gt;
&lt;li&gt;KLT优先考虑真正的并发和对阻塞系统调用的处理。&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;线程包Pthreads中相关的函数的功能？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解答: Pthreads (POSIX Threads) 是一个线程API标准，提供了一系列函数来管理线程：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pthread_create()&lt;/code&gt;: 创建一个新的线程。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_exit()&lt;/code&gt;: 终止调用该函数的线程。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_join()&lt;/code&gt;: 等待指定的线程终止。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sched_yield()&lt;/code&gt;: 主动让出CPU，让其他线程运行。部分系统提供非标准的 &lt;code&gt;pthread_yield&lt;/code&gt; 或 &lt;code&gt;pthread_yield_np&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_self()&lt;/code&gt;: 获取调用线程自身的线程ID。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_mutex_init()&lt;/code&gt;, &lt;code&gt;pthread_mutex_lock()&lt;/code&gt;, &lt;code&gt;pthread_mutex_unlock()&lt;/code&gt;, &lt;code&gt;pthread_mutex_destroy()&lt;/code&gt;: 互斥锁相关操作，用于保护临界区，实现线程互斥。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pthread_cond_init()&lt;/code&gt;, &lt;code&gt;pthread_cond_wait()&lt;/code&gt;, &lt;code&gt;pthread_cond_signal()&lt;/code&gt;, &lt;code&gt;pthread_cond_broadcast()&lt;/code&gt;, &lt;code&gt;pthread_cond_destroy()&lt;/code&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;ul&gt;
&lt;li&gt;上下文切换触发: 时钟中断 (Timer Interrupt) 使得操作系统可以剥夺当前运行进程/线程的CPU使用权 (时间片用完) ，进行调度，切换到其他就绪的进程/线程。这是实现分时复用的基础。&lt;/li&gt;
&lt;li&gt;状态转换: I/O完成中断会通知操作系统，操作系统可以将等待该I/O的进程/线程从等待态转换为就绪态。&lt;/li&gt;
&lt;li&gt;系统调用: 进程通过执行特定的指令 (如 &lt;code&gt;syscall&lt;/code&gt; 或 &lt;code&gt;int 0x80&lt;/code&gt;) 产生异常 (陷阱 Trap) ，主动陷入内核态，请求操作系统服务 (如创建进程、读写文件、阻塞等待) 。内核处理完请求后，可能会进行调度。&lt;/li&gt;
&lt;li&gt;错误处理: 异常 (如除零、缺页故障 Page Fault) 也需要内核介入处理。缺页故障处理是虚拟内存管理的核心部分，可能导致进程阻塞 (等待页面从磁盘调入) 。&lt;/li&gt;
&lt;li&gt;保存与恢复现场: 发生中断/异常时，硬件和操作系统内核协作，必须保存当前进程/线程的执行上下文 (寄存器、PC、状态等) ，处理事件后，再恢复某个进程/线程 (可能是同一个，也可能是不同的) 的上下文继续执行。&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;有点像操作系统提供的 API 和调用这些 API 的策略之间的关系&lt;/li&gt;
&lt;li&gt;解答： 机制与策略分离 (Separation of Mechanism and Policy) 是操作系统设计的重要原则，意指提供实现某种功能的基础能力 (机制) ，与决定何时、如何使用这些能力的决策逻辑 (策略) 分开。
&lt;ul&gt;
&lt;li&gt;进程/线程状态管理 (机制)  vs. 调度算法 (策略) ： 操作系统提供了进程/线程状态 (就绪、运行、等待等) 以及在它们之间转换的机制 (如阻塞/唤醒原语、上下文切换) 。但是，选择 哪个就绪进程/线程投入运行，则是调度算法 (策略) 决定的 (如FIFO、轮转、优先级调度等) 。&lt;/li&gt;
&lt;li&gt;挂起/激活 (机制)  vs. 负载调节 (策略) ： 操作系统提供将进程换出到磁盘 (挂起) 和换回内存 (激活) 的机制。但是，决定 何时挂起哪个进程 (例如，为了降低内存压力或提高系统吞吐量) ，则是系统负载调节策略的一部分。&lt;/li&gt;
&lt;li&gt;线程实现 (机制)  vs. 应用并发模型 (策略) ： 用户级线程库提供创建和管理线程的机制。应用程序如何 利用这些线程来构建并发逻辑 (例如，线程池大小、任务分配方式) 则是应用层面的策略。&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;ul&gt;
&lt;li&gt;是什么： 协程 (Coroutine) 是一种比线程更轻量级的用户态并发 (或协作式多任务) 实现方式。它们是可以在特定点暂停执行，并在稍后从同一点恢复执行的计算过程。协程之间的切换由程序员 (或协程库/语言运行时) 显式控制，通常发生在用户态，不需要内核介入。&lt;/li&gt;
&lt;li&gt;为什么引入：
&lt;ul&gt;
&lt;li&gt;极低的切换开销: 协程切换完全在用户态进行，避免了内核态和用户态之间的切换以及内核调度，开销远小于线程切换。&lt;/li&gt;
&lt;li&gt;高并发能力: 单个线程可以管理成千上万个协程，特别适合处理大量并发连接 (如网络服务器) 或I/O密集型任务，能有效减少线程数量和内存消耗。&lt;/li&gt;
&lt;li&gt;简化异步编程: 允许使用看似同步的代码风格来编写异步逻辑 (例如，使用 &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; 关键字) ，从而避免 回调地狱 (Callback Hell)。回调地狱是指在传统异步编程中，当一个操作依赖于另一个异步操作的结果时，需要将后续操作放在前一个操作的回调函数中，如果存在多层依赖，就会形成层层嵌套的回调函数结构，导致代码难以阅读、理解和维护。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;怎么用： 通常通过编程语言或第三方库提供的支持来使用。
&lt;ul&gt;
&lt;li&gt;语言原生支持: 如 Python (&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;), C++20 (&lt;code&gt;co_await&lt;/code&gt;, &lt;code&gt;co_yield&lt;/code&gt;, &lt;code&gt;co_return&lt;/code&gt;), Rust (&lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt;)。Go 的 goroutine 是语言运行时调度的轻量级线程，常用于相似的高并发场景。&lt;/li&gt;
&lt;li&gt;库支持: 通过特定的协程库在不支持原生协程的语言中使用。&lt;/li&gt;
&lt;li&gt;用法: 开发者定义协程函数，在需要等待的操作 (通常是I/O) 前使用特定关键字 (如 &lt;code&gt;await&lt;/code&gt; 或 &lt;code&gt;yield&lt;/code&gt;) 暂停当前协程，让出执行权给其他协程或事件循环，当操作完成后，协程从暂停点恢复执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;2-进程模型-process-model&quot;&gt;2. 进程模型 (Process Model)&lt;a class=&quot;anchor&quot; href=&quot;#2-进程模型-process-model&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;21-基本概念&quot;&gt;2.1 基本概念&lt;a class=&quot;anchor&quot; href=&quot;#21-基本概念&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;顺序程序与顺序环境 (Sequential Program &amp;amp; Environment)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序 (Program): 指令或语句的序列，体现某种算法，是静态的。&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;多道程序设计 (Multiprogramming)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;允许多个程序 同时 进入内存并 交替 运行。&lt;/li&gt;
&lt;li&gt;目的: 提高CPU利用率和系统整体效率。当一个程序等待I/O时，CPU可以切换去执行另一个程序。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;并发环境与并发程序 (Concurrent Environment &amp;amp; Program)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;并发环境: 一段时间间隔内，单处理器上有两个或以上程序同时处于开始运行但尚未结束的状态，并且执行次序不确定。宏观上并行，微观上串行 (在单核CPU上) 。&lt;/li&gt;
&lt;li&gt;并发程序: 在并发环境中执行的程序。&lt;/li&gt;
&lt;li&gt;特征:
&lt;ul&gt;
&lt;li&gt;间断性: 程序执行走走停停 (&lt;code&gt;执行 -&amp;gt; 停 -&amp;gt; 执行&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;资源共享: 多个程序可能共享系统资源 (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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程 (Process)&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;li&gt;具有独立功能的程序在某个数据集合上的一次运行活动。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程是对CPU的抽象: 如前所述，它将物理CPU虚拟化为多个逻辑CPU。&lt;/li&gt;
&lt;li&gt;资源分配单位: 系统资源 (如内存、文件句柄) 以进程为单位进行分配。每个进程通常拥有独立的 地址空间。&lt;/li&gt;
&lt;li&gt;调度单位: 操作系统将CPU时间片调度给进程 (或进程中的线程) 。&lt;/li&gt;
&lt;li&gt;进程与程序的区别:
&lt;ul&gt;
&lt;li&gt;动态 vs. 静态: 进程是动态的 (有生命周期) ，程序是静态的 (文件) 。&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;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;22-进程模型详解&quot;&gt;2.2 进程模型详解&lt;a class=&quot;anchor&quot; href=&quot;#22-进程模型详解&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;进程状态 (Process States)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;三种基本状态:
&lt;ul&gt;
&lt;li&gt;运行态 (Running): 进程占有CPU，并在CPU上运行。&lt;/li&gt;
&lt;li&gt;就绪态 (Ready): 进程已具备运行条件 (资源到位) ，但因无空闲CPU而等待。&lt;/li&gt;
&lt;li&gt;等待态 (Waiting/Blocked): 进程因等待某一事件 (如I/O完成、信号量) 而暂时不能运行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;其他状态:
&lt;ul&gt;
&lt;li&gt;创建态 (New): 进程正在被创建，OS已分配PCB，但尚未完成所有初始化或未被批准执行。&lt;/li&gt;
&lt;li&gt;终止态 (Terminated): 进程已停止执行，等待OS回收资源。&lt;/li&gt;
&lt;li&gt;挂起态 (Suspended): 进程映像被从内存移到外存 (磁盘) ，用于调节系统负载或用户请求。可以有 挂起就绪 (Suspended Ready) 和 挂起阻塞 (Suspended Blocked) 两种状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程状态转换模型 (State Transition Models)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;三状态模型:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph LR
    Ready(就绪) --&amp;gt; |调度|Running(运行)
    Running --&amp;gt; |时间片到/高优先级进程抢占|Ready
    Running --&amp;gt; |等待事件|Waiting(阻塞)
    Waiting --&amp;gt; |事件发生|Ready&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;五状态模型:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph LR
    New(创建) --&amp;gt; |提交|Ready(就绪)
    Ready --&amp;gt; |调度|Running(运行)
    Running --&amp;gt; |时间片到/高优先级进程抢占|Ready
    Running --&amp;gt; |等待事件|Waiting(阻塞)
    Waiting --&amp;gt; |事件发生|Ready
    Running --&amp;gt; |完成|Terminated(终止)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;七状态模型:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;pre&gt;&lt;code&gt;graph LR
    New(创建) --&amp;gt; |提交|Ready(就绪)
    Ready --&amp;gt; |调度|Running(运行)
    Running --&amp;gt; |时间片到/高优先级进程抢占|Ready
    Running --&amp;gt; |等待事件|Waiting(阻塞)
    Waiting --&amp;gt; |事件发生|Ready
    Running --&amp;gt; |完成|Terminated(终止)
    Ready --&amp;gt; |挂起Suspend|SReady(就绪挂起)
    SReady --&amp;gt; |激活Activate|Ready
    Waiting --&amp;gt; |挂起Suspend|SWaiting(阻塞挂起)
    SWaiting --&amp;gt; |事件发生|SReady
    SWaiting --&amp;gt; |激活Activate|Waiting
    New --&amp;gt; |提交|SReady
    Running --&amp;gt; |挂起Suspend|SReady&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Linux 进程状态: 包括 &lt;code&gt;R (TASK_RUNNING)&lt;/code&gt; (运行或就绪), &lt;code&gt;S (TASK_INTERRUPTIBLE)&lt;/code&gt; (可中断睡眠), &lt;code&gt;D (TASK_UNINTERRUPTIBLE)&lt;/code&gt; (不可中断睡眠), &lt;code&gt;T (TASK_STOPPED)&lt;/code&gt; (停止), &lt;code&gt;Z&lt;/code&gt; (僵尸, 内核 exit_state 中的 &lt;code&gt;EXIT_ZOMBIE&lt;/code&gt;) 等。其状态模型与理论模型有所差异，更贴近实现。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;XV6 进程状态: &lt;code&gt;UNUSED&lt;/code&gt;, &lt;code&gt;USED&lt;/code&gt;, &lt;code&gt;SLEEPING&lt;/code&gt;, &lt;code&gt;RUNNABLE&lt;/code&gt;, &lt;code&gt;RUNNING&lt;/code&gt;, &lt;code&gt;ZOMBIE&lt;/code&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;li&gt;
&lt;p&gt;进程控制块 (Process Control Block, PCB)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义: 操作系统用于管理进程的核心数据结构，是进程存在的唯一标志。也称进程描述符。所有进程的PCB集合构成进程表。&lt;/li&gt;
&lt;li&gt;作用: 保存进程状态、资源、上下文等信息，供OS进行调度和管理。&lt;/li&gt;
&lt;li&gt;主要内容:
&lt;ul&gt;
&lt;li&gt;进程描述信息: PID (唯一标识), 进程名, 用户ID (UID), 进程组关系。&lt;/li&gt;
&lt;li&gt;进程控制信息:
&lt;ul&gt;
&lt;li&gt;当前状态 (State)。&lt;/li&gt;
&lt;li&gt;优先级 (Priority)。&lt;/li&gt;
&lt;li&gt;CPU现场信息 (Context): 程序计数器 (PC), 各种CPU寄存器, 程序状态字 (PSW), 栈指针 (SP)。这是进程切换时需要保存和恢复的关键信息。&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;I/O设备信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;进程间通信与同步信息: 消息队列指针, 信号量等。&lt;/li&gt;
&lt;li&gt;记账信息: CPU使用时间, 内存使用量等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;具体实现: 不同OS有不同结构，如 Linux 的 &lt;code&gt;task_struct&lt;/code&gt;, Windows 的 &lt;code&gt;EPROCESS&lt;/code&gt;/&lt;code&gt;KPROCESS&lt;/code&gt;/&lt;code&gt;PEB&lt;/code&gt;, Solaris 的 &lt;code&gt;proc_t&lt;/code&gt;。真实系统中的PCB结构庞大复杂。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程地址空间 (Process Address Space)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;概念: 操作系统为每个进程分配的、独立的 虚拟内存 范围。是对内存的抽象。&lt;/li&gt;
&lt;li&gt;典型布局 (从低地址到高地址):
&lt;ul&gt;
&lt;li&gt;代码段 (.text): 存放程序指令，通常只读。&lt;/li&gt;
&lt;li&gt;数据段 (.data, .bss): 存放已初始化的全局/静态变量 (.data) 和未初始化的全局/静态变量 (.bss)。&lt;/li&gt;
&lt;li&gt;堆 (Heap): 动态内存分配区域 (&lt;code&gt;malloc&lt;/code&gt;, &lt;code&gt;new&lt;/code&gt;)，向上增长。&lt;/li&gt;
&lt;li&gt;(文件映射区/共享库): 加载动态链接库、内存映射文件等。&lt;/li&gt;
&lt;li&gt;栈 (Stack): 存放函数参数、局部变量、返回地址等，向下增长。&lt;/li&gt;
&lt;li&gt;内核空间: 每个进程地址空间的高地址部分映射到操作系统的内核空间，供系统调用和中断处理使用 (用户态不可直接访问) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;独立性来源: 每个进程有自己的页表/段表，将相同的虚拟地址映射到不同的物理内存页 (或相同的只读页，如共享库代码) 。&lt;/li&gt;
&lt;li&gt;写时复制 (Copy-on-Write, COW): &lt;code&gt;fork()&lt;/code&gt; 创建子进程时，并不立即复制整个地址空间，而是让父子进程共享物理页面，并将页面标记为只读。当任何一方尝试写入时，触发异常，内核才真正复制该页面，使其私有化。这极大地优化了 &lt;code&gt;fork()&lt;/code&gt; 的效率，特别是 &lt;code&gt;fork()&lt;/code&gt; 后立即 &lt;code&gt;exec()&lt;/code&gt; 的情况，因为&lt;code&gt;exec()&lt;/code&gt;会替换整个地址空间，使得大部分共享页面在被写入前就已被丢弃，从而避免了不必要的复制开销。
&lt;ul&gt;
&lt;li&gt;COW异常的详细处理流程：
&lt;ol&gt;
&lt;li&gt;写操作触发页面故障： 当父进程或子进程尝试写入共享的只读页面时，CPU检测到违反内存保护，触发页面故障异常(page fault)。&lt;/li&gt;
&lt;li&gt;进入内核态： CPU立即切换到内核态，保存当前上下文，并跳转到页面故障处理程序。&lt;/li&gt;
&lt;li&gt;异常处理： 内核的页面故障处理程序检查故障原因，发现是COW页面的写操作。&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;/li&gt;
&lt;li&gt;查看: &lt;code&gt;cat /proc/&amp;lt;PID&amp;gt;/maps&lt;/code&gt; (Linux) 可以查看进程的虚拟内存区域布局。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进程队列 (Process Queues)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;操作系统通常根据进程状态将PCB组织在不同的队列中。&lt;/li&gt;
&lt;li&gt;就绪队列 (Ready Queue): 存放所有处于就绪态的进程PCB。调度器从中选择下一个要运行的进程。可能按优先级组织成多个队列。&lt;/li&gt;
&lt;li&gt;等待队列 (Waiting Queues): 可能有多个，每个队列对应一个特定的等待事件 (如等待磁盘I/O、等待键盘输入、等待某个信号量) 。当进程等待某事件时，其PCB被移入相应的等待队列。&lt;/li&gt;
&lt;li&gt;进程状态的改变伴随着其PCB在不同队列间的移动。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;23-进程控制&quot;&gt;2.3 进程控制&lt;a class=&quot;anchor&quot; href=&quot;#23-进程控制&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;进程控制原语&quot;&gt;进程控制原语&lt;a class=&quot;anchor&quot; href=&quot;#进程控制原语&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;原语 (Primitive) 是完成某种特定功能的一段程序，具有不可分割性或不可中断性，即原语的执行必须是连续的，在执行过程中不允许被中断，也称为原子操作 (Atomic) 。&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;li&gt;改变进程优先级原语&lt;/li&gt;
&lt;li&gt;等等&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;进程的生命周期&quot;&gt;进程的生命周期&lt;a class=&quot;anchor&quot; href=&quot;#进程的生命周期&quot;&gt;#&lt;/a&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;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;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;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;/ul&gt;
&lt;h4 id=&quot;进程控制操作&quot;&gt;进程控制操作&lt;a class=&quot;anchor&quot; href=&quot;#进程控制操作&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;h5 id=&quot;进程的创建&quot;&gt;进程的创建&lt;a class=&quot;anchor&quot; href=&quot;#进程的创建&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;进程创建的主要步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;给新进程分配一个唯一标识(pid)以及进程控制块(PCB)&lt;/li&gt;
&lt;li&gt;为进程分配地址空间&lt;/li&gt;
&lt;li&gt;初始化进程控制块
&lt;ul&gt;
&lt;li&gt;设置默认值 (如：状态为 New，…)&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;p&gt;不同操作系统的实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UNIX：fork/exec&lt;/li&gt;
&lt;li&gt;WINDOWS：CreateProcess&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;进程的撤销&quot;&gt;进程的撤销&lt;a class=&quot;anchor&quot; href=&quot;#进程的撤销&quot;&gt;#&lt;/a&gt;&lt;/h5&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;撤销该进程的PCB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不同操作系统的实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UNIX：exit&lt;/li&gt;
&lt;li&gt;WINDOWS：ExitProcess&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;进程阻塞和进程唤醒&quot;&gt;进程阻塞和进程唤醒&lt;a class=&quot;anchor&quot; href=&quot;#进程阻塞和进程唤醒&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;处于运行状态的进程，在其运行过程中期待某一事件发生 (如等待键盘输入、等待磁盘数据传输完成、等待其它进程发送消息) ，当被等待的事件未发生时，由进程自己执行阻塞原语，使自己由运行态变为阻塞态。&lt;/p&gt;
&lt;p&gt;不同操作系统的实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UNIX：wait&lt;/li&gt;
&lt;li&gt;WINDOWS：WaitForSingleObject&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;unix系统设计的进程控制操作&quot;&gt;UNIX系统设计的进程控制操作&lt;a class=&quot;anchor&quot; href=&quot;#unix系统设计的进程控制操作&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;UNIX系统提供了一系列系统调用来实现进程控制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fork(): 通过复制调用进程来建立新的进程，是最基本的进程建立过程&lt;/li&gt;
&lt;li&gt;exec(): 包括一系列系统调用，它们都是通过用一段新的代码覆盖原来的内存空间，实现进程执行代码的转换&lt;/li&gt;
&lt;li&gt;wait(): 提供初级的进程同步措施，能使一个进程等待，直到另外一个进程结束为止&lt;/li&gt;
&lt;li&gt;exit(): 用来终止一个进程的运行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些系统调用之间的关联 (shell、fork()、exec()、wait()) 体现了UNIX进程管理的设计哲学，通过简单而正交的原语组合实现复杂功能。&lt;/p&gt;
&lt;h4 id=&quot;unix的fork实现及优化&quot;&gt;UNIX的fork()实现及优化&lt;a class=&quot;anchor&quot; href=&quot;#unix的fork实现及优化&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;UNIX的fork()实现步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为子进程分配一个空闲的进程描述符 (proc结构)&lt;/li&gt;
&lt;li&gt;分配给子进程唯一标识pid&lt;/li&gt;
&lt;li&gt;以一次一页的方式复制父进程地址空间&lt;/li&gt;
&lt;li&gt;从父进程处继承共享资源，如打开的文件和当前工作目录等&lt;/li&gt;
&lt;li&gt;将子进程的状态设为就绪，插入到就绪队列&lt;/li&gt;
&lt;li&gt;对子进程返回标识符0&lt;/li&gt;
&lt;li&gt;对父进程返回子进程的pid&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;优化方案：
Linux的解决方案是利用存储管理模块中的”写时复制技术”COW (Copy-On-Write) 对fork()进行了优化。&lt;/p&gt;
&lt;h4 id=&quot;写时复制-copy-on-write-cow-技术&quot;&gt;写时复制 (Copy-on-Write, COW) 技术&lt;a class=&quot;anchor&quot; href=&quot;#写时复制-copy-on-write-cow-技术&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;重新审视fork函数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;虚拟内存和内存映射解释了fork如何为每个进程提供私有地址空间
&lt;ul&gt;
&lt;li&gt;fork()后跟exec()的常见情况的常用方法&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;为新进程创建虚拟地址空间的步骤：
&lt;ul&gt;
&lt;li&gt;创建新的进程mm_struct、vm_area_struct、页表的精确副本&lt;/li&gt;
&lt;li&gt;将两个进程中的每个页面标记为只读&lt;/li&gt;
&lt;li&gt;将两个进程中的每个vm_area_struct标记为私有COW&lt;/li&gt;
&lt;li&gt;返回时，每个进程都有虚拟内存的精确副本&lt;/li&gt;
&lt;li&gt;后续写入使用COW机制创建新页面&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-线程模型-thread-model&quot;&gt;3. 线程模型 (Thread Model)&lt;a class=&quot;anchor&quot; href=&quot;#3-线程模型-thread-model&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-线程的引入&quot;&gt;3.1 线程的引入&lt;a class=&quot;anchor&quot; href=&quot;#31-线程的引入&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;为什么引入线程？
&lt;ul&gt;
&lt;li&gt;应用的需要: 一个应用程序内部往往有多个并发执行流的需求。例如：
&lt;ul&gt;
&lt;li&gt;字处理软件: 用户输入 (前台线程) 、后台自动保存 (后台线程) 、拼写检查 (后台线程) 。&lt;/li&gt;
&lt;li&gt;Web服务器: 主线程监听连接，每个连接分配一个工作线程处理请求 (读文件、网络发送) 。&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;li&gt;并行计算: 在多核CPU上，同一进程的多个线程可以真正并行执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;32-线程的基本概念&quot;&gt;3.2 线程的基本概念&lt;a class=&quot;anchor&quot; href=&quot;#32-线程的基本概念&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;线程 (Thread)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程内的一个 执行实体 (或执行流)。&lt;/li&gt;
&lt;li&gt;是 CPU调度 的基本单位。&lt;/li&gt;
&lt;li&gt;有时称为 轻量级进程 (Lightweight Process, LWP)。&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;拥有独立的状态: (Running, Ready, Blocked等)，需要进行状态转换管理。&lt;/li&gt;
&lt;li&gt;拥有独立的执行上下文: 程序计数器 (PC), 寄存器集合, 栈 (Stack) 和栈指针 (SP)。线程切换时保存/恢复的是这部分私有上下文。&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;多线程进程模型: 一个进程包含一个PCB和多个线程控制块 (Thread Control Block, TCB)。所有TCB共享进程的地址空间和资源，但每个TCB有自己独立的PC、寄存器和栈。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;33-线程的实现&quot;&gt;3.3 线程的实现&lt;a class=&quot;anchor&quot; href=&quot;#33-线程的实现&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;用户级线程 (User-Level Threads, ULT)&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;ul&gt;
&lt;li&gt;阻塞问题: 如果一个用户级线程执行了阻塞式系统调用，整个进程都会被内核阻塞，即使其他线程是就绪的。&lt;/li&gt;
&lt;li&gt;多核利用问题: 内核只把CPU分配给进程，所以一个进程中的多个ULT不能在多核上并行执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;阻塞处理:
&lt;ul&gt;
&lt;li&gt;使用非阻塞系统调用。&lt;/li&gt;
&lt;li&gt;使用 “Jacketing” / “Wrapper” 技术：库函数在调用可能阻塞的系统调用前检查，如果会阻塞，则不调用，而是切换到另一个用户线程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;核心级线程 (Kernel-Level Threads, KLT)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实现: 线程的管理 (创建、调度、同步) 由操作系统内核完成。内核维护每个线程的TCB。&lt;/li&gt;
&lt;li&gt;优点:
&lt;ul&gt;
&lt;li&gt;一个线程阻塞不影响进程内其他线程的执行。&lt;/li&gt;
&lt;li&gt;内核可以直接调度线程，可以在多核CPU上实现真正的并行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;缺点:
&lt;ul&gt;
&lt;li&gt;线程的创建、销毁、切换都需要进入内核态，开销比ULT大 (但仍远小于进程切换) 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;例子: Windows 线程, Linux 的 NPTL (Native POSIX Thread Library，实际上是KLT)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;混合模型 (Hybrid Implementation)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实现: 内核支持KLT，用户空间线程库将多个ULT映射到少量KLT上 (M:N模型) 。线程创建在用户态快，调度利用内核。&lt;/li&gt;
&lt;li&gt;例子: 早期的 Solaris。&lt;/li&gt;
&lt;li&gt;目标: 试图结合ULT的低开销和KLT的并发优势，但实现复杂，现在较少见，Linux和Windows都主要采用KLT模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;4-协程-coroutine&quot;&gt;4. 协程 (Coroutine)&lt;a class=&quot;anchor&quot; href=&quot;#4-协程-coroutine&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;为什么引入协程？&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;/ul&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;li&gt;协程可以在执行过程中的特定点 暂停 (yield)，然后在未来从同一点 恢复 (resume)。&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;/li&gt;
&lt;li&gt;常见模式:
&lt;ul&gt;
&lt;li&gt;定义协程函数 (如 Python 的 &lt;code&gt;async def&lt;/code&gt;) 。&lt;/li&gt;
&lt;li&gt;在协程函数内部，遇到需要等待的操作 (如异步I/O) 时，使用特定关键字 (如 &lt;code&gt;await&lt;/code&gt;, &lt;code&gt;yield&lt;/code&gt;) 主动让出控制权。&lt;/li&gt;
&lt;li&gt;一个事件循环 (Event Loop) 或调度器负责管理协程的暂停和恢复。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;例子: Python &lt;code&gt;asyncio&lt;/code&gt;, C++20 &lt;code&gt;coroutine&lt;/code&gt;, Rust &lt;code&gt;async/await&lt;/code&gt;。Go 的 goroutine 是运行时调度的轻量级线程，常被放在同类用户态并发机制里比较。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;纤程 (Fiber)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 操作系统提供的一种类似协程的机制，也是用户态调度的轻量级执行单元，一个线程内可以包含多个纤程。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;5-重点小结&quot;&gt;5. 重点小结&lt;a class=&quot;anchor&quot; href=&quot;#5-重点小结&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;进程 (Process):
&lt;ul&gt;
&lt;li&gt;并发执行程序的实例，动态产生和消亡。&lt;/li&gt;
&lt;li&gt;OS 资源分配 的独立单位，拥有独立地址空间。&lt;/li&gt;
&lt;li&gt;基本特征：并发性、动态性、独立性、制约性、异步性。&lt;/li&gt;
&lt;li&gt;进程映像 = 代码 + 数据 + 栈 + 堆 + PCB。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;线程 (Thread):
&lt;ul&gt;
&lt;li&gt;进程内的一个执行流，CPU调度 的基本单位。&lt;/li&gt;
&lt;li&gt;拥有私有执行上下文 (PC, Regs, Stack) ，共享进程资源 (地址空间, 文件) 。&lt;/li&gt;
&lt;li&gt;引入原因：应用并发需求、低开销、高性能 (通信、并行) 。&lt;/li&gt;
&lt;li&gt;实现方式：用户级 (ULT)、核心级 (KLT)、混合。各有优劣。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;协程 (Coroutine):
&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;/li&gt;
&lt;li&gt;可再入程序 (Reentrant Program):
&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><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 02: Interrupt and Exception Mechanism</title><link>https://www.lyt0112.com/blog/operating_systems_note_02-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_02-zh</guid><description>Operating Systems Notes 02: Interrupt and Exception Mechanism</description><pubDate>Thu, 13 Mar 2025 04:20:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-核心问题解答&quot;&gt;1. 核心问题解答&lt;a class=&quot;anchor&quot; href=&quot;#1-核心问题解答&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&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;ul&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;中断通常由外部设备 (如键盘、鼠标、网络接口等) 产生，而异常通常由CPU在执行指令时检测到的错误 (如除零错误、非法指令等) 产生。处理方式有所不同，中断处理程序通常较为简单，主要负责响应外部设备的请求，而异常处理程序则需要更复杂的错误处理机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;回顾一下：ICS对异常的描述及分类&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ICS (计算机系统结构) 将异常分为四类：陷入 (Trap) 、故障 (Fault) 、终止 (Abort) 和中断 (Interrupt) 。陷入是由用户程序主动发起的系统调用，故障是可恢复的错误，终止是不可恢复的错误，中断是由外部设备发起的请求。&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;/li&gt;
&lt;/ul&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;ul&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;软件异常是由软件引发的异常情况，例如非法内存访问、除零错误等。软件异常通过硬件检测并触发相应的异常处理程序，操作系统负责处理这些异常并采取相应的措施，如终止进程、生成错误报告等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;X86有哪些控制和状态寄存器？所起的作用是什么？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;X86处理器有多个控制和状态寄存器，包括CR0-CR4 (控制寄存器) 、EFLAGS (状态寄存器) 、GDTR/IDTR (全局/中断描述符表寄存器) 等。控制寄存器用于控制处理器的操作模式，状态寄存器保存处理器的状态标志，描述符表寄存器用于存储全局和中断描述符表的地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;X86在PentiumII 300之后提供了sysenter/sysexit指令，为什么？与int 0x80/iret有什么不同？X86-64提供的系统调用指令是什么？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sysenter/sysexit指令提供了更高效的系统调用机制，减少了系统调用的开销。与int 0x80/iret相比，sysenter/sysexit指令不需要保存和恢复中断标志，减少了上下文切换的开销。X86-64提供的系统调用指令是syscall/sysret。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;关于基于x86体系结构的Linux的系统调用实现：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;系统调用入口程序system_call()与中断描述符表是什么关系？与系统调用表是什么关系？
&lt;ul&gt;
&lt;li&gt;system_call()是系统调用的入口程序，通过中断描述符表 (IDT) 中的中断向量指向。系统调用表 (sys_call_table) 存储了所有系统调用的地址，system_call()根据系统调用号查找并调用相应的系统调用处理程序。&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;ol start=&quot;12&quot;&gt;
&lt;li&gt;系统调用与函数/过程调用的区别是什么？系统调用与C函数调用的区别？系统调用与API的关系？
&lt;ul&gt;
&lt;li&gt;系统调用是操作系统提供的接口，用于应用程序请求操作系统服务。函数/过程调用是程序内部的调用机制。系统调用与C函数调用的区别在于系统调用需要从用户态切换到内核态，而C函数调用在用户态内执行。API (应用程序编程接口) 是应用程序与操作系统或库函数之间的接口，系统调用是API的一部分，提供底层操作系统服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;关键核心：ECF——异常控制流&lt;/p&gt;
&lt;p&gt;理解ECF (异常控制流) 是深入理解计算机系统和操作系统交互的关键。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;理解应用程序是如何与操作系统交互的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ECF描述了应用程序在运行过程中如何通过系统调用、中断和异常与操作系统进行交互。通过理解ECF，可以更好地理解操作系统如何管理硬件资源和提供服务。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;编写有趣的新应用程序：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过掌握ECF的原理，开发者可以编写更高效、更可靠的应用程序。理解系统调用和异常处理机制，可以帮助开发者优化程序性能，并处理各种异常情况。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;理解并发：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ECF在并发编程中起着重要作用。通过理解中断和异常的处理流程，可以更好地设计和实现多线程、多进程的并发程序，确保程序的正确性和高效性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;理解软件异常如何工作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;软件异常是ECF的重要组成部分。通过理解软件异常的触发和处理机制，可以更好地调试和维护程序，提升程序的稳定性和安全性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;2-中央处理器cpu&quot;&gt;2. 中央处理器(CPU)&lt;a class=&quot;anchor&quot; href=&quot;#2-中央处理器cpu&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;21-关于寄存器&quot;&gt;2.1 关于寄存器&lt;a class=&quot;anchor&quot; href=&quot;#21-关于寄存器&quot;&gt;#&lt;/a&gt;&lt;/h3&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;条件码寄存器：保存CPU操作结果的标记位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;控制和状态寄存器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用于控制处理器操作，在特权级别下可访问&lt;/li&gt;
&lt;li&gt;程序计数器(PC, Program Counter)&lt;/li&gt;
&lt;li&gt;指令寄存器(IR, Instruction Register)&lt;/li&gt;
&lt;li&gt;程序状态字(PSW, Program Status Word)：存储处理器当前运行状态的关键寄存器，包含多种重要信息：
&lt;ul&gt;
&lt;li&gt;条件码标志位：如零标志(Zero)、进位标志(Carry)、溢出标志(Overflow)等，反映算术和逻辑运算的结果&lt;/li&gt;
&lt;li&gt;中断控制位：控制处理器对中断的响应方式，如中断使能/禁止标志&lt;/li&gt;
&lt;li&gt;处理器模式位：指示当前CPU运行在什么特权级别(如用户态/内核态)&lt;/li&gt;
&lt;li&gt;内存管理相关标志：如分页模式、虚拟内存使能等
PSW在进程上下文切换和中断处理过程中会被保存和恢复，是操作系统实现特权保护和进程隔离的核心机制。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;22-操作系统的需求之一--保护&quot;&gt;2.2 操作系统的需求之一 —— 保护&lt;a class=&quot;anchor&quot; href=&quot;#22-操作系统的需求之一--保护&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;从操作系统的特征考虑&quot;&gt;从操作系统的特征考虑&lt;a class=&quot;anchor&quot; href=&quot;#从操作系统的特征考虑&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;操作系统需要处理并发和共享资源的问题，这就提出了对系统进行保护与控制的要求。为了实现这一点，操作系统依赖于硬件机制来隔离操作系统和用户程序。&lt;/p&gt;
&lt;h4 id=&quot;硬件机制的支持&quot;&gt;硬件机制的支持&lt;a class=&quot;anchor&quot; href=&quot;#硬件机制的支持&quot;&gt;#&lt;/a&gt;&lt;/h4&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;/ul&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;li&gt;在低特权级别 (如用户态) ，处理器只能执行非特权指令，访问受限的资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过这些硬件机制，操作系统能够有效地保护自身不被用户程序破坏，同时也能控制用户程序的行为，确保系统的稳定和安全。&lt;/p&gt;
&lt;h3 id=&quot;23-处理器的状态模式&quot;&gt;2.3 处理器的状态(模式)&lt;a class=&quot;anchor&quot; href=&quot;#23-处理器的状态模式&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;现代处理器通常将CPU状态划分为两种、三种或四种，在程序状态字寄存器PSW中设置位，根据运行程序对资源和指令的使用权限设置不同的CPU状态。&lt;/p&gt;
&lt;p&gt;例如X86架构中的EFLAGS寄存器，RISC-V的三种特权模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;机器模式(M模式)&lt;/li&gt;
&lt;li&gt;用户模式(U模式)&lt;/li&gt;
&lt;li&gt;监管模式(S模式)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;24-特权指令和非特权指令&quot;&gt;2.4 特权指令和非特权指令&lt;a class=&quot;anchor&quot; href=&quot;#24-特权指令和非特权指令&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;操作系统需要两种CPU状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内核态(Kernel Mode)：运行操作系统程序&lt;/li&gt;
&lt;li&gt;用户态(User Mode)：运行用户程序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;特权指令：只能由操作系统使用、用户程序不能使用的指令
非特权指令：用户程序可以使用的指令&lt;/p&gt;
&lt;p&gt;X86支持4个处理器特权级别(特权环 Ring)：R0、R1、R2和R3&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;R0相当于内核态，特权能力最高&lt;/li&gt;
&lt;li&gt;R3相当于用户态，特权能力最低&lt;/li&gt;
&lt;li&gt;目前大多数基于x86处理器的操作系统只用了R0和R3两个特权级别&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;25-cpu状态之间的转换&quot;&gt;2.5 CPU状态之间的转换&lt;a class=&quot;anchor&quot; href=&quot;#25-cpu状态之间的转换&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;用户态 → 内核态：唯一途径是通过中断/异常/陷入机制&lt;/li&gt;
&lt;li&gt;内核态 → 用户态：通过设置程序状态字PSW&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;陷入指令(访管指令, supervisor call)：提供给用户程序的接口，用于调用操作系统功能
例如：int, trap, syscall, sysenter/sysexit, ecall&lt;/p&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-中断机制&quot;&gt;3. 中断机制&lt;a class=&quot;anchor&quot; href=&quot;#3-中断机制&quot;&gt;#&lt;/a&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;捕获用户程序提出的服务请求&lt;/li&gt;
&lt;li&gt;防止用户程序执行过程中的破坏性活动&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;31-中断异常的概念&quot;&gt;3.1 中断/异常的概念&lt;a class=&quot;anchor&quot; href=&quot;#31-中断异常的概念&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CPU对系统发生的某个事件作出的一种反应：&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;/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;h4 id=&quot;中断异常的来源及处理方式&quot;&gt;中断/异常的来源及处理方式&lt;a class=&quot;anchor&quot; href=&quot;#中断异常的来源及处理方式&quot;&gt;#&lt;/a&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;/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;中断处理：中断处理程序通常是操作系统的一部分，负责响应外部设备的请求。处理过程包括保存当前CPU状态、执行中断处理程序、恢复CPU状态并返回被中断的程序。&lt;/li&gt;
&lt;li&gt;异常处理：异常处理程序也由操作系统提供，负责处理程序运行过程中出现的错误或特殊事件。处理过程包括识别异常类型、执行相应的处理程序、根据异常类型决定是否返回被中断的程序或终止程序。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;术语演化的历史背景&quot;&gt;术语演化的历史背景&lt;a class=&quot;anchor&quot; href=&quot;#术语演化的历史背景&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;中断的引入：为了支持CPU和设备之间的并行操作&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当CPU启动设备进行输入/输出后，设备便可以独立工作，CPU转去处理与此次输入/输出不相关的事情；当设备完成输入/输出后，通过向CPU发中断报告此次输入/输出的结果，让CPU决定如何处理以后的事情&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;异常的引入：表示CPU执行指令时本身出现的问题&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如算术溢出、除零、取数时的奇偶错，访存地址时越界或执行了“陷入指令”等，这时硬件改变了CPU当前的执行流程，转到相应的错误处理程序或异常处理程序或执行系统调用&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-事件&quot;&gt;3.2 事件&lt;a class=&quot;anchor&quot; href=&quot;#32-事件&quot;&gt;#&lt;/a&gt;&lt;/h3&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;中断(Interrupt)&lt;/td&gt;&lt;td&gt;来自I/O设备、其他硬件部件&lt;/td&gt;&lt;td&gt;异步&lt;/td&gt;&lt;td&gt;总是返回到下一条指令&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;陷入(Trap)&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;故障(Fault)&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;终止(Abort)&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;h3 id=&quot;33-中断异常机制工作原理&quot;&gt;3.3 中断/异常机制工作原理&lt;a class=&quot;anchor&quot; href=&quot;#33-中断异常机制工作原理&quot;&gt;#&lt;/a&gt;&lt;/h3&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;/ul&gt;
&lt;p&gt;在每条指令执行周期的最后时刻扫描中断寄存器，查看是否有中断信号。
若无中断信号，继续执行下一条指令。
若有中断，中断硬件将该中断触发器内容按规定编码送入PSW的相应位，称为中断码，通过交换中断向量引出中断处理程序。&lt;/p&gt;
&lt;p&gt;硬件——中断响应过程示意：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在每条指令执行周期的最后时刻，扫描中断寄存器。&lt;/li&gt;
&lt;li&gt;检查是否有中断信号。
&lt;ul&gt;
&lt;li&gt;若无中断信号，继续执行下一条指令。&lt;/li&gt;
&lt;li&gt;若有中断信号，中断硬件将中断触发器内容按规定编码送入PSW的相应位，称为中断码。&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;中断处理完成后，恢复CPU状态并返回被中断的程序。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;34-软硬协同中断向量表&quot;&gt;3.4 软硬协同——中断向量表&lt;a class=&quot;anchor&quot; href=&quot;#34-软硬协同中断向量表&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;中断向量：用于定位中断或异常处理入口的信息。实模式中可理解为入口地址表；x86 保护模式下对应 IDT 描述符，包含处理程序入口、段选择子和属性等信息。&lt;/p&gt;
&lt;p&gt;硬件执行流程按中断号/异常类型的不同，通过中断向量表转移控制权给中断处理程序&lt;/p&gt;
&lt;p&gt;Linux中的中断向量(X86，教学示例，具体分配会随内核版本和APIC配置变化)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0～19：不可屏蔽中断和异常
&lt;ul&gt;
&lt;li&gt;0: 除法错误 #DE&lt;/li&gt;
&lt;li&gt;1: 调试异常 #DB&lt;/li&gt;
&lt;li&gt;2: NMI中断&lt;/li&gt;
&lt;li&gt;3: 断点异常 #BP&lt;/li&gt;
&lt;li&gt;4: 溢出异常 #OF&lt;/li&gt;
&lt;li&gt;5: 边界检查异常 #BR&lt;/li&gt;
&lt;li&gt;6: 无效操作码异常 #UD&lt;/li&gt;
&lt;li&gt;7: 设备不可用异常 #NM&lt;/li&gt;
&lt;li&gt;8: 双重故障异常 #DF&lt;/li&gt;
&lt;li&gt;9: 协处理器段越界异常&lt;/li&gt;
&lt;li&gt;10: 无效TSS异常 #TS&lt;/li&gt;
&lt;li&gt;11: 段不存在异常 #NP&lt;/li&gt;
&lt;li&gt;12: 栈段错误 #SS&lt;/li&gt;
&lt;li&gt;13: 通用保护异常 #GP&lt;/li&gt;
&lt;li&gt;14: 页错误异常 #PF (包含COW写时复制机制的页错误)&lt;/li&gt;
&lt;li&gt;15: 保留&lt;/li&gt;
&lt;li&gt;16: 浮点异常 #MF&lt;/li&gt;
&lt;li&gt;17: 对齐检查异常 #AC&lt;/li&gt;
&lt;li&gt;18: 机器检查异常 #MC&lt;/li&gt;
&lt;li&gt;19: SIMD浮点异常 #XF&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;20～31：Intel保留&lt;/li&gt;
&lt;li&gt;32～127：外部中断(IRQ)&lt;/li&gt;
&lt;li&gt;128(0x80)：用于系统调用的可编程异常&lt;/li&gt;
&lt;li&gt;129～238：外部中断&lt;/li&gt;
&lt;li&gt;239：本地APIC时钟中断&lt;/li&gt;
&lt;li&gt;240：本地APIC高温中断&lt;/li&gt;
&lt;li&gt;241～250：Linux保留&lt;/li&gt;
&lt;li&gt;251～253：处理器间中断&lt;/li&gt;
&lt;li&gt;254：本地APIC错误中断&lt;/li&gt;
&lt;li&gt;255：本地APIC伪中断&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;35-中断响应流程&quot;&gt;3.5 中断响应流程&lt;a class=&quot;anchor&quot; href=&quot;#35-中断响应流程&quot;&gt;#&lt;/a&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;li&gt;从中断向量表或 IDT 中取出处理程序入口，并把控制权转移到该入口&lt;/li&gt;
&lt;li&gt;执行中断处理程序&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;36-上半部和下半部处理&quot;&gt;3.6 上半部和下半部处理&lt;a class=&quot;anchor&quot; href=&quot;#36-上半部和下半部处理&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在 Linux 系统中，中断处理程序应该尽量短且快，以减少对正常进程调度的影响。然而，中断处理程序可能会暂时关闭中断，如果执行时间过长，可能会丢失其他设备的中断请求。为了解决这个问题，Linux 将中断过程分为上半部和下半部。&lt;/p&gt;
&lt;p&gt;上半部用于快速处理中断，通常会暂时关闭当前中断线或在受限的中断上下文中执行，主要负责处理与硬件紧密相关或时间敏感的任务。下半部用于延迟处理上半部未完成的工作，Linux 中常见形式包括软中断、tasklet 和 workqueue。&lt;/p&gt;
&lt;p&gt;上半部 (Top Half) ：&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;下半部 (Bottom Half) ：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下半部是中断处理程序的第二部分，通常由上半部调度执行。&lt;/li&gt;
&lt;li&gt;其主要任务是延迟处理上半部未完成的工作，完成较为复杂和耗时的处理。&lt;/li&gt;
&lt;li&gt;软中断和 tasklet 运行在中断或软中断上下文中，不能睡眠。普通 workqueue 运行在内核线程上下文中，可以睡眠。&lt;/li&gt;
&lt;li&gt;典型的下半部操作包括：数据处理、更新数据结构、唤醒等待的进程等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如，当网卡收到网络包后，通过 DMA 将数据写入内存，并通过硬件中断通知内核有新数据到达。内核调用中断处理程序，分为上半部和下半部。上半部会先禁止网卡中断，避免频繁硬中断降低内核效率，然后触发软中断，将耗时且复杂的任务交给软中断处理程序 (下半部) 处理，如解析网络数据并将其传递给应用程序。&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;/ol&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;还有一个区别，硬中断 (上半部) 会打断 CPU 正在执行的任务，然后立即执行中断处理程序。软中断可以在中断返回路径上执行；当软中断负载较重或需要推迟时，也可能由每个 CPU 对应的 &lt;code&gt;ksoftirqd/CPU编号&lt;/code&gt; 内核线程处理，比如 &lt;code&gt;ksoftirqd/0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Ref: &lt;a href=&quot;https://xiaolincoding.com/os/1_hardware/soft_interrupt.html#%E4%BB%80%E4%B9%88%E6%98%AF%E8%BD%AF%E4%B8%AD%E6%96%AD&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;软中断&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;37-软件中断处理程序&quot;&gt;3.7 软件——中断处理程序&lt;a class=&quot;anchor&quot; href=&quot;#37-软件中断处理程序&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;设计操作系统时，为每一类中断/异常事件编好相应的处理程序，并设置好中断向量表。系统运行时若响应中断，中断硬件部件将CPU控制权转给中断处理程序：&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 id=&quot;38-中断异常机制小结&quot;&gt;3.8 中断/异常机制小结&lt;a class=&quot;anchor&quot; href=&quot;#38-中断异常机制小结&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;以设备输入输出中断为例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;打印机给CPU发中断信号&lt;/li&gt;
&lt;li&gt;CPU处理完当前指令后检测到中断，判断出中断来源并向相关设备发确认信号&lt;/li&gt;
&lt;li&gt;CPU开始为软件处理中断做准备：
&lt;ul&gt;
&lt;li&gt;处理器状态被切换到内核态&lt;/li&gt;
&lt;li&gt;在系统栈中保存被中断程序的重要上下文环境，主要是程序计数器PC、程序状态字PSW&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CPU根据中断码查中断向量表，获得与该中断相关的处理程序的入口地址，并将PC设置成该地址，新的指令周期开始时，CPU控制转移到中断处理程序&lt;/li&gt;
&lt;li&gt;中断处理程序开始工作
&lt;ul&gt;
&lt;li&gt;在系统栈中保存现场信息&lt;/li&gt;
&lt;li&gt;检查I/O设备的状态信息，操纵I/O设备或者在设备和内存之间传送数据等等&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;中断处理结束时，CPU检测到中断返回指令，从系统栈中恢复被中断程序的上下文环境，CPU状态恢复成原来的状态，PSW和PC恢复成中断前的值，CPU开始一个新的指令周期&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;4-ia32-体系结构对中断的支持&quot;&gt;4. IA32 体系结构对中断的支持&lt;a class=&quot;anchor&quot; href=&quot;#4-ia32-体系结构对中断的支持&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;41-基本概念x86处理器&quot;&gt;4.1 基本概念——X86处理器&lt;a class=&quot;anchor&quot; href=&quot;#41-基本概念x86处理器&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;中断：由硬件信号引发的，分为可屏蔽和不可屏蔽中断&lt;/p&gt;
&lt;p&gt;异常：由指令执行引发的，比如除零异常&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;80x86处理器发布了大约20种不同的异常&lt;/li&gt;
&lt;li&gt;对于某些异常，CPU会在执行异常处理程序之前产生硬件出错码，并压入内核态堆栈&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;系统调用：异常的一种，用户态到系统态的唯一入口&lt;/p&gt;
&lt;h3 id=&quot;42-ia32体系结构对中断的支持&quot;&gt;4.2 IA32体系结构对中断的支持&lt;a class=&quot;anchor&quot; href=&quot;#42-ia32体系结构对中断的支持&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;中断控制器(PIC或APIC)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;负责将硬件的中断信号转换为中断向量，引发CPU中断&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实模式：中断向量表(Interrupt Vector)&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;保护模式：中断描述符表(Interrupt Descriptor table)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;采用门(gate)描述符数据结构描述中断向量&lt;/li&gt;
&lt;li&gt;表项包含四种类型门描述符：
&lt;ul&gt;
&lt;li&gt;任务门(Task Gate)&lt;/li&gt;
&lt;li&gt;中断门(Interrupt Gate)&lt;/li&gt;
&lt;li&gt;陷阱门(Trap Gate)&lt;/li&gt;
&lt;li&gt;调用门(Call Gate)&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;ul&gt;
&lt;li&gt;任务门(Task Gate)
&lt;ul&gt;
&lt;li&gt;中断发生时，必须取代当前进程的那个进程的TSS选择符存放在任务门中 (Linux没有使用任务门)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;中断门(Interrupt Gate)
&lt;ul&gt;
&lt;li&gt;给出段选择符 (Segment Selector)、中断/异常程序的段内偏移量 (Offset)&lt;/li&gt;
&lt;li&gt;通过中断门后系统会自动禁止中断&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;陷阱门(Trap Gate)
&lt;ul&gt;
&lt;li&gt;与中断门类似，但通过陷阱门后系统不会自动关中断&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;调用门(Call Gate)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;中断/异常的硬件处理过程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;确定与中断或异常关联的向量i&lt;/li&gt;
&lt;li&gt;通过IDTR寄存器找到IDT表，获得中断描述符 (表中的第i个表项)&lt;/li&gt;
&lt;li&gt;从GDTR寄存器获得GDT的地址，结合中断描述符中的段选择符，在GDT表获取对应的段描述符&lt;/li&gt;
&lt;li&gt;特权级检查&lt;/li&gt;
&lt;li&gt;检查是否发生了特权级的变化，如需要则进行堆栈切换&lt;/li&gt;
&lt;li&gt;硬件压栈，保存上下文环境&lt;/li&gt;
&lt;li&gt;如果是中断，清IF位&lt;/li&gt;
&lt;li&gt;通过中断描述符中的段内偏移量和段描述符中的基地址，找到中断/异常处理程序的入口地址，执行其第一条指令&lt;/li&gt;
&lt;/ol&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;5-系统调用system-call&quot;&gt;5. 系统调用(System Call)&lt;a class=&quot;anchor&quot; href=&quot;#5-系统调用system-call&quot;&gt;#&lt;/a&gt;&lt;/h2&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;h4 id=&quot;经典问题系统调用与c函数调用的区别&quot;&gt;经典问题：系统调用与C函数调用的区别？&lt;a class=&quot;anchor&quot; href=&quot;#经典问题系统调用与c函数调用的区别&quot;&gt;#&lt;/a&gt;&lt;/h4&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;执行环境：系统调用会导致CPU从用户态切换到内核态，执行内核中的代码。&lt;/li&gt;
&lt;li&gt;实现方式：通过特定的陷入指令 (如&lt;code&gt;int 0x80&lt;/code&gt;) 触发中断或异常，进入内核态执行相应的系统调用服务例程。&lt;/li&gt;
&lt;li&gt;开销：由于涉及用户态到内核态的切换，系统调用的开销较大。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;C函数调用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义：C函数调用是程序内部的函数调用，用于实现特定的功能或算法。&lt;/li&gt;
&lt;li&gt;执行环境：C函数调用在用户态执行，不涉及特权操作。&lt;/li&gt;
&lt;li&gt;实现方式：通过函数调用指令 (如&lt;code&gt;call&lt;/code&gt;) 在程序内部跳转到函数的入口地址执行。&lt;/li&gt;
&lt;li&gt;开销：C函数调用的开销较小，因为不涉及用户态和内核态的切换。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;例如，&lt;code&gt;printf&lt;/code&gt;函数是一个C库函数，它最终会调用系统调用&lt;code&gt;write&lt;/code&gt;来将数据输出到终端。&lt;code&gt;printf&lt;/code&gt;函数本身在用户态执行，而&lt;code&gt;write&lt;/code&gt;系统调用会切换到内核态执行实际的输出操作。&lt;/p&gt;
&lt;h3 id=&quot;51-静态系统调用机制的设计&quot;&gt;5.1 静态：系统调用机制的设计&lt;a class=&quot;anchor&quot; href=&quot;#51-静态系统调用机制的设计&quot;&gt;#&lt;/a&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;li&gt;系统调用表：存放系统调用服务例程的入口地址&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;52-静态参数传递过程问题&quot;&gt;5.2 静态：参数传递过程问题&lt;a class=&quot;anchor&quot; href=&quot;#52-静态参数传递过程问题&quot;&gt;#&lt;/a&gt;&lt;/h3&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;p&gt;数据段部分：&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;section .data
output:
    ascii &amp;quot;Hello!\n&amp;quot;
output_end:
equ len, output_end - output&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;section .data&lt;/code&gt;：定义数据段，用于存放程序中的数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;output:&lt;/code&gt;：定义一个标签，表示数据的起始位置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ascii &amp;quot;Hello!\n&amp;quot;&lt;/code&gt;：定义一个ASCII字符串”Hello!”，后跟换行符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;output_end:&lt;/code&gt;：定义另一个标签，表示数据的结束位置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;equ len, output_end - output&lt;/code&gt;：定义一个常量&lt;code&gt;len&lt;/code&gt;，其值为&lt;code&gt;output_end&lt;/code&gt;和&lt;code&gt;output&lt;/code&gt;之间的字节数，即字符串的长度&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;代码段部分：&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;section .text
globl _start
_start:
    movl $4, %eax     #eax存放系统调用号
    movl $1, %ebx
    movl $output, %ecx
    movl $len, %edx
    int $0x80         #引发一次系统调用
end:
    movl $1, %eax     #1这个系统调用的作用？
    movl $0, %ebx
    int $0x80&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;section .text&lt;/code&gt;：定义代码段，用于存放程序的指令&lt;/li&gt;
&lt;li&gt;&lt;code&gt;globl _start&lt;/code&gt;：声明&lt;code&gt;_start&lt;/code&gt;标签为全局的，使链接器能够找到程序的入口点&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_start:&lt;/code&gt;：程序的入口点&lt;/li&gt;
&lt;li&gt;&lt;code&gt;movl $4, %eax&lt;/code&gt;：将4放入eax寄存器，4是Linux系统调用表中&lt;code&gt;write&lt;/code&gt;函数的调用号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;movl $1, %ebx&lt;/code&gt;：将1放入ebx寄存器，1代表标准输出文件描述符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;movl $output, %ecx&lt;/code&gt;：将output字符串的地址放入ecx寄存器，作为要输出的数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;movl $len, %edx&lt;/code&gt;：将len (字符串长度) 放入edx寄存器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int $0x80&lt;/code&gt;：触发中断0x80，执行系统调用，这里执行的是write(1, “Hello!\n”, 7)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;movl $1, %eax&lt;/code&gt;：将1放入eax寄存器，1是Linux系统调用表中&lt;code&gt;exit&lt;/code&gt;函数的调用号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;movl $0, %ebx&lt;/code&gt;：将0放入ebx寄存器，作为exit()的参数，表示程序正常退出 (返回值为0)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int $0x80&lt;/code&gt;：再次触发中断0x80，执行系统调用exit(0)，终止程序&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;53-动态系统调用的执行过程&quot;&gt;5.3 动态：系统调用的执行过程&lt;a class=&quot;anchor&quot; href=&quot;#53-动态系统调用的执行过程&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;当CPU执行到特殊的陷入指令时：&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;hr/&gt;
&lt;h2 id=&quot;6-linux系统调用实现&quot;&gt;6. Linux系统调用实现&lt;a class=&quot;anchor&quot; href=&quot;#6-linux系统调用实现&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;基于x86体系结构的Linux系统调用实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;陷入指令选择：&lt;code&gt;int $0x80&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;门描述符：系统初始化时对IDT表中的第128号门初始化&lt;/li&gt;
&lt;li&gt;门类型：15，陷阱门：陷阱门不会自动屏蔽中断，允许在处理系统调用时继续响应其他中断，提高系统的并发性和响应速度。&lt;/li&gt;
&lt;li&gt;DPL：3，与用户级别相同，允许用户进程使用该门描述符&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;61-系统调用号示例&quot;&gt;6.1 系统调用号示例&lt;a class=&quot;anchor&quot; href=&quot;#61-系统调用号示例&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;# define __NR_exit 1
# define __NR_fork 2
# define __NR_read 3
# define __NR_write 4
# define __NR_open 5
# define __NR_close 6
# define __NR_waitpid 7
# define __NR_creat 8
# define __NR_link 9
# define __NR_unlink 10
# define __NR_execve 11
# define __NR_chdir 12
# define __NR_time 13
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;62-系统执行-int-0x80-指令&quot;&gt;6.2 系统执行 &lt;code&gt;int $0x80&lt;/code&gt; 指令&lt;a class=&quot;anchor&quot; href=&quot;#62-系统执行-int-0x80-指令&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;特权级的改变：由于从用户态切换到内核态，CPU需要切换栈。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户栈切换到内核栈：CPU从任务状态段 (TSS) 中装入新的栈指针 (SS:ESP) ，指向内核栈。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保存用户态信息：用户栈的信息 (SS:ESP) 、EFLAGS、用户态CS、EIP寄存器的内容会被压栈，以便返回时使用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将EFLAGS压栈后，复位TF (陷阱标志) ，IF (中断标志) 位保持不变。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查找IDT：使用128在中断描述符表 (IDT) 中找到对应的门描述符，从中找出段选择符装入代码段寄存器CS。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代码段描述符中的基地址加上陷阱门描述符中的偏移量，定位到system_call的入口地址。&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;系统调用号和参数传递：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;系统调用号：通过EAX寄存器传递。&lt;/li&gt;
&lt;li&gt;系统调用参数：通过EBX、ECX、EDX、ESI、EDI寄存器传递。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;执行系统调用：根据系统调用号，查找系统调用表，找到对应的系统调用处理例程并执行。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;处理完成后，将结果放入EAX寄存器，并通过ret_from_sys_call例程返回用户态程序。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;63-linux系统调用执行流程&quot;&gt;6.3 Linux系统调用执行流程&lt;a class=&quot;anchor&quot; href=&quot;#63-linux系统调用执行流程&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;应用程序 → 封装例程 → 陷入处理 → 内核函数&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;用户态下调用C库的库函数，比如write()&lt;/li&gt;
&lt;li&gt;封装后的write()先做好参数传递工作，然后使用int 0x80指令产生一次异常&lt;/li&gt;
&lt;li&gt;CPU通过0x80号在IDT中找到对应的服务例程system_call()，并调用之&lt;/li&gt;
&lt;li&gt;system_call()将参数保存在内核栈；根据系统调用号索引系统调用表，找到系统调用程序入口，比如sys_write()&lt;/li&gt;
&lt;li&gt;sys_write()执行完后，经过ret_from_sys_call()例程返回用户程序&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;64-示例系统调用的参数传递&quot;&gt;6.4 示例：系统调用的参数传递&lt;a class=&quot;anchor&quot; href=&quot;#64-示例系统调用的参数传递&quot;&gt;#&lt;/a&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;eax用于保存系统调用号和系统调用返回值&lt;/li&gt;
&lt;li&gt;系统调用参数保存在ebx, ecx, edx, esi和edi中，参数个数不超过6个&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;进入内核态后，system_call再将这些参数保存在内核堆栈中。&lt;/p&gt;
&lt;p&gt;假如C库中封装的系统调用号3的函数原型如下：&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;movl 0x8(%esp), %ecx  # 将用户态堆栈中的para2放入ecx
movl 0x4(%esp), %ebx  # 将用户态堆栈中的para1放入ebx
movl $0x3, %eax       # 系统调用号保存在eax中
int $0x80             # 引发系统调用
movl %eax, errno      # 将结果存入全局变量errno中
movl $-1, %eax        # eax置为-1，表示出错&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;则调用时，参数传递如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;eax = 3&lt;/li&gt;
&lt;li&gt;ebx = para1&lt;/li&gt;
&lt;li&gt;ecx = para2&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;7-系统调用小结&quot;&gt;7. 系统调用小结&lt;a class=&quot;anchor&quot; href=&quot;#7-系统调用小结&quot;&gt;#&lt;/a&gt;&lt;/h2&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;li&gt;每个操作系统都提供几百种系统调用 (POSIX标准)&lt;/li&gt;
&lt;li&gt;系统调用与C函数调用的区别？&lt;/li&gt;
&lt;li&gt;完成系统调用机制的运行需要什么条件 (准备工作) ？
&lt;ul&gt;
&lt;li&gt;静态 和 动态&lt;/li&gt;
&lt;li&gt;封装内核函数 - 库函数 (API) ；访管指令与陷入机制；编译器；操作系统 (初始化、系统调用编号及参数；系统调用表)&lt;/li&gt;
&lt;li&gt;陷入内核，总入口程序，保存现场 (压栈) ，查表分派，执行返回&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>Operating Systems Notes 01: Overview of Operating Systems</title><link>https://www.lyt0112.com/blog/operating_systems_note_01-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/operating_systems_note_01-zh</guid><description>Operating Systems Notes 01: Overview of Operating Systems</description><pubDate>Tue, 11 Mar 2025 22:14:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;AIGC Declaration&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;AIGC Declaration&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;Model: &lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This article uses LLM to improve efficiency, which can make mistakes. I have tried my best to check and proofread, but still cannot guarantee complete accuracy.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;1-操作系统是什么&quot;&gt;1. 操作系统是什么？&lt;a class=&quot;anchor&quot; href=&quot;#1-操作系统是什么&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;11-操作系统的定义&quot;&gt;1.1 操作系统的定义&lt;a class=&quot;anchor&quot; href=&quot;#11-操作系统的定义&quot;&gt;#&lt;/a&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 id=&quot;12-操作系统的作用&quot;&gt;1.2 操作系统的作用&lt;a class=&quot;anchor&quot; href=&quot;#12-操作系统的作用&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;虚拟机：将物理资源(处理器、内存、磁盘)转换成更通用、更强大、更易用的虚拟形式&lt;/li&gt;
&lt;li&gt;APIs：提供可供用户调用的接口，提供应用程序的标准库&lt;/li&gt;
&lt;li&gt;资源管理器：允许多程序运行(共享CPU)，允许多程序并发访问内存，允许多程序访问设备&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;13-设计与实现目标&quot;&gt;1.3 设计与实现目标&lt;a class=&quot;anchor&quot; href=&quot;#13-设计与实现目标&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;抽象
&lt;ul&gt;
&lt;li&gt;模块化&lt;/li&gt;
&lt;li&gt;使用高级语言(C)而非汇编&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;/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;hr/&gt;
&lt;h2 id=&quot;2-从不同角度认知操作系统&quot;&gt;2. 从不同角度认知操作系统&lt;a class=&quot;anchor&quot; href=&quot;#2-从不同角度认知操作系统&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;21-资源管理的观点&quot;&gt;2.1 资源管理的观点&lt;a class=&quot;anchor&quot; href=&quot;#21-资源管理的观点&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;自底向上 操作系统是资源的管理者&lt;/li&gt;
&lt;li&gt;硬件资源：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;/ul&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;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;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;/ul&gt;
&lt;h3 id=&quot;22-进程的观点&quot;&gt;2.2 进程的观点&lt;a class=&quot;anchor&quot; href=&quot;#22-进程的观点&quot;&gt;#&lt;/a&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 id=&quot;23-虚机器观点&quot;&gt;2.3 虚机器观点&lt;a class=&quot;anchor&quot; href=&quot;#23-虚机器观点&quot;&gt;#&lt;/a&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;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;3-操作系统的特征&quot;&gt;3. 操作系统的特征&lt;a class=&quot;anchor&quot; href=&quot;#3-操作系统的特征&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-并发concurrency&quot;&gt;3.1 并发(Concurrency)&lt;a class=&quot;anchor&quot; href=&quot;#31-并发concurrency&quot;&gt;#&lt;/a&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;微观上：单CPU情况下，任何时刻只有一个程序在执行，即这些程序在CPU上轮流执行&lt;/li&gt;
&lt;li&gt;由并发引起的问题：活动切换、保护、相互依赖的活动间的同步&lt;/li&gt;
&lt;li&gt;并行(parallel)与”并发”的区别&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-共享sharing&quot;&gt;3.2 共享(Sharing)&lt;a class=&quot;anchor&quot; href=&quot;#32-共享sharing&quot;&gt;#&lt;/a&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;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;h3 id=&quot;33-虚拟virtual&quot;&gt;3.3 虚拟(Virtual)&lt;a class=&quot;anchor&quot; href=&quot;#33-虚拟virtual&quot;&gt;#&lt;/a&gt;&lt;/h3&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 id=&quot;34-随机性不确定性&quot;&gt;3.4 随机性(不确定性)&lt;a class=&quot;anchor&quot; href=&quot;#34-随机性不确定性&quot;&gt;#&lt;/a&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;hr/&gt;
&lt;h2 id=&quot;4-操作系统发展历史&quot;&gt;4. 操作系统发展历史&lt;a class=&quot;anchor&quot; href=&quot;#4-操作系统发展历史&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;技术变化与概念重用&quot;&gt;技术变化与概念重用&lt;a class=&quot;anchor&quot; href=&quot;#技术变化与概念重用&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;技术变化导致某些思想过时并迅速消失，但技术的另一种变化还可能使它们复活。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;磁盘上文件分配—连续文件(CD-ROM文件系统)&lt;/li&gt;
&lt;li&gt;硬件保护&lt;/li&gt;
&lt;li&gt;动态链接(MULTICS首先提出)&lt;/li&gt;
&lt;li&gt;计算服务(MULTICS，以大量的、附有相对简单用户机器的、集中式Internet服务器形式回归) → 云计算&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;41-操作系统发展阶段&quot;&gt;4.1 操作系统发展阶段&lt;a class=&quot;anchor&quot; href=&quot;#41-操作系统发展阶段&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;第1阶段1948-1970硬件昂贵人工便宜&quot;&gt;第1阶段(1948-1970)：硬件昂贵，人工便宜&lt;a class=&quot;anchor&quot; href=&quot;#第1阶段1948-1970硬件昂贵人工便宜&quot;&gt;#&lt;/a&gt;&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;SPOOLing技术：批处理作业处理流程&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;第2阶段1970-1985硬件便宜人工昂贵&quot;&gt;第2阶段(1970-1985)：硬件便宜，人工昂贵&lt;a class=&quot;anchor&quot; href=&quot;#第2阶段1970-1985硬件便宜人工昂贵&quot;&gt;#&lt;/a&gt;&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;第一个分时操作系统CTSS：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1959年在MIT提出分时系统思想&lt;/li&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;OS/360：IBM发布时带着已知的1000个错误&lt;/li&gt;
&lt;li&gt;Multics：1963年开始，1969年才发布&lt;/li&gt;
&lt;li&gt;UNIX：一群计算机迷在贝尔实验室开发，初衷是在DEC PDP-7小型计算机上玩星际探险游戏&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;第3阶段1981-硬件便宜人工昂贵&quot;&gt;第3阶段(1981-)：硬件便宜，人工昂贵&lt;a class=&quot;anchor&quot; href=&quot;#第3阶段1981-硬件便宜人工昂贵&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;个人计算时代&lt;/li&gt;
&lt;li&gt;开始PC硬件资源有限，一次运行一个程序，OS是一个例程库，回归简单&lt;/li&gt;
&lt;li&gt;逐渐PC资源丰富，OS又成为一个庞然大物(大型OS)，存储保护、多道程序设计再次出现&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;第4阶段1981-分布式&quot;&gt;第4阶段(1981-)：分布式&lt;a class=&quot;anchor&quot; href=&quot;#第4阶段1981-分布式&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;网络：允许不同机器很容易地相互共享资源(打印机、文件服务器、Web服务器)&lt;/li&gt;
&lt;li&gt;解决问题：共享，安全&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;第5阶段1995-移动计算时代&quot;&gt;第5阶段(1995-)：移动计算时代&lt;a class=&quot;anchor&quot; href=&quot;#第5阶段1995-移动计算时代&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;各种移动终端的出现(笔记本、平板、手机、机顶盒、可穿戴设备等)&lt;/li&gt;
&lt;li&gt;特点：小型、移动、便宜，但能力有限&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;第6阶段2006-云计算时代&quot;&gt;第6阶段(2006-)：云计算时代&lt;a class=&quot;anchor&quot; href=&quot;#第6阶段2006-云计算时代&quot;&gt;#&lt;/a&gt;&lt;/h4&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;li&gt;管理海量的计算任务以及资源调配&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;第7阶段200-泛在计算普适计算物联网&quot;&gt;第7阶段(200?-)：泛在计算/普适计算/物联网&lt;a class=&quot;anchor&quot; href=&quot;#第7阶段200-泛在计算普适计算物联网&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;许多联网设备为许多人提供个性化的服务&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;5-操作系统分类&quot;&gt;5. 操作系统分类&lt;a class=&quot;anchor&quot; href=&quot;#5-操作系统分类&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;51-传统分类&quot;&gt;5.1 传统分类&lt;a class=&quot;anchor&quot; href=&quot;#51-传统分类&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;1-批处理操作系统&quot;&gt;1. 批处理操作系统&lt;a class=&quot;anchor&quot; href=&quot;#1-批处理操作系统&quot;&gt;#&lt;/a&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;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;/li&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;单道批处理系统(simple batch processing, uni-programming)&lt;/li&gt;
&lt;li&gt;多道批处理系统(multiprogramming system)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SPOOLing系统(技术)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1961年，英国曼彻斯特大学，Atalas机&lt;/li&gt;
&lt;li&gt;Simultaneous Peripheral Operation On-Line(同时的外围设备联机操作)—假脱机技术&lt;/li&gt;
&lt;li&gt;思想：利用磁盘作缓冲，将输入、计算、输出分别组织成独立的任务流，使I/O和计算真正并行&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;提高I/O速度&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;h4 id=&quot;2-分时操作系统time-sharing-system&quot;&gt;2. 分时操作系统(Time-Sharing System)&lt;a class=&quot;anchor&quot; href=&quot;#2-分时操作系统time-sharing-system&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;时间片(time slice)：操作系统将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;h4 id=&quot;3-实时操作系统&quot;&gt;3. 实时操作系统&lt;a class=&quot;anchor&quot; href=&quot;#3-实时操作系统&quot;&gt;#&lt;/a&gt;&lt;/h4&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;/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;h4 id=&quot;4-个人计算机操作系统&quot;&gt;4. 个人计算机操作系统&lt;a class=&quot;anchor&quot; href=&quot;#4-个人计算机操作系统&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;计算机在某一时间内为单用户服务&lt;/li&gt;
&lt;li&gt;追求目标：界面友好，使用方便，丰富的应用软件&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;5-网络操作系统&quot;&gt;5. 网络操作系统&lt;a class=&quot;anchor&quot; href=&quot;#5-网络操作系统&quot;&gt;#&lt;/a&gt;&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;h4 id=&quot;6-分布式操作系统&quot;&gt;6. 分布式操作系统&lt;a class=&quot;anchor&quot; href=&quot;#6-分布式操作系统&quot;&gt;#&lt;/a&gt;&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;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;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;7-嵌入式操作系统&quot;&gt;7. 嵌入式操作系统&lt;a class=&quot;anchor&quot; href=&quot;#7-嵌入式操作系统&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;嵌入式系统：在各种设备、装置或系统中，完成特定功能的软硬件系统&lt;/li&gt;
&lt;li&gt;嵌入式操作系统：运行在嵌入式系统环境中，对整个嵌入式系统及其所操作、控制的各种部件装置等资源进行统一协调、调度、指挥和控制的系统软件&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;52-tanenbaum分类&quot;&gt;5.2 Tanenbaum分类&lt;a class=&quot;anchor&quot; href=&quot;#52-tanenbaum分类&quot;&gt;#&lt;/a&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;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 id=&quot;智能卡操作系统&quot;&gt;智能卡操作系统&lt;a class=&quot;anchor&quot; href=&quot;#智能卡操作系统&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;智能卡：一种包含有一块CPU芯片的信用卡&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;有些智能卡是面向Java的，ROM中有Java虚拟机解释器&lt;/li&gt;
&lt;li&gt;Java小程序被下载到卡中并由JVM解释器解释&lt;/li&gt;
&lt;li&gt;有些卡可以同时处理多个Java小程序，需要多道程序调度&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;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;6-重点小结&quot;&gt;6. 重点小结&lt;a class=&quot;anchor&quot; href=&quot;#6-重点小结&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;61-典型的国产操作系统&quot;&gt;6.1 典型的国产操作系统&lt;a class=&quot;anchor&quot; href=&quot;#61-典型的国产操作系统&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;麒麟、鸿蒙、Openeuler、统信等&lt;/p&gt;
&lt;h3 id=&quot;62-核心概念&quot;&gt;6.2 核心概念&lt;a class=&quot;anchor&quot; href=&quot;#62-核心概念&quot;&gt;#&lt;/a&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;ul&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;操作系统的分类&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;63-重要历史操作系统&quot;&gt;6.3 重要历史操作系统&lt;a class=&quot;anchor&quot; href=&quot;#63-重要历史操作系统&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;OS/360&lt;/li&gt;
&lt;li&gt;MULTICS&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;64-操作系统特征&quot;&gt;6.4 操作系统特征&lt;a class=&quot;anchor&quot; href=&quot;#64-操作系统特征&quot;&gt;#&lt;/a&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;/ul&gt;
&lt;hr/&gt;
&lt;h2 id=&quot;7-常见问题解答&quot;&gt;7. 常见问题解答&lt;a class=&quot;anchor&quot; href=&quot;#7-常见问题解答&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;71-buffer-cache的作用和工作原理&quot;&gt;7.1 Buffer Cache的作用和工作原理&lt;a class=&quot;anchor&quot; href=&quot;#71-buffer-cache的作用和工作原理&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在UNIX操作系统中，文件子系统与块设备之间的Buffer Cache：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;作用：
&lt;ul&gt;
&lt;li&gt;减少对物理设备的访问次数，提高I/O性能&lt;/li&gt;
&lt;li&gt;协调CPU与I/O设备之间的速度差异&lt;/li&gt;
&lt;li&gt;支持数据的读写缓冲，实现数据共享&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;工作原理：
&lt;ul&gt;
&lt;li&gt;当进程请求读取数据时，系统首先检查Buffer Cache中是否有所需数据&lt;/li&gt;
&lt;li&gt;如果有 (命中) ，直接从Buffer Cache返回数据&lt;/li&gt;
&lt;li&gt;如果没有 (未命中) ，从物理设备读取数据到Buffer Cache，再返回给进程&lt;/li&gt;
&lt;li&gt;写操作时，数据先写入Buffer Cache，再由系统决定何时写回物理设备&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;72-unix的名字来历-猜测&quot;&gt;7.2 UNIX的名字来历 (猜测)&lt;a class=&quot;anchor&quot; href=&quot;#72-unix的名字来历-猜测&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;UNIX名字可能来源于MULTICS (Multiplexed Information and Computing Service) 的简化和双关语：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MULTICS是一个复杂的分时系统&lt;/li&gt;
&lt;li&gt;UNIX (Uniplexed Information and Computing Service) 表示简化版的MULTICS&lt;/li&gt;
&lt;li&gt;也有说法是”UNIX”是”eUNuchs” (阉割版的MULTICS) 的变体&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;73-批处理操作系统的区别&quot;&gt;7.3 批处理操作系统的区别&lt;a class=&quot;anchor&quot; href=&quot;#73-批处理操作系统的区别&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;单道批处理：一次只能执行一个作业，作业按顺序依次执行，前一个作业完成后才能执行下一个&lt;/li&gt;
&lt;li&gt;多道批处理：内存中同时存放多个作业，CPU在这些作业之间切换执行，提高了CPU利用率和系统吞吐量&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;74-spooling技术的现状&quot;&gt;7.4 SPOOLing技术的现状&lt;a class=&quot;anchor&quot; href=&quot;#74-spooling技术的现状&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;SPOOLing技术并未过时，在现代计算机系统中仍有广泛应用：&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 id=&quot;75-传统分时系统在今天的意义&quot;&gt;7.5 传统分时系统在今天的意义&lt;a class=&quot;anchor&quot; href=&quot;#75-传统分时系统在今天的意义&quot;&gt;#&lt;/a&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;/ul&gt;
&lt;h3 id=&quot;76-操作系统的主要作用和典型特征&quot;&gt;7.6 操作系统的主要作用和典型特征&lt;a class=&quot;anchor&quot; href=&quot;#76-操作系统的主要作用和典型特征&quot;&gt;#&lt;/a&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;li&gt;持久性：操作系统常驻内存&lt;/li&gt;
&lt;li&gt;特权性：拥有对硬件的直接控制权&lt;/li&gt;
&lt;li&gt;复杂性：结构复杂，功能丰富&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/operating_systems.Dib9zy_r.jpeg"/></item><item><title>How to Make a Beautiful Crystal at Home</title><link>https://www.lyt0112.com/blog/crystal-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/crystal-zh</guid><description>其实是魅力化学的课程论文</description><pubDate>Tue, 28 Jan 2025 15:07:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;What is this?&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;What is this?&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;本文为博主 2022 年秋季学期的 &lt;a href=&quot;https://www.lyt0112.com/blog/course_review-zh#%E9%AD%85%E5%8A%9B%E5%8C%96%E5%AD%A6&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;魅力化学课程&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 论文, 感觉很有纪念意义故发布.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;一前言&quot;&gt;一、前言&lt;a class=&quot;anchor&quot; href=&quot;#一前言&quot;&gt;#&lt;/a&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;img src=&quot;https://cdn.lyt0112.com/2025/01/d6cabdaa77a79da3c5d75099e0297ca1.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 十二水合硫酸铝钾包裹的硫酸铬钾 &lt;/p&gt;
&lt;p&gt;最后由于时间久远，本文中的图片没有完全使用我自己拍摄的图片，使用了一些网络图片作为补充（将标明）。&lt;/p&gt;
&lt;h2 id=&quot;二一般的制作流程&quot;&gt;二、一般的制作流程&lt;a class=&quot;anchor&quot; href=&quot;#二一般的制作流程&quot;&gt;#&lt;/a&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;h3 id=&quot;一晶体类型选择&quot;&gt;（一）晶体类型选择&lt;a class=&quot;anchor&quot; href=&quot;#一晶体类型选择&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;第一次制备晶体时的最佳选择是五水合硫酸铜（胆矾）。硫酸铜晶体析出的稳定性极强，制备难度极低。 硫酸铜溶解度高， 溶液稍有杂质或者过饱和也能长出明显的平行四边形晶体， 颜色亮蓝色鲜艳好看， 且毒性低。 同时它的晶体容易悬吊，适合使用悬吊法制做出极大的单晶。&lt;/p&gt;
&lt;p&gt;其次就是十二水合硫酸铝钾（明矾）和十二水合硫酸铬钾（铬矾），因为它们是类质同晶的， 也就是可以做它们的不同配比的混晶， 做出来的晶体为正六面体或者正八面体，形状规则好看。&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/8c23534fcee7014cd3cc9d3572c74d8f.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 体积较大的分层明矾铬钾 &lt;/p&gt;
&lt;p&gt;稍有难度的是氯化钠的晶体，虽然常用的食盐中绝大部分都是它，但是想要做一个大且透明的单晶是难上加难， 制作的时候一定不要使用食盐， 因为杂质与添加剂太多， 不会形成单晶。 氯化钠晶体的制作难度首先是它的溶解度较低， 想要冷却热饱和溶液制备晶种几乎不可能， 其次是容易产生碎晶， 也很难做出晶莹剔透的晶体。&lt;/p&gt;
&lt;p&gt;一般在制备的时候都会添加一些甘氨酸&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; （8g/L） 作为添加剂以提高氯化钠晶体的透明度， 如果想要进一步提高晶体的透明度可以加入少量的硝酸铅&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;（0.1g/L）。&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/c7855e97b98aeaa232583d7592fcd115.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 氯化钠单晶 &lt;/p&gt;
&lt;h3 id=&quot;二配置母液&quot;&gt;（二）配置母液&lt;a class=&quot;anchor&quot; href=&quot;#二配置母液&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;首先应该配置饱和溶液。一般采用母液法，虽然理论上可以通过查询溶解度表， 再计算溶质与结晶水的质量来使溶液恰好饱和， 但是考虑到晶体制作的流水线连续性、 溶液复用性、 室温的波动以及溶解度表的不准确性， 使用一个烧杯存放母液作为缓冲往往可以得到更好的效果。&lt;/p&gt;
&lt;p&gt;首先需要准备一个容量较大的烧杯作为母液烧杯， 根据溶解度加入大致质量的溶质并等待一段时间， 如果烧杯底部仍有未溶解的溶质 （因为溶解结晶是动态平衡，因此多余的试剂往往会变成晶体”大饼”），就说明这杯溶液饱和了，我们将其作为母液，此时母液的上层清液便是饱和溶液。&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/d7fd8b270d269289125abfaa61568efb.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 烧杯底部的晶体”大饼”&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; &lt;/p&gt;
&lt;h3 id=&quot;三析晶方法&quot;&gt;（三）析晶方法&lt;a class=&quot;anchor&quot; href=&quot;#三析晶方法&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;现在我们有了饱和溶液， 那么具体如何操作才能使晶体从水中析出呢？一般有两种方法，分别是冷却热饱和溶液法和常温蒸发结晶法。&lt;/p&gt;
&lt;p&gt;冷却热饱和溶液法适用于溶解度对温度变化敏感的溶质，比如硫酸铜。具体操作是对每 100 克水加入比当前温度溶解度多 20 克左右的溶质（加多了容易析出大饼） ， 搅拌加热至完全溶解， 将溶液立刻倒入结晶皿中并等待结晶，此时要注意在结晶皿上盖一张滤纸以防止灰尘落入结晶皿， 灰尘可能会造成溶液迅速析出大量碎晶；待温度下降溶液结晶后用镊子夹出品相较好的小晶体作为晶核/晶种， 注意一旦镊子进入溶液也会造成溶液迅速析出大量碎晶。 在拿出心仪的小晶种之后，将剩余液体与碎晶倒回母液烧杯。&lt;/p&gt;
&lt;p&gt;冷却热饱和溶液法由于溶解度下降的速率较快，往往用来制作大量小晶种，也就是以量取胜，难以制作单个的形状规则的大晶体。&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/890d97267732323be2f9afe31a84d51d.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 冷却热硫酸铜溶液析晶&lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;/p&gt;
&lt;p&gt;常温蒸发结晶法适用于任何种类的溶质，只要取母液的上层清液，倒入结晶皿，放入小晶核（不放其实也可以），静置析晶即可。&lt;/p&gt;
&lt;p&gt;常温蒸发结晶法除了可以将晶种放在结晶皿底部，也可以用透明细鱼线（或许头发也可以） 将晶种悬挂在饱和溶液中， 这个方法的好处在于晶体底部不会被结晶皿底部限制而产生不自然的凹面，但是坏处是鱼线会留在晶体内部。&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/75df6136c6f68b5159a8689872cf09c6.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 栓绳的胆矾晶体与悬吊法生长的明矾 &lt;/p&gt;
&lt;p&gt;最后在从溶液中拿出晶体后，一定要注意立刻将晶体表面的溶液擦干，因为表面的溶液由于于空气接触面积极大， 会迅速风干， 在晶体表面留下许多细小的杂晶，导致一段时间过后晶体表面变得粗糙。&lt;/p&gt;
&lt;h3 id=&quot;四保存方法&quot;&gt;（四）保存方法&lt;a class=&quot;anchor&quot; href=&quot;#四保存方法&quot;&gt;#&lt;/a&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;/ol&gt;
&lt;p&gt;拍照保存实在是保存时间最久的方法， 下图中的硫酸亚铁早已因为保存不当而丢失结晶水风化为白色粉末了，但是它的遗照仍然存放在我的电脑中。&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/26fc8b6a04e2eb6d220a9da862b7eaec.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 硫酸亚铁（N 年之前） &lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;使用矿物标本盒&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;顾名思义，这个方法往往用来展示固体标本而不是长时间存放，如果是易风化（含有结晶水的任何物质）、光解（三草酸合铁酸钾）的晶体不要使用这样的方法展示。&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/d27a36d11034061e70089878eaaef386.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 矿标盒里的铝钾铬钾混晶&lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; &lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;使用密封袋&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;好处在于价格亲民，保存非常方便，对于普通的晶体如氯化钠，可以做到长期的保存。但如果稍微有一点难度，如会风化（硫酸铜）、吸湿（氢氧化钠）、光解（三草酸合铁酸钾）、热分解（高锰酸钾）、氧化（硫酸亚铁），只用密封袋保存的话，它们会迅速变质，因此往往将晶体用清漆包裹一层再放入密封袋。&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;西林瓶+液体石蜡（石蜡油）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;能够较为长久地保存晶体，但是坏处在于石蜡油极其难以清除，如果需要使用晶体或者用其他方法保存晶体，表面的石蜡油就很难清理干净了。&lt;/p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;环氧树脂&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个方法可以非常长久的保存晶体 （除了光解、 热分解的晶体） ， 方便把玩，不怕摔碎， 实在是保存晶体的上佳方式。 但是不建议与还原性物质一起使用， 可能会造成晶体的氧化变质，比如硫酸亚铁可能会氧化发黑&lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2025/01/92a3009ee49ac56b9981b95153e145c6.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; 环氧树脂里的铁氰化钾&lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt; &lt;/p&gt;
&lt;h2 id=&quot;三-实验安全&quot;&gt;三、 实验安全&lt;a class=&quot;anchor&quot; href=&quot;#三-实验安全&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;一选材安全&quot;&gt;（一）选材安全&lt;a class=&quot;anchor&quot; href=&quot;#一选材安全&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;应当遵守法律法规，遵守《危险化学品安全管理条例》、《中华人民共和国治安管理处罚法》、 《中华人民共和国消防法》，不使用有毒有机溶剂结晶、不使用有毒性、致癌的药品，如重铬酸钾。&lt;/p&gt;
&lt;h3 id=&quot;二过程安全&quot;&gt;（二）过程安全&lt;a class=&quot;anchor&quot; href=&quot;#二过程安全&quot;&gt;#&lt;/a&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;h2 id=&quot;四-环保要求&quot;&gt;四、 环保要求&lt;a class=&quot;anchor&quot; href=&quot;#四-环保要求&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;尤其需要注意的是废水排放需要遵循《污水综合排放标准》，排入下水道应遵循《污水排入城市下水道水质标准》&lt;sup&gt;&lt;a href=&quot;#user-content-fn-7&quot; id=&quot;user-content-fnref-7&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;，以铜为例，排入下水道的废液中总铜含量应小于 2mg/L。&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;li&gt;碱沉淀铜离子&lt;/li&gt;
&lt;li&gt;活泼金属置换铜&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;五-总结&quot;&gt;五、 总结&lt;a class=&quot;anchor&quot; href=&quot;#五-总结&quot;&gt;#&lt;/a&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;hr/&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor&quot; href=&quot;#footnote-label&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;翁贤芬. 大颗粒氯化钠的制备研究[J]. 盐业与化工, 2009. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;双色大饼，你值得拥有[EB/OL]. 百度贴吧. &lt;a href=&quot;https://tieba.baidu.com/p/7821134717&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tieba.baidu.com/p/7821134717&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-2-2&quot; data-footnote-backref aria-label=&quot;Back to reference 2-2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt;从零开始的晶体教程（3）[EB/OL]. 科创网. &lt;a href=&quot;https://www.kechuang.org/t/88078&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.kechuang.org/t/88078&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;百度贴吧[EB/OL]. &lt;a href=&quot;https://tieba.baidu.com/p/7714718573&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tieba.baidu.com/p/7714718573&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;从零开始的晶体教程（3.5）[EB/OL]. 科创网. &lt;a href=&quot;https://www.kechuang.org/t/88081&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://www.kechuang.org/t/88081&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;百度贴吧[EB/OL]. &lt;a href=&quot;https://tieba.baidu.com/p/7714718573&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tieba.baidu.com/p/7714718573&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-6&quot; data-footnote-backref aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-7&quot;&gt;
&lt;p&gt;GB/T 31962-2015, 污水排入城镇下水道水质标准[S]. &lt;a href=&quot;http://www.wxbh.gov.cn/doc/2022/09/01/3743836.shtml&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;http://www.wxbh.gov.cn/doc/2022/09/01/3743836.shtml&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; &lt;a href=&quot;#user-content-fnref-7&quot; data-footnote-backref aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/crystal.qzggYUtQ.png"/></item><item><title>Compiler Principles Lab Notes</title><link>https://www.lyt0112.com/blog/compiler_principles_lab_note-zh</link><guid isPermaLink="true">https://www.lyt0112.com/blog/compiler_principles_lab_note-zh</guid><description>Implement a compiler that compiles SysY language to Koopa IR, finally to RISC-V assembly</description><pubDate>Sun, 12 Jan 2025 02:15:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;related-links&quot;&gt;Related Links&lt;a class=&quot;anchor&quot; href=&quot;#related-links&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Compiler_Principles-2024Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;编译原理资料仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Compiler_Principles-2024Fall-PKU&quot;&gt;EmptyBlueBox/Compiler_Principles-2024Fall-PKU&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;本文档中所有代码均在 &lt;a href=&quot;https://github.com/EmptyBlueBox/Compiler_Principles_Lab-2024Fall-PKU&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;我的编译原理 Lab 仓库&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 中开源, 欢迎大家参考.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/EmptyBlueBox/Compiler_Principles_Lab-2024Fall-PKU&quot;&gt;EmptyBlueBox/Compiler_Principles_Lab-2024Fall-PKU&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://pku-minic.github.io/online-doc/#/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;实验文档&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://course.educg.net/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;实验评测平台&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://gitlab.eduxiji.net/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;实验代码上传平台: GitLab&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://arthals.ink/tags/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Arthals 的编译原理资料&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;aside aria-label=&quot;Why Notes&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Why Notes&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&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;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;以下是一些小建议~&lt;/p&gt;
&lt;aside aria-label=&quot;General Best Practice&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-green-500 bg-green-50 dark:bg-green-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-green-700 dark:text-green-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;General Best Practice&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;ul&gt;
&lt;li&gt;一个较容易的完成 Lab 的方法是在写每一个 Lab &lt;code&gt;x&lt;/code&gt; 的时候, 将我的 (或者别人的) Lab &lt;code&gt;x-1&lt;/code&gt; 和 Lab &lt;code&gt;x&lt;/code&gt; 的代码进行对比, 比如 &lt;code&gt;VSCode&lt;/code&gt; 的 &lt;code&gt;compare selected&lt;/code&gt; 功能, 然后在自己的 Lab &lt;code&gt;x-1&lt;/code&gt; 的基础上写 Lab &lt;code&gt;x&lt;/code&gt; 的代码 (而不是抄袭别人的 Lab &lt;code&gt;x&lt;/code&gt; 的代码) , 这样能保证考虑到所有的逻辑, 大幅减少 debug 的时间, 还能锻炼自己阅读代码的能力 🌚.&lt;/li&gt;
&lt;li&gt;每一个 Level 的测试点在你实现正确的情况下都是可以全部通过的, 如果你没有通过说明你的逻辑存在问题, 不存在需要后面的特性才能解决前面测试点的情况.&lt;/li&gt;
&lt;li&gt;强烈建议大家在大四没有绩点压力的时候选这门课!
&lt;ul&gt;
&lt;li&gt;这样最后可以放掉 Lv9 不完成, 因为 Lv9 非常浪费时间并且占所有 Lab 的 &lt;code&gt;27%&lt;/code&gt; 的分数, 也就是总评的 8 分, 如果你在绩点压力下完成所有 Lab 会浪费很多时间.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;lv-0&quot;&gt;Lv 0&lt;a class=&quot;anchor&quot; href=&quot;#lv-0&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;prepare-environment&quot;&gt;Prepare Environment&lt;a class=&quot;anchor&quot; href=&quot;#prepare-environment&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;首先安装并运行 &lt;a href=&quot;https://www.docker.com/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Docker&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;拉取镜像:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;docker pull maxxing/compiler-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;使用镜像创建容器:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;docker run -it --name compiler -v &amp;lt;compiler_lab_path&amp;gt;:/root/compiler maxxing/compiler-dev bash&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中参数的含义:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-it&lt;/code&gt; 表示以交互模式运行容器.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name compiler&lt;/code&gt; 表示将容器重命名为 &lt;code&gt;compiler&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v &amp;lt;compiler_lab_path&amp;gt;:/root/compiler&lt;/code&gt; 表示将宿主机的编译 Lab 项目文件夹挂载到容器中.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxxing/compiler-dev&lt;/code&gt; 表示使用 &lt;code&gt;maxxing&lt;/code&gt; 提供的 &lt;code&gt;compiler-dev&lt;/code&gt; 镜像.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bash&lt;/code&gt; 表示以 &lt;code&gt;bash&lt;/code&gt; 为启动命令.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;博主的编译项目结构类似这样, 其中 &lt;code&gt;~/Documents/xxx/Lv7&lt;/code&gt; 就是你的宿主机编译 Lab 项目文件夹:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;Mac OS 1926-08-17 12:00:00
emptyblue ~/Documents/xxx/Lv7
❯ tree 

.
├── CMakeLists.txt
└── src
    ├── include
    │   ├── koopa.h
    │   ├── koopa.hpp
    │   ├── koopa_util.hpp
    │   ├── riscv.hpp
    │   └── riscv_util.hpp
    ├── koopa.cpp
    ├── koopa_util.cpp
    ├── main.cpp
    ├── riscv.cpp
    ├── riscv_util.cpp
    ├── sysy.l
    └── sysy.y

3 directories, 13 files&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;LLDB Issue&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-yellow-500 bg-yellow-50 dark:bg-yellow-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-yellow-700 dark:text-yellow-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;LLDB Issue&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;如果非 MacOS, 可以加入 &lt;code&gt;--cap-add=SYS_PTRACE --security-opt seccomp=unconfined&lt;/code&gt; 表示添加 &lt;code&gt;SYS_PTRACE&lt;/code&gt; 权限, 取消 seccomp 限制, 以方便使用 &lt;code&gt;LLDB&lt;/code&gt; 在 Docker 中调试.&lt;/p&gt;&lt;p&gt;但是我没有找到方法在 MacOS 上的 Docker 中运行 &lt;code&gt;LLDB&lt;/code&gt;, 如果你找到了可以解决这个问题的办法, 可以在下面写一个评论!&lt;/p&gt;&lt;p&gt;2024-12-23 Update:&lt;/p&gt;&lt;p&gt;似乎助教的实验文档提及了如何调试: &lt;a href=&quot;https://pku-minic.github.io/online-doc/#/misc-app-ref/environment?id=%E8%B0%83%E8%AF%95-risc-v-%E7%A8%8B%E5%BA%8F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;调试 risc-v 程序&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;每次需要进入容器时, 先启动容器, 再进入:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;docker start compiler
docker exec -it compiler bash&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;退出容器 (Control + D):&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;exit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;查看所有容器:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;停止所有容器:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;docker stop $(docker ps -aq)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;删除 &lt;code&gt;compiler&lt;/code&gt; 容器:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;docker rm -f compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;删除所有容器:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;docker rm $(docker ps -aq)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;compile-and-test&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;请仔细阅读 &lt;a href=&quot;https://pku-minic.github.io/online-doc/#/misc-app-ref/environment&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;实验环境使用说明&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;cmake&lt;/code&gt; 生成 Makefile 文件, 指定编译类型为 &lt;code&gt;Debug&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;cmake -DCMAKE_BUILD_TYPE=Debug -B build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;使用 &lt;code&gt;cmake&lt;/code&gt; 生成 Makefile 文件, 不指定编译类型:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;cmake -B build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;使用 &lt;code&gt;cmake&lt;/code&gt; 编译:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;cmake --build build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv&amp;lt;lv_number&amp;gt; /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;riscv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv&amp;lt;lv_number&amp;gt; /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果本地测试不通过, 可以把 &lt;code&gt;/opt/bin/testcases&lt;/code&gt; 中的测试用例复制到当前路径进行查看调试.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;cp -r /opt/bin/testcases .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果想测试自己的测试用例, 可以将自己的测试用例所在的目录传给 &lt;code&gt;autotest&lt;/code&gt; 命令, 比如:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -t &amp;lt;test_case_dir&amp;gt; /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Your Test Case&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-green-500 bg-green-50 dark:bg-green-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-green-700 dark:text-green-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Your Test Case&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;注意每一个 Test Case 包含两个名称相同的 &lt;code&gt;.c&lt;/code&gt; 和 &lt;code&gt;.out&lt;/code&gt; 文件, 其中 &lt;code&gt;.c&lt;/code&gt; 是输入给编译器的代码, &lt;code&gt;.out&lt;/code&gt; 是你的编译器应该返回的结果.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;lv-1&quot;&gt;Lv 1&lt;a class=&quot;anchor&quot; href=&quot;#lv-1&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lab 的第一个 checkpoint 要求大家完成编译器的基础逻辑结构搭建, 包括:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;词法分析器的逻辑&lt;/li&gt;
&lt;li&gt;语法分析器的逻辑&lt;/li&gt;
&lt;li&gt;简单的中间代码 &lt;code&gt;Koopa&lt;/code&gt; 生成器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我使用的是 CMake 作为构建系统, 采用了 &lt;a href=&quot;https://github.com/pku-minic/sysy-cmake-template&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;基于 CMake 的 SysY 编译器项目模板&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 的结构.&lt;/p&gt;
&lt;p&gt;可以先把&lt;a href=&quot;https://pku-minic.github.io/online-doc/#/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;实验文档&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;中 &lt;code&gt;Lv 1&lt;/code&gt; 中给出的代码复制下来作为 &lt;code&gt;codebase&lt;/code&gt; .&lt;/p&gt;
&lt;h3 id=&quot;lexer&quot;&gt;Lexer&lt;a class=&quot;anchor&quot; href=&quot;#lexer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我们只需要在 &lt;code&gt;src/sysy.l&lt;/code&gt; 中写一段代码说明如何定义 Token 类型, 如何把读到的字符串转化为整数或者浮点数, 然后就可以使用 &lt;code&gt;flex&lt;/code&gt; 读入 &lt;code&gt;src/sysy.l&lt;/code&gt; 来生成词法分析器, 所以实际上不用自己写一个词法分析器.&lt;/p&gt;
&lt;aside aria-label=&quot;Flex&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Flex&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;&lt;code&gt;flex&lt;/code&gt; 的输入文件的语法规则可以参考&lt;a href=&quot;https://pku-minic.github.io/online-doc/#/lv1-main/lexer-parser&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;实验文档对应章节&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; .&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;示例:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;&amp;quot;int&amp;quot;           { return INT; }
&amp;quot;return&amp;quot;        { return RETURN; }
{Identifier}    { yylval.str_val = new string(yytext); return IDENT; }
{Decimal}       { yylval.int_val = strtol(yytext, nullptr, 0); return INT_CONST; }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中 &lt;code&gt;INT&lt;/code&gt;, &lt;code&gt;RETURN&lt;/code&gt;, &lt;code&gt;IDENT&lt;/code&gt; 等返回值, 其实是 Bison 生成的固定枚举类型值, 就是一个整数.&lt;/p&gt;
&lt;p&gt;所以这段代码代表的含义为:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;int&amp;quot;&lt;/code&gt; 和 &lt;code&gt;&amp;quot;return&amp;quot;&lt;/code&gt; 是正则表达式, 这就是告诉当匹配到这些字符串时, 返回给语法分析器 &lt;code&gt;INT&lt;/code&gt; 和 &lt;code&gt;RETURN&lt;/code&gt; 类型, 告诉语法分析器这是一个保留字.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{Identifier}&lt;/code&gt; 是上面定义好的正则表达式, 当这个表达式匹配到某个字符串时, 将这个字符串赋值给语法分析器定义的 &lt;code&gt;yylval.str_val&lt;/code&gt; 变量, 然后返回给语法分析器 &lt;code&gt;IDENT&lt;/code&gt; 类型, 告诉语法分析器这是一个标识符, 比如函数名, 语法分析器知道现在读取到了一个标识符, 就从 &lt;code&gt;yylval.str_val&lt;/code&gt; 中取出这个字符串.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{Decimal}&lt;/code&gt; 是上面定义好的正则表达式, 当这个表达式匹配到某个字符串时, 将这个字符串赋值给语法分析器定义的 &lt;code&gt;yylval.int_val&lt;/code&gt; 变量, 然后返回给语法分析器 &lt;code&gt;INT_CONST&lt;/code&gt; 类型, 告诉语法分析器这是一个整数常量, 语法分析器知道现在读取到了一个整数常量, 就从 &lt;code&gt;yylval.int_val&lt;/code&gt; 中取出这个整数.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;parser&quot;&gt;Parser&lt;a class=&quot;anchor&quot; href=&quot;#parser&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;我们只需要在 &lt;code&gt;src/sysy.y&lt;/code&gt; 中写一段代码说明如何定义语法规则, 比如一个函数是如何由函数类型, 函数名, 变量声明列表和函数体组成的, 然后就可以使用 &lt;code&gt;bison&lt;/code&gt; 读入 &lt;code&gt;src/sysy.y&lt;/code&gt; 来生成语法分析器.&lt;/p&gt;
&lt;p&gt;规约示例:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;FuncDef
  : FuncType IDENT &amp;#39;(&amp;#39; &amp;#39;)&amp;#39; Block {
    auto ast = new FuncDefAST();
    ast-&amp;gt;func_type = unique_ptr&amp;lt;BaseAST&amp;gt;($1);
    ast-&amp;gt;ident = *unique_ptr&amp;lt;string&amp;gt;($2);
    ast-&amp;gt;block = unique_ptr&amp;lt;BaseAST&amp;gt;($5);
    $$ = ast;
  }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;其中 &lt;code&gt;FuncDefAST&lt;/code&gt; 等类是 &lt;code&gt;include/ast.hpp&lt;/code&gt; 中由你定义好的, 其中包括了每个语法规则包含的语法单元, 比如 &lt;code&gt;FuncDef&lt;/code&gt; 语法规则包含 &lt;code&gt;FuncType&lt;/code&gt;, &lt;code&gt;IDENT&lt;/code&gt;, &lt;code&gt;Block&lt;/code&gt; 等语法单元, 语法分析器现在使用这些类来构造抽象语法树.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$1&lt;/code&gt; 是代表子语法单元的变量, 比如 &lt;code&gt;FuncDef&lt;/code&gt; 语法规则的第一个子语法单元是 &lt;code&gt;FuncType&lt;/code&gt;, 那么 &lt;code&gt;$1&lt;/code&gt; 就代表 &lt;code&gt;FuncType&lt;/code&gt;, 语法分析器递归地从 &lt;code&gt;FuncType&lt;/code&gt; 继续规约, 注意这里的变量标号是从 1 开始的.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$$ = ast;&lt;/code&gt; 是告诉语法分析器, &lt;code&gt;FuncDef&lt;/code&gt; 语法规则规约的结果是一个 &lt;code&gt;FuncDefAST&lt;/code&gt; 类, 语法分析器现在知道 &lt;code&gt;FuncDef&lt;/code&gt; 语法规则规约的结果 &lt;code&gt;FuncDefAST&lt;/code&gt; 类中都是什么数据了.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;koopa-ir-generation&quot;&gt;&lt;code&gt;Koopa IR&lt;/code&gt; Generation&lt;a class=&quot;anchor&quot; href=&quot;#koopa-ir-generation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;当语法分析器规约出抽象语法树后, 我们就可以遍历抽象语法树, 遇到叶子节点就 &lt;code&gt;print&lt;/code&gt; , 遍历结束就生成了中间代码 &lt;code&gt;Koopa&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;main.cpp&lt;/code&gt; 中定义编译器本身的 &lt;code&gt;main&lt;/code&gt; 函数, 读入需要编译的源文件, 调用词法分析器和语法分析器, 得到放好数据的抽象语法树, 然后调用语法树类中定义好的 &lt;code&gt;print&lt;/code&gt; 函数生成中间代码 &lt;code&gt;Koopa&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;我的具体结构安排是:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 &lt;code&gt;main.cpp&lt;/code&gt; 中定义编译器本身的 &lt;code&gt;main&lt;/code&gt; 函数, 读入需要编译的源文件, 调用词法分析器和语法分析器, 得到放好数据的抽象语法树, 然后调用语法树类中定义好的 &lt;code&gt;print&lt;/code&gt; 函数生成中间代码 &lt;code&gt;Koopa&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;include/ast.hpp&lt;/code&gt; 中定义抽象语法树的节点类, 每个节点类中定义一个 &lt;code&gt;print&lt;/code&gt; 函数, 用于生成中间代码 &lt;code&gt;Koopa&lt;/code&gt; 或 Debug 信息.&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;ast.cpp&lt;/code&gt; 中定义抽象语法树的各个节点类, 并实现 &lt;code&gt;print&lt;/code&gt; 函数.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;incomplete-parts-of-the-codebase&quot;&gt;Incomplete Parts of the Codebase&lt;a class=&quot;anchor&quot; href=&quot;#incomplete-parts-of-the-codebase&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在 handout 给出的 codebase 中, 词法分析器和语法分析器大部分已经写好了, 但是还有一些需要修改的地方:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 &lt;code&gt;src/sysy.y&lt;/code&gt; 中, 需要加入 &lt;code&gt;include/ast.hpp&lt;/code&gt; 的引用, 否则语法分析器会找不到你定义的抽象语法树的节点类, 就不能把数据写到你定义的抽象语法树中.&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;include/ast.hpp&lt;/code&gt; 中, 需要定义示例代码中没有给出的抽象语法树的节点类, 并定义 &lt;code&gt;print&lt;/code&gt; 函数.&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;ast.cpp&lt;/code&gt; 中, 需要实现示例代码中没有实现的抽象语法树的各个节点类, 并实现 &lt;code&gt;print&lt;/code&gt; 函数.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside aria-label=&quot;BlockComment&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-green-500 bg-green-50 dark:bg-green-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-green-700 dark:text-green-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;BlockComment&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;确保在 &lt;code&gt;src/sysy.l&lt;/code&gt; 中定义正确的 BlockComment 的正则表达式, 否则有一些测试点不通过.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;好的, 现在已经完成了编译器的基础逻辑结构搭建!&lt;/p&gt;
&lt;h3 id=&quot;compile-and-test-1&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-1&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;以下均默认你已经进入容器, 并且当前目录为编译 Lab 的一级目录.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;cmake&lt;/code&gt; 生成 Makefile 文件:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;cmake -DCMAKE_BUILD_TYPE=Debug -B build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;使用 &lt;code&gt;cmake&lt;/code&gt; 编译:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;cmake --build build&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv1 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果本地测试不通过, 可以把 &lt;code&gt;/opt/bin/testcases&lt;/code&gt; 中的测试用例复制到当前路径进行查看调试.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;cp -r /opt/bin/testcases .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lv-2&quot;&gt;Lv 2&lt;a class=&quot;anchor&quot; href=&quot;#lv-2&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;首先回顾一下编译器的三层结构:&lt;/p&gt;
&lt;aside aria-label=&quot;Compiler Structure&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Compiler Structure&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;前端&lt;/strong&gt;: 通过词法分析和语法分析, 将源代码解析成抽象语法树 (abstract syntax tree, AST). 通过语义分析, 扫描抽象语法树, 检查其是否存在语义错误.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中端&lt;/strong&gt;: 将抽象语法树转换为中间表示 (intermediate representation, IR), 并在此基础上完成一些机器无关优化.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后端&lt;/strong&gt;: 将中间表示转换为目标平台的汇编代码, 并在此基础上完成一些机器相关优化.&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;在 Lv 1 中已经完成了前端和中端, 现在来完成后端.&lt;/p&gt;
&lt;p&gt;虽说是完成后端, 但是实际上助教团队已经帮助实现好了能够处理 Koopa IR 的库, 我们只需要调用他们提供的库 (即调用 &lt;code&gt;koopa.h&lt;/code&gt; 中定义的函数) 就可以完成后端的大部分实现了, 我们只需要自己实现 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码的输出就可以了.&lt;/p&gt;
&lt;p&gt;具体如何调用, 参阅 &lt;a href=&quot;https://pku-minic.github.io/online-doc/#/lv2-code-gen/processing-ir&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;实验文档&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 把代码复制下来即可.&lt;/p&gt;
&lt;p&gt;最后的后端代码入口应该类似:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;#include &amp;quot;include/backend.hpp&amp;quot;

int backend(const char *koopa_str)
{
    // 解析字符串 str, 得到 Koopa IR 程序
    koopa_program_t program;
    koopa_error_code_t ret = koopa_parse_from_string(koopa_str, &amp;amp;program);
    assert(ret == KOOPA_EC_SUCCESS); // 确保解析时没有出错
    // 创建一个 raw program builder, 用来构建 raw program
    koopa_raw_program_builder_t builder = koopa_new_raw_program_builder();
    // 将 Koopa IR 程序转换为 raw program
    koopa_raw_program_t raw = koopa_build_raw_program(builder, program);
    // 释放 Koopa IR 程序占用的内存
    koopa_delete_program(program);

    // 处理 raw program
    visit(raw);

    // 处理完成, 释放 raw program builder 占用的内存
    // 注意, raw program 中所有的指针指向的内存均为 raw program builder 的内存
    // 所以不要在 raw program 处理完毕之前释放 builder
    koopa_delete_raw_program_builder(builder);

    return 0;
}

void visit(const koopa_raw_slice_t &amp;amp;slice)
{
  // ...
}

void visit(const koopa_raw_program_t &amp;amp;program)
{
  // ...
}

// ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;traverse-the-abstract-syntax-tree&quot;&gt;Traverse the Abstract Syntax Tree&lt;a class=&quot;anchor&quot; href=&quot;#traverse-the-abstract-syntax-tree&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;调用库之后我们就得到了以抽象语法树形式表示的 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码, 现在需要 DFS 遍历这棵抽象语法树, 将其转换为字符串并输出, 我们选择使用函数递归的方式来遍历这棵抽象语法树, 具体如何遍历可以参考实验文档, 这里不再赘述, 但是有一些 high level 的 idea 可以帮助你理解这颗树上的各种助教定义的 type.&lt;/p&gt;
&lt;p&gt;这颗语法树的节点大致是 &lt;code&gt;program&lt;/code&gt;, &lt;code&gt;function&lt;/code&gt;, &lt;code&gt;basic_block&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, 这些节点很多都包含同类型的东西, 比如一个程序有很多的函数, 一个函数有很多的基本块, 一个基本块有很多指令, 那么比如 &lt;code&gt;program&lt;/code&gt; 这个节点的这一堆函数就会存在&lt;strong&gt;一个&lt;/strong&gt; &lt;code&gt;koopa_raw_slice_t&lt;/code&gt; 类型中, 所以对于一个 &lt;code&gt;program&lt;/code&gt; 节点中的所有函数, 只需要对包含这些函数的这一个 &lt;code&gt;koopa_raw_slice_t&lt;/code&gt; 调用一次 &lt;code&gt;visit&lt;/code&gt; 函数即可, 如下所示, &lt;code&gt;visit&lt;/code&gt; 函数会把这一堆东西逐个帮你访问.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;void visit(const koopa_raw_slice_t &amp;amp;slice)
{
    for (size_t i = 0; i &amp;lt; slice.len; ++i)
    {
        auto ptr = slice.buffer[i];
        // 根据 slice 的 kind 决定将 ptr 视作何种元素
        switch (slice.kind)
        {
        case KOOPA_RSIK_FUNCTION:
            // 访问函数
            visit(reinterpret_cast&amp;lt;koopa_raw_function_t&amp;gt;(ptr));
            break;
        case KOOPA_RSIK_BASIC_BLOCK:
            // 访问基本块
            visit(reinterpret_cast&amp;lt;koopa_raw_basic_block_t&amp;gt;(ptr));
            break;
        case KOOPA_RSIK_VALUE:
            // 访问指令
            visit(reinterpret_cast&amp;lt;koopa_raw_value_t&amp;gt;(ptr));
            break;
        default:
            // 我们暂时不会遇到其他内容, 于是不对其做任何处理
            assert(false);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;访问 &lt;code&gt;koopa_raw_value_t&lt;/code&gt; 类型的函数大致如下所示, &lt;code&gt;koopa_raw_value_t&lt;/code&gt; 类型是一个指针, 这个指针可以指向 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码的一个值 (一条指令的结果可以代表这个指令, 所以一条指令也算一个值) .&lt;/p&gt;
&lt;p&gt;如果碰上代表一个指令, 就可能重复调用 &lt;code&gt;visit koopa_raw_value_t&lt;/code&gt; 两次, 因为指令中包含值, 比如调用 &lt;code&gt;visit koopa_raw_value_t&lt;/code&gt; 时发现这是一个返回指令, 就调用 &lt;code&gt;visit koopa_raw_return_t&lt;/code&gt; , 返回值就会需要再调用一次 &lt;code&gt;visit koopa_raw_value_t&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;void visit(const koopa_raw_value_t &amp;amp;value)
{
    // 根据指令类型判断后续需要如何访问
    const auto &amp;amp;kind = value-&amp;gt;kind;
    switch (kind.tag)
    {
    case KOOPA_RVT_RETURN:
        // 访问 return 指令
        visit(kind.data.ret);
        break;
    case KOOPA_RVT_INTEGER:
        // 访问 integer 指令
        visit(kind.data.integer, value);
        break;
    // ...
    default:
        // 其他类型暂时遇不到
        throw std::runtime_error(&amp;quot;visit: invalid instruction&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;link-middleend-and-backend&quot;&gt;Link Middleend and Backend&lt;a class=&quot;anchor&quot; href=&quot;#link-middleend-and-backend&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;与此同时, 你还需要将你在 Lv 1 中输出的 &lt;code&gt;Koopa IR&lt;/code&gt; 输入给后端, 在这里我推荐使用 &lt;code&gt;std::stringstream&lt;/code&gt; 类型来存储 &lt;code&gt;Koopa IR&lt;/code&gt;, 然后交给后端即可, 实现代码可以类似:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;std::ostringstream koopa_ir; // 用于存储 Koopa IR 的 stringstream
// ...
ast-&amp;gt;print(&amp;amp;koopa_ir);
  // ...
  // inside print(): 你的中端代码将 Koopa IR 输出到 koopa_ir 中
  // ...
freopen(output, &amp;quot;w&amp;quot;, stdout); // 将 stdout 重定向到 output 文件, output 是你的输出文件路径
backend(koopa_ir.str().c_str()); // 将 stringstream 转换为 C style string 后交给后端
  // ...
  // inside backend(): 后端代码将 RISC-V 汇编代码输出到 stdout 中
  // ...
fclose(stdout);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样写的优点包括:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实现简洁明了.&lt;/li&gt;
&lt;li&gt;无需进行硬盘交互 (把 Koopa IR 输出到文件系统再从硬盘读取, 再交给后端), 这样实现之后 Koopa IR 的数据保存在内存中, 后端直接从内存中读取即可.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;compile-and-test-2&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv1 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lv-3&quot;&gt;Lv 3&lt;a class=&quot;anchor&quot; href=&quot;#lv-3&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本章将在上一章的基础上, 实现一个能够处理表达式 (一元/二元) 的编译器.&lt;/p&gt;
&lt;p&gt;需要完成对抽象语法树和 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码输出这两部分的修改.&lt;/p&gt;
&lt;p&gt;你的编译器将可以处理如下的 SysY 程序:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() 
{
  return 1 + 2 * -3;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;需要完成对抽象语法树和 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码输出这两部分的修改.&lt;/p&gt;
&lt;h3 id=&quot;lexer-1&quot;&gt;Lexer&lt;a class=&quot;anchor&quot; href=&quot;#lexer-1&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;修改 &lt;code&gt;src/sysy.l&lt;/code&gt; 文件, 添加对运算符的识别, 比如 &lt;code&gt;!&lt;/code&gt; 和 &lt;code&gt;-&lt;/code&gt; 运算符.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;/* 运算符 */
ExclusiveUnaryOp       &amp;quot;!&amp;quot;
MulOp         [\*/%]
AddOp         [\+\-]
RelOp         (&amp;quot;&amp;lt;&amp;quot;|&amp;quot;&amp;gt;&amp;quot;|&amp;quot;&amp;lt;=&amp;quot;|&amp;quot;&amp;gt;=&amp;quot;)
EqOp          (&amp;quot;==&amp;quot;|&amp;quot;!=&amp;quot;)
AndOp         &amp;quot;&amp;amp;&amp;amp;&amp;quot;
OrOp          &amp;quot;||&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这里我的逻辑是对每一种运算都定义一个正则表达式, 用来表达这个运算会使用的所有字符, 这样就不用在语法分析器中对每一个运算符都写一个规约规则了.&lt;/p&gt;
&lt;p&gt;但是有一个问题是单元运算符和二元加减运算符有两个符号是重叠的, 所以我选择使用 &lt;code&gt;ExclusiveUnaryOp&lt;/code&gt; 来表示只有单元运算符使用的符号, &lt;code&gt;AddOp&lt;/code&gt; 就代表着单元运算和二元加减运算共同使用的符号了.&lt;/p&gt;
&lt;p&gt;另外还要加入识别 token 之后如何返回给语法分析器, 类似 &lt;code&gt;Identifier&lt;/code&gt; 和 &lt;code&gt;Decimal&lt;/code&gt; 那样, 使用 &lt;code&gt;yylval&lt;/code&gt; 来返回字符串给语法分析器.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;{ExclusiveUnaryOp}      { yylval.str_val = new string(yytext); return EXCLUSIVE_UNARY_OP; }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;parser-1&quot;&gt;Parser&lt;a class=&quot;anchor&quot; href=&quot;#parser-1&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;修改 &lt;code&gt;src/sysy.y&lt;/code&gt; 文件和 &lt;code&gt;include/ast.hpp&lt;/code&gt; 文件, 添加对新的语法规则的规约.&lt;/p&gt;
&lt;p&gt;举例说明:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;UnaryExp
  : PrimaryExp {
    auto ast = new UnaryExpAST();
    ast-&amp;gt;primary_exp = unique_ptr&amp;lt;BaseAST&amp;gt;($1);
    $$ = ast;
  }
  | EXCLUSIVE_UNARY_OP UnaryExp {
    auto ast = new UnaryExpAST();
    ast-&amp;gt;op = *unique_ptr&amp;lt;string&amp;gt;($1);
    ast-&amp;gt;unary_exp = unique_ptr&amp;lt;BaseAST&amp;gt;($2);
    $$ = ast;
  }
  | ADD_OP UnaryExp {
    auto ast = new UnaryExpAST();
    ast-&amp;gt;op = *unique_ptr&amp;lt;string&amp;gt;($1);
    ast-&amp;gt;unary_exp = unique_ptr&amp;lt;BaseAST&amp;gt;($2);
    $$ = ast;
  }
  ;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;对应如下的抽象语法树类:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/**
 * @brief 一元表达式抽象语法树类. 
 */
class UnaryExpAST : public BaseAST
{
public:
    std::optional&amp;lt;std::unique_ptr&amp;lt;BaseAST&amp;gt;&amp;gt; primary_exp; // 可选的基本表达式
    std::optional&amp;lt;std::string&amp;gt; op;                       // 可选的操作符 (&amp;quot;+&amp;quot;, &amp;quot;-&amp;quot;, &amp;quot;!&amp;quot;)
    std::optional&amp;lt;std::unique_ptr&amp;lt;BaseAST&amp;gt;&amp;gt; unary_exp;   // 可选的一元表达式

    /**
     * @brief 打印抽象语法树. 
     * @param[in] output_stream 输出流. 
     * @return 打印操作的结果
     */
    Result print(std::stringstream &amp;amp;output_stream) const override; // 打印抽象语法树, 稍后解释这个返回类型的用处
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中有两个实现细节:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;std::optional&lt;/code&gt;
&lt;ol&gt;
&lt;li&gt;是 &lt;strong&gt;C++17&lt;/strong&gt; 引入的类型, 你可能需要修改 &lt;code&gt;VSCode&lt;/code&gt; 的编译器版本, 否则无法正常高亮显示.&lt;/li&gt;
&lt;li&gt;它用于表示一个可能存在也可能不存在的值, 如果值存在, 则可以使用 &lt;code&gt;value()&lt;/code&gt; 方法获取该值, 如果值不存在, 则可以使用 &lt;code&gt;has_value()&lt;/code&gt; 方法判断是否存在, 或者使用 &lt;code&gt;operator*&lt;/code&gt; 获取该值, 用来判断在多个规约规则中具体选了那个规约规则.&lt;/li&gt;
&lt;li&gt;比如下面的规约规则有两个选择, 分别是 &lt;code&gt;PrimaryExp&lt;/code&gt; 和 &lt;code&gt;UnaryOp UnaryExp&lt;/code&gt;, 如果选择了 &lt;code&gt;PrimaryExp&lt;/code&gt; 那么 &lt;code&gt;primary_exp&lt;/code&gt; 就会存在, &lt;code&gt;op&lt;/code&gt; 和 &lt;code&gt;unary_exp&lt;/code&gt; 就不存在, 反之亦然.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;print&lt;/code&gt; 函数返回一个 &lt;code&gt;Result&lt;/code&gt; 类型的变量, 稍后解释这个返回类型的用处.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上的代码代表着如下的规约规则:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;UnaryExp    ::= PrimaryExp | UnaryOp UnaryExp;
UnaryOp     ::= &amp;quot;+&amp;quot; | &amp;quot;-&amp;quot; | &amp;quot;!&amp;quot;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中使用了我们在对词法分析器作修改的时候定义的新 token 种类 &lt;code&gt;EXCLUSIVE_UNARY_OP&lt;/code&gt; 和 &lt;code&gt;ADD_OP&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;当然别忘了要在 &lt;code&gt;src/sysy.y&lt;/code&gt; 文件中定义新的终结符和非终结符的类型.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;// lexer 返回的所有 token 种类的声明, 终结符的类型为 str_val 和 int_val
%token INT RETURN
%token &amp;lt;str_val&amp;gt; IDENT
%token &amp;lt;int_val&amp;gt; INT_CONST
%token &amp;lt;str_val&amp;gt; EXCLUSIVE_UNARY_OP MUL_OP ADD_OP REL_OP EQ_OP AND_OP OR_OP // Operators

// 非终结符的类型定义
%type &amp;lt;ast_val&amp;gt; FuncDef FuncType Block Stmt Exp UnaryExp PrimaryExp MulExp AddExp LOrExp LAndExp RelExp EqExp
%type &amp;lt;int_val&amp;gt; Number&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;koopa-ir-generation-1&quot;&gt;&lt;code&gt;Koopa IR&lt;/code&gt; Generation&lt;a class=&quot;anchor&quot; href=&quot;#koopa-ir-generation-1&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;修改 &lt;code&gt;ast.cpp&lt;/code&gt; 文件, 添加对新的语法规则的 &lt;code&gt;print&lt;/code&gt; 函数.&lt;/p&gt;
&lt;p&gt;在实现之前我们先来思考一个例子:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() 
{
  return 6;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这是之前的编译器可以处理的代码, 我们在调用 &lt;code&gt;RetAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数时, 先输出 &lt;code&gt;ret &lt;/code&gt; 然后调用 &lt;code&gt;NumberExpAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数, 输出 &lt;code&gt;6&lt;/code&gt;, 最后回到 &lt;code&gt;RetAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数输出 &lt;code&gt;\n&lt;/code&gt;, 就可以得到如下的 &lt;code&gt;Koopa IR&lt;/code&gt; 代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;fun @main(): i32 {
%entry:
  ret 6
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;现在我们考虑这个例子:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() 
{
  return -6;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果还是按照之前的逻辑, 先输出 &lt;code&gt;ret &lt;/code&gt; 然后调用 &lt;code&gt;ExpAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数, 就会出现问题, 因为我们还需要一个 &lt;code&gt;sub&lt;/code&gt; 指令才能计算出 &lt;code&gt;-6&lt;/code&gt;, 但是此时 &lt;code&gt;Koopa IR&lt;/code&gt; 已经输出到文件中了, 我们无法在 &lt;code&gt;ret&lt;/code&gt; 指令后面继续输出 &lt;code&gt;sub&lt;/code&gt; 指令了.&lt;/p&gt;
&lt;p&gt;这是我们期望得到的 &lt;code&gt;Koopa IR&lt;/code&gt; 代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;fun @main(): i32 {
%entry:
  %0 = sub 0, 6
  ret %0
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;所以我们在进入任何一个 &lt;code&gt;print&lt;/code&gt; 函数时, 不能先入为主地输出任何 &lt;code&gt;Koopa IR&lt;/code&gt; 指令, 需要先调用这个抽象语法树的所有子变量的 &lt;code&gt;print&lt;/code&gt; 函数, 等到它们把类似上文的 &lt;code&gt;sub&lt;/code&gt; 指令输出完成之后再输出当前的 &lt;code&gt;Koopa IR&lt;/code&gt; 指令.&lt;/p&gt;
&lt;p&gt;但是如果子变量的所有 &lt;code&gt;print&lt;/code&gt; 函数都没有返回任何信息, 那么我们怎么知道这些子变量把计算结果储存到哪里了呢?&lt;/p&gt;
&lt;p&gt;比如 &lt;code&gt;RetAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数, 当它调用完成 &lt;code&gt;ExpAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数之后, 它怎么知道 &lt;code&gt;ExpAST&lt;/code&gt; 的计算结果是储存在 &lt;code&gt;%0&lt;/code&gt; 这个寄存器中了而不是 &lt;code&gt;%1&lt;/code&gt; 或者其他寄存器中呢?&lt;/p&gt;
&lt;p&gt;我们不希望使用全局变量解决任何问题, 这样非常 dirty, 所以我们需要每一个 &lt;code&gt;print&lt;/code&gt; 函数返回一个 &lt;code&gt;Result&lt;/code&gt; 类型的变量, 告诉父变量这个子变量的计算结果储存在哪里, 以便父变量决定如何输出当前的 &lt;code&gt;Koopa IR&lt;/code&gt; 指令.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/**
 * @brief 用于存储计算结果的类, 可以是符号或立即数. 
 * @note 如果当前函数会产生一个计算结果, 那么这个计算结果会存储在返回的 `Result` 类型的变量中
 * @note 比如 `PrimaryExpAST` 的 `print` 函数, 当它是从数字规约而来时, 它的 `Result` 变量会被初始化为立即数, 返回 `Result(Result::Type::IMM, *number)` 这样一个变量
 * @note 如果当前函数不会产生计算结果, 那么返回的 `Result` 变量会被初始化为立即数 0
 * @date 2024-11-27
 */
class Result
{
public:
    /**
     * @brief 当前计算值, 存储在 `%current_value_symbol_index` 符号中. 
     * @date 2024-11-27
     */
    static int current_symbol_index;

    enum class Type
    {
        IMM, // 立即数
        REG  // 寄存器
    };
    Type type; // 结果的类型
    int val;   // 结果的值

    // 默认构造函数, 初始化为立即数 0, 没有用到它的地方
    Result() : type(Type::IMM), val(0) {}

    // 带有指定类型的构造函数, 主要用来初始化寄存器
    Result(Type type) : type(type), val(0)
    {
        if (type == Type::REG)
        {
            val = ++current_symbol_index;
        }
    }

    // 带有指定类型和值的构造函数, 主要用来初始化立即数
    Result(Type type, int val) : type(type), val(val)
    {
        if (type == Type::REG)
        {
            val = ++current_symbol_index;
        }
    }

    // 重载 &amp;lt;&amp;lt;
    friend std::ostream &amp;amp;operator&amp;lt;&amp;lt;(std::ostream &amp;amp;os, const Result &amp;amp;result)
    {
        os &amp;lt;&amp;lt; (result.type == Result::Type::REG ? &amp;quot;%&amp;quot; : &amp;quot;&amp;quot;) &amp;lt;&amp;lt; result.val;
        return os;
    }
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Result&lt;/code&gt; 类中有一个静态变量 &lt;code&gt;current_symbol_index&lt;/code&gt;, 这个变量用于给每一个计算结果分配一个唯一的寄存器. 当 &lt;code&gt;Result&lt;/code&gt; 类被初始化为立即数时, 这个变量不会被用到, 而当 &lt;code&gt;Result&lt;/code&gt; 类被初始化为寄存器时, 这个变量会被用来给计算结果分配一个唯一的寄存器, 然后这个 &lt;code&gt;current_symbol_index&lt;/code&gt; 的值会加一.&lt;/p&gt;
&lt;p&gt;同时为了方便输出寄存器和立即数, 我们重载了 &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; 操作符.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;Result UnaryExpAST::print(std::stringstream &amp;amp;output_stream) const
{
    if (primary_exp &amp;amp;&amp;amp; !op &amp;amp;&amp;amp; !unary_exp)
    {
        return (*primary_exp)-&amp;gt;print(output_stream);
    }
    else if (!primary_exp &amp;amp;&amp;amp; op &amp;amp;&amp;amp; unary_exp)
    {
        Result unary_result = (*unary_exp)-&amp;gt;print(output_stream);
        Result result = Result(Result::Type::REG);
        if (*op == &amp;quot;+&amp;quot;)
        {
            output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &amp;quot; = add 0, &amp;quot; &amp;lt;&amp;lt; unary_result &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
        }
        else if (*op == &amp;quot;-&amp;quot;)
        {
            output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &amp;quot; = sub 0, &amp;quot; &amp;lt;&amp;lt; unary_result &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
        }
        else if (*op == &amp;quot;!&amp;quot;)
        {
            output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &amp;quot; = eq 0, &amp;quot; &amp;lt;&amp;lt; unary_result &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
        }
        else
        {
            throw std::runtime_error(&amp;quot;UnaryExpAST::print: invalid unary operator&amp;quot;);
        }
        return result;
    }
    else
    {
        throw std::runtime_error(&amp;quot;UnaryExpAST::print: invalid unary expression&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样看起来就很清晰了.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当 &lt;code&gt;UnaryExpAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数被调用时, 如果它选择了 &lt;code&gt;UnaryExp ::= PrimaryExp&lt;/code&gt; 这条规约规则, 那么它就会调用 &lt;code&gt;PrimaryExpAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数&lt;/li&gt;
&lt;li&gt;此时 &lt;code&gt;UnaryExpAST&lt;/code&gt; 并没有做任何计算, 所以直接返回 &lt;code&gt;PrimaryExpAST&lt;/code&gt; 的计算结果即可.&lt;/li&gt;
&lt;li&gt;如果它选择了 &lt;code&gt;UnaryExp ::= UnaryOp UnaryExp&lt;/code&gt; 这条规约规则, 就需要根据 &lt;code&gt;UnaryOp&lt;/code&gt; 的值输出相应的 &lt;code&gt;Koopa IR&lt;/code&gt; 指令, 运算的结果需要使用一个新的寄存器来储存, 所以构造一个新的 &lt;code&gt;Result(Result::Type::REG)&lt;/code&gt; 变量, 并返回这个变量.&lt;/li&gt;
&lt;li&gt;最后如果出现任何例外情况, 直接抛出异常, 这样是很好的防御型编程操作实践.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;risc-v-assembly-code-generation&quot;&gt;&lt;code&gt;RISC-V&lt;/code&gt; Assembly Code Generation&lt;a class=&quot;anchor&quot; href=&quot;#risc-v-assembly-code-generation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在这一部分你需要修改 &lt;code&gt;include/backend.hpp&lt;/code&gt; 和 &lt;code&gt;src/backend.cpp&lt;/code&gt; 文件, 完成新的语法规则的 &lt;code&gt;print&lt;/code&gt; 函数来输出 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码.&lt;/p&gt;
&lt;p&gt;这部分的难点在于如何分配寄存器, 储存在内存中的 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码是没有进行寄存器分配的.&lt;/p&gt;
&lt;p&gt;比如一个加法运算, 你只知道左操作数和右操作数是两个表达式, 但是你并不知道这两个表达式的结果分别在哪个寄存器当中, 内存中的汇编代码也不提供具体的寄存器编号, 所以你需要在输出的同时为每一行运算都分配一个寄存器来保存运算的结果, 并且不能覆盖之前刚计算完还没用过的寄存器.&lt;/p&gt;
&lt;p&gt;寄存器分配问题是一个 NPC 问题, 但是很好的一点是 Lv3 的测试样例中不会出现需要寄存器复用的情况, 所以我们可以使用贪心算法来解决这个问题.&lt;/p&gt;
&lt;p&gt;首先仔细观察一个例子:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() 
{
  return 1 + 2 * -3;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;可以得到如下 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;	.text
	.globl main
main:
  li  t0, 2
  li  t1, 3
  mul t1, t0, t1
  li  t2, 1
  add t2, t1, t2
  mv a0, t2
  ret&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;可以发现当前&lt;strong&gt;每一行汇编代码的计算结果都只会被使用一次&lt;/strong&gt;, 如果把这个汇编代码修改为:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;	.text
	.globl main
main:
  li  t0, 2
  li  t1, 3
  mul t0, t0, t1
  li  t1, 1
  add t0, t0, t1
  mv a0, t0
  ret&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;不会有任何问题.&lt;/p&gt;
&lt;h3 id=&quot;risc-v-register-manager&quot;&gt;&lt;code&gt;RISC-V&lt;/code&gt; Register Manager&lt;a class=&quot;anchor&quot; href=&quot;#risc-v-register-manager&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;为了方便管理寄存器, 我设计了一个 &lt;code&gt;RegisterManager&lt;/code&gt; 类, 这个类可以设置一个寄存器为可以覆盖, 判断一个值是否占用了一个寄存器, 给一个值分配一个寄存器, 输出某个值对应的寄存器名称.&lt;/p&gt;
&lt;p&gt;所有计算结果, 包括一个表达式, 一个立即数, 都是跟一个 &lt;code&gt;koopa_raw_value_t&lt;/code&gt; 类型的指针一一对应的, 所以我们可以使用 &lt;code&gt;koopa_raw_value_t&lt;/code&gt; 类型的指针来作为寄存器管理器中的键值, 这样就可以方便地找到一个值对应的寄存器名称.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/**
 * @brief 寄存器管理器, 可以设置一个寄存器为可以覆盖, 判断一个值是否占用了一个寄存器, 给一个值分配一个寄存器, 输出某个值对应的寄存器名称
 */
class RegisterManager
{
private:
    // 值到寄存器名称的映射
    std::unordered_map&amp;lt;koopa_raw_value_t, std::string&amp;gt; _value_to_reg_string;
    // 存储当前所有寄存器是否可能会再次被利用, 比如将立即数转移给 a0 寄存器, 我们现在就认为 a0 寄存器被占用了, 但是如果 a0 寄存器之后被调用了, 这个立即数被使用过了, 那么 a0 寄存器就会被标记为不被占用, 因为到目前为止我们认为每一个结果只被使用一次
    std::unordered_map&amp;lt;std::string, bool&amp;gt; _reg_is_used;
    /**
     * @brief 设置一个值对应哪个寄存器, 内部函数不被外部调用
     * @param[in] value 值
     * @param[in] reg_string 寄存器名称
     */
    void _set_value_to_reg_string(const koopa_raw_value_t &amp;amp;value, const std::string &amp;amp;reg_string);

public:
    /**
     * @brief 构造函数, 初始化所有寄存器为未占用
     */
    RegisterManager()
    {
        // 初始化所有寄存器为未占用
        for (int i = 0; i &amp;lt;= 6; ++i)
        {
            _reg_is_used[&amp;quot;t&amp;quot; + std::to_string(i)] = false;
        }
        for (int i = 0; i &amp;lt;= 7; ++i)
        {
            _reg_is_used[&amp;quot;a&amp;quot; + std::to_string(i)] = false;
        }
    }

    /**
     * @brief 设置一个值对应的寄存器为未占用, 当一个值被使用过之后, 我们将它占用的寄存器设置为未占用, 因为我们认为每一个结果只被使用一次
     * @param[in] value 值
     */
    void set_reg_free(const koopa_raw_value_t &amp;amp;value);

    /**
     * @brief 判断一个值是否已经分配了寄存器
     * @param[in] value 值
     * @return 是否已经分配了寄存器
     */
    bool exist(const koopa_raw_value_t &amp;amp;value);

    /**
     * @brief 给一个值分配一个寄存器, 自动选择一个未被占用的寄存器
     * @note x0 是一个特殊的寄存器, 它的值恒为 0, 且向它写入的任何数据都会被丢弃, t0 到 t6 寄存器, 以及 a0 到 a7 寄存器可以用来存放临时值
     * @param[in] value 值
     * @param[in] is_zero 如果是立即数, 那么是否是立即数 0
     */
    void allocate_reg(const koopa_raw_value_t &amp;amp;value, bool is_zero = false);

    /**
     * @brief 找出这个值占用哪个寄存器, 用于输出 RISC-V 汇编代码
     * @param[in] value 值
     * @return 寄存器名称
     */
    std::string value_to_reg_string(const koopa_raw_value_t &amp;amp;value);
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;risc-v-assembly-code-output&quot;&gt;&lt;code&gt;RISC-V&lt;/code&gt; Assembly Code Output&lt;a class=&quot;anchor&quot; href=&quot;#risc-v-assembly-code-output&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;为了方便维护 &lt;code&gt;RegisterManager&lt;/code&gt; 类, 我选择将 &lt;code&gt;koopa_raw_value_t&lt;/code&gt; 类型的指针也传给 &lt;code&gt;koopa_raw_integer_t&lt;/code&gt; 和 &lt;code&gt;koopa_raw_binary_t&lt;/code&gt; 的 &lt;code&gt;visit&lt;/code&gt; 函数, 这样在这两个函数中就可以调用 &lt;code&gt;RegisterManager&lt;/code&gt; 类的方法来管理寄存器了.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;// 访问 koopa_raw_value_t
void visit(const koopa_raw_value_t &amp;amp;value)
{
    const auto &amp;amp;kind = value-&amp;gt;kind;
    switch (kind.tag)
    {
    case KOOPA_RVT_RETURN:
        visit(kind.data.ret);
        break;
    case KOOPA_RVT_INTEGER:
        visit(kind.data.integer, value);
        break;
    case KOOPA_RVT_BINARY:
        visit(kind.data.binary, value);
        break;
    default:
        throw std::runtime_error(&amp;quot;visit: invalid instruction&amp;quot;);
    }
}

// 访问 return 指令
void visit(const koopa_raw_return_t &amp;amp;ret)
{
    // 根据 ret 的 value 类型判断后续需要如何访问
    if (ret.value)
    {
        // 特判如果是立即数, 则直接赋值给 a0 寄存器, 跳过访问 value 的过程
        if (ret.value-&amp;gt;kind.tag == KOOPA_RVT_INTEGER)
        {
            std::cout &amp;lt;&amp;lt; &amp;quot;\tli a0, &amp;quot; &amp;lt;&amp;lt; ret.value-&amp;gt;kind.data.integer.value &amp;lt;&amp;lt; std::endl;
        }
        // 否则, 访问这个值, 然后把这个值存储在的寄存器名称移动给 a0 寄存器, 注意不是 li
        else
        {
            bool is_allocated = register_manager.exist(ret.value);
            if (!is_allocated)
            {
                visit(ret.value);
            }
            std::cout &amp;lt;&amp;lt; &amp;quot;\tmv a0, &amp;quot; &amp;lt;&amp;lt; register_manager.value_to_reg_string(ret.value) &amp;lt;&amp;lt; std::endl;
        }
    }
    // 如果 ret 的 value 为空, 则直接赋值 0 给 a0 寄存器, 然后返回
    else
    {
        std::cout &amp;lt;&amp;lt; &amp;quot;\tli a0, 0&amp;quot; &amp;lt;&amp;lt; std::endl;
    }
    std::cout &amp;lt;&amp;lt; &amp;quot;\tret&amp;quot; &amp;lt;&amp;lt; std::endl;
}

// 访问 integer
void visit(const koopa_raw_integer_t &amp;amp;integer, const koopa_raw_value_t &amp;amp;value)
{
    if (integer.value == 0)
    {
        register_manager.allocate_reg(value, true);
    }
    else
    {
        register_manager.allocate_reg(value);
        std::cout &amp;lt;&amp;lt; &amp;quot;\tli &amp;quot; &amp;lt;&amp;lt; register_manager.value_to_reg_string(value) &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; integer.value &amp;lt;&amp;lt; std::endl;
    }
}

// 访问 binary 指令
void visit(const koopa_raw_binary_t &amp;amp;binary, const koopa_raw_value_t &amp;amp;value)
{
    // lhs 和 rhs 是否已经分配过寄存器, 如果没分配过, 则需要先访问 lhs 和 rhs, 访问过程中会分配寄存器, 注意 ricsv 不能直接操作立即数, 必须先加载到寄存器中!
    bool lhs_is_allocated = register_manager.exist(binary.lhs);
    if (!lhs_is_allocated)
    {
        visit(binary.lhs);
    }
    bool rhs_is_allocated = register_manager.exist(binary.rhs);
    if (!rhs_is_allocated)
    {
        visit(binary.rhs);
    }
    // 我们认为每个结果仅使用一次, 所以可以设置两个子结果的寄存器可以被覆盖了.
    // 比如将立即数转移给 a0 寄存器, 我们现在就认为 a0 寄存器被占用了, 但是如果 a0 寄存器之后被调用了, 这个立即数被使用过了, 那么 a0 寄存器就会被标记为不被占用, 因为到目前为止我们认为每一个结果只被使用一次
    register_manager.set_reg_free(binary.lhs);
    register_manager.set_reg_free(binary.rhs);
    register_manager.allocate_reg(value);

    // 获取当前结果, lhs 和 rhs 对应的寄存器名称
    std::string cur = register_manager.value_to_reg_string(value);
    std::string lhs = register_manager.value_to_reg_string(binary.lhs);
    std::string rhs = register_manager.value_to_reg_string(binary.rhs);

    // 根据二元运算符的类型进行处理
    switch (binary.op)
    {
    case KOOPA_RBO_EQ:
        std::cout &amp;lt;&amp;lt; &amp;quot;\txor &amp;quot; &amp;lt;&amp;lt; cur &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; lhs &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; rhs &amp;lt;&amp;lt; std::endl;
        std::cout &amp;lt;&amp;lt; &amp;quot;\tseqz &amp;quot; &amp;lt;&amp;lt; cur &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; cur &amp;lt;&amp;lt; std::endl;
        break;
    case KOOPA_RBO_NOT_EQ:
        std::cout &amp;lt;&amp;lt; &amp;quot;\txor &amp;quot; &amp;lt;&amp;lt; cur &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; lhs &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; rhs &amp;lt;&amp;lt; std::endl;
        std::cout &amp;lt;&amp;lt; &amp;quot;\tsnez &amp;quot; &amp;lt;&amp;lt; cur &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; cur &amp;lt;&amp;lt; std::endl;
        break;
    // ...
    default:
        throw std::runtime_error(&amp;quot;visit: invalid binary operator&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Possible Issues&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-green-500 bg-green-50 dark:bg-green-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-green-700 dark:text-green-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Possible Issues&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;如果无法通过全部 Lv3 的测试样例, 可以检查如下几个问题:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;把数据从寄存器移动到寄存器时, 使用的是 &lt;code&gt;mv&lt;/code&gt; 指令, 而不是 &lt;code&gt;li&lt;/code&gt; 指令.&lt;/li&gt;
&lt;li&gt;访问 &lt;code&gt;return&lt;/code&gt; 指令时, 在访问 &lt;code&gt;ret.value&lt;/code&gt; 之前有没有检查它是否已经分配了寄存器, 否则可能造成同一个运算被输出多次.&lt;/li&gt;
&lt;li&gt;还有一个测试样例, 可以检查你的寄存器是否会溢出:&lt;/li&gt;
&lt;/ul&gt;&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() 
{
  return 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0 + 0 * 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;compile-and-test-3&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv3 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;riscv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv3 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lv-4&quot;&gt;Lv 4&lt;a class=&quot;anchor&quot; href=&quot;#lv-4&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本节需要让你的编译器可以处理变量的声明和定义, 用例如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() {
  const int x = 233 * 4;
  int y = 10;
  y = y + x / 2;
  return y;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;整体来讲, 有如下几个重点:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;koopa&lt;/code&gt; 部分需要增加符号表来管理变量.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;koopa&lt;/code&gt; 部分需要完成 &lt;code&gt;const&lt;/code&gt; 变量的编译期求值.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;riscv&lt;/code&gt; 部分在处理不同条 &lt;code&gt;koopa&lt;/code&gt; 指令间, 只需要维护栈帧.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;riscv&lt;/code&gt; 部分在处理同一条 &lt;code&gt;koopa&lt;/code&gt; 指令时, 需要维护寄存器.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;koopa-symbol-table&quot;&gt;&lt;code&gt;Koopa&lt;/code&gt; Symbol Table&lt;a class=&quot;anchor&quot; href=&quot;#koopa-symbol-table&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;如果是&lt;strong&gt;常量定义&lt;/strong&gt;, 比如 &lt;code&gt;int x = 233;&lt;/code&gt;, 它不需要 &lt;code&gt;koopa&lt;/code&gt; 指令来完成.&lt;/p&gt;
&lt;p&gt;你只需要在符号表中记录 &lt;code&gt;@x&lt;/code&gt; 这个变量和 &lt;code&gt;233&lt;/code&gt; 这个立即数, 当其他命令调用变量 &lt;code&gt;x&lt;/code&gt; 时, 比如 &lt;code&gt;y = x;&lt;/code&gt;, 从符号表中找到 &lt;code&gt;@x&lt;/code&gt; 这个变量对应的数值, 然后直接使用 &lt;code&gt;store 233, @y&lt;/code&gt; 指令把 &lt;code&gt;@x&lt;/code&gt; 这个变量的值加载到寄存器中给这个指令用.&lt;/p&gt;
&lt;p&gt;如果是&lt;strong&gt;变量定义&lt;/strong&gt;, 比如 &lt;code&gt;int y = x + 1;&lt;/code&gt;, 它需要若干 &lt;code&gt;koopa&lt;/code&gt; 指令来完成 (假设 &lt;code&gt;x&lt;/code&gt; 不是常量, 需要从内存中加载):&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;%0 = load @x
add %0, 1
@y = alloc i32
store %0, @y&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;你需要在符号表中记录 &lt;code&gt;@y&lt;/code&gt; 这个变量, 当其他命令调用变量 &lt;code&gt;y&lt;/code&gt; 时, 检查符号表中是否有 &lt;code&gt;@y&lt;/code&gt; 这个变量, 如果有, 则使用 &lt;code&gt;%0 = load @y&lt;/code&gt; 指令把 &lt;code&gt;@y&lt;/code&gt; 这个变量的值加载到寄存器中给这个指令用.&lt;/p&gt;
&lt;p&gt;综上所述:&lt;/p&gt;
&lt;p&gt;符号表的 &lt;code&gt;key&lt;/code&gt; 就是 &lt;code&gt;@&lt;/code&gt; 开头的内存名, &lt;code&gt;value&lt;/code&gt; 是 &lt;code&gt;i32&lt;/code&gt; 类型的立即数 (常量定义) 或什么都没有 (变量定义, 但是如果你想保持一致性也可以把 &lt;code&gt;%&lt;/code&gt; 开头的寄存器名存下来, 在上面这个例子中是 &lt;code&gt;%0&lt;/code&gt; ; 不过在多层嵌套的块中, 我们可以把这个变量处在的定义域的层级用 &lt;code&gt;value&lt;/code&gt; 来传递, 具体可以参考 Lv5 的实现).&lt;/p&gt;
&lt;p&gt;符号和符号表&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/**
 * @brief 符号
 * @date 2024-12-22
 */
class Symbol
{
public:
    enum class Type
    {
        VAR,
        VAL
    };
    Type type;
    int val;
    Symbol() : type(Type::VAL), val(0) {}
    Symbol(Type type, int val) : type(type), val(val) {}
};

/**
 * @brief 符号表
 * @date 2024-12-22
 */
class SymbolTable
{
private:
    std::unordered_map&amp;lt;std::string, Symbol&amp;gt; symbol_table;
    bool is_returned = false;

public:
    void create(const std::string &amp;amp;name, Symbol symbol);
    bool exist(const std::string &amp;amp;name);
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;在常量的定义中维护符号表:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;Result ConstDefAST::print(std::stringstream &amp;amp;output_stream) const
{
    if (symbol_table.exist(const_symbol)) // 如果符号表中已经存在这个符号, 则抛出错误
    {
        throw std::runtime_error(&amp;quot;ConstDefAST::print: const identifier already exists&amp;quot;);
    }
    Result value_result = const_init_val-&amp;gt;print(output_stream); // 计算常量表达式的值
    symbol_table.create(const_symbol, Symbol(Symbol::Type::VAL, value_result.val)); // 将常量表达式的值存入符号表
    return Result(); // 返回空结果, 为什么不返回调用 print 的返回值? 因为我们的先验知识 (语义规范) 告诉我们, 声明和定义语句不会返回任何值
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;koopa-const-variable-compile-time-computation&quot;&gt;&lt;code&gt;Koopa&lt;/code&gt; Const Variable Compile-Time Computation&lt;a class=&quot;anchor&quot; href=&quot;#koopa-const-variable-compile-time-computation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() {
  const int x = 1 + 1;
  return x;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样一个返回常量的代码, 我们直接返回 &lt;code&gt;2&lt;/code&gt; 即可:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;fun @main(): i32 {
%entry:
  ret 2
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;可以看到 &lt;code&gt;1 + 1&lt;/code&gt; 在编译期就被求值为 &lt;code&gt;2&lt;/code&gt; 了, 那么如何完成 &lt;code&gt;const&lt;/code&gt; 变量的编译期求值呢?&lt;/p&gt;
&lt;p&gt;我们只需要在访问每一个计算节点, 比如 &lt;code&gt;AddExpAST::print&lt;/code&gt; 的时候判断它的左右操作数是不是都是立即数, 如果是立即数就返回计算完的立即数, 如果不是立即数才需要 &lt;code&gt;%1 = add %0, 1&lt;/code&gt; 这样的计算指令.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;Result AddExpAST::print(std::stringstream &amp;amp;output_stream) const
{
    if (!add_exp &amp;amp;&amp;amp; !op &amp;amp;&amp;amp; mul_exp)
    {
        return (*mul_exp)-&amp;gt;print(output_stream);
    }
    else if (add_exp &amp;amp;&amp;amp; op &amp;amp;&amp;amp; mul_exp)
    {
        Result result_left = (*add_exp)-&amp;gt;print(output_stream);
        Result result_right = (*mul_exp)-&amp;gt;print(output_stream);
        if (result_left.type == Result::Type::IMM &amp;amp;&amp;amp; result_right.type == Result::Type::IMM)
        {
            if (*op == &amp;quot;+&amp;quot;)
            {
                return Result(Result::Type::IMM, result_left.val + result_right.val);
            }
            else if (*op == &amp;quot;-&amp;quot;)
            {
                return Result(Result::Type::IMM, result_left.val - result_right.val);
            }
            else
            {
                throw std::runtime_error(&amp;quot;AddExpAST::print: invalid add operator when both operands are immediate&amp;quot;);
            }
        }
        else
        {
            Result result = Result(Result::Type::REG);
            if (*op == &amp;quot;+&amp;quot;)
            {
                output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &amp;quot; = add &amp;quot; &amp;lt;&amp;lt; result_left &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; result_right &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
            }
            else if (*op == &amp;quot;-&amp;quot;)
            {
                output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &amp;quot; = sub &amp;quot; &amp;lt;&amp;lt; result_left &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; result_right &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
            }
            else
            {
                throw std::runtime_error(&amp;quot;AddExpAST::print: invalid add operator when one of the operands is not immediate&amp;quot;);
            }
            return result;
        }
    }
    else
    {
        throw std::runtime_error(&amp;quot;AddExpAST::print: invalid add expression&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;可以看出, 如果 &lt;code&gt;AddExpAST::print&lt;/code&gt; 的左右操作数都是立即数, 那么 &lt;code&gt;AddExpAST::print&lt;/code&gt; 的返回值就是立即数, 否则就是寄存器.&lt;/p&gt;
&lt;p&gt;这样我们避免了已经知道左右操作数的真实数值的情况下依然生成 &lt;code&gt;add&lt;/code&gt; 指令, 从而实现了编译期求值, 同时这是一个递归的过程, 所以仅需要修改很少的代码就可以实现.&lt;/p&gt;
&lt;h3 id=&quot;risc-v-stack-frame&quot;&gt;&lt;code&gt;RISC-V&lt;/code&gt; Stack Frame&lt;a class=&quot;anchor&quot; href=&quot;#risc-v-stack-frame&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在处理不同条 &lt;code&gt;koopa&lt;/code&gt; 指令间只需要维护栈帧而不需要维护寄存器, 具体来讲, 在使用到 &lt;code&gt;@x&lt;/code&gt; 或 &lt;code&gt;%1&lt;/code&gt; 等&lt;strong&gt;所有&lt;/strong&gt; &lt;code&gt;koopa&lt;/code&gt; 变量和内存时维护栈帧.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@x&lt;/code&gt; 存在栈帧上是没有问题的, 毕竟 &lt;code&gt;koopa&lt;/code&gt; 就是这么做的.&lt;/p&gt;
&lt;p&gt;但是 &lt;code&gt;%1&lt;/code&gt; 这样的 &lt;code&gt;koopa&lt;/code&gt; 寄存器应该存在哪里呢? 我们为了简化寄存器分配, 因此将 &lt;code&gt;koopa&lt;/code&gt; 的所有寄存器计算出来之后, 也存在 &lt;code&gt;riscv&lt;/code&gt; 的栈帧上, 如果这个 &lt;code&gt;%1&lt;/code&gt; 之后被使用了, 就从栈帧中找到 &lt;code&gt;%1&lt;/code&gt; 对应的内存即可.&lt;/p&gt;
&lt;p&gt;可以看下面的例子, &lt;code&gt;%2 = load @y&lt;/code&gt; 计算出来了 &lt;code&gt;%2&lt;/code&gt; 的值, 我们的操作是把它存在了 &lt;code&gt;sp + 12&lt;/code&gt; 这个位置, 然后 &lt;code&gt;ret %2&lt;/code&gt; 的时候从 &lt;code&gt;sp + 12&lt;/code&gt; 这个位置取出 &lt;code&gt;%2&lt;/code&gt; 的值, 从而在不同 &lt;code&gt;koopa&lt;/code&gt; 指令间通过栈帧传递信息, 完全不使用寄存器.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;	.text
	.globl main
main:
	addi sp, sp, -16

  # store 10, @y
	li t0, 10
	sw t0, 0(sp)

  # %0 = load @y
	lw t0, 0(sp)
	sw t0, 4(sp) 

  # %1 = add %0, 466
	lw t0, 4(sp)
	li t1, 466
	add t0, t0, t1
	sw t0, 8(sp)

  # store %1, @y
	lw t0, 8(sp)
	sw t0, 0(sp)

  # %2 = load @y
	lw t0, 0(sp)
	sw t0, 12(sp)

  # ret %2
	lw a0, 12(sp)
	addi sp, sp, 16
    ret&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;我使用了 &lt;code&gt;ContextManager&lt;/code&gt; 来管理栈帧和寄存器, 这是从 &lt;code&gt;Lv3&lt;/code&gt; 的 &lt;code&gt;RegisterManager&lt;/code&gt; 加入了栈帧的管理器得到的.&lt;/p&gt;
&lt;p&gt;所有代码共用一个 &lt;code&gt;ContextManager&lt;/code&gt;, 每一个函数在进入的时候单开一个 &lt;code&gt;StackManager&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;具体来讲:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/**
 * @brief 寄存器和所有函数的栈管理器, 是全局共用的, 可以维护值和寄存器的关系, 可以维护一个值和这个值对应的函数的栈信息
 * @author Yutong Liang
 * @date 2024-11-28
 */
class ContextManager
{
private:
    // 值到寄存器名称的映射
    std::unordered_map&amp;lt;koopa_raw_value_t, std::string&amp;gt; _value_to_reg_string;

    // 存储当前所有寄存器是否有不能被覆盖的值
    std::unordered_map&amp;lt;std::string, bool&amp;gt; _reg_is_used;

    /**
     * @brief 设置一个值对应哪个寄存器, 内部函数不被外部调用
     * @param[in] value 值
     * @param[in] reg_string 寄存器名称
     * @author Yutong Liang
     * @date 2024-11-28
     */
    void _set_value_to_reg_string(const koopa_raw_value_t &amp;amp;value, const std::string &amp;amp;reg_string);

    // 函数名到这个函数的 StackManager 的映射
    std::unordered_map&amp;lt;std::string, StackManager&amp;gt; _function_name_to_stack_manager;

    // 当前正在处理的函数的函数名
    std::string current_function_name;
};

/**
 * @brief 单个函数的栈管理器, 是一个函数使用的, 可以维护值 (比如 `@x`, `%1`) 和栈地址的关系
 * @author Yutong Liang
 * @date 2024-12-22
 */
class StackManager
{
private:
    // 栈帧大小, 初始化的时候确定的, 单位是字节
    int stack_size;

    // 栈帧当前使用情况, 初始化时0, 直到增长为 stack_size 为止, 单位是字节
    int stack_used_byte;

    // 值到栈地址的映射, 栈地址的表示方法是 &amp;quot;sp + offset&amp;quot; 中的 int offset
    std::unordered_map&amp;lt;koopa_raw_value_t, int&amp;gt; value_to_stack_offset;
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;risc-v-register-allocation&quot;&gt;&lt;code&gt;RISC-V&lt;/code&gt; Register Allocation&lt;a class=&quot;anchor&quot; href=&quot;#risc-v-register-allocation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;aside aria-label=&quot;寄存器分配原则&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;寄存器分配原则&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;不同 &lt;code&gt;koopa&lt;/code&gt; 代码之间共享栈帧但是寄存器不互相影响, 同一行 &lt;code&gt;koopa&lt;/code&gt; 代码之间才可能产生寄存器的影响, 每一条 &lt;code&gt;koopa&lt;/code&gt; 指令使用自己的寄存器然后释放自己的寄存器.&lt;/p&gt;&lt;p&gt;这是因为每一行 &lt;code&gt;koopa&lt;/code&gt; 代码只会使用 &lt;code&gt;@x&lt;/code&gt;, &lt;code&gt;%1&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt; 这样的值, 而这些值在 RISC-V 中要么在内存中, 要么就是立即数, 所以&lt;strong&gt;任意两个 &lt;code&gt;koopa&lt;/code&gt; 指令之间是不会产生寄存器复用的&lt;/strong&gt;.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;有了这个原则, 我们在实现的时候就可以安心地使用寄存器了.&lt;/p&gt;
&lt;p&gt;在访问 &lt;code&gt;koopa_raw_value_t&lt;/code&gt; 的时候, 只访问对应 &lt;code&gt;koopa&lt;/code&gt; 指令的抽象语法树, 不对应的不访问, 比如立即数就不要访问, 因为这样安排逻辑清晰, 保证了寄存器使用的解耦, 两个 &lt;code&gt;visit&lt;/code&gt; 函数之间没有寄存器依赖关系, 同时 &lt;code&gt;switch&lt;/code&gt; 中每一个 &lt;code&gt;visit&lt;/code&gt; 函数都会使用后释放自己的寄存器.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;// 访问指令
void visit(const koopa_raw_value_t &amp;amp;value)
{
    const auto &amp;amp;kind = value-&amp;gt;kind;
    switch (kind.tag)
    {
    case KOOPA_RVT_RETURN:
        // 访问 return 指令
        visit(kind.data.ret);
        break;
    case KOOPA_RVT_BINARY:
        // 访问 binary 计算指令
        visit(kind.data.binary, value);
        break;
    case KOOPA_RVT_ALLOC:
        // 访问 alloc 指令, 分配内存是实际存在的指令, 但是汇编语言的内存分配是直接用栈指针管理的, 所以不需要显式的内存分配, 可以忽略 koopa 的 alloc 指令
        // 比如 @x = alloc i32 这样一个指令, 如果只有这个指令本身, 并不需要做任何操作也能保证 RISC-V 的正确性
        // 只有在使用了 @x 的时候, 比如 store 10, @x 这样的指令, 才需要设定 @x 的栈地址, 所以 alloc 指令可以忽略
        break;
    case KOOPA_RVT_LOAD:
        // 访问 load 指令
        visit(kind.data.load, value);
        break;
    case KOOPA_RVT_STORE:
        // 访问 store 指令
        visit(kind.data.store, value);
        break;
    default:
        // 其他类型暂时遇不到
        throw std::runtime_error(&amp;quot;visit: invalid instruction&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;比如 &lt;code&gt;%1 = add %0, 1&lt;/code&gt; 这样的指令, 只访问 &lt;code&gt;add&lt;/code&gt; 指令, 在访问 &lt;code&gt;add&lt;/code&gt; 的时候不调用 &lt;code&gt;visit( 立即数 1 )&lt;/code&gt; , 参考如下 7-17 行代码, 而是直接将 &lt;code&gt;1&lt;/code&gt; 加载到寄存器中, 因为立即数不是 &lt;code&gt;koopa&lt;/code&gt; 指令, 如果访问了就违反了我们最开始的原则了.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;// 访问 binary 指令
void visit(const koopa_raw_binary_t &amp;amp;binary, const koopa_raw_value_t &amp;amp;value)
{
    // 判断 lhs 是立即数还是内存, 如果是立即数就 li, 否则就 lw
    context_manager.allocate_reg(binary.lhs);
    std::string lhs = context_manager.value_to_reg_string(binary.lhs);
    if (binary.lhs-&amp;gt;kind.tag == KOOPA_RVT_INTEGER) // 这里不要调用 visit( 立即数 1 ), 因为这不是一个 koopa 指令
    {
        riscv_printer.li(lhs, binary.lhs-&amp;gt;kind.data.integer.value);
    }
    else
    {
        // 当前函数的 StackManager
        StackManager &amp;amp;stack_manager = context_manager.get_current_function_stack_manager();
        // 从栈中加载数据到寄存器
        riscv_printer.lw(lhs, &amp;quot;sp&amp;quot;, stack_manager.get_value_stack_offset(binary.lhs), context_manager);
    }
    // 判断 rhs 是立即数还是内存, 如果是立即数就 li, 否则就 lw
    context_manager.allocate_reg(binary.rhs);
    std::string rhs = context_manager.value_to_reg_string(binary.rhs);
    if (binary.rhs-&amp;gt;kind.tag == KOOPA_RVT_INTEGER)
    {
        riscv_printer.li(rhs, binary.rhs-&amp;gt;kind.data.integer.value);
    }
    else
    {
        // 当前函数的 StackManager
        StackManager &amp;amp;stack_manager = context_manager.get_current_function_stack_manager();
        // 从栈中加载数据到寄存器
        riscv_printer.lw(rhs, &amp;quot;sp&amp;quot;, stack_manager.get_value_stack_offset(binary.rhs), context_manager);
    }

    // 给结果分配一个寄存器, 分配之前可以先释放掉 lhs 和 rhs 对应的寄存器, 因为他们相当于已经加载进来了, 一会使用的时候可以覆盖, 比如 add t0, t0, t1
    context_manager.set_reg_free(binary.lhs);
    context_manager.set_reg_free(binary.rhs);
    context_manager.allocate_reg(value);
    std::string cur = context_manager.value_to_reg_string(value);

    // 根据二元运算符的类型进行处理
    switch (binary.op)
    {
    case KOOPA_RBO_EQ:
        riscv_printer.xor_(cur, lhs, rhs);
        riscv_printer.seqz(cur, cur);
        break;
    // ...
    default:
        throw std::runtime_error(&amp;quot;visit: invalid binary operator&amp;quot;);
    }
    // 当前函数的 StackManager
    StackManager &amp;amp;stack_manager = context_manager.get_current_function_stack_manager();
    // 把结果存回栈中
    stack_manager.save_value_to_stack(value);
    riscv_printer.sw(cur, &amp;quot;sp&amp;quot;, stack_manager.get_value_stack_offset(value), context_manager);
    // 当前结果所在的寄存器已经被使用过了, 释放
    context_manager.set_reg_free(value);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;compile-and-test-4&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-4&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv4 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;riscv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv4 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lv5&quot;&gt;Lv5&lt;a class=&quot;anchor&quot; href=&quot;#lv5&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这是一个非常简单的 Level 呀, 只需要在 Lv4 的基础上将一个 &lt;code&gt;SymbolTable&lt;/code&gt; 变为多个就可以了.&lt;/p&gt;
&lt;h3 id=&quot;multiple-symbol-table&quot;&gt;Multiple Symbol Table&lt;a class=&quot;anchor&quot; href=&quot;#multiple-symbol-table&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Lv4 中的 &lt;code&gt;SymbolTable&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;class SymbolTable
{
private:
    std::unordered_map&amp;lt;std::string, Symbol&amp;gt; symbol_table;
    bool is_returned = false;

public:
    // CRUD ...
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lv5 中的 &lt;code&gt;SymbolTable&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;class SymbolTable
{
private:
    std::vector&amp;lt;std::unordered_map&amp;lt;std::string, Symbol&amp;gt;&amp;gt; symbol_table; // 每进入一个块, 就创建一个新的符号表, 块包括函数的大括号和语句块的大括号
    bool is_returned = false;

public:
    // 每进入一个块, 就创建一个新的符号表, 块包括函数的大括号和语句块的大括号
    void new_symbol_table_hierarchy();
    // 每离开一个块, 就删除一个符号表
    void delete_symbol_table_hierarchy();
    // CRUD ...
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;区别在于我们把 &lt;code&gt;SymbolTable&lt;/code&gt; 从单个变为多个, 构建为一个栈, 每进入一个块, 就创建一个新的符号表, 每离开一个块, 就删除一个符号表.&lt;/p&gt;
&lt;p&gt;同时我们需要修改内存的存储位置, 因为不同层的定义域可能使用同一个变量名 (例子如下), 此时应该从内向外找到最近的变量名然后使用这一层的内存地址.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;fun @main(): i32 {
%entry:
  @a_1 = alloc i32
  store 1, @a_1
  store 2, @a_1
  @a_2 = alloc i32
  store 3, @a_2
  %0 = load @a_1
  ret %0
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;naming-same-symbol-from-different-scope&quot;&gt;Naming Same Symbol from Different Scope&lt;a class=&quot;anchor&quot; href=&quot;#naming-same-symbol-from-different-scope&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;那么具体应该如何命名呢? 我们恰巧可以使用这个变量处于的 &lt;code&gt;vector&lt;/code&gt; 的 &lt;code&gt;index&lt;/code&gt; 来命名, 比如第一个 &lt;code&gt;@a&lt;/code&gt; 处于 &lt;code&gt;symbol_table[0]&lt;/code&gt; 中, 那么我们就把访问第一层中的 &lt;code&gt;@a&lt;/code&gt; 的内存地址表示为 &lt;code&gt;@a_1&lt;/code&gt;, 第二个 &lt;code&gt;@a&lt;/code&gt; 处于 &lt;code&gt;symbol_table[1]&lt;/code&gt; 中, 那么我们就把访问第二层中的 &lt;code&gt;@a&lt;/code&gt; 的内存地址表示为 &lt;code&gt;@a_2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;具体实现可以参考:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;class Symbol
{
public:
    enum class Type
    {
        VAR,
        VAL
    };
    Type type;
    int val; // 如果 type 是 VAL, 那么 val 是立即数的数值; 如果 type 是 VAR, 那么 val 是变量的层级, 比如 `a = 2;` 如果在符号表中在层级 1 找到这个符号, 那么就会返回 1, 得到 @a_1
    Symbol() : type(Type::VAL), val(0) {}
    Symbol(Type type, int val) : type(type), val(val) {}
};

Symbol SymbolTable::read(const std::string &amp;amp;name)
{
    for (int i = symbol_table.size() - 1; i &amp;gt;= 0; --i)
    {
        if (symbol_table[i].find(name) != symbol_table[i].end())
        {
            Symbol symbol = symbol_table[i].at(name);
            if (symbol.type == Symbol::Type::VAL)
            {
                return symbol; // 如果是常量, 直接返回常量的值
            }
            else if (symbol.type == Symbol::Type::VAR)
            {
                return Symbol(Symbol::Type::VAR, i + 1); // 如果是变量, 返回变量所在的 SymbolTable 的 index
            }
            else
            {
                throw std::runtime_error(&amp;quot;SymbolTable::read: invalid symbol type&amp;quot;);
            }
        }
    }
    throw std::runtime_error(&amp;quot;SymbolTable::read: identifier does not exist&amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果是 &lt;code&gt;Symbol::Type::VAR&lt;/code&gt; , 我们可以正好使用 &lt;code&gt;Symbol::val&lt;/code&gt; 来表示它处于的 &lt;code&gt;symbol_table&lt;/code&gt; 的 &lt;code&gt;index&lt;/code&gt; , 毕竟这个 &lt;code&gt;Symbol&lt;/code&gt; 初始化的时候 &lt;code&gt;Symbol::val&lt;/code&gt; 就是没用的, 可以参考如下的初始化代码和它的 Context:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;Result VarDefAST::print(std::stringstream &amp;amp;output_stream) const
{
    if (var_init_val)
    {
        Result value_result = (*var_init_val)-&amp;gt;print(output_stream);
        symbol_table.insert_symbol(var_symbol, Symbol(Symbol::Type::VAR, value_result.val));
        std::string symbol_name = var_symbol;
        std::string suffix = std::to_string(symbol_table.read(symbol_name).val);
        std::string symbol_name_with_suffix = symbol_name + &amp;quot;_&amp;quot; + suffix;
        output_stream &amp;lt;&amp;lt; &amp;quot;\t@&amp;quot; &amp;lt;&amp;lt; symbol_name_with_suffix &amp;lt;&amp;lt; &amp;quot; = alloc i32\n&amp;quot;;
        output_stream &amp;lt;&amp;lt; &amp;quot;\tstore &amp;quot; &amp;lt;&amp;lt; value_result &amp;lt;&amp;lt; &amp;quot;, @&amp;quot; &amp;lt;&amp;lt; symbol_name_with_suffix &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
    }
    else
    {
        symbol_table.insert_symbol(var_symbol, Symbol(Symbol::Type::VAR, 0));
        std::string symbol_name = var_symbol;
        std::string suffix = std::to_string(symbol_table.read(symbol_name).val);
        std::string symbol_name_with_suffix = symbol_name + &amp;quot;_&amp;quot; + suffix;
        output_stream &amp;lt;&amp;lt; &amp;quot;\t@&amp;quot; &amp;lt;&amp;lt; symbol_name_with_suffix &amp;lt;&amp;lt; &amp;quot; = alloc i32\n&amp;quot;;
    }
    return Result();
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中第六行中用 &lt;code&gt;value_result.val&lt;/code&gt; 初始化了这个 &lt;code&gt;Symbol&lt;/code&gt; , 但是实际上这个值是 &lt;code&gt;koopa&lt;/code&gt; 寄存器的名称, 我们并不需要保存这个某个内存和它用了某个寄存器来初始化的关系, 所以我们可以用 &lt;code&gt;symbol_table&lt;/code&gt; 的 &lt;code&gt;index&lt;/code&gt; 来覆盖这个值, 没有影响.&lt;/p&gt;
&lt;p&gt;最后 &lt;code&gt;RISC-V&lt;/code&gt; 部分不需要修改, 因为 &lt;code&gt;koopa&lt;/code&gt; 代码的可用指令没有修改.&lt;/p&gt;
&lt;h3 id=&quot;compile-and-test-5&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-5&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv5 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;riscv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv5 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lv6&quot;&gt;Lv6&lt;a class=&quot;anchor&quot; href=&quot;#lv6&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这部分我们需要完成 &lt;code&gt;if&lt;/code&gt; 语句的编译, 示例如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() 
{
  int a = 1;
  if (a == 2 || a == 3) 
  {
    return 0;
  } 
  else 
  {
    return a + 1;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;有如下几个重点:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;生成 &lt;code&gt;Koopa&lt;/code&gt; 中间代码时解决分支语句在语法分析的时候产生的移入/归约冲突.&lt;/li&gt;
&lt;li&gt;生成 &lt;code&gt;Koopa&lt;/code&gt; 中间代码时多 &lt;code&gt;if&lt;/code&gt; 的编号问题.&lt;/li&gt;
&lt;li&gt;生成 &lt;code&gt;Koopa&lt;/code&gt; 中间代码时解决控制流提前结束的问题.&lt;/li&gt;
&lt;li&gt;生成 &lt;code&gt;Koopa&lt;/code&gt; 中间代码时解决同一层级多次分配相同名称的内存的问题.&lt;/li&gt;
&lt;li&gt;生成 &lt;code&gt;Koopa&lt;/code&gt; 中间代码时逻辑运算短路求值的特性.&lt;/li&gt;
&lt;li&gt;生成 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码时添加两条 &lt;code&gt;RISC-V&lt;/code&gt; 指令.&lt;/li&gt;
&lt;li&gt;生成 &lt;code&gt;RISC-V&lt;/code&gt; 汇编代码时容易出现的 bug 和解决方法, 主要是 12 位立即数溢出的问题.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;shiftreduce-conflict&quot;&gt;Shift/Reduce Conflict&lt;a class=&quot;anchor&quot; href=&quot;#shiftreduce-conflict&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;If&lt;/code&gt; 相关的语法规则如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;Stmt ::= &amp;quot;if&amp;quot; &amp;quot;(&amp;quot; Exp &amp;quot;)&amp;quot; Stmt [&amp;quot;else&amp;quot; Stmt]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;对于移入/归约冲突的原因分析可以参考 &lt;a href=&quot;https://pku-minic.github.io/online-doc/#/lv6-if/if-else&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Lv6 的 Lab 文档&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;, 这里不再赘述.&lt;/p&gt;
&lt;p&gt;为了避免这样的问题, &lt;code&gt;SysY&lt;/code&gt; 的语义规定了 &lt;code&gt;else&lt;/code&gt; 必须和最近的 &lt;code&gt;if&lt;/code&gt; 进行匹配, 助教在这里提示拆分可以解决问题, 那么具体怎么做呢?&lt;/p&gt;
&lt;p&gt;一个重要的观察是, 如果一个 &lt;code&gt;if ...&lt;/code&gt; 语句在语法分析后跟随了一个 &lt;code&gt;else ...&lt;/code&gt; 语句, 那么这个 &lt;code&gt;if ...&lt;/code&gt; 语句内部中所有可能出现的 &lt;code&gt;if ...&lt;/code&gt; 语句都必须是跟随 &lt;code&gt;else ...&lt;/code&gt; 语句的, 否则就和 &lt;code&gt;SysY&lt;/code&gt; 的语义规定冲突了.&lt;/p&gt;
&lt;p&gt;因此我们可以将原有的语法规则修改为:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;Stmt ::= &amp;quot;if&amp;quot; &amp;quot;(&amp;quot; Exp &amp;quot;)&amp;quot; Stmt
       | &amp;quot;if&amp;quot; &amp;quot;(&amp;quot; Exp &amp;quot;)&amp;quot; StmtWithElse &amp;quot;else&amp;quot; Stmt

StmtWithElse ::= &amp;quot;if&amp;quot; &amp;quot;(&amp;quot; Exp &amp;quot;)&amp;quot; StmtWithElse &amp;quot;else&amp;quot; StmtWithElse&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样就可以解决移入/归约冲突的问题了.&lt;/p&gt;
&lt;p&gt;在 Parser 中的参考实现:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/* ast.hpp */

class StmtAST : public BaseAST
{
public:
    enum class StmtType
    {
        Assign,
        Expression,
        Block,
        Return,
        If
    };
    StmtType stmt_type;
    std::optional&amp;lt;std::unique_ptr&amp;lt;BaseAST&amp;gt;&amp;gt; lval;             // 语句中的左值
    std::optional&amp;lt;std::unique_ptr&amp;lt;BaseAST&amp;gt;&amp;gt; exp;              // 语句中的表达式
    std::optional&amp;lt;std::unique_ptr&amp;lt;BaseAST&amp;gt;&amp;gt; block;            // 语句中的基本块, 其实是另一个用大括号包裹的语句块
    std::optional&amp;lt;std::unique_ptr&amp;lt;BaseAST&amp;gt;&amp;gt; inside_if_stmt;   // if ... 中的语句块
    std::optional&amp;lt;std::unique_ptr&amp;lt;BaseAST&amp;gt;&amp;gt; inside_else_stmt; // else ... 中的语句块

    Result print(std::stringstream &amp;amp;output_stream) const override;
};

/* sysy.y */

Stmt
  // Assign, Expression, Block, Return ...
  | IF &amp;#39;(&amp;#39; Exp &amp;#39;)&amp;#39; Stmt {
    // ...
  }
  | IF &amp;#39;(&amp;#39; Exp &amp;#39;)&amp;#39; StmtWithElse ELSE Stmt {
    // ...
  }
  ;

StmtWithElse
  // Assign, Expression, Block, Return ...
  | IF &amp;#39;(&amp;#39; Exp &amp;#39;)&amp;#39; StmtWithElse ELSE StmtWithElse {
    // ...
  }
  ;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;为什么我把 &lt;code&gt;StmtWithElse&lt;/code&gt; 和 &lt;code&gt;Stmt&lt;/code&gt; 的语法规则分开写, 重复写了一遍 &lt;code&gt;Stmt&lt;/code&gt; 中的其它内容呢?&lt;/p&gt;
&lt;p&gt;因为对于 &lt;code&gt;Stmt&lt;/code&gt; 来说, 无法在规约的时候传递一个参数说明这个 &lt;code&gt;Stmt&lt;/code&gt; 是有 &lt;code&gt;else ...&lt;/code&gt; 语句的, 还是没有 &lt;code&gt;else ...&lt;/code&gt; 语句的, 所以需要新开一个语法规则. 同时我又不想大幅修改 &lt;code&gt;Stmt&lt;/code&gt; 原始的语法规则, 想要保持前后的一致性, 所以只能复制一遍 &lt;code&gt;Stmt&lt;/code&gt; 中的其它语法规则到 &lt;code&gt;StmtWithElse&lt;/code&gt; 中了.&lt;/p&gt;
&lt;h3 id=&quot;multiple-if-statement&quot;&gt;Multiple If Statement&lt;a class=&quot;anchor&quot; href=&quot;#multiple-if-statement&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;不同的 &lt;code&gt;if&lt;/code&gt; 语句都有自己的 &lt;code&gt;%then&lt;/code&gt;, &lt;code&gt;%else&lt;/code&gt; 和 &lt;code&gt;%end&lt;/code&gt; 标签, 所以需要一个计数器来区分不同的 &lt;code&gt;if&lt;/code&gt; 语句, 这个标签只要遇见一次 &lt;code&gt;if ...&lt;/code&gt; 语句就加一, 这样就可以区分不同的 &lt;code&gt;if&lt;/code&gt; 语句了.&lt;/p&gt;
&lt;p&gt;具体的输出代码可以参考:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/* koopa.cpp */

koopa_context_manager.total_if_else_statement_count++; // 每遇见一次 if ... 语句, 就加一
std::string then_label = &amp;quot;%then_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);
std::string else_label = &amp;quot;%else_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);
std::string end_label = &amp;quot;%end_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);

Result exp_result = (*exp)-&amp;gt;print(output_stream);

output_stream &amp;lt;&amp;lt; &amp;quot;\tbr &amp;quot; &amp;lt;&amp;lt; exp_result &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; then_label &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; (inside_else_stmt ? else_label : end_label) &amp;lt;&amp;lt; std::endl;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;handle-control-flow-early-end&quot;&gt;Handle Control Flow Early End&lt;a class=&quot;anchor&quot; href=&quot;#handle-control-flow-early-end&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;先来看一个例子:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() 
{
  if (0) 
  {
    return 1;
  } 
  else 
  {
    return 2;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果我们 naive 地实现 &lt;code&gt;if&lt;/code&gt; 语句的 &lt;code&gt;koopa&lt;/code&gt; 输出, 比如这样:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/* koopa.cpp */

Result StmtAST::print(std::stringstream &amp;amp;output_stream) const
{
    if (stmt_type == StmtType::Assign)
    {
        // ...
    }
    else if (stmt_type == StmtType::Return)
    {
        // ...
    }
    else if (stmt_type == StmtType::Expression)
    {
        // ...
    }
    else if (stmt_type == StmtType::Block)
    {
        // ...
    }
    else if (stmt_type == StmtType::If)
    {
        koopa_context_manager.total_if_else_statement_count++;
        std::string then_label = &amp;quot;%then_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);
        std::string else_label = &amp;quot;%else_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);
        std::string end_label = &amp;quot;%end_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);

        // 计算表达式, 根据表达式结果跳转到不同的分支
        Result exp_result = (*exp)-&amp;gt;print(output_stream);
        output_stream &amp;lt;&amp;lt; &amp;quot;\tbr &amp;quot; &amp;lt;&amp;lt; exp_result &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; then_label &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; (inside_else_stmt ? else_label : end_label) &amp;lt;&amp;lt; std::endl;

        // 进入 if 语句块
        output_stream &amp;lt;&amp;lt; then_label &amp;lt;&amp;lt; &amp;quot;:&amp;quot; &amp;lt;&amp;lt; std::endl;
        Result result_if = (*inside_if_stmt)-&amp;gt;print(output_stream);
        output_stream &amp;lt;&amp;lt; &amp;quot;\tjump &amp;quot; &amp;lt;&amp;lt; end_label &amp;lt;&amp;lt; std::endl;

        // else 语句块
        Result result_else = Result();
        if (inside_else_stmt)
        {
            output_stream &amp;lt;&amp;lt; else_label &amp;lt;&amp;lt; &amp;quot;:&amp;quot; &amp;lt;&amp;lt; std::endl;
            result_else = (*inside_else_stmt)-&amp;gt;print(output_stream);
            output_stream &amp;lt;&amp;lt; &amp;quot;\tjump &amp;quot; &amp;lt;&amp;lt; end_label &amp;lt;&amp;lt; std::endl;
        }

        Result result = Result();
        return result;
    }
    else
    {
        throw std::runtime_error(&amp;quot;StmtAST::print: invalid statement&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;首先回忆基本块的定义:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;基本块 (basic block) 是编译领域的一个很常见的概念, 它指的是一系列指令的集合, 基本块满足:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只有一个入口点: 所有基本块中的指令如果要执行跳转, 只能跳到某个基本块的开头, 而不能跳到中间.&lt;/li&gt;
&lt;li&gt;只有一个出口点: 基本块中, 只有最后一条指令能进行控制流的转移, 也就是跳到其他基本块, 或者从函数中返回 (执行 return 操作).
基本块的存在可以简化很多编译过程中需要进行的分析, 所以 Koopa IR 要求函数中的指令必须预先按照基本块分类. 同时, Koopa IR 约定, 函数的第一个基本块为函数的入口基本块, 也就是执行函数时, 首先会执行第一个基本块中的指令.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么我们可能会得到这样的 &lt;code&gt;koopa&lt;/code&gt; 代码, 注意其中的 &lt;code&gt;%then_1&lt;/code&gt; 和 &lt;code&gt;%else_1&lt;/code&gt; 在返回后存在跳转到 &lt;code&gt;%end_1&lt;/code&gt; 的指令, 这和基本块的定义 (只有最后一条指令能进行控制流的转移) 冲突了, 从而会造成编译器后端的错误.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;fun @main(): i32 {
%entry:
    br 0, %then_1, %else_1
%then_1:
    ret 1
    jump %end_1
%else_1:
    ret 2
    jump %end_1
%end_1:
ret 0
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;所以我们需要在输出 &lt;code&gt;if&lt;/code&gt; 内部的 &lt;code&gt;jump&lt;/code&gt; 前判断改控制流是否提前被 &lt;code&gt;ret&lt;/code&gt; 打断了, 如果被打断了就不输出 &lt;code&gt;jump&lt;/code&gt; 了.&lt;/p&gt;
&lt;p&gt;一个非常直观的想法是在每一个 &lt;code&gt;print&lt;/code&gt; 函数的输出 &lt;code&gt;Result&lt;/code&gt; 中加入一个 &lt;code&gt;bool&lt;/code&gt; 变量, 表示这个语句以及其嵌套的语句内是否被 &lt;code&gt;ret&lt;/code&gt; 语句显式地返回了, 然后根据这个变量来决定是否输出 &lt;code&gt;jump&lt;/code&gt; 指令, 同时还可以提前停止输出, 提高代码运行效率.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/* koopa.cpp */

Result StmtAST::print(std::stringstream &amp;amp;output_stream) const
{
    if (stmt_type == StmtType::Assign)
    {
        // ...
    }
    else if (stmt_type == StmtType::Return)
    {
        if (!lval &amp;amp;&amp;amp; exp &amp;amp;&amp;amp; !block)
        {
            Result result = (*exp)-&amp;gt;print(output_stream);
            output_stream &amp;lt;&amp;lt; &amp;quot;\tret &amp;quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
            result.control_flow_returned = true; // 如果语句返回了, 就设置该控制流返回
            return result;
        }
        else if (!lval &amp;amp;&amp;amp; !exp &amp;amp;&amp;amp; !block)
        {
            output_stream &amp;lt;&amp;lt; &amp;quot;\tret\n&amp;quot;;
            Result result = Result();
            result.control_flow_returned = true; // 如果语句返回了, 就设置该控制流返回
            return result;
        }
        else
        {
            throw std::runtime_error(&amp;quot;StmtAST::print: invalid return statement&amp;quot;);
        }
    }
    else if (stmt_type == StmtType::Expression)
    {
        // ...
    }
    else if (stmt_type == StmtType::Block)
    {
        if (!lval &amp;amp;&amp;amp; !exp &amp;amp;&amp;amp; block)
        {
            Result result = (*block)-&amp;gt;print(output_stream);
            return result;
        }
        else
        {
            throw std::runtime_error(&amp;quot;StmtAST::print: invalid block statement&amp;quot;);
        }
    }
    else if (stmt_type == StmtType::If)
    {
        koopa_context_manager.total_if_else_statement_count++;
        std::string then_label = &amp;quot;%then_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);
        std::string else_label = &amp;quot;%else_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);
        std::string end_label = &amp;quot;%end_&amp;quot; + std::to_string(koopa_context_manager.total_if_else_statement_count);

        Result exp_result = (*exp)-&amp;gt;print(output_stream);
        if (!inside_if_stmt &amp;amp;&amp;amp; !inside_else_stmt)
        {
            throw std::runtime_error(&amp;quot;StmtAST::print: invalid if statement, there&amp;#39;s no if&amp;quot;);
        }

        output_stream &amp;lt;&amp;lt; &amp;quot;\tbr &amp;quot; &amp;lt;&amp;lt; exp_result &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; then_label &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; (inside_else_stmt ? else_label : end_label) &amp;lt;&amp;lt; std::endl;

        // if 语句块
        output_stream &amp;lt;&amp;lt; then_label &amp;lt;&amp;lt; &amp;quot;:&amp;quot; &amp;lt;&amp;lt; std::endl;
        Result result_if = (*inside_if_stmt)-&amp;gt;print(output_stream);

        // 如果 if 语句块显式的返回了, 就不要跳转了, 否则输出这样的 koopa 代码是错误的:
        // fun @main(): i32 {
        // %entry:
        //     br 0, %then_1, %else_1
        // %then_1:
        //     ret 1
        //     jump %end_1
        // %else_1:
        //     ret 2
        //     jump %end_1
        // %end_1:
        // }
        if (!result_if.control_flow_returned)
        {
            output_stream &amp;lt;&amp;lt; &amp;quot;\tjump &amp;quot; &amp;lt;&amp;lt; end_label &amp;lt;&amp;lt; std::endl;
        }

        // else 语句块
        Result result_else = Result();
        if (inside_else_stmt)
        {
            output_stream &amp;lt;&amp;lt; else_label &amp;lt;&amp;lt; &amp;quot;:&amp;quot; &amp;lt;&amp;lt; std::endl;

            result_else = (*inside_else_stmt)-&amp;gt;print(output_stream);

            // 如果 else 语句块显式的返回了, 就不要跳转了
            if (!result_else.control_flow_returned)
            {
                output_stream &amp;lt;&amp;lt; &amp;quot;\tjump &amp;quot; &amp;lt;&amp;lt; end_label &amp;lt;&amp;lt; std::endl;
            }
        }

        // 如果 if 语句块和 else 语句块都返回了, 则注明整个 if ... else ... 语句块返回了
        // 但是为了避免这样的空 %end , 如果已经结束了就不输出 %end 了
        // fun @main(): i32 {
        // %entry:
        // 	   br 0, %then_1, %else_1
        // %then_1:
        //     ret 1
        // %else_1:
        //     ret 2
        // %end_1:
        // }
        Result result = Result();
        if (!result_if.control_flow_returned || !result_else.control_flow_returned)
        {
            output_stream &amp;lt;&amp;lt; end_label &amp;lt;&amp;lt; &amp;quot;:&amp;quot; &amp;lt;&amp;lt; std::endl;
        }
        else
        {
            result.control_flow_returned = true; // 如果是 if ... else ... 语句, 则 if ... 和 else ... 语句块都返回了才设置整体函数返回
        }
        return result;
    }
    else
    {
        throw std::runtime_error(&amp;quot;StmtAST::print: invalid statement&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;最后给 &lt;code&gt;FuncDefAST&lt;/code&gt; 的 &lt;code&gt;print&lt;/code&gt; 函数加上控制流返回的判断, 如果 &lt;code&gt;block&lt;/code&gt; 没有显式的 &lt;code&gt;ret&lt;/code&gt; 指令, 则补上一个 &lt;code&gt;ret 0&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/* koopa.cpp */

Result FuncDefAST::print(std::stringstream &amp;amp;output_stream) const
{
    output_stream &amp;lt;&amp;lt; &amp;quot;fun @&amp;quot; &amp;lt;&amp;lt; ident &amp;lt;&amp;lt; &amp;quot;(): &amp;quot;;
    func_type-&amp;gt;print(output_stream);
    output_stream &amp;lt;&amp;lt; &amp;quot; {&amp;quot; &amp;lt;&amp;lt; std::endl;
    output_stream &amp;lt;&amp;lt; &amp;quot;%entry:&amp;quot; &amp;lt;&amp;lt; std::endl;
    Result result = block-&amp;gt;print(output_stream);
    // 如果 block 没有显式的 ret 指令, 则补上一个 ret 0
    if (!result.control_flow_returned)
    {
        output_stream &amp;lt;&amp;lt; &amp;quot;\tret 0&amp;quot; &amp;lt;&amp;lt; std::endl;
    }
    output_stream &amp;lt;&amp;lt; &amp;quot;}&amp;quot; &amp;lt;&amp;lt; std::endl;
    return result;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;handle-same-variable-name-in-same-level&quot;&gt;Handle Same Variable Name in Same Level&lt;a class=&quot;anchor&quot; href=&quot;#handle-same-variable-name-in-same-level&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;一个例子:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main()
{
    {
        int a = 2;
    }
    {
        int a = 3;
    }
    return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;如果按照我们之前的处理方式, 会得出这样的 &lt;code&gt;koopa&lt;/code&gt; 代码:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;fun @main(): i32 {
%entry:
    @a_2 = alloc i32
    store 2, @a_2
    @a_2 = alloc i32
    sstore 3, @a_2
    ret 0
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这下就出问题了, 因为 &lt;code&gt;@a_2&lt;/code&gt; 被分配了两次. 但是这件事情很好处理, 只要维护一个 &lt;code&gt;std::map&amp;lt;std::pair&amp;lt;std::string, int&amp;gt;, int&amp;gt;&lt;/code&gt; 来记录每个变量名在每个层级中是否已经分配了, 然后每次分配变量的时候, 先检查这个变量名是否已经存在, 如果存在就使用这个变量的地址, 否则就分配一个新的地址.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/* koopa_util.hpp */

class KoopaContextManager
{
private:
    // ...
    // 用于判断当前符号是否在当前下标被分配, 比如 @a_1 在 symbol_tables[0] 中被分配, 那么 is_symbol_allocated_in_this_level[std::make_pair(&amp;quot;a&amp;quot;, 1)] == true
    std::map&amp;lt;std::pair&amp;lt;std::string, int&amp;gt;, bool&amp;gt; _is_symbol_allocated_in_this_level;

public:
    // ...
};

/* koopa.cpp */

Result VarDefAST::print(std::stringstream &amp;amp;output_stream) const
{
    if (var_init_val)
    {
        Result value_result = (*var_init_val)-&amp;gt;print(output_stream);
        koopa_context_manager.insert_symbol(var_symbol, Symbol(Symbol::Type::VAR, value_result.val));
        std::string symbol_name = var_symbol;
        std::string suffix = std::to_string(koopa_context_manager.name_to_symbol(symbol_name).val);
        std::string symbol_name_with_suffix = symbol_name + &amp;quot;_&amp;quot; + suffix;
        // 如果这个变量名在当前层级中没有被分配过, 则分配一个新的地址
        if (!koopa_context_manager.is_symbol_allocated_in_this_level(symbol_name))
        {
            output_stream &amp;lt;&amp;lt; &amp;quot;\t@&amp;quot; &amp;lt;&amp;lt; symbol_name_with_suffix &amp;lt;&amp;lt; &amp;quot; = alloc i32\n&amp;quot;;
        }
        koopa_context_manager.set_symbol_allocated_in_this_level(symbol_name);
        output_stream &amp;lt;&amp;lt; &amp;quot;\tstore &amp;quot; &amp;lt;&amp;lt; value_result &amp;lt;&amp;lt; &amp;quot;, @&amp;quot; &amp;lt;&amp;lt; symbol_name_with_suffix &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
    }
    else
    {
        koopa_context_manager.insert_symbol(var_symbol, Symbol(Symbol::Type::VAR, 0));
        std::string symbol_name = var_symbol;
        std::string suffix = std::to_string(koopa_context_manager.name_to_symbol(symbol_name).val);
        std::string symbol_name_with_suffix = symbol_name + &amp;quot;_&amp;quot; + suffix;
        // 如果这个变量名在当前层级中没有被分配过, 则分配一个新的地址
        if (!koopa_context_manager.is_symbol_allocated_in_this_level(symbol_name))
        {
            output_stream &amp;lt;&amp;lt; &amp;quot;\t@&amp;quot; &amp;lt;&amp;lt; symbol_name_with_suffix &amp;lt;&amp;lt; &amp;quot; = alloc i32\n&amp;quot;;
        }
        koopa_context_manager.set_symbol_allocated_in_this_level(symbol_name);
    }
    return Result();
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;short-circuit-evaluation-of-logical-expressions&quot;&gt;Short-Circuit Evaluation of Logical Expressions&lt;a class=&quot;anchor&quot; href=&quot;#short-circuit-evaluation-of-logical-expressions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;编译器对逻辑运算, 比如 &lt;code&gt;||&lt;/code&gt; 实际上是做了如下操作:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int result = 1;

if (lhs == 0) 
{
  result = rhs != 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;这样当 &lt;code&gt;lhs == 1&lt;/code&gt; 时, 编译器会直接返回 &lt;code&gt;result&lt;/code&gt;, 而不会计算 &lt;code&gt;rhs&lt;/code&gt; 的值.&lt;/p&gt;
&lt;p&gt;在具体实现的时候你可以对 &lt;code&gt;lhs&lt;/code&gt; 的返回值进行判断, 如果是立即数就不要使用跳转指令, 这样可能会造成常量表达式的求值失败.&lt;/p&gt;
&lt;p&gt;如果编译期无法确定是否可以短路求值, 我们需要使用内存来保存逻辑表达式的结果. 假设第一个操作数存在了 &lt;code&gt;%1&lt;/code&gt; 这个寄存器中, 编译期我们不知道第二个操作数 &lt;code&gt;%2&lt;/code&gt; 是否存在, 所以无法返回 &lt;code&gt;or&lt;/code&gt; 表达式整体的答案存在哪里了, 所以需要结果存在内存中以保证可以修改.&lt;/p&gt;
&lt;p&gt;一个短路求值示例如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;int main() 
{
    int x = 1;
    int y = 0;
    return x || y;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;对应的 &lt;code&gt;koopa&lt;/code&gt; 代码如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;fun @main(): i32 {
%entry:
	@x_1 = alloc i32
	store 1, @x_1
	@y_1 = alloc i32
	store 0, @y_1
	%0 = load @x_1
	%1 = ne %0, 0
	@or_result_in_memory_1 = alloc i32
	store %1, @or_result_in_memory_1
	br %1, %or_end_1, %or_second_operator_1
%or_second_operator_1:
	%2 = load @y_1
	%3 = ne %2, 0
	%4 = or %1, %3
	store %4, @or_result_in_memory_1
	jump %or_end_1
%or_end_1:
	%5 = load @or_result_in_memory_1
	ret %5
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;其中我们可以看到最后返回的 &lt;code&gt;%5&lt;/code&gt; 是 &lt;code&gt;@or_result_in_memory_1&lt;/code&gt; 这个内存地址中的值, 这个内存地址中保存的值有可能来源于 &lt;code&gt;%1&lt;/code&gt; 这个寄存器, 对应 &lt;code&gt;lhs&lt;/code&gt; 的值, 也有可能来源于 &lt;code&gt;%4&lt;/code&gt; 这个寄存器, 对应 &lt;code&gt;rhs&lt;/code&gt; 的值. 为了避免不知道返回 &lt;code&gt;%1&lt;/code&gt; 还是 &lt;code&gt;%4&lt;/code&gt; 的情况, 我们使用内存来保存结果, 最后从内存中读取结果到 &lt;code&gt;%5&lt;/code&gt; 中即可.&lt;/p&gt;
&lt;p&gt;一个可能的实现方式如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/* koopa.cpp */

Result LOrExpAST::print(std::stringstream &amp;amp;output_stream) const
{
    if (!left_or_exp &amp;amp;&amp;amp; !op &amp;amp;&amp;amp; left_and_exp)
    {
        return (*left_and_exp)-&amp;gt;print(output_stream);
    }
    else if (left_or_exp &amp;amp;&amp;amp; op &amp;amp;&amp;amp; left_and_exp)
    {
        Result result_left = (*left_or_exp)-&amp;gt;print(output_stream);

        if (result_left.type == Result::Type::IMM &amp;amp;&amp;amp; result_left.val != 0) // 立即数非 0
        {
            return Result(Result::Type::IMM, 1);
        }
        else if (result_left.type == Result::Type::IMM &amp;amp;&amp;amp; result_left.val == 0) // 立即数 0
        {
            Result result_right = (*left_and_exp)-&amp;gt;print(output_stream);
            if (result_right.type == Result::Type::IMM)
            {
                return Result(Result::Type::IMM, 0 || result_right.val);
            }
            else
            {
                Result temp = Result(Result::Type::REG);
                output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; temp &amp;lt;&amp;lt; &amp;quot; = ne &amp;quot; &amp;lt;&amp;lt; result_right &amp;lt;&amp;lt; &amp;quot;, 0\n&amp;quot;;
                return temp;
            }
        }
        else if (result_left.type == Result::Type::REG) // 如果是寄存器, 不能在编译期完成短路求值, 就需要跳转来完成短路求值, 如果判断寄存器是 0 直接跳转到 or_end_label
        {
            // 每进入一个需要用分支跳转语句达成短路求值的 || 语句, 就设置一个跳转标签
            koopa_context_manager.total_or_statement_count++;

            // 设置跳转标签
            std::string or_second_operator_label = &amp;quot;%or_second_operator_&amp;quot; + std::to_string(koopa_context_manager.total_or_statement_count);
            std::string or_end_label = &amp;quot;%or_end_&amp;quot; + std::to_string(koopa_context_manager.total_or_statement_count);

            // 假设第一个操作数存在了 %1 这个寄存器中, 编译期不知道第二个操作数 %2 是否存在, 所以无法返回 or 表达式整体的答案存在哪里了, 所以需要结果存在内存中以保证可以修改
            std::string or_result_in_memory = &amp;quot;@or_result_in_memory_&amp;quot; + std::to_string(koopa_context_manager.total_or_statement_count);

            // 如果第一个操作数是 0, 则跳转到 or_second_operator_label 看看第二个操作数是否是 0, 否则跳转到 or_end_label
            Result temp_1 = Result(Result::Type::REG);
            output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; temp_1 &amp;lt;&amp;lt; &amp;quot; = ne &amp;quot; &amp;lt;&amp;lt; result_left &amp;lt;&amp;lt; &amp;quot;, 0\n&amp;quot;;
            output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; or_result_in_memory &amp;lt;&amp;lt; &amp;quot; = alloc i32\n&amp;quot;;
            output_stream &amp;lt;&amp;lt; &amp;quot;\tstore &amp;quot; &amp;lt;&amp;lt; temp_1 &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; or_result_in_memory &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
            output_stream &amp;lt;&amp;lt; &amp;quot;\tbr &amp;quot; &amp;lt;&amp;lt; temp_1 &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; or_end_label &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; or_second_operator_label &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;

            // 输出没有短路求值的控制流 label
            output_stream &amp;lt;&amp;lt; or_second_operator_label &amp;lt;&amp;lt; &amp;quot;:&amp;quot; &amp;lt;&amp;lt; std::endl;

            // 计算第二个操作数
            Result result_right = (*left_and_exp)-&amp;gt;print(output_stream);
            Result temp_2 = Result(Result::Type::REG);
            Result temp_3 = Result(Result::Type::REG);
            output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; temp_2 &amp;lt;&amp;lt; &amp;quot; = ne &amp;quot; &amp;lt;&amp;lt; result_right &amp;lt;&amp;lt; &amp;quot;, 0\n&amp;quot;;
            output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; temp_3 &amp;lt;&amp;lt; &amp;quot; = or &amp;quot; &amp;lt;&amp;lt; temp_1 &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; temp_2 &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
            output_stream &amp;lt;&amp;lt; &amp;quot;\tstore &amp;quot; &amp;lt;&amp;lt; temp_3 &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; or_result_in_memory &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
            output_stream &amp;lt;&amp;lt; &amp;quot;\tjump &amp;quot; &amp;lt;&amp;lt; or_end_label &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;

            // 输出短路求值之后的控制流合并 label
            output_stream &amp;lt;&amp;lt; or_end_label &amp;lt;&amp;lt; &amp;quot;:&amp;quot; &amp;lt;&amp;lt; std::endl;

            // 把结果从内存中读取到寄存器中
            Result result = Result(Result::Type::REG);
            output_stream &amp;lt;&amp;lt; &amp;quot;\t&amp;quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &amp;quot; = load &amp;quot; &amp;lt;&amp;lt; or_result_in_memory &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
            return result;
        }
        else
        {
            throw std::runtime_error(&amp;quot;LOrExpAST::print: invalid first operand of logical OR expression&amp;quot;);
        }
    }
    else
    {
        throw std::runtime_error(&amp;quot;LOrExpAST::print: invalid logical OR expression&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;generate-risc-v-branch-code&quot;&gt;Generate RISC-V Branch Code&lt;a class=&quot;anchor&quot; href=&quot;#generate-risc-v-branch-code&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;在完成了 &lt;code&gt;koopa&lt;/code&gt; 的生成后, 我们就可以开始生成 &lt;code&gt;RISC-V&lt;/code&gt; 的汇编代码了.&lt;/p&gt;
&lt;p&gt;具体来讲只需要实现 &lt;code&gt;bnez&lt;/code&gt; 和 &lt;code&gt;j&lt;/code&gt; 这两个 &lt;code&gt;RISC-V&lt;/code&gt; 指令的生成以满足 &lt;code&gt;br&lt;/code&gt; 和 &lt;code&gt;jump&lt;/code&gt; 这两个 &lt;code&gt;koopa&lt;/code&gt; 指令的生成即可, 难度不大.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;/* riscv.cpp */

// 访问 branch 指令, 这个指令的输入是立即数或内存, 所以需要判断 branch.cond-&amp;gt;kind.tag
void visit(const koopa_raw_branch_t &amp;amp;branch, const koopa_raw_value_t &amp;amp;value)
{
    // 当前函数的 StackManager
    StackManager &amp;amp;stack_manager = riscv_context_manager.get_current_function_stack_manager();
    // 给中间结果分配一个寄存器
    riscv_context_manager.allocate_reg(value);
    std::string temp_reg_name = riscv_context_manager.value_to_reg_string(value);
    // 使用立即数或从栈中加载数据到寄存器
    if (branch.cond-&amp;gt;kind.tag == KOOPA_RVT_INTEGER)
    {
        riscv_printer.li(temp_reg_name, branch.cond-&amp;gt;kind.data.integer.value);
    }
    else
    {
        riscv_printer.lw(temp_reg_name, &amp;quot;sp&amp;quot;, stack_manager.get_value_stack_offset(branch.cond), riscv_context_manager);
    }
    // 访问 branch 指令
    riscv_printer.bnez(temp_reg_name, branch.true_bb-&amp;gt;name + 1);
    riscv_printer.jump(branch.false_bb-&amp;gt;name + 1);
    // 当前操作数所在的寄存器已经被使用过了, 释放
    riscv_context_manager.set_reg_free(value);
}

// 访问 jump 指令
void visit(const koopa_raw_jump_t &amp;amp;jump)
{
    // 访问 jump 指令
    riscv_printer.jump(jump.target-&amp;gt;name + 1);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;handle-immediate-number-overflow&quot;&gt;Handle Immediate Number Overflow&lt;a class=&quot;anchor&quot; href=&quot;#handle-immediate-number-overflow&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;但是在这里, &lt;code&gt;Lv6&lt;/code&gt; 的 &lt;code&gt;RISC-V&lt;/code&gt; 的所有测试点中有一个叫做 &lt;code&gt;logical1&lt;/code&gt; 的测试点比较特殊, 它测试了 &lt;code&gt;lw&lt;/code&gt;, &lt;code&gt;sw&lt;/code&gt; 和 &lt;code&gt;addi&lt;/code&gt; 这三个指令中立即数的范围.&lt;/p&gt;
&lt;aside aria-label=&quot;Immediate Number Overflow First Tested Here&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-blue-500 bg-blue-50 dark:bg-blue-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-blue-700 dark:text-blue-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;Immediate Number Overflow First Tested Here&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;非常奇怪的是这个立即数溢出的问题在 &lt;a href=&quot;https://pku-minic.github.io/online-doc/#/lv4-const-n-var/var-n-assign&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Lv4.2. 变量和赋值&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 中第一次提到, 但是到了 &lt;code&gt;Lv6&lt;/code&gt; 的 &lt;code&gt;RISC-V&lt;/code&gt; 生成部分才第一次被测试, 并且这个测试点的名字非常具有误导性, 让人以为是逻辑运算的测试点, 实际上错误出在 &lt;code&gt;RISC-V&lt;/code&gt; 的立即数范围上…&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;回忆 &lt;code&gt;RISC-V&lt;/code&gt; 的指令格式, 立即数范围为十二位整数, 即 &lt;code&gt;-2048&lt;/code&gt; 到 &lt;code&gt;2047&lt;/code&gt;, 所以这三个指令一旦遇到立即数超过这个范围的指令就需要进行额外的处理, 以 &lt;code&gt;lw&lt;/code&gt; 为例:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;void RISCVPrinter::lw(const std::string &amp;amp;rd, const std::string &amp;amp;base, const int &amp;amp;bias, RISCVContextManager &amp;amp;context_manager)
{
    // 检查偏移量是否在 12 位立即数范围内
    if (bias &amp;gt;= -2048 &amp;amp;&amp;amp; bias &amp;lt; 2048)
    {
        std::cout &amp;lt;&amp;lt; &amp;quot;\tlw &amp;quot; &amp;lt;&amp;lt; rd &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; bias &amp;lt;&amp;lt; &amp;quot;(&amp;quot; &amp;lt;&amp;lt; base &amp;lt;&amp;lt; &amp;quot;)&amp;quot; &amp;lt;&amp;lt; std::endl;
    }
    else
    {
        std::string reg = context_manager.new_temp_reg();
        li(reg, bias);
        add(reg, reg, base);
        std::cout &amp;lt;&amp;lt; &amp;quot;\tlw &amp;quot; &amp;lt;&amp;lt; rd &amp;lt;&amp;lt; &amp;quot;, &amp;quot; &amp;lt;&amp;lt; &amp;quot;(&amp;quot; &amp;lt;&amp;lt; reg &amp;lt;&amp;lt; &amp;quot;)&amp;quot; &amp;lt;&amp;lt; std::endl;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;并且注意指令格式是 &lt;code&gt;lw rd, bias(base)&lt;/code&gt;, 其中 &lt;code&gt;bias&lt;/code&gt; 是&lt;strong&gt;立即数不是寄存器&lt;/strong&gt;, 所以 &lt;code&gt;%0 = load @x&lt;/code&gt; 指令 (其中 &lt;code&gt;@x&lt;/code&gt; 在栈偏移量为 2048 的地方) 应该写成:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;li t1, 2048
add t1, t1, sp
lw t0, (t1)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;而不是:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;asm&quot;&gt;&lt;pre&gt;&lt;code&gt;li t1, 2048
lw t0, t1(sp)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;compile-and-test-6&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-6&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv6 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;riscv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv6 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lv7&quot;&gt;Lv7&lt;a class=&quot;anchor&quot; href=&quot;#lv7&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这个 Level 需要实现 &lt;code&gt;while&lt;/code&gt; 和它配套的 &lt;code&gt;break&lt;/code&gt; 和 &lt;code&gt;continue&lt;/code&gt; 语句.&lt;/p&gt;
&lt;aside aria-label=&quot;WA Cases&quot; class=&quot;aside border-border my-3 overflow-hidden rounded-2xl border&quot; data-astro-cid-mjo7ae67&gt;&lt;div class=&quot;aside-container border-l-8 px-4 py-3 border-l-yellow-500 bg-yellow-50 dark:bg-yellow-900/20&quot; data-astro-cid-mjo7ae67&gt;&lt;p class=&quot;not-prose flex items-center gap-x-2 font-medium text-yellow-700 dark:text-yellow-300&quot; aria-hidden=&quot;true&quot; data-astro-cid-mjo7ae67&gt;WA Cases&lt;/p&gt;&lt;div class=&quot;aside-content mt-2&quot; data-astro-cid-mjo7ae67&gt;&lt;p&gt;这个 Level 的测试点中我有两个 WA 的测试点, 分别是 &lt;code&gt;12_break1&lt;/code&gt; 和 &lt;code&gt;14_summary1&lt;/code&gt; . 如果您有关于 &lt;code&gt;Lv7&lt;/code&gt; 的任何 corner case 的测试点, 欢迎和我交流!&lt;/p&gt;&lt;p&gt;Update 2025-01-06: 问题出在控制流分析出错. 我之前认为如果在 &lt;code&gt;while&lt;/code&gt; 语句块中有 &lt;code&gt;return&lt;/code&gt; 语句, 那么 &lt;code&gt;while&lt;/code&gt; 语句也告诉上一层控制流返回了, 但是实际上这个 &lt;code&gt;while&lt;/code&gt; 语句可能根本不会进去, 是否进入 &lt;code&gt;while&lt;/code&gt; 语句块取决于运行时条件, 所以无论 &lt;code&gt;while&lt;/code&gt; 语句块中是否存在 &lt;code&gt;return&lt;/code&gt; 语句, 都应该告诉上一层控制流还没有返回.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;while-statement&quot;&gt;&lt;code&gt;While&lt;/code&gt; Statement&lt;a class=&quot;anchor&quot; href=&quot;#while-statement&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;while&lt;/code&gt; 语句的语法如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;Stmt ::= &amp;quot;while&amp;quot; &amp;quot;(&amp;quot; Exp &amp;quot;)&amp;quot; Stmt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;和 &lt;code&gt;if&lt;/code&gt; 语句类似, 不再赘述.&lt;/p&gt;
&lt;h3 id=&quot;break-and-continue-statement&quot;&gt;&lt;code&gt;Break&lt;/code&gt; and &lt;code&gt;Continue&lt;/code&gt; Statement&lt;a class=&quot;anchor&quot; href=&quot;#break-and-continue-statement&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;break&lt;/code&gt; 和 &lt;code&gt;continue&lt;/code&gt; 语句的语法如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;Stmt ::= &amp;quot;break;&amp;quot;;
       | &amp;quot;continue;&amp;quot;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;主要的难点在于如何正确获取跳转的目标, 因为跳转的目标标签是在访问 &lt;code&gt;while&lt;/code&gt; 语句时定义的, 当你访问 &lt;code&gt;break&lt;/code&gt; 或 &lt;code&gt;continue&lt;/code&gt; 语句时, 需要获取 &lt;code&gt;while&lt;/code&gt; 语句的跳转目标标签.&lt;/p&gt;
&lt;p&gt;我们采用栈的方式获取跳转的目标标签, 当进入 &lt;code&gt;while&lt;/code&gt; 语句时, 把当前的 &lt;code&gt;while&lt;/code&gt; 语句的序号压入栈中, 当访问 &lt;code&gt;break&lt;/code&gt; 或 &lt;code&gt;continue&lt;/code&gt; 语句时, 从栈中读取当前的栈顶序号, 然后生成跳转指令.&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;pre&gt;&lt;code&gt;else if (stmt_type == StmtType::Break)
{
    if (koopa_context_manager.while_statement_stack.empty())
    {
        throw std::runtime_error(&amp;quot;StmtAST::print: invalid break statement, not in a while statement&amp;quot;);
    }
    int current_while_statement_count = koopa_context_manager.while_statement_stack.top();
    std::string while_end_label = &amp;quot;%while_end_&amp;quot; + std::to_string(current_while_statement_count);
    output_stream &amp;lt;&amp;lt; &amp;quot;\tjump &amp;quot; &amp;lt;&amp;lt; while_end_label &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;;
    Result result = Result();
    result.control_flow_while_interrupted = true;
    return result;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;compile-and-test-7&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-7&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv7 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;riscv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv7 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lv8&quot;&gt;Lv8&lt;a class=&quot;anchor&quot; href=&quot;#lv8&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这一章节需要实现一个能够处理函数 (包括 SysY 库函数) 和全局变量的编译器.&lt;/p&gt;
&lt;p&gt;示例函数如下:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;c&quot;&gt;&lt;pre&gt;&lt;code&gt;
int var;

int func(int x) 
{
  var = var + x;
  return var;
}

int main() 
{
  // putint 和 putch 都是 SysY 库函数
  // SysY 要求库函数不声明就可以使用
  putint(func(1));
  var = var * 10;
  putint(func(2));
  putch(10);
  return var;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;some-simple-advice&quot;&gt;Some Simple Advice&lt;a class=&quot;anchor&quot; href=&quot;#some-simple-advice&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;中间代码生成部分只需要注意重新 &lt;code&gt;load&lt;/code&gt; 函数参数, 这样可以为目标代码的生成省一些事, 具体来讲, 如果这样做了之后只有 &lt;code&gt;load&lt;/code&gt; 指令可能收到 &lt;code&gt;KOOPA_RVT_GLOBAL_ALLOC&lt;/code&gt; 这个代表函数参数的 &lt;code&gt;tag&lt;/code&gt;, 其他函数不用修改.&lt;/li&gt;
&lt;li&gt;在进入一个新函数后, 要在顶部保存 &lt;code&gt;ra&lt;/code&gt; 寄存器, 在退出函数后恢复 &lt;code&gt;ra&lt;/code&gt; 寄存器, 所以栈大小要多加一, 多分配一条 store 指令来存储 ra 寄存器, ra 是调用者保存寄存器, 调用者把它的 ra 存在每个栈帧的最上面, 调用函数之前修改这个寄存器为 call 的下一条指令, 然后进入下一个函数, 代表调用者的下一条指令.&lt;/li&gt;
&lt;li&gt;在栈上存变量的时候, 要注意把栈顶的留给函数参数的位置空出来, 不要存储局部变量, 否则可能造成函数参数对局部变量的覆盖.&lt;/li&gt;
&lt;li&gt;在调用完函数后, 需要检测 &lt;code&gt;value-&amp;gt;ty-&amp;gt;tag == KOOPA_RTT_UNIT&lt;/code&gt; 这个条件, 如果为真, 则函数没有返回值, 不需要生成 &lt;code&gt;return&lt;/code&gt; 指令, 否则需要.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;compile-and-test-8&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-8&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv8 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;riscv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv8 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lv9&quot;&gt;Lv9&lt;a class=&quot;anchor&quot; href=&quot;#lv9&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;终于知道为什么说编译原理比 ICS 的任务量多了…&lt;/p&gt;
&lt;p&gt;建议在没有绩点压力的大四选这门课就可以不用写 Lv9 了, 因为 Lv9 非常浪费时间并且占所有 Lab 的 27% 的分数, 也就是总评的 8 分, 如果在绩点压力下完成所有 Lab 会浪费很多时间.&lt;/p&gt;
&lt;p&gt;引流到 &lt;a href=&quot;https://arthals.ink/tags/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Arthals 的编译原理相关博客&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; , 里面有更多关于编译原理课程和 Lab 的笔记, 相信他会把 Lv9 的 Lab 讲的更清楚.&lt;/p&gt;
&lt;h3 id=&quot;compile-and-test-9&quot;&gt;Compile and Test&lt;a class=&quot;anchor&quot; href=&quot;#compile-and-test-9&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -koopa debug/hello.c -o debug/hello.koopa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;运行编译器, 把 &lt;code&gt;debug/hello.c&lt;/code&gt; 编译为 &lt;code&gt;debug/hello.S&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;./build/compiler -riscv debug/hello.c -o debug/hello.S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;koopa&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -koopa -s lv9 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;本地自动评测 &lt;code&gt;riscv&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;pre&gt;&lt;code&gt;autotest -riscv -s lv9 /root/compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;a class=&quot;anchor&quot; href=&quot;#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这门课的 Lab 任务量很大, 但是思维难度实际上不大, 助教哥哥的文档写的还是比较清楚的, 只建议大四同学选这门课, 否则完成 Lab 的压力会很大.&lt;/p&gt;
&lt;p&gt;谢谢大家!&lt;/p&gt;
&lt;p&gt;Yutong Liang&lt;/p&gt;
&lt;p&gt;2025-01-12&lt;/p&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/how_compilers_work.CwrE3bLr.png"/></item><item><title>La La Land 2024</title><link>https://www.lyt0112.com/blog/lalaland_competition-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/lalaland_competition-en</guid><description>WCA Competition @ Los Angeles, met new friends and broke my personal WCA record.</description><pubDate>Sun, 01 Sep 2024 20:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The competition experience of La La Land 2024&lt;/p&gt;
&lt;p&gt;The last competition was the Tianjin competition five years ago.&lt;/p&gt;
&lt;h2 id=&quot;set-out&quot;&gt;Set Out&lt;a class=&quot;anchor&quot; href=&quot;#set-out&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Heading to the competition venue at 10:30 in the morning to sign in (So Hilton, the registration fee of $70 is too expensive).&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/05996340f6efa287c093d4ed70d23f24.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Sign-in desk &lt;/p&gt;
&lt;p&gt;Then we waited in the rest area for the WCA Delegate to explain the competition matters and how to be a judge.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/654e62948759d0f47442fa60a624bf4f.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Get ready to start the competition! &lt;/p&gt;
&lt;p&gt;Some people were even selling cookies they baked with Rubik’s Cube patterns. The guy sitting next to me, &lt;a href=&quot;https://www.worldcubeassociation.org/persons/2019HOIN01&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Preston Hoing (2019HOIN01)&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;, bought two and gave me one as a souvenir.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/baca0857505b7787c5be390ada24e845.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Little Biscuit &lt;/p&gt;
&lt;h2 id=&quot;comp-process&quot;&gt;Comp Process&lt;a class=&quot;anchor&quot; href=&quot;#comp-process&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a 24-hour race, which means the race is continuous for 24 hours. If you participate in many events (like me), you will need to stay up all night. In fact, I did stay up all night.&lt;/p&gt;
&lt;p&gt;Respect the air conditioning temperature here, I will never wear shorts in a 24h comp again…&lt;/p&gt;
&lt;p&gt;Shivering all over in the mid-night, I need to go to the bathroom to wash my hands with hot water to maintain a good condition.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/b5f69e8ce00a7d532bb54aeb5d6914bc.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Timetable &lt;/p&gt;
&lt;p&gt;A total of nine events participated in this competition, choose two interesting events to talk about.&lt;/p&gt;
&lt;h3 id=&quot;3x3&quot;&gt;3x3&lt;a class=&quot;anchor&quot; href=&quot;#3x3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cubing for 7 years, improved by 5 seconds in the first 3 years, improved by 5 seconds in the last 4 years…&lt;/p&gt;
&lt;p&gt;This competition broke the single and average personal records, with a single time Sub10. During regular practice, there might be one sub-10 out of 20 attempts, so I’m a bit excited.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/a55df8e1a1f50489065e8d2daa64559b.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; SUB 10 &lt;/p&gt;
&lt;p&gt;Next time, make a resolution to average SUB 10!&lt;/p&gt;
&lt;h3 id=&quot;pyraminx&quot;&gt;Pyraminx&lt;a class=&quot;anchor&quot; href=&quot;#pyraminx&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I don’t know why I often achieve higher rankings with the regular 4-sided, this time I felt good in the second round, broke my personal records for single and average, and also made it to the finals.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/27cde2edbc4f4f23d7712b35a5950f8d.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Pyraminx Final &lt;/p&gt;
&lt;p&gt;It might be because I was too tired from staying up all night, I didn’t perform well in the finals, but I’m still very happy to have made it to the finals.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/f24ddb5390a8f806689d217e70ae3b0d.png&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Pyraminx Grades &lt;/p&gt;
&lt;h2 id=&quot;interesting-things&quot;&gt;Interesting Things&lt;a class=&quot;anchor&quot; href=&quot;#interesting-things&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;clock-wr&quot;&gt;Clock WR&lt;a class=&quot;anchor&quot; href=&quot;#clock-wr&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Witnessing the birth of a world record for the first time, &lt;a href=&quot;https://www.worldcubeassociation.org/persons/2021DUNA01&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Brendyn Dunagan (2021DUNA01)&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; set a new single world record for Clock with a time of 1.973 seconds.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/11654f62590abf0ee07922eb4d0407f7.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Clock WR: 1.973s &lt;/p&gt;
&lt;p&gt;Took a photo with him, congratulated him Nice Record!&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/b9065e59f1e666a9516c4a037de17738.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;differences-between-china-and-us-competitions&quot;&gt;Differences Between China and US Competitions&lt;a class=&quot;anchor&quot; href=&quot;#differences-between-china-and-us-competitions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At first, I thought that the process of foreign competitions was the same as that of Chinese competitions, but actually, there are quite a few differences.&lt;/p&gt;
&lt;p&gt;In Chinese competitions, the Judge is responsible for taking your cube, having it scrambled by the scrambler in the scrambling area, and then bringing it back to the competition table. Each person will not leave their seat until all five attempts are finished.&lt;/p&gt;
&lt;p&gt;In US competitions, you first submit your cube. After scrambling is completed, a Runner will place your cube on an idle competition table and call your name. You go to this competition table to complete the solve. The Judge records your result. After finishing one solve, you need to leave the competition table and return to the waiting area. The Runner will take your cube back to the scrambling area to wait for the next time your name is called at another competition table. So, you need to complete five solves on five different competition tables.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/79c8be15c1efa460499d1df6665c0055.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Submit Rubik’s Cube &lt;/p&gt;
&lt;p&gt;It seems that the efficiency is higher abroad because there are no idle competition tables, and the tables are not occupied during shuffling.&lt;/p&gt;
&lt;p&gt;In addition, judges and scramblers are different. US judges are all players. When each person is assigned to a competition group, they are also assigned to another judge group. During the competition of this group, you need to find a free judge position to act as a judge.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/706d865a79f21113a14b457eac5bdc7a.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; I’m a Judge &lt;/p&gt;
&lt;p&gt;Of course, the number of judges is oversaturated, because for one event you only need to judge 5 matches, but you need to judge all the people who come to this competition table, which may require judging about 50 matches, so you can also choose to slack off and not go when it’s your turn to judge.&lt;/p&gt;
&lt;p&gt;However, many people are unwilling to be a judge, which often results in a lack of referees. The WCA Delegate has to shout into the microphone to find people to be judges, especially late at night when many kids have gone to bed, and sometimes there’s only one person acting as a judge.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/a75cce188fa719394bffe39a94a659af.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; The judge’s stopwatch ⏱️ &lt;/p&gt;
&lt;p&gt;However, judges in China are recruited through a questionnaire registration system. Before the competition, referees are recruited by sending out questionnaires in various groups. This ensures that there are enough referees, but the downside is that it requires effort to recruit them.&lt;/p&gt;
&lt;h2 id=&quot;people--friends&quot;&gt;People &amp;amp; Friends&lt;a class=&quot;anchor&quot; href=&quot;#people--friends&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.worldcubeassociation.org/persons/2019HOIN01&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Preston Hoing (2019HOIN01)&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; was the first person I met at the competition venue. He is a bit of a chatterbox and chatted with me very enthusiastically, even mentioning a good scramble he encountered last time with the magic clock.&lt;/p&gt;
&lt;p&gt;He is a participant in all events of this competition. The 24-hour all-event challenge was really exhausting for him, but he still persevered until the end, completed his first five-blind, and even made it to the three-blind finals. Impressive. However, I forgot to take a photo with him, which is a bit regrettable.&lt;/p&gt;
&lt;p&gt;Then I sat next to a little boy and his father, a very cute little boy from Brazil: &lt;a href=&quot;https://www.worldcubeassociation.org/persons/2024REIN02&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Benjamin Dias Reino (2024REIN02)&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/f1e39d4dd80004f7565226252ff8e84a.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; With Benjamin and His Dad &lt;/p&gt;
&lt;p&gt;Then two other kids came, whom Benjamin met in the last competition. The younger brother of the two brothers participated in the competition &lt;a href=&quot;https://www.worldcubeassociation.org/persons/2024SEMI01&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ren Davis Semien (2024SEMI01)&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/8e2ae80b737d44f9cec4aaac9f1f84d1.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Later, a local high school student from Los Angeles &lt;a href=&quot;https://www.worldcubeassociation.org/persons/2022LEAV02&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Kierran Leavitt (2022LEAV02)&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; joined the table. He mentioned that he is learning to fly planes, including gliders and powered aircraft, which is quite fun.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/4c4b93340fddad8be9ba7fa1a5a015f9.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the last match of the first round of the third stage, I met a Chinese judge, &lt;a href=&quot;https://www.worldcubeassociation.org/persons/2023SHAO01&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Tianshuo Shao (2023SHAO01)&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;. If it weren’t for him, I might have had to speak English all day, my brain would have burned out.&lt;/p&gt;
&lt;p&gt;Later, during the break in the game, he drove me around Los Angeles. In the evening, we went to Santa Monica Beach and Pier, and there was a strong smell of marijuana.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/f19484316f13ab410cc2b23fee2e1089.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Santa Monica Beach &lt;/p&gt;
&lt;p&gt;Forgot to take a group photo, will make up for it in a couple of days.&lt;/p&gt;
&lt;p&gt;Update on Oct 28, 2024: Just remembered to put the photo here&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/10/b667444035a036c0237c74c9530631e2.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; With Tianshuo at SacCubing XVIII 2024 @ Sacremento &lt;/p&gt;
&lt;h2 id=&quot;epilogue&quot;&gt;Epilogue&lt;a class=&quot;anchor&quot; href=&quot;#epilogue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Put some pictures&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/6442333155869fddeb83320c0c207071.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Before the match &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/0efe5b4c011d7e65b29bd0e32b6c21f6.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; The person practicing on the balcony &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/c9b29b35ecbab01b75e1cae1e99a92ca.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; The view from the hotel’s large balcony, the parking lot across the street cost Tianshuo and me $30 &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/c58c4616b3c570f976f808735931f128.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Drive into a dead end &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/02572e9ca7c9c95946394798b76f1815.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Drove into a dead end again &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/e95dbf5a6ad9b229a58c3e6f876d14a2.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Everyone was celebrating at midnight &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/1ea26f2a69a459430e42cab1a45dc451.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; The pool at the Hilton Hotel &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/63f6b1905945306d9b75c4cc9e84ed23.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; With Benjamin &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/ba355b102143d09fcb13e49df9a93d26.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Santa Monica Pier &lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/09/7d991c7629e50ef9a0ee4261dbc4817d.jpeg&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; People fishing on the Santa Monica Pier &lt;/p&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/la_cubing.DGDROiHK.jpeg"/></item><item><title>Decade with DW</title><link>https://www.lyt0112.com/blog/dw3-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/dw3-en</guid><description>We are all different people all through our lives and that&apos;s okay, you&apos;ve got to keep moving so long as you remember all the people that you used to be.</description><pubDate>Fri, 23 Aug 2024 20:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;start-of-the-journey&quot;&gt;Start of the Journey&lt;a class=&quot;anchor&quot; href=&quot;#start-of-the-journey&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I still clearly remember that night in the summer of 2014, in the &lt;a href=&quot;https://tieba.baidu.com/p/3250946750&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;pinned post&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;, I saw an episode of Doctor Who that I had never seen before. An old man with particularly thick eyebrows and Clara came out of the TARDIS. I thought it was another TV series starring Jenna Coleman (at that time, I hadn’t seen the eyebrows in “The Day of the Doctor” or the regeneration in “The Time of the Doctor”). Later, I realized it was the just-premiered S08E01.&lt;/p&gt;
&lt;p&gt;The first time I learned about Doctor Who was because it was one of the many sci-fi series that the teacher played in class. Later, with his help, I found resources to follow the series.&lt;/p&gt;
&lt;h2 id=&quot;back-to-the-beginning&quot;&gt;Back to the Beginning&lt;a class=&quot;anchor&quot; href=&quot;#back-to-the-beginning&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Rewatched this S08E01 episode on the night of its ten-year premiere, every line of dialogue is so familiar, like an old friend recounting the stories he told me when we first met. I know every word he will say, every joke, and every expression on his face.&lt;/p&gt;
&lt;p&gt;Sometimes, I even get a sense of déjà vu, making me feel like I remember the emotions from ten years ago. For example, when Clara was ‘abandoned’ by the Doctor in the restaurant basement, a feeling of unfamiliarity and despair suddenly surged in my heart, as if I shared the same emotion with my past self… Although it’s the same episode, the personal experience of “so the feelings are the same” is truly very wonderful.&lt;/p&gt;
&lt;p&gt;P.S. In this episode, what Mrs. Vastra said, “Well then, here we go again,” was something Brigadier once said when the Third Doctor regenerated into the Fourth Doctor. Having such long-term memories and tributes is one of the very important reasons why I like DW, because I am also someone who doesn’t like endings, just like tearing out the last page of a book.&lt;/p&gt;
&lt;p&gt;This episode really well portrays the Doctor’s confusion and vulnerability after his regeneration. He is a person full of energy, but also feels fear and loneliness. When translating the dinosaur’s roar, he might really be expressing his own fear. Under such a metaphor, the Doctor’s assurance of the dinosaur’s safety is so fragile, making him feel so guilty. This is somewhat similar to the situation with the Tenth Doctor during the eruption of Mount Vesuvius, but the difference is that the Twelfth Doctor ultimately did not save the dinosaur, whereas the Tenth Doctor saved the Pompeii merchant.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/2b4d8c4c2d6db6a004c7172cf6c1711f.png&quot; alt=&quot;You will be home again!&quot; style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; You will be home again! &lt;/p&gt;
&lt;p&gt;Clara is somewhat distant from the newly regenerated 12 but is very worried about him. She is not yet ready to accept that the Doctor has suddenly turned into a completely unfamiliar appearance, to the point of using a very aggressive tone with Madam Vastra. However, she eventually starts to accept 12 after the final call from 11, but 12’s quest for his own identity has just begun.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/86b5121501ed46c370b8d674b204e7af.png&quot; alt=&quot;How dare you!&quot; style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; How dare you! &lt;/p&gt;
&lt;h2 id=&quot;how-does-it-feel&quot;&gt;How Does It Feel?&lt;a class=&quot;anchor&quot; href=&quot;#how-does-it-feel&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At first, I was very eager for the Doctor’s travels. Being a Doctor or a Companion is the coolest job in the universe. I think almost every Whovian has fantasized about what it would be like to travel with the Doctor, and it is this curiosity that drives our love for DW.&lt;/p&gt;
&lt;p&gt;As I grew up, I gradually understood the emotions of the characters in the series and the values of the Doctor, such curiosity and responsibility, “Never cruel, never cowardly; never give up, never give in.” I have always liked this phrase very much and hope that everyone can stick to their beliefs.&lt;/p&gt;
&lt;p&gt;Of course, as a character with an extremely long lifespan, the only Time Lord, possessing such powerful abilities and wisdom, how to restrain one’s behavior, I think this proposition was fully explored in the three episodes where Clara left.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Face the Raven&lt;/code&gt;, &lt;code&gt;Heaven Sent&lt;/code&gt;, and &lt;code&gt;Hell Bent&lt;/code&gt; as a whole for the season finale, tell the story of what choices the Doctor will make after losing a loved one, even to the point of abandoning his principles for revenge and choosing to alter time. However, in the end, he still cannot prevent things from happening. Perhaps it is the same for everyone; when faced with extreme situations, people often subconsciously deny and try to reverse the situation. But here, even though the Doctor can manipulate time, he still cannot let Clara escape her fate.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/a3804c28dd6f6f8c29255b50e18efaaf.png&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; I heard that there is also writing on the back of this painting saying “I’m in 12”, but the Doctor couldn’t bear to turn it over… &lt;/p&gt;
&lt;p&gt;If the education at school is the transmission of knowledge, then I believe DW definitely holds a place in my values. Curiosity and adventurous spirit, how to face difficulties, and the exploration of human nature. The discussion on these values and propositions has led Doctor Who to have a significant impact on me, making me want to personally &lt;code&gt;see the world&lt;/code&gt;, and often ask myself: &lt;code&gt;What would The Doctor do?&lt;/code&gt; Perhaps I can get a very good answer.&lt;/p&gt;
&lt;h2 id=&quot;words&quot;&gt;Words&lt;a class=&quot;anchor&quot; href=&quot;#words&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are really many lines in DW that I like, some of which are very famous. Here are some that I particularly like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One day, I shall come back. Yes, I shall come back. Until then, there must be no regrets, no tears, no anxieties. Just go forward in all your beliefs, and prove to me that I am not mistaken in mine.&lt;/li&gt;
&lt;li&gt;I burned up a sun just to say goodbye.&lt;/li&gt;
&lt;li&gt;Never cruel, never cowardly; never give up, never give in.&lt;/li&gt;
&lt;li&gt;Goodness is not goodness that seeks advantage. Good is good in the final hour, in the deepest pit without hope, without witness, without reward. Virtue is only virtue in extremis.&lt;/li&gt;
&lt;li&gt;It all just disappears doesn’t it? Everything you are, gone in a moment like breath on a mirror. Any moment now, he’s a coming, (Clara: Who’s coming?) The Doctor. (Clara: But you are, you are the doctor.) And I always will be. But times change and so must I. We all change, when you think about it. We are all different people all through our lives and that’s okay, that’s good, you’ve got to keep moving so long as you remember all the people that you used to be. I will not forget one line of this, not one day, I swear. I will always remember when The Doctor was me.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recommend some videos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Tx411P746&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Bilibili - Are You The One&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;This song is amazing, perfectly fitting the image of the Doctor. I fell in love with this song through this mashup, and the editing is also very well done.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/7li5eZ3JZbA?si=TDZun38jWCOf7z9Q&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;YouTube - The First Question - 50th Anniversary Trailer&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;The 50th anniversary mashup, a video from 12 years ago, but still not outdated, especially the selection and coordination of lines, as well as the choice of music, are all very well done.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/-CdJUYWkrR0?si=bmuiay9TYl3zI1zF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;YouTube - The First Question - 60th Anniversary Edition&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;A 60th-anniversary mashup, a remastered version of the previous video from ten years ago, is also very exciting.&lt;/li&gt;
&lt;li&gt;This video has not been uploaded to Bilibili yet, so I contacted the author and obtained permission to repost it.&lt;/li&gt;
&lt;li&gt;Bilibili repost: &lt;a href=&quot;https://www.bilibili.com/video/BV1unW1e8EwR/?share_source=copy_web&amp;amp;vd_source=715b7965ee40cd347a349e6161f34dfc&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Bilibili - The First Question - 60th Anniversary Edition&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;epilogue&quot;&gt;Epilogue&lt;a class=&quot;anchor&quot; href=&quot;#epilogue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This article briefly talks about the story with DW, but there are more delicate emotions that are hard to express in words. Many episodes are really worth watching repeatedly, and my favorite episode is still &lt;code&gt;Heaven Sent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, I really hope that the next season of DW can put more effort into the plot. The last episode of S14/New Version S1 is a bit hard to endure… Related discussions are welcome to visit &lt;a href=&quot;https://www.lyt0112.com/blog/dw2&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;[DW] 幽默RTD, 幽默S14E08&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; to watch.&lt;/p&gt;
&lt;p&gt;Every Doctor’s regeneration is not just a process of the Doctor seeking a new identity, but also a process for the audience to accept the new Doctor. The first episode of 12 slowly expresses this process through the emotional changes of the companion. In fact, the audience’s acceptance of the new Doctor is also a process of accepting a new self, which in reality represents whether we are willing to bravely start a new stage of life, and whether we are willing to give up things we previously had, even if those things are good and cherished by us.&lt;/p&gt;
&lt;p&gt;Just like this sentence: &lt;code&gt;So long as you remember all the people that you used to be&lt;/code&gt; As long as we remember those beautiful moments on the road of life, we haven’t wasted that time. How could I have imagined what I would be like ten years ago, but as long as I remember the emotions brought by DW (or the bad plot from CC) over the past ten years, it’s enough.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/064d075c4012d8d81a893f308d2b72b3.png&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; The Doctors &lt;/p&gt;
&lt;p&gt;What will the next decade be like for me and DW? Nobody knows, but I will always look forward to the next episode of Doctor Who!&lt;/p&gt;
&lt;p&gt;If you are also a Whovian, feel free to share your stories with DW or your favorite lines/episodes in the comments.&lt;/p&gt;
&lt;p&gt;After all, that’s how the all started!&lt;/p&gt;
&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;a class=&quot;anchor&quot; href=&quot;#credits&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.facebook.com/DoctorWho/posts/pfbid04btj2pTSrWnJXfoMMJQ8rWiHKuz9H9P21QN7Hamwei3sNv6HkL7V8tiS3v8UetFXl&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Doctor Who Facebook - Flashpoint, final episode of the Dalek Invasion of Earth&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/YWfpn6k79pQ?si=eBJH5Zyb2dzMLj_F&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Doctor Who Clips - The Five Doctors&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Doctor_Who_series_8&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Wiki - DW S8&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Doctor_Who_series_9&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Wiki - DW S9&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/7L9cUG3BxN0?si=3b19mIGhbglNDfWz&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;YouTube - The Subtle Brilliance Of Doctor Who’s Most Divisive Finale&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/7li5eZ3JZbA?si=TDZun38jWCOf7z9Q&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;YouTube - Doctor Who: The First Question - 50th Anniversary Trailer&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/-CdJUYWkrR0?si=bmuiay9TYl3zI1zF&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;YouTube - Doctor Who: The First Question - 60th Anniversary Edition&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Tx411P746&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Bilibili - Are You The One&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/dw-s08e01.DivpxHP8.png"/></item><item><title>SQ-1: Basic Lars Method</title><link>https://www.lyt0112.com/blog/sq1-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/sq1-en</guid><description>SQ-1 cube&apos;s most basic solution, even though relatively basic, is still quite complicated.</description><pubDate>Tue, 06 Aug 2024 00:23:20 GMT</pubDate><content:encoded>&lt;h2 id=&quot;intoduction-to-sq-1&quot;&gt;Intoduction to SQ-1&lt;a class=&quot;anchor&quot; href=&quot;#intoduction-to-sq-1&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Square-1 (previously called as Cube 21 and Back to Square One) is a shape-shifting three-layered twisty puzzle. Its solution is very unique because the kite-shaped corners and the triangular edges are indistinguishable to the puzzle’s inner mechanism, meaning that corners can be swapped with edges and therefore it’s possible to have 10 pieces in the upper layer while only 6 in the bottom.&lt;/p&gt;
&lt;p&gt;The puzzle was invented in 1990 by Karel Hršel and Vojtech Kopský.&lt;/p&gt;
&lt;p&gt;It’s an official WCA competition event, the fastest solution being held by Ryan Pilat from USA (3.41 seconds).&lt;/p&gt;
&lt;h2 id=&quot;notation&quot;&gt;Notation&lt;a class=&quot;anchor&quot; href=&quot;#notation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The top (bottom) layer is on the left (right) side of the image, all images are top views.&lt;/p&gt;
&lt;p&gt;Take SQ1 and let the left side of the front of the equator (middle layer) be the short edge. Do not rotate SQ1 as a whole, always hold the short edge of the equator with your left hand.&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Notation&lt;/th&gt;&lt;th&gt;Meaning&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;±&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;±&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\pm x \pm y&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;±&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;±&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;Rotate U layer for &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mo&gt;±&lt;/mo&gt;&lt;mn&gt;30&lt;/mn&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(\pm 30x)^\circ&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;±&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6741em&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∘&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;  AND rotate D layer for &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mo&gt;±&lt;/mo&gt;&lt;mn&gt;30&lt;/mn&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(\pm 30y)^\circ&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;±&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0359em&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6741em&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mbin mtight&quot;&gt;∘&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;munder accentunder=&quot;true&quot;&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;true&quot;&gt;‾&lt;/mo&gt;&lt;/munder&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;n, \underline{n}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6306em;vertical-align:-0.2em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord underline&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.4306em&quot;&gt;&lt;span style=&quot;top:-2.84em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;underline-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;n0, 0n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8389em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;U&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;U, D&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8778em;vertical-align:-0.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.109em&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em&quot;&gt;D&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;munder accentunder=&quot;true&quot;&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mo stretchy=&quot;true&quot;&gt;‾&lt;/mo&gt;&lt;/munder&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;3, \underline{3}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8444em;vertical-align:-0.2em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord underline&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6444em&quot;&gt;&lt;span style=&quot;top:-2.84em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;underline-line&quot; style=&quot;border-bottom-width:0.04em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;M2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.109em&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;1/-1-1/01&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;steps&quot;&gt;Steps&lt;a class=&quot;anchor&quot; href=&quot;#steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;cubeshape&quot;&gt;Cubeshape&lt;a class=&quot;anchor&quot; href=&quot;#cubeshape&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The end of any cubeshape is to transform into Kite-Kite. One can make 4 paired corners then transform to Scallop-Kite. Or make and place 3 paired corners in DL, then 3 more in DR, and then transform into 8-Star or 71-Star.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/254e906316de7b015fdec3222447837f.png&quot; alt style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Cubeshape &lt;/p&gt;
&lt;p&gt;It is recommended for beginners to use the latter method. First, place all the 60-degree angle pieces on the bottom layer to form a star, then you can apply the formula. Once you understand the principle of the formula, you can try to solve it without the formula, using your own thinking, similar to F2L.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/180d3388aba2984d036b25ba96b609a6.png&quot; alt style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Hexagram formula &lt;/p&gt;
&lt;h3 id=&quot;corner-orientation&quot;&gt;Corner Orientation&lt;a class=&quot;anchor&quot; href=&quot;#corner-orientation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This step is relatively simple, logically similar to the restoration of the center piece of a 4x4 Rubik’s Cube.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/d0246c15fb3a5b24a6c3f9da4deb8129.png&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Corner Orientation &lt;/p&gt;
&lt;h3 id=&quot;edge-orientation&quot;&gt;Edge Orientation&lt;a class=&quot;anchor&quot; href=&quot;#edge-orientation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Single edge exchange is about memorizing the formula, but for double edge exchange, you can try this formula and understand the workflow of this commutator.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/d82f9088ff0b22f0d1e73d57db4d1b54.png&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Edge Orientation &lt;/p&gt;
&lt;h3 id=&quot;corner-permutation&quot;&gt;Corner Permutation&lt;a class=&quot;anchor&quot; href=&quot;#corner-permutation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Two relatively simple and understandable formulas.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/619c09768085fb0a35c1e1b6067d0b4e.png&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Corner Permutation &lt;/p&gt;
&lt;p&gt;Other situations: There are definitely corner pieces with the same color on the same face for the top and bottom layer. After placing this face on the left or right side, perform /U’/UD/D’/ to turn it into the first situation in the above diagram.&lt;/p&gt;
&lt;h3 id=&quot;edge-permutation&quot;&gt;Edge Permutation&lt;a class=&quot;anchor&quot; href=&quot;#edge-permutation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The adjacent edge swap here also requires memorizing the formula, but for opposite edge swaps, you can also try this formula and understand the workflow of this commutator.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/9f5c54c46e782181c647230f51bed3a5.png&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Edge Permutation &lt;/p&gt;
&lt;h3 id=&quot;parity&quot;&gt;Parity&lt;a class=&quot;anchor&quot; href=&quot;#parity&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The most complicated formula…&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/dea1dcd8cd993c8df2e4737e54519e41.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Parity &lt;/p&gt;
&lt;h3 id=&quot;equator-flip&quot;&gt;Equator Flip&lt;a class=&quot;anchor&quot; href=&quot;#equator-flip&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If there is an error in the equator (middle layer) position at the end, you can use this formula to solve it.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/08/38203d39633028103431b9472423bde7.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt; Equator Flip &lt;/p&gt;
&lt;h2 id=&quot;credit&quot;&gt;Credit&lt;a class=&quot;anchor&quot; href=&quot;#credit&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://ruwix.com/twisty-puzzles/square-1-back-to-square-one/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ruwix: Twisty Puzzle Wiki - Square-1&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cuberoot.me/sq-beginner-lars/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cube Root - Square-1&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/sq1_cover.B1UPAu-D.jpg"/></item><item><title>San Francisco Trip</title><link>https://www.lyt0112.com/blog/sf_trip</link><guid isPermaLink="true">https://www.lyt0112.com/blog/sf_trip</guid><description>First Caltrain Journey, Berkeley, San Francisco.</description><pubDate>Mon, 22 Jul 2024 17:44:14 GMT</pubDate><content:encoded>&lt;h2 id=&quot;first-caltrain-journey&quot;&gt;First Caltrain Journey&lt;a class=&quot;anchor&quot; href=&quot;#first-caltrain-journey&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I was quite curious before boarding the Caltrain for my first experience with public transportation in the US. Purchasing a ticket or acquiring a Clipper card turned out to be straightforward - you can buy a ticket at any station or download a digital card via the Clipper App.&lt;/p&gt;
&lt;p&gt;Upon arriving at the Caltrain station, I immediately noticed the contrast with Chinese high-speed railway stations. Caltrain stations are considerably more open; you don’t have to swipe your Clipper card or show your train ticket to access the platform. Although it’s possible to board the train without paying, conductors periodically check for valid tickets.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/0b9a4e4c29ae1a2ba844f390368ad18e.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p&gt;Caltrain features double-decker trains equipped with one or two bike cars. The first level is dedicated to cyclists, making it extremely bike-friendly. As for seating, the second level offers two configurations: a single-seat arrangement with an open view to the lower level, and standard seats accompanied by tables.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/17b03723da946ef1f74c12296c91a228.jpeg&quot; alt=&quot;Inside Caltrain: single-seat&quot; style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Inside Caltrain: single-seat&lt;/p&gt;
&lt;p&gt;I took the Caltrain from California Ave Station to SF Station and back, departing at 7:48AM and returning at 8:57PM on Saturday.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/38bb81bbbb1162f59746c79aac13fc20.jpeg&quot; alt=&quot;Inside Caltrain: seat with tables&quot; style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Inside Caltrain: seat with tables&lt;/p&gt;
&lt;p&gt;I generally love its convenience, but it’s a bit slow, taking more than 1 hour for a 60km trip.&lt;/p&gt;
&lt;h2 id=&quot;berkeley&quot;&gt;Berkeley&lt;a class=&quot;anchor&quot; href=&quot;#berkeley&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I took an Uber to Berkeley and enjoyed a brief hour-long tour, wandering around.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/4a9736ee55a6d6accd6efef6acb461c6.jpeg&quot; alt=&quot;Berkeley street&quot; style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Berkeley street&lt;/p&gt;
&lt;h2 id=&quot;san-francisco&quot;&gt;San Francisco&lt;a class=&quot;anchor&quot; href=&quot;#san-francisco&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;lunch&quot;&gt;Lunch&lt;a class=&quot;anchor&quot; href=&quot;#lunch&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In-N-Out Burger!&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/c2763b0537d17e7528a9fa987717987e.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;h3 id=&quot;exploring-and-vintage-shops&quot;&gt;Exploring and Vintage Shops&lt;a class=&quot;anchor&quot; href=&quot;#exploring-and-vintage-shops&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Be cautious of coyotes!&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/9643544f661c1f8c0fe880557d29f7ba.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p&gt;Enjoy exploring the beautiful mountainous terrain and have fun.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/6a0ed7d1d43bb688064aaaae6f0aef62.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;

&lt;p&gt;Also, purchased chocolates worth $130.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/b8344a123c5b4bd59d8e4a0c95ec51b9.jpeg&quot; alt=&quot;A book from 1911&quot; style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align:center;&quot;&gt;A book from 1911&lt;/p&gt;
&lt;h3 id=&quot;dinner&quot;&gt;Dinner&lt;a class=&quot;anchor&quot; href=&quot;#dinner&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Very good location and scene! It’s called &lt;code&gt;Crab House&lt;/code&gt; at Pier 39.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/4777989589e3a52852379e139f1c16b4.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p&gt;There were people celebrating a birthday there, and we encountered two birthday parties!&lt;/p&gt;

&lt;h3 id=&quot;golden-gate-bridge&quot;&gt;Golden Gate Bridge&lt;a class=&quot;anchor&quot; href=&quot;#golden-gate-bridge&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I took some fantastic photos!&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/d06018a03c08d427cb3629b28b3af5f6.jpeg&quot; alt style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p&gt;It was extremely windy and cold, so please dress more warmly!&lt;/p&gt;
&lt;h2 id=&quot;home&quot;&gt;Home&lt;a class=&quot;anchor&quot; href=&quot;#home&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It was very cold at the SF Caltrain Station before I boarded the train at 9 PM.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/cddeb86952694cb7694c55728466a9ba.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p&gt;After arriving at California Ave Station at 10PM, I found it convenient that I had parked my bike next to the platform that morning.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/07/670664b2b5377b9e405dac0908b93475.jpeg&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/golden_gate_bridge.bM31Ko2T.jpeg"/></item><item><title>Rubik&apos;s Clock: 7simul Flip Method</title><link>https://www.lyt0112.com/blog/clock-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/clock-en</guid><description>7simul flip Method for Rubik&apos;s Clock</description><pubDate>Tue, 25 Jun 2024 08:15:03 GMT</pubDate><content:encoded>&lt;h2 id=&quot;7simul-flip&quot;&gt;7simul Flip&lt;a class=&quot;anchor&quot; href=&quot;#7simul-flip&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;observation&quot;&gt;Observation&lt;a class=&quot;anchor&quot; href=&quot;#observation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For example, with the following shuffle:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;UR3- DR4- DL1- UL2- U1+ R4+ D2- L3- ALL1+ y2 U3- R1- D4- L4+ ALL1+ DR DL UL&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Assume the side facing you uses uppercase letters, the back side uses lowercase letters, and when reading the code, flip it using &lt;code&gt;x2&lt;/code&gt; (flip up and down), not the left-right flip used during scrambling. You can use your preferred method for number and letter encoding.&lt;/p&gt;
&lt;p&gt;This example uses the side facing up during scrambling as the front.&lt;/p&gt;















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Steps&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;th&gt;Numerical value&lt;/th&gt;&lt;th&gt;Encoding&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(R\rightarrow D)+(l\rightarrow ul)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0077em&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0197em&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0197em&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;u\rightarrow c&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;-3&lt;/td&gt;&lt;td&gt;C&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;l&lt;/mi&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;l\rightarrow u&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0197em&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;u&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;-5&lt;/td&gt;&lt;td&gt;E&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;mi&gt;U&lt;/mi&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(r\rightarrow d)+(L\rightarrow UL)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0278em&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.109em&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;-3&lt;/td&gt;&lt;td&gt;C&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;U&lt;/mi&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;U\rightarrow C&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.109em&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.0715em&quot;&gt;C&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;mi&gt;U&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;L\rightarrow U&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.109em&quot;&gt;U&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;operation&quot;&gt;Operation&lt;a class=&quot;anchor&quot; href=&quot;#operation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Assuming the four pillars facing you are &lt;code&gt;UL&lt;/code&gt;, &lt;code&gt;UR&lt;/code&gt;, &lt;code&gt;DR&lt;/code&gt;, &lt;code&gt;DL&lt;/code&gt;, the following descriptions indicate which pillars are protruding.&lt;/p&gt;



























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Steps&lt;/th&gt;&lt;th&gt;Protruding column&lt;/th&gt;&lt;th&gt;How many steps &lt;code&gt;UL&lt;/code&gt; turns&lt;/th&gt;&lt;th&gt;How many steps &lt;code&gt;UR&lt;/code&gt; turns&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;code&gt;UL&lt;/code&gt;, &lt;code&gt;DR&lt;/code&gt;, &lt;code&gt;DL&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Encoding of Step 1&lt;/td&gt;&lt;td&gt;Encoding of step 2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;&lt;code&gt;UL&lt;/code&gt;, &lt;code&gt;DL&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Align &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; dials&lt;/td&gt;&lt;td&gt;Encoding of Step 3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;&lt;code&gt;UL&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Align &lt;code&gt;C&lt;/code&gt; and &lt;code&gt;D&amp;amp;R&lt;/code&gt; dials&lt;/td&gt;&lt;td&gt;Align &lt;code&gt;DR&lt;/code&gt; and &lt;code&gt;D&amp;amp;R&lt;/code&gt; dials&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;&lt;code&gt;x2&lt;/code&gt; Flip over&lt;/td&gt;&lt;td&gt;&lt;code&gt;x2&lt;/code&gt; Flip over&lt;/td&gt;&lt;td&gt;&lt;code&gt;x2&lt;/code&gt; Flip over&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;&lt;code&gt;UL&lt;/code&gt;, &lt;code&gt;DR&lt;/code&gt;, &lt;code&gt;DL&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Encoding of step 4&lt;/td&gt;&lt;td&gt;Encoding of step 5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;&lt;code&gt;UL&lt;/code&gt;, &lt;code&gt;DL&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Align &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; dials&lt;/td&gt;&lt;td&gt;Encoding of step 6&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;&lt;code&gt;UL&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Align the top-left four dials and the &lt;code&gt;D&amp;amp;R&lt;/code&gt; dial.&lt;/td&gt;&lt;td&gt;Align &lt;code&gt;DR&lt;/code&gt; and &lt;code&gt;D&amp;amp;R&lt;/code&gt; dials&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;&lt;code&gt;UL&lt;/code&gt;, &lt;code&gt;DR&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Except for &lt;code&gt;DL&lt;/code&gt; and &lt;code&gt;UR&lt;/code&gt;, align the remaining dials to twelve o’clock.&lt;/td&gt;&lt;td&gt;&lt;code&gt;DL&lt;/code&gt;, &lt;code&gt;UR&lt;/code&gt; dial aligned to twelve o’clock&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;a class=&quot;anchor&quot; href=&quot;#credits&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV17S421d7K2/?share_source=copy_web&amp;amp;vd_source=715b7965ee40cd347a349e6161f34dfc&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;不喜欢D轮？来学7simul flip吧&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV13f421D7uw/?share_source=copy_web&amp;amp;vd_source=715b7965ee40cd347a349e6161f34dfc&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;零基础带你学会魔表7simul法（Tommy版）&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1pb4y177r4/?p=2&amp;amp;share_source=copy_web&amp;amp;vd_source=715b7965ee40cd347a349e6161f34dfc&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;魔表基础还原教程&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/clock_cover.zli7n4gz.png"/></item><item><title>Skewb: Optimized LBL Method</title><link>https://www.lyt0112.com/blog/skewb-en</link><guid isPermaLink="true">https://www.lyt0112.com/blog/skewb-en</guid><description>Skewb Optimized LBL Method, including FL, LL, side center restoration</description><pubDate>Sun, 23 Jun 2024 08:20:20 GMT</pubDate><content:encoded>&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;a class=&quot;anchor&quot; href=&quot;#overview&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;lbl-method&quot;&gt;LBL Method&lt;a class=&quot;anchor&quot; href=&quot;#lbl-method&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/ec1b64284e79c86336a5f8b66bc56fd9.png&quot; alt style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;LBL Method&lt;/p&gt;
&lt;p&gt;The detailed tutorial for this part can be referred to &lt;a href=&quot;https://www.gancube.com/zh/gancube-tutorials-skewb/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gan’s Skewb tutorial&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;optimized-lbl-method&quot;&gt;Optimized LBL Method&lt;a class=&quot;anchor&quot; href=&quot;#optimized-lbl-method&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Optimized LBL Method: Based on the LBL Method, complete the top corners while returning the top face center to its position.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/e85a55daba635388686e567efed55211.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Skewb Optimized LBL Method&lt;/p&gt;
&lt;p&gt;Multi-directional LBL Method: Based on Optimized LBL Method, avoid four-central-block swaps or skip three-central-block swaps. In fact, Multi-directional LBL Method does not necessarily require controlling the top surface center. When you can align two side centers, the remaining will at most be a three-central-block swap involving the top surface.&lt;/p&gt;
&lt;h3 id=&quot;l2l-method&quot;&gt;L2L Method&lt;a class=&quot;anchor&quot; href=&quot;#l2l-method&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The two-step method is a more advanced method, but in some cases, the solution is not as good as LBL Method. For example, in some OLL cases in ZBLL, it is better to directly do OLL itself.&lt;/p&gt;
&lt;p&gt;L2L, Last Two Layers: After completing the bottom face, at most five S or H moves are needed to restore the skewb. Remember the SH combinations and the rotations corresponding to each situation, and restore the cube in one go.
A total of 134 formulas can be referenced. &lt;a href=&quot;https://www.cuberoot.me/skewb-l2l-by-learning-order/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Skewb L2L Method&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;NS Method: A supplement to the L2L Method. In the L2L Method, when it requires four or five SH superpositions to complete, continuing to use the L2L Method involves too many steps and takes too long. Therefore, the NS Method was created to supplement the cumbersome situations in the L2L Method with new formulas.&lt;/p&gt;
&lt;p&gt;KK method: Same as the NS method, but not very useful, gradually phased out.&lt;/p&gt;
&lt;h3 id=&quot;full-prediction&quot;&gt;Full Prediction&lt;a class=&quot;anchor&quot; href=&quot;#full-prediction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Deduce the situation after restoring one side, then restore the skewb cube using the two-step method without any pauses or thinking in between.&lt;/p&gt;
&lt;h2 id=&quot;symbol-description&quot;&gt;Symbol Description&lt;a class=&quot;anchor&quot; href=&quot;#symbol-description&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;basic-notations&quot;&gt;Basic Notations&lt;a class=&quot;anchor&quot; href=&quot;#basic-notations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/0888e748beac57de67e7257e3cb47a96.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Corner notations&lt;/p&gt;
&lt;p&gt;The rotation method is the same as the third order, when looking at a face, a clockwise rotation on that face is the notation for that corner (without an apostrophe).&lt;/p&gt;
&lt;h3 id=&quot;hs-notations&quot;&gt;H&amp;amp;S Notations&lt;a class=&quot;anchor&quot; href=&quot;#hs-notations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The names S (Sledge) and H (Hedge) originally came from the terminology of the 3x3 cube, but as they were gradually phased out in the 3x3, they were found to be very suitable for the Skewb, and thus were popularized.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/2ea724936d1f88d3ded9b23ac3ead694.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;S&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/017ba2526586fd1f3a2b27c38f4dabf5.png&quot; alt style=&quot;width: 30%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;H&lt;/p&gt;
&lt;h2 id=&quot;optimized-lbl-method-1&quot;&gt;Optimized LBL Method&lt;a class=&quot;anchor&quot; href=&quot;#optimized-lbl-method-1&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;fl-first-layer&quot;&gt;FL (First Layer)&lt;a class=&quot;anchor&quot; href=&quot;#fl-first-layer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This step requires restoring the bottom corner pieces and the bottom center.&lt;/p&gt;
&lt;p&gt;Overall, it is similar to the bottom cross of a 3x3 cube, requiring a lot of practice. Once you become more familiar with how the Skewb rotates, this step will be easily completed.&lt;/p&gt;
&lt;p&gt;However, during the first few solves, you might encounter trouble with the last corner. It is very likely that while trying to solve this corner, you might disrupt an already solved one. At this point, there are two situations that can be handled with algorithms.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/b24a72c097f018387f86afff3aeb3b9b.png&quot; alt style=&quot;width: 80%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;FL (First Layer)&lt;/p&gt;
&lt;h3 id=&quot;ll-last-layer&quot;&gt;LL (Last Layer)&lt;a class=&quot;anchor&quot; href=&quot;#ll-last-layer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This step requires simultaneously restoring the top layer corner pieces and the top layer center.&lt;/p&gt;
&lt;p&gt;There are a total of ten small cases, the formulas are as follows.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/b63670ef8be810c18201fd1a2e9b8ed1.png&quot; alt style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;LL (Last Layer)&lt;/p&gt;
&lt;h3 id=&quot;side-centers&quot;&gt;Side Centers&lt;a class=&quot;anchor&quot; href=&quot;#side-centers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This step requires restoring the center pieces on the sides, similar to the beginner’s method.&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/a847de72555ff98704f28da51bb65a5d.png&quot; alt style=&quot;width: 100%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Side Centers&lt;/p&gt;
&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;a class=&quot;anchor&quot; href=&quot;#credits&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cuberoot.me/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Cube Root&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;李宗阳 &lt;a href=&quot;https://www.bilibili.com/video/BV1Tp4y1e74q/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;控心法&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gancube.com/zh/gancube-tutorials-skewb/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gan’s Skewb tutorial&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/421944291&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;斜转魔方: Skewb速拧法&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/skewb_cover.DfR1oFly.png"/></item><item><title>S14E08 Review</title><link>https://www.lyt0112.com/blog/dw2</link><guid isPermaLink="true">https://www.lyt0112.com/blog/dw2</guid><description>有钱是真有钱了, 但是 RTD 有点像在逗我</description><pubDate>Sat, 22 Jun 2024 20:16:05 GMT</pubDate><content:encoded>&lt;p&gt;看了看 &lt;a href=&quot;https://tieba.baidu.com/p/9061254728&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;讨论贴&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt; 基本上贬大于褒, 参考一些讨论总结一下&lt;/p&gt;
&lt;h2 id=&quot;优点&quot;&gt;优点&lt;a class=&quot;anchor&quot; href=&quot;#优点&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;这一季 Kate 的存在感足够多, 很帅.&lt;/li&gt;
&lt;li&gt;还有 Sarah Jane 的一个反派 Trickster出现, 有点唤起了六年之前的暑假的回忆.&lt;/li&gt;
&lt;/ol&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/0c13faf65b8067bbaa7e372f860030c5.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Sarah Jane 年轻时候好好看&lt;/p&gt;
&lt;h2 id=&quot;缺点&quot;&gt;缺点&lt;a class=&quot;anchor&quot; href=&quot;#缺点&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;像遛狗一样就遛死 Sutekh 了??? 有点草率了感觉.&lt;/li&gt;
&lt;li&gt;Ruby 是一个 human 也不是不行, 试图跳出俗套也挺好的, 那凭什么就下雪了❄️还有圣诞颂歌, 毫无逻辑, 真就是因为 “We invest things with significance.” 呗.&lt;/li&gt;
&lt;li&gt;为什么 Goblin 会管 Ruby 叫 “The Beast” , 难绷.&lt;/li&gt;
&lt;li&gt;之前Ruby寻亲的时候没找到亲生父母还可以勉强解释成他父母的基因都没入寻亲机构的基因库. 但是既然在2046年战争首相就强制采集DNA了, 那在遥远的未来, 救护机为什么检测不到Ruby亲生父母的信息呢.&lt;/li&gt;
&lt;li&gt;那个勺子桥段也太离谱了, 不懂意义在哪里, 摸不着头脑, 如果是为了搞笑为啥那么严肃… 看看之前的勺子都是怎么做的, 比如 12 的 Robin hood 那一集还挺好玩的.&lt;/li&gt;
&lt;/ol&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/56b619eae61a8d44d88d30bc4908c196.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Spoon!&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;73 yard 明明是 Moffat 在桥边为了让人恰好看不清脸随便找的一个距离, 怎么就恰巧跟T娘的设定一样了.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;wait-and-see&quot;&gt;Wait and See&lt;a class=&quot;anchor&quot; href=&quot;#wait-and-see&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;尚未揭晓的伏笔还很多&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mrs.Flood是谁? 能打破次元壁与观众互动、知道T娘、知道Ruby会是博士的同伴、知道苏泰克, 或许还有其他神、衣着模仿克拉拉&amp;amp;Romana I, 还会说“clever boy”, 所以Mrs. Flood到底是谁? 从本集她谈论造物主和结尾的那段话, 感觉不太像是克拉拉、Susan和Romana (她们仨应该不会用讲故事的语气和表情说15的结局会很惨吧) … 会是Missy吗? 还是Rani?&lt;/li&gt;
&lt;/ol&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/b443e59a1742e92f8a1ec248d2934881.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Romana I&lt;/p&gt;
&lt;p&gt;在翻贴吧的时候发现 Romana II 美美美&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/a56e978a48ccc0dbbabbda9f9e5cc9ff.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;Romana II&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;gravity变成mavity, 60周年特辑的这个伏笔到现在也没有任何揭晓的迹象. 考虑到“ma-”这个字头和”magic”, “marvel”相似，而且牛顿在物理学的奠基地位, 这个伏笔似乎是在说世界由科学转向魔法. 这是个最基础的伏笔,至少要到第三季结尾,才会真正扭转过来, 考虑到 RTD 确实喜欢埋多季的伏笔, 这样也不是不行.&lt;/li&gt;
&lt;li&gt;撒盐对世界的影响，这和第一条是类似的, 都加重了这个世界的魔法性.&lt;/li&gt;
&lt;li&gt;Ruby的超能力: 下雪, Maestro说这个能力来源于她内心深处隐藏的一首歌, 而Maestro无法将其吸出, 所以Ruby的谜题并未全部揭晓.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;next&quot;&gt;Next&lt;a class=&quot;anchor&quot; href=&quot;#next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;魔法特将于2024年圣诞特辑《普世欢腾》(Joy to the World）回归:&lt;/p&gt;
&lt;p&gt;“我不能拒绝, 我喜欢写圣诞特辑, 我想过 ‘写过《Boom》了, 不需要再写了’ 然后RTD给我发邮件 ‘你想要写圣诞特辑吗?’ 我回他‘好的, 必须写’ 每写一个故事都可能是我最后一个故事, 我总是想发掘一些东西, 有时圣诞特辑似乎比其它常规集更以博士为核心. 我真的不能多说, 圣诞礼物要留在圣诞节拆开.”&lt;/p&gt;
&lt;img src=&quot;https://cdn.lyt0112.com/2024/06/64ca724c6fb95d124d40e644962b6204.png&quot; alt style=&quot;width: 50%; height: auto; display: block; margin: 0 auto;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;/&gt;
&lt;p style=&quot;text-align: center; font-style: italic; color: gray;&quot;&gt;2024 Christmas Special 剧照&lt;/p&gt;
&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;a class=&quot;anchor&quot; href=&quot;#credits&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.digitalspy.com/tv/a60835258/doctor-who-varada-sethu-surprise-appearance&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Doctor Who’s Steven Moffat addresses surprise early Varada Sethu appearance&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://screenrant.com/doctor-who-season-14-ncuti-gatwa-modern-doctors-interaction-steven-moffat/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;How The New Doctor Would Interact With Other Modern Doctors Revealed By Past Doctor Who Showrunner&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://screenrant.com/doctor-who-season-14-steven-moffat-interview/&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Steven Moffat Explains What Makes “Boom” A Defining Doctor Who Episode For Ncuti Gatwa’s Doctor&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tvinsider.com/1136487/doctor-who-boom-varada-sethu-mundy-susan-twist-characters-steven-moffat&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;‘Doctor Who’: Steven Moffat Talks Introducing Spoiler &amp;amp; What Wasn’t in First Draft of ‘Boom’ Script&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/dw-s14.Dt39ouu8.jpg"/></item><item><title>Doctor Who S14 24hrs to Go!</title><link>https://www.lyt0112.com/blog/dw1</link><guid isPermaLink="true">https://www.lyt0112.com/blog/dw1</guid><description>Woohoo! Here we go!</description><pubDate>Thu, 02 May 2024 20:15:26 GMT</pubDate><content:encoded>&lt;h2 id=&quot;说明&quot;&gt;说明&lt;a class=&quot;anchor&quot; href=&quot;#说明&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;本季是自1963年老版开播至今的第40季，是2005年新版复播软重启至今的第14季，也是BBC和Disney+在2023年四部特辑再度软重启全新开启的第1季。日常怎么称呼都可以（比如把1季称呼为14季），只需了解BBC官方定义和运作人RTD专访上《神秘博士》电视剧历史阶段划分为三个时代。综合BBC官方、运作人RTD、外网胡粉、国内胡粉对三个时代的称谓，总结如下（为了避免大家问名单里譬如“怎么没有战争博士和逃犯博士”这种问题，我在其他博士写明他们，只出现在官方杂志、或运作人采访譬如月亮博士、以及老版的养蜂人，因未在银幕中证明身份的，只是运作人嘴上说说，却没在剧里呈现的，我就不加入里面了）&lt;/p&gt;
&lt;h3 id=&quot;一经典系列老版bbc&quot;&gt;一、经典系列老版（BBC）&lt;a class=&quot;anchor&quot; href=&quot;#一经典系列老版bbc&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;1963年至1996年
（1963年至1989年共26季和1996年电影）&lt;/p&gt;
&lt;p&gt;主演:  1任至8任博士
其他博士: 莫比乌斯博士（1任之前）、Valeyard（无法重生后博士具象化黑暗面）、梅林（未来博士）&lt;/p&gt;
&lt;h3 id=&quot;二复活系列05新版bbc&quot;&gt;二、复活系列05新版（BBC）&lt;a class=&quot;anchor&quot; href=&quot;#二复活系列05新版bbc&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;2005年至2022年（共13季）&lt;/p&gt;
&lt;p&gt;主演: 9任至13任博士
其他博士: 战争博士（8任和9任之间时间大战时期）、馆长/策展人(博士退休后可自由更改过去最爱面孔的老年形态)、逃犯博士（1任之前）&lt;/p&gt;
&lt;h3 id=&quot;三复活系列23新版bbc--disney&quot;&gt;三、复活系列23新版（BBC &amp;amp; Disney+）&lt;a class=&quot;anchor&quot; href=&quot;#三复活系列23新版bbc--disney&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;2023年至今&lt;/p&gt;
&lt;p&gt;主演: 14任（结局为博士退休）/15任博士至今
其他博士: 老年5任博士（双重生存活至今），老年6任博士（双重生存活至今），老年7任博士（双重生存活至今）&lt;/p&gt;
&lt;h2 id=&quot;credits&quot;&gt;Credits&lt;a class=&quot;anchor&quot; href=&quot;#credits&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://tieba.baidu.com/p/9010746968&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://tieba.baidu.com/p/9010746968&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/tardis-clara.DKqFUOna.jpeg"/></item><item><title>Hello World!</title><link>https://www.lyt0112.com/blog/hello_world</link><guid isPermaLink="true">https://www.lyt0112.com/blog/hello_world</guid><description>Hello dear someone in the world, nice to meet you from the beginning of the story!</description><pubDate>Wed, 01 May 2024 09:13:43 GMT</pubDate><content:encoded>&lt;h2 id=&quot;hi-there&quot;&gt;Hi There!&lt;a class=&quot;anchor&quot; href=&quot;#hi-there&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Hello dear someone in the world, hello from the beginning of the story!&lt;/p&gt;
&lt;h2 id=&quot;my-blogs&quot;&gt;My Blogs&lt;a class=&quot;anchor&quot; href=&quot;#my-blogs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;My first website theme was created on May 1, 2024, followed the style of &lt;a href=&quot;https://github.com/shiro/blog&quot; rel=&quot;nofollow noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Shiro&lt;span&gt; ↗&lt;/span&gt;&lt;/a&gt;, and was stopped at July 28, 2024.&lt;/li&gt;
&lt;li&gt;The second website theme was created on July 28, 2024, and is still in use.&lt;/li&gt;
&lt;/ol&gt;</content:encoded><h:img src="https://www.lyt0112.com/_astro/thumbnail-test-4.B8eVBn7q.jpg"/></item></channel></rss>