<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>QEMU on 夜云泊</title>
    <link>https://lifeislife.cn/tags/qemu/</link>
    <description>feedId:57980998056508425+userId:73222296380546048 Recent content in QEMU on 夜云泊</description>
    <generator>Hugo -- 0.157.0</generator>
    <language>zh</language>
    <lastBuildDate>Sat, 10 Aug 2024 21:08:03 +0000</lastBuildDate>
    <atom:link href="https://lifeislife.cn/tags/qemu/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>QEMU Decodetree详解</title>
      <link>https://lifeislife.cn/posts/qemu-decodetree%E8%AF%A6%E8%A7%A3/</link>
      <pubDate>Sat, 10 Aug 2024 21:08:03 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu-decodetree%E8%AF%A6%E8%A7%A3/</guid>
      <description>&lt;p&gt;QEMU 在 decode 指令的时候，需要调用各平台所定义的 instruction decoders 来解析指令。如在 ARM 平台下，就定义了：&lt;code&gt;disas_arm_insn()&lt;/code&gt;、&lt;code&gt;disas_thumb_insn()&lt;/code&gt; 及 &lt;code&gt;disas_thumb2_insn()&lt;/code&gt; 等来分别负责 ARM 32-bits 指令、ARM Thumb 指令及 ARM Thumb2 指令的解析。&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;Decodetree&lt;/code&gt; 则是由 &lt;code&gt;Bastian Koppelmann&lt;/code&gt; 于 2017 年在 移植 RISC-V QEMU 的时候所提出来的机制 (详见：&lt;a href=&#34;https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07735.html&#34;&gt;讨论邮件1&lt;/a&gt;、&lt;a href=&#34;https://lists.gnu.org/archive/html/qemu-devel/2017-10/msg05046.html&#34;&gt;讨论邮件2&lt;/a&gt;)。提出该机制主要是因为过往的 instruction decoders (如：ARM) 都是采用一堆 &lt;code&gt;switch-case&lt;/code&gt; 来做判断。不仅难阅读，也难以维护。&lt;/p&gt;
&lt;p&gt;因此 &lt;code&gt;Bastian Koppelmann&lt;/code&gt; 就提出了 &lt;code&gt;Decodetree&lt;/code&gt; 的机制，开发者只需要通过 &lt;code&gt;Decodetree&lt;/code&gt; 的语法定义各个指令的格式，便可交由 &lt;code&gt;Decodetree&lt;/code&gt; 来动态生成对应包含 &lt;code&gt;switch-case&lt;/code&gt; 的 instruction decoder &lt;code&gt;.c&lt;/code&gt; 文档。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 特别适合像 RISC-V 这种具有&lt;strong&gt;固定指令格式&lt;/strong&gt;的 ISA。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;因为各字段都在固定的位置，(如 RISC-V 的 &lt;code&gt;opcode&lt;/code&gt; 都是固定在 &lt;code&gt;bits[6..0]&lt;/code&gt; 的位置)。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2F5a051d22c43a2ce34069fecd2e4fb8c0.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2F5a051d22c43a2ce34069fecd2e4fb8c0.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 其实是由 Python script (&lt;code&gt;./scripts/decodetree.py&lt;/code&gt;) 所生成的。使用文档可以参考：&lt;code&gt;./docs/devel/decodetree.rst&lt;/code&gt;，里面有详细定义了其语法的格式。QEMU 在编译时，会调用 &lt;code&gt;Decodetree&lt;/code&gt;，根据各平台所定义的 decode 文档，动态生成对应的 decoder。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如 RISC-V 的 instruction decoders 就是被定义在：&lt;code&gt;./target/riscv/*.decode&lt;/code&gt; 中。其 &lt;code&gt;Makefile.obj&lt;/code&gt; 就有如下的声明：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;DECODETREE = $(SRC_PATH)/scripts/decodetree.py
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;decode32-y = $(SRC_PATH)/target/riscv/insn32.decode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;decode32-$(TARGET_RISCV64) += $(SRC_PATH)/target/riscv/insn32-64.decode
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;target/riscv/decode_insn32.inc.c: $(decode32-y) $(DECODETREE)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	$(call quiet-command, \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	  $(PYTHON) $(DECODETREE) -o $@ --static-decode decode_insn32 \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          $(decode32-y), &amp;#34;GEN&amp;#34;, $(TARGET_DIR)$@)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 的语法共分为：Fields、Argument Sets、Formats、Patterns 五部分。本文将介绍如何通过 &lt;code&gt;Decodetree&lt;/code&gt; 的语法，来动态生成一个指令的 decoder。&lt;/p&gt;
&lt;h2 id=&#34;field&#34;&gt;Field&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Field&lt;/code&gt; 定义如何取出一指令中，各&lt;strong&gt;字段&lt;/strong&gt; (eg: &lt;code&gt;rd&lt;/code&gt;, &lt;code&gt;rs1&lt;/code&gt;, &lt;code&gt;rs2&lt;/code&gt;, &lt;code&gt;imm&lt;/code&gt;) 的值。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;field_def     := &amp;#39;%&amp;#39; identifier ( unnamed_field )* ( !function=identifier )?
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;unnamed_field := number &amp;#39;:&amp;#39; ( &amp;#39;s&amp;#39; ) number
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其语法由 &lt;code&gt;%&lt;/code&gt; 开头，随后紧接着一个 &lt;code&gt;identifier&lt;/code&gt; 及零个或多个 &lt;code&gt;unamed_field&lt;/code&gt;，并可再加上可选的 &lt;code&gt;!function&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;identifier&lt;/code&gt; 可由开发者自定，如：&lt;code&gt;rd&lt;/code&gt;、&lt;code&gt;imm&lt;/code&gt;… 等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unamed_field&lt;/code&gt; 定义了该字段的所在比特。第一个数字定义了该字段的 &lt;code&gt;least-significant bit position&lt;/code&gt;，第二个数字则定义了该字段的&lt;code&gt;比特长度&lt;/code&gt;。另外可加上可选的 &lt;code&gt;s&lt;/code&gt; 字符来标明在取出该字段后，是否需要做 符号扩展。
&lt;ul&gt;
&lt;li&gt;Eg：&lt;code&gt;%rd 7:5&lt;/code&gt; 代表 &lt;code&gt;rd&lt;/code&gt; 占了指令中 bits 7 ~ bits 11 的位置 (insn[11:7])，共 5 bits。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!function&lt;/code&gt; 定义在截取出该字段的值后，所会再调用的 function。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Field&lt;/code&gt; (32-bits 指令) 最后会生成对应的 &lt;code&gt;extract32()&lt;/code&gt; 及 &lt;code&gt;sextract32()&lt;/code&gt; 代码，以用来取得指令中各字段的值：&lt;/p&gt;
&lt;h3 id=&#34;field-示例&#34;&gt;Field 示例&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Input&lt;/th&gt;
          &lt;th&gt;Generated code&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;%disp 0:s16&lt;/td&gt;
          &lt;td&gt;sextract(i, 0, 16)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;%imm9 16:6 10:3&lt;/td&gt;
          &lt;td&gt;extract(i, 16, 6) &amp;laquo; 3&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;%disp12 0:s1 1:1 2:10&lt;/td&gt;
          &lt;td&gt;sextract(i, 0, 1) &amp;laquo; 11&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;%shimm8 5:s8 13:1 !function=expand_shimm8&lt;/td&gt;
          &lt;td&gt;expand_shimm8(sextract(i, 5, 8)) &amp;laquo; 1&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;以 RISC-V 的 &lt;code&gt;U-type&lt;/code&gt; 指令为例：&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2Ffb558390c2c5a8c8b9e1645d90cabfc8.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2Ffb558390c2c5a8c8b9e1645d90cabfc8.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;其中，&lt;code&gt;imm&lt;/code&gt; 占 &lt;code&gt;insn[31:12]&lt;/code&gt;，共20位，&lt;code&gt;rd&lt;/code&gt; 占 &lt;code&gt;insn[11:7]&lt;/code&gt;，且 &lt;code&gt;imm&lt;/code&gt; 需要做 符号扩展 后 &lt;code&gt;左移 12 位&lt;/code&gt; (&lt;code&gt;20-bit immediate is shifted left by 12 bits to form U immediates&lt;/code&gt;)。因此，如果我们要定义 RISC-V 的 &lt;code&gt;U-type&lt;/code&gt; 指令，则可以声明成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd       7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%imm_u    12:s20                 !function=ex_shift_12
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;20 表示占 20 bits&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最后会生成如下的代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;static void decode_insn32_extract_u()&lt;/code&gt; 是由下文 Format 定义所生成的，而 &lt;code&gt;arg_u *a&lt;/code&gt; 则是由 Argument Set 定义所生成的，将会在后面的部分再做说明。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可以看到：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;a-&amp;gt;imm&lt;/code&gt; 是由 &lt;code&gt;insn[31:12]&lt;/code&gt; 所取得并做符号扩展，且会再调用 &lt;code&gt;ex_shift_12()&lt;/code&gt; 来 &lt;code&gt;左移 12 个 bits&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;P.S. RISC-V 的 &lt;code&gt;ex_shift_12()&lt;/code&gt; 是通过定义在&lt;code&gt;./target/riscv/translate.c&lt;/code&gt; 中 &lt;code&gt;EX_SH&lt;/code&gt; 这个 macro 所展开的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define EX_SH(amount) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    static int ex_shift_##amount(DisasContext *ctx, int imm) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    {                                         \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        return imm &amp;lt;&amp;lt; amount;                 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;EX_SH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;a-&amp;gt;rd&lt;/code&gt; 是由 &lt;code&gt;insn[11:7]&lt;/code&gt; 所取得。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，在 &lt;code&gt;Decodetree&lt;/code&gt; 的 spec 中也有提到，我们可以通过只定义 &lt;code&gt;!function&lt;/code&gt; 来直接调用该 function。在这种情况下，只有 &lt;code&gt;DisasContext&lt;/code&gt; 会被传入该 function。&lt;/p&gt;
&lt;p&gt;如 ARM Thumb &lt;code&gt;./target/arm/t16.decode&lt;/code&gt; 就有定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Set S if the instruction is outside of an IT block.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%s               !function=t16_setflags
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;disas_t16_extract_addsub_2i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_s_rri_rot&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;t16_setflags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rot&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;请注意，未包含任何 &lt;code&gt;unnamed_fields&lt;/code&gt; 或 &lt;code&gt;!function&lt;/code&gt; 的 &lt;code&gt;Field&lt;/code&gt; 会被视为错误。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;argument-set&#34;&gt;Argument Set&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Argument Set&lt;/code&gt; 定义用来保存从指令中所截取出来各字段的值。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;args_def    := &amp;#39;&amp;amp;&amp;#39; identifier ( args_elt )+ ( !extern )?
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;args_elt    := identifier
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其语法由 &lt;code&gt;&amp;amp;&lt;/code&gt; 开头，随后紧接着一个或多个的 &lt;code&gt;identifier&lt;/code&gt; ，并可再加上可选的 &lt;code&gt;!extern&lt;/code&gt; 。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;identifier&lt;/code&gt; 可由开发者自订，如：&lt;code&gt;regs&lt;/code&gt;、&lt;code&gt;loadstore&lt;/code&gt;… 等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!extern&lt;/code&gt; 则表示是否在其他地方已经由其他的 decoder 定义过。如果有该字段，就&lt;strong&gt;不会&lt;/strong&gt;再次生成对应的 &lt;code&gt;argument set struct&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;argument-set-示例&#34;&gt;Argument Set 示例&lt;/h3&gt;
&lt;p&gt;例1：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;ampreg3 ra rb rc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会生成以下的 &lt;code&gt;argument set struct&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ra&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_reg3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例2：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;loadstore reg base offset
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;则会生成以下的 &lt;code&gt;argument set struct&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_loadstore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因此，以刚刚的 RISC-V &lt;code&gt;U-type&lt;/code&gt; 指令为例，我们需要从指令中截取 &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 字段的值，可以声明其 &lt;code&gt;argument set&lt;/code&gt; 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;u    imm rd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后会生成以下的 &lt;code&gt;argument set struct&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此 &lt;code&gt;argument set struct&lt;/code&gt; 会被传入由 &lt;code&gt;Format&lt;/code&gt; 定义所生成的 extract function：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所传入的&lt;code&gt;arg_u&lt;/code&gt; 会保存从指令中截取出的 &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 字段的值，待后续使用。&lt;/p&gt;
&lt;h2 id=&#34;format&#34;&gt;Format&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Format&lt;/code&gt; 定义了指令的格式 (如 RISC-V 中的 &lt;code&gt;R&lt;/code&gt;、&lt;code&gt;I&lt;/code&gt;、&lt;code&gt;S&lt;/code&gt;、&lt;code&gt;B&lt;/code&gt;、&lt;code&gt;U&lt;/code&gt;、&lt;code&gt;J-type&lt;/code&gt;)，并会生成对应的 decode function。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fmt_def      := &amp;#39;@&amp;#39; identifier ( fmt_elt )+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fmt_elt      := fixedbit_elt | field_elt | field_ref | args_ref
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fixedbit_elt := [01.-]+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;field_elt    := identifier &amp;#39;:&amp;#39; &amp;#39;s&amp;#39;? number
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;field_ref    := &amp;#39;%&amp;#39; identifier | identifier &amp;#39;=&amp;#39; &amp;#39;%&amp;#39; identifier
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;args_ref     := &amp;#39;&amp;amp;&amp;#39; identifier
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其语法由 &lt;code&gt;@&lt;/code&gt; 开头，随后紧接着一个 &lt;code&gt;identifier&lt;/code&gt; 及一个以上的 &lt;code&gt;fmt_elt&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;identifier&lt;/code&gt; 可由开发者自订，如：&lt;code&gt;opr&lt;/code&gt;、&lt;code&gt;opi&lt;/code&gt;… 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fmt_elt&lt;/code&gt; 则可以采用以下不同的语法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fixedbit_elt&lt;/code&gt; 包含一个或多个 &lt;code&gt;0&lt;/code&gt;、&lt;code&gt;1&lt;/code&gt;、&lt;code&gt;.&lt;/code&gt;、&lt;code&gt;-&lt;/code&gt;，每一个代表指令中的 1 个 bit。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.&lt;/code&gt; 代表该 bit 可以用 &lt;code&gt;0&lt;/code&gt; 或是 &lt;code&gt;1&lt;/code&gt; 来表示。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-&lt;/code&gt; 代表该 bit 完全被忽略。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;field_elt&lt;/code&gt; 可以用 Field 的语法来声明。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Eg：&lt;code&gt;ra:5&lt;/code&gt;、&lt;code&gt;rb:5&lt;/code&gt;、&lt;code&gt;lit:8&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;field_ref&lt;/code&gt; 有下列两种格式 (以下范例参考上文所定义之 Field)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&#39;%&#39; identifier&lt;/code&gt;：直接参考一个被定义过的 &lt;code&gt;Field&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如：&lt;code&gt;%rd&lt;/code&gt;，会生成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;identifier &#39;=&#39; &#39;%&#39; identifier&lt;/code&gt;：直接参考一个被定义过的 &lt;code&gt;Field&lt;/code&gt;，但通过第一个 &lt;code&gt;identifier&lt;/code&gt; 来重命名其所对应的 &lt;code&gt;argument&lt;/code&gt; 名称。此方式可以用来指定不同的 &lt;code&gt;argument&lt;/code&gt; 名称来参考至同一个 &lt;code&gt;Field&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如：&lt;code&gt;my_rd=%rd&lt;/code&gt;，会生成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;my_rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;args_ref&lt;/code&gt; 指定所传入 decode function 的 &lt;code&gt;Argument Set&lt;/code&gt;。若没有指定 &lt;code&gt;args_ref&lt;/code&gt; 的话，&lt;code&gt;Decodetree&lt;/code&gt; 会根据 &lt;code&gt;field_elt&lt;/code&gt; 或 &lt;code&gt;field_ref&lt;/code&gt; 自动生成一个 &lt;code&gt;Argument Set&lt;/code&gt;。此外，一个 &lt;code&gt;Format&lt;/code&gt; 最多只能包含一个 &lt;code&gt;args_ref&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当 &lt;code&gt;fixedbit_elt&lt;/code&gt; 或 &lt;code&gt;field_ref&lt;/code&gt; 被定义时，该 &lt;code&gt;Foramt&lt;/code&gt; 的所有的 bits 都必须被定义 (可通过 &lt;code&gt;fixedbit_elt&lt;/code&gt; 或 &lt;code&gt;.&lt;/code&gt; 来定义各个 bits，&lt;code&gt;空格&lt;/code&gt;会被忽略)。&lt;/p&gt;
&lt;h3 id=&#34;format-示例&#34;&gt;Format 示例&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@opi    ...... ra:5 lit:8    1 ....... rc:5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义了 &lt;code&gt;op1&lt;/code&gt; 这个 &lt;code&gt;Format&lt;/code&gt;，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[31:26] 可为 &lt;code&gt;0&lt;/code&gt; 或 &lt;code&gt;1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[25:21] 为 &lt;code&gt;ra&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[20:13] 为 &lt;code&gt;lit&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[12] 固定为 &lt;code&gt;1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[11:5] 可为 &lt;code&gt;0&lt;/code&gt; 或 &lt;code&gt;1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[4:0] 为 &lt;code&gt;rc&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此 &lt;code&gt;Format&lt;/code&gt; 会生成以下的 decode function：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ra&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_opi&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ra&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;13&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;由于我们没有指定 &lt;code&gt;args_ref&lt;/code&gt;，因此 &lt;code&gt;Decodetree&lt;/code&gt; 根据了 &lt;code&gt;field_elt&lt;/code&gt; 的定义，自动生成了 &lt;code&gt;arg_decode_insn320&lt;/code&gt; 这个 &lt;code&gt;Argument Set&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;以 RISC-V &lt;code&gt;I-type&lt;/code&gt; 指令为例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fields:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rs1       15:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd        7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# immediates:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%imm_i    20:s12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Argment sets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;i    imm rs1 rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@i       ........ ........ ........ ........ &amp;amp;i      imm=%imm_i     %rs1 %rd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义了 &lt;code&gt;i&lt;/code&gt; 这个 &lt;code&gt;Format&lt;/code&gt;，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[31:20] 为 &lt;code&gt;imm&lt;/code&gt;，且为 符号扩展。&lt;/li&gt;
&lt;li&gt;insn[19:5] 为 &lt;code&gt;rs1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[11:7] 为 &lt;code&gt;rd&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，我们可以看到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;此 &lt;code&gt;Format&lt;/code&gt; 指定了 &lt;code&gt;Argument Set&lt;/code&gt;：&lt;code&gt;&amp;amp;i&lt;/code&gt;。 &lt;code&gt;&amp;amp;i&lt;/code&gt; 中必须包含所有有用到的 &lt;code&gt;arguments&lt;/code&gt; (也就是：&lt;code&gt;imm&lt;/code&gt;、&lt;code&gt;rs1&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;imm&lt;/code&gt; 是通过重命名的方式来参考 &lt;code&gt;%imm_i&lt;/code&gt; 这个 &lt;code&gt;Field&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此范例会生成以下的 decode function：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rs1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32extract_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rs1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;15&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;相比于第一个范例，由于这次我们有指定 &lt;code&gt;args_ref&lt;/code&gt;：&lt;code&gt;&amp;amp;i&lt;/code&gt;，因此对应的 &lt;code&gt;arg_i&lt;/code&gt; 会被传入 decode function。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;回到先前的 RISC-V &lt;code&gt;U-type&lt;/code&gt; 指令，我们可以如同 &lt;code&gt;I-type&lt;/code&gt; 指令定义其格式：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fields:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd        7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# immediates:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%imm_u    12:s20                 !function=ex_shift_12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Argument sets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;u    imm rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@u       ....................      ..... ....... &amp;amp;u      imm=%imm_u          %rd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义了 &lt;code&gt;u&lt;/code&gt; 这个 &lt;code&gt;Format&lt;/code&gt;，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[31:12] 为 &lt;code&gt;imm&lt;/code&gt;，且为 符号扩展。&lt;/li&gt;
&lt;li&gt;insn[11:7] 为 &lt;code&gt;rd&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;会生成以下的 decode function：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们可以看到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;此 &lt;code&gt;Format&lt;/code&gt; 指定了 &lt;code&gt;Argument Set&lt;/code&gt;：&lt;code&gt;&amp;amp;u&lt;/code&gt;。 &lt;code&gt;&amp;amp;u&lt;/code&gt; 中必须包含所有有用到的 &lt;code&gt;arguments&lt;/code&gt; (也就是：&lt;code&gt;imm&lt;/code&gt;、&lt;code&gt;rd&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;imm&lt;/code&gt; 是通过重命名的方式来参考 &lt;code&gt;%imm_u&lt;/code&gt; 这个 &lt;code&gt;Field&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;pattern&#34;&gt;Pattern&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Pattern&lt;/code&gt; 实际定义了一个指令的 decode 方式。&lt;code&gt;Decodetree&lt;/code&gt; 会根据 &lt;code&gt;Patterns&lt;/code&gt; 的定义，来动态产生出对应的 &lt;code&gt;switch-case&lt;/code&gt; decode 判断分支。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pat_def      := identifier ( pat_elt )+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pat_elt      := fixedbit_elt | field_elt | field_ref | args_ref | fmt_ref | const_elt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fmt_ref      := &amp;#39;@&amp;#39; identifier
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const_elt    := identifier &amp;#39;=&amp;#39; number
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其语法由用户所定义的 &lt;code&gt;identifier&lt;/code&gt;，随后紧接着一个以上的 &lt;code&gt;pat_elt&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;identifier&lt;/code&gt; 可由开发者自订，如：&lt;code&gt;addl_r&lt;/code&gt;、&lt;code&gt;addli&lt;/code&gt; … 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;pat_elt&lt;/code&gt; 则可以采用以下不同的语法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fixedbit_elt&lt;/code&gt; 与在 &lt;code&gt;Format&lt;/code&gt; 中 &lt;code&gt;fixedbit_elt&lt;/code&gt; 的定义相同。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;field_elt&lt;/code&gt; 与在 &lt;code&gt;Format&lt;/code&gt; 中 &lt;code&gt;field_elt&lt;/code&gt; 的定义相同。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;field_ref&lt;/code&gt; 与在 &lt;code&gt;Format&lt;/code&gt; 中 &lt;code&gt;field_ref&lt;/code&gt; 的定义相同。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;args_ref&lt;/code&gt; 与在 &lt;code&gt;Format&lt;/code&gt; 中 &lt;code&gt;args_ref&lt;/code&gt; 的定义相同。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fmt_ref&lt;/code&gt; 直接参考一个被定义过的Format。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;const_elt&lt;/code&gt; 可以直接指定某一个 &lt;code&gt;argument&lt;/code&gt; 的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由于 &lt;code&gt;Pattern&lt;/code&gt; 实际定义了一个指令的 decode 方式，因此&lt;strong&gt;所有的 bits&lt;/strong&gt; 及 &lt;strong&gt;arguments (如果有参考 args_ref 的话)&lt;/strong&gt;  都必须明确的被定义，如果在搭配了所有的 &lt;code&gt;pat_elt&lt;/code&gt; 后还有未定义的 bits 或是 arguments 的话，&lt;code&gt;Decodetree&lt;/code&gt; 便会报错。&lt;/p&gt;
&lt;p&gt;此外，&lt;code&gt;Pattern&lt;/code&gt; 所产生出来的 decoder，最后还会调用对应的 &lt;code&gt;translator function&lt;/code&gt;。&lt;code&gt;translator function&lt;/code&gt; 需开发者自行定义。&lt;/p&gt;
&lt;h3 id=&#34;pattern-示例&#34;&gt;Pattern 示例&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;addl_i   010000 ..... ..... .... 0000000 ..... @opi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义了 &lt;code&gt;addl_i&lt;/code&gt; 这个指令的 &lt;code&gt;Pattern&lt;/code&gt;，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[31:26] 为 &lt;code&gt;010000&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[11:5] 为 &lt;code&gt;0000000&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;参考了 Format 示例中 定义的 &lt;code&gt;@opi&lt;/code&gt; &lt;code&gt;Format&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;由于 &lt;code&gt;Pattern&lt;/code&gt; 的&lt;strong&gt;所有 bits&lt;/strong&gt; 都必须明确的被定义，因此 &lt;code&gt;@opi&lt;/code&gt; 必须包含其余 &lt;code&gt;insn[25:12]&lt;/code&gt; 及 &lt;code&gt;insn[4:0]&lt;/code&gt; 的格式定义，否则 &lt;code&gt;Decodetree&lt;/code&gt; 便会报错。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后 &lt;code&gt;addl_i&lt;/code&gt; 的 decoder 还会调用 &lt;code&gt;trans_addl_i()&lt;/code&gt; 这个 &lt;code&gt;translator function&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;搭配之前介绍的 Fields、Argument Sets 及 Formats，让我们再看几个完整的例子应该会更清楚 &lt;code&gt;Decodetree&lt;/code&gt; 是怎产生一个指令的 decoder 的。&lt;/p&gt;
&lt;p&gt;首先是 RISC-V 的 &lt;code&gt;lui&lt;/code&gt; 及 &lt;code&gt;auipc&lt;/code&gt; 指令：&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F922be39211d73158159edf9993a2c906.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F922be39211d73158159edf9993a2c906.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fields:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd        7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# immediates:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%imm_u    12:s20                 !function=ex_shift_12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Argument sets:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;u    imm rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Formats:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@u       ....................      ..... ....... &amp;amp;u      imm=%imm_u          %rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Patterns
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lui      ....................       ..... 0110111 @u
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;auipc    ....................       ..... 0010111 @u
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会产生以下 &lt;code&gt;lui&lt;/code&gt; 及 &lt;code&gt;auipc&lt;/code&gt; 的 decoder：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000007f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000017&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_auipc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000037&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_lui&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;回顾到目前为止所介绍的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Argument Sets&lt;/code&gt;：&lt;code&gt;&amp;amp;u&lt;/code&gt; 这个 &lt;code&gt;argument set&lt;/code&gt; 包含了 &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 这两个 &lt;code&gt;arguments&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Fields&lt;/code&gt;： &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 分别位在 insn[31:12] 及 insn[11:7]，且 &lt;code&gt;imm&lt;/code&gt; 为 符号扩展。最后在截取出 &lt;code&gt;imm&lt;/code&gt; 的值后，还会调用 &lt;code&gt;ex_shift_12()&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Formats&lt;/code&gt;：&lt;code&gt;@u&lt;/code&gt; 定义了 RISC-V &lt;code&gt;U-type&lt;/code&gt; 指令的格式&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;参考了 &lt;code&gt;&amp;amp;u&lt;/code&gt; 这个 &lt;code&gt;Argument Set&lt;/code&gt;，因此 decode function 会传入 &lt;code&gt;arg_u&lt;/code&gt; 作为参数。&lt;/li&gt;
&lt;li&gt;insn[31:12] 参考了 &lt;code&gt;imm_u&lt;/code&gt; 这个 &lt;code&gt;Field&lt;/code&gt; (并重命名为 &lt;code&gt;imm&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;insn[11:7] 参考了 &lt;code&gt;rd&lt;/code&gt; 这个 &lt;code&gt;Field&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ex_shift_12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sextract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Patterns&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lui&lt;/code&gt; 的 &lt;code&gt;opcode&lt;/code&gt; (insn[6:0]) 为 &lt;code&gt;0010111&lt;/code&gt;，也就是 &lt;code&gt;0x17&lt;/code&gt;，在产生出来的 &lt;code&gt;switch-case&lt;/code&gt; 中可以看到其对应的 &lt;code&gt;case&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lui&lt;/code&gt; 的 decoder 最后调用了 &lt;code&gt;trans_lui()&lt;/code&gt;，并传入 &lt;code&gt;DisasContext&lt;/code&gt; 及经由 &lt;code&gt;decode_insn32_extract_u()&lt;/code&gt; 所解析出来的 &lt;code&gt;arg_u&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auipc&lt;/code&gt; 的 &lt;code&gt;opcode&lt;/code&gt; (insn[6:0]) 为 &lt;code&gt;0110111&lt;/code&gt;，也就是 &lt;code&gt;0x37&lt;/code&gt;，在产生出来的 &lt;code&gt;switch-case&lt;/code&gt; 中可以看到其对应的 &lt;code&gt;case&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auipc&lt;/code&gt; 的 decoder 最后调用了 &lt;code&gt;trans_auipc()&lt;/code&gt;，并传入 &lt;code&gt;DisasContext&lt;/code&gt; 及经由 &lt;code&gt;decode_insn32_extract_u()&lt;/code&gt; 所解析出来的 &lt;code&gt;arg_u&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;P.S. 这边由于 &lt;code&gt;Decodetree&lt;/code&gt; 发现 &lt;code&gt;lui&lt;/code&gt; 及 &lt;code&gt;auipc&lt;/code&gt; 可以共用 &lt;code&gt;decode_insn32_extract_u()&lt;/code&gt;，因此将其提到了 &lt;code&gt;switch-case&lt;/code&gt; 之外。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_u&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000007f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000017&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_auipc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000037&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_lui&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们另外可以发现，&lt;code&gt;Pattern&lt;/code&gt; + &lt;code&gt;Format&lt;/code&gt; 把所有的 32-bits 都给了明确的定义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Pattern&lt;/code&gt; 定义了 &lt;code&gt;opcode&lt;/code&gt; (insn[6:0])。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Format&lt;/code&gt; 参考了 &lt;code&gt;imm&lt;/code&gt; (insn[31:12]) 及 &lt;code&gt;rd&lt;/code&gt; (insn[11:7])。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果有任何未明确定义的 bits 的话，&lt;code&gt;Decodetree&lt;/code&gt; 便会报错，例如如果我们将 &lt;code&gt;lui&lt;/code&gt; 的 &lt;code&gt;opcode&lt;/code&gt; 最高 2 个 bits (insn[6:5]) 由 &lt;code&gt;01&lt;/code&gt; 改成 &lt;code&gt;..&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lui      ....................       ..... ..10111 @u
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 在解析时，便会报错：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;./insn32.decode:17: error: (‘bits left unspecified (0x00000060)’,)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;Decodetree&lt;/code&gt; 提醒我们，insn[6:5] (&lt;code&gt;0x00000060&lt;/code&gt;) 尚未给出明确定义，并会显示出其错误的行数。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;trans_lui()&lt;/code&gt; 和 &lt;code&gt;trans_auipc()&lt;/code&gt; 被定义在 &lt;code&gt;target/riscv/insn_trans/trans_rvi.inc.c&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;trans_lui&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_lui&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;tcg_gen_movi_tl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu_gpr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;trans_auipc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_auipc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;tcg_gen_movi_tl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cpu_gpr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pc_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到 &lt;code&gt;trans_*()&lt;/code&gt; 负责实际指令的业务逻辑及产生对应的 &lt;code&gt;TCG codes&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如同先前所介绍，&lt;code&gt;Patterns&lt;/code&gt; 的 &lt;code&gt;pat_elt&lt;/code&gt; 也可以采用 &lt;code&gt;field_elt&lt;/code&gt; 语法，如 RISC-V 的 &lt;code&gt;fence&lt;/code&gt; 指令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fence    ---- pred:4 succ:4 ----- 000 ----- 0001111
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;insn[27:24] 为 &lt;code&gt;pred&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[23:20] 为 &lt;code&gt;succ&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[14:12] 固定为 &lt;code&gt;000&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;insn[6:0] 为 &lt;code&gt;opcode&lt;/code&gt; (&lt;code&gt;0001111&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;没有参考任何的 &lt;code&gt;Format&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;剩下的 insn[31:28]、insn[19:15]、insn[11:7] 被声明为 &lt;code&gt;-&lt;/code&gt;，因此就算没有被明确定义也没有关系。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所生成 &lt;code&gt;fence&lt;/code&gt; 的 decoder 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;succ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_decode_insn32_Fmt_0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pred&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;succ&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_decode_insn320&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;decode_insn32_extract_decode_insn32_Fmt_0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000707f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000000f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_fence&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode_insn320&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;值得注意的是，虽然这次我们没有参考任何的 &lt;code&gt;Argument Set&lt;/code&gt;，但 &lt;code&gt;Decodetree&lt;/code&gt; 还是替我们生成了一个包含 &lt;code&gt;pred&lt;/code&gt; 和 &lt;code&gt;succ&lt;/code&gt; 的 &lt;code&gt;arg_decode_insn320&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;trans_fence()&lt;/code&gt; 同样是被定义在 &lt;code&gt;./target/riscv/insn_trans/trans_rvi.inc.c&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;trans_fence&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_fence&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;tcg_gen_mb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TCG_MO_ALL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TCG_BAR_SC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;pattern-groups&#34;&gt;Pattern Groups&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Pattern Groups&lt;/code&gt; 由一个以上的 &lt;code&gt;Patterns&lt;/code&gt; 所组成，其主要差别是不同 &lt;code&gt;Patterns&lt;/code&gt; 之间的 bits 可以 overlap。当同组中有多个 &lt;code&gt;Patterns&lt;/code&gt; 时，会依据该组中各 &lt;code&gt;Pattern&lt;/code&gt; 的声明顺序依序判断目前的指令是否符合其定义。除此之外，当符合的 &lt;code&gt;Pattern&lt;/code&gt; 其 &lt;code&gt;trans_*()&lt;/code&gt; 回传值为 &lt;code&gt;false&lt;/code&gt; 时，也会被视为&lt;strong&gt;不相符&lt;/strong&gt;，而继续判断该组中的下一个 &lt;code&gt;Pattern&lt;/code&gt;。因此 &lt;code&gt;Pattern Groups&lt;/code&gt; 非常适合将多个相似格式的指令给组成同一个 &lt;code&gt;Pattern Group&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;group    := &amp;#39;{&amp;#39; ( pat_def | group )+ &amp;#39;}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;各 &lt;code&gt;Pattern Group&lt;/code&gt; 以 &lt;code&gt;{&lt;/code&gt; 开头，并以 &lt;code&gt;}&lt;/code&gt; 结尾，且允许 &lt;code&gt;nested pattern groups&lt;/code&gt; 的存在，其他语法皆与 &lt;code&gt;Pattern&lt;/code&gt; 相同。&lt;/p&gt;
&lt;h3 id=&#34;pattern-group-示例&#34;&gt;Pattern Group 示例&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    nop   000010 ----- ----- 0000 001001 0 00000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    copy  000010 00000 r1:5  0000 001001 0 rt:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  or      000010 rt2:5 r1:5  cf:4 001001 0 rt:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;会产生以下的 decoder：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0xfc000fe0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x08000240&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000f000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000001f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;extract_decode_Fmt_0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_nop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x03e00000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nf&#34;&gt;extract_decode_Fmt_1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;				　
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;extract_decode_Fmt_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_or&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当指令的值符合 &lt;code&gt;nop&lt;/code&gt; 及 &lt;code&gt;copy&lt;/code&gt; 这个内层 &lt;code&gt;Pattern Group&lt;/code&gt; 时，会先判断该指令是否符合 &lt;code&gt;nop&lt;/code&gt; 指令的定义，且 &lt;code&gt;trans_nop()&lt;/code&gt; 的回传值为 &lt;code&gt;true&lt;/code&gt;。否则的话，就会继续判断是否符合同组中的 &lt;code&gt;copy&lt;/code&gt; 指令。若都不符，就会再判断是否符合外层 &lt;code&gt;Pattern Group&lt;/code&gt; 的 &lt;code&gt;or&lt;/code&gt; 指令。若仍不符，才会回传 &lt;code&gt;false&lt;/code&gt; 表示 decode 失败。&lt;/p&gt;
&lt;p&gt;与单纯使用 &lt;code&gt;Pattern&lt;/code&gt; 最大不同的是，当一 &lt;code&gt;Pattern&lt;/code&gt; 的 &lt;code&gt;trans_*()&lt;/code&gt; 回传值为 &lt;code&gt;false&lt;/code&gt; 时，不会直接回传 &lt;code&gt;false&lt;/code&gt; (代表 decode 失败)，而是会接续着判断后续的 &lt;code&gt;Patterns&lt;/code&gt; 是否相符。&lt;/p&gt;
&lt;p&gt;RISC-V Compressed-Extension 中的 &lt;code&gt;c.ebreak&lt;/code&gt;、&lt;code&gt;c.jalr&lt;/code&gt;、及 &lt;code&gt;c.add&lt;/code&gt; 指令，由于这三个指令的格式非常相似，因此非常适合使用 &lt;code&gt;Pattern Group&lt;/code&gt; 来定义：&lt;/p&gt;
&lt;p&gt;RISC-V spec. 中定义：&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F722c64cc1a321ef23fab81e722a24154.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F722c64cc1a321ef23fab81e722a24154.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;C.EBREAK&lt;/code&gt;指令与&lt;code&gt;C.ADD&lt;/code&gt;指令共享相同的&lt;code&gt;opcode&lt;/code&gt;，但是&lt;code&gt;rd&lt;/code&gt;和&lt;code&gt;rs2&lt;/code&gt;都为&lt;code&gt;zero&lt;/code&gt;，因此也可以使用&lt;code&gt;CR&lt;/code&gt;格式。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C.JALR&lt;/code&gt;指令只有在&lt;code&gt;rs1≠x0&lt;/code&gt;时才有效；当&lt;code&gt;rs1=x0&lt;/code&gt;时，对应的代码点是&lt;code&gt;C.EBREAK&lt;/code&gt;指令。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C.ADD&lt;/code&gt;指令只有在&lt;code&gt;rs2≠x0&lt;/code&gt;时才有效；当&lt;code&gt;rs2=x0&lt;/code&gt;时，对应的代码点是&lt;code&gt;C.JALR&lt;/code&gt;和&lt;code&gt;C.EBREAK&lt;/code&gt;指令。具有&lt;code&gt;rs2̸=x0&lt;/code&gt;和&lt;code&gt;rd=x0&lt;/code&gt;的代码点是&lt;code&gt;HINTs&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;c.ebreak&lt;/code&gt;、&lt;code&gt;c.jalr&lt;/code&gt;、&lt;code&gt;c.add&lt;/code&gt; 三个指令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;insn[15:13]、insn[12]、insn[1:0] 的值皆相同。&lt;/li&gt;
&lt;li&gt;当 insn[11:7] 且 insn[6:2] 的值皆为 &lt;code&gt;0&lt;/code&gt; (&lt;code&gt;rs1=0&lt;/code&gt; 且 &lt;code&gt;rs2=0&lt;/code&gt;) 时为 &lt;code&gt;c.ebreak&lt;/code&gt; 指令。&lt;/li&gt;
&lt;li&gt;当只有 insn[11:7] 的值为 &lt;code&gt;0&lt;/code&gt; (&lt;code&gt;rs1=0&lt;/code&gt; 且 &lt;code&gt;rs2≠0&lt;/code&gt;) 时为 &lt;code&gt;c.jalr&lt;/code&gt; 指令。&lt;/li&gt;
&lt;li&gt;否则为 &lt;code&gt;c.add&lt;/code&gt; 指令 (&lt;code&gt;rs1≠x0&lt;/code&gt; 且 &lt;code&gt;rs2≠0&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Fields
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rd        7:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;%rs2_5     2:5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Argument Sets
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;r         rd rs1 rs2   !extern
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;amp;i         imm rs1 rd   !extern
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Formats
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@cr        ....  ..... .....  .. &amp;amp;r      rs2=%rs2_5       rs1=%rd     %rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;@c_jalr    ... . .....  ..... .. &amp;amp;i      imm=0 rs1=%rd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# Pattern Groups
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ebreak          100 1  00000  00000 10
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  jalr            100 1  .....  00000 10 @c_jalr rd=1  # C.JALR
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  add             100 1  .....  ..... 10 @cr
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所生成的 decoder 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_c_jalr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;imm&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rs1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_cr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rs2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rs1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;extract32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_decode_insn16_Fmt_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg_decode_insn162&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;decode_insn16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DisasContext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint16_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;union&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_decode_insn162&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_decode_insn162&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_i&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;arg_r&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f_r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000f003&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00009002&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000ffc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_decode_insn16_Fmt_2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode_insn162&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_ebreak&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_decode_insn162&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x0000007c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x00000000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_c_jalr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_jalr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;decode_insn16_extract_cr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;insn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;trans_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;u&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;f_r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当指令格式符合 &lt;code&gt;c.ebreak&lt;/code&gt;、&lt;code&gt;c.jalr&lt;/code&gt;、&lt;code&gt;c.add&lt;/code&gt; 的 &lt;code&gt;Pattern Group&lt;/code&gt; 时，会依序判断该指令是否符合 &lt;code&gt;c.ebreak&lt;/code&gt;、&lt;code&gt;c.jalr&lt;/code&gt;、&lt;code&gt;c.add&lt;/code&gt; 的定义以及其对应的 &lt;code&gt;trans_*()&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;另外值得一提的是，在 &lt;code&gt;c_jalr&lt;/code&gt; &lt;code&gt;Format&lt;/code&gt; 和 &lt;code&gt;jalr&lt;/code&gt; &lt;code&gt;Pattern&lt;/code&gt; 中有分别指定其 &lt;code&gt;imm&lt;/code&gt; 及 &lt;code&gt;rd&lt;/code&gt; 的值为 &lt;code&gt;0&lt;/code&gt;，所生成的 codes 也会分别在对应的地方将该值设为 &lt;code&gt;0&lt;/code&gt; (见 codes 注解说明)。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;以上就是 &lt;code&gt;Decodetree&lt;/code&gt; 的语法说明。通过 &lt;code&gt;Decodetree&lt;/code&gt;，我们不用再像以前以样写一大包的 &lt;code&gt;switch-case&lt;/code&gt; 来 decode 指令。将不同类型的指令写至不同的 decode 档，不仅方便维护，阅读起来也更为容易。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--translate&lt;/code&gt;：translator function 的 prefix，默认为 &lt;code&gt;trans&lt;/code&gt;。一旦指定后，translator function 的 scope 就不会再是 &lt;code&gt;static&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--decode&lt;/code&gt;：decode function 的 prefix，默认为 &lt;code&gt;decode&lt;/code&gt;，且 scope 为 &lt;code&gt;static&lt;/code&gt;。一旦指定后，decode function 的 scope 就不会再是 &lt;code&gt;static&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--static-decode&lt;/code&gt;：如同 &lt;code&gt;--decode&lt;/code&gt;，不过 decode function 的 scope 仍维持为 &lt;code&gt;static&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o&lt;/code&gt; / &lt;code&gt;--output&lt;/code&gt;：指定生成的 decoder &lt;code&gt;.c&lt;/code&gt; 档路径。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-w&lt;/code&gt; / &lt;code&gt;--insnwidth&lt;/code&gt;：指令长度，eg：&lt;code&gt;32&lt;/code&gt; or &lt;code&gt;16&lt;/code&gt;，默认为 &lt;code&gt;32&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--varinsnwidth&lt;/code&gt;：指令为不定长度。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;最后一个参数&lt;/code&gt;为输入的 decode 档路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;运行范例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./decodetree.py -o target/riscv/decode_insn16.inc.c --static-decode decode_insn16 \
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -w 16 ./insn16.decode
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;static inline int32_t sextract32(uint32_t value, int start, int length){    assert(start &amp;gt;= 0 &amp;amp;&amp;amp; length &amp;gt; 0 &amp;amp;&amp;amp; length &amp;lt;= 32 - start);        return ((int32_t)(value &amp;lt;&amp;lt; (32 - length - start))) &amp;gt;&amp;gt; (32 - length);}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
      <content:encoded><![CDATA[<p>QEMU 在 decode 指令的时候，需要调用各平台所定义的 instruction decoders 来解析指令。如在 ARM 平台下，就定义了：<code>disas_arm_insn()</code>、<code>disas_thumb_insn()</code> 及 <code>disas_thumb2_insn()</code> 等来分别负责 ARM 32-bits 指令、ARM Thumb 指令及 ARM Thumb2 指令的解析。</p>
<p>而 <code>Decodetree</code> 则是由 <code>Bastian Koppelmann</code> 于 2017 年在 移植 RISC-V QEMU 的时候所提出来的机制 (详见：<a href="https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07735.html">讨论邮件1</a>、<a href="https://lists.gnu.org/archive/html/qemu-devel/2017-10/msg05046.html">讨论邮件2</a>)。提出该机制主要是因为过往的 instruction decoders (如：ARM) 都是采用一堆 <code>switch-case</code> 来做判断。不仅难阅读，也难以维护。</p>
<p>因此 <code>Bastian Koppelmann</code> 就提出了 <code>Decodetree</code> 的机制，开发者只需要通过 <code>Decodetree</code> 的语法定义各个指令的格式，便可交由 <code>Decodetree</code> 来动态生成对应包含 <code>switch-case</code> 的 instruction decoder <code>.c</code> 文档。</p>
<p><code>Decodetree</code> 特别适合像 RISC-V 这种具有<strong>固定指令格式</strong>的 ISA。</p>
<ul>
<li>因为各字段都在固定的位置，(如 RISC-V 的 <code>opcode</code> 都是固定在 <code>bits[6..0]</code> 的位置)。</li>
</ul>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2F5a051d22c43a2ce34069fecd2e4fb8c0.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2F5a051d22c43a2ce34069fecd2e4fb8c0.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p><code>Decodetree</code> 其实是由 Python script (<code>./scripts/decodetree.py</code>) 所生成的。使用文档可以参考：<code>./docs/devel/decodetree.rst</code>，里面有详细定义了其语法的格式。QEMU 在编译时，会调用 <code>Decodetree</code>，根据各平台所定义的 decode 文档，动态生成对应的 decoder。</p>
<ul>
<li>
<p>如 RISC-V 的 instruction decoders 就是被定义在：<code>./target/riscv/*.decode</code> 中。其 <code>Makefile.obj</code> 就有如下的声明：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">DECODETREE = $(SRC_PATH)/scripts/decodetree.py
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">decode32-y = $(SRC_PATH)/target/riscv/insn32.decode
</span></span><span class="line"><span class="cl">decode32-$(TARGET_RISCV64) += $(SRC_PATH)/target/riscv/insn32-64.decode
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">target/riscv/decode_insn32.inc.c: $(decode32-y) $(DECODETREE)
</span></span><span class="line"><span class="cl">	$(call quiet-command, \
</span></span><span class="line"><span class="cl">	  $(PYTHON) $(DECODETREE) -o $@ --static-decode decode_insn32 \
</span></span><span class="line"><span class="cl">          $(decode32-y), &#34;GEN&#34;, $(TARGET_DIR)$@)
</span></span></code></pre></div></li>
</ul>
<p><code>Decodetree</code> 的语法共分为：Fields、Argument Sets、Formats、Patterns 五部分。本文将介绍如何通过 <code>Decodetree</code> 的语法，来动态生成一个指令的 decoder。</p>
<h2 id="field">Field</h2>
<p><code>Field</code> 定义如何取出一指令中，各<strong>字段</strong> (eg: <code>rd</code>, <code>rs1</code>, <code>rs2</code>, <code>imm</code>) 的值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">field_def     := &#39;%&#39; identifier ( unnamed_field )* ( !function=identifier )?
</span></span><span class="line"><span class="cl">unnamed_field := number &#39;:&#39; ( &#39;s&#39; ) number
</span></span></code></pre></div><p>其语法由 <code>%</code> 开头，随后紧接着一个 <code>identifier</code> 及零个或多个 <code>unamed_field</code>，并可再加上可选的 <code>!function</code>。</p>
<ul>
<li><code>identifier</code> 可由开发者自定，如：<code>rd</code>、<code>imm</code>… 等。</li>
<li><code>unamed_field</code> 定义了该字段的所在比特。第一个数字定义了该字段的 <code>least-significant bit position</code>，第二个数字则定义了该字段的<code>比特长度</code>。另外可加上可选的 <code>s</code> 字符来标明在取出该字段后，是否需要做 符号扩展。
<ul>
<li>Eg：<code>%rd 7:5</code> 代表 <code>rd</code> 占了指令中 bits 7 ~ bits 11 的位置 (insn[11:7])，共 5 bits。</li>
</ul>
</li>
<li><code>!function</code> 定义在截取出该字段的值后，所会再调用的 function。</li>
</ul>
<p><code>Field</code> (32-bits 指令) 最后会生成对应的 <code>extract32()</code> 及 <code>sextract32()</code> 代码，以用来取得指令中各字段的值：</p>
<h3 id="field-示例">Field 示例</h3>
<table>
  <thead>
      <tr>
          <th>Input</th>
          <th>Generated code</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>%disp 0:s16</td>
          <td>sextract(i, 0, 16)</td>
      </tr>
      <tr>
          <td>%imm9 16:6 10:3</td>
          <td>extract(i, 16, 6) &laquo; 3</td>
      </tr>
      <tr>
          <td>%disp12 0:s1 1:1 2:10</td>
          <td>sextract(i, 0, 1) &laquo; 11</td>
      </tr>
      <tr>
          <td>%shimm8 5:s8 13:1 !function=expand_shimm8</td>
          <td>expand_shimm8(sextract(i, 5, 8)) &laquo; 1</td>
      </tr>
  </tbody>
</table>
<p>以 RISC-V 的 <code>U-type</code> 指令为例：</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2Ffb558390c2c5a8c8b9e1645d90cabfc8.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F18%2Ffb558390c2c5a8c8b9e1645d90cabfc8.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>其中，<code>imm</code> 占 <code>insn[31:12]</code>，共20位，<code>rd</code> 占 <code>insn[11:7]</code>，且 <code>imm</code> 需要做 符号扩展 后 <code>左移 12 位</code> (<code>20-bit immediate is shifted left by 12 bits to form U immediates</code>)。因此，如果我们要定义 RISC-V 的 <code>U-type</code> 指令，则可以声明成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">%rd       7:5
</span></span><span class="line"><span class="cl">%imm_u    12:s20                 !function=ex_shift_12
</span></span></code></pre></div><blockquote>
<p>20 表示占 20 bits</p>
</blockquote>
<p>最后会生成如下的代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><blockquote>
<p><code>static void decode_insn32_extract_u()</code> 是由下文 Format 定义所生成的，而 <code>arg_u *a</code> 则是由 Argument Set 定义所生成的，将会在后面的部分再做说明。</p>
</blockquote>
<p>可以看到：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span></code></pre></div><ul>
<li>
<p><code>a-&gt;imm</code> 是由 <code>insn[31:12]</code> 所取得并做符号扩展，且会再调用 <code>ex_shift_12()</code> 来 <code>左移 12 个 bits</code>。</p>
<ul>
<li>
<p>P.S. RISC-V 的 <code>ex_shift_12()</code> 是通过定义在<code>./target/riscv/translate.c</code> 中 <code>EX_SH</code> 这个 macro 所展开的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define EX_SH(amount) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    static int ex_shift_##amount(DisasContext *ctx, int imm) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">        return imm &lt;&lt; amount;                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nf">EX_SH</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p><code>a-&gt;rd</code> 是由 <code>insn[11:7]</code> 所取得。</p>
</li>
</ul>
<p>此外，在 <code>Decodetree</code> 的 spec 中也有提到，我们可以通过只定义 <code>!function</code> 来直接调用该 function。在这种情况下，只有 <code>DisasContext</code> 会被传入该 function。</p>
<p>如 ARM Thumb <code>./target/arm/t16.decode</code> 就有定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Set S if the instruction is outside of an IT block.
</span></span><span class="line"><span class="cl">%s               !function=t16_setflags
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">disas_t16_extract_addsub_2i</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_s_rri_rot</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rn</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">s</span> <span class="o">=</span> <span class="nf">t16_setflags</span><span class="p">(</span><span class="n">ctx</span><span class="p">);</span> 
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rot</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>请注意，未包含任何 <code>unnamed_fields</code> 或 <code>!function</code> 的 <code>Field</code> 会被视为错误。</p>
<hr>
<h2 id="argument-set">Argument Set</h2>
<p><code>Argument Set</code> 定义用来保存从指令中所截取出来各字段的值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">args_def    := &#39;&amp;&#39; identifier ( args_elt )+ ( !extern )?
</span></span><span class="line"><span class="cl">args_elt    := identifier
</span></span></code></pre></div><p>其语法由 <code>&amp;</code> 开头，随后紧接着一个或多个的 <code>identifier</code> ，并可再加上可选的 <code>!extern</code> 。</p>
<ul>
<li><code>identifier</code> 可由开发者自订，如：<code>regs</code>、<code>loadstore</code>… 等。</li>
<li><code>!extern</code> 则表示是否在其他地方已经由其他的 decoder 定义过。如果有该字段，就<strong>不会</strong>再次生成对应的 <code>argument set struct</code>。</li>
</ul>
<h3 id="argument-set-示例">Argument Set 示例</h3>
<p>例1：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">&amp;ampreg3 ra rb rc
</span></span></code></pre></div><p>会生成以下的 <code>argument set struct</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">ra</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_reg3</span><span class="p">;</span>
</span></span></code></pre></div><p>例2：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">&amp;loadstore reg base offset
</span></span></code></pre></div><p>则会生成以下的 <code>argument set struct</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">reg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_loadstore</span><span class="p">;</span>
</span></span></code></pre></div><p>因此，以刚刚的 RISC-V <code>U-type</code> 指令为例，我们需要从指令中截取 <code>imm</code> 及 <code>rd</code> 字段的值，可以声明其 <code>argument set</code> 如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">&amp;u    imm rd
</span></span></code></pre></div><p>最后会生成以下的 <code>argument set struct</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_u</span><span class="p">;</span>
</span></span></code></pre></div><p>此 <code>argument set struct</code> 会被传入由 <code>Format</code> 定义所生成的 extract function：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>所传入的<code>arg_u</code> 会保存从指令中截取出的 <code>imm</code> 及 <code>rd</code> 字段的值，待后续使用。</p>
<h2 id="format">Format</h2>
<p><code>Format</code> 定义了指令的格式 (如 RISC-V 中的 <code>R</code>、<code>I</code>、<code>S</code>、<code>B</code>、<code>U</code>、<code>J-type</code>)，并会生成对应的 decode function。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">fmt_def      := &#39;@&#39; identifier ( fmt_elt )+
</span></span><span class="line"><span class="cl">fmt_elt      := fixedbit_elt | field_elt | field_ref | args_ref
</span></span><span class="line"><span class="cl">fixedbit_elt := [01.-]+
</span></span><span class="line"><span class="cl">field_elt    := identifier &#39;:&#39; &#39;s&#39;? number
</span></span><span class="line"><span class="cl">field_ref    := &#39;%&#39; identifier | identifier &#39;=&#39; &#39;%&#39; identifier
</span></span><span class="line"><span class="cl">args_ref     := &#39;&amp;&#39; identifier
</span></span></code></pre></div><p>其语法由 <code>@</code> 开头，随后紧接着一个 <code>identifier</code> 及一个以上的 <code>fmt_elt</code>。</p>
<ul>
<li>
<p><code>identifier</code> 可由开发者自订，如：<code>opr</code>、<code>opi</code>… 等。</p>
</li>
<li>
<p><code>fmt_elt</code> 则可以采用以下不同的语法：</p>
<ul>
<li>
<p><code>fixedbit_elt</code> 包含一个或多个 <code>0</code>、<code>1</code>、<code>.</code>、<code>-</code>，每一个代表指令中的 1 个 bit。</p>
<ul>
<li><code>.</code> 代表该 bit 可以用 <code>0</code> 或是 <code>1</code> 来表示。</li>
<li><code>-</code> 代表该 bit 完全被忽略。</li>
</ul>
</li>
<li>
<p><code>field_elt</code> 可以用 Field 的语法来声明。</p>
<ul>
<li>Eg：<code>ra:5</code>、<code>rb:5</code>、<code>lit:8</code></li>
</ul>
</li>
<li>
<p><code>field_ref</code> 有下列两种格式 (以下范例参考上文所定义之 Field)：</p>
<ul>
<li>
<p><code>'%' identifier</code>：直接参考一个被定义过的 <code>Field</code>。</p>
<ul>
<li>
<p>如：<code>%rd</code>，会生成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span></code></pre></div></li>
</ul>
</li>
<li>
<p><code>identifier '=' '%' identifier</code>：直接参考一个被定义过的 <code>Field</code>，但通过第一个 <code>identifier</code> 来重命名其所对应的 <code>argument</code> 名称。此方式可以用来指定不同的 <code>argument</code> 名称来参考至同一个 <code>Field</code>。</p>
<ul>
<li>
<p>如：<code>my_rd=%rd</code>，会生成：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">my_rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span> 
</span></span></code></pre></div></li>
</ul>
</li>
</ul>
</li>
<li>
<p><code>args_ref</code> 指定所传入 decode function 的 <code>Argument Set</code>。若没有指定 <code>args_ref</code> 的话，<code>Decodetree</code> 会根据 <code>field_elt</code> 或 <code>field_ref</code> 自动生成一个 <code>Argument Set</code>。此外，一个 <code>Format</code> 最多只能包含一个 <code>args_ref</code>。</p>
</li>
</ul>
</li>
</ul>
<p>当 <code>fixedbit_elt</code> 或 <code>field_ref</code> 被定义时，该 <code>Foramt</code> 的所有的 bits 都必须被定义 (可通过 <code>fixedbit_elt</code> 或 <code>.</code> 来定义各个 bits，<code>空格</code>会被忽略)。</p>
<h3 id="format-示例">Format 示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">@opi    ...... ra:5 lit:8    1 ....... rc:5
</span></span></code></pre></div><p>定义了 <code>op1</code> 这个 <code>Format</code>，其中：</p>
<ul>
<li>insn[31:26] 可为 <code>0</code> 或 <code>1</code>。</li>
<li>insn[25:21] 为 <code>ra</code>。</li>
<li>insn[20:13] 为 <code>lit</code>。</li>
<li>insn[12] 固定为 <code>1</code>。</li>
<li>insn[11:5] 可为 <code>0</code> 或 <code>1</code>。</li>
<li>insn[4:0] 为 <code>rc</code>。</li>
</ul>
<p>此 <code>Format</code> 会生成以下的 decode function：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">lit</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">ra</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_decode_insn320</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_opi</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_decode_insn320</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">ra</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">lit</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rc</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>由于我们没有指定 <code>args_ref</code>，因此 <code>Decodetree</code> 根据了 <code>field_elt</code> 的定义，自动生成了 <code>arg_decode_insn320</code> 这个 <code>Argument Set</code>。</p>
<p>以 RISC-V <code>I-type</code> 指令为例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Fields:
</span></span><span class="line"><span class="cl">%rs1       15:5
</span></span><span class="line"><span class="cl">%rd        7:5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># immediates:
</span></span><span class="line"><span class="cl">%imm_i    20:s12
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Argment sets:
</span></span><span class="line"><span class="cl">&amp;i    imm rs1 rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@i       ........ ........ ........ ........ &amp;i      imm=%imm_i     %rs1 %rd
</span></span></code></pre></div><p>定义了 <code>i</code> 这个 <code>Format</code>，其中：</p>
<ul>
<li>insn[31:20] 为 <code>imm</code>，且为 符号扩展。</li>
<li>insn[19:5] 为 <code>rs1</code>。</li>
<li>insn[11:7] 为 <code>rd</code>。</li>
</ul>
<p>此外，我们可以看到：</p>
<ul>
<li>此 <code>Format</code> 指定了 <code>Argument Set</code>：<code>&amp;i</code>。 <code>&amp;i</code> 中必须包含所有有用到的 <code>arguments</code> (也就是：<code>imm</code>、<code>rs1</code> 及 <code>rd</code>)</li>
<li><code>imm</code> 是通过重命名的方式来参考 <code>%imm_i</code> 这个 <code>Field</code>。</li>
</ul>
<p>此范例会生成以下的 decode function：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rs1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32extract_i</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_i</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">12</span><span class="p">);</span> 
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rs1</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>相比于第一个范例，由于这次我们有指定 <code>args_ref</code>：<code>&amp;i</code>，因此对应的 <code>arg_i</code> 会被传入 decode function。</p>
<hr>
<p>回到先前的 RISC-V <code>U-type</code> 指令，我们可以如同 <code>I-type</code> 指令定义其格式：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Fields:
</span></span><span class="line"><span class="cl">%rd        7:5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># immediates:
</span></span><span class="line"><span class="cl">%imm_u    12:s20                 !function=ex_shift_12
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Argument sets:
</span></span><span class="line"><span class="cl">&amp;u    imm rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">@u       ....................      ..... ....... &amp;u      imm=%imm_u          %rd
</span></span></code></pre></div><p>定义了 <code>u</code> 这个 <code>Format</code>，其中：</p>
<ul>
<li>insn[31:12] 为 <code>imm</code>，且为 符号扩展。</li>
<li>insn[11:7] 为 <code>rd</code>。</li>
</ul>
<p>会生成以下的 decode function：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我们可以看到：</p>
<ul>
<li>此 <code>Format</code> 指定了 <code>Argument Set</code>：<code>&amp;u</code>。 <code>&amp;u</code> 中必须包含所有有用到的 <code>arguments</code> (也就是：<code>imm</code>、<code>rd</code>)</li>
<li><code>imm</code> 是通过重命名的方式来参考 <code>%imm_u</code> 这个 <code>Field</code>。</li>
</ul>
<h2 id="pattern">Pattern</h2>
<p><code>Pattern</code> 实际定义了一个指令的 decode 方式。<code>Decodetree</code> 会根据 <code>Patterns</code> 的定义，来动态产生出对应的 <code>switch-case</code> decode 判断分支。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">pat_def      := identifier ( pat_elt )+
</span></span><span class="line"><span class="cl">pat_elt      := fixedbit_elt | field_elt | field_ref | args_ref | fmt_ref | const_elt
</span></span><span class="line"><span class="cl">fmt_ref      := &#39;@&#39; identifier
</span></span><span class="line"><span class="cl">const_elt    := identifier &#39;=&#39; number
</span></span></code></pre></div><p>其语法由用户所定义的 <code>identifier</code>，随后紧接着一个以上的 <code>pat_elt</code>。</p>
<ul>
<li>
<p><code>identifier</code> 可由开发者自订，如：<code>addl_r</code>、<code>addli</code> … 等。</p>
</li>
<li>
<p><code>pat_elt</code> 则可以采用以下不同的语法：</p>
<ul>
<li><code>fixedbit_elt</code> 与在 <code>Format</code> 中 <code>fixedbit_elt</code> 的定义相同。</li>
<li><code>field_elt</code> 与在 <code>Format</code> 中 <code>field_elt</code> 的定义相同。</li>
<li><code>field_ref</code> 与在 <code>Format</code> 中 <code>field_ref</code> 的定义相同。</li>
<li><code>args_ref</code> 与在 <code>Format</code> 中 <code>args_ref</code> 的定义相同。</li>
<li><code>fmt_ref</code> 直接参考一个被定义过的Format。</li>
<li><code>const_elt</code> 可以直接指定某一个 <code>argument</code> 的值。</li>
</ul>
</li>
</ul>
<p>由于 <code>Pattern</code> 实际定义了一个指令的 decode 方式，因此<strong>所有的 bits</strong> 及 <strong>arguments (如果有参考 args_ref 的话)</strong>  都必须明确的被定义，如果在搭配了所有的 <code>pat_elt</code> 后还有未定义的 bits 或是 arguments 的话，<code>Decodetree</code> 便会报错。</p>
<p>此外，<code>Pattern</code> 所产生出来的 decoder，最后还会调用对应的 <code>translator function</code>。<code>translator function</code> 需开发者自行定义。</p>
<h3 id="pattern-示例">Pattern 示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">addl_i   010000 ..... ..... .... 0000000 ..... @opi
</span></span></code></pre></div><p>定义了 <code>addl_i</code> 这个指令的 <code>Pattern</code>，其中：</p>
<ul>
<li>insn[31:26] 为 <code>010000</code>。</li>
<li>insn[11:5] 为 <code>0000000</code>。</li>
<li>参考了 Format 示例中 定义的 <code>@opi</code> <code>Format</code>。</li>
<li>由于 <code>Pattern</code> 的<strong>所有 bits</strong> 都必须明确的被定义，因此 <code>@opi</code> 必须包含其余 <code>insn[25:12]</code> 及 <code>insn[4:0]</code> 的格式定义，否则 <code>Decodetree</code> 便会报错。</li>
</ul>
<p>最后 <code>addl_i</code> 的 decoder 还会调用 <code>trans_addl_i()</code> 这个 <code>translator function</code>。</p>
<p>搭配之前介绍的 Fields、Argument Sets 及 Formats，让我们再看几个完整的例子应该会更清楚 <code>Decodetree</code> 是怎产生一个指令的 decoder 的。</p>
<p>首先是 RISC-V 的 <code>lui</code> 及 <code>auipc</code> 指令：</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F922be39211d73158159edf9993a2c906.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F922be39211d73158159edf9993a2c906.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Fields:
</span></span><span class="line"><span class="cl">%rd        7:5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># immediates:
</span></span><span class="line"><span class="cl">%imm_u    12:s20                 !function=ex_shift_12
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Argument sets:
</span></span><span class="line"><span class="cl">&amp;u    imm rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Formats:
</span></span><span class="line"><span class="cl">@u       ....................      ..... ....... &amp;u      imm=%imm_u          %rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Patterns
</span></span><span class="line"><span class="cl">lui      ....................       ..... 0110111 @u
</span></span><span class="line"><span class="cl">auipc    ....................       ..... 0010111 @u
</span></span></code></pre></div><p>会产生以下 <code>lui</code> 及 <code>auipc</code> 的 decoder：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">decode_insn32</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_u</span> <span class="n">f_u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000007f</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00000017</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_auipc</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00000037</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_lui</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>回顾到目前为止所介绍的：</p>
<ul>
<li>
<p><code>Argument Sets</code>：<code>&amp;u</code> 这个 <code>argument set</code> 包含了 <code>imm</code> 及 <code>rd</code> 这两个 <code>arguments</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">imm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">rd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_u</span><span class="p">;</span>
</span></span></code></pre></div></li>
<li>
<p><code>Fields</code>： <code>imm</code> 及 <code>rd</code> 分别位在 insn[31:12] 及 insn[11:7]，且 <code>imm</code> 为 符号扩展。最后在截取出 <code>imm</code> 的值后，还会调用 <code>ex_shift_12()</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span></code></pre></div></li>
<li>
<p><code>Formats</code>：<code>@u</code> 定义了 RISC-V <code>U-type</code> 指令的格式</p>
<ul>
<li>参考了 <code>&amp;u</code> 这个 <code>Argument Set</code>，因此 decode function 会传入 <code>arg_u</code> 作为参数。</li>
<li>insn[31:12] 参考了 <code>imm_u</code> 这个 <code>Field</code> (并重命名为 <code>imm</code>)</li>
<li>insn[11:7] 参考了 <code>rd</code> 这个 <code>Field</code>。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_u</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="nf">ex_shift_12</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="nf">sextract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">20</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div></li>
<li>
<p><code>Patterns</code>：</p>
<ul>
<li><code>lui</code> 的 <code>opcode</code> (insn[6:0]) 为 <code>0010111</code>，也就是 <code>0x17</code>，在产生出来的 <code>switch-case</code> 中可以看到其对应的 <code>case</code>。</li>
<li><code>lui</code> 的 decoder 最后调用了 <code>trans_lui()</code>，并传入 <code>DisasContext</code> 及经由 <code>decode_insn32_extract_u()</code> 所解析出来的 <code>arg_u</code>。</li>
<li><code>auipc</code> 的 <code>opcode</code> (insn[6:0]) 为 <code>0110111</code>，也就是 <code>0x37</code>，在产生出来的 <code>switch-case</code> 中可以看到其对应的 <code>case</code>。</li>
<li><code>auipc</code> 的 decoder 最后调用了 <code>trans_auipc()</code>，并传入 <code>DisasContext</code> 及经由 <code>decode_insn32_extract_u()</code> 所解析出来的 <code>arg_u</code>。</li>
<li>P.S. 这边由于 <code>Decodetree</code> 发现 <code>lui</code> 及 <code>auipc</code> 可以共用 <code>decode_insn32_extract_u()</code>，因此将其提到了 <code>switch-case</code> 之外。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">decode_insn32</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_u</span> <span class="n">f_u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">decode_insn32_extract_u</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000007f</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00000017</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_auipc</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00000037</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_lui</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_u</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>我们另外可以发现，<code>Pattern</code> + <code>Format</code> 把所有的 32-bits 都给了明确的定义：</p>
<ul>
<li><code>Pattern</code> 定义了 <code>opcode</code> (insn[6:0])。</li>
<li><code>Format</code> 参考了 <code>imm</code> (insn[31:12]) 及 <code>rd</code> (insn[11:7])。</li>
</ul>
<p>如果有任何未明确定义的 bits 的话，<code>Decodetree</code> 便会报错，例如如果我们将 <code>lui</code> 的 <code>opcode</code> 最高 2 个 bits (insn[6:5]) 由 <code>01</code> 改成 <code>..</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">lui      ....................       ..... ..10111 @u
</span></span></code></pre></div><p><code>Decodetree</code> 在解析时，便会报错：</p>
<blockquote>
<p>./insn32.decode:17: error: (‘bits left unspecified (0x00000060)’,)</p>
</blockquote>
<p><code>Decodetree</code> 提醒我们，insn[6:5] (<code>0x00000060</code>) 尚未给出明确定义，并会显示出其错误的行数。</p>
<p><code>trans_lui()</code> 和 <code>trans_auipc()</code> 被定义在 <code>target/riscv/insn_trans/trans_rvi.inc.c</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">trans_lui</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_lui</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tcg_gen_movi_tl</span><span class="p">(</span><span class="n">cpu_gpr</span><span class="p">[</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span><span class="p">],</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">trans_auipc</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_auipc</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">tcg_gen_movi_tl</span><span class="p">(</span><span class="n">cpu_gpr</span><span class="p">[</span><span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span><span class="p">],</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">+</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">base</span><span class="p">.</span><span class="n">pc_next</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>可以看到 <code>trans_*()</code> 负责实际指令的业务逻辑及产生对应的 <code>TCG codes</code>。</p>
</li>
</ul>
<p>如同先前所介绍，<code>Patterns</code> 的 <code>pat_elt</code> 也可以采用 <code>field_elt</code> 语法，如 RISC-V 的 <code>fence</code> 指令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">fence    ---- pred:4 succ:4 ----- 000 ----- 0001111
</span></span></code></pre></div><ul>
<li>insn[27:24] 为 <code>pred</code>。</li>
<li>insn[23:20] 为 <code>succ</code>。</li>
<li>insn[14:12] 固定为 <code>000</code>。</li>
<li>insn[6:0] 为 <code>opcode</code> (<code>0001111</code>)。</li>
<li>没有参考任何的 <code>Format</code>。</li>
<li>剩下的 insn[31:28]、insn[19:15]、insn[11:7] 被声明为 <code>-</code>，因此就算没有被明确定义也没有关系。</li>
</ul>
<p>所生成 <code>fence</code> 的 decoder 如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">pred</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">succ</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">arg_decode_insn320</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn32_extract_decode_insn32_Fmt_0</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_decode_insn320</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">pred</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">succ</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">decode_insn32</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_decode_insn320</span> <span class="n">f_decode_insn320</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">decode_insn32_extract_decode_insn32_Fmt_0</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode_insn320</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000707f</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x0000000f</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_fence</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode_insn320</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>值得注意的是，虽然这次我们没有参考任何的 <code>Argument Set</code>，但 <code>Decodetree</code> 还是替我们生成了一个包含 <code>pred</code> 和 <code>succ</code> 的 <code>arg_decode_insn320</code> 。</p>
<p><code>trans_fence()</code> 同样是被定义在 <code>./target/riscv/insn_trans/trans_rvi.inc.c</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">trans_fence</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_fence</span> <span class="o">*</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="nf">tcg_gen_mb</span><span class="p">(</span><span class="n">TCG_MO_ALL</span> <span class="o">|</span> <span class="n">TCG_BAR_SC</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="pattern-groups">Pattern Groups</h2>
<p><code>Pattern Groups</code> 由一个以上的 <code>Patterns</code> 所组成，其主要差别是不同 <code>Patterns</code> 之间的 bits 可以 overlap。当同组中有多个 <code>Patterns</code> 时，会依据该组中各 <code>Pattern</code> 的声明顺序依序判断目前的指令是否符合其定义。除此之外，当符合的 <code>Pattern</code> 其 <code>trans_*()</code> 回传值为 <code>false</code> 时，也会被视为<strong>不相符</strong>，而继续判断该组中的下一个 <code>Pattern</code>。因此 <code>Pattern Groups</code> 非常适合将多个相似格式的指令给组成同一个 <code>Pattern Group</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">group    := &#39;{&#39; ( pat_def | group )+ &#39;}&#39;
</span></span></code></pre></div><p>各 <code>Pattern Group</code> 以 <code>{</code> 开头，并以 <code>}</code> 结尾，且允许 <code>nested pattern groups</code> 的存在，其他语法皆与 <code>Pattern</code> 相同。</p>
<h3 id="pattern-group-示例">Pattern Group 示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  {
</span></span><span class="line"><span class="cl">    nop   000010 ----- ----- 0000 001001 0 00000
</span></span><span class="line"><span class="cl">    copy  000010 00000 r1:5  0000 001001 0 rt:5
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">  or      000010 rt2:5 r1:5  cf:4 001001 0 rt:5
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>会产生以下的 decoder：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0xfc000fe0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mh">0x08000240</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000f000</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000001f</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">            <span class="nf">extract_decode_Fmt_0</span><span class="p">(</span><span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode0</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nf">trans_nop</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode0</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">				
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x03e00000</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">          <span class="nf">extract_decode_Fmt_1</span><span class="p">(</span><span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode1</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nf">trans_copy</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode1</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">				　
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="nf">extract_decode_Fmt_2</span><span class="p">(</span><span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode2</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nf">trans_or</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode2</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>当指令的值符合 <code>nop</code> 及 <code>copy</code> 这个内层 <code>Pattern Group</code> 时，会先判断该指令是否符合 <code>nop</code> 指令的定义，且 <code>trans_nop()</code> 的回传值为 <code>true</code>。否则的话，就会继续判断是否符合同组中的 <code>copy</code> 指令。若都不符，就会再判断是否符合外层 <code>Pattern Group</code> 的 <code>or</code> 指令。若仍不符，才会回传 <code>false</code> 表示 decode 失败。</p>
<p>与单纯使用 <code>Pattern</code> 最大不同的是，当一 <code>Pattern</code> 的 <code>trans_*()</code> 回传值为 <code>false</code> 时，不会直接回传 <code>false</code> (代表 decode 失败)，而是会接续着判断后续的 <code>Patterns</code> 是否相符。</p>
<p>RISC-V Compressed-Extension 中的 <code>c.ebreak</code>、<code>c.jalr</code>、及 <code>c.add</code> 指令，由于这三个指令的格式非常相似，因此非常适合使用 <code>Pattern Group</code> 来定义：</p>
<p>RISC-V spec. 中定义：</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F722c64cc1a321ef23fab81e722a24154.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/%2F2024%2F03%2F19%2F722c64cc1a321ef23fab81e722a24154.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<ul>
<li><code>C.EBREAK</code>指令与<code>C.ADD</code>指令共享相同的<code>opcode</code>，但是<code>rd</code>和<code>rs2</code>都为<code>zero</code>，因此也可以使用<code>CR</code>格式。</li>
<li><code>C.JALR</code>指令只有在<code>rs1≠x0</code>时才有效；当<code>rs1=x0</code>时，对应的代码点是<code>C.EBREAK</code>指令。</li>
<li><code>C.ADD</code>指令只有在<code>rs2≠x0</code>时才有效；当<code>rs2=x0</code>时，对应的代码点是<code>C.JALR</code>和<code>C.EBREAK</code>指令。具有<code>rs2̸=x0</code>和<code>rd=x0</code>的代码点是<code>HINTs</code>。</li>
</ul>
<p><code>c.ebreak</code>、<code>c.jalr</code>、<code>c.add</code> 三个指令：</p>
<ul>
<li>insn[15:13]、insn[12]、insn[1:0] 的值皆相同。</li>
<li>当 insn[11:7] 且 insn[6:2] 的值皆为 <code>0</code> (<code>rs1=0</code> 且 <code>rs2=0</code>) 时为 <code>c.ebreak</code> 指令。</li>
<li>当只有 insn[11:7] 的值为 <code>0</code> (<code>rs1=0</code> 且 <code>rs2≠0</code>) 时为 <code>c.jalr</code> 指令。</li>
<li>否则为 <code>c.add</code> 指令 (<code>rs1≠x0</code> 且 <code>rs2≠0</code>)。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl"># Fields
</span></span><span class="line"><span class="cl">%rd        7:5
</span></span><span class="line"><span class="cl">%rs2_5     2:5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Argument Sets
</span></span><span class="line"><span class="cl">&amp;r         rd rs1 rs2   !extern
</span></span><span class="line"><span class="cl">&amp;i         imm rs1 rd   !extern
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Formats
</span></span><span class="line"><span class="cl">@cr        ....  ..... .....  .. &amp;r      rs2=%rs2_5       rs1=%rd     %rd
</span></span><span class="line"><span class="cl">@c_jalr    ... . .....  ..... .. &amp;i      imm=0 rs1=%rd
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Pattern Groups
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">  ebreak          100 1  00000  00000 10
</span></span><span class="line"><span class="cl">  jalr            100 1  .....  00000 10 @c_jalr rd=1  # C.JALR
</span></span><span class="line"><span class="cl">  add             100 1  .....  ..... 10 @cr
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>所生成的 decoder 如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn16_extract_c_jalr</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_i</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">imm</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rs1</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn16_extract_cr</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_r</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rs2</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rs1</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">a</span><span class="o">-&gt;</span><span class="n">rd</span> <span class="o">=</span> <span class="nf">extract32</span><span class="p">(</span><span class="n">insn</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">decode_insn16_extract_decode_insn16_Fmt_2</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="n">arg_decode_insn162</span> <span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">bool</span> <span class="nf">decode_insn16</span><span class="p">(</span><span class="n">DisasContext</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">insn</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_decode_insn162</span> <span class="n">f_decode_insn162</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_i</span> <span class="n">f_i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">arg_r</span> <span class="n">f_r</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">u</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000f003</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mh">0x00009002</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x00000ffc</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">            <span class="nf">decode_insn16_extract_decode_insn16_Fmt_2</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode_insn162</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nf">trans_ebreak</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_decode_insn162</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">insn</span> <span class="o">&amp;</span> <span class="mh">0x0000007c</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x00000000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">            <span class="nf">decode_insn16_extract_c_jalr</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_i</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">u</span><span class="p">.</span><span class="n">f_i</span><span class="p">.</span><span class="n">rd</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nf">trans_jalr</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_i</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="nf">decode_insn16_extract_cr</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_r</span><span class="p">,</span> <span class="n">insn</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">trans_add</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">u</span><span class="p">.</span><span class="n">f_r</span><span class="p">))</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>当指令格式符合 <code>c.ebreak</code>、<code>c.jalr</code>、<code>c.add</code> 的 <code>Pattern Group</code> 时，会依序判断该指令是否符合 <code>c.ebreak</code>、<code>c.jalr</code>、<code>c.add</code> 的定义以及其对应的 <code>trans_*()</code>。</p>
<p>另外值得一提的是，在 <code>c_jalr</code> <code>Format</code> 和 <code>jalr</code> <code>Pattern</code> 中有分别指定其 <code>imm</code> 及 <code>rd</code> 的值为 <code>0</code>，所生成的 codes 也会分别在对应的地方将该值设为 <code>0</code> (见 codes 注解说明)。</p>
<h2 id="总结">总结</h2>
<p>以上就是 <code>Decodetree</code> 的语法说明。通过 <code>Decodetree</code>，我们不用再像以前以样写一大包的 <code>switch-case</code> 来 decode 指令。将不同类型的指令写至不同的 decode 档，不仅方便维护，阅读起来也更为容易。</p>
<hr>
<ul>
<li><code>--translate</code>：translator function 的 prefix，默认为 <code>trans</code>。一旦指定后，translator function 的 scope 就不会再是 <code>static</code>。</li>
<li><code>--decode</code>：decode function 的 prefix，默认为 <code>decode</code>，且 scope 为 <code>static</code>。一旦指定后，decode function 的 scope 就不会再是 <code>static</code>。</li>
<li><code>--static-decode</code>：如同 <code>--decode</code>，不过 decode function 的 scope 仍维持为 <code>static</code>。</li>
<li><code>-o</code> / <code>--output</code>：指定生成的 decoder <code>.c</code> 档路径。</li>
<li><code>-w</code> / <code>--insnwidth</code>：指令长度，eg：<code>32</code> or <code>16</code>，默认为 <code>32</code>。</li>
<li><code>--varinsnwidth</code>：指令为不定长度。</li>
<li><code>最后一个参数</code>为输入的 decode 档路径。</li>
</ul>
<p>运行范例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">./decodetree.py -o target/riscv/decode_insn16.inc.c --static-decode decode_insn16 \
</span></span><span class="line"><span class="cl">    -w 16 ./insn16.decode
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">static inline int32_t sextract32(uint32_t value, int start, int length){    assert(start &gt;= 0 &amp;&amp; length &gt; 0 &amp;&amp; length &lt;= 32 - start);        return ((int32_t)(value &lt;&lt; (32 - length - start))) &gt;&gt; (32 - length);}
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>QEMU 常用命令</title>
      <link>https://lifeislife.cn/posts/qemu%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</link>
      <pubDate>Sun, 07 Jul 2024 10:22:12 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</guid>
      <description>&lt;p&gt;QEMU 是一个开源的虚拟化软件，它能够模拟不同的硬件平台，让用户在不同的操作系统之间进行切换和测试。以下是 QEMU 常用命令的总结文档，包含每个命令的功能说明。&lt;/p&gt;
&lt;h2 id=&#34;qemu-安装&#34;&gt;QEMU 安装&lt;/h2&gt;
&lt;h3 id=&#34;源码下载&#34;&gt;源码下载&lt;/h3&gt;
&lt;h4 id=&#34;压缩包方式&#34;&gt;压缩包方式&lt;/h4&gt;
&lt;p&gt;版本可以修改&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget  &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://download.qemu.org/qemu-8.0.2.tar.xz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xf qemu-8.0.2.tar.xz --strip-components&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;clone-方式&#34;&gt;Clone 方式&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;git clone https://gitlab.com/qemu-project/qemu.git&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;cd qemu&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;git submodule init&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;git submodule update --recursive&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;配置编译选项&#34;&gt;配置编译选项&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --target-list&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--enable-kvm &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--enable-debug &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--enable-sdl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/user/program/riscv64-qemu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--python&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/usr/bin/python3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# --python=python路径，如果提示默认python版本低，可以加这个参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# --prefix 选项设置qemu的安装位置绝对路径，之后若要卸载删除qemu只要删除该文件夹即可，--enable-kvm开启kvm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# config完，可以在指定的qemu安装文件夹下面找到config-host.mak文件，&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 该文件记录着qemu配置的选项，可以和自己设置的进行对比，确保配置和自己已知&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;一些常用的编译选项&#34;&gt;一些常用的编译选项&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ndash;enable-debug：编译调试版本，调试版本的运行速度非常慢&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-werror：忽略警告，否则任何编译警告都被视为编译错误&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-plugins：开启TCG Plugin支持&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-stack-protector：关闭QEMU自身的栈保护&lt;/li&gt;
&lt;li&gt;&amp;ndash;extra-cflags=&amp;quot;-O3&amp;quot;：能让你的QEMU提速5~10%，如果编译时报错，请加上&lt;code&gt;--disable-werror&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&amp;ndash;prefix=&amp;lt;路径&amp;gt;：指定安装目录的路径&lt;/li&gt;
&lt;li&gt;&amp;ndash;target-list=&amp;lt;架构&amp;gt;：指定要编译的目标架构列表，例如 &lt;code&gt;x86_64-softmmu,arm-softmmu&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-&amp;lt;功能&amp;gt;：启用指定的功能。例如，&lt;code&gt;--enable-kvm&lt;/code&gt; 启用 KVM 支持，&lt;code&gt;--enable-gtk&lt;/code&gt; 启用 GTK 图形界面等。&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-&amp;lt;功能&amp;gt;：禁用指定的功能。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-debug：启用调试模式，包括调试符号和调试输出。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-virtfs：启用 virtio 文件系统支持。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-modules：启用模块支持。&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-guest-agent：禁用客户机代理支持。&lt;/li&gt;
&lt;li&gt;&amp;ndash;enable-trace-backend=&amp;lt;后端&amp;gt;：指定跟踪后端，例如 &lt;code&gt;simple&lt;/code&gt;、&lt;code&gt;log&lt;/code&gt; 或 &lt;code&gt;dtrace&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&amp;ndash;disable-vhost-net：禁用 vhost-net 支持。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;编译时输出i-文件用于查看宏定义展开&#34;&gt;编译时输出.i 文件用于查看宏定义展开&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;$ ./configure  --extra-cflags=&amp;#34;-save-temps&amp;#34;   --target-list=riscv64-linux-user,riscv64-softmmu --disable-werror  --python=/usr/bin/python3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;启动虚拟机&#34;&gt;启动虚拟机&lt;/h2&gt;
&lt;p&gt;以下命令用于启动虚拟机：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -boot d -cdrom /path/to/iso -m &lt;span class=&#34;m&#34;&gt;1024&lt;/span&gt; -hda /path/to/hda.img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-boot d：从 CD/DVD 启动&lt;/li&gt;
&lt;li&gt;-cdrom /path/to/iso：指定 ISO 文件的路径&lt;/li&gt;
&lt;li&gt;-m 1024：设置虚拟机的内存大小为 1024MB&lt;/li&gt;
&lt;li&gt;-hda /path/to/hda.img：指定虚拟硬盘的路径&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;安装系统至磁盘&#34;&gt;安装系统至磁盘&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -cdrom ~/Downloads/ubuntu.iso &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -drive &lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;ubuntu.qcow2 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -enable-kvm &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -cpu host &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -smp &lt;span class=&#34;nv&#34;&gt;cores&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;2,threads&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -m 2G &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -vga virtio &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -display sdl,gl&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;on 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;网络配置&#34;&gt;网络配置&lt;/h2&gt;
&lt;p&gt;以下命令用于配置虚拟机的网络：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -net nic -net user,hostfwd&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tcp::2222-:22
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-net nic：启用虚拟网卡&lt;/li&gt;
&lt;li&gt;-net user：使用用户模式网络堆栈&lt;/li&gt;
&lt;li&gt;hostfwd=tcp::2222-:22：将主机的 2222 端口转发到虚拟机的 22 端口&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;调试相关命令&#34;&gt;调试相关命令&lt;/h2&gt;
&lt;h3 id=&#34;启动调试模式&#34;&gt;启动调试模式&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -s -S
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-s：启用 GDB 调试&lt;/li&gt;
&lt;li&gt;-S：在启动时暂停虚拟机，等待调试器连接&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;输出调试日志&#34;&gt;输出调试日志&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;l&#34;&gt;/home/user/develop/qemu/build/qemu-riscv64 -d in_asm,exec,cpu,strace -D ./log hello&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;QEMU 的 &lt;code&gt;-d&lt;/code&gt; 参数说明&lt;/p&gt;
&lt;p&gt;日志选项（用逗号分隔）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;out_asm&lt;/code&gt;：显示每个已编译基本块生成的宿主汇编代码&lt;/li&gt;
&lt;li&gt;&lt;code&gt;in_asm&lt;/code&gt;：显示每个已编译基本块的目标汇编代码&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op&lt;/code&gt;：显示每个已编译基本块的微操作&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op_opt&lt;/code&gt;：显示优化后的微操作&lt;/li&gt;
&lt;li&gt;&lt;code&gt;op_ind&lt;/code&gt;：显示间接降低前的微操作&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt;：以简短格式显示中断/异常&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exec&lt;/code&gt;：在每次执行基本块前显示跟踪（大量日志）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cpu&lt;/code&gt;：在进入基本块前显示 CPU 寄存器（大量日志）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fpu&lt;/code&gt;：在“cpu”日志中包含 FPU 寄存器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mmu&lt;/code&gt;：记录与 MMU 相关的活动&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pcall&lt;/code&gt;：仅限 x86：显示保护模式下的远程调用/返回/异常&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cpu_reset&lt;/code&gt;：在 CPU 重置前显示 CPU 状态&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unimp&lt;/code&gt;：记录未实现的功能&lt;/li&gt;
&lt;li&gt;&lt;code&gt;guest_errors&lt;/code&gt;：当来宾操作系统执行无效操作（例如访问不存在的寄存器）时记录日志&lt;/li&gt;
&lt;li&gt;&lt;code&gt;page&lt;/code&gt;：在用户模式仿真开始时转储页面&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nochain&lt;/code&gt;：不链式编译基本块，以便“exec”和“cpu”显示完整的跟踪&lt;/li&gt;
&lt;li&gt;&lt;code&gt;plugin&lt;/code&gt;：输出来自 TCG 插件的信息&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strace&lt;/code&gt;：记录每个用户模式系统调用、其输入和结果&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trace:PATTERN&lt;/code&gt;：启用跟踪事件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用 &lt;code&gt;&amp;quot;-d trace:help&amp;quot;&lt;/code&gt; 来获取跟踪事件列表。&lt;/p&gt;
&lt;h2 id=&#34;qemu-system-xxx-常用相关参数&#34;&gt;qemu-system-xxx 常用相关参数&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;-M：指定 machine，-help 可以列出所有所支持的 machine&lt;/li&gt;
&lt;li&gt;-cpu：指定模拟的 CPU 型号，例如 &lt;code&gt;cortex-a57&lt;/code&gt;、&lt;code&gt;cortex-a53&lt;/code&gt; 等。&lt;/li&gt;
&lt;li&gt;-smp：CPU 核数&lt;/li&gt;
&lt;li&gt;-m：RAM 容量&lt;/li&gt;
&lt;li&gt;-kernel：Linux kernel 文件&lt;/li&gt;
&lt;li&gt;-append：Linux Kernel 的 bootargs。这个命令的参数很复杂，具体可以参考 kernel 文档。&lt;/li&gt;
&lt;li&gt;-console：设备名必须和 machine 的串口一致，否则会看不到 kernel log。而不同的平台 console 名称都是不一样的，这是 Kernel 很不友好的一点。&lt;/li&gt;
&lt;li&gt;-ignore_loglevel：可以让你看到尽可能多的 kernel log，当然也会减慢 kernel 的启动速度&lt;/li&gt;
&lt;li&gt;-init：必须保证 initrd 里有/linuxrc 这个文件，否则会无法启动 shell&lt;/li&gt;
&lt;li&gt;-initrd：指定 initrd 文件&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;快照管理&#34;&gt;快照管理&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 拍快照&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img snapshot -c oe-rv-snapshot1  openEuler-22.09-riscv64-qemu.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 列举快照&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img snapshot-l openEuler-22.09-riscv64-qemu.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 恢复快照&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img snapshot -a my_snapshot mydisk.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;查看虚拟硬盘信息&#34;&gt;查看虚拟硬盘信息&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img info /path/to/image
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;将虚拟硬盘转换为-qcow2-格式&#34;&gt;将虚拟硬盘转换为 QCOW2 格式&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img convert -O qcow2 /path/to/image /path/to/new/image
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;调整磁盘大小&#34;&gt;调整磁盘大小&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-img resize ubuntu.qcow2 +5G
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;显示器选项&#34;&gt;显示器选项&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -vga std
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -display sdl
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -display gtk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-vga std：使用标准 VGA 显示器&lt;/li&gt;
&lt;li&gt;-display sdl：使用 SDL 显示器&lt;/li&gt;
&lt;li&gt;-display gtk：使用 GTK 显示器&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;输入选项&#34;&gt;输入选项&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -k en-us
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -usb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -device usb-mouse
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-k en-us：使用英文键盘布局&lt;/li&gt;
&lt;li&gt;-usb：启用 USB 支持&lt;/li&gt;
&lt;li&gt;-device usb-mouse：使用 USB 鼠标设备&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;声音选项&#34;&gt;声音选项&lt;/h2&gt;
&lt;p&gt;以下命令用于配置虚拟机的声音：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -soundhw all
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -soundhw sb16
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -audiodev pa,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;pa1,out.mixing-engine&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;off
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-soundhw all：启用所有声卡&lt;/li&gt;
&lt;li&gt;-soundhw sb16：启用 SoundBlaster 16 声卡&lt;/li&gt;
&lt;li&gt;-audiodev pa,id=pa1,out.mixing-engine=off：使用 PulseAudio 声音设备&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;usb-设备管理&#34;&gt;USB 设备管理&lt;/h2&gt;
&lt;p&gt;以下命令用于管理虚拟机的 USB 设备：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -usbdevice host:1234:5678
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -usbdevice tablet
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -usbdevice keyboard
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;-usbdevice host🔢5678：将主机的 USB 设备 1234:5678 分配给虚拟机&lt;/li&gt;
&lt;li&gt;-usbdevice tablet：使用 USB 触摸板&lt;/li&gt;
&lt;li&gt;-usbdevice keyboard：使用 USB 键盘&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<p>QEMU 是一个开源的虚拟化软件，它能够模拟不同的硬件平台，让用户在不同的操作系统之间进行切换和测试。以下是 QEMU 常用命令的总结文档，包含每个命令的功能说明。</p>
<h2 id="qemu-安装">QEMU 安装</h2>
<h3 id="源码下载">源码下载</h3>
<h4 id="压缩包方式">压缩包方式</h4>
<p>版本可以修改</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> wget  <span class="s2">&#34;https://download.qemu.org/qemu-8.0.2.tar.xz&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">tar -xf qemu-8.0.2.tar.xz --strip-components<span class="o">=</span><span class="m">1</span> 
</span></span></code></pre></div><h4 id="clone-方式">Clone 方式</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="l">git clone https://gitlab.com/qemu-project/qemu.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">cd qemu</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">git submodule init</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">git submodule update --recursive</span><span class="w">
</span></span></span></code></pre></div><h3 id="配置编译选项">配置编译选项</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./configure --target-list<span class="o">=</span>riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu <span class="se">\
</span></span></span><span class="line"><span class="cl">--enable-kvm <span class="se">\
</span></span></span><span class="line"><span class="cl">--enable-debug <span class="se">\
</span></span></span><span class="line"><span class="cl">--enable-sdl <span class="se">\
</span></span></span><span class="line"><span class="cl">--prefix<span class="o">=</span>/home/user/program/riscv64-qemu <span class="se">\
</span></span></span><span class="line"><span class="cl">--python<span class="o">=</span>/usr/bin/python3
</span></span><span class="line"><span class="cl"><span class="c1"># --python=python路径，如果提示默认python版本低，可以加这个参数</span>
</span></span><span class="line"><span class="cl"><span class="c1"># --prefix 选项设置qemu的安装位置绝对路径，之后若要卸载删除qemu只要删除该文件夹即可，--enable-kvm开启kvm</span>
</span></span><span class="line"><span class="cl"><span class="c1"># config完，可以在指定的qemu安装文件夹下面找到config-host.mak文件，</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 该文件记录着qemu配置的选项，可以和自己设置的进行对比，确保配置和自己已知</span>
</span></span></code></pre></div><h4 id="一些常用的编译选项">一些常用的编译选项</h4>
<ul>
<li>&ndash;enable-debug：编译调试版本，调试版本的运行速度非常慢</li>
<li>&ndash;disable-werror：忽略警告，否则任何编译警告都被视为编译错误</li>
<li>&ndash;enable-plugins：开启TCG Plugin支持</li>
<li>&ndash;disable-stack-protector：关闭QEMU自身的栈保护</li>
<li>&ndash;extra-cflags=&quot;-O3&quot;：能让你的QEMU提速5~10%，如果编译时报错，请加上<code>--disable-werror</code></li>
<li>&ndash;prefix=&lt;路径&gt;：指定安装目录的路径</li>
<li>&ndash;target-list=&lt;架构&gt;：指定要编译的目标架构列表，例如 <code>x86_64-softmmu,arm-softmmu</code>。</li>
<li>&ndash;enable-&lt;功能&gt;：启用指定的功能。例如，<code>--enable-kvm</code> 启用 KVM 支持，<code>--enable-gtk</code> 启用 GTK 图形界面等。</li>
<li>&ndash;disable-&lt;功能&gt;：禁用指定的功能。</li>
<li>&ndash;enable-debug：启用调试模式，包括调试符号和调试输出。</li>
<li>&ndash;enable-virtfs：启用 virtio 文件系统支持。</li>
<li>&ndash;enable-modules：启用模块支持。</li>
<li>&ndash;disable-guest-agent：禁用客户机代理支持。</li>
<li>&ndash;enable-trace-backend=&lt;后端&gt;：指定跟踪后端，例如 <code>simple</code>、<code>log</code> 或 <code>dtrace</code>。</li>
<li>&ndash;disable-vhost-net：禁用 vhost-net 支持。</li>
</ul>
<h4 id="编译时输出i-文件用于查看宏定义展开">编译时输出.i 文件用于查看宏定义展开</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="l">$ ./configure  --extra-cflags=&#34;-save-temps&#34;   --target-list=riscv64-linux-user,riscv64-softmmu --disable-werror  --python=/usr/bin/python3</span><span class="w">
</span></span></span></code></pre></div><h2 id="启动虚拟机">启动虚拟机</h2>
<p>以下命令用于启动虚拟机：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -boot d -cdrom /path/to/iso -m <span class="m">1024</span> -hda /path/to/hda.img
</span></span></code></pre></div><ul>
<li>-boot d：从 CD/DVD 启动</li>
<li>-cdrom /path/to/iso：指定 ISO 文件的路径</li>
<li>-m 1024：设置虚拟机的内存大小为 1024MB</li>
<li>-hda /path/to/hda.img：指定虚拟硬盘的路径</li>
</ul>
<h2 id="安装系统至磁盘">安装系统至磁盘</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 <span class="se">\
</span></span></span><span class="line"><span class="cl">    -cdrom ~/Downloads/ubuntu.iso <span class="se">\
</span></span></span><span class="line"><span class="cl">    -drive <span class="nv">file</span><span class="o">=</span>ubuntu.qcow2 <span class="se">\
</span></span></span><span class="line"><span class="cl">    -enable-kvm <span class="se">\
</span></span></span><span class="line"><span class="cl">    -cpu host <span class="se">\
</span></span></span><span class="line"><span class="cl">    -smp <span class="nv">cores</span><span class="o">=</span>2,threads<span class="o">=</span><span class="m">2</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    -m 2G <span class="se">\
</span></span></span><span class="line"><span class="cl">    -vga virtio <span class="se">\
</span></span></span><span class="line"><span class="cl">    -display sdl,gl<span class="o">=</span>on 
</span></span></code></pre></div><h2 id="网络配置">网络配置</h2>
<p>以下命令用于配置虚拟机的网络：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -net nic -net user,hostfwd<span class="o">=</span>tcp::2222-:22
</span></span></code></pre></div><ul>
<li>-net nic：启用虚拟网卡</li>
<li>-net user：使用用户模式网络堆栈</li>
<li>hostfwd=tcp::2222-:22：将主机的 2222 端口转发到虚拟机的 22 端口</li>
</ul>
<h2 id="调试相关命令">调试相关命令</h2>
<h3 id="启动调试模式">启动调试模式</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -s -S
</span></span></code></pre></div><ul>
<li>-s：启用 GDB 调试</li>
<li>-S：在启动时暂停虚拟机，等待调试器连接</li>
</ul>
<h3 id="输出调试日志">输出调试日志</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="l">/home/user/develop/qemu/build/qemu-riscv64 -d in_asm,exec,cpu,strace -D ./log hello</span><span class="w">
</span></span></span></code></pre></div><p>QEMU 的 <code>-d</code> 参数说明</p>
<p>日志选项（用逗号分隔）：</p>
<ul>
<li><code>out_asm</code>：显示每个已编译基本块生成的宿主汇编代码</li>
<li><code>in_asm</code>：显示每个已编译基本块的目标汇编代码</li>
<li><code>op</code>：显示每个已编译基本块的微操作</li>
<li><code>op_opt</code>：显示优化后的微操作</li>
<li><code>op_ind</code>：显示间接降低前的微操作</li>
<li><code>int</code>：以简短格式显示中断/异常</li>
<li><code>exec</code>：在每次执行基本块前显示跟踪（大量日志）</li>
<li><code>cpu</code>：在进入基本块前显示 CPU 寄存器（大量日志）</li>
<li><code>fpu</code>：在“cpu”日志中包含 FPU 寄存器</li>
<li><code>mmu</code>：记录与 MMU 相关的活动</li>
<li><code>pcall</code>：仅限 x86：显示保护模式下的远程调用/返回/异常</li>
<li><code>cpu_reset</code>：在 CPU 重置前显示 CPU 状态</li>
<li><code>unimp</code>：记录未实现的功能</li>
<li><code>guest_errors</code>：当来宾操作系统执行无效操作（例如访问不存在的寄存器）时记录日志</li>
<li><code>page</code>：在用户模式仿真开始时转储页面</li>
<li><code>nochain</code>：不链式编译基本块，以便“exec”和“cpu”显示完整的跟踪</li>
<li><code>plugin</code>：输出来自 TCG 插件的信息</li>
<li><code>strace</code>：记录每个用户模式系统调用、其输入和结果</li>
<li><code>trace:PATTERN</code>：启用跟踪事件</li>
</ul>
<p>使用 <code>&quot;-d trace:help&quot;</code> 来获取跟踪事件列表。</p>
<h2 id="qemu-system-xxx-常用相关参数">qemu-system-xxx 常用相关参数</h2>
<ul>
<li>-M：指定 machine，-help 可以列出所有所支持的 machine</li>
<li>-cpu：指定模拟的 CPU 型号，例如 <code>cortex-a57</code>、<code>cortex-a53</code> 等。</li>
<li>-smp：CPU 核数</li>
<li>-m：RAM 容量</li>
<li>-kernel：Linux kernel 文件</li>
<li>-append：Linux Kernel 的 bootargs。这个命令的参数很复杂，具体可以参考 kernel 文档。</li>
<li>-console：设备名必须和 machine 的串口一致，否则会看不到 kernel log。而不同的平台 console 名称都是不一样的，这是 Kernel 很不友好的一点。</li>
<li>-ignore_loglevel：可以让你看到尽可能多的 kernel log，当然也会减慢 kernel 的启动速度</li>
<li>-init：必须保证 initrd 里有/linuxrc 这个文件，否则会无法启动 shell</li>
<li>-initrd：指定 initrd 文件</li>
</ul>
<h2 id="快照管理">快照管理</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 拍快照</span>
</span></span><span class="line"><span class="cl">qemu-img snapshot -c oe-rv-snapshot1  openEuler-22.09-riscv64-qemu.qcow2
</span></span><span class="line"><span class="cl"><span class="c1"># 列举快照</span>
</span></span><span class="line"><span class="cl">qemu-img snapshot-l openEuler-22.09-riscv64-qemu.qcow2
</span></span><span class="line"><span class="cl"><span class="c1"># 恢复快照</span>
</span></span><span class="line"><span class="cl">qemu-img snapshot -a my_snapshot mydisk.qcow2
</span></span></code></pre></div><h2 id="查看虚拟硬盘信息">查看虚拟硬盘信息</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-img info /path/to/image
</span></span></code></pre></div><h2 id="将虚拟硬盘转换为-qcow2-格式">将虚拟硬盘转换为 QCOW2 格式</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-img convert -O qcow2 /path/to/image /path/to/new/image
</span></span></code></pre></div><h3 id="调整磁盘大小">调整磁盘大小</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-img resize ubuntu.qcow2 +5G
</span></span></code></pre></div><h2 id="显示器选项">显示器选项</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -vga std
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -display sdl
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -display gtk
</span></span></code></pre></div><ul>
<li>-vga std：使用标准 VGA 显示器</li>
<li>-display sdl：使用 SDL 显示器</li>
<li>-display gtk：使用 GTK 显示器</li>
</ul>
<h2 id="输入选项">输入选项</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -k en-us
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -usb
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -device usb-mouse
</span></span></code></pre></div><ul>
<li>-k en-us：使用英文键盘布局</li>
<li>-usb：启用 USB 支持</li>
<li>-device usb-mouse：使用 USB 鼠标设备</li>
</ul>
<h2 id="声音选项">声音选项</h2>
<p>以下命令用于配置虚拟机的声音：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -soundhw all
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -soundhw sb16
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -audiodev pa,id<span class="o">=</span>pa1,out.mixing-engine<span class="o">=</span>off
</span></span></code></pre></div><ul>
<li>-soundhw all：启用所有声卡</li>
<li>-soundhw sb16：启用 SoundBlaster 16 声卡</li>
<li>-audiodev pa,id=pa1,out.mixing-engine=off：使用 PulseAudio 声音设备</li>
</ul>
<h2 id="usb-设备管理">USB 设备管理</h2>
<p>以下命令用于管理虚拟机的 USB 设备：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-x86_64 -usbdevice host:1234:5678
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -usbdevice tablet
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -usbdevice keyboard
</span></span></code></pre></div><ul>
<li>-usbdevice host🔢5678：将主机的 USB 设备 1234:5678 分配给虚拟机</li>
<li>-usbdevice tablet：使用 USB 触摸板</li>
<li>-usbdevice keyboard：使用 USB 键盘</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 虚拟机网络配置</title>
      <link>https://lifeislife.cn/posts/qemu%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE/</link>
      <pubDate>Sat, 05 Aug 2023 14:47:54 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE/</guid>
      <description>&lt;h1 id=&#34;quick-setup&#34;&gt;Quick Setup&lt;/h1&gt;
&lt;h2 id=&#34;安装工具&#34;&gt;安装工具&lt;/h2&gt;
&lt;p&gt;安装两个网络管理工具用于建立网桥以及虚拟网卡：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 安装虚拟网桥工具&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install bridge-utils -y
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# UML（User-mode linux）工具        &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install uml-utilities  -y   
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;配置脚本&#34;&gt;配置脚本&lt;/h2&gt;
&lt;h3 id=&#34;qemu-ifup&#34;&gt;qemu-ifup&lt;/h3&gt;
&lt;p&gt;将下面的脚本保存为文件 &lt;code&gt;qemu-ifup&lt;/code&gt;，并赋予可执行权限：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为了方便复制脚本，在 confluence 页面提供了脚本内容，可以直接复制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /etc/qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv qemu-ifup /etc/qemu &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mv qemu-ifdown /etc/qemu 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x qemu-ifup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x qemu-ifdown
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因为网卡信息不容易定位，可能一台机器有多个网卡，所以不方便用脚本获取，需要手动设置一下。将下面的&lt;code&gt;NIC&lt;/code&gt;值修改为宿主机可以上网的网卡名称。可以通过&lt;code&gt;ifconfig&lt;/code&gt;命令查看。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置默认网卡信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NIC&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;enp2s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置用户名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;USER_NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;user
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网桥名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BRIDGE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网络信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NIC_IP&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;inet\b&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NIC_NETMAST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;inet\b&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NIC_BROADCAST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;inet\b&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $6}&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NETMASK&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;255.255.240.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置默认网关地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;GATEWAY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;10.12.192.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 获取宿主机网卡MAC地址，因为创建的网桥MAC地址是随机的，&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 无法接入公司，需要从开发机网卡将其MAC地址赋值给网桥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;MAC&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;ether\b&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查网桥是否已创建，已创建就忽略&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; check_bridge&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    echO &lt;span class=&#34;s2&#34;&gt;&amp;#34;Check bridge...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; brctl show &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;^&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;&amp;gt; /dev/null&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 创建网桥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; create_bridge&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Start Create bridge...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    brctl addbr &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    brctl addif &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$NIC&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ifconfig br0 0.0.0.0 promisc up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    dhclient &lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# ifconfig &amp;#34;$BRIDGE&amp;#34; &amp;#34;$NIC_IP&amp;#34; netmask &amp;#34;$NIC_NETMAST&amp;#34; broadcast &amp;#34;$NIC_BROADCAST&amp;#34;  hw ether &amp;#34;$MAC&amp;#34; promisc up&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启用IP转发&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; enable_ip_forward&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &amp;gt; /proc/sys/net/ipv4/ip_forward
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网桥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; setup_bridge&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    check_bridge &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$?&lt;/span&gt; -eq &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        create_bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    enable_ip_forward
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    setup_bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Creating &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    tunctl -t &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -u &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$USER_NAME&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ifconfig &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; 0.0.0.0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Adding &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; to &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    brctl addif &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    sleep &lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ifconfig &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$BRIDGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;  hw ether &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MAC&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; promisc up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Error: no interface specified.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;qemu-ifdown&#34;&gt;qemu-ifdown&lt;/h3&gt;
&lt;p&gt;以下是&lt;code&gt;qemu-ifdown&lt;/code&gt;脚本，用于在关闭 QEMU 时关闭虚拟网卡，将其从网桥中移除，删除虚拟网卡。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置网桥名称&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;BRIDGE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -n &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 将tap设备从网桥中移除&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	brctl delif &lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;BRIDGE&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;c1&#34;&gt;# 关闭tap设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	ip link link &lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt; down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 删除tap设备&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ip link del &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	tunctl -d &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Error: no interface specified&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将系统镜像复制一份并修改文件名，QEMU 不能同时使用一个镜像启动两个虚拟机。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp openEuler-22.09-riscv64-qemu.qcow2 openEuler-22.09-riscv64-qemu-vm1.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;需要修改启动脚本中的镜像文件名，以及启动参数，将&lt;code&gt;drive&lt;/code&gt;以及&lt;code&gt;cmd&lt;/code&gt;变量的内容覆盖为下面的内容，修改&lt;code&gt;mac&lt;/code&gt;为分配给自己的虚拟机的 MAC 地址，&lt;code&gt;script&lt;/code&gt;为上面的脚本&lt;code&gt;qemu-ifup&lt;/code&gt;的路径。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 该脚本用于启动VM0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;drive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;openEuler-22.09-riscv64-qemu.qcow2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;....
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,format=qcow2,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -object rng-random,filename=/dev/urandom,id=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-vga \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-rng-device,rng=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-net-device,netdev=tapnet,mac=e0:be:03:88:54:e8 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -netdev tap,id=tapnet,script=/etc/qemu/qemu-ifup,downscript=/etc/qemu/qemu-ifdown \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以&lt;code&gt;sudo&lt;/code&gt;权限启动脚本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ./preview_start_vm0.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以下为配置 VM1 过程，VM1 的启动脚本与 VM0 的启动脚本类似，只需要修改&lt;code&gt;drive&lt;/code&gt;以及&lt;code&gt;MAC&lt;/code&gt;，必须保证&lt;code&gt;MAC&lt;/code&gt;与 VM0 的&lt;code&gt;MAC&lt;/code&gt;不同。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 该脚本用于启动VM1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;drive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;openEuler-22.09-riscv64-qemu.qcow2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;....
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,format=qcow2,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -object rng-random,filename=/dev/urandom,id=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-vga \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-rng-device,rng=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-net-device,netdev=tapnet,mac=80:d4:09:62:cd:3c \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -netdev tap,id=tapnet,script=/etc/qemu/qemu-ifup,downscript=/etc/qemu/qemu-ifdown \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;网络通信测试&#34;&gt;网络通信测试&lt;/h2&gt;
&lt;p&gt;当前网络状态如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;HOST:10.12.192.177
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;VM0:10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;VM1:10.12.193.101
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;host--vm0&#34;&gt;HOST &amp;ndash;&amp;gt; VM0&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# user @ ubuntu18 in ~/openeuler/openEuler2209 [18:41:54] &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ping -c &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; 10.12.193.101
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING 10.12.193.101 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.12.193.101&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.101: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.37 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.101: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.897 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.101: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.890 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- 10.12.193.101 ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2002ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.890/1.055/1.378/0.228 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Host &amp;ndash;&amp;gt; VM1 的测试结果与 Host &amp;ndash;&amp;gt; VM0 的测试结果相同。&lt;/p&gt;
&lt;h3 id=&#34;vm0--host&#34;&gt;VM0 &amp;ndash;&amp;gt; HOST&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ping -c 3 10.12.193.53 &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING 10.12.193.53 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.12.193.53&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.716 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.74 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.81 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- 10.12.193.53 ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2009ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.716/1.424/1.812/0.501 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;VM1 &amp;ndash;&amp;gt; Host 的测试结果与 Host &amp;ndash;&amp;gt; VM0 的测试结果相同。&lt;/p&gt;
&lt;h3 id=&#34;vm1--vm0&#34;&gt;VM1 &amp;ndash;&amp;gt; VM0&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ping -c 3 10.12.193.53 &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING 10.12.193.53 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.12.193.53&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.716 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.74 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.12.193.53: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1.81 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- 10.12.193.53 ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2009ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.716/1.424/1.812/0.501 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;VM0 &amp;ndash;&amp;gt; VM1 与 VM1 &amp;ndash;&amp;gt; VM0 的测试结果相同。&lt;/p&gt;
&lt;h3 id=&#34;vm0--github&#34;&gt;VM0 &amp;ndash;&amp;gt; github&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ping -c 4 github.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;221&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;277&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;216&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;218&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- github.com ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 3014ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 215.984/232.733/276.593/25.374 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;host--github&#34;&gt;HOST &amp;ndash;&amp;gt; github&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# user @ ubuntu18 in ~/openeuler/openEuler2209 [17:59:40] &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ping -c &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;  github.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;PING github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;218&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;216&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from lb-192-30-255-113-sea.github.com &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;192.30.255.113&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;216&lt;/span&gt; ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;--- github.com ping statistics ---
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2002ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 216.252/217.087/218.409/0.945 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;原理探究-ongoing&#34;&gt;原理探究 (Ongoing)&lt;/h1&gt;
&lt;h2 id=&#34;step-by-step-解析&#34;&gt;Step by Step 解析&lt;/h2&gt;
&lt;p&gt;查看一下网络接口信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp3s0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::a00:27ff:fe32:e709  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 08:00:27:32:e7:09  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;6017&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;5412928&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;5.4 MB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;1979&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;179467&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;179.4 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lo: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;65536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 127.0.0.1  netmask 255.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 ::1  prefixlen &lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;  scopeid 0x10&amp;lt;host&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        loop  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Local Loopback&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;125&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;10142&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.1 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;125&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;10142&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.1 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建一个名为&lt;code&gt;br0&lt;/code&gt;的网桥&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl addbr br0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将网桥与宿主机的网卡绑定&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl addif br0 enp3s0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启用 &lt;code&gt;br0&lt;/code&gt; 接口，并从 DHCP 服务器获得 IP 地址&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig br0 0.0.0.0 promisc up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo dhclient br0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看虚拟网桥列表&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl show br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bridge name	    bridge id		    STP enabled    interfaces
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0		            8000.e0be0388eec9  no             enp3s0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看 &lt;code&gt;br0&lt;/code&gt; 的各接口信息&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl showstp br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; bridge id		8000.e0be0388eec9
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.e0be0388eec9
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; root port			0			path cost &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; max age			20.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; forward delay		15.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;			2.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ageing &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;		300.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello timer		0.00s		&amp;lt;tbd&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; forward timer		0.00s		&amp;lt;tbd&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ageing timer		0.00s		&amp;lt;tbd&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; enp3s0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id			8001			&lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; state forwarding
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; path cost			&lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated port	&lt;span class=&#34;m&#34;&gt;8001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; forward delay		15.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;			2.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; max age			20.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ageing &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;		300.00s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; priority			&lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当前网络拓扑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            |          Internet                 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   enp3s0 (Host Interface)               |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   IP: 10.12.192.173                     |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                         br0 (Bridge)                    |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                      IP: 10.12.192.173                  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建一个 &lt;code&gt;tap0&lt;/code&gt; 接口用于&lt;code&gt;VM0&lt;/code&gt;使用，允许 &lt;code&gt;user&lt;/code&gt; 用户访问&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo tunctl -t tap0 -u user       
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在虚拟网桥中增加 &lt;code&gt;tap0&lt;/code&gt; 接口&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl addif br0 tap0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启用 tap0 接口，混杂模式&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig tap0 0.0.0.0 promisc up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;将网桥的 MAC 地址修改为宿主机的 MAC 地址，这样就可以接入公司网络了。否则因为内网的 MAC 地址过滤，无法接入公司网络。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ifconfig br0  hw ether 08:00:27:32:e7:09  promisc up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看虚拟网桥列表&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo brctl show br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bridge name	    bridge id		    STP enabled    interfaces
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0		            8000.08002732e709  no             enp3s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                        tap0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看当前的网桥状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo brctl showstp br0 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; bridge id		8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; root port		   0			path cost		   &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; max age		  20.00			bridge max age		  20.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;		   2.00			bridge hello &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;	   2.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; forward delay		  15.00			bridge forward delay	  15.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; ageing &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt;		 300.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; hello timer		   0.00			tcn timer		   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; topology change timer	   0.00			gc timer		   7.75
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; flags			
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp2s0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id		8001			state		     forwarding
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709	path cost		   &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709	message age timer	   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated port	8001			forward delay timer	   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated cost	   0			hold timer		   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; flags			
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tap0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id		8002			state		     disabled
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709	path cost		 &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709	message age timer	   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated port	8002			forward delay timer	   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated cost	   0			hold timer		   0.00
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; flags				
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;tap0&lt;/code&gt;可能处于&lt;code&gt;disabled&lt;/code&gt;状态，因为还没有虚拟机使用它。启动虚拟机之后会自动切换到&lt;code&gt;forwarding&lt;/code&gt;状态。&lt;/p&gt;
&lt;p&gt;当前网络拓扑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            |          Internet                 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   enp3s0 (Host Interface)               |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   IP: 10.12.192.173                     |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                         br0 (Bridge)                    |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                      IP: 10.12.192.173                  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  +---------------------+                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |        tap0         |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |     IP: 0.0.0.0     |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启动 QEMU&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,format=qcow2,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -object rng-random,filename=/dev/urandom,id=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-vga \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-rng-device,rng=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-net-device,netdev=tapnet,mac=e0:be:03:88:54:e8 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -netdev tap,id=tapnet,ifname=tap0,script=no,downscript=no \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关注这段脚本的网络配置部分：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;详细解释可以查看“QEMU 网络虚拟化章节”，第一个参数 &lt;code&gt;-device virtio-net-device&lt;/code&gt; 定义了名为 &lt;code&gt;virtio-net-device&lt;/code&gt; 的网络设备，并将其连接到一个名为 &lt;code&gt;tapnet&lt;/code&gt; 的网络设备上，指定它的 MAC 地址为 &lt;code&gt;e0:be:03:88:54:e8&lt;/code&gt;。第二个参数 &lt;code&gt;-netdev tap&lt;/code&gt; 用于指定后端实现，使用&lt;code&gt;tap&lt;/code&gt;方式，并且指定唯一 ID 为&lt;code&gt;tapnet&lt;/code&gt;由&lt;code&gt;-device&lt;/code&gt;参数中的子参数&lt;code&gt;netdev&lt;/code&gt;使用，指定&lt;code&gt;ifname=tap0&lt;/code&gt;，表示使用&lt;code&gt;tap0&lt;/code&gt;接口作为虚拟化的后端。&lt;code&gt;script=no&lt;/code&gt;和&lt;code&gt;downscript=no&lt;/code&gt;表示不使用脚本来启动和关闭&lt;code&gt;tap0&lt;/code&gt;接口。&lt;/p&gt;
&lt;p&gt;查看当前的网络接口信息&lt;code&gt;ifconfig&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4419&amp;lt;UP,BROADCAST,RUNNING,PROMISC,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::e2be:3ff:fe88:eec9  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 08:00:27:32:e7:09  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;861148&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;310707296&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;310.7 MB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;17556062&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;1516515693&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.5 GB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp2s0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::4964:61f8:420d:6781  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 08:00:27:32:e7:09  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;894523&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;325547917&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;325.5 MB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;1926&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;17563568&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;1516947572&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.5 GB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lo: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;65536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 127.0.0.1  netmask 255.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 ::1  prefixlen &lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;  scopeid 0x10&amp;lt;host&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        loop  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Local Loopback&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;1654925876&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;134933568498&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;134.9 GB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;1654925876&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;134933568498&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;134.9 GB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tap0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::f8ae:85ff:fed7:f9cd  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether fa:ae:85:d7:f9:cd  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;557&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;44913&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;44.9 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;7165&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;832171&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;832.1 KB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;55942&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当前网络拓扑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            |          Internet                 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   enp3s0 (Host Interface)               |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   IP: 10.12.192.173                     |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                         br0 (Bridge)                    |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                      IP: 10.12.192.173                  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  +---------------------+                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |        tap0         |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |     IP: 0.0.0.0     |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------|-----------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------|-----------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |        eth0         |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  |     IP:10.12.193.53 |                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                  +---------------------+                |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                     VM0 (QEMU)                          |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看当前的网桥状态，可以看到 tap0 已经处于 forwarding 状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo brctl showstp br0 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;br0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; bridge id		8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;enp2s0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id		8001			state		     forwarding
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709	path cost		   &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709	message age 		
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tap0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;2&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; port id		8002			state		     forwarding
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated root	8000.08002732e709	path cost		 &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; designated bridge	8000.08002732e709	message age 			
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;添加 VM1 过程就忽略了，添加后的网络拓扑如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            |          Internet                 |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            +-----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   enp3s0 (Host Interface)               |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                   IP: 10.12.192.173                     |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                         br0 (Bridge)                    |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|                      IP: 10.12.192.173                  |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|    +---------------------+  +-------------------+       |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|    |        tap0         |  |        tap1       |       |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|    |     IP: 0.0.0.0     |  |     IP: 0.0.0.0   |       |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------|-------------------------|---------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                |                         |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                v                         v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---------------|----------+  +-----------|---------------+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|   |     eth0         |   |  |   |        eth0       |   |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|   |  IP:10.12.193.53 |   |  |   |  IP:10.12.193.101 |   |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|   +---------------------+|  |   +-------------------+   |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|         VM0 (QEMU)       |  |         VM1 (QEMU)        |
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+--------------------------+  +---------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;qemu-网络虚拟化&#34;&gt;QEMU 网络虚拟化&lt;/h2&gt;
&lt;p&gt;QEMU 对于网络的虚拟化需要两个参数来指定：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;其中一个用于指定网络的前端驱动，也就是 Guest 中的实现&lt;/li&gt;
&lt;li&gt;另一个用于指定网络的后端实现，也就是在 Host 中的实现。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;QEMU 支持两种方式来实现网络虚拟化，一种是旧版本上使用的参数为 &lt;code&gt;-net&lt;/code&gt; 配合 &lt;code&gt;-net&lt;/code&gt; ，另一种是在新版本上支持的 &lt;code&gt;-device&lt;/code&gt; 配合 &lt;code&gt;-netdev&lt;/code&gt; 。QEMU 的发展趋势是倾向于用 &lt;code&gt;-device&lt;/code&gt; 一种命令格式来虚拟出不同的设备，其中包括网卡设备。&lt;/p&gt;
&lt;h3 id=&#34;-net---net-legacy&#34;&gt;-net &amp;amp; -net (legacy)&lt;/h3&gt;
&lt;p&gt;虽然仍然支持，但是逐步被废弃，不推荐使用。&lt;/p&gt;
&lt;p&gt;我们以以下命令为例，来说明 &lt;code&gt;-net&lt;/code&gt; 和 &lt;code&gt;-net&lt;/code&gt; 的使用方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;vcpu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;memory&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;drive&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;openEuler-22.09-V1-riscv64-qemu.qcow2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;fw&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;fw_payload_oe_qemuvirt.elf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;cmd&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;qemu-system-riscv64 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -nographic -machine virt \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -smp &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; -m &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;G \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -kernel &amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -bios none \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -drive file=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;,format=qcow2,id=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -object rng-random,filename=/dev/urandom,id=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-vga \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-rng-device,rng=rng0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device virtio-blk-device,drive=hd0 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -net nic,mac=52:54:00:12:34:56 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -net tap,ifname=tap0,script=no,downscript=no \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;  -append &amp;#39;root=/dev/vda1 rw console=ttyS0 swiotlb=1 loglevel=3 systemd.default_timeout_start_sec=600 selinux=0 highres=off mem=&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory_append&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;M earlycon&amp;#39; &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中这两个参数即实现了虚拟化网络：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -net nic,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;52:54:00:12:34:56 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -net tap,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数 &lt;code&gt;-net nic&lt;/code&gt; 用于指定上述所说的前端驱动，也就是 Guest 中的实现，这里使用的是 默认的驱动，这个驱动是 QEMU 中的一个虚拟网卡设备，指定它的 MAC 地址为 &lt;code&gt;52:54:00:12:34:56&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第二个参数 &lt;code&gt;-net tap&lt;/code&gt; 用于指定后端实现，也就是 Host 中的实现，这里使用的是 &lt;code&gt;tap&lt;/code&gt; 驱动，它的网卡名称为 &lt;code&gt;tap0&lt;/code&gt;，并且不执行任何脚本。这两个参数的组合就实现了虚拟化网络。&lt;/p&gt;
&lt;p&gt;更多示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-net nic,model&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;virtio &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-net tap,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap3,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/ect/qemu/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数 &lt;code&gt;-net nic&lt;/code&gt; 用于指定上述所说的前端驱动，也就是 Guest 中的实现，这里使用的是 &lt;code&gt;virtio&lt;/code&gt; 驱动，这个驱动是 QEMU 中的一个虚拟网卡设备。第二个参数 &lt;code&gt;-net tap&lt;/code&gt; 用于指定后端实现，也就是 Host 中的实现，这里使用的是 &lt;code&gt;tap&lt;/code&gt; 驱动，它的网卡名称为 &lt;code&gt;tap3&lt;/code&gt;，并且执行脚本 &lt;code&gt;/ect/qemu/qemu-ifup&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;解释&lt;code&gt;/ect/qemu/qemu-ifup&lt;/code&gt;
该脚本用于创建网桥，将网桥与宿主机的网卡绑定，然后将虚拟网卡绑定到网桥上，这样虚拟机就可以通过网桥与宿主机通信，宿主机也可以通过网桥与虚拟机通信。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;-device---netdev-recommended&#34;&gt;-device &amp;amp; -netdev （Recommended）&lt;/h3&gt;
&lt;p&gt;这是新版本的 QEMU 支持的命令格式，也是 QEMU 未来的发展趋势，我们以以下命令为例，来说明 &lt;code&gt;-device&lt;/code&gt; 和 &lt;code&gt;-netdev&lt;/code&gt; 的使用方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-riscv64 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -nographic -machine virt &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -smp &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;G &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -bios &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -drive &lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;,format&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;qcow2,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hd0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -object rng-random,filename&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/urandom,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rng0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-vga &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-rng-device,rng&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rng0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-blk-device,drive&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hd0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/qemu-script/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中这两个参数即实现了虚拟化网络：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tap0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;~/qemu-script/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数 &lt;code&gt;-device virtio-net-device&lt;/code&gt; 用于指定上述所说的前端驱动，也就是 Guest 中的实现，定义了名为 &lt;code&gt;virtio-net-device&lt;/code&gt; 的网络设备，并将其连接到一个名为 &lt;code&gt;tapnet&lt;/code&gt; 的网络设备上，指定它的 MAC 地址为 &lt;code&gt;e0:be:03:88:54:e8&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第二个参数 &lt;code&gt;-netdev tap&lt;/code&gt; 用于指定后端实现，使用&lt;code&gt;tap&lt;/code&gt;方式，并且指定唯一 ID 为&lt;code&gt;tapnet&lt;/code&gt;由&lt;code&gt;-device&lt;/code&gt;参数中的子参数&lt;code&gt;netdev&lt;/code&gt;使用。网卡名称为&lt;code&gt;tap0&lt;/code&gt;并且执行脚本 &lt;code&gt;~/qemu-script/qemu-ifup&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;-netdev 参数中 id 的使用
-netdev 参数中的 id 用于指定唯一的 ID，这个 ID 会被 &lt;code&gt;-device&lt;/code&gt; 参数中的子参数 &lt;code&gt;netdev&lt;/code&gt; 使用，这样 &lt;code&gt;-device&lt;/code&gt; 参数就知道要将前端驱动连接到哪个后端实现上了。id 可以自定义任意唯一字符串如&lt;code&gt;-netdev tap,id=test&lt;/code&gt;对应&lt;code&gt;-device virtio-net-device,netdev=test&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;更多示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-pci,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;no &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一个参数 &lt;code&gt;-device virtio-net-pci&lt;/code&gt; 定义了名为 &lt;code&gt;virtio-net-pci&lt;/code&gt; 的网络设备，并将其连接到一个名为 &lt;code&gt;tapnet&lt;/code&gt; 的网络设备上，指定它的 MAC 地址为 &lt;code&gt;e0:be:03:88:54:e8&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第二个参数，仔细观察会发现，我们没有定义链接到后端网卡的名称&lt;code&gt;ifname&lt;/code&gt;，这是因为以&lt;code&gt;tap&lt;/code&gt;模式启动 QEMU 时会自动创建&lt;code&gt;tap&lt;/code&gt;设备，具体网卡名称根据当前宿主机的网卡情况而定，默认会创建一个名为&lt;code&gt;tap0&lt;/code&gt;的网卡，如果启动了两个虚拟机，那么第二个虚拟机的网卡名称就是&lt;code&gt;tap1&lt;/code&gt;，以此类推。&lt;/p&gt;
&lt;h3 id=&#34;区分-tap-模式与-bridge-模式&#34;&gt;区分 tap 模式与 bridge 模式&lt;/h3&gt;
&lt;p&gt;我们有时候会用以下的命令进行 QEMU 虚拟机桥接网络的配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;bridgenet,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;52:54:00:12:34:57 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev bridge,ifname&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;br0,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;bridgenet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这也能为我们创建一个桥接网络，这是因为它和 &lt;code&gt;-netdev tap&lt;/code&gt; 的工作方式是一样的，只是 &lt;code&gt;-netdev bridge&lt;/code&gt; 的简化写法，&lt;code&gt;qemu-bridge-helper&lt;/code&gt; 在背后替我们做了 &lt;code&gt;tap&lt;/code&gt; 设备创建以及将 &lt;code&gt;tap&lt;/code&gt; 设备加入桥接口的所有事情。&lt;/p&gt;
&lt;h3 id=&#34;添加多张网卡&#34;&gt;添加多张网卡&lt;/h3&gt;
&lt;p&gt;如果了解上述内容，添加多张网卡就十分容易实现了，我们只需要再添加一对 &lt;code&gt;-device&lt;/code&gt; 和 &lt;code&gt;-netdev&lt;/code&gt; 参数即可，如下所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-riscv64 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -nographic -machine virt &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -smp &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$vcpu&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; -m &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$memory&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;G &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -bios &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$fw&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -drive &lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$drive&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;,format&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;qcow2,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hd0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -object rng-random,filename&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/dev/urandom,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rng0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-vga &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-rng-device,rng&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;rng0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-blk-device,drive&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hd0 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet0,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet0,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/qemu/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/qemu/qemu-ifdown &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device virtio-net-device,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet1,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;e0:be:03:88:54:e8 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -netdev tap,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;tapnet1,script&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/qemu/qemu-ifup,downscript&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/etc/qemu/qemu-ifdown &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -device qemu-xhci -usb -device usb-kbd -device usb-tablet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;需要注意的是，我们需要为每个 &lt;code&gt;-device&lt;/code&gt; 参数指定一个唯一的 ID，这个 ID 会被 &lt;code&gt;-netdev&lt;/code&gt; 参数中的子参数 &lt;code&gt;netdev&lt;/code&gt; 使用，这样 &lt;code&gt;-device&lt;/code&gt; 参数就知道要将前端驱动连接到哪个后端实现上了。并且每个 &lt;code&gt;tap&lt;/code&gt; 设备只能被一个虚拟机使用，所以每个虚拟机的 &lt;code&gt;tap&lt;/code&gt; 设备名称不能相同。&lt;/p&gt;
&lt;p&gt;登录虚拟机查看网卡信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ifconfig &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;eth0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.193.53  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::9e6:287b:30a2:574d  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether e0:be:03:88:54:e8  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;81&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;9871&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;9.6 KiB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;19&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;1735&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.6 KiB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;eth1: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 10.12.193.101  netmask 255.255.240.0  broadcast 10.12.207.255
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 fe80::4fe0:9e1e:4681:52b7  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ether 80:d4:09:62:cd:3c  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Ethernet&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;76&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;9471&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;9.2 KiB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;15&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;1708&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;1.6 KiB&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lo: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;73&amp;lt;UP,LOOPBACK,RUNNING&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;65536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet 127.0.0.1  netmask 255.0.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        inet6 ::1  prefixlen &lt;span class=&#34;m&#34;&gt;128&lt;/span&gt;  scopeid 0x10&amp;lt;host&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        loop  txqueuelen &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;Local Loopback&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX packets &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.0 B&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        RX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  frame &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX packets &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  bytes &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.0 B&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        TX errors &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  dropped &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; overruns &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  carrier &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  collisions &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;不同网络策略工作方式&#34;&gt;不同网络策略工作方式&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;NAT 网络模式
&lt;ul&gt;
&lt;li&gt;NAT 网络以路由器的 NAT 功能为原理，允许虚拟机通过共享主机的 IP 地址访问互联网，但虚拟机之间不能直接通信。通过端口转发可以实现虚拟机之间的连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;桥接网络模式
&lt;ul&gt;
&lt;li&gt;桥接网络模式通过虚拟交换机连接虚拟机和主机，使得虚拟机可以通过局域网访问互联网，并允许虚拟机之间直接通信。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;内部网络模式
&lt;ul&gt;
&lt;li&gt;内部网络模式使得虚拟机可以创建一个完全隔离的网络，虚拟机之间可以直接通信，但无法访问互联网或外部网络。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;仅主机网络模式
&lt;ul&gt;
&lt;li&gt;仅主机网络模式允许虚拟机之间可以通信，并且与主机之间也可以通信，但无法访问互联网或外部网络。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;VM &amp;lt;&amp;gt; VM&lt;/th&gt;
          &lt;th&gt;VM → HOST&lt;/th&gt;
          &lt;th&gt;HOST → VM&lt;/th&gt;
          &lt;th&gt;VM → Internet&lt;/th&gt;
          &lt;th&gt;Internet → VM&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;网络地址转换 NAT&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;NAT 网络&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;Bridged Adapter 桥接网卡&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;h2 id=&#34;tuntap-网络设备&#34;&gt;TUN/TAP 网络设备&lt;/h2&gt;
&lt;p&gt;TAP 属于 Linux 内核支持的一种虚拟化网络设备，还有 TUN 也属于这种设备，它们完全由软件模拟实现，TUN/TAP 负责在内核协议栈和用户进程之间传送协议数据单元。TUN 工作在网络层，而 TAP 工作在数据链路层，TUN 负责与应用程序交换 IP 数据包，而 TAP 与应用程序交换以太网帧。所以 TUN 经常涉及路由，而 TAP 常用于网络桥接。&lt;/p&gt;
&lt;h1 id=&#34;ssh-远程登录虚拟机&#34;&gt;SSH 远程登录虚拟机&lt;/h1&gt;
&lt;p&gt;宿主机任意下目录执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ssh-keygen -t rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Generating public/private rsa key pair.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter file in which to save the key &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;/home/user/.ssh/id_rsa&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: host2vm0_id_irsa
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter passphrase &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;empty &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; no passphrase&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Enter same passphrase again: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your identification has been saved in host2vm0_id_irsa.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Your public key has been saved in host2vm0_id_irsa.pub.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The key fingerprint is:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SHA256:OkWcw+R3x6Z2mzeYQuG033H3N9qIeym3TZKzz6YD8tQ user@ubuntu18
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The key&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;s randomart image is:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+---&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;RSA 2048&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;----+
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        .        &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; .   .   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        B .o. +  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       . oo.o+   &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;        S  ++ ..o&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       o ..+.E&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;      o   +..B+&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;       .   &lt;span class=&#34;nv&#34;&gt;oo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;@o+&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;           &lt;span class=&#34;nv&#34;&gt;o&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;**&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;+----&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;SHA256&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;-----+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一直回车确定，生成公私钥，保存在&lt;code&gt;~/.ssh&lt;/code&gt;目录下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我在宿主机上生成的公私钥名称为，分别是&lt;code&gt;host2vm0_id_rsa&lt;/code&gt;,&lt;code&gt;host2vm0_id_rsa.pub&lt;/code&gt;方便我记忆。如果一直回车，那么生成的公私钥名称为&lt;code&gt;id_rsa&lt;/code&gt;，&lt;code&gt;id_rsa.pub&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;将公钥复制到虚拟机 &lt;code&gt;VM0&lt;/code&gt; 上，以当前虚拟机 &lt;code&gt;VM0&lt;/code&gt; 的 IP：&lt;code&gt;10.12.193.53&lt;/code&gt; 为例。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ssh-copy-id 10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;s&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, to filter out any that are already installed
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/usr/bin/ssh-copy-id: INFO: &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; key&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;s&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; remain to be installed -- &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; you are prompted now it is to install the new keys
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;user@10.12.193.53&lt;span class=&#34;s1&#34;&gt;&amp;#39;s password: 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;Number of key(s) added: 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s1&#34;&gt;Now try logging into the machine, with:   &amp;#34;ssh &amp;#39;&lt;/span&gt;10.12.193.53&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;and check to make sure that only the key(s) you wanted were added.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后就可以直接免密码登录了：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh user@10.12.193.53
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;fixed-problems-ongoing&#34;&gt;Fixed Problems (Ongoing)&lt;/h1&gt;
&lt;h2 id=&#34;cannot-ioctl-tunsetiff-tap0-device-or-resource-busy-errno16&#34;&gt;cannot ioctl tunsetiff tap0 device or resource busy (errno=16)&lt;/h2&gt;
&lt;h2 id=&#34;failed-to-initialize-tap-device-operation-not-permitted&#34;&gt;failed to initialize tap device: Operation not permitted&lt;/h2&gt;
&lt;p&gt;同类型错误：failed to create TAP device: Operation not permitted。因为创建虚拟设备 &lt;code&gt;tap&lt;/code&gt; 需要 &lt;code&gt;root&lt;/code&gt; 权限，所以需要使用 &lt;code&gt;sudo&lt;/code&gt; 命令。执行 QEMU 启动是需要添加 &lt;code&gt;sudo&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;qemu-虚拟机启动后网卡处于-down-状态无法获取-ip&#34;&gt;QEMU 虚拟机启动后网卡处于 DOWN 状态，无法获取 IP&lt;/h2&gt;
&lt;p&gt;查看是否是 MAC 地址配置错误，使用下面命令检查：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig eth0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SIOCSIFFLAGS: Cannot assign requested address
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果报错，参考下面章节&lt;strong&gt;SIOCSIFFLAGS: Cannot assign requested address&lt;/strong&gt;解决方法进行解决。&lt;/p&gt;
&lt;h2 id=&#34;虚拟机可以-ping-通外网宿主机无法-ping-外网&#34;&gt;虚拟机可以 ping 通外网，宿主机无法 ping 外网&lt;/h2&gt;
&lt;p&gt;这种情况说明基本网络没有问题，只是 DNS 解析有问题，可以通过修改&lt;code&gt;/etc/resolv.conf&lt;/code&gt;文件解决。&lt;/p&gt;
&lt;p&gt;海宁 DNS 服务器地址：&lt;code&gt;10.12.2.21&lt;/code&gt; 和 &lt;code&gt;10.12.2.22&lt;/code&gt;，我的情况是只能 &lt;code&gt;ping 10.12.2.21&lt;/code&gt;，可以选择自己能 &lt;code&gt;ping&lt;/code&gt; 通的 DNS 服务器地址。如果无法 &lt;code&gt;ping&lt;/code&gt; 通，说明问题不在这，需要自行解决。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 修改 DNS 服务器地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo vim /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加以下内容&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nameserver 10.12.2.21
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nameserver 10.12.2.22
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;网络配置错误如何恢复配置之前的状态&#34;&gt;网络配置错误，如何恢复配置之前的状态&lt;/h2&gt;
&lt;p&gt;最简单的方式 - 重启，因为所有操作都是命令行配置，都是临时配置，可以直接重启解决。&lt;/p&gt;
&lt;p&gt;既然有这一小节，说明肯定有时候不方便直接重启，那么就需要手动恢复配置之前的状态。但是能够恢复的&lt;strong&gt;前提是需要记得之前的网卡 IP 地址、子网掩码、网关、广播地址等信息&lt;/strong&gt;。这些信息在局域网里，可能只有 IP 不同，其他信息如果没记住可以查看其他同事的网卡配置即可。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 将网桥绑定的网卡从网桥上移除&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl delif br0 enp2s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo brctl delif br0 tap0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 配置宿主机网卡信息，必须一字不差，保持和之前一模一样才能恢复&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip addr add 10.12.192.173/20 broadcast 10.12.207.255 dev enp2s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 必须设置网关&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ip route add default via 10.12.192.1 dev enp2s0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 重启网络管理器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl restart NetworkManager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;-netdev-tapidtapnetscriptqemu-scriptqemu-ifupnetwork-script-qemu-scriptqemu-ifup-failed-with-status-256&#34;&gt;-netdev tap,id=tapnet,script=/qemu-script/qemu-ifup,:network script /qemu-script/qemu-ifup failed with status 256&lt;/h2&gt;
&lt;p&gt;可能原因 1: &lt;code&gt;qemu-ifup&lt;/code&gt; 脚本没有执行权限，需要添加执行权限。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod +x qemu-ifup
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可能原因 2: &lt;code&gt;qemu-ifup&lt;/code&gt; 路径不对，必须放到&lt;code&gt;/etc/qemu/&lt;/code&gt;目录下。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p /etc/qemu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv qemu-ifup /etc/qemu &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mv qemu-ifdown /etc/qemu 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x qemu-ifup
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo chmod +x qemu-ifdown
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;siocsifflags-cannot-assign-requested-address&#34;&gt;SIOCSIFFLAGS: Cannot assign requested address&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig eth0 up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;SIOCSIFFLAGS: Cannot assign requested address
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一般由于 MAC 地址配置错误导致，可以通过修改 MAC 地址为多播地址解决。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig enp2s0 hw ether 00:11:22:33:44:55
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重启网卡&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig enp2s0 down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo ifconfig enp2s0 up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MAC 地址的第一个字节中的最后一位（即第 7 位）用于标识该地址是单播，多播还是广播地址。如果这个位设置为 0，则表示这是一个单播地址；如果设置为 1，则表示这是一个多播或广播地址。&lt;/p&gt;
&lt;p&gt;使用这种方法，我们可以确定上述每个 MAC 地址是否是单播地址：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cd:c2:05:84:c8:2c&lt;/code&gt; - 单播地址&lt;/li&gt;
&lt;li&gt;&lt;code&gt;13:7b:49:fc:a6:aa&lt;/code&gt; - 单播地址&lt;/li&gt;
&lt;li&gt;&lt;code&gt;8f:aa:42:29:e8:68&lt;/code&gt; - 单播地址&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;00:11:22:33:44:55&lt;/code&gt; 是多播地址。&lt;/p&gt;
&lt;h2 id=&#34;qemu--device-drive-with-0-bus0-unit0-exists&#34;&gt;qemu -device drive with 0 bus=0 unit=0 exists&lt;/h2&gt;
&lt;p&gt;这个错误通常意味着您尝试在 QEMU VM 中添加一个重复的设备。&lt;/p&gt;
&lt;p&gt;如果您已经在 VM 中添加了驱动器，则可能会出现此问题。您可以检查是否存在两个具有相同 &lt;code&gt;bus&lt;/code&gt; 和 &lt;code&gt;unit&lt;/code&gt; 的设备（在此情况下，都是 0）。解决此问题的方法是删除重复设备或更改其配置以包括唯一的 &lt;code&gt;bus&lt;/code&gt; 和 &lt;code&gt;unit&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果您没有意图添加重复的设备，在运行 QEMU 之前，您可能需要检查您的命令行，以确保正确设置了 &lt;code&gt;-drive&lt;/code&gt; 选项。请注意，当使用 &lt;code&gt;-device&lt;/code&gt; 添加设备时，您还应该避免使用 &lt;code&gt;-drive&lt;/code&gt; 选项，因为它们可能引起冲突。&lt;/p&gt;
&lt;p&gt;如果您需要进一步帮助，建议提供完整的 QEMU 命令和参数列表，以便更好地理解问题并提供更详细的建议。&lt;/p&gt;
&lt;h1 id=&#34;参考资料&#34;&gt;参考资料&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://tomwei7.com/2021/10/09/qemu-network-config/&#34;&gt;QEMU 网络配置 // 围城&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.junmajinlong.com/img/virtual/1594802457384.png&#34;&gt;理解 Linux 虚拟网卡设备 tun/tap 的一切 | 骏马金龙&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://wzt.ac.cn/2021/05/28/QEMU-networking/&#34;&gt;QEMU 网络配置一把梭 | CataLpa&amp;rsquo;s Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.csdn.net/u014022631/article/details/53411557&#34;&gt;qemu 虚拟机与外部网络的通信 li_Jiejun 的博客-CSDN 博客&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://zhou-yuxin.github.io/articles/2018/%E5%AE%89%E8%A3%85qemu-kvm%E4%BB%A5%E5%8F%8A%E9%85%8D%E7%BD%AE%E6%A1%A5%E6%8E%A5%E7%BD%91%E7%BB%9C/index.html&#34;&gt;安装 qemu-kvm 以及配置桥接网络&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.csdn.net/rikeyone/article/details/106767540&#34;&gt;QEMU 中的网络虚拟化配置_程序猿 Ricky 的日常干货的博客-CSDN 博客&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/QEMU/&#34;&gt;Nginx Directory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cnblogs.com/huqingyu/archive/2005/04/03/131102.html&#34;&gt;QEMU 网络配置 - 浙林龙哥 - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://zhuanlan.zhihu.com/p/432022126&#34;&gt;【qemu】qemu 网络配置 - 知乎&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://tomwei7.com/2021/10/09/qemu-network-config/&#34;&gt;QEMU 网络配置 // 围城&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://zhou-yuxin.github.io/articles/2018/%E5%AE%89%E8%A3%85qemu-kvm%E4%BB%A5%E5%8F%8A%E9%85%8D%E7%BD%AE%E6%A1%A5%E6%8E%A5%E7%BD%91%E7%BB%9C/index.html&#34;&gt;安装 qemu-kvm 以及配置桥接网络&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.virt.ltd/blog/archives/37/&#34;&gt;在 qemu 中使用桥接网络 - T^3 Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://wiki.yanick.site/wiki/os/qemu/&#34;&gt;为 QEMU 配置网桥上网 | Yanick&amp;rsquo;s Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.junmajinlong.com/virtual/network/all_about_tun_tap/&#34;&gt;理解 Linux 虚拟网卡设备 tun/tap 的一切 | 骏马金龙&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://wzt.ac.cn/2021/05/28/QEMU-networking/&#34;&gt;QEMU 网络配置一把梭 | CataLpa&amp;rsquo;s Site&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;附录&#34;&gt;附录&lt;/h1&gt;
</description>
      <content:encoded><![CDATA[<h1 id="quick-setup">Quick Setup</h1>
<h2 id="安装工具">安装工具</h2>
<p>安装两个网络管理工具用于建立网桥以及虚拟网卡：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 安装虚拟网桥工具</span>
</span></span><span class="line"><span class="cl">sudo apt install bridge-utils -y
</span></span><span class="line"><span class="cl"><span class="c1"># UML（User-mode linux）工具        </span>
</span></span><span class="line"><span class="cl">sudo apt install uml-utilities  -y   
</span></span></code></pre></div><h2 id="配置脚本">配置脚本</h2>
<h3 id="qemu-ifup">qemu-ifup</h3>
<p>将下面的脚本保存为文件 <code>qemu-ifup</code>，并赋予可执行权限：</p>
<blockquote>
<p>为了方便复制脚本，在 confluence 页面提供了脚本内容，可以直接复制。</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p /etc/qemu
</span></span><span class="line"><span class="cl">mv qemu-ifup /etc/qemu <span class="o">&amp;&amp;</span> mv qemu-ifdown /etc/qemu 
</span></span><span class="line"><span class="cl">sudo chmod +x qemu-ifup
</span></span><span class="line"><span class="cl">sudo chmod +x qemu-ifdown
</span></span></code></pre></div><p>因为网卡信息不容易定位，可能一台机器有多个网卡，所以不方便用脚本获取，需要手动设置一下。将下面的<code>NIC</code>值修改为宿主机可以上网的网卡名称。可以通过<code>ifconfig</code>命令查看。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># 设置默认网卡信息</span>
</span></span><span class="line"><span class="cl"><span class="nv">NIC</span><span class="o">=</span>enp2s0
</span></span><span class="line"><span class="cl"><span class="c1"># 设置用户名</span>
</span></span><span class="line"><span class="cl"><span class="nv">USER_NAME</span><span class="o">=</span>user
</span></span><span class="line"><span class="cl"><span class="c1"># 设置网桥名称</span>
</span></span><span class="line"><span class="cl"><span class="nv">BRIDGE</span><span class="o">=</span>br0
</span></span><span class="line"><span class="cl"><span class="c1"># 设置网络信息</span>
</span></span><span class="line"><span class="cl"><span class="nv">NIC_IP</span><span class="o">=</span><span class="k">$(</span>ifconfig <span class="nv">$NIC</span> <span class="p">|</span> grep <span class="s2">&#34;inet\b&#34;</span> <span class="p">|</span> awk <span class="s1">&#39;{print $2}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">NIC_NETMAST</span><span class="o">=</span><span class="k">$(</span>ifconfig <span class="nv">$NIC</span> <span class="p">|</span> grep <span class="s2">&#34;inet\b&#34;</span> <span class="p">|</span> awk <span class="s1">&#39;{print $4}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">NIC_BROADCAST</span><span class="o">=</span><span class="k">$(</span>ifconfig <span class="nv">$NIC</span> <span class="p">|</span> grep <span class="s2">&#34;inet\b&#34;</span> <span class="p">|</span> awk <span class="s1">&#39;{print $6}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">NETMASK</span><span class="o">=</span>255.255.240.0
</span></span><span class="line"><span class="cl"><span class="c1"># 设置默认网关地址</span>
</span></span><span class="line"><span class="cl"><span class="nv">GATEWAY</span><span class="o">=</span>10.12.192.1
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 获取宿主机网卡MAC地址，因为创建的网桥MAC地址是随机的，</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 无法接入公司，需要从开发机网卡将其MAC地址赋值给网桥</span>
</span></span><span class="line"><span class="cl"><span class="nv">MAC</span><span class="o">=</span><span class="k">$(</span>ifconfig <span class="nv">$NIC</span> <span class="p">|</span> grep <span class="s2">&#34;ether\b&#34;</span> <span class="p">|</span> awk <span class="s1">&#39;{print $2}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查网桥是否已创建，已创建就忽略</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> check_bridge<span class="o">()</span> 
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">    echO <span class="s2">&#34;Check bridge...&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> brctl show <span class="p">|</span> grep <span class="s2">&#34;^</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span> <span class="p">&amp;</span>&gt; /dev/null<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 创建网桥</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> create_bridge<span class="o">()</span> 
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Start Create bridge...&#34;</span>
</span></span><span class="line"><span class="cl">    brctl addbr <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    brctl addif <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span>  <span class="s2">&#34;</span><span class="nv">$NIC</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    ifconfig br0 0.0.0.0 promisc up
</span></span><span class="line"><span class="cl">    dhclient <span class="nv">$BRIDGE</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ifconfig &#34;$BRIDGE&#34; &#34;$NIC_IP&#34; netmask &#34;$NIC_NETMAST&#34; broadcast &#34;$NIC_BROADCAST&#34;  hw ether &#34;$MAC&#34; promisc up</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 启用IP转发</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> enable_ip_forward<span class="o">()</span> 
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="m">1</span> &gt; /proc/sys/net/ipv4/ip_forward
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 设置网桥</span>
</span></span><span class="line"><span class="cl"><span class="k">function</span> setup_bridge<span class="o">()</span>
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">    check_bridge <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="nv">$?</span> -eq <span class="m">0</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">        create_bridge
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    enable_ip_forward
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    setup_bridge
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Creating </span><span class="nv">$1</span><span class="s2">...&#34;</span>
</span></span><span class="line"><span class="cl">    tunctl -t <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> -u <span class="s2">&#34;</span><span class="nv">$USER_NAME</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    ifconfig <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> 0.0.0.0 up
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Adding </span><span class="nv">$1</span><span class="s2"> to </span><span class="nv">$BRIDGE</span><span class="s2">...&#34;</span>
</span></span><span class="line"><span class="cl">    brctl addif <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    sleep <span class="m">5</span>
</span></span><span class="line"><span class="cl">    ifconfig <span class="s2">&#34;</span><span class="nv">$BRIDGE</span><span class="s2">&#34;</span>  hw ether <span class="s2">&#34;</span><span class="nv">$MAC</span><span class="s2">&#34;</span> promisc up
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Error: no interface specified.&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><h3 id="qemu-ifdown">qemu-ifdown</h3>
<p>以下是<code>qemu-ifdown</code>脚本，用于在关闭 QEMU 时关闭虚拟网卡，将其从网桥中移除，删除虚拟网卡。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="c1"># 设置网桥名称</span>
</span></span><span class="line"><span class="cl"><span class="nv">BRIDGE</span><span class="o">=</span>br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> -n <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 将tap设备从网桥中移除</span>
</span></span><span class="line"><span class="cl">	brctl delif <span class="si">${</span><span class="nv">BRIDGE</span><span class="si">}</span> <span class="nv">$1</span>
</span></span><span class="line"><span class="cl">	<span class="c1"># 关闭tap设备</span>
</span></span><span class="line"><span class="cl">	ip link link <span class="nv">$1</span> down
</span></span><span class="line"><span class="cl">    <span class="c1"># 删除tap设备</span>
</span></span><span class="line"><span class="cl">    ip link del <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">	tunctl -d <span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">else</span>
</span></span><span class="line"><span class="cl">	<span class="nb">echo</span> <span class="s2">&#34;Error: no interface specified&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span></code></pre></div><p>将系统镜像复制一份并修改文件名，QEMU 不能同时使用一个镜像启动两个虚拟机。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cp openEuler-22.09-riscv64-qemu.qcow2 openEuler-22.09-riscv64-qemu-vm1.qcow2
</span></span></code></pre></div><p>需要修改启动脚本中的镜像文件名，以及启动参数，将<code>drive</code>以及<code>cmd</code>变量的内容覆盖为下面的内容，修改<code>mac</code>为分配给自己的虚拟机的 MAC 地址，<code>script</code>为上面的脚本<code>qemu-ifup</code>的路径。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 该脚本用于启动VM0</span>
</span></span><span class="line"><span class="cl"><span class="nv">drive</span><span class="o">=</span><span class="s2">&#34;openEuler-22.09-riscv64-qemu.qcow2&#34;</span>
</span></span><span class="line"><span class="cl">....
</span></span><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=&#34;</span><span class="nv">$drive</span><span class="s2">&#34;,format=qcow2,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -object rng-random,filename=/dev/urandom,id=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-vga \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-rng-device,rng=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-net-device,netdev=tapnet,mac=e0:be:03:88:54:e8 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -netdev tap,id=tapnet,script=/etc/qemu/qemu-ifup,downscript=/etc/qemu/qemu-ifdown \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&#34;</span>
</span></span></code></pre></div><p>以<code>sudo</code>权限启动脚本：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ./preview_start_vm0.sh
</span></span></code></pre></div><p>以下为配置 VM1 过程，VM1 的启动脚本与 VM0 的启动脚本类似，只需要修改<code>drive</code>以及<code>MAC</code>，必须保证<code>MAC</code>与 VM0 的<code>MAC</code>不同。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 该脚本用于启动VM1</span>
</span></span><span class="line"><span class="cl"><span class="nv">drive</span><span class="o">=</span><span class="s2">&#34;openEuler-22.09-riscv64-qemu.qcow2&#34;</span>
</span></span><span class="line"><span class="cl">....
</span></span><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=&#34;</span><span class="nv">$drive</span><span class="s2">&#34;,format=qcow2,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -object rng-random,filename=/dev/urandom,id=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-vga \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-rng-device,rng=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-net-device,netdev=tapnet,mac=80:d4:09:62:cd:3c \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -netdev tap,id=tapnet,script=/etc/qemu/qemu-ifup,downscript=/etc/qemu/qemu-ifdown \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&#34;</span>
</span></span></code></pre></div><h2 id="网络通信测试">网络通信测试</h2>
<p>当前网络状态如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">HOST:10.12.192.177
</span></span><span class="line"><span class="cl">VM0:10.12.193.53
</span></span><span class="line"><span class="cl">VM1:10.12.193.101
</span></span></code></pre></div><h3 id="host--vm0">HOST &ndash;&gt; VM0</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># user @ ubuntu18 in ~/openeuler/openEuler2209 [18:41:54] </span>
</span></span><span class="line"><span class="cl">$ ping -c <span class="m">3</span> 10.12.193.101
</span></span><span class="line"><span class="cl">PING 10.12.193.101 <span class="o">(</span>10.12.193.101<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.101: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.37 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.101: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>0.897 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.101: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>0.890 ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- 10.12.193.101 ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">3</span> received, 0% packet loss, <span class="nb">time</span> 2002ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 0.890/1.055/1.378/0.228 ms
</span></span></code></pre></div><p>Host &ndash;&gt; VM1 的测试结果与 Host &ndash;&gt; VM0 的测试结果相同。</p>
<h3 id="vm0--host">VM0 &ndash;&gt; HOST</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ping -c 3 10.12.193.53 </span>
</span></span><span class="line"><span class="cl">PING 10.12.193.53 <span class="o">(</span>10.12.193.53<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>0.716 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.74 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.81 ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- 10.12.193.53 ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">3</span> received, 0% packet loss, <span class="nb">time</span> 2009ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 0.716/1.424/1.812/0.501 ms
</span></span></code></pre></div><p>VM1 &ndash;&gt; Host 的测试结果与 Host &ndash;&gt; VM0 的测试结果相同。</p>
<h3 id="vm1--vm0">VM1 &ndash;&gt; VM0</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ping -c 3 10.12.193.53 </span>
</span></span><span class="line"><span class="cl">PING 10.12.193.53 <span class="o">(</span>10.12.193.53<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>0.716 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.74 ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from 10.12.193.53: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">time</span><span class="o">=</span>1.81 ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- 10.12.193.53 ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">3</span> received, 0% packet loss, <span class="nb">time</span> 2009ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 0.716/1.424/1.812/0.501 ms
</span></span></code></pre></div><p>VM0 &ndash;&gt; VM1 与 VM1 &ndash;&gt; VM0 的测试结果相同。</p>
<h3 id="vm0--github">VM0 &ndash;&gt; github</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ping -c 4 github.com</span>
</span></span><span class="line"><span class="cl">PING github.com <span class="o">(</span>192.30.255.113<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">221</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">277</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">216</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">4</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">218</span> ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- github.com ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">4</span> packets transmitted, <span class="m">4</span> received, 0% packet loss, <span class="nb">time</span> 3014ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 215.984/232.733/276.593/25.374 ms
</span></span></code></pre></div><h3 id="host--github">HOST &ndash;&gt; github</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># user @ ubuntu18 in ~/openeuler/openEuler2209 [17:59:40] </span>
</span></span><span class="line"><span class="cl">$ ping -c <span class="m">3</span>  github.com
</span></span><span class="line"><span class="cl">PING github.com <span class="o">(</span>192.30.255.113<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">218</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">216</span> ms
</span></span><span class="line"><span class="cl"><span class="m">64</span> bytes from lb-192-30-255-113-sea.github.com <span class="o">(</span>192.30.255.113<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">3</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">46</span> <span class="nv">time</span><span class="o">=</span><span class="m">216</span> ms
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">--- github.com ping statistics ---
</span></span><span class="line"><span class="cl"><span class="m">3</span> packets transmitted, <span class="m">3</span> received, 0% packet loss, <span class="nb">time</span> 2002ms
</span></span><span class="line"><span class="cl">rtt min/avg/max/mdev <span class="o">=</span> 216.252/217.087/218.409/0.945 ms
</span></span></code></pre></div><h1 id="原理探究-ongoing">原理探究 (Ongoing)</h1>
<h2 id="step-by-step-解析">Step by Step 解析</h2>
<p>查看一下网络接口信息：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ifconfig
</span></span><span class="line"><span class="cl">enp3s0: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::a00:27ff:fe32:e709  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 08:00:27:32:e7:09  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">6017</span>  bytes <span class="m">5412928</span> <span class="o">(</span>5.4 MB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">1979</span>  bytes <span class="m">179467</span> <span class="o">(</span>179.4 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">lo: <span class="nv">flags</span><span class="o">=</span>73&lt;UP,LOOPBACK,RUNNING&gt;  mtu <span class="m">65536</span>
</span></span><span class="line"><span class="cl">        inet 127.0.0.1  netmask 255.0.0.0
</span></span><span class="line"><span class="cl">        inet6 ::1  prefixlen <span class="m">128</span>  scopeid 0x10&lt;host&gt;
</span></span><span class="line"><span class="cl">        loop  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Local Loopback<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">125</span>  bytes <span class="m">10142</span> <span class="o">(</span>10.1 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">125</span>  bytes <span class="m">10142</span> <span class="o">(</span>10.1 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span></code></pre></div><p>创建一个名为<code>br0</code>的网桥</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl addbr br0
</span></span></code></pre></div><p>将网桥与宿主机的网卡绑定</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl addif br0 enp3s0
</span></span></code></pre></div><p>启用 <code>br0</code> 接口，并从 DHCP 服务器获得 IP 地址</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ifconfig br0 0.0.0.0 promisc up
</span></span><span class="line"><span class="cl">sudo dhclient br0
</span></span></code></pre></div><p>查看虚拟网桥列表</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl show br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">bridge name	    bridge id		    STP enabled    interfaces
</span></span><span class="line"><span class="cl">br0		            8000.e0be0388eec9  no             enp3s0
</span></span></code></pre></div><p>查看 <code>br0</code> 的各接口信息</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl showstp br0
</span></span><span class="line"><span class="cl">br0
</span></span><span class="line"><span class="cl"> bridge id		8000.e0be0388eec9
</span></span><span class="line"><span class="cl"> designated root	8000.e0be0388eec9
</span></span><span class="line"><span class="cl"> root port			0			path cost <span class="m">0</span>
</span></span><span class="line"><span class="cl"> max age			20.00s
</span></span><span class="line"><span class="cl"> forward delay		15.00s
</span></span><span class="line"><span class="cl"> hello <span class="nb">time</span>			2.00s
</span></span><span class="line"><span class="cl"> ageing <span class="nb">time</span>		300.00s
</span></span><span class="line"><span class="cl"> hello timer		0.00s		&lt;tbd&gt;
</span></span><span class="line"><span class="cl"> forward timer		0.00s		&lt;tbd&gt;
</span></span><span class="line"><span class="cl"> ageing timer		0.00s		&lt;tbd&gt;
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"> enp3s0 <span class="o">(</span>1<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id			8001			<span class="nb">local</span> state forwarding
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709
</span></span><span class="line"><span class="cl"> path cost			<span class="m">100</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709
</span></span><span class="line"><span class="cl"> designated port	<span class="m">8001</span>
</span></span><span class="line"><span class="cl"> forward delay		15.00s
</span></span><span class="line"><span class="cl"> hello <span class="nb">time</span>			2.00s
</span></span><span class="line"><span class="cl"> max age			20.00s
</span></span><span class="line"><span class="cl"> ageing <span class="nb">time</span>		300.00s
</span></span><span class="line"><span class="cl"> priority			<span class="m">128</span>
</span></span></code></pre></div><p>当前网络拓扑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">            |          Internet                 |
</span></span><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                   enp3s0 (Host Interface)               |
</span></span><span class="line"><span class="cl">|                   IP: 10.12.192.173                     |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                         br0 (Bridge)                    |
</span></span><span class="line"><span class="cl">|                      IP: 10.12.192.173                  |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span></code></pre></div><p>创建一个 <code>tap0</code> 接口用于<code>VM0</code>使用，允许 <code>user</code> 用户访问</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo tunctl -t tap0 -u user       
</span></span></code></pre></div><p>在虚拟网桥中增加 <code>tap0</code> 接口</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo brctl addif br0 tap0
</span></span></code></pre></div><p>启用 tap0 接口，混杂模式</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ifconfig tap0 0.0.0.0 promisc up
</span></span></code></pre></div><p>将网桥的 MAC 地址修改为宿主机的 MAC 地址，这样就可以接入公司网络了。否则因为内网的 MAC 地址过滤，无法接入公司网络。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ifconfig br0  hw ether 08:00:27:32:e7:09  promisc up
</span></span></code></pre></div><p>查看虚拟网桥列表</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo brctl show br0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">bridge name	    bridge id		    STP enabled    interfaces
</span></span><span class="line"><span class="cl">br0		            8000.08002732e709  no             enp3s0
</span></span><span class="line"><span class="cl">                                                        tap0
</span></span></code></pre></div><p>查看当前的网桥状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo brctl showstp br0 
</span></span><span class="line"><span class="cl">br0
</span></span><span class="line"><span class="cl"> bridge id		8000.08002732e709
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709
</span></span><span class="line"><span class="cl"> root port		   0			path cost		   <span class="m">0</span>
</span></span><span class="line"><span class="cl"> max age		  20.00			bridge max age		  20.00
</span></span><span class="line"><span class="cl"> hello <span class="nb">time</span>		   2.00			bridge hello <span class="nb">time</span>	   2.00
</span></span><span class="line"><span class="cl"> forward delay		  15.00			bridge forward delay	  15.00
</span></span><span class="line"><span class="cl"> ageing <span class="nb">time</span>		 300.00
</span></span><span class="line"><span class="cl"> hello timer		   0.00			tcn timer		   0.00
</span></span><span class="line"><span class="cl"> topology change timer	   0.00			gc timer		   7.75
</span></span><span class="line"><span class="cl"> flags			
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">enp2s0 <span class="o">(</span>1<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id		8001			state		     forwarding
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709	path cost		   <span class="m">4</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709	message age timer	   0.00
</span></span><span class="line"><span class="cl"> designated port	8001			forward delay timer	   0.00
</span></span><span class="line"><span class="cl"> designated cost	   0			hold timer		   0.00
</span></span><span class="line"><span class="cl"> flags			
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tap0 <span class="o">(</span>2<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id		8002			state		     disabled
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709	path cost		 <span class="m">100</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709	message age timer	   0.00
</span></span><span class="line"><span class="cl"> designated port	8002			forward delay timer	   0.00
</span></span><span class="line"><span class="cl"> designated cost	   0			hold timer		   0.00
</span></span><span class="line"><span class="cl"> flags				
</span></span></code></pre></div><p><code>tap0</code>可能处于<code>disabled</code>状态，因为还没有虚拟机使用它。启动虚拟机之后会自动切换到<code>forwarding</code>状态。</p>
<p>当前网络拓扑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">            |          Internet                 |
</span></span><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                   enp3s0 (Host Interface)               |
</span></span><span class="line"><span class="cl">|                   IP: 10.12.192.173                     |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                         br0 (Bridge)                    |
</span></span><span class="line"><span class="cl">|                      IP: 10.12.192.173                  |
</span></span><span class="line"><span class="cl">|                  +---------------------+                |
</span></span><span class="line"><span class="cl">|                  |        tap0         |                |
</span></span><span class="line"><span class="cl">|                  |     IP: 0.0.0.0     |                |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span></code></pre></div><p>启动 QEMU</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=&#34;</span><span class="nv">$drive</span><span class="s2">&#34;,format=qcow2,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -object rng-random,filename=/dev/urandom,id=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-vga \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-rng-device,rng=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-net-device,netdev=tapnet,mac=e0:be:03:88:54:e8 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -netdev tap,id=tapnet,ifname=tap0,script=no,downscript=no \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet&#34;</span>
</span></span></code></pre></div><p>关注这段脚本的网络配置部分：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet,ifname<span class="o">=</span>tap0,script<span class="o">=</span>no,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>详细解释可以查看“QEMU 网络虚拟化章节”，第一个参数 <code>-device virtio-net-device</code> 定义了名为 <code>virtio-net-device</code> 的网络设备，并将其连接到一个名为 <code>tapnet</code> 的网络设备上，指定它的 MAC 地址为 <code>e0:be:03:88:54:e8</code>。第二个参数 <code>-netdev tap</code> 用于指定后端实现，使用<code>tap</code>方式，并且指定唯一 ID 为<code>tapnet</code>由<code>-device</code>参数中的子参数<code>netdev</code>使用，指定<code>ifname=tap0</code>，表示使用<code>tap0</code>接口作为虚拟化的后端。<code>script=no</code>和<code>downscript=no</code>表示不使用脚本来启动和关闭<code>tap0</code>接口。</p>
<p>查看当前的网络接口信息<code>ifconfig</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">br0: <span class="nv">flags</span><span class="o">=</span>4419&lt;UP,BROADCAST,RUNNING,PROMISC,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::e2be:3ff:fe88:eec9  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 08:00:27:32:e7:09  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">861148</span>  bytes <span class="m">310707296</span> <span class="o">(</span>310.7 MB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">17556062</span>  bytes <span class="m">1516515693</span> <span class="o">(</span>1.5 GB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">enp2s0: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.192.173  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::4964:61f8:420d:6781  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 08:00:27:32:e7:09  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">894523</span>  bytes <span class="m">325547917</span> <span class="o">(</span>325.5 MB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">1926</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">17563568</span>  bytes <span class="m">1516947572</span> <span class="o">(</span>1.5 GB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">lo: <span class="nv">flags</span><span class="o">=</span>73&lt;UP,LOOPBACK,RUNNING&gt;  mtu <span class="m">65536</span>
</span></span><span class="line"><span class="cl">        inet 127.0.0.1  netmask 255.0.0.0
</span></span><span class="line"><span class="cl">        inet6 ::1  prefixlen <span class="m">128</span>  scopeid 0x10&lt;host&gt;
</span></span><span class="line"><span class="cl">        loop  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Local Loopback<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">1654925876</span>  bytes <span class="m">134933568498</span> <span class="o">(</span>134.9 GB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">1654925876</span>  bytes <span class="m">134933568498</span> <span class="o">(</span>134.9 GB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tap0: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet6 fe80::f8ae:85ff:fed7:f9cd  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether fa:ae:85:d7:f9:cd  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">557</span>  bytes <span class="m">44913</span> <span class="o">(</span>44.9 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">7165</span>  bytes <span class="m">832171</span> <span class="o">(</span>832.1 KB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">55942</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span></code></pre></div><p>当前网络拓扑：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">            |          Internet                 |
</span></span><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                   enp3s0 (Host Interface)               |
</span></span><span class="line"><span class="cl">|                   IP: 10.12.192.173                     |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                         br0 (Bridge)                    |
</span></span><span class="line"><span class="cl">|                      IP: 10.12.192.173                  |
</span></span><span class="line"><span class="cl">|                  +---------------------+                |
</span></span><span class="line"><span class="cl">|                  |        tap0         |                |
</span></span><span class="line"><span class="cl">|                  |     IP: 0.0.0.0     |                |
</span></span><span class="line"><span class="cl">+---------------------------|-----------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------|-----------------------------+
</span></span><span class="line"><span class="cl">|                  |        eth0         |                |
</span></span><span class="line"><span class="cl">|                  |     IP:10.12.193.53 |                |
</span></span><span class="line"><span class="cl">|                  +---------------------+                |
</span></span><span class="line"><span class="cl">|                     VM0 (QEMU)                          |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span></code></pre></div><p>查看当前的网桥状态，可以看到 tap0 已经处于 forwarding 状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ sudo brctl showstp br0 
</span></span><span class="line"><span class="cl">br0
</span></span><span class="line"><span class="cl"> bridge id		8000.08002732e709
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">enp2s0 <span class="o">(</span>1<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id		8001			state		     forwarding
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709	path cost		   <span class="m">4</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709	message age 		
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">tap0 <span class="o">(</span>2<span class="o">)</span>
</span></span><span class="line"><span class="cl"> port id		8002			state		     forwarding
</span></span><span class="line"><span class="cl"> designated root	8000.08002732e709	path cost		 <span class="m">100</span>
</span></span><span class="line"><span class="cl"> designated bridge	8000.08002732e709	message age 			
</span></span></code></pre></div><p>添加 VM1 过程就忽略了，添加后的网络拓扑如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">            |          Internet                 |
</span></span><span class="line"><span class="cl">            +-----------------------------------+
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             |
</span></span><span class="line"><span class="cl">                             v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                   enp3s0 (Host Interface)               |
</span></span><span class="line"><span class="cl">|                   IP: 10.12.192.173                     |
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">                            |
</span></span><span class="line"><span class="cl">                            v
</span></span><span class="line"><span class="cl">+---------------------------------------------------------+
</span></span><span class="line"><span class="cl">|                         br0 (Bridge)                    |
</span></span><span class="line"><span class="cl">|                      IP: 10.12.192.173                  |
</span></span><span class="line"><span class="cl">|    +---------------------+  +-------------------+       |
</span></span><span class="line"><span class="cl">|    |        tap0         |  |        tap1       |       |
</span></span><span class="line"><span class="cl">|    |     IP: 0.0.0.0     |  |     IP: 0.0.0.0   |       |
</span></span><span class="line"><span class="cl">+---------------|-------------------------|---------------+
</span></span><span class="line"><span class="cl">                |                         |
</span></span><span class="line"><span class="cl">                v                         v
</span></span><span class="line"><span class="cl">+---------------|----------+  +-----------|---------------+
</span></span><span class="line"><span class="cl">|   |     eth0         |   |  |   |        eth0       |   |
</span></span><span class="line"><span class="cl">|   |  IP:10.12.193.53 |   |  |   |  IP:10.12.193.101 |   |
</span></span><span class="line"><span class="cl">|   +---------------------+|  |   +-------------------+   |
</span></span><span class="line"><span class="cl">|         VM0 (QEMU)       |  |         VM1 (QEMU)        |
</span></span><span class="line"><span class="cl">+--------------------------+  +---------------------------+
</span></span></code></pre></div><h2 id="qemu-网络虚拟化">QEMU 网络虚拟化</h2>
<p>QEMU 对于网络的虚拟化需要两个参数来指定：</p>
<ul>
<li>其中一个用于指定网络的前端驱动，也就是 Guest 中的实现</li>
<li>另一个用于指定网络的后端实现，也就是在 Host 中的实现。</li>
</ul>
<p>QEMU 支持两种方式来实现网络虚拟化，一种是旧版本上使用的参数为 <code>-net</code> 配合 <code>-net</code> ，另一种是在新版本上支持的 <code>-device</code> 配合 <code>-netdev</code> 。QEMU 的发展趋势是倾向于用 <code>-device</code> 一种命令格式来虚拟出不同的设备，其中包括网卡设备。</p>
<h3 id="-net---net-legacy">-net &amp; -net (legacy)</h3>
<p>虽然仍然支持，但是逐步被废弃，不推荐使用。</p>
<p>我们以以下命令为例，来说明 <code>-net</code> 和 <code>-net</code> 的使用方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">vcpu</span><span class="o">=</span><span class="m">8</span>
</span></span><span class="line"><span class="cl"><span class="nv">memory</span><span class="o">=</span><span class="m">8</span>
</span></span><span class="line"><span class="cl"><span class="nv">drive</span><span class="o">=</span><span class="s2">&#34;openEuler-22.09-V1-riscv64-qemu.qcow2&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">fw</span><span class="o">=</span><span class="s2">&#34;fw_payload_oe_qemuvirt.elf&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">cmd</span><span class="o">=</span><span class="s2">&#34;qemu-system-riscv64 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -nographic -machine virt \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -smp &#34;</span><span class="nv">$vcpu</span><span class="s2">&#34; -m &#34;</span><span class="nv">$memory</span><span class="s2">&#34;G \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -kernel &#34;</span><span class="nv">$fw</span><span class="s2">&#34; \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -bios none \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -drive file=&#34;</span><span class="nv">$drive</span><span class="s2">&#34;,format=qcow2,id=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -object rng-random,filename=/dev/urandom,id=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-vga \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-rng-device,rng=rng0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device virtio-blk-device,drive=hd0 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -net nic,mac=52:54:00:12:34:56 \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -net tap,ifname=tap0,script=no,downscript=no \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet \
</span></span></span><span class="line"><span class="cl"><span class="s2">  -append &#39;root=/dev/vda1 rw console=ttyS0 swiotlb=1 loglevel=3 systemd.default_timeout_start_sec=600 selinux=0 highres=off mem=&#34;</span><span class="nv">$memory_append</span><span class="s2">&#34;M earlycon&#39; &#34;</span>
</span></span></code></pre></div><p>其中这两个参数即实现了虚拟化网络：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -net nic,mac<span class="o">=</span>52:54:00:12:34:56 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -net tap,ifname<span class="o">=</span>tap0,script<span class="o">=</span>no,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>第一个参数 <code>-net nic</code> 用于指定上述所说的前端驱动，也就是 Guest 中的实现，这里使用的是 默认的驱动，这个驱动是 QEMU 中的一个虚拟网卡设备，指定它的 MAC 地址为 <code>52:54:00:12:34:56</code>。</p>
<p>第二个参数 <code>-net tap</code> 用于指定后端实现，也就是 Host 中的实现，这里使用的是 <code>tap</code> 驱动，它的网卡名称为 <code>tap0</code>，并且不执行任何脚本。这两个参数的组合就实现了虚拟化网络。</p>
<p>更多示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">-net nic,model<span class="o">=</span>virtio <span class="se">\
</span></span></span><span class="line"><span class="cl">-net tap,ifname<span class="o">=</span>tap3,script<span class="o">=</span>/ect/qemu/qemu-ifup,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>第一个参数 <code>-net nic</code> 用于指定上述所说的前端驱动，也就是 Guest 中的实现，这里使用的是 <code>virtio</code> 驱动，这个驱动是 QEMU 中的一个虚拟网卡设备。第二个参数 <code>-net tap</code> 用于指定后端实现，也就是 Host 中的实现，这里使用的是 <code>tap</code> 驱动，它的网卡名称为 <code>tap3</code>，并且执行脚本 <code>/ect/qemu/qemu-ifup</code>。</p>
<blockquote>
<p>解释<code>/ect/qemu/qemu-ifup</code>
该脚本用于创建网桥，将网桥与宿主机的网卡绑定，然后将虚拟网卡绑定到网桥上，这样虚拟机就可以通过网桥与宿主机通信，宿主机也可以通过网桥与虚拟机通信。</p>
</blockquote>
<h3 id="-device---netdev-recommended">-device &amp; -netdev （Recommended）</h3>
<p>这是新版本的 QEMU 支持的命令格式，也是 QEMU 未来的发展趋势，我们以以下命令为例，来说明 <code>-device</code> 和 <code>-netdev</code> 的使用方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-riscv64 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -nographic -machine virt <span class="se">\
</span></span></span><span class="line"><span class="cl">  -smp <span class="s2">&#34;</span><span class="nv">$vcpu</span><span class="s2">&#34;</span> -m <span class="s2">&#34;</span><span class="nv">$memory</span><span class="s2">&#34;</span>G <span class="se">\
</span></span></span><span class="line"><span class="cl">  -bios <span class="s2">&#34;</span><span class="nv">$fw</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -drive <span class="nv">file</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$drive</span><span class="s2">&#34;</span>,format<span class="o">=</span>qcow2,id<span class="o">=</span>hd0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -object rng-random,filename<span class="o">=</span>/dev/urandom,id<span class="o">=</span>rng0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-vga <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-rng-device,rng<span class="o">=</span>rng0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-blk-device,drive<span class="o">=</span>hd0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet,ifname<span class="o">=</span>tap0,script<span class="o">=</span>~/qemu-script/qemu-ifup,downscript<span class="o">=</span>no <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet
</span></span></code></pre></div><p>其中这两个参数即实现了虚拟化网络：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet,ifname<span class="o">=</span>tap0,script<span class="o">=</span>~/qemu-script/qemu-ifup,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>第一个参数 <code>-device virtio-net-device</code> 用于指定上述所说的前端驱动，也就是 Guest 中的实现，定义了名为 <code>virtio-net-device</code> 的网络设备，并将其连接到一个名为 <code>tapnet</code> 的网络设备上，指定它的 MAC 地址为 <code>e0:be:03:88:54:e8</code>。</p>
<p>第二个参数 <code>-netdev tap</code> 用于指定后端实现，使用<code>tap</code>方式，并且指定唯一 ID 为<code>tapnet</code>由<code>-device</code>参数中的子参数<code>netdev</code>使用。网卡名称为<code>tap0</code>并且执行脚本 <code>~/qemu-script/qemu-ifup</code>。</p>
<blockquote>
<p>-netdev 参数中 id 的使用
-netdev 参数中的 id 用于指定唯一的 ID，这个 ID 会被 <code>-device</code> 参数中的子参数 <code>netdev</code> 使用，这样 <code>-device</code> 参数就知道要将前端驱动连接到哪个后端实现上了。id 可以自定义任意唯一字符串如<code>-netdev tap,id=test</code>对应<code>-device virtio-net-device,netdev=test</code></p>
</blockquote>
<p>更多示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -device virtio-net-pci,netdev<span class="o">=</span>tapnet,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet,script<span class="o">=</span>no,downscript<span class="o">=</span>no <span class="se">\
</span></span></span></code></pre></div><p>第一个参数 <code>-device virtio-net-pci</code> 定义了名为 <code>virtio-net-pci</code> 的网络设备，并将其连接到一个名为 <code>tapnet</code> 的网络设备上，指定它的 MAC 地址为 <code>e0:be:03:88:54:e8</code>。</p>
<p>第二个参数，仔细观察会发现，我们没有定义链接到后端网卡的名称<code>ifname</code>，这是因为以<code>tap</code>模式启动 QEMU 时会自动创建<code>tap</code>设备，具体网卡名称根据当前宿主机的网卡情况而定，默认会创建一个名为<code>tap0</code>的网卡，如果启动了两个虚拟机，那么第二个虚拟机的网卡名称就是<code>tap1</code>，以此类推。</p>
<h3 id="区分-tap-模式与-bridge-模式">区分 tap 模式与 bridge 模式</h3>
<p>我们有时候会用以下的命令进行 QEMU 虚拟机桥接网络的配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>bridgenet,mac<span class="o">=</span>52:54:00:12:34:57 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev bridge,ifname<span class="o">=</span>br0,id<span class="o">=</span>bridgenet
</span></span></code></pre></div><p>这也能为我们创建一个桥接网络，这是因为它和 <code>-netdev tap</code> 的工作方式是一样的，只是 <code>-netdev bridge</code> 的简化写法，<code>qemu-bridge-helper</code> 在背后替我们做了 <code>tap</code> 设备创建以及将 <code>tap</code> 设备加入桥接口的所有事情。</p>
<h3 id="添加多张网卡">添加多张网卡</h3>
<p>如果了解上述内容，添加多张网卡就十分容易实现了，我们只需要再添加一对 <code>-device</code> 和 <code>-netdev</code> 参数即可，如下所示：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-riscv64 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -nographic -machine virt <span class="se">\
</span></span></span><span class="line"><span class="cl">  -smp <span class="s2">&#34;</span><span class="nv">$vcpu</span><span class="s2">&#34;</span> -m <span class="s2">&#34;</span><span class="nv">$memory</span><span class="s2">&#34;</span>G <span class="se">\
</span></span></span><span class="line"><span class="cl">  -bios <span class="s2">&#34;</span><span class="nv">$fw</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -drive <span class="nv">file</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$drive</span><span class="s2">&#34;</span>,format<span class="o">=</span>qcow2,id<span class="o">=</span>hd0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -object rng-random,filename<span class="o">=</span>/dev/urandom,id<span class="o">=</span>rng0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-vga <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-rng-device,rng<span class="o">=</span>rng0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-blk-device,drive<span class="o">=</span>hd0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet0,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet0,script<span class="o">=</span>/etc/qemu/qemu-ifup,downscript<span class="o">=</span>/etc/qemu/qemu-ifdown <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>tapnet1,mac<span class="o">=</span>e0:be:03:88:54:e8 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -netdev tap,id<span class="o">=</span>tapnet1,script<span class="o">=</span>/etc/qemu/qemu-ifup,downscript<span class="o">=</span>/etc/qemu/qemu-ifdown <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device qemu-xhci -usb -device usb-kbd -device usb-tablet
</span></span></code></pre></div><p>需要注意的是，我们需要为每个 <code>-device</code> 参数指定一个唯一的 ID，这个 ID 会被 <code>-netdev</code> 参数中的子参数 <code>netdev</code> 使用，这样 <code>-device</code> 参数就知道要将前端驱动连接到哪个后端实现上了。并且每个 <code>tap</code> 设备只能被一个虚拟机使用，所以每个虚拟机的 <code>tap</code> 设备名称不能相同。</p>
<p>登录虚拟机查看网卡信息：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ifconfig </span>
</span></span><span class="line"><span class="cl">eth0: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.193.53  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::9e6:287b:30a2:574d  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether e0:be:03:88:54:e8  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">81</span>  bytes <span class="m">9871</span> <span class="o">(</span>9.6 KiB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">19</span>  bytes <span class="m">1735</span> <span class="o">(</span>1.6 KiB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">eth1: <span class="nv">flags</span><span class="o">=</span>4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu <span class="m">1500</span>
</span></span><span class="line"><span class="cl">        inet 10.12.193.101  netmask 255.255.240.0  broadcast 10.12.207.255
</span></span><span class="line"><span class="cl">        inet6 fe80::4fe0:9e1e:4681:52b7  prefixlen <span class="m">64</span>  scopeid 0x20&lt;link&gt;
</span></span><span class="line"><span class="cl">        ether 80:d4:09:62:cd:3c  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Ethernet<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">76</span>  bytes <span class="m">9471</span> <span class="o">(</span>9.2 KiB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">15</span>  bytes <span class="m">1708</span> <span class="o">(</span>1.6 KiB<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">lo: <span class="nv">flags</span><span class="o">=</span>73&lt;UP,LOOPBACK,RUNNING&gt;  mtu <span class="m">65536</span>
</span></span><span class="line"><span class="cl">        inet 127.0.0.1  netmask 255.0.0.0
</span></span><span class="line"><span class="cl">        inet6 ::1  prefixlen <span class="m">128</span>  scopeid 0x10&lt;host&gt;
</span></span><span class="line"><span class="cl">        loop  txqueuelen <span class="m">1000</span>  <span class="o">(</span>Local Loopback<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX packets <span class="m">0</span>  bytes <span class="m">0</span> <span class="o">(</span>0.0 B<span class="o">)</span>
</span></span><span class="line"><span class="cl">        RX errors <span class="m">0</span>  dropped <span class="m">0</span>  overruns <span class="m">0</span>  frame <span class="m">0</span>
</span></span><span class="line"><span class="cl">        TX packets <span class="m">0</span>  bytes <span class="m">0</span> <span class="o">(</span>0.0 B<span class="o">)</span>
</span></span><span class="line"><span class="cl">        TX errors <span class="m">0</span>  dropped <span class="m">0</span> overruns <span class="m">0</span>  carrier <span class="m">0</span>  collisions <span class="m">0</span>
</span></span></code></pre></div><h2 id="不同网络策略工作方式">不同网络策略工作方式</h2>
<ul>
<li>NAT 网络模式
<ul>
<li>NAT 网络以路由器的 NAT 功能为原理，允许虚拟机通过共享主机的 IP 地址访问互联网，但虚拟机之间不能直接通信。通过端口转发可以实现虚拟机之间的连接。</li>
</ul>
</li>
<li>桥接网络模式
<ul>
<li>桥接网络模式通过虚拟交换机连接虚拟机和主机，使得虚拟机可以通过局域网访问互联网，并允许虚拟机之间直接通信。</li>
</ul>
</li>
<li>内部网络模式
<ul>
<li>内部网络模式使得虚拟机可以创建一个完全隔离的网络，虚拟机之间可以直接通信，但无法访问互联网或外部网络。</li>
</ul>
</li>
<li>仅主机网络模式
<ul>
<li>仅主机网络模式允许虚拟机之间可以通信，并且与主机之间也可以通信，但无法访问互联网或外部网络。</li>
</ul>
</li>
</ul>
<table>
  <thead>
      <tr>
          <th></th>
          <th>VM &lt;&gt; VM</th>
          <th>VM → HOST</th>
          <th>HOST → VM</th>
          <th>VM → Internet</th>
          <th>Internet → VM</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>网络地址转换 NAT</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
      </tr>
      <tr>
          <td>NAT 网络</td>
          <td>√</td>
          <td>√</td>
          <td>×</td>
          <td>√</td>
          <td>×</td>
      </tr>
      <tr>
          <td>Bridged Adapter 桥接网卡</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
          <td>√</td>
      </tr>
  </tbody>
</table>
<h2 id="tuntap-网络设备">TUN/TAP 网络设备</h2>
<p>TAP 属于 Linux 内核支持的一种虚拟化网络设备，还有 TUN 也属于这种设备，它们完全由软件模拟实现，TUN/TAP 负责在内核协议栈和用户进程之间传送协议数据单元。TUN 工作在网络层，而 TAP 工作在数据链路层，TUN 负责与应用程序交换 IP 数据包，而 TAP 与应用程序交换以太网帧。所以 TUN 经常涉及路由，而 TAP 常用于网络桥接。</p>
<h1 id="ssh-远程登录虚拟机">SSH 远程登录虚拟机</h1>
<p>宿主机任意下目录执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ ssh-keygen -t rsa
</span></span><span class="line"><span class="cl">Generating public/private rsa key pair.
</span></span><span class="line"><span class="cl">Enter file in which to save the key <span class="o">(</span>/home/user/.ssh/id_rsa<span class="o">)</span>: host2vm0_id_irsa
</span></span><span class="line"><span class="cl">Enter passphrase <span class="o">(</span>empty <span class="k">for</span> no passphrase<span class="o">)</span>: 
</span></span><span class="line"><span class="cl">Enter same passphrase again: 
</span></span><span class="line"><span class="cl">Your identification has been saved in host2vm0_id_irsa.
</span></span><span class="line"><span class="cl">Your public key has been saved in host2vm0_id_irsa.pub.
</span></span><span class="line"><span class="cl">The key fingerprint is:
</span></span><span class="line"><span class="cl">SHA256:OkWcw+R3x6Z2mzeYQuG033H3N9qIeym3TZKzz6YD8tQ user@ubuntu18
</span></span><span class="line"><span class="cl">The key<span class="err">&#39;</span>s randomart image is:
</span></span><span class="line"><span class="cl">+---<span class="o">[</span>RSA 2048<span class="o">]</span>----+
</span></span><span class="line"><span class="cl"><span class="p">|</span>        .        <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       <span class="o">=</span> .   .   <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>        B .o. +  <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       . oo.o+   <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>        S  ++ ..o<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       o ..+.E<span class="o">=</span><span class="nv">o</span><span class="o">=</span><span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>      o   +..B+<span class="o">=</span>+<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>       .   <span class="nv">oo</span><span class="o">=</span>@o+<span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>           <span class="nv">o</span><span class="o">=</span>**<span class="o">=</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl">+----<span class="o">[</span>SHA256<span class="o">]</span>-----+
</span></span></code></pre></div><p>一直回车确定，生成公私钥，保存在<code>~/.ssh</code>目录下。</p>
<blockquote>
<p>我在宿主机上生成的公私钥名称为，分别是<code>host2vm0_id_rsa</code>,<code>host2vm0_id_rsa.pub</code>方便我记忆。如果一直回车，那么生成的公私钥名称为<code>id_rsa</code>，<code>id_rsa.pub</code>。</p>
</blockquote>
<p>将公钥复制到虚拟机 <code>VM0</code> 上，以当前虚拟机 <code>VM0</code> 的 IP：<code>10.12.193.53</code> 为例。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ ssh-copy-id 10.12.193.53
</span></span><span class="line"><span class="cl"><span class="c1"># 输入密码</span>
</span></span><span class="line"><span class="cl">/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key<span class="o">(</span>s<span class="o">)</span>, to filter out any that are already installed
</span></span><span class="line"><span class="cl">/usr/bin/ssh-copy-id: INFO: <span class="m">1</span> key<span class="o">(</span>s<span class="o">)</span> remain to be installed -- <span class="k">if</span> you are prompted now it is to install the new keys
</span></span><span class="line"><span class="cl">user@10.12.193.53<span class="s1">&#39;s password: 
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">Number of key(s) added: 1
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">Now try logging into the machine, with:   &#34;ssh &#39;</span>10.12.193.53<span class="err">&#39;</span><span class="s2">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">and check to make sure that only the key(s) you wanted were added.
</span></span></span></code></pre></div><p>然后就可以直接免密码登录了：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">ssh user@10.12.193.53
</span></span></code></pre></div><h1 id="fixed-problems-ongoing">Fixed Problems (Ongoing)</h1>
<h2 id="cannot-ioctl-tunsetiff-tap0-device-or-resource-busy-errno16">cannot ioctl tunsetiff tap0 device or resource busy (errno=16)</h2>
<h2 id="failed-to-initialize-tap-device-operation-not-permitted">failed to initialize tap device: Operation not permitted</h2>
<p>同类型错误：failed to create TAP device: Operation not permitted。因为创建虚拟设备 <code>tap</code> 需要 <code>root</code> 权限，所以需要使用 <code>sudo</code> 命令。执行 QEMU 启动是需要添加 <code>sudo</code>。</p>
<h2 id="qemu-虚拟机启动后网卡处于-down-状态无法获取-ip">QEMU 虚拟机启动后网卡处于 DOWN 状态，无法获取 IP</h2>
<p>查看是否是 MAC 地址配置错误，使用下面命令检查：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo ifconfig eth0 up
</span></span><span class="line"><span class="cl">SIOCSIFFLAGS: Cannot assign requested address
</span></span></code></pre></div><p>如果报错，参考下面章节<strong>SIOCSIFFLAGS: Cannot assign requested address</strong>解决方法进行解决。</p>
<h2 id="虚拟机可以-ping-通外网宿主机无法-ping-外网">虚拟机可以 ping 通外网，宿主机无法 ping 外网</h2>
<p>这种情况说明基本网络没有问题，只是 DNS 解析有问题，可以通过修改<code>/etc/resolv.conf</code>文件解决。</p>
<p>海宁 DNS 服务器地址：<code>10.12.2.21</code> 和 <code>10.12.2.22</code>，我的情况是只能 <code>ping 10.12.2.21</code>，可以选择自己能 <code>ping</code> 通的 DNS 服务器地址。如果无法 <code>ping</code> 通，说明问题不在这，需要自行解决。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 修改 DNS 服务器地址</span>
</span></span><span class="line"><span class="cl">sudo vim /etc/resolv.conf
</span></span><span class="line"><span class="cl"><span class="c1"># 添加以下内容</span>
</span></span><span class="line"><span class="cl">nameserver 10.12.2.21
</span></span><span class="line"><span class="cl">nameserver 10.12.2.22
</span></span></code></pre></div><h2 id="网络配置错误如何恢复配置之前的状态">网络配置错误，如何恢复配置之前的状态</h2>
<p>最简单的方式 - 重启，因为所有操作都是命令行配置，都是临时配置，可以直接重启解决。</p>
<p>既然有这一小节，说明肯定有时候不方便直接重启，那么就需要手动恢复配置之前的状态。但是能够恢复的<strong>前提是需要记得之前的网卡 IP 地址、子网掩码、网关、广播地址等信息</strong>。这些信息在局域网里，可能只有 IP 不同，其他信息如果没记住可以查看其他同事的网卡配置即可。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 将网桥绑定的网卡从网桥上移除</span>
</span></span><span class="line"><span class="cl">sudo brctl delif br0 enp2s0
</span></span><span class="line"><span class="cl">sudo brctl delif br0 tap0
</span></span><span class="line"><span class="cl"><span class="c1"># 配置宿主机网卡信息，必须一字不差，保持和之前一模一样才能恢复</span>
</span></span><span class="line"><span class="cl">sudo ip addr add 10.12.192.173/20 broadcast 10.12.207.255 dev enp2s0
</span></span><span class="line"><span class="cl"><span class="c1"># 必须设置网关</span>
</span></span><span class="line"><span class="cl">sudo ip route add default via 10.12.192.1 dev enp2s0
</span></span><span class="line"><span class="cl"><span class="c1"># 重启网络管理器</span>
</span></span><span class="line"><span class="cl">systemctl restart NetworkManager
</span></span></code></pre></div><h2 id="-netdev-tapidtapnetscriptqemu-scriptqemu-ifupnetwork-script-qemu-scriptqemu-ifup-failed-with-status-256">-netdev tap,id=tapnet,script=/qemu-script/qemu-ifup,:network script /qemu-script/qemu-ifup failed with status 256</h2>
<p>可能原因 1: <code>qemu-ifup</code> 脚本没有执行权限，需要添加执行权限。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod +x qemu-ifup
</span></span></code></pre></div><p>可能原因 2: <code>qemu-ifup</code> 路径不对，必须放到<code>/etc/qemu/</code>目录下。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p /etc/qemu
</span></span><span class="line"><span class="cl">mv qemu-ifup /etc/qemu <span class="o">&amp;&amp;</span> mv qemu-ifdown /etc/qemu 
</span></span><span class="line"><span class="cl">sudo chmod +x qemu-ifup
</span></span><span class="line"><span class="cl">sudo chmod +x qemu-ifdown
</span></span></code></pre></div><h2 id="siocsifflags-cannot-assign-requested-address">SIOCSIFFLAGS: Cannot assign requested address</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo ifconfig eth0 up
</span></span><span class="line"><span class="cl">SIOCSIFFLAGS: Cannot assign requested address
</span></span></code></pre></div><p>一般由于 MAC 地址配置错误导致，可以通过修改 MAC 地址为多播地址解决。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo ifconfig enp2s0 hw ether 00:11:22:33:44:55
</span></span></code></pre></div><p>重启网卡</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo ifconfig enp2s0 down
</span></span><span class="line"><span class="cl">sudo ifconfig enp2s0 up
</span></span></code></pre></div><p>MAC 地址的第一个字节中的最后一位（即第 7 位）用于标识该地址是单播，多播还是广播地址。如果这个位设置为 0，则表示这是一个单播地址；如果设置为 1，则表示这是一个多播或广播地址。</p>
<p>使用这种方法，我们可以确定上述每个 MAC 地址是否是单播地址：</p>
<ul>
<li><code>cd:c2:05:84:c8:2c</code> - 单播地址</li>
<li><code>13:7b:49:fc:a6:aa</code> - 单播地址</li>
<li><code>8f:aa:42:29:e8:68</code> - 单播地址</li>
</ul>
<p><code>00:11:22:33:44:55</code> 是多播地址。</p>
<h2 id="qemu--device-drive-with-0-bus0-unit0-exists">qemu -device drive with 0 bus=0 unit=0 exists</h2>
<p>这个错误通常意味着您尝试在 QEMU VM 中添加一个重复的设备。</p>
<p>如果您已经在 VM 中添加了驱动器，则可能会出现此问题。您可以检查是否存在两个具有相同 <code>bus</code> 和 <code>unit</code> 的设备（在此情况下，都是 0）。解决此问题的方法是删除重复设备或更改其配置以包括唯一的 <code>bus</code> 和 <code>unit</code>。</p>
<p>如果您没有意图添加重复的设备，在运行 QEMU 之前，您可能需要检查您的命令行，以确保正确设置了 <code>-drive</code> 选项。请注意，当使用 <code>-device</code> 添加设备时，您还应该避免使用 <code>-drive</code> 选项，因为它们可能引起冲突。</p>
<p>如果您需要进一步帮助，建议提供完整的 QEMU 命令和参数列表，以便更好地理解问题并提供更详细的建议。</p>
<h1 id="参考资料">参考资料</h1>
<ol>
<li><a href="https://tomwei7.com/2021/10/09/qemu-network-config/">QEMU 网络配置 // 围城</a></li>
<li><a href="https://www.junmajinlong.com/img/virtual/1594802457384.png">理解 Linux 虚拟网卡设备 tun/tap 的一切 | 骏马金龙</a></li>
<li><a href="https://wzt.ac.cn/2021/05/28/QEMU-networking/">QEMU 网络配置一把梭 | CataLpa&rsquo;s Site</a></li>
<li><a href="https://blog.csdn.net/u014022631/article/details/53411557">qemu 虚拟机与外部网络的通信 li_Jiejun 的博客-CSDN 博客</a></li>
<li><a href="https://zhou-yuxin.github.io/articles/2018/%E5%AE%89%E8%A3%85qemu-kvm%E4%BB%A5%E5%8F%8A%E9%85%8D%E7%BD%AE%E6%A1%A5%E6%8E%A5%E7%BD%91%E7%BB%9C/index.html">安装 qemu-kvm 以及配置桥接网络</a></li>
<li><a href="https://blog.csdn.net/rikeyone/article/details/106767540">QEMU 中的网络虚拟化配置_程序猿 Ricky 的日常干货的博客-CSDN 博客</a></li>
<li><a href="https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/QEMU/">Nginx Directory</a></li>
<li><a href="https://www.cnblogs.com/huqingyu/archive/2005/04/03/131102.html">QEMU 网络配置 - 浙林龙哥 - 博客园</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/432022126">【qemu】qemu 网络配置 - 知乎</a></li>
<li><a href="https://tomwei7.com/2021/10/09/qemu-network-config/">QEMU 网络配置 // 围城</a></li>
<li><a href="https://zhou-yuxin.github.io/articles/2018/%E5%AE%89%E8%A3%85qemu-kvm%E4%BB%A5%E5%8F%8A%E9%85%8D%E7%BD%AE%E6%A1%A5%E6%8E%A5%E7%BD%91%E7%BB%9C/index.html">安装 qemu-kvm 以及配置桥接网络</a></li>
<li><a href="https://blog.virt.ltd/blog/archives/37/">在 qemu 中使用桥接网络 - T^3 Blog</a></li>
<li><a href="http://wiki.yanick.site/wiki/os/qemu/">为 QEMU 配置网桥上网 | Yanick&rsquo;s Wiki</a></li>
<li><a href="https://www.junmajinlong.com/virtual/network/all_about_tun_tap/">理解 Linux 虚拟网卡设备 tun/tap 的一切 | 骏马金龙</a></li>
<li><a href="https://wzt.ac.cn/2021/05/28/QEMU-networking/">QEMU 网络配置一把梭 | CataLpa&rsquo;s Site</a></li>
</ol>
<h1 id="附录">附录</h1>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU启动RISC-V架构OpenEuler并配置OSC环境</title>
      <link>https://lifeislife.cn/posts/qemu%E5%90%AF%E5%8A%A8risc-v%E6%9E%B6%E6%9E%84openeuler%E5%B9%B6%E9%85%8D%E7%BD%AEosc%E7%8E%AF%E5%A2%83/</link>
      <pubDate>Sun, 23 Jul 2023 19:28:29 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E5%90%AF%E5%8A%A8risc-v%E6%9E%B6%E6%9E%84openeuler%E5%B9%B6%E9%85%8D%E7%BD%AEosc%E7%8E%AF%E5%A2%83/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;基于Ubuntu 18.04，QEMU 8.0.2，OpenEuler 22.09&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;安装qemu&#34;&gt;安装QEMU&lt;/h1&gt;
&lt;h2 id=&#34;安装基础编译工具&#34;&gt;安装基础编译工具&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install build-essential autoconf automake autotools-dev pkg-config bc curl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 gawk git bison flex texinfo gperf libtool patchutils mingw-w64 libmpc-dev &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 libmpfr-dev libgmp-dev libexpat-dev libfdt-dev zlib1g-dev libglib2.0-dev &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 libpixman-1-dev libncurses5-dev libncursesw5-dev meson libvirglrenderer-dev libsdl2-dev  -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo add-apt-repository ppa:deadsnakes/ppa
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install python3.8 python3-pip  -y
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install -f
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pip3 install meson
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;下载qemu&#34;&gt;下载QEMU&lt;/h2&gt;
&lt;p&gt;建立文件夹用于编译：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir -p qemu-build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;建立文件夹用于安装：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir -p /home/user/program/riscv64-qemu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可登录&lt;a href=&#34;https://www.qemu.org/download/&#34;&gt;官网&lt;/a&gt;将版本号换成最新版本即可：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget  &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://download.qemu.org/qemu-8.0.2.tar.xz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tar -xf qemu-8.0.2.tar.xz --strip-components&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;安装qemu-1&#34;&gt;安装QEMU&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./configure --target-list&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --enable-kvm --enable-sdl &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/user/program/riscv64-qemu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make install -j &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;配置环境变量&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;export QEMU_HOME=/home/user/program/riscv64-qemu&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;export PATH=$QEMU_HOME/bin:$PATH&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;下载-openeuler-risc-v-系统镜像&#34;&gt;下载 OpenEuler RISC-V 系统镜像&lt;/h1&gt;
&lt;p&gt;建立目录：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdir -p /home/user/openeuler
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;根据自己的用户名修改user&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下载&lt;a href=&#34;https://repo.openeuler.org/openEuler-preview/RISC-V/openEuler-22.09-riscv64/QEMU/&#34;&gt;OpenEuler 22.09版本&lt;/a&gt;，下载目录下所有文件/home/user/openeuler。如需下载其他版本请进入其他目录选择下载即可。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;也可以根据自己的情况进入&lt;a href=&#34;https://www.openeuler.org/zh/mirror/list/&#34;&gt;镜像站列表&lt;/a&gt;选择下载速度更快的镜像站下载&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;最新的23.03版本需要在&lt;a href=&#34;https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-23.03-V1-riscv64/&#34;&gt;中科院镜像站&lt;/a&gt;下载&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;文件说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fw_payload_oe_qemuvirt.elf&lt;/code&gt;: 利用 openSBI 将 kernel-5.10 的 image 作为 payload 所制作的 QEMU 启动所需文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openEuler-22.09-qemu-xfce.qcow2.tar.zst&lt;/code&gt;: openEuler RISC-V QEMU GUI 镜像压缩包&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preview_start_vm_xfce.sh&lt;/code&gt;: GUI 虚拟机启动脚本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openeuler-22.09-qemu.qcow2.tar.zst&lt;/code&gt;: openEuler RISC-V QEMU headless 镜像压缩包&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preview_start_vm.sh&lt;/code&gt;: headless 虚拟机启动脚本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解压：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; sudo apt-get install zstd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; tar -I &lt;span class=&#34;s1&#34;&gt;&amp;#39;zstdmt&amp;#39;&lt;/span&gt; -xvf openEuler-22.09-riscv64-qemu.qcow2.tar.zst
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行启动脚本&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod +x preview_start_vm.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bash preview_start_vm.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;登录系统&#34;&gt;登录系统&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;用户名: &lt;code&gt;root&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;默认密码: &lt;code&gt;openEuler12#$&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openEuler 22.09
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Kernel 5.10.0 on an riscv64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4penEuler-riscv6
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; login: openEuler 22.09
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Kernel 5.10.0 on an riscv64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openEuler-riscv64 login: root
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Password: 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Welcome to 5.10.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;System information as of time:   Mon Jul  &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; 07:52:19 PM CST &lt;span class=&#34;m&#34;&gt;2023&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;System load:   0.17
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Processes:   &lt;span class=&#34;m&#34;&gt;117&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Memory used:   .6%
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Swap used:   0.0%
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Usage On:   6%
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Users online:   &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# ls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-riscv64 ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# pwd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;远程登录系统&#34;&gt;远程登录系统&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ssh -p &lt;span class=&#34;m&#34;&gt;12055&lt;/span&gt; root@localhost
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;配置系统&#34;&gt;配置系统&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;以下操作均在root用户下执行，如果切换了用户会有提示。因为系统初始状态没有普通用户，也没有sudo，所以需要使用root完成一些基础配置。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;修改root密码&#34;&gt;修改root密码&lt;/h2&gt;
&lt;p&gt;原密码太复杂，修改简单密码&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;passwd root
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入两次密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;添加普通用户&#34;&gt;添加普通用户&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加用户 user&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;useradd -s /bin/bash -d /home/user -m user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;passwd user
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输入两次密码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 添加管理员权限&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;usermod -aG wheel user
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;修改时间&#34;&gt;修改时间&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NTP=ntp.aliyun.com&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/systemd/timesyncd.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl restart systemd-timesyncd.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看&lt;code&gt;timesyncd&lt;/code&gt;运行状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl status systemd-timesyncd.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;date&lt;/code&gt;命令可查看当前系统时间。验证是否配置成功。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;时间务必正确设置&lt;/strong&gt;，错误的时间会影响诸如https的TLS认证等过程。&lt;/p&gt;
&lt;h2 id=&#34;配置dns&#34;&gt;配置DNS&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vim /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nameserver 119.29.29.29
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;配置软件包源&#34;&gt;配置软件包源&lt;/h2&gt;
&lt;p&gt;配置文件为 /etc/yum.repos.d/openEuler.repo 下&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mv /etc/yum.repos.d/openEuler.repo  /etc/yum.repos.d/openEuler.repo.bk &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo bash -c &lt;span class=&#34;s2&#34;&gt;&amp;#34;cat &amp;lt;&amp;lt; EOF &amp;gt; /etc/yum.repos.d/openEuler.repo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;# just for test
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;[mainline]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;name=mainline
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/22.09/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;gpgcheck=0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;# just for test
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;[epol]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;name=epol
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/22.09/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;gpgcheck=0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;[extra]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;name=extra
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/extra/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;enabled=1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;gpgcheck=0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;EOF&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;需要注意的是，因为OpenEuler还在快速发展中，镜像地址可能会发生变化，所以需确认地址是否能够正常访问，如无法访问会导致404错误&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[repoid]中的repoid为软件仓库（repository）的ID号，所有.repo配置文件中的各repoid不能重复，必须唯一。示例中repoid设置为base。
name为软件仓库描述的字符串。
baseurl为软件仓库的地址。
enabled为是否启用该软件源仓库，可选值为1和0。默认值为1，表示启用该软件源仓库。
gpgcheck可设置为1或0，1表示进行gpg（GNU Private Guard）校验，0表示不进行gpg校验，gpgcheck可以确定rpm包的来源是有效和安全的。
gpgkey为验证签名用的公钥。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;磁盘扩容&#34;&gt;磁盘扩容&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在宿主机上安装 &lt;code&gt;qemu-img&lt;/code&gt; 工具:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt install qemu-utils
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;在 openEuler RISC-V 虚拟机上安装 &lt;code&gt;growpart&lt;/code&gt; 工具:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;dnf install cloud-utils-growpart
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;关闭QEMU虚拟机&lt;/li&gt;
&lt;li&gt;把 qcow2 文件的容量加200GB：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ qemu-img resize *.qcow2 +200G
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Image resized.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ qemu-img info *.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;image: openEuler-preview.riscv64.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;file format: qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;virtual size: &lt;span class=&#34;m&#34;&gt;220&lt;/span&gt; GiB 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;disk size: 9.58 GiB
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cluster_size: &lt;span class=&#34;m&#34;&gt;65536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Format specific information:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    compat: 1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    compression type: zlib
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    lazy refcounts: &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    refcount bits: &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    corrupt: &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    extended l2: &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;QEMU 启动 openEuler RISC-V。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;启动以后，我们先看看分区情况：可以看到根目录对应的分区只使用了 10G。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-RISCV-rare ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# lsblk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vda    254:0    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  220G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─vda1 254:1    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;   10G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part /
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;扩展分区 &lt;code&gt;vda1&lt;/code&gt;，执行&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;growpart /dev/vda1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行 &lt;code&gt;lsblk&lt;/code&gt; 可以看到 / 所在的 &lt;code&gt;vda1&lt;/code&gt; 分区已经扩展到了预期大小&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-RISCV-rare ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# growpart /dev/vda 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;CHANGED: &lt;span class=&#34;nv&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2048&lt;/span&gt; old: &lt;span class=&#34;nv&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20969472&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;20971520&lt;/span&gt; new: &lt;span class=&#34;nv&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;419428319&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;419430367&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;root@openEuler-RISCV-rare ~&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;# lsblk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vda    254:0    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  220G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└─vda1 254:1    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;  220G  &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; part /
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;扩展文件系统：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;resize2fs /dev/vda1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;bug&#34;&gt;BUG&lt;/h1&gt;
&lt;h2 id=&#34;network-backend-user-is-not-compiled-into-this-binary&#34;&gt;network backend ‘user‘ is not compiled into this binary&lt;/h2&gt;
&lt;p&gt;git clone &lt;a href=&#34;https://gitlab.freedesktop.org/slirp/libslirp.git&#34;&gt;https://gitlab.freedesktop.org/slirp/libslirp.git&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://security.ubuntu.com/ubuntu/pool/main/libs/libslirp/libslirp-dev_4.1.0-2ubuntu2.2_amd64.deb&#34;&gt;http://security.ubuntu.com/ubuntu/pool/main/libs/libslirp/libslirp-dev_4.1.0-2ubuntu2.2_amd64.deb&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install libslirp-dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重新编译QEMU：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; rm -rf build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; qemu-build &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./configure --target-list&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --enable-kvm --enable-sdl --enable-slirp&lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               --prefix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/home/user/program/riscv64-qemu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-Bash&#34; data-lang=&#34;Bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make install -j &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;nproc&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;参考文档&#34;&gt;参考文档&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/openeuler-mirror/RISC-V/blob/master/doc/tutorials/vm-qemu-oErv.md&#34;&gt;RISC-V/doc/tutorials/vm-qemu-oErv.md at master · openeuler-mirror/RISC-V · GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.openeuler.org/whitepaper/openEuler-whitepaper-2209.pdf&#34;&gt;openEuler 22.09技术白皮书&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<blockquote>
<p>基于Ubuntu 18.04，QEMU 8.0.2，OpenEuler 22.09</p>
</blockquote>
<h1 id="安装qemu">安装QEMU</h1>
<h2 id="安装基础编译工具">安装基础编译工具</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt install build-essential autoconf automake autotools-dev pkg-config bc curl <span class="se">\
</span></span></span><span class="line"><span class="cl">                 gawk git bison flex texinfo gperf libtool patchutils mingw-w64 libmpc-dev <span class="se">\
</span></span></span><span class="line"><span class="cl">                 libmpfr-dev libgmp-dev libexpat-dev libfdt-dev zlib1g-dev libglib2.0-dev <span class="se">\
</span></span></span><span class="line"><span class="cl">                 libpixman-1-dev libncurses5-dev libncursesw5-dev meson libvirglrenderer-dev libsdl2-dev  -y
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo add-apt-repository ppa:deadsnakes/ppa
</span></span><span class="line"><span class="cl">sudo apt install python3.8 python3-pip  -y
</span></span><span class="line"><span class="cl">sudo apt install -f
</span></span><span class="line"><span class="cl">pip3 install meson
</span></span></code></pre></div><h2 id="下载qemu">下载QEMU</h2>
<p>建立文件夹用于编译：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">&amp;&amp;</span> mkdir -p qemu-build
</span></span></code></pre></div><p>建立文件夹用于安装：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">&amp;&amp;</span> mkdir -p /home/user/program/riscv64-qemu
</span></span></code></pre></div><p>可登录<a href="https://www.qemu.org/download/">官网</a>将版本号换成最新版本即可：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> wget  <span class="s2">&#34;https://download.qemu.org/qemu-8.0.2.tar.xz&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">tar -xf qemu-8.0.2.tar.xz --strip-components<span class="o">=</span><span class="m">1</span> 
</span></span></code></pre></div><h2 id="安装qemu-1">安装QEMU</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> ./configure --target-list<span class="o">=</span>riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu <span class="se">\
</span></span></span><span class="line"><span class="cl">               --enable-kvm --enable-sdl <span class="se">\
</span></span></span><span class="line"><span class="cl">               --prefix<span class="o">=</span>/home/user/program/riscv64-qemu
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">make install -j <span class="k">$(</span>nproc<span class="k">)</span>
</span></span></code></pre></div><p>配置环境变量</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;export QEMU_HOME=/home/user/program/riscv64-qemu&#39;</span> &gt;&gt; ~/.bashrc <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s1">&#39;export PATH=$QEMU_HOME/bin:$PATH&#39;</span> &gt;&gt; ~/.bashrc
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">source</span> ~/.bashrc
</span></span></code></pre></div><h1 id="下载-openeuler-risc-v-系统镜像">下载 OpenEuler RISC-V 系统镜像</h1>
<p>建立目录：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> <span class="o">&amp;&amp;</span> mkdir -p /home/user/openeuler
</span></span></code></pre></div><blockquote>
<p>根据自己的用户名修改user</p>
</blockquote>
<p>下载<a href="https://repo.openeuler.org/openEuler-preview/RISC-V/openEuler-22.09-riscv64/QEMU/">OpenEuler 22.09版本</a>，下载目录下所有文件/home/user/openeuler。如需下载其他版本请进入其他目录选择下载即可。</p>
<blockquote>
<p>也可以根据自己的情况进入<a href="https://www.openeuler.org/zh/mirror/list/">镜像站列表</a>选择下载速度更快的镜像站下载</p>
</blockquote>
<blockquote>
<p>最新的23.03版本需要在<a href="https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-23.03-V1-riscv64/">中科院镜像站</a>下载</p>
</blockquote>
<p>文件说明：</p>
<ul>
<li><code>fw_payload_oe_qemuvirt.elf</code>: 利用 openSBI 将 kernel-5.10 的 image 作为 payload 所制作的 QEMU 启动所需文件</li>
<li><code>openEuler-22.09-qemu-xfce.qcow2.tar.zst</code>: openEuler RISC-V QEMU GUI 镜像压缩包</li>
<li><code>preview_start_vm_xfce.sh</code>: GUI 虚拟机启动脚本</li>
<li><code>openeuler-22.09-qemu.qcow2.tar.zst</code>: openEuler RISC-V QEMU headless 镜像压缩包</li>
<li><code>preview_start_vm.sh</code>: headless 虚拟机启动脚本</li>
</ul>
<p>解压：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"> sudo apt-get install zstd
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"> tar -I <span class="s1">&#39;zstdmt&#39;</span> -xvf openEuler-22.09-riscv64-qemu.qcow2.tar.zst
</span></span></code></pre></div><p>执行启动脚本</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">chmod +x preview_start_vm.sh
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">bash preview_start_vm.sh
</span></span></code></pre></div><h1 id="登录系统">登录系统</h1>
<ul>
<li>用户名: <code>root</code></li>
<li>默认密码: <code>openEuler12#$</code></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">openEuler 22.09
</span></span><span class="line"><span class="cl">Kernel 5.10.0 on an riscv64
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">4penEuler-riscv6
</span></span><span class="line"><span class="cl"> login: openEuler 22.09
</span></span><span class="line"><span class="cl">Kernel 5.10.0 on an riscv64
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">openEuler-riscv64 login: root
</span></span><span class="line"><span class="cl">Password: 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Welcome to 5.10.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">System information as of time:   Mon Jul  <span class="m">3</span> 07:52:19 PM CST <span class="m">2023</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">System load:   0.17
</span></span><span class="line"><span class="cl">Processes:   <span class="m">117</span>
</span></span><span class="line"><span class="cl">Memory used:   .6%
</span></span><span class="line"><span class="cl">Swap used:   0.0%
</span></span><span class="line"><span class="cl">Usage On:   6%
</span></span><span class="line"><span class="cl">Users online:   <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># ls</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-riscv64 ~<span class="o">]</span><span class="c1"># pwd</span>
</span></span></code></pre></div><h3 id="远程登录系统">远程登录系统</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">ssh -p <span class="m">12055</span> root@localhost
</span></span></code></pre></div><h1 id="配置系统">配置系统</h1>
<blockquote>
<p>以下操作均在root用户下执行，如果切换了用户会有提示。因为系统初始状态没有普通用户，也没有sudo，所以需要使用root完成一些基础配置。</p>
</blockquote>
<h2 id="修改root密码">修改root密码</h2>
<p>原密码太复杂，修改简单密码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">passwd root
</span></span><span class="line"><span class="cl"><span class="c1"># 输入两次密码</span>
</span></span></code></pre></div><h2 id="添加普通用户">添加普通用户</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># 添加用户 user</span>
</span></span><span class="line"><span class="cl">useradd -s /bin/bash -d /home/user -m user
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">passwd user
</span></span><span class="line"><span class="cl"><span class="c1"># 输入两次密码</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="c1"># 添加管理员权限</span>
</span></span><span class="line"><span class="cl">usermod -aG wheel user
</span></span></code></pre></div><h2 id="修改时间">修改时间</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;NTP=ntp.aliyun.com&#34;</span> &gt;&gt; /etc/systemd/timesyncd.conf
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">systemctl restart systemd-timesyncd.service
</span></span></code></pre></div><p>查看<code>timesyncd</code>运行状态：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">systemctl status systemd-timesyncd.service
</span></span></code></pre></div><p><code>date</code>命令可查看当前系统时间。验证是否配置成功。</p>
<p><strong>时间务必正确设置</strong>，错误的时间会影响诸如https的TLS认证等过程。</p>
<h2 id="配置dns">配置DNS</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">vim /etc/resolv.conf
</span></span><span class="line"><span class="cl">nameserver 119.29.29.29
</span></span></code></pre></div><h2 id="配置软件包源">配置软件包源</h2>
<p>配置文件为 /etc/yum.repos.d/openEuler.repo 下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">mv /etc/yum.repos.d/openEuler.repo  /etc/yum.repos.d/openEuler.repo.bk <span class="o">&amp;&amp;</span> sudo bash -c <span class="s2">&#34;cat &lt;&lt; EOF &gt; /etc/yum.repos.d/openEuler.repo
</span></span></span><span class="line"><span class="cl"><span class="s2"># just for test
</span></span></span><span class="line"><span class="cl"><span class="s2">[mainline]
</span></span></span><span class="line"><span class="cl"><span class="s2">name=mainline
</span></span></span><span class="line"><span class="cl"><span class="s2">baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/22.09/
</span></span></span><span class="line"><span class="cl"><span class="s2">enabled=1
</span></span></span><span class="line"><span class="cl"><span class="s2">gpgcheck=0
</span></span></span><span class="line"><span class="cl"><span class="s2"># just for test
</span></span></span><span class="line"><span class="cl"><span class="s2">[epol]
</span></span></span><span class="line"><span class="cl"><span class="s2">name=epol
</span></span></span><span class="line"><span class="cl"><span class="s2">baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/22.09/
</span></span></span><span class="line"><span class="cl"><span class="s2">enabled=1
</span></span></span><span class="line"><span class="cl"><span class="s2">gpgcheck=0
</span></span></span><span class="line"><span class="cl"><span class="s2">[extra]
</span></span></span><span class="line"><span class="cl"><span class="s2">name=extra
</span></span></span><span class="line"><span class="cl"><span class="s2">baseurl=https://mirror.iscas.ac.cn/openeuler-sig-riscv/openEuler-RISC-V/preview/openEuler-22.09-V1-riscv64/repo/extra/
</span></span></span><span class="line"><span class="cl"><span class="s2">enabled=1
</span></span></span><span class="line"><span class="cl"><span class="s2">gpgcheck=0
</span></span></span><span class="line"><span class="cl"><span class="s2">EOF&#34;</span>
</span></span></code></pre></div><blockquote>
<p>需要注意的是，因为OpenEuler还在快速发展中，镜像地址可能会发生变化，所以需确认地址是否能够正常访问，如无法访问会导致404错误</p>
</blockquote>
<blockquote>
<p>[repoid]中的repoid为软件仓库（repository）的ID号，所有.repo配置文件中的各repoid不能重复，必须唯一。示例中repoid设置为base。
name为软件仓库描述的字符串。
baseurl为软件仓库的地址。
enabled为是否启用该软件源仓库，可选值为1和0。默认值为1，表示启用该软件源仓库。
gpgcheck可设置为1或0，1表示进行gpg（GNU Private Guard）校验，0表示不进行gpg校验，gpgcheck可以确定rpm包的来源是有效和安全的。
gpgkey为验证签名用的公钥。</p>
</blockquote>
<h2 id="磁盘扩容">磁盘扩容</h2>
<ol>
<li>在宿主机上安装 <code>qemu-img</code> 工具:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">apt install qemu-utils
</span></span></code></pre></div><ol start="2">
<li>在 openEuler RISC-V 虚拟机上安装 <code>growpart</code> 工具:</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">dnf install cloud-utils-growpart
</span></span></code></pre></div><ol start="3">
<li>关闭QEMU虚拟机</li>
<li>把 qcow2 文件的容量加200GB：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">$ qemu-img resize *.qcow2 +200G
</span></span><span class="line"><span class="cl">Image resized.
</span></span><span class="line"><span class="cl">$ qemu-img info *.qcow2
</span></span><span class="line"><span class="cl">image: openEuler-preview.riscv64.qcow2
</span></span><span class="line"><span class="cl">file format: qcow2
</span></span><span class="line"><span class="cl">virtual size: <span class="m">220</span> GiB 
</span></span><span class="line"><span class="cl">disk size: 9.58 GiB
</span></span><span class="line"><span class="cl">cluster_size: <span class="m">65536</span>
</span></span><span class="line"><span class="cl">Format specific information:
</span></span><span class="line"><span class="cl">    compat: 1.1
</span></span><span class="line"><span class="cl">    compression type: zlib
</span></span><span class="line"><span class="cl">    lazy refcounts: <span class="nb">false</span>
</span></span><span class="line"><span class="cl">    refcount bits: <span class="m">16</span>
</span></span><span class="line"><span class="cl">    corrupt: <span class="nb">false</span>
</span></span><span class="line"><span class="cl">    extended l2: <span class="nb">false</span>
</span></span></code></pre></div><ol start="5">
<li>QEMU 启动 openEuler RISC-V。</li>
</ol>
<p>启动以后，我们先看看分区情况：可以看到根目录对应的分区只使用了 10G。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-RISCV-rare ~<span class="o">]</span><span class="c1"># lsblk</span>
</span></span><span class="line"><span class="cl">NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
</span></span><span class="line"><span class="cl">vda    254:0    <span class="m">0</span>  220G  <span class="m">0</span> disk
</span></span><span class="line"><span class="cl">└─vda1 254:1    <span class="m">0</span>   10G  <span class="m">0</span> part /
</span></span></code></pre></div><ol>
<li>扩展分区 <code>vda1</code>，执行</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">growpart /dev/vda1
</span></span></code></pre></div><p>执行 <code>lsblk</code> 可以看到 / 所在的 <code>vda1</code> 分区已经扩展到了预期大小</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-RISCV-rare ~<span class="o">]</span><span class="c1"># growpart /dev/vda 1</span>
</span></span><span class="line"><span class="cl">CHANGED: <span class="nv">partition</span><span class="o">=</span><span class="m">1</span> <span class="nv">start</span><span class="o">=</span><span class="m">2048</span> old: <span class="nv">size</span><span class="o">=</span><span class="m">20969472</span> <span class="nv">end</span><span class="o">=</span><span class="m">20971520</span> new: <span class="nv">size</span><span class="o">=</span><span class="m">419428319</span> <span class="nv">end</span><span class="o">=</span><span class="m">419430367</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>root@openEuler-RISCV-rare ~<span class="o">]</span><span class="c1"># lsblk</span>
</span></span><span class="line"><span class="cl">NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
</span></span><span class="line"><span class="cl">vda    254:0    <span class="m">0</span>  220G  <span class="m">0</span> disk
</span></span><span class="line"><span class="cl">└─vda1 254:1    <span class="m">0</span>  220G  <span class="m">0</span> part /
</span></span></code></pre></div><ol>
<li>扩展文件系统：</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">resize2fs /dev/vda1
</span></span></code></pre></div><h1 id="bug">BUG</h1>
<h2 id="network-backend-user-is-not-compiled-into-this-binary">network backend ‘user‘ is not compiled into this binary</h2>
<p>git clone <a href="https://gitlab.freedesktop.org/slirp/libslirp.git">https://gitlab.freedesktop.org/slirp/libslirp.git</a></p>
<p><a href="http://security.ubuntu.com/ubuntu/pool/main/libs/libslirp/libslirp-dev_4.1.0-2ubuntu2.2_amd64.deb">http://security.ubuntu.com/ubuntu/pool/main/libs/libslirp/libslirp-dev_4.1.0-2ubuntu2.2_amd64.deb</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">sudo apt-get install libslirp-dev
</span></span></code></pre></div><p>重新编译QEMU：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> rm -rf build
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl"><span class="nb">cd</span> qemu-build <span class="o">&amp;&amp;</span> ./configure --target-list<span class="o">=</span>riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu <span class="se">\
</span></span></span><span class="line"><span class="cl">               --enable-kvm --enable-sdl --enable-slirp<span class="se">\
</span></span></span><span class="line"><span class="cl">               --prefix<span class="o">=</span>/home/user/program/riscv64-qemu
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Bash" data-lang="Bash"><span class="line"><span class="cl">make install -j <span class="k">$(</span>nproc<span class="k">)</span>
</span></span></code></pre></div><h1 id="参考文档">参考文档</h1>
<p><a href="https://github.com/openeuler-mirror/RISC-V/blob/master/doc/tutorials/vm-qemu-oErv.md">RISC-V/doc/tutorials/vm-qemu-oErv.md at master · openeuler-mirror/RISC-V · GitHub</a></p>
<p><a href="https://www.openeuler.org/whitepaper/openEuler-whitepaper-2209.pdf">openEuler 22.09技术白皮书</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU&#39;s instance_init() vs. realize()</title>
      <link>https://lifeislife.cn/posts/qemu-s-instance-init-vs-realize/</link>
      <pubDate>Tue, 01 Nov 2022 09:51:28 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu-s-instance-init-vs-realize/</guid>
      <description>&lt;p&gt;转载自&lt;a href=&#34;https://github.com/huth&#34;&gt;huth (Thomas Huth)&lt;/a&gt;的一篇文章，原文已经 404，从网页快照中找回的文章。&lt;/p&gt;
&lt;p&gt;Note that this is a blog post for (new) QEMU developers. If you are just interested in using QEMU, you can certainly skip this text. Otherwise, in case you have ever been in touch with the QEMU device model (&amp;ldquo;qdev&amp;rdquo;), you are likely aware of the basic qdev code boilerplate already:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mydev_realize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DeviceState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Error&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mydev_instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Property&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_properties&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;DEFINE_PROP_xxx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;myprop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MyDevState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;field&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;DEFINE_PROP_END_OF_LIST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mydev_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;oc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DeviceClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;DEVICE_CLASS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;oc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;realize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_realize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;desc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;My cool device&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;props&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_properties&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;          &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_MYDEV&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mydev_state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mydev_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mydev_register_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;type_register_static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mydev_info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mydev_register_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are three different initialization functions involved here, the &lt;strong&gt;class_init&lt;/strong&gt;, the &lt;strong&gt;instance_init&lt;/strong&gt; and the &lt;strong&gt;realize&lt;/strong&gt; function. While it is quite obvious to distinguish the &lt;em&gt;class_init&lt;/em&gt; function from the two others (it is used for initializing the class data, not the data that is used for an instance … this is similar to the object model with classes and instances in C++), I initially always wondered about the difference between the &lt;em&gt;instance_init()&lt;/em&gt; and the &lt;em&gt;realize()&lt;/em&gt; functions. Having fixed quite a lot of related bugs in the past months in the QEMU code base, I now know that a lot of other people are also not properly aware of the difference here, so I think it is now time to write down some information that I&amp;rsquo;m now aware of, to make sure that I don&amp;rsquo;t forget about this again, and maybe help others to avoid related bugs in the future ;-)&lt;/p&gt;
&lt;p&gt;First it is of course always a good idea to have a look at the documentation. While the &lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l427&#34;&gt;documentation of &lt;/a&gt;&lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l427&#34;&gt;&lt;em&gt;TypeInfo&lt;/em&gt;&lt;/a&gt; (where &lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l434&#34;&gt;&lt;em&gt;instance_init()&lt;/em&gt;&lt;/a&gt; is defined) is not very helpful to understand the differences, the &lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l40&#34;&gt;documentation of &lt;/a&gt;&lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l40&#34;&gt;&lt;em&gt;DeviceClass&lt;/em&gt;&lt;/a&gt; (where &lt;a href=&#34;https://translate.google.com/website?sl=en&amp;amp;tl=zh-CN&amp;amp;hl=zh-CN&amp;amp;prev=search&amp;amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l43&#34;&gt;&lt;em&gt;realize()&lt;/em&gt;&lt;/a&gt; is defined) has some more useful information: You can learn here that the object instantiation is done first, before the device is realized, i.e. the &lt;em&gt;instance_init()&lt;/em&gt; function is called first, and the &lt;em&gt;realize()&lt;/em&gt; function is called afterwards. The former must not fail, while the latter can return an error to its caller via a pointer to an &lt;em&gt;“Error”&lt;/em&gt; object pointer.&lt;/p&gt;
&lt;p&gt;So the basic idea here is that device objects are first instantiated, then these objects can be inspected for their interfaces and their creators can set up their properties to configure their settings and wire them up with other devices, before the device finally becomes &amp;ldquo;active&amp;rdquo; by being &lt;em&gt;realized&lt;/em&gt;. It is important here to notice that &lt;strong&gt;devices can be instantiated (and also finalized) &lt;em&gt;&lt;strong&gt;&lt;strong&gt;without&lt;/strong&gt;&lt;/strong&gt;&lt;/em&gt; being realized&lt;/strong&gt;! This happens for example if the device is introspected: If you enter for example &lt;code&gt;device_add xyz,help&lt;/code&gt; at the HMP monitor, or if you send the &lt;code&gt;device-list-properties&lt;/code&gt; QOM command to QEMU to retrieve the device&amp;rsquo;s properties, QEMU creates a temporary instance of the device to query the properties of the object, without realizing it. The object gets destroyed (&amp;ldquo;finalized&amp;rdquo;) immediately afterwards.&lt;/p&gt;
&lt;p&gt;Knowing this, you can avoid a set of bugs which could be found with a couple of devices in the past:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you want your device to provide properties for other parts of the QEMU code or for the users, and you want to add those properties via one of the many object_property_add*() functions of QEMU (instead of using the &lt;em&gt;“props”&lt;/em&gt; field of the &lt;em&gt;DeviceClass&lt;/em&gt;), then you should do this in the &lt;em&gt;instance_init()&lt;/em&gt; and not in the &lt;em&gt;realize()&lt;/em&gt; function. Otherwise the properties won&amp;rsquo;t show up when the user runs &lt;code&gt;--device xyz,help&lt;/code&gt; or the &lt;code&gt;device-list-properties&lt;/code&gt; QOM command to get some information about your device.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;instance_init()&lt;/em&gt; functions must really never fail, i.e. also not call &lt;em&gt;abort()&lt;/em&gt; or &lt;em&gt;exit()&lt;/em&gt;. Otherwise QEMU can terminate unexpectedly when a user simply wanted to have a look at the list of device properties with &lt;code&gt;device_add xyz,help&lt;/code&gt; or the &lt;code&gt;device-list-properties&lt;/code&gt; QOM command. If your device cannot work in certain circumstances, check for the error condition in the &lt;em&gt;realize()&lt;/em&gt; function instead and return with an appropriate error there.&lt;/li&gt;
&lt;li&gt;Never assume that your device is always instantiated only with the machine that it was designed for. It&amp;rsquo;s of course a good idea to set the &lt;em&gt;“user_creatable = false”&lt;/em&gt; flag in the &lt;em&gt;DeviceClass&lt;/em&gt; of your device if your device cannot be plugged in arbitrary machines. But device introspection can still happen at any time, with any machine. So if you wrote a device called &amp;ldquo;mydev-a&amp;rdquo; that only works with &lt;code&gt;--machine A&lt;/code&gt;, the user still can start QEMU with the option &lt;code&gt;--machine B&lt;/code&gt; instead and then run &lt;code&gt;device_add mydev-a,help&lt;/code&gt; or the &lt;code&gt;device-list-properties&lt;/code&gt; QOM command. The &lt;em&gt;instance_init()&lt;/em&gt; function of your device will be called to create a temporary instance of your device, even though the base machine is B and not A here. So you especially should take care to not depend on the availability of certain buses or other devices in the &lt;em&gt;instance_init()&lt;/em&gt; function, nor use things like &lt;em&gt;serial_hd()&lt;/em&gt; or &lt;em&gt;nd_table[]&lt;/em&gt; in your &lt;em&gt;instance_init()&lt;/em&gt; function, since these might (and should) have been used by the machine init function already. If your device needs to be wired up, provide properties as interfaces to the outside and let the creator of your device (e.g. the machine init code) wire your device between the device instantiation and the realize phase instead.&lt;/li&gt;
&lt;li&gt;Make sure that your device leaves a clean state after a temporary instance is destroyed again, i.e. don&amp;rsquo;t assume that there will be only one instance of your device which is created at the beginning right after QEMU has been started and is destroyed at the very end before QEMU terminates. Thus do not assume that the things that you do in your &lt;em&gt;instance_init()&lt;/em&gt; don&amp;rsquo;t need explicit clean-up since the device instance will only be destroyed when QEMU terminates. Device instances can be created and destroyed at any time, so when the device is finalized, you must not leave any dangling pointers or references to your device behind you, e.g. in the QOM tree. When you create other objects in your &lt;em&gt;instance_init()&lt;/em&gt; function, make sure to set proper parents of these objects or use an &lt;em&gt;instance_finalize()&lt;/em&gt; function, so that the created objects get cleaned up correctly again when your device is destroyed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All in all, if you write code for a new QEMU device, it is likely a good idea to use the &lt;em&gt;instance_init()&lt;/em&gt; function only for e.g. creating properties and other things that are required before device realization, and then do the main work in the &lt;em&gt;realize()&lt;/em&gt; function instead.&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>转载自<a href="https://github.com/huth">huth (Thomas Huth)</a>的一篇文章，原文已经 404，从网页快照中找回的文章。</p>
<p>Note that this is a blog post for (new) QEMU developers. If you are just interested in using QEMU, you can certainly skip this text. Otherwise, in case you have ever been in touch with the QEMU device model (&ldquo;qdev&rdquo;), you are likely aware of the basic qdev code boilerplate already:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">mydev_realize</span><span class="p">(</span><span class="n">DeviceState</span> <span class="o">*</span><span class="n">dev</span><span class="p">,</span> <span class="n">Error</span> <span class="o">**</span><span class="n">errp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">mydev_instance_init</span><span class="p">(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">Property</span> <span class="n">mydev_properties</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">DEFINE_PROP_xxx</span><span class="p">(</span><span class="s">&#34;myprop&#34;</span><span class="p">,</span> <span class="n">MyDevState</span><span class="p">,</span> <span class="n">field</span><span class="p">,</span> <span class="p">...),</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">DEFINE_PROP_END_OF_LIST</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">mydev_class_init</span><span class="p">(</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="n">oc</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DeviceClass</span> <span class="o">*</span><span class="n">dc</span> <span class="o">=</span> <span class="nf">DEVICE_CLASS</span><span class="p">(</span><span class="n">oc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">realize</span> <span class="o">=</span> <span class="n">mydev_realize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">desc</span> <span class="o">=</span> <span class="s">&#34;My cool device&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">props</span> <span class="o">=</span> <span class="n">mydev_properties</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">mydev_info</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span>          <span class="o">=</span> <span class="n">TYPE_MYDEV</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span>        <span class="o">=</span> <span class="n">TYPE_SYS_BUS_DEVICE</span><span class="p">,</span>  
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">mydev_state</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_init</span> <span class="o">=</span> <span class="n">mydev_instance_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">class_init</span>    <span class="o">=</span> <span class="n">mydev_class_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">mydev_register_types</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">type_register_static</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mydev_info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">type_init</span><span class="p">(</span><span class="n">mydev_register_types</span><span class="p">)</span>
</span></span></code></pre></div><p>There are three different initialization functions involved here, the <strong>class_init</strong>, the <strong>instance_init</strong> and the <strong>realize</strong> function. While it is quite obvious to distinguish the <em>class_init</em> function from the two others (it is used for initializing the class data, not the data that is used for an instance … this is similar to the object model with classes and instances in C++), I initially always wondered about the difference between the <em>instance_init()</em> and the <em>realize()</em> functions. Having fixed quite a lot of related bugs in the past months in the QEMU code base, I now know that a lot of other people are also not properly aware of the difference here, so I think it is now time to write down some information that I&rsquo;m now aware of, to make sure that I don&rsquo;t forget about this again, and maybe help others to avoid related bugs in the future ;-)</p>
<p>First it is of course always a good idea to have a look at the documentation. While the <a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l427">documentation of </a><a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l427"><em>TypeInfo</em></a> (where <a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/qom/object.h;hb%3Dv3.0.0%23l434"><em>instance_init()</em></a> is defined) is not very helpful to understand the differences, the <a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l40">documentation of </a><a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l40"><em>DeviceClass</em></a> (where <a href="https://translate.google.com/website?sl=en&amp;tl=zh-CN&amp;hl=zh-CN&amp;prev=search&amp;u=https://git.qemu.org/?p%3Dqemu.git;a%3Dblob;f%3Dinclude/hw/qdev-core.h;hb%3Dv3.0.0%23l43"><em>realize()</em></a> is defined) has some more useful information: You can learn here that the object instantiation is done first, before the device is realized, i.e. the <em>instance_init()</em> function is called first, and the <em>realize()</em> function is called afterwards. The former must not fail, while the latter can return an error to its caller via a pointer to an <em>“Error”</em> object pointer.</p>
<p>So the basic idea here is that device objects are first instantiated, then these objects can be inspected for their interfaces and their creators can set up their properties to configure their settings and wire them up with other devices, before the device finally becomes &ldquo;active&rdquo; by being <em>realized</em>. It is important here to notice that <strong>devices can be instantiated (and also finalized) <em><strong><strong>without</strong></strong></em> being realized</strong>! This happens for example if the device is introspected: If you enter for example <code>device_add xyz,help</code> at the HMP monitor, or if you send the <code>device-list-properties</code> QOM command to QEMU to retrieve the device&rsquo;s properties, QEMU creates a temporary instance of the device to query the properties of the object, without realizing it. The object gets destroyed (&ldquo;finalized&rdquo;) immediately afterwards.</p>
<p>Knowing this, you can avoid a set of bugs which could be found with a couple of devices in the past:</p>
<ul>
<li>If you want your device to provide properties for other parts of the QEMU code or for the users, and you want to add those properties via one of the many object_property_add*() functions of QEMU (instead of using the <em>“props”</em> field of the <em>DeviceClass</em>), then you should do this in the <em>instance_init()</em> and not in the <em>realize()</em> function. Otherwise the properties won&rsquo;t show up when the user runs <code>--device xyz,help</code> or the <code>device-list-properties</code> QOM command to get some information about your device.</li>
<li><em>instance_init()</em> functions must really never fail, i.e. also not call <em>abort()</em> or <em>exit()</em>. Otherwise QEMU can terminate unexpectedly when a user simply wanted to have a look at the list of device properties with <code>device_add xyz,help</code> or the <code>device-list-properties</code> QOM command. If your device cannot work in certain circumstances, check for the error condition in the <em>realize()</em> function instead and return with an appropriate error there.</li>
<li>Never assume that your device is always instantiated only with the machine that it was designed for. It&rsquo;s of course a good idea to set the <em>“user_creatable = false”</em> flag in the <em>DeviceClass</em> of your device if your device cannot be plugged in arbitrary machines. But device introspection can still happen at any time, with any machine. So if you wrote a device called &ldquo;mydev-a&rdquo; that only works with <code>--machine A</code>, the user still can start QEMU with the option <code>--machine B</code> instead and then run <code>device_add mydev-a,help</code> or the <code>device-list-properties</code> QOM command. The <em>instance_init()</em> function of your device will be called to create a temporary instance of your device, even though the base machine is B and not A here. So you especially should take care to not depend on the availability of certain buses or other devices in the <em>instance_init()</em> function, nor use things like <em>serial_hd()</em> or <em>nd_table[]</em> in your <em>instance_init()</em> function, since these might (and should) have been used by the machine init function already. If your device needs to be wired up, provide properties as interfaces to the outside and let the creator of your device (e.g. the machine init code) wire your device between the device instantiation and the realize phase instead.</li>
<li>Make sure that your device leaves a clean state after a temporary instance is destroyed again, i.e. don&rsquo;t assume that there will be only one instance of your device which is created at the beginning right after QEMU has been started and is destroyed at the very end before QEMU terminates. Thus do not assume that the things that you do in your <em>instance_init()</em> don&rsquo;t need explicit clean-up since the device instance will only be destroyed when QEMU terminates. Device instances can be created and destroyed at any time, so when the device is finalized, you must not leave any dangling pointers or references to your device behind you, e.g. in the QOM tree. When you create other objects in your <em>instance_init()</em> function, make sure to set proper parents of these objects or use an <em>instance_finalize()</em> function, so that the created objects get cleaned up correctly again when your device is destroyed.</li>
</ul>
<p>All in all, if you write code for a new QEMU device, it is likely a good idea to use the <em>instance_init()</em> function only for e.g. creating properties and other things that are required before device realization, and then do the main work in the <em>realize()</em> function instead.</p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 源码分析-QOM</title>
      <link>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-qom/</link>
      <pubDate>Wed, 09 Mar 2022 16:02:19 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-qom/</guid>
      <description>&lt;h2 id=&#34;qom-简介&#34;&gt;QOM 简介&lt;/h2&gt;
&lt;p&gt;QOM(QEMU Object Model) 是 QEMU 的一个模块，用于描述虚拟机的结构，包括虚拟机的 CPU、内存、硬盘、网络、输入输出设备等。QEMU 为了方便整个系统的构建，实现了自己的一套的面向对象机制，也就是 QOM(QEMU Object Model)。它能够方便的表示各个设备（Device）与总线（Bus）之间的关系。&lt;/p&gt;
&lt;p&gt;这个模型主要包含四个结构体：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Object: 是所有对象的 基类 Base Object&lt;/li&gt;
&lt;li&gt;ObjectClass: 是所有类对象的基类&lt;/li&gt;
&lt;li&gt;TypeInfo：是用户用来定义一个 Type 的工具型的数据结构&lt;/li&gt;
&lt;li&gt;TypeImpl：TypeInfo 抽象数据结构，TypeInfo 的属性与 TypeImpl 的属性对应&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 QEMU 里要初始化一个对象需要完成四步：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将 TypeInfo 注册 TypeImpl&lt;/li&gt;
&lt;li&gt;实例化 Class（ObjectClass）&lt;/li&gt;
&lt;li&gt;实例化 Object&lt;/li&gt;
&lt;li&gt;添加 Property&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-09-19-4995177f1aeb782fadaf586f8f5c8b32-qemu-QOM%E5%88%86%E6%9E%90.drawio%20-2--1a4eac.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-09-19-4995177f1aeb782fadaf586f8f5c8b32-qemu-QOM%E5%88%86%E6%9E%90.drawio%20-2--1a4eac.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h2 id=&#34;qom-中的面向对象&#34;&gt;QOM 中的面向对象&lt;/h2&gt;
&lt;h3 id=&#34;继承&#34;&gt;继承&lt;/h3&gt;
&lt;p&gt;在 QEMU 中通过 &lt;strong&gt;TypeInfo&lt;/strong&gt; 来定义一个类。&lt;/p&gt;
&lt;p&gt;例如 &lt;code&gt;x86_base_cpu_type_info&lt;/code&gt; 就是一个 &lt;code&gt;class&lt;/code&gt;，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x86_base_cpu_type_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;X86_CPU_TYPE_NAME&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;base&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_X86_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x86_cpu_base_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;利用结构体包含来实现继承&lt;/strong&gt;。这应该是所有的语言实现继承的方法，在 C++ 中，结构体包含的操作被语言内部实现了，而 C 语言需要自己实现。&lt;/p&gt;
&lt;p&gt;例如 &lt;code&gt;x86_cpu_type_info&lt;/code&gt; 的 &lt;code&gt;parent&lt;/code&gt; 是 &lt;code&gt;cpu_type_info&lt;/code&gt;, 他们的结构体分别是 &lt;code&gt;X86CPU&lt;/code&gt; 和 &lt;code&gt;CPUState&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x86_cpu_type_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_X86_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X86CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cpu_type_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CPUState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;X86CPU&lt;/code&gt; 中包含一个 &lt;code&gt;CPUState&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X86CPU&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/*&amp;lt; private &amp;gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;CPUState&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent_obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/*&amp;lt; public &amp;gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;CPUNegativeOffsetState&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;静态成员&#34;&gt;静态成员&lt;/h3&gt;
&lt;p&gt;静态成员变量可以在类的多个对象中访问，但是要在类外声明。&lt;strong&gt;不同对象访问的其实是同一个实体，静态成员变量被多个对象共享&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x86_cpu_type_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_X86_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X86CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x86_cpu_initfn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_post_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x86_cpu_post_initfn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X86CPUClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x86_cpu_common_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中 &lt;code&gt;X86CPU&lt;/code&gt; 描述的是非静态成员，而 &lt;code&gt;X86CPUClass&lt;/code&gt; 描述的是静态的成员。也就是说&lt;code&gt;class_init&lt;/code&gt;初始化静态成员，&lt;code&gt;instance_init&lt;/code&gt;初始化非静态成员。&lt;/p&gt;
&lt;p&gt;那么何时初始化静态成员呢？首先得告诉系统，咱有&lt;code&gt;class_init&lt;/code&gt;这个初始化函数，等需要的时候随时可以调用它初始化，所有先解决如何将这个初始化函数注册到系统中？&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;target/i386/cpu.c&lt;/code&gt;最后使用了&lt;code&gt;type_init&lt;/code&gt;。在&lt;code&gt;qemu/include/qemu/module.h&lt;/code&gt;中有一个&lt;code&gt;type_init&lt;/code&gt;宏定义，除了&lt;code&gt;type_init&lt;/code&gt;还有其他宏，比如&lt;code&gt;block_init&lt;/code&gt;，&lt;code&gt;opts_init&lt;/code&gt;等。每个宏都表示一类&lt;code&gt;module&lt;/code&gt;，通过&lt;code&gt;module_init&lt;/code&gt;构造出来。我们展开这个宏，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;__attribute__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;do_qemu_init_x86_cpu_register_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;register_module_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x86_cpu_register_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MODULE_INIT_QOM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过 &lt;code&gt;gcc&lt;/code&gt; 扩展属性&lt;code&gt;__attribute__((constructor))&lt;/code&gt;可以让 &lt;code&gt;do_qemu_init_x86_cpu_register_types&lt;/code&gt; 在运行 &lt;code&gt;main&lt;/code&gt; 函数之前运行。 &lt;code&gt;register_module_init&lt;/code&gt; 会让 &lt;code&gt;x86_cpu_register_types&lt;/code&gt; 这个函数挂载到 &lt;code&gt;init_type_list[MODULE_INIT_QOM]&lt;/code&gt; 这个链表上。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210907133931.svg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210907133931.svg&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;到底，所有的 &lt;code&gt;TypeInfo&lt;/code&gt; 通过 &lt;code&gt;type_init&lt;/code&gt; 都被放到 &lt;code&gt;type_table&lt;/code&gt; 上了，之后通过 &lt;code&gt;Typeinfo&lt;/code&gt; 的名称调用 &lt;code&gt;type_table_lookup&lt;/code&gt; 获取到 &lt;code&gt;TypeImpl&lt;/code&gt; 了。&lt;/p&gt;
&lt;p&gt;到这里，将&lt;code&gt;TYPE_X86_CPU&lt;/code&gt;注册进类系统，包括其初始化函数，这部分也就是 QEMU 中类型的构造。那么何时调用静态成员初始化函数呢？也就是类型的初始化。&lt;/p&gt;
&lt;p&gt;静态成员是所有的对象公用的，其初始化显然要发生在所有的对象初始化之前。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;qemu_init&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;select_machine&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;object_class_get_list&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;object_class_foreach&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;g_hash_table_foreach&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;n&#34;&gt;object_class_foreach_tramp&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;n&#34;&gt;type_initialize&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;n&#34;&gt;type_initialize&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                    &lt;span class=&#34;n&#34;&gt;x86_cpu_common_class_init&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;select_machine&lt;/code&gt; 需要获取所有的 &lt;code&gt;TYPE_MACHINE&lt;/code&gt; 的 &lt;code&gt;class&lt;/code&gt;, 其首先会调用所有的&lt;code&gt;class_list&lt;/code&gt;，其会遍历 &lt;code&gt;type_table&lt;/code&gt;，遍历的过程中会顺带 &lt;code&gt;type_initialize&lt;/code&gt; 所有的 &lt;code&gt;TypeImpl&lt;/code&gt; 进而调用的 &lt;code&gt;class_init&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;说完类型的初始化，再讲一下对象的初始化，也就是初始化非静态成员，也就是&lt;code&gt;instance_init&lt;/code&gt;在何时被调用？&lt;/p&gt;
&lt;p&gt;对象初始化，通过调用 &lt;code&gt;object_new&lt;/code&gt; 来实现初始化。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;object_initialize_with_type&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;初始化一个空的 :&lt;code&gt;Object::properties&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;object_init_with_type&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;如果 &lt;code&gt;object&lt;/code&gt; 有 &lt;code&gt;parent&lt;/code&gt;，那么调用 &lt;code&gt;object_init_with_type&lt;/code&gt; 首先初始化 &lt;code&gt;parent&lt;/code&gt; 的&lt;/li&gt;
&lt;li&gt;调用&lt;code&gt;TypeImpl::instance_init&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;main&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;qemu_init&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;qmp_x_exit_preconfig&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;qemu_init_board&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;machine_run_board_init&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;pc_init_v6_1&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;n&#34;&gt;pc_init1&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;n&#34;&gt;x86_cpus_init&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;n&#34;&gt;x86_cpu_new&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                    &lt;span class=&#34;n&#34;&gt;object_new&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                        &lt;span class=&#34;n&#34;&gt;object_new_with_type&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                            &lt;span class=&#34;n&#34;&gt;object_initialize_with_type&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                &lt;span class=&#34;n&#34;&gt;object_init_with_type&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                    &lt;span class=&#34;n&#34;&gt;x86_cpu_initfn&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;多态&#34;&gt;多态&lt;/h3&gt;
&lt;p&gt;多态是指同一操作作用于不同的对象，可以有不同的解释，产生不同的执行结果。为了实现多态，QOM 实现了一个非常重要的功能，就是动态类型转换。我们可以使用相关的函数，将一个&lt;code&gt;Object&lt;/code&gt;的指针在运行时转换为子类对象的指针，可以将一个&lt;code&gt;ObjectClass&lt;/code&gt;的指针在运行时转换为子类的指针。这样就可以调用子类中定义的函数指针来完成相应的功能。&lt;/p&gt;
&lt;p&gt;QEMU 定义了一些列的宏封来进行动态类型转换：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//include/qom/object.h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * DECLARE_INSTANCE_CHECKER:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @InstanceType: instance struct name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @OBJ_NAME: the object name in uppercase with underscore separators
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @TYPENAME: type name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Direct usage of this macro should be avoided, and the complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT_DECLARE_TYPE macro is recommended instead.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * This macro will provide the instance type cast functions for a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * QOM type.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    static inline G_GNUC_UNUSED InstanceType * \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    OBJ_NAME(const void *obj) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    { return OBJECT_CHECK(InstanceType, obj, TYPENAME); }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * DECLARE_CLASS_CHECKERS:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @ClassType: class struct name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @OBJ_NAME: the object name in uppercase with underscore separators
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @TYPENAME: type name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Direct usage of this macro should be avoided, and the complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT_DECLARE_TYPE macro is recommended instead.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * This macro will provide the class type cast functions for a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * QOM type.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    static inline G_GNUC_UNUSED ClassType * \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    OBJ_NAME##_GET_CLASS(const void *obj) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    { return OBJECT_GET_CLASS(ClassType, obj, TYPENAME); } \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    static inline G_GNUC_UNUSED ClassType * \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    OBJ_NAME##_CLASS(const void *klass) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    { return OBJECT_CLASS_CHECK(ClassType, klass, TYPENAME); }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * DECLARE_OBJ_CHECKERS:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @InstanceType: instance struct name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @ClassType: class struct name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @OBJ_NAME: the object name in uppercase with underscore separators
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @TYPENAME: type name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Direct usage of this macro should be avoided, and the complete
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT_DECLARE_TYPE macro is recommended instead.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * This macro will provide the three standard type cast functions for a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * QOM type.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define DECLARE_OBJ_CHECKERS(InstanceType, ClassType, OBJ_NAME, TYPENAME) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT_DECLARE_TYPE:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @InstanceType: instance struct name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @ClassType: class struct name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * This macro is typically used in a header file, and will:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *   - create the typedefs for the object and class structs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *   - register the type for use with g_autoptr
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *   - provide three standard type cast functions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * The object struct and class struct need to be declared manually.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define OBJECT_DECLARE_TYPE(InstanceType, ClassType, MODULE_OBJ_NAME) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    typedef struct InstanceType InstanceType; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    typedef struct ClassType ClassType; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    G_DEFINE_AUTOPTR_CLEANUP_FUNC(InstanceType, object_unref) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    DECLARE_OBJ_CHECKERS(InstanceType, ClassType, \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;                         MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @obj: A derivative of #Object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Converts an object to a #Object.  Since all objects are #Objects,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * this function will always succeed.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define OBJECT(obj) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    ((Object *)(obj))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT_CLASS:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @class: A derivative of #ObjectClass.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Converts a class to an #ObjectClass.  Since all objects are #Objects,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * this function will always succeed.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define OBJECT_CLASS(class) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    ((ObjectClass *)(class))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT_CHECK:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @type: The C type to use for the return value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @obj: A derivative of @type to cast.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @name: The QOM typename of @type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * A type safe version of @object_dynamic_cast_assert.  Typically each class
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * will define a macro based on this type to perform type safe dynamic_casts to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * this object type.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * If an invalid object is passed to this function, a run time assert will be
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * generated.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define OBJECT_CHECK(type, obj, name) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    ((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;                                        __FILE__, __LINE__, __func__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT_CLASS_CHECK:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @class_type: The C type to use for the return value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @class: A derivative class of @class_type to cast.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @name: the QOM typename of @class_type.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * A type safe version of @object_class_dynamic_cast_assert.  This macro is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * typically wrapped by each type to perform type safe casts of a class to a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * specific class type.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define OBJECT_CLASS_CHECK(class_type, class, name) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;                                               __FILE__, __LINE__, __func__))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * OBJECT_GET_CLASS:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @class: The C type to use for the return value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @obj: The object to obtain the class for.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @name: The QOM typename of @obj.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * This function will return a specific class for a given object.  Its generally
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * used by each type to provide a type safe macro to get a specific class type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * from an object.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define OBJECT_GET_CLASS(class, obj, name) \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以&lt;code&gt;OBJECT_DECLARE_TYPE(X86CPU, X86CPUClass, X86_CPU)&lt;/code&gt;为例，宏展开如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X86CPU&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X86CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X86CPUClass&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X86CPUClass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;G_DEFINE_AUTOPTR_CLEANUP_FUNC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X86CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;object_unref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;G_GNUC_UNUSED&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X86CPU&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;X86_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X86CPU&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;object_dynamic_cast_assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TYPE_X86_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s&#34;&gt;&amp;#34;~/core/vn/docs/qemu/res/qom-macros.c&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;__func__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;G_GNUC_UNUSED&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X86CPUClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;X86_CPU_GET_CLASS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X86CPUClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;object_class_dynamic_cast_assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;object_get_class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))))),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TYPE_X86_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s&#34;&gt;&amp;#34;~/core/vn/docs/qemu/res/qom-macros.c&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;__func__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;G_GNUC_UNUSED&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X86CPUClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;X86_CPU_CLASS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;X86CPUClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;object_class_dynamic_cast_assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TYPE_X86_CPU&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;s&#34;&gt;&amp;#34;~/core/vn/docs/qemu/res/qom-macros.c&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;__func__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;OBJECT_DECLARE_TYPE&lt;/code&gt;通常在头文件中使用，效果是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建了&lt;code&gt;X86CPU&lt;/code&gt;和&lt;code&gt;X86CPUClass&lt;/code&gt;的&lt;code&gt;typedef&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;用&lt;code&gt;G_DEFINE_AUTOPTR_CLEANUP_FUNC&lt;/code&gt;注册类型&lt;/li&gt;
&lt;li&gt;创建了三个类型转换函数
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;X86_CPU&lt;/code&gt; : 将任何一个 &lt;code&gt;object&lt;/code&gt; 指针 转换为 &lt;code&gt;X86CPU&lt;/code&gt;（Object 转子对象）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X86_CPU_GET_CLASS&lt;/code&gt; : 根据 &lt;code&gt;object&lt;/code&gt; 指针获取到 &lt;code&gt;X86CPUClass&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X86_CPU_CLASS&lt;/code&gt; : 根据 &lt;code&gt;ObjectClass&lt;/code&gt; 指针转换到 &lt;code&gt;X86CPUClass&lt;/code&gt;（基类转子类）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里的转换依赖内存布局，子类型的第一个成员总是基类型。子类转基类就很容易，只需要强制类型转换就可以实现。&lt;/p&gt;
&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://martins3.github.io/qemu/qom.html#init&#34;&gt;QEMU 中的面向对象 : QOM | Deep Dark Fantasy&lt;/a&gt;
&lt;a href=&#34;https://www.jianshu.com/p/4a9d26abb44d&#34;&gt;浅谈 QEMU 的对象系统 - 简书&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="qom-简介">QOM 简介</h2>
<p>QOM(QEMU Object Model) 是 QEMU 的一个模块，用于描述虚拟机的结构，包括虚拟机的 CPU、内存、硬盘、网络、输入输出设备等。QEMU 为了方便整个系统的构建，实现了自己的一套的面向对象机制，也就是 QOM(QEMU Object Model)。它能够方便的表示各个设备（Device）与总线（Bus）之间的关系。</p>
<p>这个模型主要包含四个结构体：</p>
<ul>
<li>Object: 是所有对象的 基类 Base Object</li>
<li>ObjectClass: 是所有类对象的基类</li>
<li>TypeInfo：是用户用来定义一个 Type 的工具型的数据结构</li>
<li>TypeImpl：TypeInfo 抽象数据结构，TypeInfo 的属性与 TypeImpl 的属性对应</li>
</ul>
<p>在 QEMU 里要初始化一个对象需要完成四步：</p>
<ul>
<li>将 TypeInfo 注册 TypeImpl</li>
<li>实例化 Class（ObjectClass）</li>
<li>实例化 Object</li>
<li>添加 Property</li>
</ul>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-09-19-4995177f1aeb782fadaf586f8f5c8b32-qemu-QOM%E5%88%86%E6%9E%90.drawio%20-2--1a4eac.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-09-19-4995177f1aeb782fadaf586f8f5c8b32-qemu-QOM%E5%88%86%E6%9E%90.drawio%20-2--1a4eac.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h2 id="qom-中的面向对象">QOM 中的面向对象</h2>
<h3 id="继承">继承</h3>
<p>在 QEMU 中通过 <strong>TypeInfo</strong> 来定义一个类。</p>
<p>例如 <code>x86_base_cpu_type_info</code> 就是一个 <code>class</code>，</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">x86_base_cpu_type_info</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="nf">X86_CPU_TYPE_NAME</span><span class="p">(</span><span class="s">&#34;base&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">TYPE_X86_CPU</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">class_init</span> <span class="o">=</span> <span class="n">x86_cpu_base_class_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p><strong>利用结构体包含来实现继承</strong>。这应该是所有的语言实现继承的方法，在 C++ 中，结构体包含的操作被语言内部实现了，而 C 语言需要自己实现。</p>
<p>例如 <code>x86_cpu_type_info</code> 的 <code>parent</code> 是 <code>cpu_type_info</code>, 他们的结构体分别是 <code>X86CPU</code> 和 <code>CPUState</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">x86_cpu_type_info</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">TYPE_X86_CPU</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">TYPE_CPU</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">X86CPU</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">cpu_type_info</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">TYPE_CPU</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">TYPE_DEVICE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">CPUState</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>在 <code>X86CPU</code> 中包含一个 <code>CPUState</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">X86CPU</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/*&lt; private &gt;*/</span>
</span></span><span class="line"><span class="cl">    <span class="n">CPUState</span> <span class="n">parent_obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/*&lt; public &gt;*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">CPUNegativeOffsetState</span> <span class="n">neg</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="静态成员">静态成员</h3>
<p>静态成员变量可以在类的多个对象中访问，但是要在类外声明。<strong>不同对象访问的其实是同一个实体，静态成员变量被多个对象共享</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">x86_cpu_type_info</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">TYPE_X86_CPU</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">TYPE_CPU</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">X86CPU</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_init</span> <span class="o">=</span> <span class="n">x86_cpu_initfn</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_post_init</span> <span class="o">=</span> <span class="n">x86_cpu_post_initfn</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">abstract</span> <span class="o">=</span> <span class="nb">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">class_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">X86CPUClass</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">class_init</span> <span class="o">=</span> <span class="n">x86_cpu_common_class_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>其中 <code>X86CPU</code> 描述的是非静态成员，而 <code>X86CPUClass</code> 描述的是静态的成员。也就是说<code>class_init</code>初始化静态成员，<code>instance_init</code>初始化非静态成员。</p>
<p>那么何时初始化静态成员呢？首先得告诉系统，咱有<code>class_init</code>这个初始化函数，等需要的时候随时可以调用它初始化，所有先解决如何将这个初始化函数注册到系统中？</p>
<p>在<code>target/i386/cpu.c</code>最后使用了<code>type_init</code>。在<code>qemu/include/qemu/module.h</code>中有一个<code>type_init</code>宏定义，除了<code>type_init</code>还有其他宏，比如<code>block_init</code>，<code>opts_init</code>等。每个宏都表示一类<code>module</code>，通过<code>module_init</code>构造出来。我们展开这个宏，</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">constructor</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nf">do_qemu_init_x86_cpu_register_types</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nf">register_module_init</span><span class="p">(</span><span class="n">x86_cpu_register_types</span><span class="p">,</span> <span class="n">MODULE_INIT_QOM</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>通过 <code>gcc</code> 扩展属性<code>__attribute__((constructor))</code>可以让 <code>do_qemu_init_x86_cpu_register_types</code> 在运行 <code>main</code> 函数之前运行。 <code>register_module_init</code> 会让 <code>x86_cpu_register_types</code> 这个函数挂载到 <code>init_type_list[MODULE_INIT_QOM]</code> 这个链表上。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210907133931.svg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210907133931.svg" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>到底，所有的 <code>TypeInfo</code> 通过 <code>type_init</code> 都被放到 <code>type_table</code> 上了，之后通过 <code>Typeinfo</code> 的名称调用 <code>type_table_lookup</code> 获取到 <code>TypeImpl</code> 了。</p>
<p>到这里，将<code>TYPE_X86_CPU</code>注册进类系统，包括其初始化函数，这部分也就是 QEMU 中类型的构造。那么何时调用静态成员初始化函数呢？也就是类型的初始化。</p>
<p>静态成员是所有的对象公用的，其初始化显然要发生在所有的对象初始化之前。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="n">main</span>
</span></span><span class="line"><span class="cl">    <span class="n">qemu_init</span> 
</span></span><span class="line"><span class="cl">        <span class="n">select_machine</span> 
</span></span><span class="line"><span class="cl">            <span class="n">object_class_get_list</span> 
</span></span><span class="line"><span class="cl">                <span class="n">object_class_foreach</span> 
</span></span><span class="line"><span class="cl">                    <span class="n">g_hash_table_foreach</span> 
</span></span><span class="line"><span class="cl">                        <span class="n">object_class_foreach_tramp</span> 
</span></span><span class="line"><span class="cl">                            <span class="n">type_initialize</span> 
</span></span><span class="line"><span class="cl">                                <span class="n">type_initialize</span> 
</span></span><span class="line"><span class="cl">                                    <span class="n">x86_cpu_common_class_init</span> 
</span></span></code></pre></div><p><code>select_machine</code> 需要获取所有的 <code>TYPE_MACHINE</code> 的 <code>class</code>, 其首先会调用所有的<code>class_list</code>，其会遍历 <code>type_table</code>，遍历的过程中会顺带 <code>type_initialize</code> 所有的 <code>TypeImpl</code> 进而调用的 <code>class_init</code>。</p>
<p>说完类型的初始化，再讲一下对象的初始化，也就是初始化非静态成员，也就是<code>instance_init</code>在何时被调用？</p>
<p>对象初始化，通过调用 <code>object_new</code> 来实现初始化。</p>
<ul>
<li><code>object_initialize_with_type</code>
<ul>
<li>初始化一个空的 :<code>Object::properties</code></li>
<li><code>object_init_with_type</code>
<ul>
<li>如果 <code>object</code> 有 <code>parent</code>，那么调用 <code>object_init_with_type</code> 首先初始化 <code>parent</code> 的</li>
<li>调用<code>TypeImpl::instance_init</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="n">main</span> 
</span></span><span class="line"><span class="cl">    <span class="n">qemu_init</span> 
</span></span><span class="line"><span class="cl">        <span class="n">qmp_x_exit_preconfig</span> 
</span></span><span class="line"><span class="cl">            <span class="n">qemu_init_board</span> 
</span></span><span class="line"><span class="cl">                <span class="n">machine_run_board_init</span> 
</span></span><span class="line"><span class="cl">                    <span class="n">pc_init_v6_1</span> 
</span></span><span class="line"><span class="cl">                        <span class="n">pc_init1</span> 
</span></span><span class="line"><span class="cl">                            <span class="n">x86_cpus_init</span> 
</span></span><span class="line"><span class="cl">                                <span class="n">x86_cpu_new</span> 
</span></span><span class="line"><span class="cl">                                    <span class="n">object_new</span> 
</span></span><span class="line"><span class="cl">                                        <span class="n">object_new_with_type</span> 
</span></span><span class="line"><span class="cl">                                            <span class="n">object_initialize_with_type</span> 
</span></span><span class="line"><span class="cl">                                                <span class="n">object_init_with_type</span> 
</span></span><span class="line"><span class="cl">                                                    <span class="n">x86_cpu_initfn</span> 
</span></span></code></pre></div><h3 id="多态">多态</h3>
<p>多态是指同一操作作用于不同的对象，可以有不同的解释，产生不同的执行结果。为了实现多态，QOM 实现了一个非常重要的功能，就是动态类型转换。我们可以使用相关的函数，将一个<code>Object</code>的指针在运行时转换为子类对象的指针，可以将一个<code>ObjectClass</code>的指针在运行时转换为子类的指针。这样就可以调用子类中定义的函数指针来完成相应的功能。</p>
<p>QEMU 定义了一些列的宏封来进行动态类型转换：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">//include/qom/object.h
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * DECLARE_INSTANCE_CHECKER:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @InstanceType: instance struct name
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @OBJ_NAME: the object name in uppercase with underscore separators
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @TYPENAME: type name
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Direct usage of this macro should be avoided, and the complete
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT_DECLARE_TYPE macro is recommended instead.
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * This macro will provide the instance type cast functions for a
</span></span></span><span class="line"><span class="cl"><span class="cm"> * QOM type.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    static inline G_GNUC_UNUSED InstanceType * \
</span></span></span><span class="line"><span class="cl"><span class="cp">    OBJ_NAME(const void *obj) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    { return OBJECT_CHECK(InstanceType, obj, TYPENAME); }
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * DECLARE_CLASS_CHECKERS:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @ClassType: class struct name
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @OBJ_NAME: the object name in uppercase with underscore separators
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @TYPENAME: type name
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Direct usage of this macro should be avoided, and the complete
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT_DECLARE_TYPE macro is recommended instead.
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * This macro will provide the class type cast functions for a
</span></span></span><span class="line"><span class="cl"><span class="cm"> * QOM type.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    static inline G_GNUC_UNUSED ClassType * \
</span></span></span><span class="line"><span class="cl"><span class="cp">    OBJ_NAME##_GET_CLASS(const void *obj) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    { return OBJECT_GET_CLASS(ClassType, obj, TYPENAME); } \
</span></span></span><span class="line"><span class="cl"><span class="cp">    \
</span></span></span><span class="line"><span class="cl"><span class="cp">    static inline G_GNUC_UNUSED ClassType * \
</span></span></span><span class="line"><span class="cl"><span class="cp">    OBJ_NAME##_CLASS(const void *klass) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    { return OBJECT_CLASS_CHECK(ClassType, klass, TYPENAME); }
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * DECLARE_OBJ_CHECKERS:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @InstanceType: instance struct name
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @ClassType: class struct name
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @OBJ_NAME: the object name in uppercase with underscore separators
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @TYPENAME: type name
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Direct usage of this macro should be avoided, and the complete
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT_DECLARE_TYPE macro is recommended instead.
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * This macro will provide the three standard type cast functions for a
</span></span></span><span class="line"><span class="cl"><span class="cm"> * QOM type.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define DECLARE_OBJ_CHECKERS(InstanceType, ClassType, OBJ_NAME, TYPENAME) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    \
</span></span></span><span class="line"><span class="cl"><span class="cp">    DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT_DECLARE_TYPE:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @InstanceType: instance struct name
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @ClassType: class struct name
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * This macro is typically used in a header file, and will:
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> *   - create the typedefs for the object and class structs
</span></span></span><span class="line"><span class="cl"><span class="cm"> *   - register the type for use with g_autoptr
</span></span></span><span class="line"><span class="cl"><span class="cm"> *   - provide three standard type cast functions
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * The object struct and class struct need to be declared manually.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define OBJECT_DECLARE_TYPE(InstanceType, ClassType, MODULE_OBJ_NAME) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    typedef struct InstanceType InstanceType; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    typedef struct ClassType ClassType; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    \
</span></span></span><span class="line"><span class="cl"><span class="cp">    G_DEFINE_AUTOPTR_CLEANUP_FUNC(InstanceType, object_unref) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    \
</span></span></span><span class="line"><span class="cl"><span class="cp">    DECLARE_OBJ_CHECKERS(InstanceType, ClassType, \
</span></span></span><span class="line"><span class="cl"><span class="cp">                         MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @obj: A derivative of #Object
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Converts an object to a #Object.  Since all objects are #Objects,
</span></span></span><span class="line"><span class="cl"><span class="cm"> * this function will always succeed.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define OBJECT(obj) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((Object *)(obj))
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT_CLASS:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @class: A derivative of #ObjectClass.
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Converts a class to an #ObjectClass.  Since all objects are #Objects,
</span></span></span><span class="line"><span class="cl"><span class="cm"> * this function will always succeed.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define OBJECT_CLASS(class) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((ObjectClass *)(class))
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT_CHECK:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @type: The C type to use for the return value.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @obj: A derivative of @type to cast.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @name: The QOM typename of @type
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * A type safe version of @object_dynamic_cast_assert.  Typically each class
</span></span></span><span class="line"><span class="cl"><span class="cm"> * will define a macro based on this type to perform type safe dynamic_casts to
</span></span></span><span class="line"><span class="cl"><span class="cm"> * this object type.
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * If an invalid object is passed to this function, a run time assert will be
</span></span></span><span class="line"><span class="cl"><span class="cm"> * generated.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define OBJECT_CHECK(type, obj, name) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                        __FILE__, __LINE__, __func__))
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT_CLASS_CHECK:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @class_type: The C type to use for the return value.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @class: A derivative class of @class_type to cast.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @name: the QOM typename of @class_type.
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * A type safe version of @object_class_dynamic_cast_assert.  This macro is
</span></span></span><span class="line"><span class="cl"><span class="cm"> * typically wrapped by each type to perform type safe casts of a class to a
</span></span></span><span class="line"><span class="cl"><span class="cm"> * specific class type.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define OBJECT_CLASS_CHECK(class_type, class, name) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                               __FILE__, __LINE__, __func__))
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * OBJECT_GET_CLASS:
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @class: The C type to use for the return value.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @obj: The object to obtain the class for.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @name: The QOM typename of @obj.
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * This function will return a specific class for a given object.  Its generally
</span></span></span><span class="line"><span class="cl"><span class="cm"> * used by each type to provide a type safe macro to get a specific class type
</span></span></span><span class="line"><span class="cl"><span class="cm"> * from an object.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define OBJECT_GET_CLASS(class, obj, name) \
</span></span></span><span class="line"><span class="cl"><span class="cp">    OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name)
</span></span></span></code></pre></div><p>以<code>OBJECT_DECLARE_TYPE(X86CPU, X86CPUClass, X86_CPU)</code>为例，宏展开如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">X86CPU</span> <span class="n">X86CPU</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">X86CPUClass</span> <span class="n">X86CPUClass</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nf">G_DEFINE_AUTOPTR_CLEANUP_FUNC</span><span class="p">(</span><span class="n">X86CPU</span><span class="p">,</span> <span class="n">object_unref</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="n">G_GNUC_UNUSED</span> <span class="n">X86CPU</span> <span class="o">*</span><span class="nf">X86_CPU</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">((</span><span class="n">X86CPU</span> <span class="o">*</span><span class="p">)</span><span class="nf">object_dynamic_cast_assert</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="p">((</span><span class="n">Object</span> <span class="o">*</span><span class="p">)(</span><span class="n">obj</span><span class="p">)),</span> <span class="p">(</span><span class="n">TYPE_X86_CPU</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;~/core/vn/docs/qemu/res/qom-macros.c&#34;</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="n">__func__</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="n">G_GNUC_UNUSED</span> <span class="n">X86CPUClass</span> <span class="o">*</span><span class="nf">X86_CPU_GET_CLASS</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">((</span><span class="n">X86CPUClass</span> <span class="o">*</span><span class="p">)</span><span class="nf">object_class_dynamic_cast_assert</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="p">((</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="p">)(</span><span class="nf">object_get_class</span><span class="p">(((</span><span class="n">Object</span> <span class="o">*</span><span class="p">)(</span><span class="n">obj</span><span class="p">))))),</span> <span class="p">(</span><span class="n">TYPE_X86_CPU</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;~/core/vn/docs/qemu/res/qom-macros.c&#34;</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="n">__func__</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kr">inline</span> <span class="n">G_GNUC_UNUSED</span> <span class="n">X86CPUClass</span> <span class="o">*</span><span class="nf">X86_CPU_CLASS</span><span class="p">(</span><span class="k">const</span> <span class="kt">void</span> <span class="o">*</span><span class="n">klass</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">((</span><span class="n">X86CPUClass</span> <span class="o">*</span><span class="p">)</span><span class="nf">object_class_dynamic_cast_assert</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="p">((</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="p">)(</span><span class="n">klass</span><span class="p">)),</span> <span class="p">(</span><span class="n">TYPE_X86_CPU</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">      <span class="s">&#34;~/core/vn/docs/qemu/res/qom-macros.c&#34;</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="n">__func__</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>OBJECT_DECLARE_TYPE</code>通常在头文件中使用，效果是：</p>
<ul>
<li>创建了<code>X86CPU</code>和<code>X86CPUClass</code>的<code>typedef</code></li>
<li>用<code>G_DEFINE_AUTOPTR_CLEANUP_FUNC</code>注册类型</li>
<li>创建了三个类型转换函数
<ul>
<li><code>X86_CPU</code> : 将任何一个 <code>object</code> 指针 转换为 <code>X86CPU</code>（Object 转子对象）</li>
<li><code>X86_CPU_GET_CLASS</code> : 根据 <code>object</code> 指针获取到 <code>X86CPUClass</code></li>
<li><code>X86_CPU_CLASS</code> : 根据 <code>ObjectClass</code> 指针转换到 <code>X86CPUClass</code>（基类转子类）</li>
</ul>
</li>
</ul>
<p>这里的转换依赖内存布局，子类型的第一个成员总是基类型。子类转基类就很容易，只需要强制类型转换就可以实现。</p>
<h2 id="参考">参考</h2>
<p><a href="https://martins3.github.io/qemu/qom.html#init">QEMU 中的面向对象 : QOM | Deep Dark Fantasy</a>
<a href="https://www.jianshu.com/p/4a9d26abb44d">浅谈 QEMU 的对象系统 - 简书</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 源码分析-内存虚拟化</title>
      <link>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E5%86%85%E5%AD%98%E8%99%9A%E6%8B%9F%E5%8C%96/</link>
      <pubDate>Tue, 25 Jan 2022 13:42:11 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E5%86%85%E5%AD%98%E8%99%9A%E6%8B%9F%E5%8C%96/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;1.大部分转载自&lt;a href=&#34;https://abelsu7.top/2019/07/07/kvm-memory-virtualization/&#34;&gt;QEMU 内存虚拟化源码分析 | Keep Coding | 苏易北&lt;/a&gt;
2.原文源码为 QEMU1.2.0，版本较旧，部分源码内容根据 QEMU6.2 版本修改
3.部分内容根据自己理解补充添加&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;概述&#34;&gt;概述&lt;/h2&gt;
&lt;p&gt;我们知道操作系统给每个进程分配虚拟内存，通过页表映射，变成物理内存进行访问。当有了虚拟机之后，情况会变得更加复杂。因为虚拟机对于物理机来讲是一个进程，但是虚拟机里面也有内核，也有虚拟机里面跑的进程。所以有了虚拟机，内存就变成了四类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;虚拟机里面的虚拟内存（Guest OS Virtual Memory，GVA），这是虚拟机里面的进程看到的内存空间；&lt;/li&gt;
&lt;li&gt;虚拟机里面的物理内存（Guest OS Physical Memory，GPA），这是虚拟机里面的操作系统看到的内存，它认为这是物理内存；&lt;/li&gt;
&lt;li&gt;物理机的虚拟内存（Host Virtual Memory，HVA），这是物理机上的 qemu 进程看到的内存空间；&lt;/li&gt;
&lt;li&gt;物理机的物理内存（Host Physical Memory，HPA），这是物理机上的操作系统看到的内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;内存虚拟化的关键在于维护 &lt;code&gt;GPA&lt;/code&gt; 到 &lt;code&gt;HVA&lt;/code&gt; 的映射关系。&lt;/p&gt;
&lt;h2 id=&#34;页面分配和映射的两种方式&#34;&gt;页面分配和映射的两种方式&lt;/h2&gt;
&lt;p&gt;要搞清楚 QEMU system emulation 的仿真架构，首先对于 Host OS，将 QEMU 作为进程启动，然后对于 QEMU 进程，会仿真各种硬件和运行 Guest OS，在这层 OS 上运行要全系统模拟的应用程序，因此对于 Guest OS 管理的内存要实现到 QEMU 进程的虚拟空间的转换需要 softMMU（即需要对 GPA 到 HVA 进行转换）。从 GVA 到 GPA 到 HVA 到 HPA，性能很差，为了解决这个问题，有两种主要的思路。&lt;/p&gt;
&lt;h3 id=&#34;影子页表-shadow-page-tablespt&#34;&gt;影子页表 Shadow Page Table，SPT&lt;/h3&gt;
&lt;p&gt;第一种方式就是软件的方式，影子页表（Shadow Page Table）。&lt;/p&gt;
&lt;p&gt;KVM 通过维护记录 GVA-&amp;gt;HPA 的影子页表 SPT，减少了地址转换带来的开销，可以直接将 GVA 转换为 HPA。&lt;/p&gt;
&lt;p&gt;内存映射要通过页表来管理，页表地址应该放在 CR3 寄存器里面。在软件虚拟化的内存转换中，GVA 到 GPA 的转换通过查询 CR3 寄存器来完成，CR3 中保存了 Guest 的页表基地址，然后载入 MMU 中进行地址转换。&lt;/p&gt;
&lt;p&gt;在加入了 SPT 技术后，当 Guest 访问 CR3 时，KVM 会捕获到这个操作 EXIT_REASON_CR_ACCESS，之后 KVM 会载入特殊的 CR3 和影子页表，欺骗 Guest 这就是真实的 CR3。之后就和传统的访问内存方式一致，当需要访问物理内存的时候，只会经过一层影子页表的转换。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本来的过程是，客户机要通过 cr3 找到客户机的页表，实现从 GVA 到 GPA 的转换，然后在宿主机上，要通过 cr3 找到宿主机的页表，实现从 HVA 到 HPA 的转换。
为了实现客户机虚拟地址空间到宿主机物理地址空间的直接映射。客户机中每个进程都有自己的虚拟地址空间，所以 KVM 需要为客户机中的每个进程页表都要维护一套相应的影子页表。
在客户机访问内存时，使用的不是客户机的原来的页表，而是这个页表对应的影子页表，从而实现了从客户机虚拟地址到宿主机物理地址的直接转换。而且，在 TLB 和 CPU 缓存上缓存的是来自影子页表中客户机虚拟地址和宿主机物理地址之间的映射，也因此提高了缓存的效率。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220124192700.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220124192700.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;为了快速检索 Guest 页表对应的影子页表，KVM 为每个客户机维护了一个 hash 表来进行客户机页表到影子页表之间的映射。对于每一个 Guest 来说，其页目录和页表都有唯一的 GPA，通过页目录/页表的 GPA 就可以在哈希链表中快速地找到对应的影子页目录/页表。&lt;/p&gt;
&lt;p&gt;当 Guest 切换进程时，Guest 会把待切换进程的页表基址载入 CR3，而 KVM 将会截获这一特权指令。KVM 在哈希表中找到与此页表基址对应的影子页表基址，载入 Guest CR3，使 Guest 在恢复运行时 CR3 实际指向的是新切换进程对应的影子页表。&lt;/p&gt;
&lt;p&gt;影子页表的引入，减少了 GVA-&amp;gt;HPA 的转换开销，但是缺点在于需要为 Guest 的每个进程都维护一个影子页表，这将带来很大的内存开销。同时影子页表的建立是很耗时的，如果 Guest 的进程过多，将导致影子页表频繁切换。&lt;/p&gt;
&lt;p&gt;因此 Intel 和 AMD 在此基础上提供了基于硬件的虚拟化技术 EPT。&lt;/p&gt;
&lt;h3 id=&#34;扩展页表-extent-page-tableept&#34;&gt;扩展页表 Extent Page Table，EPT&lt;/h3&gt;
&lt;p&gt;Intel 的 EPT（Extent Page Table）技术和 AMD 的 NPT（Nest Page Table）技术都对内存虚拟化提供了硬件支持。这两种技术原理类似，都是在硬件层面上实现 GVA 到 HPA 之间的转换。下面就以 EPT 为例分析一下 KVM 基于硬件辅助的内存虚拟化实现。&lt;/p&gt;
&lt;p&gt;EPT 在原有客户机页表对客户机虚拟地址 GVA 到客户机物理地址 GPA 映射的基础上，又引入了 EPT 页表来实现客户机物理地址 GPA 到宿主机物理地址 HPA 的另一次映射。客户机运行时，客户机页表被载入 CR3，而 EPT 页表被载入专门的 EPT 页表指针寄存器 EPTP。&lt;/p&gt;
&lt;p&gt;即 EPT 技术采用了在两级页表结构，即原有 Guest OS 页表对 &lt;strong&gt;GVA-&amp;gt;GPA&lt;/strong&gt; 映射的基础上，又引入了 EPT 页表来实现 &lt;strong&gt;GPA-&amp;gt;HPA&lt;/strong&gt; 的另一次映射，这&lt;strong&gt;两次地址映射都是由硬件自动完成&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;有了 EPT，在&lt;strong&gt;GPA-&amp;gt;HPA&lt;/strong&gt;转换的过程中，缺页会产生 EPT 缺页异常。KVM 首先根据引起异常的客户机物理地址，映射到对应的宿主机虚拟地址，然后为此虚拟地址分配新的物理页，最后 KVM 再更新 EPT 页表，建立起引起异常的客户机物理地址到宿主机物理地址之间的映射。&lt;/p&gt;
&lt;p&gt;KVM 只需为每个客户机维护一套 EPT 页表，也大大减少了内存的开销。&lt;/p&gt;
&lt;p&gt;这里，我们重点看第二种方式。因为使用了 EPT 之后，客户机里面的页表映射，也即从 GVA 到 GPA 的转换，还是用传统的方式，和在内存管理那一章讲的没有什么区别。而 EPT 重点帮我们解决的就是从 GPA 到 HPA 的转换问题。因为要经过两次页表，所以 EPT 又 tdp(two dimentional paging)。&lt;/p&gt;
&lt;p&gt;EPT 的页表结构也是分为四层，EPT Pointer（EPTP）指向 PML4 的首地址。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220124194059.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220124194059.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h2 id=&#34;qemu-的主要工作&#34;&gt;QEMU 的主要工作&lt;/h2&gt;
&lt;p&gt;内存虚拟化的目的就是让虚拟机能够无缝的访问内存。有了 Intel EPT 的支持后，CPU 在 VMX non-root 状态时进行内存访问会再做一次 EPT 转换。在这个过程中，QEMU 会负责以下内容：&lt;/p&gt;
&lt;p&gt;首先需要从自己的进程地址空间中申请内存用于 Guest
需要将上一步中申请到的内存的虚拟地址（HVA）和 Guest 的物理地址之间的映射关系传递给 KVM（kernel），即 GPA-&amp;gt;HVA
需要组织一系列的数据结构来管理虚拟内存空间，并在内存拓扑结构更改时将最新的内存信息同步至 KVM 中&lt;/p&gt;
&lt;h2 id=&#34;qemu-和-kvm-的工作分界&#34;&gt;QEMU 和 KVM 的工作分界&lt;/h2&gt;
&lt;p&gt;QEMU 和 KVM 之间是通过 KVM 提供的 ioctl() 接口进行交互的。在内核的 kvm_vm_ioctl() 中，&lt;strong&gt;设置虚拟机内存&lt;/strong&gt;的系统调用【kernel 就是一系列系统调用函数接口和处理逻辑，其中有个处理”创建/设置虚拟机内存“的系统调用接口】为  &lt;code&gt;VM_SET_USER_MEMORY_REGION&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_vm_ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;               &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;arg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;KVM_SET_USER_MEMORY_REGION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 在 KVM 中注册用户空间传入的内存信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_userspace_memory_region&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_userspace_mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EFAULT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;c1&#34;&gt;// 将传入的数据结构复制到内核空间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;copy_from_user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kvm_userspace_mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;argp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_userspace_mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;goto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;c1&#34;&gt;// 实际进行处理的函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_vm_ioctl_set_memory_region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kvm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kvm_userspace_mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;goto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;out&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到这里需要传递的参数类型为 &lt;code&gt;kvm_userspace_memory_region&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* for KVM_SET_USER_MEMORY_REGION */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_userspace_memory_region&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// slot 编号 [参考：https://www.cnblogs.com/LoyenWang/p/11922887.html]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;           &lt;span class=&#34;c1&#34;&gt;// 标志位，例如是否追踪脏页、是否可用等
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;guest_phys_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Guest 物理地址，即 GPA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memory_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// 内存大小，单位 bytes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;userspace_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 从 QEMU 进程地址空间中分配内存的起始地址，即 HVA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;KVM_SET_USER_MEMORY_REGION&lt;/code&gt;这个 &lt;code&gt;ioctl&lt;/code&gt; 主要目的就是设置&lt;code&gt;GPA-&amp;gt;HVA&lt;/code&gt;的映射关系，KVM 会继续调用&lt;code&gt;kvm_vm_ioctl_set_memory_region()&lt;/code&gt;，在内核空间维护并管理 Guest 的内存。&lt;/p&gt;
&lt;h2 id=&#34;相关数据结构&#34;&gt;相关数据结构&lt;/h2&gt;
&lt;h3 id=&#34;addressspace&#34;&gt;AddressSpace&lt;/h3&gt;
&lt;h4 id=&#34;结构体定义&#34;&gt;结构体定义&lt;/h4&gt;
&lt;p&gt;QEMU 用 AddressSpace 结构体表示 Guest 中 CPU/设备看到的内存【也就是Guest OS 可以在 QEMU 进程虚存中用到的所有内存，是 MemoryRegion 的集合，即 GPA 的整体】，类似于物理机中地址空间的概念，但在这里表示的是 Guest 的一段地址空间，如内存地址空间 &lt;code&gt;address_space_memory&lt;/code&gt; 、&lt;code&gt;I/O&lt;/code&gt; 地址空间&lt;code&gt;address_space_io&lt;/code&gt;，它在 QEMU 源码&lt;code&gt;memory.c&lt;/code&gt;中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* private: */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rcu_head&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rcu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Accessed via RCU.  */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FlatView&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ioeventfd_nb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionIoeventfd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ioeventfds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_ENTRY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;address_spaces_link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个 AddressSpace 一般包含一系列的 MemoryRegion：&lt;code&gt;root&lt;/code&gt;指针指向根级 MemoryRegion，而 root 可能有自己的若干个 sub-regions（子节点），于是形成树状结构。这些 MemoryRegion 通过树连接起来，树的根即为 AddressSpace 的 root 域。&lt;/p&gt;
&lt;h4 id=&#34;全局变量&#34;&gt;全局变量&lt;/h4&gt;
&lt;p&gt;另外，QEMU 中有两个全局的静态 AddressSpace，在 memory.c 中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;address_space_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 内存地址空间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;address_space_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// I/O 地址空间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其 root 域分别指向之后会提到的两个 MemoryRegion 类型变量：system_memory、system_io。&lt;/p&gt;
&lt;h3 id=&#34;memoryregion&#34;&gt;MemoryRegion&lt;/h3&gt;
&lt;h4 id=&#34;结构体定义-1&#34;&gt;结构体定义&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;MemoryRegion&lt;/code&gt; 表示在 &lt;code&gt;Guest Memory Layout&lt;/code&gt; 中的一段内存区域【也就是单元级 GPA 的概念，Guest OS 可以管理到的那些 Guest 物理内存单元】，它是联系 &lt;code&gt;GPA&lt;/code&gt; 和 &lt;code&gt;RAMBlocks&lt;/code&gt;（描述真实内存）之间的桥梁，在&lt;code&gt;memory.h&lt;/code&gt;中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* All fields are private - violators will be prosecuted */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionOps&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ops&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// 回调函数集合
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// 父 MemoryRegion 指针
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Int128&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                     &lt;span class=&#34;c1&#34;&gt;// 该区域内存的大小
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;target_phys_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;         &lt;span class=&#34;c1&#34;&gt;// 在 Address Space 中的地址，即 HVA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;destructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;             &lt;span class=&#34;c1&#34;&gt;// MemoryRegion 的起始地址，即 GPA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subpage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;terminates&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                        &lt;span class=&#34;c1&#34;&gt;// 是否表示 RAM
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readonly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* For RAM regions */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;enabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                    &lt;span class=&#34;c1&#34;&gt;// 是否已经通知 KVM 使用这段内存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rom_device&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;warning_printed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* For reservations */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;             &lt;span class=&#34;c1&#34;&gt;// 是否为 MemoryRegion alias
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;target_phys_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;alias_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 若为 alias，在原 MemoryRegion 中的 offset
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;may_overlap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;subregions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subregions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 子区域链表头
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_ENTRY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subregions_link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// 子区域链表节点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;coalesced_ranges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CoalescedMemoryRange&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;coalesced&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// MemoryRegion 的名字，调试时使用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirty_log_mask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 表示哪一种 dirty map 被使用，共分三种
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ioeventfd_nb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegionIoeventfd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ioeventfds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;全局变量-1&#34;&gt;全局变量&lt;/h4&gt;
&lt;p&gt;在 QEMU 的 exec.c 中也定义了两个静态的 MemoryRegion 指针变量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 内存 MemoryRegion，对应 address_space_memory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// I/O MemoryRegion，对应 address_space_io
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;与两个全局 AddressSpace 对应，即 AddressSpace 的 root 域指向这两个 MemoryRegion。&lt;/p&gt;
&lt;h4 id=&#34;memoryregion-的类型&#34;&gt;MemoryRegion 的类型&lt;/h4&gt;
&lt;p&gt;MemoryRegion 有多种类型，可以表示一段 RAM、ROM、MMIO、alias(别名)。&lt;/p&gt;
&lt;p&gt;若为 alias 则表示一个 MemoryRegion 的部分区域，例如，QEMU 会为 pc.ram 这个表示 RAM 的 MemoryRegion 添加两个 alias：ram-below-4g 和 ram-above-4g，之后会看到具体的代码实例。&lt;/p&gt;
&lt;p&gt;另外，MemoryRegion 也可以表示一个 container，这就表示它只是其他若干个 MemoryRegion 的容器。&lt;/p&gt;
&lt;p&gt;那么要如何创建不同类型的 &lt;code&gt;MemoryRegion&lt;/code&gt; 呢？&lt;/p&gt;
&lt;p&gt;在 QEMU 中实际上是通过调用不同的初始化函数区分的。根据不同的初始化函数及其功能，可以将 MemoryRegion 划分为以下三种类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;根级 MemoryRegion：直接通过 memory_region_init 初始化，没有自己的内存，用于管理 subregion，例如 system_memory：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ops&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;int128_make64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UINT64_MAX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;int128_2_64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;addr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;subpage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;enabled&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;terminates&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 非实体 MemoryRegion，搜索时会继续前往其 subregions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// 根级 MemoryRegion 不分配内存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;readable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rom_device&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;destructor&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memory_region_destructor_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;may_overlap&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alias&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_INIT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;subregions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;subregions_link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;subregions_link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_INIT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;coalesced&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_strdup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirty_log_mask&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ioeventfd_nb&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ioeventfds&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到 mr-&amp;gt;addr 被设置为 0，而 mr-&amp;gt;ram_addr 则并没有初始化。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实体 &lt;code&gt;MemoryRegion&lt;/code&gt;：通过&lt;code&gt;memory_region_init_ram()&lt;/code&gt;初始化，有自己的内存（从 QEMU 进程地址空间中分配），大小为&lt;code&gt;size&lt;/code&gt;，例如&lt;code&gt;ram_memory&lt;/code&gt;、 &lt;code&gt;pci_memory&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;pc_memory_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kernel_filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kernel_cmdline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;initrd_filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;below_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;above_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rom_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;option_rom_mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* ...*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Allocate RAM.  We allocate it as a single memory region and use
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * aliases to address portions of it, mostly for backwards compatibility
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * with older qemus that used qemu_ram_alloc().
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 调用 memory_region_init_ram 对 ram_memory 进行初始化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init_ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;pc.ram&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;below_4g_mem_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;above_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;vmstate_register_ram_global&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_memory&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init_ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;terminates&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;destructor&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memory_region_destructor_ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_addr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_ram_alloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到这里是先调用了&lt;code&gt;memory_region_init()&lt;/code&gt;，之后设置 &lt;code&gt;RAM&lt;/code&gt; 属性，并继续调用&lt;code&gt;qemu_ram_alloc()&lt;/code&gt;分配内存。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;别名 &lt;code&gt;MemoryRegion&lt;/code&gt;：通过&lt;code&gt;memory_region_init_alias()&lt;/code&gt; 初始化，没有自己的内存，表示实体 &lt;code&gt;MemoryRegion&lt;/code&gt; 的一部分。通过 &lt;code&gt;alias&lt;/code&gt; 成员指向实体 &lt;code&gt;MemoryRegion&lt;/code&gt;，&lt;code&gt;alias_offset&lt;/code&gt;为在实体 &lt;code&gt;MemoryRegion&lt;/code&gt; 中的偏移量，例如&lt;code&gt;ram_below_4g&lt;/code&gt;、&lt;code&gt;ram_above_4g&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;pc_memory_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kernel_filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kernel_cmdline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;initrd_filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;below_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;above_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rom_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_above_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 调用 memory_region_init_alias 对 ram_below_4g 进行初始化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init_alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;ram-below-4g&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;below_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* ..
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init_alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                              &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                              &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;orig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                              &lt;span class=&#34;kt&#34;&gt;target_phys_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                              &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alias&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;orig&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 指向实体 MemoryRegion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alias_offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//通过 offset 得到实体的某一个部分
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ramblock&#34;&gt;RAMBlock&lt;/h3&gt;
&lt;h4 id=&#34;结构体定义-2&#34;&gt;结构体定义&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;MemoryRegion&lt;/code&gt; 用来描述一段逻辑层面上的内存区域，而记录实际分配的内存地址信息的结构体则是 &lt;code&gt;RAMBlock&lt;/code&gt;，在&lt;code&gt;ramblock.h&lt;/code&gt;中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAMBlock&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rcu_head&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rcu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;colo_cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;cm&#34;&gt;/* For colo, VM&amp;#39;s ram cache */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;used_length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;max_length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resized&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Protected by iothread lock.  */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;idstr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;256&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* RCU-enabled, writes protected by the ramlist lock */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QLIST_ENTRY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RAMBlock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QLIST_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAMBlockNotifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ramblock_notifiers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;size_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;page_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* dirty bitmap used during migration */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bmap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* bitmap of already received pages in postcopy */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;receivedmap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * bitmap to track already cleared dirty bitmap.  When the bit is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * set, it means the corresponding memory chunk needs a log-clear.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * Set this up to non-NULL to enable the capability to postpone
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * and split clearing of dirty bitmap on the remote node (e.g.,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * KVM).  The bitmap will be set only when doing global sync.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * NOTE: this bitmap is different comparing to the other bitmaps
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * in that one bit can represent multiple guest pages (which is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * decided by the `clear_bmap_shift&amp;#39; variable below).  On
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * destination side, this should always be NULL, and the variable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * `clear_bmap_shift&amp;#39; is meaningless.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clear_bmap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;clear_bmap_shift&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * RAM block length that corresponds to the used_length on the migration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * source (after RAM block sizes were synchronized). Especially, after
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * starting to run the guest, used_length and postcopy_length can differ.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * Used to register/unregister uffd handlers and as the size of the received
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * bitmap. Receiving any page beyond this length will bail out, as it
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * could not have been valid on the source.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;postcopy_length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到在 &lt;code&gt;RAMBlock&lt;/code&gt; 中 host 和 offset 域分别对应了 &lt;code&gt;HVA&lt;/code&gt; 和&lt;code&gt;GPA&lt;/code&gt;，因此也可以说 &lt;code&gt;RAMBlock&lt;/code&gt; 中存储了&lt;code&gt;GPA-&amp;gt;HVA&lt;/code&gt;的映射关系，另外每一个 &lt;code&gt;RAMBlock&lt;/code&gt; 都会指向其所属的 &lt;code&gt;MemoryRegion&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id=&#34;全局变量-ram_list&#34;&gt;全局变量 ram_list&lt;/h4&gt;
&lt;p&gt;QEMU 在&lt;code&gt;ramlist.h&lt;/code&gt;中定义了一个全局变量&lt;code&gt;ram_list&lt;/code&gt;，以链表的形式维护了所有的 &lt;code&gt;RAMBlock&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAMList&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;QemuMutex&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mutex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;RAMBlock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mru_block&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* RCU-enabled, writes protected by the ramlist lock. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QLIST_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAMBlock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;blocks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DirtyMemoryBlocks&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirty_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DIRTY_MEMORY_NUM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QLIST_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAMBlockNotifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ramblock_notifiers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAMList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAMList&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每一个新分配的 &lt;code&gt;RAMBlock&lt;/code&gt; 都会被插入到&lt;code&gt;ram_list&lt;/code&gt;的头部。如需查找地址所对应的 &lt;code&gt;RAMBlock&lt;/code&gt;，则需要遍历&lt;code&gt;ram_list&lt;/code&gt;，当目标地址落在当前&lt;code&gt;RAMBlock&lt;/code&gt;的地址区间时，该 &lt;code&gt;RAMBlock&lt;/code&gt; 即为查找目标。&lt;/p&gt;
&lt;h4 id=&#34;asmrramblock-之间的关系&#34;&gt;AS、MR、RAMBlock 之间的关系&lt;/h4&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220225155936.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220225155936.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h3 id=&#34;flatview&#34;&gt;FlatView&lt;/h3&gt;
&lt;p&gt;AddressSpace 的 root 域及其子树共同构成了 Guest 的物理地址空间，但这些都是在 QEMU 侧定义的。要传入 KVM 进行设置时，复杂的树状结构是不利于内核进行处理的，因此需要将其&lt;strong&gt;转换为一个“平坦”的地址模&lt;/strong&gt;型，也就是一个从零开始、只包含地址信息的数据结构，这在 QEMU 中通过 &lt;strong&gt;FlatView&lt;/strong&gt; 来表示。每个 AddressSpace 都有一个与之对应的 FlatView 指针 current_map，表示其对应的平面展开视图。&lt;/p&gt;
&lt;h4 id=&#34;结构体定义-3&#34;&gt;结构体定义&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;FlatView&lt;/code&gt; 在&lt;code&gt;memory.c&lt;/code&gt;中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* Flattened global view of current active memory hierarchy.  Kept in sorted
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * order.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FlatView&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rcu_head&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rcu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FlatRange&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ranges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// 对应的 FlatRange 数组
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// FlatRange 的数目
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nr_allocated&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 当前数组的项数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AddressSpaceDispatch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dispatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中，&lt;code&gt;ranges&lt;/code&gt;是一个数组，记录了 &lt;code&gt;FlatView&lt;/code&gt; 下所有的 &lt;code&gt;FlatRange&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id=&#34;flatrange&#34;&gt;FlatRange&lt;/h4&gt;
&lt;p&gt;在 &lt;code&gt;FlatView&lt;/code&gt; 中，&lt;code&gt;FlatRange&lt;/code&gt; 表示在 &lt;code&gt;FlatView&lt;/code&gt; 中的一段内存范围，同样在&lt;code&gt;memory.c&lt;/code&gt;中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* Range of memory in the global map.  Addresses are absolute. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FlatRange&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;           &lt;span class=&#34;c1&#34;&gt;// 指向所属的 MemoryRegion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;hwaddr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset_in_region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 在全局 MemoryRegion 中的 offset，对应 GPA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;AddrRange&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;             &lt;span class=&#34;c1&#34;&gt;// 代表的地址区间，对应 HVA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirty_log_mask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;romd_mode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readonly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nonvolatile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个 &lt;code&gt;FlatRange&lt;/code&gt; 对应一段虚拟机物理地址区间，各个 &lt;code&gt;FlatRange&lt;/code&gt; 不会重叠，&lt;strong&gt;按照地址的顺序保存在数组中&lt;/strong&gt;，具体的地址范围由一个 &lt;code&gt;AddrRange&lt;/code&gt; 结构来描述：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * AddrRange 用于表示 FlatRange 的起始地址及大小
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AddrRange&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Int128&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Int128&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;memoryregionsection&#34;&gt;MemoryRegionSection&lt;/h3&gt;
&lt;h4 id=&#34;结构体定义-4&#34;&gt;结构体定义&lt;/h4&gt;
&lt;p&gt;在 &lt;code&gt;QEMU&lt;/code&gt; 中，还有几个起到中介作用的结构体，&lt;code&gt;MemoryRegionSection&lt;/code&gt; 就是其中之一。&lt;/p&gt;
&lt;p&gt;之前介绍的 &lt;code&gt;FlatRange&lt;/code&gt; 代表一个物理地址空间的片段，偏向于描述在 &lt;code&gt;Host&lt;/code&gt; 侧即 &lt;strong&gt;AddressSpace 中的分布【Guest 的物理空间】&lt;/strong&gt;，而 &lt;code&gt;MemoryRegionSection&lt;/code&gt; 则代表在 &lt;code&gt;Guest&lt;/code&gt; 侧即 &lt;strong&gt;MemoryRegion 中的片段&lt;/strong&gt;。&lt;code&gt;MemoryRegionSection&lt;/code&gt; 在&lt;code&gt;memory.h&lt;/code&gt;中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * MemoryRegionSection: describes a fragment of a #MemoryRegion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @mr: the region, or %NULL if empty
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @address_space: the address space the region is mapped in
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @offset_within_region: the beginning of the section, relative to @mr&amp;#39;s start
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @size: the size of the section; will not exceed @mr&amp;#39;s boundaries
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @offset_within_address_space: the address of the first byte of the section
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *     relative to the region&amp;#39;s address space
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * @readonly: writes to this section are ignored
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;c1&#34;&gt;//只是起到描述的作用，描述了是哪个 AddressSpace 的 MemoryRegion，
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;c1&#34;&gt;//并且在 MemoryRegion 中的 offset，和在 AddressSpace 展开为平坦内存的 offset
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                               &lt;span class=&#34;c1&#34;&gt;// 所属的 MemoryRegion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_space&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                    &lt;span class=&#34;c1&#34;&gt;// 关联的 AddressSpace
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;target_phys_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset_within_region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// 在 MemoryRegion 内部的 offset
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                                  &lt;span class=&#34;c1&#34;&gt;// Section 的大小
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;target_phys_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset_within_address_space&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 在 AddressSpace 内部的 offset
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;readonly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;                                  &lt;span class=&#34;c1&#34;&gt;// 是否为只读
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;offset_within_region&lt;/code&gt;：在所属 &lt;code&gt;MemoryRegion&lt;/code&gt; 中的&lt;code&gt;offset&lt;/code&gt;。一个&lt;code&gt;AddressSpace&lt;/code&gt; 可能由多个 &lt;code&gt;MemoryRegion&lt;/code&gt; 组成，因此该 &lt;code&gt;offset&lt;/code&gt; 是局部的&lt;/li&gt;
&lt;li&gt;&lt;code&gt;offset_within_address_space&lt;/code&gt;：在所属 &lt;code&gt;AddressSpace&lt;/code&gt; 中的 &lt;code&gt;offset&lt;/code&gt;，它是全局的&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;和其他数据结构之间的关系&#34;&gt;和其他数据结构之间的关系&lt;/h4&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125110422.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125110422.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AddressSpace&lt;/code&gt; 的&lt;code&gt;root&lt;/code&gt;指向对应的根级&lt;code&gt;MemoryRegion&lt;/code&gt;，&lt;code&gt;current_map&lt;/code&gt;指向&lt;code&gt;AddressSpace&lt;/code&gt; 的&lt;code&gt;root&lt;/code&gt;通过&lt;code&gt;generate_memory_topology()&lt;/code&gt;生成的 &lt;code&gt;FlatView&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FlatView&lt;/code&gt; 中的&lt;code&gt;ranges&lt;/code&gt;数组表示该&lt;code&gt;MemoryRegion&lt;/code&gt; 所表示的&lt;code&gt;Guest&lt;/code&gt;地址区间【GPA 的整个平坦物理空间】，并按照地址的顺序进行排列&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MemoryRegionSection&lt;/code&gt; 由&lt;code&gt;ranges&lt;/code&gt;数组中的 &lt;code&gt;FlatRange&lt;/code&gt; 对应生成，作为注册到 &lt;code&gt;KVM&lt;/code&gt;中的基本单位&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;code&gt;QEMU&lt;/code&gt; 在用户空间申请内存后，需要将内存信息通过一系列系统调用传入内核空间的 &lt;code&gt;KVM&lt;/code&gt;，由 &lt;code&gt;KVM&lt;/code&gt; 侧进行管理，因此 &lt;code&gt;QEMU&lt;/code&gt; 侧也定义了一些用于向 &lt;code&gt;KVM&lt;/code&gt; 传递参数的结构体。&lt;/p&gt;
&lt;p&gt;以下为&lt;code&gt;KVM&lt;/code&gt;相关的数据结构。&lt;/p&gt;
&lt;h3 id=&#34;kvmslot&#34;&gt;KVMSlot&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;在 kvm_init.h&lt;/code&gt;中定义，是 &lt;code&gt;KVM&lt;/code&gt; 中内存管理的基本单位：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;typedef&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KVMSlot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;hwaddr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Guest 物理地址，GPA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memory_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// 内存大小
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// QEMU 用户空间地址，HVA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// Slot 编号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 标志位，例如是否追踪脏页、是否可用等
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Dirty bitmap cache for the slot */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirty_bmap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirty_bmap_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Cache of the address space ID */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;as_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Cache of the offset in ram address space */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram_start_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KVMSlot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;KVMSlot&lt;/code&gt; 类似于内存插槽的概念。&lt;/p&gt;
&lt;h3 id=&#34;kvm_userspace_memory_region&#34;&gt;kvm_userspace_memory_region&lt;/h3&gt;
&lt;p&gt;调用&lt;code&gt;ioctl(KVM_SET_USER_MEMORY_REGION)&lt;/code&gt;时需要向 &lt;code&gt;KVM&lt;/code&gt; 传递的参数，在&lt;code&gt;kvm.h&lt;/code&gt;中定义&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* for KVM_SET_USER_MEMORY_REGION */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_userspace_memory_region&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// slot 编号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;           &lt;span class=&#34;c1&#34;&gt;// 标志位，例如是否追踪脏页、是否可用等
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;guest_phys_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Guest 物理地址，GPA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memory_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// 内存大小，bytes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;userspace_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 从 QEMU 进程空间分配的起始地址，HVA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;memorylistener&#34;&gt;MemoryListener&lt;/h3&gt;
&lt;h4 id=&#34;结构体定义-5&#34;&gt;结构体定义&lt;/h4&gt;
&lt;p&gt;为了监控虚拟机的物理地址访问，对于每一个 &lt;code&gt;AddressSpace&lt;/code&gt;，都会有一个 &lt;code&gt;MemoryListener&lt;/code&gt; 与之对应。每当物理映射&lt;code&gt;GPA-&amp;gt;HVA&lt;/code&gt;发生改变时，就会回调这些函数。&lt;strong&gt;MemoryListener&lt;/strong&gt; 是对一些事件的&lt;strong&gt;回调函数合集&lt;/strong&gt;，在&lt;code&gt;memory.h&lt;/code&gt;中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * MemoryListener: callbacks structure for updates to the physical memory map
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Allows a component to adjust to changes in the guest-visible memory map.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; * Use with memory_listener_register() and memory_listener_unregister().
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;begin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;commit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region_del&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region_nop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_stop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_global_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_global_stop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;eventfd_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;match_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EventNotifier&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;eventfd_del&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;match_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EventNotifier&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Lower = earlier (during add), later (during del) */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_space_filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_ENTRY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;全局变量-memory_listeners&#34;&gt;全局变量 memory_listeners&lt;/h4&gt;
&lt;p&gt;所有的 &lt;code&gt;MemoryListener&lt;/code&gt; 都会挂在全局变量&lt;code&gt;memory_listeners&lt;/code&gt;链表上，在&lt;code&gt;memory.c&lt;/code&gt;中定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;QTAILQ_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memory_listeners&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;QTAILQ_HEAD_INITIALIZER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在&lt;code&gt;memory.c&lt;/code&gt;中枚举了&lt;code&gt;ListenerDirection&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ListenerDirection&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Forward&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Reverse&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;重要数据结构总览&#34;&gt;重要数据结构总览&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;结构体名&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;说明&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;AddressSpace&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;VM 能看到的一段地址空间，偏向 Host 侧【注意指的是偏向】&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;MemoryRegion&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;地址空间中一段逻辑层面的内存区域，偏向 Guest 侧&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;RAMBlock&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;记录实际分配的内存地址信息，存储了 GPA-&amp;gt;HVA 的映射关系&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;FlatView&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;MemoryRegion 对应的平面展开视图，包含一个 FlatRange 类型的 ranges 数组&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;FlatRange&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;对应一段虚拟机物理地址区间，各个 FlatRange 不会重叠，按照地址的顺序保存在数组中&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;MemoryRegionSection&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;表示 MemoryRegion 中的片段&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;MemoryListener&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;回调函数集合&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;KVMSlot&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;KVM 中内存管理的基本单位，表示一个内存插槽&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;kvm_userspace_memory_region&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;调用 ioctl(KVM_SET_USER_MEMORY_REGION) 时需要向 KVM 传递的参数&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;具体实现机制&#34;&gt;具体实现机制&lt;/h2&gt;
&lt;p&gt;QEMU 的内存申请流程大致可分为三个部分：回调函数的注册、AddressSpace 的初始化、实际内存的分配。下面将根据在 vl.c 的 main() 函数中的调用顺序分别介绍。&lt;/p&gt;
&lt;h3 id=&#34;回调函数的注册&#34;&gt;回调函数的注册&lt;/h3&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125133808.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125133808.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;configure_accelerator&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;                                     &lt;span class=&#34;c1&#34;&gt;// 初始化 KVM
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KVM_CREATE_VM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;                  &lt;span class=&#34;c1&#34;&gt;// 创建 VM
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_arch_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;                           &lt;span class=&#34;c1&#34;&gt;// 针对不同的架构进行初始化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_listener_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;               &lt;span class=&#34;c1&#34;&gt;// 注册 kvm_memory_listener
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;listener_add_address_space&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 调用 region_add 回调
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_region_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// region_add 对应的回调实现
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                           &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_set_phys_mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 根据传入的 section 填充 KVMSlot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_set_user_memory_region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                     &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KVM_SET_USER_MEMORY_REGION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;进入&lt;code&gt;configure_accelerator()&lt;/code&gt;后，&lt;code&gt;QEMU&lt;/code&gt;会先调用&lt;code&gt;configure_accelerator()&lt;/code&gt;设置 &lt;code&gt;KVM&lt;/code&gt; 的加速支持，之后进入&lt;code&gt;kvm_init()&lt;/code&gt;。该函数主要完成对 KVM 的初始化，包括一些常规检查如 &lt;code&gt;CPU&lt;/code&gt; 个数、&lt;code&gt;KVM&lt;/code&gt; 版本等，之后通过&lt;code&gt;kvm_ioctl(KVM_CREATE_VM)&lt;/code&gt;与内核交互，创建 &lt;code&gt;KVM&lt;/code&gt; 虚拟机。在&lt;code&gt;kvm_init()&lt;/code&gt;的最后，会调用&lt;code&gt;memory_listener_register()&lt;/code&gt;注册&lt;code&gt;kvm_memory_listener&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MachineState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ms&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;MACHINE_GET_CLASS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ms&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 打开/dev/kvm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_open_old&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/dev/kvm&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;O_RDWR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 创建 VM
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KVM_CREATE_VM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EINTR&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_arch_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 针对不同的架构进行初始化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 对于以下 AddressSpace，设置其对应的 listener
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;kvm_memory_listener_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                 &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_space_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;kvm-memory&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_listener_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kvm_coalesced_pio_listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                             &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_space_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_listener_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;other&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Only one of them can be defined for a listener */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_sync&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_sync_global&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_space&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;QTAILQ_EMPTY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;QTAILQ_LAST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;QTAILQ_INSERT_TAIL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;QTAILQ_FOREACH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;other&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;other&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;QTAILQ_INSERT_BEFORE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;other&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;QTAILQ_EMPTY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;QTAILQ_LAST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;QTAILQ_INSERT_TAIL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link_as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;QTAILQ_FOREACH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;other&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link_as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;other&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;priority&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;QTAILQ_INSERT_BEFORE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;other&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;link_as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;listener_add_address_space&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后的&lt;code&gt;listener_add_address_space()&lt;/code&gt;主要是将 listener 注册到其对应的 &lt;code&gt;AddressSpace&lt;/code&gt; 上，并根据 &lt;code&gt;AddressSpace&lt;/code&gt; 对应的 &lt;code&gt;FlatRange&lt;/code&gt; 数组，生成 &lt;code&gt;MemoryRegionSection&lt;/code&gt;【&lt;code&gt;MemoryRegionSection&lt;/code&gt;就像是为&lt;code&gt;FlatRange&lt;/code&gt;数组设置的一种中介表示，便于传入&lt;code&gt;KVM&lt;/code&gt;，因为传入&lt;code&gt;KVM&lt;/code&gt;应该是对平坦内存的一种表示】，并注册到 &lt;code&gt;KVM&lt;/code&gt; 中：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;listener_add_address_space&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                       &lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FlatView&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FlatRange&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;begin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;begin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* 开启内存脏页记录 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;global_dirty_tracking&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_global_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;log_global_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* 遍历 AddressSpace 对应的 FlatRange 数组，并将其转换成 MemoryRegionSection */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;view&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_get_flatview&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;FOR_EACH_FLAT_RANGE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;MemoryRegionSection&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;section&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;section_from_flat_range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;cm&#34;&gt;/* 将 section 所代表的内存区域注册到 KVM 中 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;region_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;region_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirty_log_mask&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;log_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;log_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;section&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirty_log_mask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;commit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;commit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;flatview_unref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;由于此时 &lt;code&gt;AddressSapce&lt;/code&gt; 尚未初始化，所以此处的循环为空，仅是在全局注册了&lt;code&gt;kvm_memory_listener&lt;/code&gt;。最后调用了&lt;code&gt;kvm_memory_listener-&amp;gt;region_add()&lt;/code&gt;，对应的实现是&lt;code&gt;kvm_region_add()&lt;/code&gt;，该函数最终会通过&lt;code&gt;ioctl(KVM_SET_USER_MEMORY_REGION)&lt;/code&gt;，将 &lt;code&gt;QEMU&lt;/code&gt; 侧申请的内存信息传入 &lt;code&gt;KVM&lt;/code&gt; 进行注册，这里的流程会在下一部分进行分析。&lt;/p&gt;
&lt;h3 id=&#34;addressspace-的初始化&#34;&gt;AddressSpace 的初始化&lt;/h3&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125154841.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125154841.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cpu_exec_init_all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_map_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 初始化 system_memory/io 这两个全局 MemoryRegion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;set_system_memory_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// address_space_memory-&amp;gt;root = system_memory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_update_topology&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// 为 MemoryRegion 生成 FlatView
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;         &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_update_topology&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// as-&amp;gt;current_map = new_view
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;              &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_update_topology_pass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;                   &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_region_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// region_add 对应的回调实现
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;                        &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_set_phys_mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 根据传入的 section 填充 KVMSlot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;                             &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_set_user_memory_region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;                                  &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KVM_SET_USER_MEMORY_REGION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_listener_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 注册对应的 MemoryListener
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;         &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;listener_add_address_space&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;io_mem_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// ram/rom/unassigned/notdirty/subpage-ram/watch
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第一部分在全局注册了&lt;code&gt;kvm_memory_listener&lt;/code&gt;，但由于&lt;code&gt;AddressSpace&lt;/code&gt; 尚未初始化，实际上并未向 &lt;code&gt;KVM&lt;/code&gt; 中注册任何实际的内存信息。&lt;code&gt;QEMU&lt;/code&gt; 在&lt;code&gt;main()&lt;/code&gt;函数中会继续调用&lt;code&gt;cpu_exec_init_all()&lt;/code&gt;对&lt;code&gt;AddressSpace&lt;/code&gt;进行初始化，该函数实际上是对两个 &lt;code&gt;init&lt;/code&gt; 函数的封装调用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;cpu_exec_init_all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;qemu_mutex_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mutex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* The data structures we set up here depend on knowing the page size,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * so no more changes can be made after this point.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * In an ideal world, nothing we did before we had finished the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * machine setup would care about the target page size, and we could
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * do this much later, rather than requiring board models to state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * up front what their requirements are.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;finalize_target_page_bits&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;io_mem_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// 初始化六个I/O MemoryRegion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_map_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 初始化两个全局 AddressSpace，以及对应的 MemoryRegion、FlatView
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;qemu_mutex_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map_client_list_lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;先来看&lt;code&gt;memory_map_init()&lt;/code&gt;，主要用来初始化两个全局的系统地址空间&lt;code&gt;system_memory&lt;/code&gt;、&lt;code&gt;system_io&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_map_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 1. 初始化 system_memory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UINT64_MAX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 2. 设置 address_space_memory 关联 system_memory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 这两个都是全局变量，也就是把内存地址空间和 IO 地址空间于对应的 MemoryRegion 联系起来 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;//及其对应的 FlatView
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;address_space_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_space_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;memory&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;system_io&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 1. 初始化 system_io  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unassigned_io_ops&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;io&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                          &lt;span class=&#34;mi&#34;&gt;65536&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 2. 设置 address_space_io 关联 system_io 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 及其对应的 FlatView
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;address_space_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_space_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;system_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;I/O&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里比较重要的是&lt;code&gt;address_space_init()&lt;/code&gt;，先设置 &lt;code&gt;AddressSpace&lt;/code&gt; 对应的 &lt;code&gt;MemoryRegion&lt;/code&gt;，之后根据&lt;code&gt;system_memory&lt;/code&gt;更新&lt;code&gt;address_space_memory&lt;/code&gt;对应的 &lt;code&gt;FlatView&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_ref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 将 address_space_memory 的 root 域指向 system_memory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_map&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ioeventfd_nb&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ioeventfds&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_INIT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_INSERT_TAIL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_spaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;address_spaces_link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_strdup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;anonymous&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 根据 system_memory 更新 address_space_memory 对应的 FlatView
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;address_space_update_topology&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;address_space_update_ioeventfds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;address_space_update_topology()&lt;/code&gt;会继续调用&lt;code&gt;generate_memory_topology()&lt;/code&gt;生成 &lt;code&gt;AddressSpace&lt;/code&gt; 对应的 &lt;code&gt;FlatView&lt;/code&gt;视图：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_update_topology&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;physmr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_get_flatview_root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;flatviews_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;g_hash_table_lookup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flat_views&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;physmr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;generate_memory_topology&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;physmr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;address_space_set_flatview&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;address_space_update_topology()&lt;/code&gt;会先调用&lt;code&gt;generate_memory_topology()&lt;/code&gt;生成&lt;code&gt;system_memory&lt;/code&gt;更新后的视图&lt;code&gt;new_view&lt;/code&gt;，再将&lt;code&gt;address_space_memory&lt;/code&gt;的&lt;code&gt;current_map&lt;/code&gt;指向这个&lt;code&gt;new_view&lt;/code&gt;，最后销毁&lt;code&gt;old_view&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_set_flatview&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AddressSpace&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FlatView&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_to_flatview&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;physmr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_get_flatview_root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;FlatView&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_view&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_hash_table_lookup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flat_views&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;physmr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;flatview_ref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;flatview_ref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;QTAILQ_EMPTY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;listeners&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;FlatView&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tmpview&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;old_view2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tmpview&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;address_space_update_topology_pass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;old_view2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;address_space_update_topology_pass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;old_view2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Writes are protected by the BQL.  */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;qatomic_rcu_set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;flatview_unref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Note that all the old MemoryRegions are still alive up to this
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * point.  This relieves most MemoryListeners from the need to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * ref/unref the MemoryRegions they get---unless they use them
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * outside the iothread mutex, in which case precise reference
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * counting is necessary.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;flatview_unref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_view&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在&lt;code&gt;address_space_update_topology_pass()&lt;/code&gt;的最后，会调用&lt;code&gt;MEMORY_LISTENER_UPDATE_REGION&lt;/code&gt;这个宏，触发&lt;code&gt;region_add&lt;/code&gt;对应的回调函数&lt;code&gt;kvm_region_add()&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这个宏在&lt;code&gt;memory.c&lt;/code&gt;中定义，会将 &lt;code&gt;FlatView&lt;/code&gt; 中的 &lt;code&gt;FlatRange&lt;/code&gt; 转换为 &lt;code&gt;MemoryRegionSection&lt;/code&gt;，作为入参传递给&lt;code&gt;kvm_region_add()&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* No need to ref/unref .mr, the FlatRange keeps it alive.  */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...)  \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    do {                                                                \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        MemoryRegionSection mrs = section_from_flat_range(fr,           \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;                address_space_to_flatview(as));                         \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;        MEMORY_LISTENER_CALL(as, callback, dir, &amp;amp;mrs, ##_args);         \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    } while(0)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而&lt;code&gt;kvm_region_add()&lt;/code&gt;实际上是对&lt;code&gt;kvm_set_phys_mem()&lt;/code&gt;的封装调用。该函数比较复杂，会根据传入的&lt;code&gt;section&lt;/code&gt;填充 KVMSlot，再传递给&lt;code&gt;kvm_set_user_memory_region()&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_set_user_memory_region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KVMMemoryListener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kml&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KVMSlot&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;KVMState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_userspace_memory_region&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 根据 KVMSlot 填充 kvm_userspace_memory_region
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kml&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;guest_phys_addr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userspace_addr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;long&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;^&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KVM_MEM_READONLY&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;cm&#34;&gt;/* Set the slot size to 0 before setting the slot to the desired
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;         * value. This is needed based on KVM commit 75d61fbc. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_vm_ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KVM_SET_USER_MEMORY_REGION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;goto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;memory_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_vm_ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;KVM_SET_USER_MEMORY_REGION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;slot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;old_flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到这里又将 &lt;code&gt;KVMSlot&lt;/code&gt; 转换为 &lt;code&gt;kvm_userspace_memory_region&lt;/code&gt;，作为&lt;code&gt;ioctl()&lt;/code&gt;的参数，交给内核中的 &lt;code&gt;KVM&lt;/code&gt; 进行内存的注册【设置&lt;code&gt;GPA-&amp;gt;HVA&lt;/code&gt;的映射关系，在内核空间维护并管理 Guest 的内存】。&lt;/p&gt;
&lt;p&gt;至此 QEMU 侧负责管理内存的数据结构均已完成初始化，&lt;strong&gt;可以参考下面的图片了解各数据结构之间的对应关系&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127155506.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127155506.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h3 id=&#34;实际内存的分配&#34;&gt;实际内存的分配&lt;/h3&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127160121.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127160121.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;machine&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;pc_init_pci&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 初始化虚拟机
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;pc_init1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;system_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pci_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;pci&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// pci_memory, rom_memory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                 &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;pc_memory_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 初始化内存，分配实际的物理内存地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init_ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 创建 pc.ram, pc.rom 并分配内存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_ram_alloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;         &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_ram_alloc_from_ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;vmstate_register_ram_global&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 将 MR 的 name 写入 RAMBlock 的 idstr
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;    &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;vmstate_register_ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;         &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_ram_set_idstr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;err&#34;&gt;├─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init_alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 初始化 ram_below_4g, ram_above_4g
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_add_subregion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 在 system_memory 中添加 subregions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                           &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_add_subregion_common&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_update_topology&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 为 MemoryRegion 生成 FlatView
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                     &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_update_topology&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// as-&amp;gt;current_map = new_view
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                          &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;address_space_update_topology_pass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                               &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_region_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// region_add 对应的回调实现
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                    &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_set_phys_mem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 根据传入的 section 填充 KVMSlot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                         &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_set_user_memory_region&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                              &lt;span class=&#34;err&#34;&gt;└─&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;ioctl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KVM_SET_USER_MEMORY_REGION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;之前的回调函数注册、AddressSpace 的初始化，实际上均没有对应的物理内存。【实际的内存是在 RAMBlock 中】&lt;/p&gt;
&lt;p&gt;我们再回到 &lt;code&gt;qemu&lt;/code&gt; 启动的 &lt;code&gt;main&lt;/code&gt; 函数中。接下来的初始化过程会调用 &lt;code&gt;pc_init1&lt;/code&gt;。在这里面，对于 &lt;code&gt;CPU&lt;/code&gt; 虚拟化，我们会调用 &lt;code&gt;pc_cpus_init&lt;/code&gt;。另外，&lt;code&gt;pc_init1&lt;/code&gt; 还会调用&lt;code&gt;pc_memory_init&lt;/code&gt;，进行内存的虚拟化。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;pc_memory_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kernel_filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kernel_cmdline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;initrd_filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;below_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;above_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rom_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;option_rom_mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;         &lt;span class=&#34;c1&#34;&gt;// 两个实体 MR: pc.ram, pc.rom
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_above_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 两个别名 MR: ram_below_4g, ram_above_4g
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* Allocate RAM.  We allocate it as a single memory region and use
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * aliases to address portions of it, mostly for backwards compatibility
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     * with older qemus that used qemu_ram_alloc().
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 创建 ram
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 分配具体的内存（实际上会创建一个 RAMBlock 并将其 offset 值写入 ram.ram_addr，对应 GPA）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init_ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;pc.ram&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;below_4g_mem_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;above_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 将 MR 的 name 写入 RAMBlock 的 idstr
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;vmstate_register_ram_global&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_memory&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 创建 ram_below_4g 表示 4G 以下的内存
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init_alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;ram-below-4g&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;below_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 将 ram_below_4g 挂在 system_memory 下
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_add_subregion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram_below_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;above_4g_mem_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;ram_above_4g&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_above_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;memory_region_init_alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_above_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;ram-above-4g&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;below_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;above_4g_mem_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;memory_region_add_subregion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x100000000ULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ram_above_4g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;cm&#34;&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的重点在于&lt;code&gt;memory_region_init_ram()&lt;/code&gt;，它通过&lt;code&gt;qemu_ram_alloc()&lt;/code&gt;获取 ram 这个 &lt;code&gt;MemoryRegion&lt;/code&gt; 对应的 &lt;code&gt;RAMBlock&lt;/code&gt; 的&lt;code&gt;offset&lt;/code&gt;，并存入&lt;code&gt;ram.ram_addr&lt;/code&gt;，这样就可以在&lt;code&gt;ram_list&lt;/code&gt;中根据该字段查找 &lt;code&gt;MR&lt;/code&gt; 对应的 &lt;code&gt;RAMBlock&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;memory_region_init_ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 填充字段，初始化默认值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 表示为 RAM
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;terminates&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 表示为实体 MemoryRegion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;destructor&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memory_region_destructor_ram&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_addr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_ram_alloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 这里保存 RAMBlock 的 offset，即 GPA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而 qemu_ram_alloc() 最终会调用 qemu_ram_alloc_from_ptr()，创建一个对应大小 RAMBlock 并分配内存，返回对应的 GPA 地址存入 mr-&amp;gt;ram_addr 中：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_ram_alloc_from_ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;ram_addr_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                   &lt;span class=&#34;n&#34;&gt;MemoryRegion&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;RAMBlock&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 创建一个 RAMBlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;TARGET_PAGE_ALIGN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 页对齐
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 初始化 new_block
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 将 new_block-&amp;gt; 指向入参的 MemoryRegion
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_ram_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 从 ram_list 中的 RAMBlock 之间找到一段可以满足 size 需求的 gap，并返回起始地址的 offset，对应 GPA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 新建的 RAMBlock host 字段为空，跳过
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;flags&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RAM_PREALLOC_MASK&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mem_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 未指定 mem_path
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#if defined (__linux__) &amp;amp;&amp;amp; !defined(TARGET_S390X)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;file_ram_alloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mem_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_vmalloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;qemu_madvise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QEMU_MADV_MERGEABLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;fprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-mem-path option unsupported&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#endif
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;xen_enabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;xen_ram_alloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;kvm_enabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 从这里继续
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;cm&#34;&gt;/* some s390/kvm configurations have special constraints */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_vmalloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 实际上还是调用 qemu_vmalloc(size)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_vmalloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 从 QEMU 的线性空间中分配 size 大小的内存，返回 HVA
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;qemu_madvise&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;QEMU_MADV_MERGEABLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;length&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 将 length 设置为 size
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QLIST_INSERT_HEAD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blocks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 将该 RAMBlock 插入 ram_list 头部
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ram_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;phys_dirty&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_realloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;phys_dirty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 重新分配 ram_list.phys_dirty 的内存空间
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                       &lt;span class=&#34;nf&#34;&gt;last_ram_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TARGET_PAGE_BITS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ram_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;phys_dirty&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TARGET_PAGE_BITS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TARGET_PAGE_BITS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;cpu_physical_memory_set_dirty_range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0xff&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 对该 RAMBlock 对应的内存标记为 dirty
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;qemu_ram_setup_dump&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;kvm_enabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;kvm_setup_guest_memory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;new_block&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样一来&lt;code&gt;ram&lt;/code&gt;【其实就是&lt;code&gt;system memory&lt;/code&gt;，整个&lt;code&gt;Guest&lt;/code&gt;物理空间的大小】对应的 &lt;code&gt;RAMBlock&lt;/code&gt; 中就分配好了 &lt;code&gt;GPA&lt;/code&gt; 和 &lt;code&gt;HVA&lt;/code&gt;，就可以&lt;strong&gt;将内存信息同步至 KVM 侧&lt;/strong&gt;了。&lt;/p&gt;
&lt;p&gt;最后回到&lt;code&gt;pc_memory_init()&lt;/code&gt;中，在分配完实际内存后，会先调用&lt;code&gt;memory_region_init_alias()&lt;/code&gt;初始化&lt;code&gt;ram_below_4g&lt;/code&gt;、&lt;code&gt;ram_above_4g&lt;/code&gt;这两个&lt;code&gt;alias&lt;/code&gt;，之后调用&lt;code&gt;memory_region_add_subregion()&lt;/code&gt;将这两个 &lt;code&gt;alias&lt;/code&gt; 指向&lt;code&gt;ram&lt;/code&gt;这个实体 &lt;code&gt;MemoryRegion&lt;/code&gt;。如下图，该函数最终会触发&lt;code&gt;kvm_region_add()&lt;/code&gt;回调，将实际的内存信息传入 &lt;code&gt;KVM&lt;/code&gt; 注册。该过程如下图所示，与之前分析的流程相同，此处不再赘述。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127163205.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127163205.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;虚拟机的内存管理也是需要用户态的 &lt;code&gt;qemu&lt;/code&gt; 和内核态的 &lt;code&gt;KVM&lt;/code&gt; 共同完成。为了加速内存映射，需要借助硬件的 &lt;code&gt;EPT&lt;/code&gt; 技术。&lt;/p&gt;
&lt;h3 id=&#34;qemu-侧&#34;&gt;QEMU 侧&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;创建一系列 &lt;code&gt;MemoryRegion&lt;/code&gt;，分别表示 &lt;code&gt;Guest&lt;/code&gt; 中的 &lt;code&gt;RAM&lt;/code&gt;、&lt;code&gt;ROM&lt;/code&gt; 等区域。&lt;code&gt;MemoryRegion&lt;/code&gt;之间通过 &lt;code&gt;alias&lt;/code&gt; 或 &lt;code&gt;subregions&lt;/code&gt; 的方式维护相互之间的关系，从而进一步细化区域的定义&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于一个实体 &lt;code&gt;MemoryRegion&lt;/code&gt;（非 &lt;code&gt;alias&lt;/code&gt;），在初始化内存的过程中 &lt;code&gt;QEMU&lt;/code&gt; 会创建它所对应的 &lt;code&gt;RAMBlock&lt;/code&gt;。该 &lt;code&gt;RAMBlock&lt;/code&gt; 通过调用&lt;code&gt;qemu_ram_alloc_from_ptr()&lt;/code&gt;从 &lt;code&gt;QEMU&lt;/code&gt; 的进程地址空间中&lt;strong&gt;以 mmap 的方式分配内存&lt;/strong&gt;，并负责&lt;strong&gt;维护该 MemoryRegion 对应内存的起始 GPA/HVA/size 等相关信息&lt;/strong&gt;【在&lt;code&gt;qemu_ram_alloc_from_ptr&lt;/code&gt;中创建的新&lt;code&gt;RAMBlock&lt;/code&gt;有&lt;code&gt;offset&lt;/code&gt;、&lt;code&gt;host&lt;/code&gt;的赋值，即&lt;code&gt;GPA-&amp;gt;HVA&lt;/code&gt;的对应关系】&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;AddressSpace&lt;/code&gt; 表示 &lt;code&gt;Guest&lt;/code&gt; 的物理地址空间。如果 &lt;code&gt;AddressSpace&lt;/code&gt; 中的 &lt;code&gt;MemoryRegion&lt;/code&gt; 发生变化，则注册的 &lt;code&gt;listener&lt;/code&gt; 会被触发，将所属的 &lt;code&gt;MemoryRegion&lt;/code&gt; 树展开生成一维的 &lt;code&gt;FlatView&lt;/code&gt;，比较 FlatRange 是否发生了变化。如果是，则调用相应的方法对 &lt;code&gt;MemoryRegionSection&lt;/code&gt; 进行检查，更新 &lt;code&gt;QEMU&lt;/code&gt; 中的 &lt;code&gt;KVMSlot&lt;/code&gt;，同时填充&lt;code&gt;kvm_userspace_memory_region&lt;/code&gt;结构体，作为&lt;code&gt;ioctl()&lt;/code&gt;的参数更新 &lt;code&gt;KVM&lt;/code&gt; 中的&lt;code&gt;kvm_memory_slot&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;kvm-侧&#34;&gt;KVM 侧&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;当 &lt;code&gt;QEMU&lt;/code&gt; 通过&lt;code&gt;ioctl()&lt;/code&gt;创建 &lt;code&gt;vcpu&lt;/code&gt; 时，调用&lt;code&gt;kvm_mmu_create()&lt;/code&gt;初始化 &lt;code&gt;MMU&lt;/code&gt; 相关信息。
当 &lt;code&gt;KVM&lt;/code&gt; 要进入 &lt;code&gt;Guest&lt;/code&gt; 前，&lt;code&gt;vcpu_enter_guest()=&amp;gt;kvm_mmu_reload()&lt;/code&gt;会将根级页表地址加载到 &lt;code&gt;VMCS&lt;/code&gt;，让 &lt;code&gt;Guest&lt;/code&gt; 使用该页表&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当发生&lt;code&gt;EPT Violation&lt;/code&gt; 时，&lt;code&gt;VM-EXIT&lt;/code&gt;到 &lt;code&gt;KVM&lt;/code&gt; 中。如果是缺页，则根据 &lt;code&gt;GPA&lt;/code&gt; 算出 &lt;code&gt;gfn&lt;/code&gt;，再根据 &lt;code&gt;gfn&lt;/code&gt; 找到对应的 &lt;code&gt;KVMSlot&lt;/code&gt;，从中得到对应的 &lt;code&gt;HVA&lt;/code&gt;。然后根据 &lt;code&gt;HVA&lt;/code&gt; 算出对应的 &lt;code&gt;pfn&lt;/code&gt;，确保该 &lt;code&gt;Page&lt;/code&gt; 位于内存中。填好缺失的页之后，需要更新 &lt;code&gt;EPT&lt;/code&gt;，完善其中缺少的页表项，逐层补全页表&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127163418.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127163418.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;虚拟机的物理内存空间里面的页面当然不是一开始就映射到物理页面的，只有当虚拟机的内存被访问的时候，也即 &lt;code&gt;mmap&lt;/code&gt; 分配的虚拟内存空间被访问的时候，先查看 &lt;code&gt;EPT&lt;/code&gt; 页表，是否已经映射过，如果已经映射过，则经过四级页表映射，就能访问到物理页面。
如果没有映射过，则虚拟机会通过&lt;code&gt;VM-Exit&lt;/code&gt;指令回到宿主机模式，通过 &lt;code&gt;handle_ept_violation&lt;/code&gt; 补充页表映射。先是通过 &lt;code&gt;handle_mm_fault&lt;/code&gt;为虚拟机的物理内存空间分配真正的物理页面，然后通过 &lt;code&gt;__direct_map&lt;/code&gt; 添加 &lt;code&gt;EPT&lt;/code&gt; 页表映射。


&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127171031.jpg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127171031.jpg&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;http://xudaxian.fun/2020/09/03/QEMU%E5%86%85%E5%AD%98%E7%A9%BA%E9%97%B4%E8%99%9A%E6%8B%9F%E5%8C%96%E5%8F%8A%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/&#34;&gt;“QEMU 内存空间虚拟化及内存管理” - B10g | FΓom 许大仙&lt;/a&gt;
&lt;a href=&#34;https://www.cnblogs.com/LoyenWang/p/13943005.html&#34;&gt;【原创】Linux 虚拟化 KVM-Qemu 分析（五）之内存虚拟化 - LoyenWang - 博客园&lt;/a&gt;
&lt;a href=&#34;https://blog.csdn.net/xiongwenwu/article/details/58586013&#34;&gt;KVM/Qemu 工作原理系列目录_xiongwenwu 的专栏-CSDN 博客_qemu 目录结构&lt;/a&gt;
&lt;a href=&#34;https://abelsu7.top/2019/07/07/kvm-memory-virtualization/&#34;&gt;QEMU 内存虚拟化源码分析 | Keep Coding | 苏易北&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<blockquote>
<p>1.大部分转载自<a href="https://abelsu7.top/2019/07/07/kvm-memory-virtualization/">QEMU 内存虚拟化源码分析 | Keep Coding | 苏易北</a>
2.原文源码为 QEMU1.2.0，版本较旧，部分源码内容根据 QEMU6.2 版本修改
3.部分内容根据自己理解补充添加</p>
</blockquote>
<h2 id="概述">概述</h2>
<p>我们知道操作系统给每个进程分配虚拟内存，通过页表映射，变成物理内存进行访问。当有了虚拟机之后，情况会变得更加复杂。因为虚拟机对于物理机来讲是一个进程，但是虚拟机里面也有内核，也有虚拟机里面跑的进程。所以有了虚拟机，内存就变成了四类：</p>
<ul>
<li>虚拟机里面的虚拟内存（Guest OS Virtual Memory，GVA），这是虚拟机里面的进程看到的内存空间；</li>
<li>虚拟机里面的物理内存（Guest OS Physical Memory，GPA），这是虚拟机里面的操作系统看到的内存，它认为这是物理内存；</li>
<li>物理机的虚拟内存（Host Virtual Memory，HVA），这是物理机上的 qemu 进程看到的内存空间；</li>
<li>物理机的物理内存（Host Physical Memory，HPA），这是物理机上的操作系统看到的内存。</li>
</ul>
<p>内存虚拟化的关键在于维护 <code>GPA</code> 到 <code>HVA</code> 的映射关系。</p>
<h2 id="页面分配和映射的两种方式">页面分配和映射的两种方式</h2>
<p>要搞清楚 QEMU system emulation 的仿真架构，首先对于 Host OS，将 QEMU 作为进程启动，然后对于 QEMU 进程，会仿真各种硬件和运行 Guest OS，在这层 OS 上运行要全系统模拟的应用程序，因此对于 Guest OS 管理的内存要实现到 QEMU 进程的虚拟空间的转换需要 softMMU（即需要对 GPA 到 HVA 进行转换）。从 GVA 到 GPA 到 HVA 到 HPA，性能很差，为了解决这个问题，有两种主要的思路。</p>
<h3 id="影子页表-shadow-page-tablespt">影子页表 Shadow Page Table，SPT</h3>
<p>第一种方式就是软件的方式，影子页表（Shadow Page Table）。</p>
<p>KVM 通过维护记录 GVA-&gt;HPA 的影子页表 SPT，减少了地址转换带来的开销，可以直接将 GVA 转换为 HPA。</p>
<p>内存映射要通过页表来管理，页表地址应该放在 CR3 寄存器里面。在软件虚拟化的内存转换中，GVA 到 GPA 的转换通过查询 CR3 寄存器来完成，CR3 中保存了 Guest 的页表基地址，然后载入 MMU 中进行地址转换。</p>
<p>在加入了 SPT 技术后，当 Guest 访问 CR3 时，KVM 会捕获到这个操作 EXIT_REASON_CR_ACCESS，之后 KVM 会载入特殊的 CR3 和影子页表，欺骗 Guest 这就是真实的 CR3。之后就和传统的访问内存方式一致，当需要访问物理内存的时候，只会经过一层影子页表的转换。</p>
<blockquote>
<p>本来的过程是，客户机要通过 cr3 找到客户机的页表，实现从 GVA 到 GPA 的转换，然后在宿主机上，要通过 cr3 找到宿主机的页表，实现从 HVA 到 HPA 的转换。
为了实现客户机虚拟地址空间到宿主机物理地址空间的直接映射。客户机中每个进程都有自己的虚拟地址空间，所以 KVM 需要为客户机中的每个进程页表都要维护一套相应的影子页表。
在客户机访问内存时，使用的不是客户机的原来的页表，而是这个页表对应的影子页表，从而实现了从客户机虚拟地址到宿主机物理地址的直接转换。而且，在 TLB 和 CPU 缓存上缓存的是来自影子页表中客户机虚拟地址和宿主机物理地址之间的映射，也因此提高了缓存的效率。</p>
</blockquote>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220124192700.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220124192700.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>为了快速检索 Guest 页表对应的影子页表，KVM 为每个客户机维护了一个 hash 表来进行客户机页表到影子页表之间的映射。对于每一个 Guest 来说，其页目录和页表都有唯一的 GPA，通过页目录/页表的 GPA 就可以在哈希链表中快速地找到对应的影子页目录/页表。</p>
<p>当 Guest 切换进程时，Guest 会把待切换进程的页表基址载入 CR3，而 KVM 将会截获这一特权指令。KVM 在哈希表中找到与此页表基址对应的影子页表基址，载入 Guest CR3，使 Guest 在恢复运行时 CR3 实际指向的是新切换进程对应的影子页表。</p>
<p>影子页表的引入，减少了 GVA-&gt;HPA 的转换开销，但是缺点在于需要为 Guest 的每个进程都维护一个影子页表，这将带来很大的内存开销。同时影子页表的建立是很耗时的，如果 Guest 的进程过多，将导致影子页表频繁切换。</p>
<p>因此 Intel 和 AMD 在此基础上提供了基于硬件的虚拟化技术 EPT。</p>
<h3 id="扩展页表-extent-page-tableept">扩展页表 Extent Page Table，EPT</h3>
<p>Intel 的 EPT（Extent Page Table）技术和 AMD 的 NPT（Nest Page Table）技术都对内存虚拟化提供了硬件支持。这两种技术原理类似，都是在硬件层面上实现 GVA 到 HPA 之间的转换。下面就以 EPT 为例分析一下 KVM 基于硬件辅助的内存虚拟化实现。</p>
<p>EPT 在原有客户机页表对客户机虚拟地址 GVA 到客户机物理地址 GPA 映射的基础上，又引入了 EPT 页表来实现客户机物理地址 GPA 到宿主机物理地址 HPA 的另一次映射。客户机运行时，客户机页表被载入 CR3，而 EPT 页表被载入专门的 EPT 页表指针寄存器 EPTP。</p>
<p>即 EPT 技术采用了在两级页表结构，即原有 Guest OS 页表对 <strong>GVA-&gt;GPA</strong> 映射的基础上，又引入了 EPT 页表来实现 <strong>GPA-&gt;HPA</strong> 的另一次映射，这<strong>两次地址映射都是由硬件自动完成</strong>。</p>
<p>有了 EPT，在<strong>GPA-&gt;HPA</strong>转换的过程中，缺页会产生 EPT 缺页异常。KVM 首先根据引起异常的客户机物理地址，映射到对应的宿主机虚拟地址，然后为此虚拟地址分配新的物理页，最后 KVM 再更新 EPT 页表，建立起引起异常的客户机物理地址到宿主机物理地址之间的映射。</p>
<p>KVM 只需为每个客户机维护一套 EPT 页表，也大大减少了内存的开销。</p>
<p>这里，我们重点看第二种方式。因为使用了 EPT 之后，客户机里面的页表映射，也即从 GVA 到 GPA 的转换，还是用传统的方式，和在内存管理那一章讲的没有什么区别。而 EPT 重点帮我们解决的就是从 GPA 到 HPA 的转换问题。因为要经过两次页表，所以 EPT 又 tdp(two dimentional paging)。</p>
<p>EPT 的页表结构也是分为四层，EPT Pointer（EPTP）指向 PML4 的首地址。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220124194059.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220124194059.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h2 id="qemu-的主要工作">QEMU 的主要工作</h2>
<p>内存虚拟化的目的就是让虚拟机能够无缝的访问内存。有了 Intel EPT 的支持后，CPU 在 VMX non-root 状态时进行内存访问会再做一次 EPT 转换。在这个过程中，QEMU 会负责以下内容：</p>
<p>首先需要从自己的进程地址空间中申请内存用于 Guest
需要将上一步中申请到的内存的虚拟地址（HVA）和 Guest 的物理地址之间的映射关系传递给 KVM（kernel），即 GPA-&gt;HVA
需要组织一系列的数据结构来管理虚拟内存空间，并在内存拓扑结构更改时将最新的内存信息同步至 KVM 中</p>
<h2 id="qemu-和-kvm-的工作分界">QEMU 和 KVM 的工作分界</h2>
<p>QEMU 和 KVM 之间是通过 KVM 提供的 ioctl() 接口进行交互的。在内核的 kvm_vm_ioctl() 中，<strong>设置虚拟机内存</strong>的系统调用【kernel 就是一系列系统调用函数接口和处理逻辑，其中有个处理”创建/设置虚拟机内存“的系统调用接口】为  <code>VM_SET_USER_MEMORY_REGION</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">long</span> <span class="nf">kvm_vm_ioctl</span><span class="p">(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">filp</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">ioctl</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* ... */</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">KVM_SET_USER_MEMORY_REGION</span><span class="p">:</span> <span class="p">{</span> <span class="c1">// 在 KVM 中注册用户空间传入的内存信息
</span></span></span><span class="line"><span class="cl">        <span class="k">struct</span> <span class="n">kvm_userspace_memory_region</span> <span class="n">kvm_userspace_mem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">r</span> <span class="o">=</span> <span class="o">-</span><span class="n">EFAULT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">         <span class="c1">// 将传入的数据结构复制到内核空间
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nf">copy_from_user</span><span class="p">(</span><span class="o">&amp;</span><span class="n">kvm_userspace_mem</span><span class="p">,</span> <span class="n">argp</span><span class="p">,</span> <span class="k">sizeof</span> <span class="n">kvm_userspace_mem</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">goto</span> <span class="n">out</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">         <span class="c1">// 实际进行处理的函数
</span></span></span><span class="line"><span class="cl">        <span class="n">r</span> <span class="o">=</span> <span class="nf">kvm_vm_ioctl_set_memory_region</span><span class="p">(</span><span class="n">kvm</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">kvm_userspace_mem</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">goto</span> <span class="n">out</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* ... */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>可以看到这里需要传递的参数类型为 <code>kvm_userspace_memory_region</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* for KVM_SET_USER_MEMORY_REGION */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">kvm_userspace_memory_region</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">__u32</span> <span class="n">slot</span><span class="p">;</span>            <span class="c1">// slot 编号 [参考：https://www.cnblogs.com/LoyenWang/p/11922887.html]
</span></span></span><span class="line"><span class="cl">    <span class="n">__u32</span> <span class="n">flags</span><span class="p">;</span>           <span class="c1">// 标志位，例如是否追踪脏页、是否可用等
</span></span></span><span class="line"><span class="cl">    <span class="n">__u64</span> <span class="n">guest_phys_addr</span><span class="p">;</span> <span class="c1">// Guest 物理地址，即 GPA
</span></span></span><span class="line"><span class="cl">    <span class="n">__u64</span> <span class="n">memory_size</span><span class="p">;</span>     <span class="c1">// 内存大小，单位 bytes
</span></span></span><span class="line"><span class="cl">    <span class="n">__u64</span> <span class="n">userspace_addr</span><span class="p">;</span>  <span class="c1">// 从 QEMU 进程地址空间中分配内存的起始地址，即 HVA
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p><code>KVM_SET_USER_MEMORY_REGION</code>这个 <code>ioctl</code> 主要目的就是设置<code>GPA-&gt;HVA</code>的映射关系，KVM 会继续调用<code>kvm_vm_ioctl_set_memory_region()</code>，在内核空间维护并管理 Guest 的内存。</p>
<h2 id="相关数据结构">相关数据结构</h2>
<h3 id="addressspace">AddressSpace</h3>
<h4 id="结构体定义">结构体定义</h4>
<p>QEMU 用 AddressSpace 结构体表示 Guest 中 CPU/设备看到的内存【也就是Guest OS 可以在 QEMU 进程虚存中用到的所有内存，是 MemoryRegion 的集合，即 GPA 的整体】，类似于物理机中地址空间的概念，但在这里表示的是 Guest 的一段地址空间，如内存地址空间 <code>address_space_memory</code> 、<code>I/O</code> 地址空间<code>address_space_io</code>，它在 QEMU 源码<code>memory.c</code>中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">AddressSpace</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* private: */</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">rcu_head</span> <span class="n">rcu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">root</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Accessed via RCU.  */</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">FlatView</span> <span class="o">*</span><span class="n">current_map</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">ioeventfd_nb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">MemoryRegionIoeventfd</span> <span class="o">*</span><span class="n">ioeventfds</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_HEAD</span><span class="p">(,</span> <span class="n">MemoryListener</span><span class="p">)</span> <span class="n">listeners</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_ENTRY</span><span class="p">(</span><span class="n">AddressSpace</span><span class="p">)</span> <span class="n">address_spaces_link</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>每个 AddressSpace 一般包含一系列的 MemoryRegion：<code>root</code>指针指向根级 MemoryRegion，而 root 可能有自己的若干个 sub-regions（子节点），于是形成树状结构。这些 MemoryRegion 通过树连接起来，树的根即为 AddressSpace 的 root 域。</p>
<h4 id="全局变量">全局变量</h4>
<p>另外，QEMU 中有两个全局的静态 AddressSpace，在 memory.c 中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">AddressSpace</span> <span class="n">address_space_memory</span><span class="p">;</span> <span class="c1">// 内存地址空间
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">AddressSpace</span> <span class="n">address_space_io</span><span class="p">;</span>     <span class="c1">// I/O 地址空间
</span></span></span></code></pre></div><p>其 root 域分别指向之后会提到的两个 MemoryRegion 类型变量：system_memory、system_io。</p>
<h3 id="memoryregion">MemoryRegion</h3>
<h4 id="结构体定义-1">结构体定义</h4>
<p><code>MemoryRegion</code> 表示在 <code>Guest Memory Layout</code> 中的一段内存区域【也就是单元级 GPA 的概念，Guest OS 可以管理到的那些 Guest 物理内存单元】，它是联系 <code>GPA</code> 和 <code>RAMBlocks</code>（描述真实内存）之间的桥梁，在<code>memory.h</code>中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">MemoryRegion</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* All fields are private - violators will be prosecuted */</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="n">MemoryRegionOps</span> <span class="o">*</span><span class="n">ops</span><span class="p">;</span>      <span class="c1">// 回调函数集合
</span></span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">parent</span><span class="p">;</span>            <span class="c1">// 父 MemoryRegion 指针
</span></span></span><span class="line"><span class="cl">    <span class="n">Int128</span> <span class="n">size</span><span class="p">;</span>                     <span class="c1">// 该区域内存的大小
</span></span></span><span class="line"><span class="cl">    <span class="kt">target_phys_addr_t</span> <span class="n">addr</span><span class="p">;</span>         <span class="c1">// 在 Address Space 中的地址，即 HVA
</span></span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">destructor</span><span class="p">)(</span><span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">ram_addr_t</span> <span class="n">ram_addr</span><span class="p">;</span>             <span class="c1">// MemoryRegion 的起始地址，即 GPA
</span></span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">subpage</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">terminates</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">readable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">ram</span><span class="p">;</span>                        <span class="c1">// 是否表示 RAM
</span></span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">readonly</span><span class="p">;</span> <span class="cm">/* For RAM regions */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">enabled</span><span class="p">;</span>                    <span class="c1">// 是否已经通知 KVM 使用这段内存
</span></span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">rom_device</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">warning_printed</span><span class="p">;</span> <span class="cm">/* For reservations */</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">alias</span><span class="p">;</span>             <span class="c1">// 是否为 MemoryRegion alias
</span></span></span><span class="line"><span class="cl">    <span class="kt">target_phys_addr_t</span> <span class="n">alias_offset</span><span class="p">;</span> <span class="c1">// 若为 alias，在原 MemoryRegion 中的 offset
</span></span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="n">priority</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">may_overlap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_HEAD</span><span class="p">(</span><span class="n">subregions</span><span class="p">,</span> <span class="n">MemoryRegion</span><span class="p">)</span> <span class="n">subregions</span><span class="p">;</span> <span class="c1">// 子区域链表头
</span></span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_ENTRY</span><span class="p">(</span><span class="n">MemoryRegion</span><span class="p">)</span> <span class="n">subregions_link</span><span class="p">;</span>       <span class="c1">// 子区域链表节点
</span></span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_HEAD</span><span class="p">(</span><span class="n">coalesced_ranges</span><span class="p">,</span> <span class="n">CoalescedMemoryRange</span><span class="p">)</span> <span class="n">coalesced</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">;</span>       <span class="c1">// MemoryRegion 的名字，调试时使用
</span></span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">dirty_log_mask</span><span class="p">;</span> <span class="c1">// 表示哪一种 dirty map 被使用，共分三种
</span></span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="n">ioeventfd_nb</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegionIoeventfd</span> <span class="o">*</span><span class="n">ioeventfds</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h4 id="全局变量-1">全局变量</h4>
<p>在 QEMU 的 exec.c 中也定义了两个静态的 MemoryRegion 指针变量：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">system_memory</span><span class="p">;</span> <span class="c1">// 内存 MemoryRegion，对应 address_space_memory
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">system_io</span><span class="p">;</span>     <span class="c1">// I/O MemoryRegion，对应 address_space_io
</span></span></span></code></pre></div><p>与两个全局 AddressSpace 对应，即 AddressSpace 的 root 域指向这两个 MemoryRegion。</p>
<h4 id="memoryregion-的类型">MemoryRegion 的类型</h4>
<p>MemoryRegion 有多种类型，可以表示一段 RAM、ROM、MMIO、alias(别名)。</p>
<p>若为 alias 则表示一个 MemoryRegion 的部分区域，例如，QEMU 会为 pc.ram 这个表示 RAM 的 MemoryRegion 添加两个 alias：ram-below-4g 和 ram-above-4g，之后会看到具体的代码实例。</p>
<p>另外，MemoryRegion 也可以表示一个 container，这就表示它只是其他若干个 MemoryRegion 的容器。</p>
<p>那么要如何创建不同类型的 <code>MemoryRegion</code> 呢？</p>
<p>在 QEMU 中实际上是通过调用不同的初始化函数区分的。根据不同的初始化函数及其功能，可以将 MemoryRegion 划分为以下三种类型：</p>
<ul>
<li>根级 MemoryRegion：直接通过 memory_region_init 初始化，没有自己的内存，用于管理 subregion，例如 system_memory：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">memory_region_init</span><span class="p">(</span><span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="kt">uint64_t</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">ops</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">parent</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">=</span> <span class="nf">int128_make64</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">size</span> <span class="o">==</span> <span class="n">UINT64_MAX</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">mr</span><span class="o">-&gt;</span><span class="n">size</span> <span class="o">=</span> <span class="nf">int128_2_64</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">subpage</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">enabled</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">terminates</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span> <span class="c1">// 非实体 MemoryRegion，搜索时会继续前往其 subregions
</span></span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">ram</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>        <span class="c1">// 根级 MemoryRegion 不分配内存
</span></span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">readable</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">readonly</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">rom_device</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">destructor</span> <span class="o">=</span> <span class="n">memory_region_destructor_none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">priority</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">may_overlap</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">alias</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_INIT</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mr</span><span class="o">-&gt;</span><span class="n">subregions</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mr</span><span class="o">-&gt;</span><span class="n">subregions_link</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span> <span class="n">mr</span><span class="o">-&gt;</span><span class="n">subregions_link</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_INIT</span><span class="p">(</span><span class="o">&amp;</span><span class="n">mr</span><span class="o">-&gt;</span><span class="n">coalesced</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">=</span> <span class="nf">g_strdup</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">dirty_log_mask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">ioeventfd_nb</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">ioeventfds</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>可以看到 mr-&gt;addr 被设置为 0，而 mr-&gt;ram_addr 则并没有初始化。</p>
<ul>
<li>实体 <code>MemoryRegion</code>：通过<code>memory_region_init_ram()</code>初始化，有自己的内存（从 QEMU 进程地址空间中分配），大小为<code>size</code>，例如<code>ram_memory</code>、 <code>pci_memory</code>：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="o">*</span><span class="nf">pc_memory_init</span><span class="p">(</span><span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">system_memory</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">kernel_filename</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">kernel_cmdline</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">initrd_filename</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">ram_addr_t</span> <span class="n">below_4g_mem_size</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">ram_addr_t</span> <span class="n">above_4g_mem_size</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">rom_memory</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">MemoryRegion</span> <span class="o">**</span><span class="n">ram_memory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">ram</span><span class="p">,</span> <span class="o">*</span><span class="n">option_rom_mr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* ...*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Allocate RAM.  We allocate it as a single memory region and use
</span></span></span><span class="line"><span class="cl"><span class="cm">     * aliases to address portions of it, mostly for backwards compatibility
</span></span></span><span class="line"><span class="cl"><span class="cm">     * with older qemus that used qemu_ram_alloc().
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="n">ram</span> <span class="o">=</span> <span class="nf">g_malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">ram</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 调用 memory_region_init_ram 对 ram_memory 进行初始化
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init_ram</span><span class="p">(</span><span class="n">ram</span><span class="p">,</span> <span class="s">&#34;pc.ram&#34;</span><span class="p">,</span> <span class="n">below_4g_mem_size</span> <span class="o">+</span> <span class="n">above_4g_mem_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">vmstate_register_ram_global</span><span class="p">(</span><span class="n">ram</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">ram_memory</span> <span class="o">=</span> <span class="n">ram</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* ... */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">memory_region_init_ram</span><span class="p">(</span><span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="kt">uint64_t</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init</span><span class="p">(</span><span class="n">mr</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">ram</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">terminates</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">destructor</span> <span class="o">=</span> <span class="n">memory_region_destructor_ram</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">ram_addr</span> <span class="o">=</span> <span class="nf">qemu_ram_alloc</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">mr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>可以看到这里是先调用了<code>memory_region_init()</code>，之后设置 <code>RAM</code> 属性，并继续调用<code>qemu_ram_alloc()</code>分配内存。</p>
<ul>
<li>别名 <code>MemoryRegion</code>：通过<code>memory_region_init_alias()</code> 初始化，没有自己的内存，表示实体 <code>MemoryRegion</code> 的一部分。通过 <code>alias</code> 成员指向实体 <code>MemoryRegion</code>，<code>alias_offset</code>为在实体 <code>MemoryRegion</code> 中的偏移量，例如<code>ram_below_4g</code>、<code>ram_above_4g</code>：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="o">*</span><span class="nf">pc_memory_init</span><span class="p">(</span><span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">system_memory</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">kernel_filename</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">kernel_cmdline</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">initrd_filename</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">ram_addr_t</span> <span class="n">below_4g_mem_size</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">ram_addr_t</span> <span class="n">above_4g_mem_size</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">rom_memory</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">MemoryRegion</span> <span class="o">**</span><span class="n">ram_memory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">ram_below_4g</span><span class="p">,</span> <span class="o">*</span><span class="n">ram_above_4g</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* ... */</span>
</span></span><span class="line"><span class="cl">    <span class="n">ram_below_4g</span> <span class="o">=</span> <span class="nf">g_malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">ram_below_4g</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 调用 memory_region_init_alias 对 ram_below_4g 进行初始化
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init_alias</span><span class="p">(</span><span class="n">ram_below_4g</span><span class="p">,</span> <span class="s">&#34;ram-below-4g&#34;</span><span class="p">,</span> <span class="n">ram</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">below_4g_mem_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* ..
</span></span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">memory_region_init_alias</span><span class="p">(</span><span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">orig</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="kt">target_phys_addr_t</span> <span class="n">offset</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="kt">uint64_t</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init</span><span class="p">(</span><span class="n">mr</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">alias</span> <span class="o">=</span> <span class="n">orig</span><span class="p">;</span> <span class="c1">// 指向实体 MemoryRegion
</span></span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">alias_offset</span> <span class="o">=</span> <span class="n">offset</span><span class="p">;</span> <span class="c1">//通过 offset 得到实体的某一个部分
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="ramblock">RAMBlock</h3>
<h4 id="结构体定义-2">结构体定义</h4>
<p><code>MemoryRegion</code> 用来描述一段逻辑层面上的内存区域，而记录实际分配的内存地址信息的结构体则是 <code>RAMBlock</code>，在<code>ramblock.h</code>中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">RAMBlock</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">rcu_head</span> <span class="n">rcu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">host</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">colo_cache</span><span class="p">;</span> <span class="cm">/* For colo, VM&#39;s ram cache */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">ram_addr_t</span> <span class="n">offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">ram_addr_t</span> <span class="n">used_length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">ram_addr_t</span> <span class="n">max_length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">resized</span><span class="p">)(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">length</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">host</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">flags</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Protected by iothread lock.  */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="n">idstr</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* RCU-enabled, writes protected by the ramlist lock */</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QLIST_ENTRY</span><span class="p">(</span><span class="n">RAMBlock</span><span class="p">)</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QLIST_HEAD</span><span class="p">(,</span> <span class="n">RAMBlockNotifier</span><span class="p">)</span> <span class="n">ramblock_notifiers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">fd</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">page_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* dirty bitmap used during migration */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="n">bmap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* bitmap of already received pages in postcopy */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="n">receivedmap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">     * bitmap to track already cleared dirty bitmap.  When the bit is
</span></span></span><span class="line"><span class="cl"><span class="cm">     * set, it means the corresponding memory chunk needs a log-clear.
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Set this up to non-NULL to enable the capability to postpone
</span></span></span><span class="line"><span class="cl"><span class="cm">     * and split clearing of dirty bitmap on the remote node (e.g.,
</span></span></span><span class="line"><span class="cl"><span class="cm">     * KVM).  The bitmap will be set only when doing global sync.
</span></span></span><span class="line"><span class="cl"><span class="cm">     *
</span></span></span><span class="line"><span class="cl"><span class="cm">     * NOTE: this bitmap is different comparing to the other bitmaps
</span></span></span><span class="line"><span class="cl"><span class="cm">     * in that one bit can represent multiple guest pages (which is
</span></span></span><span class="line"><span class="cl"><span class="cm">     * decided by the `clear_bmap_shift&#39; variable below).  On
</span></span></span><span class="line"><span class="cl"><span class="cm">     * destination side, this should always be NULL, and the variable
</span></span></span><span class="line"><span class="cl"><span class="cm">     * `clear_bmap_shift&#39; is meaningless.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="n">clear_bmap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">clear_bmap_shift</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">     * RAM block length that corresponds to the used_length on the migration
</span></span></span><span class="line"><span class="cl"><span class="cm">     * source (after RAM block sizes were synchronized). Especially, after
</span></span></span><span class="line"><span class="cl"><span class="cm">     * starting to run the guest, used_length and postcopy_length can differ.
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Used to register/unregister uffd handlers and as the size of the received
</span></span></span><span class="line"><span class="cl"><span class="cm">     * bitmap. Receiving any page beyond this length will bail out, as it
</span></span></span><span class="line"><span class="cl"><span class="cm">     * could not have been valid on the source.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">ram_addr_t</span> <span class="n">postcopy_length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>可以看到在 <code>RAMBlock</code> 中 host 和 offset 域分别对应了 <code>HVA</code> 和<code>GPA</code>，因此也可以说 <code>RAMBlock</code> 中存储了<code>GPA-&gt;HVA</code>的映射关系，另外每一个 <code>RAMBlock</code> 都会指向其所属的 <code>MemoryRegion</code>。</p>
<h4 id="全局变量-ram_list">全局变量 ram_list</h4>
<p>QEMU 在<code>ramlist.h</code>中定义了一个全局变量<code>ram_list</code>，以链表的形式维护了所有的 <code>RAMBlock</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">RAMList</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">QemuMutex</span> <span class="n">mutex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">RAMBlock</span> <span class="o">*</span><span class="n">mru_block</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* RCU-enabled, writes protected by the ramlist lock. */</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QLIST_HEAD</span><span class="p">(,</span> <span class="n">RAMBlock</span><span class="p">)</span> <span class="n">blocks</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">DirtyMemoryBlocks</span> <span class="o">*</span><span class="n">dirty_memory</span><span class="p">[</span><span class="n">DIRTY_MEMORY_NUM</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">version</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QLIST_HEAD</span><span class="p">(,</span> <span class="n">RAMBlockNotifier</span><span class="p">)</span> <span class="n">ramblock_notifiers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">RAMList</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">extern</span> <span class="n">RAMList</span> <span class="n">ram_list</span><span class="p">;</span>
</span></span></code></pre></div><p>每一个新分配的 <code>RAMBlock</code> 都会被插入到<code>ram_list</code>的头部。如需查找地址所对应的 <code>RAMBlock</code>，则需要遍历<code>ram_list</code>，当目标地址落在当前<code>RAMBlock</code>的地址区间时，该 <code>RAMBlock</code> 即为查找目标。</p>
<h4 id="asmrramblock-之间的关系">AS、MR、RAMBlock 之间的关系</h4>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220225155936.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220225155936.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h3 id="flatview">FlatView</h3>
<p>AddressSpace 的 root 域及其子树共同构成了 Guest 的物理地址空间，但这些都是在 QEMU 侧定义的。要传入 KVM 进行设置时，复杂的树状结构是不利于内核进行处理的，因此需要将其<strong>转换为一个“平坦”的地址模</strong>型，也就是一个从零开始、只包含地址信息的数据结构，这在 QEMU 中通过 <strong>FlatView</strong> 来表示。每个 AddressSpace 都有一个与之对应的 FlatView 指针 current_map，表示其对应的平面展开视图。</p>
<h4 id="结构体定义-3">结构体定义</h4>
<p><code>FlatView</code> 在<code>memory.c</code>中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* Flattened global view of current active memory hierarchy.  Kept in sorted
</span></span></span><span class="line"><span class="cl"><span class="cm"> * order.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">FlatView</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">rcu_head</span> <span class="n">rcu</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="n">ref</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">FlatRange</span> <span class="o">*</span><span class="n">ranges</span><span class="p">;</span>      <span class="c1">// 对应的 FlatRange 数组
</span></span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="n">nr</span><span class="p">;</span>            <span class="c1">// FlatRange 的数目
</span></span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="n">nr_allocated</span><span class="p">;</span>  <span class="c1">// 当前数组的项数
</span></span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">AddressSpaceDispatch</span> <span class="o">*</span><span class="n">dispatch</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">root</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>其中，<code>ranges</code>是一个数组，记录了 <code>FlatView</code> 下所有的 <code>FlatRange</code>。</p>
<h4 id="flatrange">FlatRange</h4>
<p>在 <code>FlatView</code> 中，<code>FlatRange</code> 表示在 <code>FlatView</code> 中的一段内存范围，同样在<code>memory.c</code>中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* Range of memory in the global map.  Addresses are absolute. */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">FlatRange</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">;</span>           <span class="c1">// 指向所属的 MemoryRegion
</span></span></span><span class="line"><span class="cl">    <span class="n">hwaddr</span> <span class="n">offset_in_region</span><span class="p">;</span>    <span class="c1">// 在全局 MemoryRegion 中的 offset，对应 GPA
</span></span></span><span class="line"><span class="cl">    <span class="n">AddrRange</span> <span class="n">addr</span><span class="p">;</span>             <span class="c1">// 代表的地址区间，对应 HVA
</span></span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">dirty_log_mask</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">romd_mode</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">readonly</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">nonvolatile</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>每个 <code>FlatRange</code> 对应一段虚拟机物理地址区间，各个 <code>FlatRange</code> 不会重叠，<strong>按照地址的顺序保存在数组中</strong>，具体的地址范围由一个 <code>AddrRange</code> 结构来描述：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * AddrRange 用于表示 FlatRange 的起始地址及大小
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">AddrRange</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Int128</span> <span class="n">start</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Int128</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="memoryregionsection">MemoryRegionSection</h3>
<h4 id="结构体定义-4">结构体定义</h4>
<p>在 <code>QEMU</code> 中，还有几个起到中介作用的结构体，<code>MemoryRegionSection</code> 就是其中之一。</p>
<p>之前介绍的 <code>FlatRange</code> 代表一个物理地址空间的片段，偏向于描述在 <code>Host</code> 侧即 <strong>AddressSpace 中的分布【Guest 的物理空间】</strong>，而 <code>MemoryRegionSection</code> 则代表在 <code>Guest</code> 侧即 <strong>MemoryRegion 中的片段</strong>。<code>MemoryRegionSection</code> 在<code>memory.h</code>中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * MemoryRegionSection: describes a fragment of a #MemoryRegion
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @mr: the region, or %NULL if empty
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @address_space: the address space the region is mapped in
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @offset_within_region: the beginning of the section, relative to @mr&#39;s start
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @size: the size of the section; will not exceed @mr&#39;s boundaries
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @offset_within_address_space: the address of the first byte of the section
</span></span></span><span class="line"><span class="cl"><span class="cm"> *     relative to the region&#39;s address space
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @readonly: writes to this section are ignored
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//只是起到描述的作用，描述了是哪个 AddressSpace 的 MemoryRegion，
</span></span></span><span class="line"><span class="cl"> <span class="c1">//并且在 MemoryRegion 中的 offset，和在 AddressSpace 展开为平坦内存的 offset
</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">MemoryRegionSection</span> <span class="p">{</span>  
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">;</span>                               <span class="c1">// 所属的 MemoryRegion
</span></span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">address_space</span><span class="p">;</span>                    <span class="c1">// 关联的 AddressSpace
</span></span></span><span class="line"><span class="cl">    <span class="kt">target_phys_addr_t</span> <span class="n">offset_within_region</span><span class="p">;</span>        <span class="c1">// 在 MemoryRegion 内部的 offset
</span></span></span><span class="line"><span class="cl">    <span class="kt">uint64_t</span> <span class="n">size</span><span class="p">;</span>                                  <span class="c1">// Section 的大小
</span></span></span><span class="line"><span class="cl">    <span class="kt">target_phys_addr_t</span> <span class="n">offset_within_address_space</span><span class="p">;</span> <span class="c1">// 在 AddressSpace 内部的 offset
</span></span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">readonly</span><span class="p">;</span>                                  <span class="c1">// 是否为只读
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><ul>
<li><code>offset_within_region</code>：在所属 <code>MemoryRegion</code> 中的<code>offset</code>。一个<code>AddressSpace</code> 可能由多个 <code>MemoryRegion</code> 组成，因此该 <code>offset</code> 是局部的</li>
<li><code>offset_within_address_space</code>：在所属 <code>AddressSpace</code> 中的 <code>offset</code>，它是全局的</li>
</ul>
<h4 id="和其他数据结构之间的关系">和其他数据结构之间的关系</h4>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125110422.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125110422.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<ul>
<li><code>AddressSpace</code> 的<code>root</code>指向对应的根级<code>MemoryRegion</code>，<code>current_map</code>指向<code>AddressSpace</code> 的<code>root</code>通过<code>generate_memory_topology()</code>生成的 <code>FlatView</code></li>
<li><code>FlatView</code> 中的<code>ranges</code>数组表示该<code>MemoryRegion</code> 所表示的<code>Guest</code>地址区间【GPA 的整个平坦物理空间】，并按照地址的顺序进行排列</li>
<li><code>MemoryRegionSection</code> 由<code>ranges</code>数组中的 <code>FlatRange</code> 对应生成，作为注册到 <code>KVM</code>中的基本单位</li>
</ul>
<hr>
<p><code>QEMU</code> 在用户空间申请内存后，需要将内存信息通过一系列系统调用传入内核空间的 <code>KVM</code>，由 <code>KVM</code> 侧进行管理，因此 <code>QEMU</code> 侧也定义了一些用于向 <code>KVM</code> 传递参数的结构体。</p>
<p>以下为<code>KVM</code>相关的数据结构。</p>
<h3 id="kvmslot">KVMSlot</h3>
<p><code>在 kvm_init.h</code>中定义，是 <code>KVM</code> 中内存管理的基本单位：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">KVMSlot</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">hwaddr</span> <span class="n">start_addr</span><span class="p">;</span> <span class="c1">// Guest 物理地址，GPA
</span></span></span><span class="line"><span class="cl">    <span class="kt">ram_addr_t</span> <span class="n">memory_size</span><span class="p">;</span>        <span class="c1">// 内存大小
</span></span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="o">*</span><span class="n">ram</span><span class="p">;</span> <span class="c1">// QEMU 用户空间地址，HVA
</span></span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">slot</span><span class="p">;</span>  <span class="c1">// Slot 编号
</span></span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">flags</span><span class="p">;</span> <span class="c1">// 标志位，例如是否追踪脏页、是否可用等
</span></span></span><span class="line"><span class="cl">    <span class="cm">/* Dirty bitmap cache for the slot */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span><span class="n">dirty_bmap</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">dirty_bmap_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Cache of the address space ID */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">as_id</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Cache of the offset in ram address space */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">ram_addr_t</span> <span class="n">ram_start_offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">KVMSlot</span><span class="p">;</span>
</span></span></code></pre></div><p><code>KVMSlot</code> 类似于内存插槽的概念。</p>
<h3 id="kvm_userspace_memory_region">kvm_userspace_memory_region</h3>
<p>调用<code>ioctl(KVM_SET_USER_MEMORY_REGION)</code>时需要向 <code>KVM</code> 传递的参数，在<code>kvm.h</code>中定义</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* for KVM_SET_USER_MEMORY_REGION */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">kvm_userspace_memory_region</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">__u32</span> <span class="n">slot</span><span class="p">;</span>            <span class="c1">// slot 编号
</span></span></span><span class="line"><span class="cl">    <span class="n">__u32</span> <span class="n">flags</span><span class="p">;</span>           <span class="c1">// 标志位，例如是否追踪脏页、是否可用等
</span></span></span><span class="line"><span class="cl">    <span class="n">__u64</span> <span class="n">guest_phys_addr</span><span class="p">;</span> <span class="c1">// Guest 物理地址，GPA
</span></span></span><span class="line"><span class="cl">    <span class="n">__u64</span> <span class="n">memory_size</span><span class="p">;</span>     <span class="c1">// 内存大小，bytes
</span></span></span><span class="line"><span class="cl">    <span class="n">__u64</span> <span class="n">userspace_addr</span><span class="p">;</span>  <span class="c1">// 从 QEMU 进程空间分配的起始地址，HVA
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="memorylistener">MemoryListener</h3>
<h4 id="结构体定义-5">结构体定义</h4>
<p>为了监控虚拟机的物理地址访问，对于每一个 <code>AddressSpace</code>，都会有一个 <code>MemoryListener</code> 与之对应。每当物理映射<code>GPA-&gt;HVA</code>发生改变时，就会回调这些函数。<strong>MemoryListener</strong> 是对一些事件的<strong>回调函数合集</strong>，在<code>memory.h</code>中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * MemoryListener: callbacks structure for updates to the physical memory map
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Allows a component to adjust to changes in the guest-visible memory map.
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Use with memory_listener_register() and memory_listener_unregister().
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">MemoryListener</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">begin</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">commit</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">region_add</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">MemoryRegionSection</span> <span class="o">*</span><span class="n">section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">region_del</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">MemoryRegionSection</span> <span class="o">*</span><span class="n">section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">region_nop</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">MemoryRegionSection</span> <span class="o">*</span><span class="n">section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">log_start</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">MemoryRegionSection</span> <span class="o">*</span><span class="n">section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">log_stop</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">MemoryRegionSection</span> <span class="o">*</span><span class="n">section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">log_sync</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">MemoryRegionSection</span> <span class="o">*</span><span class="n">section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">log_global_start</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">log_global_stop</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">eventfd_add</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">MemoryRegionSection</span> <span class="o">*</span><span class="n">section</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="kt">bool</span> <span class="n">match_data</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">data</span><span class="p">,</span> <span class="n">EventNotifier</span> <span class="o">*</span><span class="n">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">eventfd_del</span><span class="p">)(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">MemoryRegionSection</span> <span class="o">*</span><span class="n">section</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="kt">bool</span> <span class="n">match_data</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">data</span><span class="p">,</span> <span class="n">EventNotifier</span> <span class="o">*</span><span class="n">e</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Lower = earlier (during add), later (during del) */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="n">priority</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">address_space_filter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_ENTRY</span><span class="p">(</span><span class="n">MemoryListener</span><span class="p">)</span> <span class="n">link</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h4 id="全局变量-memory_listeners">全局变量 memory_listeners</h4>
<p>所有的 <code>MemoryListener</code> 都会挂在全局变量<code>memory_listeners</code>链表上，在<code>memory.c</code>中定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="nf">QTAILQ_HEAD</span><span class="p">(,</span> <span class="n">MemoryListener</span><span class="p">)</span> <span class="n">memory_listeners</span>
</span></span><span class="line"><span class="cl">    <span class="o">=</span> <span class="nf">QTAILQ_HEAD_INITIALIZER</span><span class="p">(</span><span class="n">memory_listeners</span><span class="p">);</span>
</span></span></code></pre></div><p>在<code>memory.c</code>中枚举了<code>ListenerDirection</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">enum</span> <span class="n">ListenerDirection</span> <span class="p">{</span> <span class="n">Forward</span><span class="p">,</span> <span class="n">Reverse</span> <span class="p">};</span>
</span></span></code></pre></div><h3 id="重要数据结构总览">重要数据结构总览</h3>
<table>
  <thead>
      <tr>
          <th style="text-align: center">结构体名</th>
          <th style="text-align: left">说明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: center">AddressSpace</td>
          <td style="text-align: left">VM 能看到的一段地址空间，偏向 Host 侧【注意指的是偏向】</td>
      </tr>
      <tr>
          <td style="text-align: center">MemoryRegion</td>
          <td style="text-align: left">地址空间中一段逻辑层面的内存区域，偏向 Guest 侧</td>
      </tr>
      <tr>
          <td style="text-align: center">RAMBlock</td>
          <td style="text-align: left">记录实际分配的内存地址信息，存储了 GPA-&gt;HVA 的映射关系</td>
      </tr>
      <tr>
          <td style="text-align: center">FlatView</td>
          <td style="text-align: left">MemoryRegion 对应的平面展开视图，包含一个 FlatRange 类型的 ranges 数组</td>
      </tr>
      <tr>
          <td style="text-align: center">FlatRange</td>
          <td style="text-align: left">对应一段虚拟机物理地址区间，各个 FlatRange 不会重叠，按照地址的顺序保存在数组中</td>
      </tr>
      <tr>
          <td style="text-align: center">MemoryRegionSection</td>
          <td style="text-align: left">表示 MemoryRegion 中的片段</td>
      </tr>
      <tr>
          <td style="text-align: center">MemoryListener</td>
          <td style="text-align: left">回调函数集合</td>
      </tr>
      <tr>
          <td style="text-align: center">KVMSlot</td>
          <td style="text-align: left">KVM 中内存管理的基本单位，表示一个内存插槽</td>
      </tr>
      <tr>
          <td style="text-align: center">kvm_userspace_memory_region</td>
          <td style="text-align: left">调用 ioctl(KVM_SET_USER_MEMORY_REGION) 时需要向 KVM 传递的参数</td>
      </tr>
  </tbody>
</table>
<h2 id="具体实现机制">具体实现机制</h2>
<p>QEMU 的内存申请流程大致可分为三个部分：回调函数的注册、AddressSpace 的初始化、实际内存的分配。下面将根据在 vl.c 的 main() 函数中的调用顺序分别介绍。</p>
<h3 id="回调函数的注册">回调函数的注册</h3>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125133808.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125133808.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="err">└─</span> <span class="k">static</span> <span class="kt">int</span> <span class="nf">configure_accelerator</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">       <span class="err">└─</span> <span class="kt">int</span> <span class="nf">kvm_init</span><span class="p">()</span>                                     <span class="c1">// 初始化 KVM
</span></span></span><span class="line"><span class="cl">            <span class="err">├─</span> <span class="kt">int</span> <span class="nf">kvm_ioctl</span><span class="p">(</span><span class="n">KVM_CREATE_VM</span><span class="p">)</span>                  <span class="c1">// 创建 VM
</span></span></span><span class="line"><span class="cl">            <span class="err">├─</span> <span class="kt">int</span> <span class="nf">kvm_arch_init</span><span class="p">()</span>                           <span class="c1">// 针对不同的架构进行初始化
</span></span></span><span class="line"><span class="cl">            <span class="err">└─</span> <span class="kt">void</span> <span class="nf">memory_listener_register</span><span class="p">()</span>               <span class="c1">// 注册 kvm_memory_listener
</span></span></span><span class="line"><span class="cl">                 <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">listener_add_address_space</span><span class="p">()</span> <span class="c1">// 调用 region_add 回调
</span></span></span><span class="line"><span class="cl">                      <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">kvm_region_add</span><span class="p">()</span>        <span class="c1">// region_add 对应的回调实现
</span></span></span><span class="line"><span class="cl">                           <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">kvm_set_phys_mem</span><span class="p">()</span> <span class="c1">// 根据传入的 section 填充 KVMSlot
</span></span></span><span class="line"><span class="cl">                                <span class="err">└─</span> <span class="k">static</span> <span class="kt">int</span> <span class="nf">kvm_set_user_memory_region</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                                     <span class="err">└─</span> <span class="kt">int</span> <span class="nf">ioctl</span><span class="p">(</span><span class="n">KVM_SET_USER_MEMORY_REGION</span><span class="p">)</span>
</span></span></code></pre></div><p>进入<code>configure_accelerator()</code>后，<code>QEMU</code>会先调用<code>configure_accelerator()</code>设置 <code>KVM</code> 的加速支持，之后进入<code>kvm_init()</code>。该函数主要完成对 KVM 的初始化，包括一些常规检查如 <code>CPU</code> 个数、<code>KVM</code> 版本等，之后通过<code>kvm_ioctl(KVM_CREATE_VM)</code>与内核交互，创建 <code>KVM</code> 虚拟机。在<code>kvm_init()</code>的最后，会调用<code>memory_listener_register()</code>注册<code>kvm_memory_listener</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">kvm_init</span><span class="p">(</span><span class="n">MachineState</span> <span class="o">*</span><span class="n">ms</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MachineClass</span> <span class="o">*</span><span class="n">mc</span> <span class="o">=</span> <span class="nf">MACHINE_GET_CLASS</span><span class="p">(</span><span class="n">ms</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 打开/dev/kvm
</span></span></span><span class="line"><span class="cl">    <span class="n">s</span><span class="o">-&gt;</span><span class="n">fd</span> <span class="o">=</span> <span class="nf">qemu_open_old</span><span class="p">(</span><span class="s">&#34;/dev/kvm&#34;</span><span class="p">,</span> <span class="n">O_RDWR</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建 VM
</span></span></span><span class="line"><span class="cl">    <span class="k">do</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="nf">kvm_ioctl</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">KVM_CREATE_VM</span><span class="p">,</span> <span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">ret</span> <span class="o">==</span> <span class="o">-</span><span class="n">EINTR</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* ... */</span>
</span></span><span class="line"><span class="cl">    <span class="n">ret</span> <span class="o">=</span> <span class="nf">kvm_arch_init</span><span class="p">(</span><span class="n">s</span><span class="p">);</span> <span class="c1">// 针对不同的架构进行初始化
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 对于以下 AddressSpace，设置其对应的 listener
</span></span></span><span class="line"><span class="cl">    <span class="nf">kvm_memory_listener_register</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">memory_listener</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                 <span class="o">&amp;</span><span class="n">address_space_memory</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">&#34;kvm-memory&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memory_listener_register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">kvm_coalesced_pio_listener</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                             <span class="o">&amp;</span><span class="n">address_space_io</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* ... */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">memory_listener_register</span><span class="p">(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span> <span class="n">AddressSpace</span> <span class="o">*</span><span class="n">as</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryListener</span> <span class="o">*</span><span class="n">other</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Only one of them can be defined for a listener */</span>
</span></span><span class="line"><span class="cl">    <span class="nf">assert</span><span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">listener</span><span class="o">-&gt;</span><span class="n">log_sync</span> <span class="o">&amp;&amp;</span> <span class="n">listener</span><span class="o">-&gt;</span><span class="n">log_sync_global</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">listener</span><span class="o">-&gt;</span><span class="n">address_space</span> <span class="o">=</span> <span class="n">as</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">QTAILQ_EMPTY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">memory_listeners</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="o">||</span> <span class="n">listener</span><span class="o">-&gt;</span><span class="n">priority</span> <span class="o">&gt;=</span> <span class="nf">QTAILQ_LAST</span><span class="p">(</span><span class="o">&amp;</span><span class="n">memory_listeners</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">priority</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">QTAILQ_INSERT_TAIL</span><span class="p">(</span><span class="o">&amp;</span><span class="n">memory_listeners</span><span class="p">,</span> <span class="n">listener</span><span class="p">,</span> <span class="n">link</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">QTAILQ_FOREACH</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">memory_listeners</span><span class="p">,</span> <span class="n">link</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">listener</span><span class="o">-&gt;</span><span class="n">priority</span> <span class="o">&lt;</span> <span class="n">other</span><span class="o">-&gt;</span><span class="n">priority</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">QTAILQ_INSERT_BEFORE</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">listener</span><span class="p">,</span> <span class="n">link</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">QTAILQ_EMPTY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">listeners</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="o">||</span> <span class="n">listener</span><span class="o">-&gt;</span><span class="n">priority</span> <span class="o">&gt;=</span> <span class="nf">QTAILQ_LAST</span><span class="p">(</span><span class="o">&amp;</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">listeners</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">priority</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">QTAILQ_INSERT_TAIL</span><span class="p">(</span><span class="o">&amp;</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">listeners</span><span class="p">,</span> <span class="n">listener</span><span class="p">,</span> <span class="n">link_as</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">QTAILQ_FOREACH</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">listeners</span><span class="p">,</span> <span class="n">link_as</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">listener</span><span class="o">-&gt;</span><span class="n">priority</span> <span class="o">&lt;</span> <span class="n">other</span><span class="o">-&gt;</span><span class="n">priority</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">QTAILQ_INSERT_BEFORE</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">listener</span><span class="p">,</span> <span class="n">link_as</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">listener_add_address_space</span><span class="p">(</span><span class="n">listener</span><span class="p">,</span> <span class="n">as</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>最后的<code>listener_add_address_space()</code>主要是将 listener 注册到其对应的 <code>AddressSpace</code> 上，并根据 <code>AddressSpace</code> 对应的 <code>FlatRange</code> 数组，生成 <code>MemoryRegionSection</code>【<code>MemoryRegionSection</code>就像是为<code>FlatRange</code>数组设置的一种中介表示，便于传入<code>KVM</code>，因为传入<code>KVM</code>应该是对平坦内存的一种表示】，并注册到 <code>KVM</code> 中：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">listener_add_address_space</span><span class="p">(</span><span class="n">MemoryListener</span> <span class="o">*</span><span class="n">listener</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                       <span class="n">AddressSpace</span> <span class="o">*</span><span class="n">as</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">FlatView</span> <span class="o">*</span><span class="n">view</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">FlatRange</span> <span class="o">*</span><span class="n">fr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">listener</span><span class="o">-&gt;</span><span class="n">begin</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">listener</span><span class="o">-&gt;</span><span class="nf">begin</span><span class="p">(</span><span class="n">listener</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* 开启内存脏页记录 */</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">global_dirty_tracking</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">listener</span><span class="o">-&gt;</span><span class="n">log_global_start</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">listener</span><span class="o">-&gt;</span><span class="nf">log_global_start</span><span class="p">(</span><span class="n">listener</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* 遍历 AddressSpace 对应的 FlatRange 数组，并将其转换成 MemoryRegionSection */</span>
</span></span><span class="line"><span class="cl">    <span class="n">view</span> <span class="o">=</span> <span class="nf">address_space_get_flatview</span><span class="p">(</span><span class="n">as</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">FOR_EACH_FLAT_RANGE</span><span class="p">(</span><span class="n">fr</span><span class="p">,</span> <span class="n">view</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">MemoryRegionSection</span> <span class="n">section</span> <span class="o">=</span> <span class="nf">section_from_flat_range</span><span class="p">(</span><span class="n">fr</span><span class="p">,</span> <span class="n">view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* 将 section 所代表的内存区域注册到 KVM 中 */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">listener</span><span class="o">-&gt;</span><span class="n">region_add</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">listener</span><span class="o">-&gt;</span><span class="nf">region_add</span><span class="p">(</span><span class="n">listener</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">section</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">fr</span><span class="o">-&gt;</span><span class="n">dirty_log_mask</span> <span class="o">&amp;&amp;</span> <span class="n">listener</span><span class="o">-&gt;</span><span class="n">log_start</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">listener</span><span class="o">-&gt;</span><span class="nf">log_start</span><span class="p">(</span><span class="n">listener</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">section</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">fr</span><span class="o">-&gt;</span><span class="n">dirty_log_mask</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">listener</span><span class="o">-&gt;</span><span class="n">commit</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">listener</span><span class="o">-&gt;</span><span class="nf">commit</span><span class="p">(</span><span class="n">listener</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nf">flatview_unref</span><span class="p">(</span><span class="n">view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>由于此时 <code>AddressSapce</code> 尚未初始化，所以此处的循环为空，仅是在全局注册了<code>kvm_memory_listener</code>。最后调用了<code>kvm_memory_listener-&gt;region_add()</code>，对应的实现是<code>kvm_region_add()</code>，该函数最终会通过<code>ioctl(KVM_SET_USER_MEMORY_REGION)</code>，将 <code>QEMU</code> 侧申请的内存信息传入 <code>KVM</code> 进行注册，这里的流程会在下一部分进行分析。</p>
<h3 id="addressspace-的初始化">AddressSpace 的初始化</h3>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125154841.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220125154841.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="err">└─</span> <span class="kt">void</span> <span class="nf">cpu_exec_init_all</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">       <span class="err">├─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">memory_map_init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="err">├─</span> <span class="kt">void</span> <span class="nf">memory_region_init</span><span class="p">()</span>    <span class="c1">// 初始化 system_memory/io 这两个全局 MemoryRegion
</span></span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="err">├─</span> <span class="kt">void</span> <span class="nf">set_system_memory_map</span><span class="p">()</span> <span class="c1">// address_space_memory-&gt;root = system_memory
</span></span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="o">|</span>    <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">memory_region_update_topology</span><span class="p">()</span>        <span class="c1">// 为 MemoryRegion 生成 FlatView
</span></span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="o">|</span>         <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">address_space_update_topology</span><span class="p">()</span>   <span class="c1">// as-&gt;current_map = new_view
</span></span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="o">|</span>              <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">address_space_update_topology_pass</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="o">|</span>                   <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">kvm_region_add</span><span class="p">()</span>        <span class="c1">// region_add 对应的回调实现
</span></span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="o">|</span>                        <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">kvm_set_phys_mem</span><span class="p">()</span> <span class="c1">// 根据传入的 section 填充 KVMSlot
</span></span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="o">|</span>                             <span class="err">└─</span> <span class="k">static</span> <span class="kt">int</span> <span class="nf">kvm_set_user_memory_region</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="o">|</span>                                  <span class="err">└─</span> <span class="kt">int</span> <span class="nf">ioctl</span><span class="p">(</span><span class="n">KVM_SET_USER_MEMORY_REGION</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="o">|</span>
</span></span><span class="line"><span class="cl">       <span class="o">|</span>    <span class="err">└─</span> <span class="kt">void</span> <span class="nf">memory_listener_register</span><span class="p">()</span> <span class="c1">// 注册对应的 MemoryListener
</span></span></span><span class="line"><span class="cl">       <span class="o">|</span>         <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">listener_add_address_space</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">       <span class="o">|</span>
</span></span><span class="line"><span class="cl">       <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">io_mem_init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="err">└─</span> <span class="kt">void</span> <span class="nf">memory_region_init_io</span><span class="p">()</span> <span class="c1">// ram/rom/unassigned/notdirty/subpage-ram/watch
</span></span></span><span class="line"><span class="cl">                 <span class="err">└─</span> <span class="kt">void</span> <span class="nf">memory_region_init</span><span class="p">()</span>
</span></span></code></pre></div><p>第一部分在全局注册了<code>kvm_memory_listener</code>，但由于<code>AddressSpace</code> 尚未初始化，实际上并未向 <code>KVM</code> 中注册任何实际的内存信息。<code>QEMU</code> 在<code>main()</code>函数中会继续调用<code>cpu_exec_init_all()</code>对<code>AddressSpace</code>进行初始化，该函数实际上是对两个 <code>init</code> 函数的封装调用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">cpu_exec_init_all</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">qemu_mutex_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ram_list</span><span class="p">.</span><span class="n">mutex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* The data structures we set up here depend on knowing the page size,
</span></span></span><span class="line"><span class="cl"><span class="cm">     * so no more changes can be made after this point.
</span></span></span><span class="line"><span class="cl"><span class="cm">     * In an ideal world, nothing we did before we had finished the
</span></span></span><span class="line"><span class="cl"><span class="cm">     * machine setup would care about the target page size, and we could
</span></span></span><span class="line"><span class="cl"><span class="cm">     * do this much later, rather than requiring board models to state
</span></span></span><span class="line"><span class="cl"><span class="cm">     * up front what their requirements are.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="nf">finalize_target_page_bits</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">io_mem_init</span><span class="p">();</span>      <span class="c1">// 初始化六个I/O MemoryRegion
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_map_init</span><span class="p">();</span> <span class="c1">// 初始化两个全局 AddressSpace，以及对应的 MemoryRegion、FlatView
</span></span></span><span class="line"><span class="cl">    <span class="nf">qemu_mutex_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">map_client_list_lock</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>先来看<code>memory_map_init()</code>，主要用来初始化两个全局的系统地址空间<code>system_memory</code>、<code>system_io</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">memory_map_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_memory</span> <span class="o">=</span> <span class="nf">g_malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">system_memory</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 初始化 system_memory
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init</span><span class="p">(</span><span class="n">system_memory</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="s">&#34;system&#34;</span><span class="p">,</span> <span class="n">UINT64_MAX</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 设置 address_space_memory 关联 system_memory
</span></span></span><span class="line"><span class="cl">    <span class="c1">// 这两个都是全局变量，也就是把内存地址空间和 IO 地址空间于对应的 MemoryRegion 联系起来 
</span></span></span><span class="line"><span class="cl">    <span class="c1">//及其对应的 FlatView
</span></span></span><span class="line"><span class="cl">    <span class="nf">address_space_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">address_space_memory</span><span class="p">,</span> <span class="n">system_memory</span><span class="p">,</span> <span class="s">&#34;memory&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">system_io</span> <span class="o">=</span> <span class="nf">g_malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">system_io</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 初始化 system_io  
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init_io</span><span class="p">(</span><span class="n">system_io</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">unassigned_io_ops</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="s">&#34;io&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="mi">65536</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 设置 address_space_io 关联 system_io 
</span></span></span><span class="line"><span class="cl">    <span class="c1">// 及其对应的 FlatView
</span></span></span><span class="line"><span class="cl">    <span class="nf">address_space_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">address_space_io</span><span class="p">,</span> <span class="n">system_io</span><span class="p">,</span> <span class="s">&#34;I/O&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这里比较重要的是<code>address_space_init()</code>，先设置 <code>AddressSpace</code> 对应的 <code>MemoryRegion</code>，之后根据<code>system_memory</code>更新<code>address_space_memory</code>对应的 <code>FlatView</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">address_space_init</span><span class="p">(</span><span class="n">AddressSpace</span> <span class="o">*</span><span class="n">as</span><span class="p">,</span> <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">root</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memory_region_ref</span><span class="p">(</span><span class="n">root</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 将 address_space_memory 的 root 域指向 system_memory
</span></span></span><span class="line"><span class="cl">    <span class="n">as</span><span class="o">-&gt;</span><span class="n">root</span> <span class="o">=</span> <span class="n">root</span><span class="p">;</span>        
</span></span><span class="line"><span class="cl">    <span class="n">as</span><span class="o">-&gt;</span><span class="n">current_map</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">as</span><span class="o">-&gt;</span><span class="n">ioeventfd_nb</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">as</span><span class="o">-&gt;</span><span class="n">ioeventfds</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_INIT</span><span class="p">(</span><span class="o">&amp;</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">listeners</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_INSERT_TAIL</span><span class="p">(</span><span class="o">&amp;</span><span class="n">address_spaces</span><span class="p">,</span> <span class="n">as</span><span class="p">,</span> <span class="n">address_spaces_link</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">as</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">=</span> <span class="nf">g_strdup</span><span class="p">(</span><span class="n">name</span> <span class="o">?</span> <span class="nl">name</span> <span class="p">:</span> <span class="s">&#34;anonymous&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 根据 system_memory 更新 address_space_memory 对应的 FlatView
</span></span></span><span class="line"><span class="cl">    <span class="nf">address_space_update_topology</span><span class="p">(</span><span class="n">as</span><span class="p">);</span>  
</span></span><span class="line"><span class="cl">    <span class="nf">address_space_update_ioeventfds</span><span class="p">(</span><span class="n">as</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>address_space_update_topology()</code>会继续调用<code>generate_memory_topology()</code>生成 <code>AddressSpace</code> 对应的 <code>FlatView</code>视图：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">address_space_update_topology</span><span class="p">(</span><span class="n">AddressSpace</span> <span class="o">*</span><span class="n">as</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">physmr</span> <span class="o">=</span> <span class="nf">memory_region_get_flatview_root</span><span class="p">(</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">root</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">flatviews_init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">g_hash_table_lookup</span><span class="p">(</span><span class="n">flat_views</span><span class="p">,</span> <span class="n">physmr</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">generate_memory_topology</span><span class="p">(</span><span class="n">physmr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nf">address_space_set_flatview</span><span class="p">(</span><span class="n">as</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>address_space_update_topology()</code>会先调用<code>generate_memory_topology()</code>生成<code>system_memory</code>更新后的视图<code>new_view</code>，再将<code>address_space_memory</code>的<code>current_map</code>指向这个<code>new_view</code>，最后销毁<code>old_view</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">address_space_set_flatview</span><span class="p">(</span><span class="n">AddressSpace</span> <span class="o">*</span><span class="n">as</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">FlatView</span> <span class="o">*</span><span class="n">old_view</span> <span class="o">=</span> <span class="nf">address_space_to_flatview</span><span class="p">(</span><span class="n">as</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">physmr</span> <span class="o">=</span> <span class="nf">memory_region_get_flatview_root</span><span class="p">(</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">root</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">FlatView</span> <span class="o">*</span><span class="n">new_view</span> <span class="o">=</span> <span class="nf">g_hash_table_lookup</span><span class="p">(</span><span class="n">flat_views</span><span class="p">,</span> <span class="n">physmr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">assert</span><span class="p">(</span><span class="n">new_view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">old_view</span> <span class="o">==</span> <span class="n">new_view</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">old_view</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">flatview_ref</span><span class="p">(</span><span class="n">old_view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">flatview_ref</span><span class="p">(</span><span class="n">new_view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">QTAILQ_EMPTY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">listeners</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">FlatView</span> <span class="n">tmpview</span> <span class="o">=</span> <span class="p">{</span> <span class="p">.</span><span class="n">nr</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">},</span> <span class="o">*</span><span class="n">old_view2</span> <span class="o">=</span> <span class="n">old_view</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">old_view2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">old_view2</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">tmpview</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">address_space_update_topology_pass</span><span class="p">(</span><span class="n">as</span><span class="p">,</span> <span class="n">old_view2</span><span class="p">,</span> <span class="n">new_view</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">address_space_update_topology_pass</span><span class="p">(</span><span class="n">as</span><span class="p">,</span> <span class="n">old_view2</span><span class="p">,</span> <span class="n">new_view</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Writes are protected by the BQL.  */</span>
</span></span><span class="line"><span class="cl">    <span class="nf">qatomic_rcu_set</span><span class="p">(</span><span class="o">&amp;</span><span class="n">as</span><span class="o">-&gt;</span><span class="n">current_map</span><span class="p">,</span> <span class="n">new_view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">old_view</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">flatview_unref</span><span class="p">(</span><span class="n">old_view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Note that all the old MemoryRegions are still alive up to this
</span></span></span><span class="line"><span class="cl"><span class="cm">     * point.  This relieves most MemoryListeners from the need to
</span></span></span><span class="line"><span class="cl"><span class="cm">     * ref/unref the MemoryRegions they get---unless they use them
</span></span></span><span class="line"><span class="cl"><span class="cm">     * outside the iothread mutex, in which case precise reference
</span></span></span><span class="line"><span class="cl"><span class="cm">     * counting is necessary.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">old_view</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">flatview_unref</span><span class="p">(</span><span class="n">old_view</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在<code>address_space_update_topology_pass()</code>的最后，会调用<code>MEMORY_LISTENER_UPDATE_REGION</code>这个宏，触发<code>region_add</code>对应的回调函数<code>kvm_region_add()</code>。</p>
<p>这个宏在<code>memory.c</code>中定义，会将 <code>FlatView</code> 中的 <code>FlatRange</code> 转换为 <code>MemoryRegionSection</code>，作为入参传递给<code>kvm_region_add()</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="cm">/* No need to ref/unref .mr, the FlatRange keeps it alive.  */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...)  \
</span></span></span><span class="line"><span class="cl"><span class="cp">    do {                                                                \
</span></span></span><span class="line"><span class="cl"><span class="cp">        MemoryRegionSection mrs = section_from_flat_range(fr,           \
</span></span></span><span class="line"><span class="cl"><span class="cp">                address_space_to_flatview(as));                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">        MEMORY_LISTENER_CALL(as, callback, dir, &amp;mrs, ##_args);         \
</span></span></span><span class="line"><span class="cl"><span class="cp">    } while(0)
</span></span></span></code></pre></div><p>而<code>kvm_region_add()</code>实际上是对<code>kvm_set_phys_mem()</code>的封装调用。该函数比较复杂，会根据传入的<code>section</code>填充 KVMSlot，再传递给<code>kvm_set_user_memory_region()</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span> <span class="nf">kvm_set_user_memory_region</span><span class="p">(</span><span class="n">KVMMemoryListener</span> <span class="o">*</span><span class="n">kml</span><span class="p">,</span> <span class="n">KVMSlot</span> <span class="o">*</span><span class="n">slot</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">new</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">KVMState</span> <span class="o">*</span><span class="n">s</span> <span class="o">=</span> <span class="n">kvm_state</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">kvm_userspace_memory_region</span> <span class="n">mem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 根据 KVMSlot 填充 kvm_userspace_memory_region
</span></span></span><span class="line"><span class="cl">    <span class="n">mem</span><span class="p">.</span><span class="n">slot</span> <span class="o">=</span> <span class="n">slot</span><span class="o">-&gt;</span><span class="n">slot</span> <span class="o">|</span> <span class="p">(</span><span class="n">kml</span><span class="o">-&gt;</span><span class="n">as_id</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem</span><span class="p">.</span><span class="n">guest_phys_addr</span> <span class="o">=</span> <span class="n">slot</span><span class="o">-&gt;</span><span class="n">start_addr</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem</span><span class="p">.</span><span class="n">userspace_addr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">slot</span><span class="o">-&gt;</span><span class="n">ram</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem</span><span class="p">.</span><span class="n">flags</span> <span class="o">=</span> <span class="n">slot</span><span class="o">-&gt;</span><span class="n">flags</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">slot</span><span class="o">-&gt;</span><span class="n">memory_size</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">new</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">mem</span><span class="p">.</span><span class="n">flags</span> <span class="o">^</span> <span class="n">slot</span><span class="o">-&gt;</span><span class="n">old_flags</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">KVM_MEM_READONLY</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Set the slot size to 0 before setting the slot to the desired
</span></span></span><span class="line"><span class="cl"><span class="cm">         * value. This is needed based on KVM commit 75d61fbc. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">mem</span><span class="p">.</span><span class="n">memory_size</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">ret</span> <span class="o">=</span> <span class="nf">kvm_vm_ioctl</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">KVM_SET_USER_MEMORY_REGION</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mem</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">ret</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">goto</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">mem</span><span class="p">.</span><span class="n">memory_size</span> <span class="o">=</span> <span class="n">slot</span><span class="o">-&gt;</span><span class="n">memory_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ret</span> <span class="o">=</span> <span class="nf">kvm_vm_ioctl</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">KVM_SET_USER_MEMORY_REGION</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">mem</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">slot</span><span class="o">-&gt;</span><span class="n">old_flags</span> <span class="o">=</span> <span class="n">mem</span><span class="p">.</span><span class="n">flags</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>可以看到这里又将 <code>KVMSlot</code> 转换为 <code>kvm_userspace_memory_region</code>，作为<code>ioctl()</code>的参数，交给内核中的 <code>KVM</code> 进行内存的注册【设置<code>GPA-&gt;HVA</code>的映射关系，在内核空间维护并管理 Guest 的内存】。</p>
<p>至此 QEMU 侧负责管理内存的数据结构均已完成初始化，<strong>可以参考下面的图片了解各数据结构之间的对应关系</strong></p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127155506.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127155506.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h3 id="实际内存的分配">实际内存的分配</h3>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127160121.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127160121.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="err">└─</span> <span class="kt">void</span> <span class="n">machine</span><span class="o">-&gt;</span><span class="nf">init</span><span class="p">(</span><span class="n">ram_size</span><span class="p">,</span> <span class="p">...)</span>
</span></span><span class="line"><span class="cl">       <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">pc_init_pci</span><span class="p">(</span><span class="n">ram_size</span><span class="p">,</span> <span class="p">...)</span> <span class="c1">// 初始化虚拟机
</span></span></span><span class="line"><span class="cl">            <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">pc_init1</span><span class="p">(</span><span class="n">system_memory</span><span class="p">,</span> <span class="n">system_io</span><span class="p">,</span> <span class="n">ram_size</span><span class="p">,</span> <span class="p">...)</span>
</span></span><span class="line"><span class="cl">                 <span class="err">├─</span> <span class="kt">void</span> <span class="nf">memory_region_init</span><span class="p">(</span><span class="n">pci_memory</span><span class="p">,</span> <span class="s">&#34;pci&#34;</span><span class="p">,</span> <span class="p">...)</span> <span class="c1">// pci_memory, rom_memory
</span></span></span><span class="line"><span class="cl">                 <span class="err">└─</span> <span class="kt">void</span> <span class="nf">pc_memory_init</span><span class="p">()</span> <span class="c1">// 初始化内存，分配实际的物理内存地址
</span></span></span><span class="line"><span class="cl">                      <span class="err">├─</span> <span class="kt">void</span> <span class="nf">memory_region_init_ram</span><span class="p">()</span> <span class="c1">// 创建 pc.ram, pc.rom 并分配内存
</span></span></span><span class="line"><span class="cl">                      <span class="o">|</span>    <span class="err">├─</span> <span class="kt">void</span> <span class="nf">memory_region_init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                      <span class="o">|</span>    <span class="err">└─</span> <span class="kt">ram_addr_t</span> <span class="nf">qemu_ram_alloc</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                      <span class="o">|</span>         <span class="err">└─</span> <span class="kt">ram_addr_t</span> <span class="nf">qemu_ram_alloc_from_ptr</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                      <span class="o">|</span>
</span></span><span class="line"><span class="cl">                      <span class="err">├─</span> <span class="kt">void</span> <span class="nf">vmstate_register_ram_global</span><span class="p">()</span> <span class="c1">// 将 MR 的 name 写入 RAMBlock 的 idstr
</span></span></span><span class="line"><span class="cl">                      <span class="o">|</span>    <span class="err">└─</span> <span class="kt">void</span> <span class="nf">vmstate_register_ram</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                      <span class="o">|</span>         <span class="err">└─</span> <span class="kt">void</span> <span class="nf">qemu_ram_set_idstr</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                      <span class="o">|</span>
</span></span><span class="line"><span class="cl">                      <span class="err">├─</span> <span class="kt">void</span> <span class="nf">memory_region_init_alias</span><span class="p">()</span>    <span class="c1">// 初始化 ram_below_4g, ram_above_4g
</span></span></span><span class="line"><span class="cl">                      <span class="err">└─</span> <span class="kt">void</span> <span class="nf">memory_region_add_subregion</span><span class="p">()</span> <span class="c1">// 在 system_memory 中添加 subregions
</span></span></span><span class="line"><span class="cl">                           <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">memory_region_add_subregion_common</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                                <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">memory_region_update_topology</span><span class="p">()</span> <span class="c1">// 为 MemoryRegion 生成 FlatView
</span></span></span><span class="line"><span class="cl">                                     <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">address_space_update_topology</span><span class="p">()</span> <span class="c1">// as-&gt;current_map = new_view
</span></span></span><span class="line"><span class="cl">                                          <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">address_space_update_topology_pass</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                                               <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">kvm_region_add</span><span class="p">()</span> <span class="c1">// region_add 对应的回调实现
</span></span></span><span class="line"><span class="cl">                                                    <span class="err">└─</span> <span class="k">static</span> <span class="kt">void</span> <span class="nf">kvm_set_phys_mem</span><span class="p">()</span> <span class="c1">// 根据传入的 section 填充 KVMSlot
</span></span></span><span class="line"><span class="cl">                                                         <span class="err">└─</span> <span class="k">static</span> <span class="kt">int</span> <span class="nf">kvm_set_user_memory_region</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                                                              <span class="err">└─</span> <span class="kt">int</span> <span class="nf">ioctl</span><span class="p">(</span><span class="n">KVM_SET_USER_MEMORY_REGION</span><span class="p">)</span>
</span></span></code></pre></div><p>之前的回调函数注册、AddressSpace 的初始化，实际上均没有对应的物理内存。【实际的内存是在 RAMBlock 中】</p>
<p>我们再回到 <code>qemu</code> 启动的 <code>main</code> 函数中。接下来的初始化过程会调用 <code>pc_init1</code>。在这里面，对于 <code>CPU</code> 虚拟化，我们会调用 <code>pc_cpus_init</code>。另外，<code>pc_init1</code> 还会调用<code>pc_memory_init</code>，进行内存的虚拟化。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="o">*</span><span class="nf">pc_memory_init</span><span class="p">(</span><span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">system_memory</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">kernel_filename</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">kernel_cmdline</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">initrd_filename</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">ram_addr_t</span> <span class="n">below_4g_mem_size</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="kt">ram_addr_t</span> <span class="n">above_4g_mem_size</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">rom_memory</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="n">MemoryRegion</span> <span class="o">**</span><span class="n">ram_memory</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">ram</span><span class="p">,</span> <span class="o">*</span><span class="n">option_rom_mr</span><span class="p">;</span>         <span class="c1">// 两个实体 MR: pc.ram, pc.rom
</span></span></span><span class="line"><span class="cl">    <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">ram_below_4g</span><span class="p">,</span> <span class="o">*</span><span class="n">ram_above_4g</span><span class="p">;</span> <span class="c1">// 两个别名 MR: ram_below_4g, ram_above_4g
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Allocate RAM.  We allocate it as a single memory region and use
</span></span></span><span class="line"><span class="cl"><span class="cm">     * aliases to address portions of it, mostly for backwards compatibility
</span></span></span><span class="line"><span class="cl"><span class="cm">     * with older qemus that used qemu_ram_alloc().
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="n">ram</span> <span class="o">=</span> <span class="nf">g_malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">ram</span><span class="p">));</span> <span class="c1">// 创建 ram
</span></span></span><span class="line"><span class="cl">    <span class="c1">// 分配具体的内存（实际上会创建一个 RAMBlock 并将其 offset 值写入 ram.ram_addr，对应 GPA）
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init_ram</span><span class="p">(</span><span class="n">ram</span><span class="p">,</span> <span class="s">&#34;pc.ram&#34;</span><span class="p">,</span> <span class="n">below_4g_mem_size</span> <span class="o">+</span> <span class="n">above_4g_mem_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 将 MR 的 name 写入 RAMBlock 的 idstr
</span></span></span><span class="line"><span class="cl">    <span class="nf">vmstate_register_ram_global</span><span class="p">(</span><span class="n">ram</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="o">*</span><span class="n">ram_memory</span> <span class="o">=</span> <span class="n">ram</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 创建 ram_below_4g 表示 4G 以下的内存
</span></span></span><span class="line"><span class="cl">    <span class="n">ram_below_4g</span> <span class="o">=</span> <span class="nf">g_malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">ram_below_4g</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init_alias</span><span class="p">(</span><span class="n">ram_below_4g</span><span class="p">,</span> <span class="s">&#34;ram-below-4g&#34;</span><span class="p">,</span> <span class="n">ram</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">below_4g_mem_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 将 ram_below_4g 挂在 system_memory 下
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_region_add_subregion</span><span class="p">(</span><span class="n">system_memory</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ram_below_4g</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">above_4g_mem_size</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ram_above_4g</span> <span class="o">=</span> <span class="nf">g_malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">ram_above_4g</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="nf">memory_region_init_alias</span><span class="p">(</span><span class="n">ram_above_4g</span><span class="p">,</span> <span class="s">&#34;ram-above-4g&#34;</span><span class="p">,</span> <span class="n">ram</span><span class="p">,</span> <span class="n">below_4g_mem_size</span><span class="p">,</span> <span class="n">above_4g_mem_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">memory_region_add_subregion</span><span class="p">(</span><span class="n">system_memory</span><span class="p">,</span> <span class="mh">0x100000000ULL</span><span class="p">,</span> <span class="n">ram_above_4g</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* ... */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这里的重点在于<code>memory_region_init_ram()</code>，它通过<code>qemu_ram_alloc()</code>获取 ram 这个 <code>MemoryRegion</code> 对应的 <code>RAMBlock</code> 的<code>offset</code>，并存入<code>ram.ram_addr</code>，这样就可以在<code>ram_list</code>中根据该字段查找 <code>MR</code> 对应的 <code>RAMBlock</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">memory_region_init_ram</span><span class="p">(</span><span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init</span><span class="p">(</span><span class="n">mr</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span> <span class="c1">// 填充字段，初始化默认值
</span></span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">ram</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span> <span class="c1">// 表示为 RAM
</span></span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">terminates</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span> <span class="c1">// 表示为实体 MemoryRegion
</span></span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">destructor</span> <span class="o">=</span> <span class="n">memory_region_destructor_ram</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">mr</span><span class="o">-&gt;</span><span class="n">ram_addr</span> <span class="o">=</span> <span class="nf">qemu_ram_alloc</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">mr</span><span class="p">);</span> <span class="c1">// 这里保存 RAMBlock 的 offset，即 GPA
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>而 qemu_ram_alloc() 最终会调用 qemu_ram_alloc_from_ptr()，创建一个对应大小 RAMBlock 并分配内存，返回对应的 GPA 地址存入 mr-&gt;ram_addr 中：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="kt">ram_addr_t</span> <span class="nf">qemu_ram_alloc_from_ptr</span><span class="p">(</span><span class="kt">ram_addr_t</span> <span class="n">size</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">host</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="n">MemoryRegion</span> <span class="o">*</span><span class="n">mr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">RAMBlock</span> <span class="o">*</span><span class="n">new_block</span><span class="p">;</span> <span class="c1">// 创建一个 RAMBlock
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">size</span> <span class="o">=</span> <span class="nf">TARGET_PAGE_ALIGN</span><span class="p">(</span><span class="n">size</span><span class="p">);</span> <span class="c1">// 页对齐
</span></span></span><span class="line"><span class="cl">    <span class="n">new_block</span> <span class="o">=</span> <span class="nf">g_malloc0</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">new_block</span><span class="p">));</span> <span class="c1">// 初始化 new_block
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">mr</span> <span class="o">=</span> <span class="n">mr</span><span class="p">;</span> <span class="c1">// 将 new_block-&gt; 指向入参的 MemoryRegion
</span></span></span><span class="line"><span class="cl">    <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">offset</span> <span class="o">=</span> <span class="nf">find_ram_offset</span><span class="p">(</span><span class="n">size</span><span class="p">);</span> <span class="c1">// 从 ram_list 中的 RAMBlock 之间找到一段可以满足 size 需求的 gap，并返回起始地址的 offset，对应 GPA
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">host</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 新建的 RAMBlock host 字段为空，跳过
</span></span></span><span class="line"><span class="cl">        <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span> <span class="o">=</span> <span class="n">host</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">|=</span> <span class="n">RAM_PREALLOC_MASK</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">mem_path</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 未指定 mem_path
</span></span></span><span class="line"><span class="cl"><span class="cp">#if defined (__linux__) &amp;&amp; !defined(TARGET_S390X)
</span></span></span><span class="line"><span class="cl">            <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span> <span class="o">=</span> <span class="nf">file_ram_alloc</span><span class="p">(</span><span class="n">new_block</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">mem_path</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span> <span class="o">=</span> <span class="nf">qemu_vmalloc</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="nf">qemu_madvise</span><span class="p">(</span><span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">QEMU_MADV_MERGEABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span><span class="line"><span class="cl">            <span class="nf">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">&#34;-mem-path option unsupported</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="nf">xen_enabled</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">xen_ram_alloc</span><span class="p">(</span><span class="n">new_block</span><span class="o">-&gt;</span><span class="n">offset</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">mr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nf">kvm_enabled</span><span class="p">())</span> <span class="p">{</span> <span class="c1">// 从这里继续
</span></span></span><span class="line"><span class="cl">                <span class="cm">/* some s390/kvm configurations have special constraints */</span>
</span></span><span class="line"><span class="cl">                <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span> <span class="o">=</span> <span class="nf">kvm_vmalloc</span><span class="p">(</span><span class="n">size</span><span class="p">);</span> <span class="c1">// 实际上还是调用 qemu_vmalloc(size)
</span></span></span><span class="line"><span class="cl">            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span> <span class="o">=</span> <span class="nf">qemu_vmalloc</span><span class="p">(</span><span class="n">size</span><span class="p">);</span> <span class="c1">// 从 QEMU 的线性空间中分配 size 大小的内存，返回 HVA
</span></span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="nf">qemu_madvise</span><span class="p">(</span><span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">QEMU_MADV_MERGEABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">length</span> <span class="o">=</span> <span class="n">size</span><span class="p">;</span> <span class="c1">// 将 length 设置为 size
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">QLIST_INSERT_HEAD</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ram_list</span><span class="p">.</span><span class="n">blocks</span><span class="p">,</span> <span class="n">new_block</span><span class="p">,</span> <span class="n">next</span><span class="p">);</span> <span class="c1">// 将该 RAMBlock 插入 ram_list 头部
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">ram_list</span><span class="p">.</span><span class="n">phys_dirty</span> <span class="o">=</span> <span class="nf">g_realloc</span><span class="p">(</span><span class="n">ram_list</span><span class="p">.</span><span class="n">phys_dirty</span><span class="p">,</span> <span class="c1">// 重新分配 ram_list.phys_dirty 的内存空间
</span></span></span><span class="line"><span class="cl">                                       <span class="nf">last_ram_offset</span><span class="p">()</span> <span class="o">&gt;&gt;</span> <span class="n">TARGET_PAGE_BITS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">memset</span><span class="p">(</span><span class="n">ram_list</span><span class="p">.</span><span class="n">phys_dirty</span> <span class="o">+</span> <span class="p">(</span><span class="n">new_block</span><span class="o">-&gt;</span><span class="n">offset</span> <span class="o">&gt;&gt;</span> <span class="n">TARGET_PAGE_BITS</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">           <span class="mi">0</span><span class="p">,</span> <span class="n">size</span> <span class="o">&gt;&gt;</span> <span class="n">TARGET_PAGE_BITS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">cpu_physical_memory_set_dirty_range</span><span class="p">(</span><span class="n">new_block</span><span class="o">-&gt;</span><span class="n">offset</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">);</span> <span class="c1">// 对该 RAMBlock 对应的内存标记为 dirty
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">qemu_ram_setup_dump</span><span class="p">(</span><span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">kvm_enabled</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="nf">kvm_setup_guest_memory</span><span class="p">(</span><span class="n">new_block</span><span class="o">-&gt;</span><span class="n">host</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">new_block</span><span class="o">-&gt;</span><span class="n">offset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这样一来<code>ram</code>【其实就是<code>system memory</code>，整个<code>Guest</code>物理空间的大小】对应的 <code>RAMBlock</code> 中就分配好了 <code>GPA</code> 和 <code>HVA</code>，就可以<strong>将内存信息同步至 KVM 侧</strong>了。</p>
<p>最后回到<code>pc_memory_init()</code>中，在分配完实际内存后，会先调用<code>memory_region_init_alias()</code>初始化<code>ram_below_4g</code>、<code>ram_above_4g</code>这两个<code>alias</code>，之后调用<code>memory_region_add_subregion()</code>将这两个 <code>alias</code> 指向<code>ram</code>这个实体 <code>MemoryRegion</code>。如下图，该函数最终会触发<code>kvm_region_add()</code>回调，将实际的内存信息传入 <code>KVM</code> 注册。该过程如下图所示，与之前分析的流程相同，此处不再赘述。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127163205.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127163205.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h2 id="总结">总结</h2>
<p>虚拟机的内存管理也是需要用户态的 <code>qemu</code> 和内核态的 <code>KVM</code> 共同完成。为了加速内存映射，需要借助硬件的 <code>EPT</code> 技术。</p>
<h3 id="qemu-侧">QEMU 侧</h3>
<ul>
<li>
<p>创建一系列 <code>MemoryRegion</code>，分别表示 <code>Guest</code> 中的 <code>RAM</code>、<code>ROM</code> 等区域。<code>MemoryRegion</code>之间通过 <code>alias</code> 或 <code>subregions</code> 的方式维护相互之间的关系，从而进一步细化区域的定义</p>
</li>
<li>
<p>对于一个实体 <code>MemoryRegion</code>（非 <code>alias</code>），在初始化内存的过程中 <code>QEMU</code> 会创建它所对应的 <code>RAMBlock</code>。该 <code>RAMBlock</code> 通过调用<code>qemu_ram_alloc_from_ptr()</code>从 <code>QEMU</code> 的进程地址空间中<strong>以 mmap 的方式分配内存</strong>，并负责<strong>维护该 MemoryRegion 对应内存的起始 GPA/HVA/size 等相关信息</strong>【在<code>qemu_ram_alloc_from_ptr</code>中创建的新<code>RAMBlock</code>有<code>offset</code>、<code>host</code>的赋值，即<code>GPA-&gt;HVA</code>的对应关系】</p>
</li>
<li>
<p><code>AddressSpace</code> 表示 <code>Guest</code> 的物理地址空间。如果 <code>AddressSpace</code> 中的 <code>MemoryRegion</code> 发生变化，则注册的 <code>listener</code> 会被触发，将所属的 <code>MemoryRegion</code> 树展开生成一维的 <code>FlatView</code>，比较 FlatRange 是否发生了变化。如果是，则调用相应的方法对 <code>MemoryRegionSection</code> 进行检查，更新 <code>QEMU</code> 中的 <code>KVMSlot</code>，同时填充<code>kvm_userspace_memory_region</code>结构体，作为<code>ioctl()</code>的参数更新 <code>KVM</code> 中的<code>kvm_memory_slot</code></p>
</li>
</ul>
<h3 id="kvm-侧">KVM 侧</h3>
<ul>
<li>
<p>当 <code>QEMU</code> 通过<code>ioctl()</code>创建 <code>vcpu</code> 时，调用<code>kvm_mmu_create()</code>初始化 <code>MMU</code> 相关信息。
当 <code>KVM</code> 要进入 <code>Guest</code> 前，<code>vcpu_enter_guest()=&gt;kvm_mmu_reload()</code>会将根级页表地址加载到 <code>VMCS</code>，让 <code>Guest</code> 使用该页表</p>
</li>
<li>
<p>当发生<code>EPT Violation</code> 时，<code>VM-EXIT</code>到 <code>KVM</code> 中。如果是缺页，则根据 <code>GPA</code> 算出 <code>gfn</code>，再根据 <code>gfn</code> 找到对应的 <code>KVMSlot</code>，从中得到对应的 <code>HVA</code>。然后根据 <code>HVA</code> 算出对应的 <code>pfn</code>，确保该 <code>Page</code> 位于内存中。填好缺失的页之后，需要更新 <code>EPT</code>，完善其中缺少的页表项，逐层补全页表</p>
</li>
</ul>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127163418.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127163418.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<blockquote>
<p>虚拟机的物理内存空间里面的页面当然不是一开始就映射到物理页面的，只有当虚拟机的内存被访问的时候，也即 <code>mmap</code> 分配的虚拟内存空间被访问的时候，先查看 <code>EPT</code> 页表，是否已经映射过，如果已经映射过，则经过四级页表映射，就能访问到物理页面。
如果没有映射过，则虚拟机会通过<code>VM-Exit</code>指令回到宿主机模式，通过 <code>handle_ept_violation</code> 补充页表映射。先是通过 <code>handle_mm_fault</code>为虚拟机的物理内存空间分配真正的物理页面，然后通过 <code>__direct_map</code> 添加 <code>EPT</code> 页表映射。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127171031.jpg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220127171031.jpg" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
</blockquote>
<h2 id="reference">Reference</h2>
<p><a href="http://xudaxian.fun/2020/09/03/QEMU%E5%86%85%E5%AD%98%E7%A9%BA%E9%97%B4%E8%99%9A%E6%8B%9F%E5%8C%96%E5%8F%8A%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/">“QEMU 内存空间虚拟化及内存管理” - B10g | FΓom 许大仙</a>
<a href="https://www.cnblogs.com/LoyenWang/p/13943005.html">【原创】Linux 虚拟化 KVM-Qemu 分析（五）之内存虚拟化 - LoyenWang - 博客园</a>
<a href="https://blog.csdn.net/xiongwenwu/article/details/58586013">KVM/Qemu 工作原理系列目录_xiongwenwu 的专栏-CSDN 博客_qemu 目录结构</a>
<a href="https://abelsu7.top/2019/07/07/kvm-memory-virtualization/">QEMU 内存虚拟化源码分析 | Keep Coding | 苏易北</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 源码分析-外设模拟（以 GPIO 为例）</title>
      <link>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E5%A4%96%E8%AE%BE%E6%A8%A1%E6%8B%9F%E4%BB%A5gpio%E4%B8%BA%E4%BE%8B/</link>
      <pubDate>Thu, 11 Nov 2021 10:11:32 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E5%A4%96%E8%AE%BE%E6%A8%A1%E6%8B%9F%E4%BB%A5gpio%E4%B8%BA%E4%BE%8B/</guid>
      <description>&lt;h2 id=&#34;qemu-模拟外设的原理&#34;&gt;QEMU 模拟外设的原理&lt;/h2&gt;
&lt;p&gt;QEMU 主要是实现了 CPU 核的模拟，可以读写某个地址。
QEMU 的模拟外设的原理很简单：&lt;strong&gt;硬件即内存&lt;/strong&gt;。
要在 QEMU 上模拟某个外设，思路就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU 读某个地址时，QEMU 模拟外设的行为，把数据返回给 CPU&lt;/li&gt;
&lt;li&gt;CPU 写某个地址时，QEMU 获得数据，用来模拟外设的行为。
即：要模拟外设备，我们只需要针对外设的地址提供对应的读写函数即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以 GPIO 为例：&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20211111102930.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20211111102930.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;QEMU 为&lt;code&gt;GPIO&lt;/code&gt;内存地址提供读写回调函数，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sifive_gpio_write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hwaddr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;sifive_gpio_read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hwaddr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;给外设地址提供读写函数&#34;&gt;给外设地址提供读写函数&lt;/h2&gt;
&lt;p&gt;怎么描述某段地址：基地址、大小？如何给这段地址提供读写函数呢？这段地址设置好后，如何添加进&lt;code&gt;system_memory&lt;/code&gt;去？有 2 种方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;法 1：memory_region_init_io/memory_region_add_subregion&lt;/strong&gt;
以&lt;code&gt;SIFIVE_UART&lt;/code&gt;为例，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;memory_region_init_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mmio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;uart_ops&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;n&#34;&gt;TYPE_SIFIVE_UART&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x2000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;memory_region_add_subregion&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;address_space&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mmio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;memory_region_init_io&lt;/code&gt;函数初始化&lt;code&gt;iomem&lt;/code&gt;，读写函数，大小。
&lt;code&gt;memory_region_add_subregion&lt;/code&gt;函数&lt;code&gt;s-&amp;gt;iomem&lt;/code&gt;指定了基地址，并添加进&lt;code&gt;system_memory&lt;/code&gt;中。
以后，客户机上的程序读写这块地址时，就会导致对应的读写函数被调用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;法 2：memory_region_init_io/sysbus_init_mmio/sysbus_mmio_map&lt;/strong&gt;
以&lt;code&gt;SIFIVE_GPIO&lt;/code&gt;为例，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;memory_region_init_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mmio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;OBJECT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gpio_ops&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_SIFIVE_GPIO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SIFIVE_GPIO_SIZE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;sysbus_init_mmio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mmio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;memory_region_init_io&lt;/code&gt;函数初始化&lt;code&gt;iomem&lt;/code&gt;，读写函数，大小。
&lt;code&gt;sysbus_init_mmio&lt;/code&gt;将&lt;code&gt;mmin&lt;/code&gt;传给设备；&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;sysbus_mmio_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gpio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memmap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SIFIVE_E_DEV_GPIO0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;sysbus_mmio_map&lt;/code&gt;从设备中吧&lt;code&gt;mmio&lt;/code&gt;添加进&lt;code&gt;system_memory&lt;/code&gt;并指定基地址。&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="qemu-模拟外设的原理">QEMU 模拟外设的原理</h2>
<p>QEMU 主要是实现了 CPU 核的模拟，可以读写某个地址。
QEMU 的模拟外设的原理很简单：<strong>硬件即内存</strong>。
要在 QEMU 上模拟某个外设，思路就是：</p>
<ul>
<li>CPU 读某个地址时，QEMU 模拟外设的行为，把数据返回给 CPU</li>
<li>CPU 写某个地址时，QEMU 获得数据，用来模拟外设的行为。
即：要模拟外设备，我们只需要针对外设的地址提供对应的读写函数即可。</li>
</ul>
<p>以 GPIO 为例：</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20211111102930.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20211111102930.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>QEMU 为<code>GPIO</code>内存地址提供读写回调函数，</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">sifive_gpio_write</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="n">hwaddr</span> <span class="n">offset</span><span class="p">,</span> <span class="kt">uint64_t</span> <span class="n">value</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">uint64_t</span> <span class="nf">sifive_gpio_read</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="n">hwaddr</span> <span class="n">offset</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">size</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="给外设地址提供读写函数">给外设地址提供读写函数</h2>
<p>怎么描述某段地址：基地址、大小？如何给这段地址提供读写函数呢？这段地址设置好后，如何添加进<code>system_memory</code>去？有 2 种方法。</p>
<p><strong>法 1：memory_region_init_io/memory_region_add_subregion</strong>
以<code>SIFIVE_UART</code>为例，</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">memory_region_init_io</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">mmio</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">uart_ops</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">TYPE_SIFIVE_UART</span><span class="p">,</span> <span class="mh">0x2000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nf">memory_region_add_subregion</span><span class="p">(</span><span class="n">address_space</span><span class="p">,</span> <span class="n">base</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">mmio</span><span class="p">);</span>
</span></span></code></pre></div><p><code>memory_region_init_io</code>函数初始化<code>iomem</code>，读写函数，大小。
<code>memory_region_add_subregion</code>函数<code>s-&gt;iomem</code>指定了基地址，并添加进<code>system_memory</code>中。
以后，客户机上的程序读写这块地址时，就会导致对应的读写函数被调用。</p>
<p><strong>法 2：memory_region_init_io/sysbus_init_mmio/sysbus_mmio_map</strong>
以<code>SIFIVE_GPIO</code>为例，</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">memory_region_init_io</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">mmio</span><span class="p">,</span> <span class="nf">OBJECT</span><span class="p">(</span><span class="n">dev</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">gpio_ops</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">TYPE_SIFIVE_GPIO</span><span class="p">,</span> <span class="n">SIFIVE_GPIO_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">sysbus_init_mmio</span><span class="p">(</span><span class="nf">SYS_BUS_DEVICE</span><span class="p">(</span><span class="n">dev</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">mmio</span><span class="p">);</span>
</span></span></code></pre></div><p><code>memory_region_init_io</code>函数初始化<code>iomem</code>，读写函数，大小。
<code>sysbus_init_mmio</code>将<code>mmin</code>传给设备；</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">sysbus_mmio_map</span><span class="p">(</span><span class="nf">SYS_BUS_DEVICE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">gpio</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="n">memmap</span><span class="p">[</span><span class="n">SIFIVE_E_DEV_GPIO0</span><span class="p">].</span><span class="n">base</span><span class="p">);</span>
</span></span></code></pre></div><p><code>sysbus_mmio_map</code>从设备中吧<code>mmio</code>添加进<code>system_memory</code>并指定基地址。</p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 源码分析 - 虚拟外设创建</title>
      <link>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E8%99%9A%E6%8B%9F%E5%A4%96%E8%AE%BE%E5%88%9B%E5%BB%BA/</link>
      <pubDate>Tue, 09 Nov 2021 17:39:38 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E8%99%9A%E6%8B%9F%E5%A4%96%E8%AE%BE%E5%88%9B%E5%BB%BA/</guid>
      <description>&lt;h1 id=&#34;qom-简介&#34;&gt;QOM 简介&lt;/h1&gt;
&lt;p&gt;QOM(QEMU Object Model) 是 QEMU 的一个模块，用于描述虚拟机的结构，包括虚拟机的 CPU、内存、硬盘、网络、输入输出设备等。QEMU 为了方便整个系统的构建，实现了自己的一套的面向对象机制，也就是 QOM(QEMU Object Model)。它能够方便的表示各个设备（Device）与总线（Bus）之间的关系。&lt;/p&gt;
&lt;p&gt;这个模型主要包含四个结构体：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Object: 是所有对象的 基类 Base Object&lt;/li&gt;
&lt;li&gt;ObjectClass: 是所有类对象的基类&lt;/li&gt;
&lt;li&gt;TypeInfo：是用户用来定义一个 &lt;code&gt;Type&lt;/code&gt; 的工具型的数据结构&lt;/li&gt;
&lt;li&gt;TypeImpl：TypeInfo 抽象数据结构，&lt;code&gt;TypeInfo&lt;/code&gt; 的属性与 &lt;code&gt;TypeImpl&lt;/code&gt; 的属性对应&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 QEMU 里要初始化一个对象需要完成四步：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将 &lt;code&gt;TypeInfo&lt;/code&gt; 注册 &lt;code&gt;TypeImpl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;实例化 &lt;code&gt;Class&lt;/code&gt;（ObjectClass）&lt;/li&gt;
&lt;li&gt;实例化 &lt;code&gt;Object&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;添加 &lt;code&gt;Property&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-09-19-4995177f1aeb782fadaf586f8f5c8b32-qemu-QOM%E5%88%86%E6%9E%90.drawio%20-2--1a4eac.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-09-19-4995177f1aeb782fadaf586f8f5c8b32-qemu-QOM%E5%88%86%E6%9E%90.drawio%20-2--1a4eac.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h1 id=&#34;如何描述硬件&#34;&gt;如何描述硬件&lt;/h1&gt;
&lt;p&gt;一个板子上有很多硬件：芯片，LED、按键、LCD、触摸屏、网卡等等。芯片里面也有很多部件，比如 CPU、GPIO、SD 控制器、中断控制器等等。&lt;/p&gt;
&lt;p&gt;这些硬件，或是部件，各有不同。怎么描述它们？&lt;/p&gt;
&lt;p&gt;每一个都使用一个 &lt;code&gt;TypeInfo&lt;/code&gt; 结构体来描述，&lt;code&gt;TypeInfo&lt;/code&gt; 是用户用来定义一个 Type 的工具型的数据结构。它包含了很多成员变量，这些成员合在一起描述了一个设备类型。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// include/qom/object.h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;size_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;size_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance_align&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_post_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_finalize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;abstract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;size_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;class_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_base_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;InterfaceInfo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;interfaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个结构体我们在刚刚也提到，他在图里是独立的，在注册的时候会将它的信息都传给 Typeimpl 结构体。&lt;/p&gt;
&lt;p&gt;我们以 Timer 为例，我们要添加一个 Timer 外设，首先就要定义一个 Typeinfo 结构体。他在代码中像这样。我们只看 name，这里用一个宏赋值，这个宏是个我们定义的字符串，它唯一标识了这个硬件。这些结构体在运行时会被注册进程序里，保存在一个链表中备用，为什么是备用，因为不是每一个硬件都会被用到。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// hw/timer/dw_timer.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;          &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_DW_TIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DWTimerState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些结构体在运行时会被注册进程序里，保存在一个链表中备用，为什么是备用，因为不是每一个硬件都会被用到。&lt;/p&gt;
&lt;h1 id=&#34;如何注册硬件&#34;&gt;如何注册硬件&lt;/h1&gt;
&lt;p&gt;什么是注册，说白了就是将一些可能需要的信息添加到系统中，在系统运行时能够随时调用到。就拿 Timer 来说，现在将一些信息添加到了列表，系统运行起来时我可以随时从链表中取出 Timer 这个设备的信息，用来实例化一个 Timer，但是我没有注册 Timer，也就是没有将其加入到链表，那我后期就无法找到它。&lt;/p&gt;
&lt;p&gt;怎么注册这些&lt;code&gt;TypeInfo&lt;/code&gt;结构体呢？在实现的源码中有这个函数 &lt;code&gt;dw_timer_register_types()&lt;/code&gt;，他是用来注册 Timer 这个设备的。&lt;/p&gt;
&lt;p&gt;我们追根溯源，调用过程如下，&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-03-53-4d2fcf4deb7a9ed9c13a22a7fe3111be-20221107160352-084c2a.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-03-53-4d2fcf4deb7a9ed9c13a22a7fe3111be-20221107160352-084c2a.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分配一个 &lt;code&gt;TypeImpl&lt;/code&gt; 结构体，使用 &lt;code&gt;Typeinfo&lt;/code&gt; 来设置它&lt;/li&gt;
&lt;li&gt;把 &lt;code&gt;TypeImpl&lt;/code&gt; 加入链表：&lt;code&gt;type_table&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 QEMU 里面，有一个全局的哈希表 &lt;code&gt;type_table&lt;/code&gt;，用来存放所有定义的类。在 &lt;code&gt;type_new&lt;/code&gt; 里面，我们先从全局表里面根据名字 &lt;code&gt;type_table_lookup&lt;/code&gt; 查找找这个类。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果找到，说明这个类曾经被注册过，就报错；&lt;/li&gt;
&lt;li&gt;如果没有找到，说明这是一个新的类，则将 &lt;code&gt;Typeinfo&lt;/code&gt; 里面信息填到 &lt;code&gt;TypeImpl&lt;/code&gt; 里面。&lt;code&gt;type_table_add&lt;/code&gt; 会将这个类注册到全局的表里面。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-11-19-19d9b8686c2a5758a0231181b0fb4dfa-qemu-%E6%B3%A8%E5%86%8C%E8%AE%BE%E5%A4%87.drawio-8a4c5c.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-11-19-19d9b8686c2a5758a0231181b0fb4dfa-qemu-%E6%B3%A8%E5%86%8C%E8%AE%BE%E5%A4%87.drawio-8a4c5c.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;以上的过程可以用上图来表示。&lt;code&gt;Typeinfo&lt;/code&gt; 通过 &lt;code&gt;type_new()&lt;/code&gt; 生成一个对应的 &lt;code&gt;TypeImpl&lt;/code&gt; 类型，并以 &lt;code&gt;name&lt;/code&gt; 为关键字添加到名为 &lt;code&gt;type_table&lt;/code&gt; 的一个 hash table 中。&lt;/p&gt;
&lt;p&gt;什么时候注册这些设备呢？不需要我们去调用注册函数，以 Timer 为例，在 &lt;code&gt;hw/timer/dw_timer.c&lt;/code&gt; 中有如下代码，一般在最后一行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dw_timer_register_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;F12&lt;/code&gt;找到这个宏定义，我们追根溯源，调用过程如下&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;module_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;register_module_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dw_timer_register_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define type_init(function) module_init(function, MODULE_INIT_QOM)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define module_init(function, type)                                         \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;{                                                                           \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    register_module_init(function, type);                                   \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;register_module_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;module_init_type&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ModuleEntry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;//构造 ModuleEntry
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ModuleTypeList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;//构造链表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;//设置初始化函数，fn 即 sifive_gpio_register_types
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;l&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_INSERT_TAIL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//将 ModuleEntry 插入链表尾
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;type_init&lt;/code&gt;是个宏定义，调用了&lt;code&gt;__attribute__((constructor))&lt;/code&gt;函数，我们知道这个 C 语言中位数不多的在&lt;code&gt;main&lt;/code&gt;函数执行前，执行的函数。函数中调用了&lt;code&gt;register_module_init&lt;/code&gt;注册函数，说明在&lt;code&gt;main&lt;/code&gt;函数执行前，已经注册好硬件了。该函数将一个新的&lt;code&gt;ModuleEntry&lt;/code&gt;加到链表里。&lt;/p&gt;
&lt;p&gt;注意，注册的只是个函数，并不是注册了设备。也就是已上过程，只是把一个 &lt;code&gt;ModuleEntry&lt;/code&gt; 放到了一个链表里，这个 &lt;code&gt;ModuleEntry&lt;/code&gt; 带了两个信息，一个是函数，一个是类型。这个函数就是我们真正的注册注册函数。&lt;/p&gt;
&lt;p&gt;已上过程大概是如下所示：&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-31-54-b5c189903fc73c76057f57a95a10310d-qemu-module%20init%20%E9%93%BE%E8%A1%A8.drawio-1309c1.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-31-54-b5c189903fc73c76057f57a95a10310d-qemu-module%20init%20%E9%93%BE%E8%A1%A8.drawio-1309c1.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;那什么时候还真正注册设备呢，我们就得回到主函数，它有以下调用流程，在 &lt;code&gt;module_call_init&lt;/code&gt; 中，我们会找到 &lt;code&gt;MODULE_INIT_QOM&lt;/code&gt; 这种类型对应的 &lt;code&gt;ModuleTypeList&lt;/code&gt;
找出列表中所有的 &lt;code&gt;ModuleEntry&lt;/code&gt;，然后调用每个 &lt;code&gt;ModuleEntry&lt;/code&gt; 的 &lt;code&gt;init&lt;/code&gt; 函数。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-34-25-ad40a288b1376c0f9136c609b7d7ee0c-20221107183424-6d1641.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-34-25-ad40a288b1376c0f9136c609b7d7ee0c-20221107183424-6d1641.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// softmmu/runstate.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;module_call_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MODULE_INIT_QOM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// utils/module.csoftmmu/runstate.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;module_call_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_init_type&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ModuleTypeList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ModuleEntry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 找到 MODULE_INIT_QOM 这种类型对应的 ModuleTypeList
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;l&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_FOREACH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;初始化设备&#34;&gt;初始化设备&lt;/h1&gt;
&lt;p&gt;到这里我们需要注意，我们在注册设备的时候虽然将设备从 &lt;code&gt;Typeinfo&lt;/code&gt; 变成了 &lt;code&gt;TypeImpl&lt;/code&gt;，把 &lt;code&gt;Typeinfo&lt;/code&gt; 里的信息都复制到了 &lt;code&gt;TypeImpl&lt;/code&gt;，但是 &lt;code&gt;class_init&lt;/code&gt; 还没有被调用，也即这个类现在还处于纸面的状态。&lt;/p&gt;
&lt;p&gt;什么时候才真正初始化这个类呢，这得等在用到它的时候。我们在一块板子上才会用到一个设备。我们使用的是 Sifive-e 这个板子，准确来说我们用的不是这个板子，我们只是在原先的代码上做了修改。&lt;/p&gt;
&lt;p&gt;为了方便描述，就当是用的 sifive-e 这个板子。在实现的源码里，有 &lt;code&gt;object_initialize_child&lt;/code&gt;函数，跟踪一下调用流程可以看到最后在 &lt;code&gt;type_initialize&lt;/code&gt; 函数中初始化了类。同时我们也看到在 &lt;code&gt;object_init_with_type&lt;/code&gt; 函数中实例化了类。这个稍后再讲。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// hw/riscv/sifive_e.cstatic void sifive_e_soc_init(Object *obj)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MachineState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ms&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;MACHINE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;qdev_get_machine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;SiFiveESoCState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;RISCV_E_SOC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;object_initialize_child&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;timer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;n&#34;&gt;TYPE_DW_TIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;object_initialize_child&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_DW_TIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;object_initialize_child_internal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;object_initialize_child_with_props&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;object_initialize_child_with_propsv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;object_initialize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;nf&#34;&gt;object_initialize_with_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;nf&#34;&gt;type_initialize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;nf&#34;&gt;object_init_with_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;       
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在调用 &lt;code&gt;class_init&lt;/code&gt; 函数时，其实就是调用的设备模块下的 &lt;code&gt;dw_timer_class_init&lt;/code&gt;，这个函数中又是一些配置，尤其是 &lt;code&gt;realize&lt;/code&gt; 函数的配置。还有一些属性的配置，如 Timer 的频率。&lt;/p&gt;
&lt;p&gt;到这里，我们才有有了一个真正意义上的设备类。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;hw&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dw_timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dw_timer_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 这里又是一些配置，尤其是回调函数的配置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DeviceClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;DEVICE_CLASS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_reset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 设置 Timer 基本属性如频率等
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;device_class_set_props&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_properties&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vmsd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vmstate_dw_timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;dc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;realize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_realize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;实例化设备&#34;&gt;实例化设备&lt;/h1&gt;
&lt;p&gt;说白了初始化过程就是在配置各种结构体成员的过程，比如刚刚的初始化过程就是在配置 &lt;code&gt;DeviceClass&lt;/code&gt; 这个类的各个成员。实际上我们还没有真正实例化 Timer，我们还不能使用它。&lt;/p&gt;
&lt;p&gt;我们只有在实例化后才能使用它，也就是之前提到的 &lt;code&gt;instance_init()&lt;/code&gt;。但是在 QEMU 中要实例化一个设备，不仅仅需要调用 &lt;code&gt;instance_init&lt;/code&gt;，还需要调用刚刚初始化时设置的 &lt;code&gt;realize&lt;/code&gt; 函数。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_info&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;          &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_DW_TIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DWTimerState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// hw/timer/dw_timer.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dw_timer_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DWTimerState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;DWTIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 为这段内存注册回调函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iomem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dw_timer_ops&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                          &lt;span class=&#34;s&#34;&gt;&amp;#34;dw_timer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x2000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;sysbus_init_mmio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iomem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这两个函数的功能很像，具体细节差异我也还没弄明白，但是需要注意的是 &lt;code&gt;instance_init&lt;/code&gt; 一定要在 &lt;code&gt;realize&lt;/code&gt; 之前完成，并且没有错误。否则将无法完成实例化。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// hw/timer/dw_timer.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dw_timer_realize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DeviceState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Error&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;**&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;errp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DWTimerState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;DWTIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;sysbus_init_irq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;irq&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;timer_new_ns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QEMU_CLOCK_VIRTUAL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_interrupt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;instance_init&lt;/code&gt; 这个函数主要完成的工作就是为一段内存绑定了读写函数，为什么要这么做，我们再往下看。&lt;/p&gt;
&lt;h1 id=&#34;如何操作设备&#34;&gt;如何操作设备&lt;/h1&gt;
&lt;p&gt;设备创建完成了，那 QEMU 是如何模拟设备的行为的？这也是 QEMU 驱动开发最重要的一步，因为以上的部分是实现设备所必须的，我们只需要参考其他已经实现的模块，修改成我们的信息即可。&lt;/p&gt;
&lt;p&gt;但是每个 IP 的寄存器不同，他们的功能也就不同，这是我们真正需要实现的内容。我们知道写驱动其实就是操作各个 IP 的寄存器，以实现想要的功能。对应到 QEMU 中，就成了在操作各个寄存器时，我们要在 QEMU 中将驱动寄存器的功能先模拟出来，再返回给驱动程序。&lt;/p&gt;
&lt;p&gt;以 Timer 为例我想要获取 &lt;code&gt;TimerNLoadCount&lt;/code&gt; 的值，真实硬件有这个寄存器保存了值，但是 QEMU 上我们就得维护一个变量去保存这个值。在需要的时候能读取到。比如代码里比较重要的参数是 &lt;code&gt;offset&lt;/code&gt;，这个参数是基于外设基地址的偏移，其实就是寄存器的偏移量。比如我们查看 Timer 的手册，&lt;code&gt;TimerNLoadCount&lt;/code&gt; 偏移量为 0，所以当我们在驱动中读取地址为 &lt;code&gt;0x2000000&lt;/code&gt; 时，代码就会走到这里，因为我们维护了一个 &lt;code&gt;timer_n_load_count&lt;/code&gt; 变量，所以我直接将这个变量当前值返回即可，这就是这个寄存器的值。我们要写这个寄存器也同理，我们需要更新 &lt;code&gt;timer_n_load_count&lt;/code&gt; 这个变量。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// hw/timer/dw_timer.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dw_timer_read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hwaddr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                           &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DWTimerState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;TimerNLoadCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0x14&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0x14&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer_n_load_count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dw_timer_write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hwaddr&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;kt&#34;&gt;uint64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;val64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DWTimerState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;uint32_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;val64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;change&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;TimerNLoadCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0x14&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0x14&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x14&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer_n_load_count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;set_alarm_time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;读写函数写好了，需要给谁调用呢。我们刚刚提到了，这是个回调函数，我们需要给一段内存注册这个回调函数。如代码所示。我们给 Timer iomem 绑定了读写函数。具体哪一段地址还没定，但是我们定了 &lt;code&gt;0x2000&lt;/code&gt; 这么长一段。我觉得这里应该是最高位的一个寄存器偏移量。因为再高就没啥用了，或者就是 SoC 里定的寄存器空间大小 &lt;code&gt;0x1000&lt;/code&gt;。这里应该是为了图省事写的一个值。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// hw/timer/dw_timer.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MemoryRegionOps&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_ops&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dw_timer_write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endianness&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DEVICE_NATIVE_ENDIAN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dw_timer_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;DWTimerState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;DWTIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 为这段内存注册回调函数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;memory_region_init_io&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iomem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dw_timer_ops&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                          &lt;span class=&#34;s&#34;&gt;&amp;#34;dw_timer&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x2000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;sysbus_init_mmio&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iomem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面在&lt;code&gt;hw/riscv/sifive_e.c&lt;/code&gt;里会映射寄存器空间到 QEMU 的内存空间。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-C&#34; data-lang=&#34;C&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// hw/riscv/sifive_e.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;sysbus_mmio_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SYS_BUS_DEVICE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;memmap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SIFIVE_E_DEV_TIMER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;参考&#34;&gt;参考&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://www.cnblogs.com/haiyonghao/p/14440761.html&#34;&gt;QEMU 中基于 QOM 的 VFIO 类的定义 - EwanHai - 博客园&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
      <content:encoded><![CDATA[<h1 id="qom-简介">QOM 简介</h1>
<p>QOM(QEMU Object Model) 是 QEMU 的一个模块，用于描述虚拟机的结构，包括虚拟机的 CPU、内存、硬盘、网络、输入输出设备等。QEMU 为了方便整个系统的构建，实现了自己的一套的面向对象机制，也就是 QOM(QEMU Object Model)。它能够方便的表示各个设备（Device）与总线（Bus）之间的关系。</p>
<p>这个模型主要包含四个结构体：</p>
<ul>
<li>Object: 是所有对象的 基类 Base Object</li>
<li>ObjectClass: 是所有类对象的基类</li>
<li>TypeInfo：是用户用来定义一个 <code>Type</code> 的工具型的数据结构</li>
<li>TypeImpl：TypeInfo 抽象数据结构，<code>TypeInfo</code> 的属性与 <code>TypeImpl</code> 的属性对应</li>
</ul>
<p>在 QEMU 里要初始化一个对象需要完成四步：</p>
<ul>
<li>将 <code>TypeInfo</code> 注册 <code>TypeImpl</code></li>
<li>实例化 <code>Class</code>（ObjectClass）</li>
<li>实例化 <code>Object</code></li>
<li>添加 <code>Property</code></li>
</ul>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-09-19-4995177f1aeb782fadaf586f8f5c8b32-qemu-QOM%E5%88%86%E6%9E%90.drawio%20-2--1a4eac.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/15-09-19-4995177f1aeb782fadaf586f8f5c8b32-qemu-QOM%E5%88%86%E6%9E%90.drawio%20-2--1a4eac.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h1 id="如何描述硬件">如何描述硬件</h1>
<p>一个板子上有很多硬件：芯片，LED、按键、LCD、触摸屏、网卡等等。芯片里面也有很多部件，比如 CPU、GPIO、SD 控制器、中断控制器等等。</p>
<p>这些硬件，或是部件，各有不同。怎么描述它们？</p>
<p>每一个都使用一个 <code>TypeInfo</code> 结构体来描述，<code>TypeInfo</code> 是用户用来定义一个 Type 的工具型的数据结构。它包含了很多成员变量，这些成员合在一起描述了一个设备类型。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// include/qom/object.h
</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">TypeInfo</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">parent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">instance_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">instance_align</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">instance_init</span><span class="p">)(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">instance_post_init</span><span class="p">)(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">instance_finalize</span><span class="p">)(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">bool</span> <span class="n">abstract</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">class_size</span><span class="p">;</span>    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">class_init</span><span class="p">)(</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="n">klass</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">class_base_init</span><span class="p">)(</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="n">klass</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="o">*</span><span class="n">class_data</span><span class="p">;</span>    <span class="n">InterfaceInfo</span> <span class="o">*</span><span class="n">interfaces</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>这个结构体我们在刚刚也提到，他在图里是独立的，在注册的时候会将它的信息都传给 Typeimpl 结构体。</p>
<p>我们以 Timer 为例，我们要添加一个 Timer 外设，首先就要定义一个 Typeinfo 结构体。他在代码中像这样。我们只看 name，这里用一个宏赋值，这个宏是个我们定义的字符串，它唯一标识了这个硬件。这些结构体在运行时会被注册进程序里，保存在一个链表中备用，为什么是备用，因为不是每一个硬件都会被用到。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// hw/timer/dw_timer.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">dw_timer_info</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span>          <span class="o">=</span> <span class="n">TYPE_DW_TIMER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span>        <span class="o">=</span> <span class="n">TYPE_SYS_BUS_DEVICE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DWTimerState</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_init</span> <span class="o">=</span> <span class="n">dw_timer_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">class_init</span>    <span class="o">=</span> <span class="n">dw_timer_class_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>这些结构体在运行时会被注册进程序里，保存在一个链表中备用，为什么是备用，因为不是每一个硬件都会被用到。</p>
<h1 id="如何注册硬件">如何注册硬件</h1>
<p>什么是注册，说白了就是将一些可能需要的信息添加到系统中，在系统运行时能够随时调用到。就拿 Timer 来说，现在将一些信息添加到了列表，系统运行起来时我可以随时从链表中取出 Timer 这个设备的信息，用来实例化一个 Timer，但是我没有注册 Timer，也就是没有将其加入到链表，那我后期就无法找到它。</p>
<p>怎么注册这些<code>TypeInfo</code>结构体呢？在实现的源码中有这个函数 <code>dw_timer_register_types()</code>，他是用来注册 Timer 这个设备的。</p>
<p>我们追根溯源，调用过程如下，</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-03-53-4d2fcf4deb7a9ed9c13a22a7fe3111be-20221107160352-084c2a.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-03-53-4d2fcf4deb7a9ed9c13a22a7fe3111be-20221107160352-084c2a.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<ul>
<li>分配一个 <code>TypeImpl</code> 结构体，使用 <code>Typeinfo</code> 来设置它</li>
<li>把 <code>TypeImpl</code> 加入链表：<code>type_table</code></li>
</ul>
<p>在 QEMU 里面，有一个全局的哈希表 <code>type_table</code>，用来存放所有定义的类。在 <code>type_new</code> 里面，我们先从全局表里面根据名字 <code>type_table_lookup</code> 查找找这个类。</p>
<ul>
<li>如果找到，说明这个类曾经被注册过，就报错；</li>
<li>如果没有找到，说明这是一个新的类，则将 <code>Typeinfo</code> 里面信息填到 <code>TypeImpl</code> 里面。<code>type_table_add</code> 会将这个类注册到全局的表里面。</li>
</ul>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-11-19-19d9b8686c2a5758a0231181b0fb4dfa-qemu-%E6%B3%A8%E5%86%8C%E8%AE%BE%E5%A4%87.drawio-8a4c5c.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/16-11-19-19d9b8686c2a5758a0231181b0fb4dfa-qemu-%E6%B3%A8%E5%86%8C%E8%AE%BE%E5%A4%87.drawio-8a4c5c.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>以上的过程可以用上图来表示。<code>Typeinfo</code> 通过 <code>type_new()</code> 生成一个对应的 <code>TypeImpl</code> 类型，并以 <code>name</code> 为关键字添加到名为 <code>type_table</code> 的一个 hash table 中。</p>
<p>什么时候注册这些设备呢？不需要我们去调用注册函数，以 Timer 为例，在 <code>hw/timer/dw_timer.c</code> 中有如下代码，一般在最后一行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">type_init</span><span class="p">(</span><span class="n">dw_timer_register_types</span><span class="p">)</span>
</span></span></code></pre></div><p><code>F12</code>找到这个宏定义，我们追根溯源，调用过程如下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">type_init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="o">-&gt;</span> <span class="nf">module_init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="o">-&gt;</span> <span class="nf">register_module_init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">type_init</span><span class="p">(</span><span class="n">dw_timer_register_types</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define type_init(function) module_init(function, MODULE_INIT_QOM)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define module_init(function, type)                                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
</span></span></span><span class="line"><span class="cl"><span class="cp">{                                                                           \
</span></span></span><span class="line"><span class="cl"><span class="cp">    register_module_init(function, type);                                   \
</span></span></span><span class="line"><span class="cl"><span class="cp">}
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">register_module_init</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">fn</span><span class="p">)(</span><span class="kt">void</span><span class="p">),</span> <span class="n">module_init_type</span> <span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModuleEntry</span> <span class="o">*</span><span class="n">e</span><span class="p">;</span>     <span class="c1">//构造 ModuleEntry
</span></span></span><span class="line"><span class="cl">    <span class="n">ModuleTypeList</span> <span class="o">*</span><span class="n">l</span><span class="p">;</span>  <span class="c1">//构造链表
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">e</span> <span class="o">=</span> <span class="nf">g_malloc0</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">e</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">e</span><span class="o">-&gt;</span><span class="n">init</span> <span class="o">=</span> <span class="n">fn</span><span class="p">;</span>       <span class="c1">//设置初始化函数，fn 即 sifive_gpio_register_types
</span></span></span><span class="line"><span class="cl">    <span class="n">e</span><span class="o">-&gt;</span><span class="n">type</span> <span class="o">=</span> <span class="n">type</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">l</span> <span class="o">=</span> <span class="nf">find_type</span><span class="p">(</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_INSERT_TAIL</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">node</span><span class="p">);</span><span class="c1">//将 ModuleEntry 插入链表尾
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>type_init</code>是个宏定义，调用了<code>__attribute__((constructor))</code>函数，我们知道这个 C 语言中位数不多的在<code>main</code>函数执行前，执行的函数。函数中调用了<code>register_module_init</code>注册函数，说明在<code>main</code>函数执行前，已经注册好硬件了。该函数将一个新的<code>ModuleEntry</code>加到链表里。</p>
<p>注意，注册的只是个函数，并不是注册了设备。也就是已上过程，只是把一个 <code>ModuleEntry</code> 放到了一个链表里，这个 <code>ModuleEntry</code> 带了两个信息，一个是函数，一个是类型。这个函数就是我们真正的注册注册函数。</p>
<p>已上过程大概是如下所示：</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-31-54-b5c189903fc73c76057f57a95a10310d-qemu-module%20init%20%E9%93%BE%E8%A1%A8.drawio-1309c1.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-31-54-b5c189903fc73c76057f57a95a10310d-qemu-module%20init%20%E9%93%BE%E8%A1%A8.drawio-1309c1.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>那什么时候还真正注册设备呢，我们就得回到主函数，它有以下调用流程，在 <code>module_call_init</code> 中，我们会找到 <code>MODULE_INIT_QOM</code> 这种类型对应的 <code>ModuleTypeList</code>
找出列表中所有的 <code>ModuleEntry</code>，然后调用每个 <code>ModuleEntry</code> 的 <code>init</code> 函数。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-34-25-ad40a288b1376c0f9136c609b7d7ee0c-20221107183424-6d1641.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/18-34-25-ad40a288b1376c0f9136c609b7d7ee0c-20221107183424-6d1641.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// softmmu/runstate.c
</span></span></span><span class="line"><span class="cl"><span class="nf">module_call_init</span><span class="p">(</span><span class="n">MODULE_INIT_QOM</span><span class="p">);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// utils/module.csoftmmu/runstate.c
</span></span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">module_call_init</span><span class="p">(</span><span class="n">module_init_type</span> <span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModuleTypeList</span> <span class="o">*</span><span class="n">l</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModuleEntry</span> <span class="o">*</span><span class="n">e</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 找到 MODULE_INIT_QOM 这种类型对应的 ModuleTypeList
</span></span></span><span class="line"><span class="cl">    <span class="n">l</span> <span class="o">=</span> <span class="nf">find_type</span><span class="p">(</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_FOREACH</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">l</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="nf">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="初始化设备">初始化设备</h1>
<p>到这里我们需要注意，我们在注册设备的时候虽然将设备从 <code>Typeinfo</code> 变成了 <code>TypeImpl</code>，把 <code>Typeinfo</code> 里的信息都复制到了 <code>TypeImpl</code>，但是 <code>class_init</code> 还没有被调用，也即这个类现在还处于纸面的状态。</p>
<p>什么时候才真正初始化这个类呢，这得等在用到它的时候。我们在一块板子上才会用到一个设备。我们使用的是 Sifive-e 这个板子，准确来说我们用的不是这个板子，我们只是在原先的代码上做了修改。</p>
<p>为了方便描述，就当是用的 sifive-e 这个板子。在实现的源码里，有 <code>object_initialize_child</code>函数，跟踪一下调用流程可以看到最后在 <code>type_initialize</code> 函数中初始化了类。同时我们也看到在 <code>object_init_with_type</code> 函数中实例化了类。这个稍后再讲。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// hw/riscv/sifive_e.cstatic void sifive_e_soc_init(Object *obj)
</span></span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MachineState</span> <span class="o">*</span><span class="n">ms</span> <span class="o">=</span> <span class="nf">MACHINE</span><span class="p">(</span><span class="nf">qdev_get_machine</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">SiFiveESoCState</span> <span class="o">*</span><span class="n">s</span> <span class="o">=</span> <span class="nf">RISCV_E_SOC</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="p">.</span>
</span></span><span class="line"><span class="cl">	<span class="p">.</span>
</span></span><span class="line"><span class="cl">	<span class="p">.</span>    <span class="nf">object_initialize_child</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="s">&#34;timer&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">timer</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">TYPE_DW_TIMER</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="nf">object_initialize_child</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">timer</span><span class="p">,</span> <span class="n">TYPE_DW_TIMER</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">object_initialize_child_internal</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="nf">object_initialize_child_with_props</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="nf">object_initialize_child_with_propsv</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="nf">object_initialize</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">object_initialize_with_type</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                        <span class="nf">type_initialize</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>
</span></span><span class="line"><span class="cl">                            <span class="k">if</span> <span class="p">(</span><span class="n">ti</span><span class="o">-&gt;</span><span class="n">class_init</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="n">ti</span><span class="o">-&gt;</span><span class="nf">class_init</span><span class="p">(</span><span class="n">ti</span><span class="o">-&gt;</span><span class="n">class</span><span class="p">,</span> <span class="n">ti</span><span class="o">-&gt;</span><span class="n">class_data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="nf">object_init_with_type</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                        <span class="p">{</span>       
</span></span><span class="line"><span class="cl">                            <span class="k">if</span> <span class="p">(</span><span class="n">ti</span><span class="o">-&gt;</span><span class="n">instance_init</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                                <span class="n">ti</span><span class="o">-&gt;</span><span class="nf">instance_init</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                            <span class="p">}</span>
</span></span><span class="line"><span class="cl">                        <span class="p">}</span>
</span></span></code></pre></div><p>在调用 <code>class_init</code> 函数时，其实就是调用的设备模块下的 <code>dw_timer_class_init</code>，这个函数中又是一些配置，尤其是 <code>realize</code> 函数的配置。还有一些属性的配置，如 Timer 的频率。</p>
<p>到这里，我们才有有了一个真正意义上的设备类。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="n">hw</span><span class="o">/</span><span class="n">timer</span><span class="o">/</span><span class="n">dw_timer</span><span class="p">.</span><span class="n">c</span> 
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">dw_timer_class_init</span><span class="p">(</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="n">klass</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 这里又是一些配置，尤其是回调函数的配置
</span></span></span><span class="line"><span class="cl">    <span class="n">DeviceClass</span> <span class="o">*</span><span class="n">dc</span> <span class="o">=</span> <span class="nf">DEVICE_CLASS</span><span class="p">(</span><span class="n">klass</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">reset</span> <span class="o">=</span> <span class="n">dw_timer_reset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 设置 Timer 基本属性如频率等
</span></span></span><span class="line"><span class="cl">    <span class="nf">device_class_set_props</span><span class="p">(</span><span class="n">dc</span><span class="p">,</span> <span class="n">dw_timer_properties</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">vmsd</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">vmstate_dw_timer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">dc</span><span class="o">-&gt;</span><span class="n">realize</span> <span class="o">=</span> <span class="n">dw_timer_realize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="实例化设备">实例化设备</h1>
<p>说白了初始化过程就是在配置各种结构体成员的过程，比如刚刚的初始化过程就是在配置 <code>DeviceClass</code> 这个类的各个成员。实际上我们还没有真正实例化 Timer，我们还不能使用它。</p>
<p>我们只有在实例化后才能使用它，也就是之前提到的 <code>instance_init()</code>。但是在 QEMU 中要实例化一个设备，不仅仅需要调用 <code>instance_init</code>，还需要调用刚刚初始化时设置的 <code>realize</code> 函数。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">dw_timer_info</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span>          <span class="o">=</span> <span class="n">TYPE_DW_TIMER</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span>        <span class="o">=</span> <span class="n">TYPE_SYS_BUS_DEVICE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">DWTimerState</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_init</span> <span class="o">=</span> <span class="n">dw_timer_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">class_init</span>    <span class="o">=</span> <span class="n">dw_timer_class_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="c1">// hw/timer/dw_timer.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">dw_timer_init</span><span class="p">(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DWTimerState</span> <span class="o">*</span><span class="n">s</span> <span class="o">=</span> <span class="nf">DWTIMER</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 为这段内存注册回调函数
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init_io</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">iomem</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dw_timer_ops</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="s">&#34;dw_timer&#34;</span><span class="p">,</span> <span class="mh">0x2000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sysbus_init_mmio</span><span class="p">(</span><span class="nf">SYS_BUS_DEVICE</span><span class="p">(</span><span class="n">obj</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">iomem</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这两个函数的功能很像，具体细节差异我也还没弄明白，但是需要注意的是 <code>instance_init</code> 一定要在 <code>realize</code> 之前完成，并且没有错误。否则将无法完成实例化。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// hw/timer/dw_timer.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">dw_timer_realize</span><span class="p">(</span><span class="n">DeviceState</span> <span class="o">*</span><span class="n">dev</span><span class="p">,</span> <span class="n">Error</span> <span class="o">**</span><span class="n">errp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DWTimerState</span> <span class="o">*</span><span class="n">s</span> <span class="o">=</span> <span class="nf">DWTIMER</span><span class="p">(</span><span class="n">dev</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sysbus_init_irq</span><span class="p">(</span><span class="nf">SYS_BUS_DEVICE</span><span class="p">(</span><span class="n">dev</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">irq</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">s</span><span class="o">-&gt;</span><span class="n">timer</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nf">timer_new_ns</span><span class="p">(</span><span class="n">QEMU_CLOCK_VIRTUAL</span><span class="p">,</span> <span class="n">dw_timer_interrupt</span><span class="p">,</span> <span class="n">s</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>instance_init</code> 这个函数主要完成的工作就是为一段内存绑定了读写函数，为什么要这么做，我们再往下看。</p>
<h1 id="如何操作设备">如何操作设备</h1>
<p>设备创建完成了，那 QEMU 是如何模拟设备的行为的？这也是 QEMU 驱动开发最重要的一步，因为以上的部分是实现设备所必须的，我们只需要参考其他已经实现的模块，修改成我们的信息即可。</p>
<p>但是每个 IP 的寄存器不同，他们的功能也就不同，这是我们真正需要实现的内容。我们知道写驱动其实就是操作各个 IP 的寄存器，以实现想要的功能。对应到 QEMU 中，就成了在操作各个寄存器时，我们要在 QEMU 中将驱动寄存器的功能先模拟出来，再返回给驱动程序。</p>
<p>以 Timer 为例我想要获取 <code>TimerNLoadCount</code> 的值，真实硬件有这个寄存器保存了值，但是 QEMU 上我们就得维护一个变量去保存这个值。在需要的时候能读取到。比如代码里比较重要的参数是 <code>offset</code>，这个参数是基于外设基地址的偏移，其实就是寄存器的偏移量。比如我们查看 Timer 的手册，<code>TimerNLoadCount</code> 偏移量为 0，所以当我们在驱动中读取地址为 <code>0x2000000</code> 时，代码就会走到这里，因为我们维护了一个 <code>timer_n_load_count</code> 变量，所以我直接将这个变量当前值返回即可，这就是这个寄存器的值。我们要写这个寄存器也同理，我们需要更新 <code>timer_n_load_count</code> 这个变量。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// hw/timer/dw_timer.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">uint64_t</span> <span class="nf">dw_timer_read</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="n">hwaddr</span> <span class="n">offset</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="kt">unsigned</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DWTimerState</span> <span class="o">*</span><span class="n">s</span> <span class="o">=</span> <span class="n">opaque</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">TimerNLoadCount</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="o">*</span><span class="mh">0x14</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">2</span><span class="o">*</span><span class="mh">0x14</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">index</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">/</span> <span class="mh">0x14</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">timer_n_load_count</span><span class="p">[</span><span class="n">index</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">dw_timer_write</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">,</span> <span class="n">hwaddr</span> <span class="n">offset</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="kt">uint64_t</span> <span class="n">val64</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="n">size</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DWTimerState</span> <span class="o">*</span><span class="n">s</span> <span class="o">=</span> <span class="n">opaque</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">value</span> <span class="o">=</span> <span class="n">val64</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">change</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>    <span class="k">switch</span> <span class="p">(</span><span class="n">offset</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="nl">TimerNLoadCount</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">1</span><span class="o">*</span><span class="mh">0x14</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="mi">2</span><span class="o">*</span><span class="mh">0x14</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">index</span> <span class="o">=</span> <span class="p">(</span><span class="n">offset</span><span class="p">)</span> <span class="o">/</span> <span class="mh">0x14</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">s</span><span class="o">-&gt;</span><span class="n">timer_n_load_count</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">set_alarm_time</span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="n">index</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>读写函数写好了，需要给谁调用呢。我们刚刚提到了，这是个回调函数，我们需要给一段内存注册这个回调函数。如代码所示。我们给 Timer iomem 绑定了读写函数。具体哪一段地址还没定，但是我们定了 <code>0x2000</code> 这么长一段。我觉得这里应该是最高位的一个寄存器偏移量。因为再高就没啥用了，或者就是 SoC 里定的寄存器空间大小 <code>0x1000</code>。这里应该是为了图省事写的一个值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// hw/timer/dw_timer.c
</span></span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">MemoryRegionOps</span> <span class="n">dw_timer_ops</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">read</span> <span class="o">=</span> <span class="n">dw_timer_read</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">write</span> <span class="o">=</span> <span class="n">dw_timer_write</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">endianness</span> <span class="o">=</span> <span class="n">DEVICE_NATIVE_ENDIAN</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">dw_timer_init</span><span class="p">(</span><span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">DWTimerState</span> <span class="o">*</span><span class="n">s</span> <span class="o">=</span> <span class="nf">DWTIMER</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 为这段内存注册回调函数
</span></span></span><span class="line"><span class="cl">    <span class="nf">memory_region_init_io</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">iomem</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">dw_timer_ops</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="s">&#34;dw_timer&#34;</span><span class="p">,</span> <span class="mh">0x2000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">sysbus_init_mmio</span><span class="p">(</span><span class="nf">SYS_BUS_DEVICE</span><span class="p">(</span><span class="n">obj</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">iomem</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>下面在<code>hw/riscv/sifive_e.c</code>里会映射寄存器空间到 QEMU 的内存空间。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-C" data-lang="C"><span class="line"><span class="cl"><span class="c1">// hw/riscv/sifive_e.c
</span></span></span><span class="line"><span class="cl"><span class="nf">sysbus_mmio_map</span><span class="p">(</span><span class="nf">SYS_BUS_DEVICE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">timer</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="n">memmap</span><span class="p">[</span><span class="n">SIFIVE_E_DEV_TIMER</span><span class="p">].</span><span class="n">base</span><span class="p">);</span>
</span></span></code></pre></div><h1 id="参考">参考</h1>
<ol>
<li><a href="https://www.cnblogs.com/haiyonghao/p/14440761.html">QEMU 中基于 QOM 的 VFIO 类的定义 - EwanHai - 博客园</a></li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 源码分析-虚拟 CPU 创建</title>
      <link>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E8%99%9A%E6%8B%9Fcpu%E5%88%9B%E5%BB%BA/</link>
      <pubDate>Wed, 01 Sep 2021 18:22:14 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E8%99%9A%E6%8B%9Fcpu%E5%88%9B%E5%BB%BA/</guid>
      <description>&lt;h2 id=&#34;流程图&#34;&gt;流程图&lt;/h2&gt;
&lt;p&gt;先开个头吧，把创建流程稍微捋一下，找到创建虚拟 CPU 的模块。至于中间的流程还没有详细分析，万事开头难，先上手再说吧。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210901182329.svg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210901182329.svg&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h2 id=&#34;qemu_add_opts解析-qemu-的命令行&#34;&gt;&lt;code&gt;qemu_add_opts&lt;/code&gt;解析 qemu 的命令行&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;qemu_init&lt;/code&gt;函数中下面这一长串内容，就是在解析命令行的参数。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Legacy&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;common&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sbdry&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;runtime&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;chardev&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;device&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;netdev&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nic&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sqemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;net&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rtc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;global_opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mon&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sqemu&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;trace&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为什么有这么多的 &lt;code&gt;opts&lt;/code&gt;呢？这是因为，实际运行中创建的&lt;code&gt;kvm&lt;/code&gt;参数会复杂&lt;code&gt;N&lt;/code&gt;倍。这里我们贴一个开源云平台软件 &lt;code&gt;OpenStack&lt;/code&gt; 创建出来的&lt;code&gt;KVM&lt;/code&gt;的参数，如下所示。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-enable-kvm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-name instance-00000024
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-machine pc-i440fx-trusty,accel&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;kvm,usb&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;off
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-cpu SandyBridge,+erms,+smep,+fsgsbase,+pdpe1gb,+rdrand,+f16c,+osxsave,+dca,+pcid,+pdcm,+xtpr,+tm2,+est,+smx,+vmx,+ds_cpl,+monitor,+dtes64,+pbe,+tm,+ht,+ss,+acpi,+ds,+vme
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-m &lt;span class=&#34;m&#34;&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-smp 1,sockets&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1,cores&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;1,threads&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;......
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-rtc &lt;span class=&#34;nv&#34;&gt;base&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;utc,driftfix&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;slew
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-drive &lt;span class=&#34;nv&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/var/lib/nova/instances/1f8e6f7e-5a70-4780-89c1-464dc0e7f308/disk,if&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;none,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;drive-virtio-disk0,format&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;qcow2,cache&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;none
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-device virtio-blk-pci,scsi&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;off,bus&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;pci.0,addr&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0x4,drive&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;drive-virtio-disk0,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;virtio-disk0,bootindex&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-netdev tap,fd&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;32,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hostnet0,vhost&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;on,vhostfd&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;37&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-device virtio-net-pci,netdev&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;hostnet0,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;net0,mac&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;fa:16:3e:d1:2d:99,bus&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;pci.0,addr&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0x3
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-chardev file,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;charserial0,path&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;/var/lib/nova/instances/1f8e6f7e-5a70-4780-89c1-464dc0e7f308/console.log
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-vnc 0.0.0.0:12
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-device cirrus-vga,id&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;video0,bus&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;pci.0,addr&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0x2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-enable-kvm&lt;/code&gt;：表示启用硬件辅助虚拟化。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-name instance-00000024&lt;/code&gt;：表示虚拟机的名称。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-machine pc-i440fx-trusty,accel=kvm,usb=off&lt;/code&gt;：machine 是什么呢？其实就是计算机体系结构。不知道什么是体系结构的话，可以订阅极客时间的另一个专栏《深入浅出计算机组成原理》。
qemu 会模拟多种体系结构，常用的有普通 PC 机，也即 x86 的 32 位或者 64 位的体系结构、Mac 电脑 PowerPC 的体系结构、Sun 的体系结构、MIPS 的体系结构，精简指令集。如果使用 KVM hardware-assisted virtualization，也即 BIOS 中 VD-T 是打开的，则参数中 &lt;code&gt;accel=kvm&lt;/code&gt;。如果不使用 &lt;code&gt;hardware-assisted virtualization&lt;/code&gt;，用的是纯模拟，则有参数 &lt;code&gt;accel = tcg&lt;/code&gt;，&lt;code&gt;-no-kvm&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-cpu SandyBridge,+erms,+smep,+fsgsbase,+pdpe1gb,+rdrand,+f16c,+osxsave,+dca,+pcid,+pdcm,+xtpr,+tm2,+est,+smx,+vmx,+ds_cpl,+monitor,+dtes64,+pbe,+tm,+ht,+ss,+acpi,+ds,+vme&lt;/code&gt;：表示设置 CPU，SandyBridge 是 Intel 处理器，后面的加号都是添加的 CPU 的参数，这些参数会显示在 /proc/cpuinfo 里面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-m 2048&lt;/code&gt;：表示内存。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-smp 1,sockets=1,cores=1,threads=1&lt;/code&gt;：&lt;code&gt;SMP&lt;/code&gt; 我们解析过，叫对称多处理器，和&lt;code&gt;NUMA&lt;/code&gt; 对应。qemu 仿真了一个具有 1 个 &lt;code&gt;vcpu&lt;/code&gt;，一个 &lt;code&gt;socket&lt;/code&gt;，一个 &lt;code&gt;core&lt;/code&gt;，一个 &lt;code&gt;threads&lt;/code&gt; 的处理器。
&lt;code&gt;socket&lt;/code&gt;、&lt;code&gt;core&lt;/code&gt;、&lt;code&gt;threads&lt;/code&gt; 是什么概念呢？&lt;code&gt;socket&lt;/code&gt; 就是主板上插 CPU 的槽的数目，也即常说的“路”，&lt;code&gt;core&lt;/code&gt; 就是我们平时说的“核”，即双核、4 核等。&lt;code&gt;thread&lt;/code&gt; 就是每个 core 的硬件线程数，即超线程。举个具体的例子，某个服务器是：2 路 4 核超线程（一般默认为 2 个线程），通过 &lt;code&gt;cat /proc/cpuinfo&lt;/code&gt;，我们看到的是 242=16 个&lt;code&gt;processor&lt;/code&gt;，很多人也习惯成为 16 核了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-rtc base=utc,driftfix=slew&lt;/code&gt;：表示系统时间由参数 &lt;code&gt;-rtc&lt;/code&gt; 指定。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-device cirrus-vga,id=video0,bus=pci.0,addr=0x2&lt;/code&gt;：表示显示器用参数 &lt;code&gt;-vga&lt;/code&gt; 设置，默认为 &lt;code&gt;cirrus&lt;/code&gt;，它模拟了 &lt;code&gt;CL-GD5446PCI VGA card&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有关网卡，使用 &lt;code&gt;-net&lt;/code&gt; 参数和 &lt;code&gt;-device&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从 HOST 角度：&lt;code&gt;-netdev tap,fd=32,id=hostnet0,vhost=on,vhostfd=37&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从 GUEST 角度：&lt;code&gt;-device virtio-net-pci,netdev=hostnet0,id=net0,mac=fa:16:3e:d1:2d:99,bus=pci.0,addr=0x3&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有关硬盘，使用 &lt;code&gt;-hda -hdb&lt;/code&gt;，或者使用 &lt;code&gt;-drive&lt;/code&gt; 和 &lt;code&gt;-device&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从 HOST 角度：&lt;code&gt;-drive file=/var/lib/nova/instances/1f8e6f7e-5a70-4780-89c1-464dc0e7f308/disk,if=none,id=drive-virtio-disk0,format=qcow2,cache=none&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从 GUEST 角度：&lt;code&gt;-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-vnc 0.0.0.0:12&lt;/code&gt;：设置 VNC。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;module_call_init初始化所有模块&#34;&gt;&lt;code&gt;module_call_init&lt;/code&gt;初始化所有模块&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_init_subsystems&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;--&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;module_call_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当虚拟机真的要使用物理资源的时候，对下面的物理机上的资源要进行请求，所以它的工作模式有点儿类似操作系统对接驱动。驱动要符合一定的格式，才能算操作系统的一个模块。同理，qemu 为了模拟各种各样的设备，也需要管理各种各样的模块，这些模块也需要符合一定的格式。&lt;/p&gt;
&lt;p&gt;定义一个 qemu 模块会调用 &lt;code&gt;type_init&lt;/code&gt;。例如，&lt;code&gt;kvm&lt;/code&gt; 的模块要在 &lt;code&gt;accel/kvm/kvm-all.c&lt;/code&gt; 文件里面实现。在这个文件里面，有一行下面的代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_accel_type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_KVM_ACCEL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_ACCEL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_accel_instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_accel_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KVMState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;type_register_static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kvm_accel_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kvm_type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;找到&lt;code&gt;type_init&lt;/code&gt;的定义&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define type_init(function) module_init(function, MODULE_INIT_QOM)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从代码里面的定义我们可以看出来，&lt;code&gt;type_init&lt;/code&gt; 后面的参数是一个函数，调用 &lt;code&gt;type_init&lt;/code&gt; 就相当于调用 &lt;code&gt;module_init&lt;/code&gt;，在这里函数就是 &lt;code&gt;kvm_type_init&lt;/code&gt;，类型就是 &lt;code&gt;MODULE_INIT_QOM&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;再查看一下&lt;code&gt;module_init&lt;/code&gt;的定义&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//include/qemu/module.h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#define module_init(function, type)                                         \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;{                                                                           \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;    register_module_init(function, type);                                   \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;module_init&lt;/code&gt; 最终要调用 &lt;code&gt;register_module_init&lt;/code&gt;。属于 &lt;code&gt;MODULE_INIT_QOM&lt;/code&gt; 这种类型的，有一个 &lt;code&gt;Module&lt;/code&gt; 列表 &lt;code&gt;ModuleTypeList&lt;/code&gt;，列表里面是一项一项的 &lt;code&gt;ModuleEntry&lt;/code&gt;。&lt;code&gt;KVM&lt;/code&gt; 就是其中一项，并且会初始化每一项的 &lt;code&gt;init&lt;/code&gt; 函数为参数表示的函数 &lt;code&gt;fn&lt;/code&gt;，也即 &lt;code&gt;KVM&lt;/code&gt; 这个 &lt;code&gt;module&lt;/code&gt; 的 &lt;code&gt;init&lt;/code&gt; 函数就是 &lt;code&gt;kvm_type_init&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当然，&lt;code&gt;MODULE_INIT_QOM&lt;/code&gt; 这种类型会有很多很多的 &lt;code&gt;module&lt;/code&gt;，从后面的代码我们可以看到，所有调用 &lt;code&gt;type_init&lt;/code&gt; 的地方都注册了一个 &lt;code&gt;MODULE_INIT_QOM&lt;/code&gt; 类型的 &lt;code&gt;Module&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;了解了 &lt;code&gt;Module&lt;/code&gt; 的注册机制，我们继续回到 &lt;code&gt;qemu_init_subsystems&lt;/code&gt; 函数中 &lt;code&gt;module_call_init&lt;/code&gt; 的调用。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_init_subsystems&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Error&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;os_set_line_buffering&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;module_call_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MODULE_INIT_TRACE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;qemu_init_cpu_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;qemu_init_cpu_loop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;qemu_mutex_lock_iothread&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;atexit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu_run_exit_notifiers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;module_call_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MODULE_INIT_QOM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;module_call_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MODULE_INIT_MIGRATION&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// utils/module.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;module_call_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;module_init_type&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ModuleTypeList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ModuleEntry&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;l&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;QTAILQ_FOREACH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;module_call_init&lt;/code&gt; 中，我们会找到 &lt;code&gt;MODULE_INIT_QOM&lt;/code&gt; 这种类型对应的 &lt;code&gt;ModuleTypeList&lt;/code&gt;，找出列表中所有的 &lt;code&gt;ModuleEntry&lt;/code&gt;，然后调用每个 &lt;code&gt;ModuleEntry&lt;/code&gt; 的 &lt;code&gt;init&lt;/code&gt; 函数。这里需要注意的是，在 &lt;code&gt;module_call_init&lt;/code&gt; 调用的这一步，所有 &lt;code&gt;Module&lt;/code&gt; 的 &lt;code&gt;init&lt;/code&gt; 函数都已经被调用过了。&lt;/p&gt;
&lt;p&gt;后面我们会看到很多的 &lt;code&gt;Module&lt;/code&gt;，当我们后面再次遇到时，需要意识到，它的 &lt;code&gt;init&lt;/code&gt; 函数在这里也被调用过了。这里我们还是以对于 &lt;code&gt;kvm&lt;/code&gt; 这个 &lt;code&gt;module&lt;/code&gt; 为例子，看看它的 &lt;code&gt;init&lt;/code&gt; 函数都做了哪些事情。我们会发现，其实它调用的是 &lt;code&gt;kvm_type_init&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;kvm_type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;type_register_static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kvm_accel_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;TypeImpl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;type_register_static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;type_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;TypeImpl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;type_register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;type_register_internal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeImpl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;type_register_internal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;TypeImpl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;type_new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;type_table_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeImpl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;type_new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;TypeImpl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;type_table_lookup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_strdup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_strdup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_base_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_base_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_post_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_post_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_finalize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_finalize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;abstract&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;abstract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;interfaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;interfaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;interfaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;typename&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_strdup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;interfaces&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;num_interfaces&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;type_table_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TypeImpl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;enumerating_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;g_hash_table_insert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;type_table_get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ti&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GHashTable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;type_table_get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GHashTable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type_table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type_table&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;type_table&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_hash_table_new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;g_str_hash&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g_str_equal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type_table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TypeInfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_accel_type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_KVM_ACCEL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TYPE_ACCEL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class_init&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;kvm_accel_class_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;KVMState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;调用流程如下：虚线表示返回&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210907133931.svg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210907133931.svg&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;每一个 &lt;code&gt;Module&lt;/code&gt; 既然要模拟某种设备，那应该定义一种类型 &lt;code&gt;TypeImpl&lt;/code&gt; 来表示这些设备，这其实是一种&lt;code&gt;面向对象编程&lt;/code&gt;的思路，只不过这里用的是纯 C 语言的实现，所以需要变相实现一下类和对象。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kvm_type_init&lt;/code&gt; 会注册 &lt;code&gt;kvm_accel_type&lt;/code&gt;，定义上面的代码，我们可以认为这样动态定义了一个类。这个类的名字是 &lt;code&gt;TYPE_KVM_ACCEL&lt;/code&gt;，这个类有父类 &lt;code&gt;TYPE_ACCEL&lt;/code&gt;，这个类的初始化应该调用函数 &lt;code&gt;kvm_accel_class_init&lt;/code&gt;。如果用这个类声明一个对象，对象的大小应该是 &lt;code&gt;instance_size&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;type_register_internal&lt;/code&gt; 中，我们会根据 &lt;code&gt;kvm_accel_type&lt;/code&gt; 这个 &lt;code&gt;TypeInfo&lt;/code&gt;，创建一个&lt;code&gt;TypeImpl&lt;/code&gt; 来表示这个新注册的类，也就是说，&lt;code&gt;TypeImpl&lt;/code&gt; 才是我们想要声明的那个 &lt;code&gt;class&lt;/code&gt;。在 qemu 里面，有一个全局的哈希表 &lt;code&gt;type_table&lt;/code&gt;，用来存放所有定义的类。在 &lt;code&gt;type_new&lt;/code&gt; 里面，我们先从全局表里面根据名字&lt;code&gt;type_table_lookup&lt;/code&gt;查找找这个类。如果找到，说明这个类曾经被注册过，就报错；如果没有找到，说明这是一个新的类，则将 &lt;code&gt;TypeInfo&lt;/code&gt; 里面信息填到 &lt;code&gt;TypeImpl&lt;/code&gt; 里面。&lt;code&gt;type_table_add&lt;/code&gt; 会将这个类注册到全局的表里面。到这里，我们注意，&lt;code&gt;class_init&lt;/code&gt; 还没有被调用，也即这个类现在还处于纸面的状态。&lt;/p&gt;
&lt;p&gt;这点更加像 Java 的反射机制了。在 Java 里面，对于一个类，首先我们写代码的时候要写一个 &lt;code&gt;class xxx&lt;/code&gt; 的定义，编译好就放在&lt;code&gt;.class&lt;/code&gt; 文件中，这也是出于纸面的状态。然后，Java 会有一个 &lt;code&gt;Class&lt;/code&gt; 对象，用于读取和表示这个纸面上的 &lt;code&gt;class xxx&lt;/code&gt;，可以生成真正的对象。&lt;/p&gt;
&lt;p&gt;相同的过程在后面的代码中我们也可以看到，&lt;code&gt;class_init&lt;/code&gt; 会生成&lt;code&gt;XXXClass&lt;/code&gt;，就相当于 Java 里面的 &lt;code&gt;Class&lt;/code&gt;对象，&lt;code&gt;TypeImpl&lt;/code&gt; 还会有一个 &lt;code&gt;instance_init&lt;/code&gt; 函数，相当于构造函数，用于根据 &lt;code&gt;XXXClass&lt;/code&gt; 生成 &lt;code&gt;Object&lt;/code&gt;，这就相当于 Java 反射里面最终创建的对象。和构造函数对应的还有 &lt;code&gt;instance_finalize&lt;/code&gt;，相当于析构函数。&lt;/p&gt;
&lt;p&gt;这一套反射机制放在 &lt;code&gt;qom&lt;/code&gt; 文件夹下面，全称 &lt;code&gt;QEMU Object Model&lt;/code&gt;，也即用 C 实现了一套&lt;strong&gt;面向对象的反射机制&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;初始化-machine&#34;&gt;初始化 machine&lt;/h2&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210913115046.svg&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210913115046.svg&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//vl.c
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;qemu_create_machine&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;select_machine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在创建 machine 之前，先要通过&lt;code&gt;select_machine&lt;/code&gt;确定一个&lt;code&gt;machine&lt;/code&gt;。&lt;code&gt;select_machine&lt;/code&gt;又是怎么确定的呢，这就和我们命令行的输入有关，比如我们&lt;code&gt;-m spike&lt;/code&gt;，那么这里就会选择&lt;code&gt;spike&lt;/code&gt;作为&lt;code&gt;machine&lt;/code&gt;。它的定义在&lt;code&gt;hw/riscv/spike.c&lt;/code&gt;中。&lt;/p&gt;
&lt;p&gt;在源码最后有这么一句，会和我们上面解析的&lt;code&gt;type_init&lt;/code&gt; 是一样的，在全局的表里面注册了一个全局的名字是&lt;code&gt;spike&lt;/code&gt;的纸面上的 &lt;code&gt;class&lt;/code&gt;，也即 &lt;code&gt;TypeImpl&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;type_init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;spike_machine_init_reqister_types&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在全局表中有这个纸面上的 &lt;code&gt;class&lt;/code&gt; 了。我们回到 &lt;code&gt;select_machine&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;select_machine&lt;/code&gt; 中，有两种方式可以生成 &lt;code&gt;MachineClass&lt;/code&gt;。一种方式是 &lt;code&gt;find_default_machine&lt;/code&gt;，找一个默认的；另一种方式是 &lt;code&gt;machine_parse&lt;/code&gt;，通过解析参数生成 &lt;code&gt;MachineClass&lt;/code&gt;。无论哪种方式，都会调用 &lt;code&gt;object_class_get_list&lt;/code&gt; 获得一个 &lt;code&gt;MachineClass&lt;/code&gt; 的列表，然后在里面找。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;select_machine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;GSList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;object_class_get_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TYPE_MACHINE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machine_class&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_default_machine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;optarg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;QemuOpts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Location&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;loc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;loc_push_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_get_machine_opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;qemu_opts_loc_restore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;optarg&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;qemu_opt_get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;optarg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;machine_class&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;machine_parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;optarg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machine_class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;error_report&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;No machine specified, and there is no default&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;error_printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Use -machine help to list supported machines&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;loc_pop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;loc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;g_slist_free&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;machine_class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;find_default_machine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GSList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;GSList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;default_machineclass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;el&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;default_machineclass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Multiple default machines&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;default_machineclass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;default_machineclass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;machine_parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GSList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;GSList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;is_help_option&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Supported machines are:&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_slist_sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;machine_class_cmp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;el&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;MachineClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;el&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;nf&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%-20s %s (alias of %s)&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;alias&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;desc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;nf&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;%-20s %s%s%s&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;desc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                   &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_default&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34; (default)&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                   &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deprecation_reason&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34; (deprecated)&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_machine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;machines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;error_report&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;unsupported machine type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;error_printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Use -machine help to list supported machines&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;object_class_get_list&lt;/code&gt; 定义如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;GSList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;object_class_get_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;implements_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;include_abstract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;GSList&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;list&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;object_class_foreach&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_class_get_list_tramp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                         &lt;span class=&#34;n&#34;&gt;implements_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;include_abstract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;object_class_foreach&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                          &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;implements_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;include_abstract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                          &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;OCFData&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;implements_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;include_abstract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;enumerating_types&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;g_hash_table_foreach&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;type_table_get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;object_class_foreach_tramp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;enumerating_types&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在全局表 &lt;code&gt;type_table_get()&lt;/code&gt; 中，对于每一项 &lt;code&gt;TypeImpl&lt;/code&gt;，我们都执行 &lt;code&gt;object_class_foreach_tramp&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;object_class_foreach_tramp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gpointer&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gpointer&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                       &lt;span class=&#34;n&#34;&gt;gpointer&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;OCFData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;TypeImpl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;type_initialize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;include_abstract&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;abstract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;implements_type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;object_class_dynamic_cast&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;implements_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opaque&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;object_class_foreach_tramp&lt;/code&gt; 中，会调用将 &lt;code&gt;type_initialize&lt;/code&gt;，这里面会调用 &lt;code&gt;class_init&lt;/code&gt; 将纸面上的 &lt;code&gt;class&lt;/code&gt; 也即 &lt;code&gt;TypeImpl&lt;/code&gt; 变为 &lt;code&gt;ObjectClass&lt;/code&gt;，&lt;code&gt;ObjectClass&lt;/code&gt; 是所有&lt;code&gt;Class&lt;/code&gt; 类的祖先，&lt;code&gt;MachineClass&lt;/code&gt; 是它的子类。&lt;/p&gt;
&lt;p&gt;因为在 &lt;code&gt;machine&lt;/code&gt; 的命令行里面，我们指定了名字为&lt;code&gt;spike&lt;/code&gt;，就肯定能够找到我们注册过了的 &lt;code&gt;TypeImpl&lt;/code&gt;，并调用它的 &lt;code&gt;class_init&lt;/code&gt; 函数。&lt;/p&gt;
&lt;p&gt;所以，当 &lt;code&gt;select_machine&lt;/code&gt; 执行完毕后，就有一个 &lt;code&gt;MachineClass&lt;/code&gt; 了。&lt;/p&gt;
&lt;p&gt;接着，我们回到 &lt;code&gt;qemu_create_machine&lt;/code&gt; 中的&lt;code&gt;object_new_with_class&lt;/code&gt;。这就很好理解了，&lt;code&gt;MachineClass&lt;/code&gt; 是一个 &lt;code&gt;Class&lt;/code&gt; 类，接下来应该通过它生成一个 &lt;code&gt;Instance&lt;/code&gt;，也即对象，这就是 &lt;code&gt;object_new_with_class&lt;/code&gt; 的作用。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;object_new_with_class&lt;/code&gt; 中，&lt;code&gt;TypeImpl&lt;/code&gt; 的 &lt;code&gt;instance_init&lt;/code&gt; 会被调用，创建一个对象。&lt;code&gt;current_machine&lt;/code&gt; 就是这个对象，它的类型是&lt;code&gt;MachineState&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;object_new_with_class&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ObjectClass&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;object_new_with_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;klass&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;object_new_with_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Type&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;type_initialize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;g_malloc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;object_initialize_with_type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance_size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;free&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g_free&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;至此，绕了这么大一圈，有关体系结构的对象才创建完毕，接下来很多的设备的初始化，包括 CPU 和内存的初始化，都是围绕着体系结构的对象来的，后面我们会常常看到&lt;code&gt;current_machine&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220308180036.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220308180036.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;h2 id=&#34;参考&#34;&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.cnblogs.com/nm90/p/15661202.html&#34;&gt;Qemu CPU 虚拟化 - 人生一世，草木一秋。 - 博客园&lt;/a&gt;
&lt;a href=&#34;https://www.cnblogs.com/LoyenWang/p/13796537.html&#34;&gt;【原创】Linux 虚拟化 KVM-Qemu 分析（四）之 CPU 虚拟化（2） - LoyenWang - 博客园&lt;/a&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="流程图">流程图</h2>
<p>先开个头吧，把创建流程稍微捋一下，找到创建虚拟 CPU 的模块。至于中间的流程还没有详细分析，万事开头难，先上手再说吧。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210901182329.svg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210901182329.svg" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h2 id="qemu_add_opts解析-qemu-的命令行"><code>qemu_add_opts</code>解析 qemu 的命令行</h2>
<p><code>qemu_init</code>函数中下面这一长串内容，就是在解析命令行的参数。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">drive</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="n">drive</span> <span class="nf">opts</span><span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">Legacy</span> <span class="n">drive</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="n">drive</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">common</span> <span class="n">drive</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="n">drive</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">drive</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="n">drive</span> <span class="nf">opts</span> <span class="p">(</span><span class="n">sbdry</span> <span class="n">runtime</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="n">qemu</span> <span class="n">chardev</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">device</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">netdev</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">nic</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="n">sqemu</span> <span class="n">net</span> <span class="n">opts</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">rtc</span> <span class="n">opts</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">global_opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">qemu</span> <span class="n">mon</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">qemu</span> <span class="n">add</span> <span class="nf">opts</span> <span class="p">(</span><span class="n">sqemu</span> <span class="n">trace</span> <span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span></code></pre></div><p>为什么有这么多的 <code>opts</code>呢？这是因为，实际运行中创建的<code>kvm</code>参数会复杂<code>N</code>倍。这里我们贴一个开源云平台软件 <code>OpenStack</code> 创建出来的<code>KVM</code>的参数，如下所示。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">qemu-system-x86_64
</span></span><span class="line"><span class="cl">-enable-kvm
</span></span><span class="line"><span class="cl">-name instance-00000024
</span></span><span class="line"><span class="cl">-machine pc-i440fx-trusty,accel<span class="o">=</span>kvm,usb<span class="o">=</span>off
</span></span><span class="line"><span class="cl">-cpu SandyBridge,+erms,+smep,+fsgsbase,+pdpe1gb,+rdrand,+f16c,+osxsave,+dca,+pcid,+pdcm,+xtpr,+tm2,+est,+smx,+vmx,+ds_cpl,+monitor,+dtes64,+pbe,+tm,+ht,+ss,+acpi,+ds,+vme
</span></span><span class="line"><span class="cl">-m <span class="m">2048</span>
</span></span><span class="line"><span class="cl">-smp 1,sockets<span class="o">=</span>1,cores<span class="o">=</span>1,threads<span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl">......
</span></span><span class="line"><span class="cl">-rtc <span class="nv">base</span><span class="o">=</span>utc,driftfix<span class="o">=</span>slew
</span></span><span class="line"><span class="cl">-drive <span class="nv">file</span><span class="o">=</span>/var/lib/nova/instances/1f8e6f7e-5a70-4780-89c1-464dc0e7f308/disk,if<span class="o">=</span>none,id<span class="o">=</span>drive-virtio-disk0,format<span class="o">=</span>qcow2,cache<span class="o">=</span>none
</span></span><span class="line"><span class="cl">-device virtio-blk-pci,scsi<span class="o">=</span>off,bus<span class="o">=</span>pci.0,addr<span class="o">=</span>0x4,drive<span class="o">=</span>drive-virtio-disk0,id<span class="o">=</span>virtio-disk0,bootindex<span class="o">=</span><span class="m">1</span>
</span></span><span class="line"><span class="cl">-netdev tap,fd<span class="o">=</span>32,id<span class="o">=</span>hostnet0,vhost<span class="o">=</span>on,vhostfd<span class="o">=</span><span class="m">37</span>
</span></span><span class="line"><span class="cl">-device virtio-net-pci,netdev<span class="o">=</span>hostnet0,id<span class="o">=</span>net0,mac<span class="o">=</span>fa:16:3e:d1:2d:99,bus<span class="o">=</span>pci.0,addr<span class="o">=</span>0x3
</span></span><span class="line"><span class="cl">-chardev file,id<span class="o">=</span>charserial0,path<span class="o">=</span>/var/lib/nova/instances/1f8e6f7e-5a70-4780-89c1-464dc0e7f308/console.log
</span></span><span class="line"><span class="cl">-vnc 0.0.0.0:12
</span></span><span class="line"><span class="cl">-device cirrus-vga,id<span class="o">=</span>video0,bus<span class="o">=</span>pci.0,addr<span class="o">=</span>0x2
</span></span></code></pre></div><ul>
<li>
<p><code>-enable-kvm</code>：表示启用硬件辅助虚拟化。</p>
</li>
<li>
<p><code>-name instance-00000024</code>：表示虚拟机的名称。</p>
</li>
<li>
<p><code>-machine pc-i440fx-trusty,accel=kvm,usb=off</code>：machine 是什么呢？其实就是计算机体系结构。不知道什么是体系结构的话，可以订阅极客时间的另一个专栏《深入浅出计算机组成原理》。
qemu 会模拟多种体系结构，常用的有普通 PC 机，也即 x86 的 32 位或者 64 位的体系结构、Mac 电脑 PowerPC 的体系结构、Sun 的体系结构、MIPS 的体系结构，精简指令集。如果使用 KVM hardware-assisted virtualization，也即 BIOS 中 VD-T 是打开的，则参数中 <code>accel=kvm</code>。如果不使用 <code>hardware-assisted virtualization</code>，用的是纯模拟，则有参数 <code>accel = tcg</code>，<code>-no-kvm</code>。</p>
</li>
<li>
<p><code>-cpu SandyBridge,+erms,+smep,+fsgsbase,+pdpe1gb,+rdrand,+f16c,+osxsave,+dca,+pcid,+pdcm,+xtpr,+tm2,+est,+smx,+vmx,+ds_cpl,+monitor,+dtes64,+pbe,+tm,+ht,+ss,+acpi,+ds,+vme</code>：表示设置 CPU，SandyBridge 是 Intel 处理器，后面的加号都是添加的 CPU 的参数，这些参数会显示在 /proc/cpuinfo 里面。</p>
</li>
<li>
<p><code>-m 2048</code>：表示内存。</p>
</li>
<li>
<p><code>-smp 1,sockets=1,cores=1,threads=1</code>：<code>SMP</code> 我们解析过，叫对称多处理器，和<code>NUMA</code> 对应。qemu 仿真了一个具有 1 个 <code>vcpu</code>，一个 <code>socket</code>，一个 <code>core</code>，一个 <code>threads</code> 的处理器。
<code>socket</code>、<code>core</code>、<code>threads</code> 是什么概念呢？<code>socket</code> 就是主板上插 CPU 的槽的数目，也即常说的“路”，<code>core</code> 就是我们平时说的“核”，即双核、4 核等。<code>thread</code> 就是每个 core 的硬件线程数，即超线程。举个具体的例子，某个服务器是：2 路 4 核超线程（一般默认为 2 个线程），通过 <code>cat /proc/cpuinfo</code>，我们看到的是 242=16 个<code>processor</code>，很多人也习惯成为 16 核了。</p>
</li>
<li>
<p><code>-rtc base=utc,driftfix=slew</code>：表示系统时间由参数 <code>-rtc</code> 指定。</p>
</li>
<li>
<p><code>-device cirrus-vga,id=video0,bus=pci.0,addr=0x2</code>：表示显示器用参数 <code>-vga</code> 设置，默认为 <code>cirrus</code>，它模拟了 <code>CL-GD5446PCI VGA card</code>。</p>
</li>
<li>
<p>有关网卡，使用 <code>-net</code> 参数和 <code>-device</code>。</p>
</li>
<li>
<p>从 HOST 角度：<code>-netdev tap,fd=32,id=hostnet0,vhost=on,vhostfd=37</code>。</p>
</li>
<li>
<p>从 GUEST 角度：<code>-device virtio-net-pci,netdev=hostnet0,id=net0,mac=fa:16:3e:d1:2d:99,bus=pci.0,addr=0x3</code>。</p>
</li>
<li>
<p>有关硬盘，使用 <code>-hda -hdb</code>，或者使用 <code>-drive</code> 和 <code>-device</code>。</p>
</li>
<li>
<p>从 HOST 角度：<code>-drive file=/var/lib/nova/instances/1f8e6f7e-5a70-4780-89c1-464dc0e7f308/disk,if=none,id=drive-virtio-disk0,format=qcow2,cache=none</code></p>
</li>
<li>
<p>从 GUEST 角度：<code>-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x4,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1</code></p>
</li>
<li>
<p><code>-vnc 0.0.0.0:12</code>：设置 VNC。</p>
</li>
</ul>
<h2 id="module_call_init初始化所有模块"><code>module_call_init</code>初始化所有模块</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">--&gt;</span> <span class="nf">qemu_init</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">--&gt;</span> <span class="nf">qemu_init_subsystems</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">--&gt;</span> <span class="nf">module_call_init</span><span class="p">()</span>
</span></span></code></pre></div><p>当虚拟机真的要使用物理资源的时候，对下面的物理机上的资源要进行请求，所以它的工作模式有点儿类似操作系统对接驱动。驱动要符合一定的格式，才能算操作系统的一个模块。同理，qemu 为了模拟各种各样的设备，也需要管理各种各样的模块，这些模块也需要符合一定的格式。</p>
<p>定义一个 qemu 模块会调用 <code>type_init</code>。例如，<code>kvm</code> 的模块要在 <code>accel/kvm/kvm-all.c</code> 文件里面实现。在这个文件里面，有一行下面的代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">kvm_accel_type</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">TYPE_KVM_ACCEL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">TYPE_ACCEL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_init</span> <span class="o">=</span> <span class="n">kvm_accel_instance_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">class_init</span> <span class="o">=</span> <span class="n">kvm_accel_class_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">KVMState</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">kvm_type_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">type_register_static</span><span class="p">(</span><span class="o">&amp;</span><span class="n">kvm_accel_type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">type_init</span><span class="p">(</span><span class="n">kvm_type_init</span><span class="p">);</span>
</span></span></code></pre></div><p>找到<code>type_init</code>的定义</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define type_init(function) module_init(function, MODULE_INIT_QOM)
</span></span></span></code></pre></div><p>从代码里面的定义我们可以看出来，<code>type_init</code> 后面的参数是一个函数，调用 <code>type_init</code> 就相当于调用 <code>module_init</code>，在这里函数就是 <code>kvm_type_init</code>，类型就是 <code>MODULE_INIT_QOM</code>。</p>
<p>再查看一下<code>module_init</code>的定义</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">//include/qemu/module.h
</span></span></span><span class="line"><span class="cl"><span class="cp">#define module_init(function, type)                                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
</span></span></span><span class="line"><span class="cl"><span class="cp">{                                                                           \
</span></span></span><span class="line"><span class="cl"><span class="cp">    register_module_init(function, type);                                   \
</span></span></span><span class="line"><span class="cl"><span class="cp">}
</span></span></span></code></pre></div><p><code>module_init</code> 最终要调用 <code>register_module_init</code>。属于 <code>MODULE_INIT_QOM</code> 这种类型的，有一个 <code>Module</code> 列表 <code>ModuleTypeList</code>，列表里面是一项一项的 <code>ModuleEntry</code>。<code>KVM</code> 就是其中一项，并且会初始化每一项的 <code>init</code> 函数为参数表示的函数 <code>fn</code>，也即 <code>KVM</code> 这个 <code>module</code> 的 <code>init</code> 函数就是 <code>kvm_type_init</code>。</p>
<p>当然，<code>MODULE_INIT_QOM</code> 这种类型会有很多很多的 <code>module</code>，从后面的代码我们可以看到，所有调用 <code>type_init</code> 的地方都注册了一个 <code>MODULE_INIT_QOM</code> 类型的 <code>Module</code>。</p>
<p>了解了 <code>Module</code> 的注册机制，我们继续回到 <code>qemu_init_subsystems</code> 函数中 <code>module_call_init</code> 的调用。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">qemu_init_subsystems</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Error</span> <span class="o">*</span><span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">os_set_line_buffering</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">module_call_init</span><span class="p">(</span><span class="n">MODULE_INIT_TRACE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">qemu_init_cpu_list</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">qemu_init_cpu_loop</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">qemu_mutex_lock_iothread</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">atexit</span><span class="p">(</span><span class="n">qemu_run_exit_notifiers</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">module_call_init</span><span class="p">(</span><span class="n">MODULE_INIT_QOM</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">module_call_init</span><span class="p">(</span><span class="n">MODULE_INIT_MIGRATION</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// utils/module.c
</span></span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">module_call_init</span><span class="p">(</span><span class="n">module_init_type</span> <span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModuleTypeList</span> <span class="o">*</span><span class="n">l</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ModuleEntry</span> <span class="o">*</span><span class="n">e</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">l</span> <span class="o">=</span> <span class="nf">find_type</span><span class="p">(</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">QTAILQ_FOREACH</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">l</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">e</span><span class="o">-&gt;</span><span class="nf">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在 <code>module_call_init</code> 中，我们会找到 <code>MODULE_INIT_QOM</code> 这种类型对应的 <code>ModuleTypeList</code>，找出列表中所有的 <code>ModuleEntry</code>，然后调用每个 <code>ModuleEntry</code> 的 <code>init</code> 函数。这里需要注意的是，在 <code>module_call_init</code> 调用的这一步，所有 <code>Module</code> 的 <code>init</code> 函数都已经被调用过了。</p>
<p>后面我们会看到很多的 <code>Module</code>，当我们后面再次遇到时，需要意识到，它的 <code>init</code> 函数在这里也被调用过了。这里我们还是以对于 <code>kvm</code> 这个 <code>module</code> 为例子，看看它的 <code>init</code> 函数都做了哪些事情。我们会发现，其实它调用的是 <code>kvm_type_init</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">kvm_type_init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">type_register_static</span><span class="p">(</span><span class="o">&amp;</span><span class="n">kvm_accel_type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">TypeImpl</span> <span class="o">*</span><span class="nf">type_register_static</span><span class="p">(</span><span class="k">const</span> <span class="n">TypeInfo</span> <span class="o">*</span><span class="n">info</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">type_register</span><span class="p">(</span><span class="n">info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">TypeImpl</span> <span class="o">*</span><span class="nf">type_register</span><span class="p">(</span><span class="k">const</span> <span class="n">TypeInfo</span> <span class="o">*</span><span class="n">info</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">assert</span><span class="p">(</span><span class="n">info</span><span class="o">-&gt;</span><span class="n">parent</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">type_register_internal</span><span class="p">(</span><span class="n">info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">TypeImpl</span> <span class="o">*</span><span class="nf">type_register_internal</span><span class="p">(</span><span class="k">const</span> <span class="n">TypeInfo</span> <span class="o">*</span><span class="n">info</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">TypeImpl</span> <span class="o">*</span><span class="n">ti</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span> <span class="o">=</span> <span class="nf">type_new</span><span class="p">(</span><span class="n">info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="nf">type_table_add</span><span class="p">(</span><span class="n">ti</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ti</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">TypeImpl</span> <span class="o">*</span><span class="nf">type_new</span><span class="p">(</span><span class="k">const</span> <span class="n">TypeInfo</span> <span class="o">*</span><span class="n">info</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">TypeImpl</span> <span class="o">*</span><span class="n">ti</span> <span class="o">=</span> <span class="nf">g_malloc0</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">ti</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">type_table_lookup</span><span class="p">(</span><span class="n">info</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">=</span> <span class="nf">g_strdup</span><span class="p">(</span><span class="n">info</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">parent</span> <span class="o">=</span> <span class="nf">g_strdup</span><span class="p">(</span><span class="n">info</span><span class="o">-&gt;</span><span class="n">parent</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">class_size</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">class_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">instance_size</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">instance_size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">class_init</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">class_init</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">class_base_init</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">class_base_init</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">class_data</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">class_data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">instance_init</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">instance_init</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">instance_post_init</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">instance_post_init</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">instance_finalize</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">instance_finalize</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">abstract</span> <span class="o">=</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">abstract</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">interfaces</span> <span class="o">&amp;&amp;</span> <span class="n">info</span><span class="o">-&gt;</span><span class="n">interfaces</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">type</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">ti</span><span class="o">-&gt;</span><span class="n">interfaces</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="kr">typename</span> <span class="o">=</span> <span class="nf">g_strdup</span><span class="p">(</span><span class="n">info</span><span class="o">-&gt;</span><span class="n">interfaces</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">ti</span><span class="o">-&gt;</span><span class="n">num_interfaces</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ti</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">type_table_add</span><span class="p">(</span><span class="n">TypeImpl</span> <span class="o">*</span><span class="n">ti</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">assert</span><span class="p">(</span><span class="o">!</span><span class="n">enumerating_types</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">g_hash_table_insert</span><span class="p">(</span><span class="nf">type_table_get</span><span class="p">(),</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">ti</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">ti</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">GHashTable</span> <span class="o">*</span><span class="nf">type_table_get</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="n">GHashTable</span> <span class="o">*</span><span class="n">type_table</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">type_table</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">type_table</span> <span class="o">=</span> <span class="nf">g_hash_table_new</span><span class="p">(</span><span class="n">g_str_hash</span><span class="p">,</span> <span class="n">g_str_equal</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">type_table</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">const</span> <span class="n">TypeInfo</span> <span class="n">kvm_accel_type</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">TYPE_KVM_ACCEL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">TYPE_ACCEL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">class_init</span> <span class="o">=</span> <span class="n">kvm_accel_class_init</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">instance_size</span> <span class="o">=</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">KVMState</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>调用流程如下：虚线表示返回</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210907133931.svg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210907133931.svg" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>每一个 <code>Module</code> 既然要模拟某种设备，那应该定义一种类型 <code>TypeImpl</code> 来表示这些设备，这其实是一种<code>面向对象编程</code>的思路，只不过这里用的是纯 C 语言的实现，所以需要变相实现一下类和对象。</p>
<p><code>kvm_type_init</code> 会注册 <code>kvm_accel_type</code>，定义上面的代码，我们可以认为这样动态定义了一个类。这个类的名字是 <code>TYPE_KVM_ACCEL</code>，这个类有父类 <code>TYPE_ACCEL</code>，这个类的初始化应该调用函数 <code>kvm_accel_class_init</code>。如果用这个类声明一个对象，对象的大小应该是 <code>instance_size</code>。</p>
<p>在 <code>type_register_internal</code> 中，我们会根据 <code>kvm_accel_type</code> 这个 <code>TypeInfo</code>，创建一个<code>TypeImpl</code> 来表示这个新注册的类，也就是说，<code>TypeImpl</code> 才是我们想要声明的那个 <code>class</code>。在 qemu 里面，有一个全局的哈希表 <code>type_table</code>，用来存放所有定义的类。在 <code>type_new</code> 里面，我们先从全局表里面根据名字<code>type_table_lookup</code>查找找这个类。如果找到，说明这个类曾经被注册过，就报错；如果没有找到，说明这是一个新的类，则将 <code>TypeInfo</code> 里面信息填到 <code>TypeImpl</code> 里面。<code>type_table_add</code> 会将这个类注册到全局的表里面。到这里，我们注意，<code>class_init</code> 还没有被调用，也即这个类现在还处于纸面的状态。</p>
<p>这点更加像 Java 的反射机制了。在 Java 里面，对于一个类，首先我们写代码的时候要写一个 <code>class xxx</code> 的定义，编译好就放在<code>.class</code> 文件中，这也是出于纸面的状态。然后，Java 会有一个 <code>Class</code> 对象，用于读取和表示这个纸面上的 <code>class xxx</code>，可以生成真正的对象。</p>
<p>相同的过程在后面的代码中我们也可以看到，<code>class_init</code> 会生成<code>XXXClass</code>，就相当于 Java 里面的 <code>Class</code>对象，<code>TypeImpl</code> 还会有一个 <code>instance_init</code> 函数，相当于构造函数，用于根据 <code>XXXClass</code> 生成 <code>Object</code>，这就相当于 Java 反射里面最终创建的对象。和构造函数对应的还有 <code>instance_finalize</code>，相当于析构函数。</p>
<p>这一套反射机制放在 <code>qom</code> 文件夹下面，全称 <code>QEMU Object Model</code>，也即用 C 实现了一套<strong>面向对象的反射机制</strong>。</p>
<h2 id="初始化-machine">初始化 machine</h2>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210913115046.svg">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210913115046.svg" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">//vl.c
</span></span></span><span class="line"><span class="cl"><span class="nf">qemu_create_machine</span> <span class="p">(</span><span class="nf">select_machine</span><span class="p">());</span>
</span></span></code></pre></div><p>在创建 machine 之前，先要通过<code>select_machine</code>确定一个<code>machine</code>。<code>select_machine</code>又是怎么确定的呢，这就和我们命令行的输入有关，比如我们<code>-m spike</code>，那么这里就会选择<code>spike</code>作为<code>machine</code>。它的定义在<code>hw/riscv/spike.c</code>中。</p>
<p>在源码最后有这么一句，会和我们上面解析的<code>type_init</code> 是一样的，在全局的表里面注册了一个全局的名字是<code>spike</code>的纸面上的 <code>class</code>，也即 <code>TypeImpl</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">type_init</span><span class="p">(</span><span class="n">spike_machine_init_reqister_types</span><span class="p">)</span>
</span></span></code></pre></div><p>现在全局表中有这个纸面上的 <code>class</code> 了。我们回到 <code>select_machine</code>。</p>
<p>在 <code>select_machine</code> 中，有两种方式可以生成 <code>MachineClass</code>。一种方式是 <code>find_default_machine</code>，找一个默认的；另一种方式是 <code>machine_parse</code>，通过解析参数生成 <code>MachineClass</code>。无论哪种方式，都会调用 <code>object_class_get_list</code> 获得一个 <code>MachineClass</code> 的列表，然后在里面找。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="n">MachineClass</span> <span class="o">*</span><span class="nf">select_machine</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GSList</span> <span class="o">*</span><span class="n">machines</span> <span class="o">=</span> <span class="nf">object_class_get_list</span><span class="p">(</span><span class="n">TYPE_MACHINE</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">MachineClass</span> <span class="o">*</span><span class="n">machine_class</span> <span class="o">=</span> <span class="nf">find_default_machine</span><span class="p">(</span><span class="n">machines</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">optarg</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">QemuOpts</span> <span class="o">*</span><span class="n">opts</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Location</span> <span class="n">loc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">loc_push_none</span><span class="p">(</span><span class="o">&amp;</span><span class="n">loc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">opts</span> <span class="o">=</span> <span class="nf">qemu_get_machine_opts</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">qemu_opts_loc_restore</span><span class="p">(</span><span class="n">opts</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">optarg</span> <span class="o">=</span> <span class="nf">qemu_opt_get</span><span class="p">(</span><span class="n">opts</span><span class="p">,</span> <span class="s">&#34;type&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">optarg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">machine_class</span> <span class="o">=</span> <span class="nf">machine_parse</span><span class="p">(</span><span class="n">optarg</span><span class="p">,</span> <span class="n">machines</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">machine_class</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">error_report</span><span class="p">(</span><span class="s">&#34;No machine specified, and there is no default&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">error_printf</span><span class="p">(</span><span class="s">&#34;Use -machine help to list supported machines</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nf">loc_pop</span><span class="p">(</span><span class="o">&amp;</span><span class="n">loc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">g_slist_free</span><span class="p">(</span><span class="n">machines</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">machine_class</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">MachineClass</span> <span class="o">*</span><span class="nf">find_default_machine</span><span class="p">(</span><span class="n">GSList</span> <span class="o">*</span><span class="n">machines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GSList</span> <span class="o">*</span><span class="n">el</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">MachineClass</span> <span class="o">*</span><span class="n">default_machineclass</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">el</span> <span class="o">=</span> <span class="n">machines</span><span class="p">;</span> <span class="n">el</span><span class="p">;</span> <span class="n">el</span> <span class="o">=</span> <span class="n">el</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">MachineClass</span> <span class="o">*</span><span class="n">mc</span> <span class="o">=</span> <span class="n">el</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">mc</span><span class="o">-&gt;</span><span class="n">is_default</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">assert</span><span class="p">(</span><span class="n">default_machineclass</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">&amp;&amp;</span> <span class="s">&#34;Multiple default machines&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">default_machineclass</span> <span class="o">=</span> <span class="n">mc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">default_machineclass</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">MachineClass</span> <span class="o">*</span><span class="nf">machine_parse</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="n">GSList</span> <span class="o">*</span><span class="n">machines</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">MachineClass</span> <span class="o">*</span><span class="n">mc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GSList</span> <span class="o">*</span><span class="n">el</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">is_help_option</span><span class="p">(</span><span class="n">name</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;Supported machines are:</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">machines</span> <span class="o">=</span> <span class="nf">g_slist_sort</span><span class="p">(</span><span class="n">machines</span><span class="p">,</span> <span class="n">machine_class_cmp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="n">el</span> <span class="o">=</span> <span class="n">machines</span><span class="p">;</span> <span class="n">el</span><span class="p">;</span> <span class="n">el</span> <span class="o">=</span> <span class="n">el</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">MachineClass</span> <span class="o">*</span><span class="n">mc</span> <span class="o">=</span> <span class="n">el</span><span class="o">-&gt;</span><span class="n">data</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="p">(</span><span class="n">mc</span><span class="o">-&gt;</span><span class="n">alias</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%-20s %s (alias of %s)</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">mc</span><span class="o">-&gt;</span><span class="n">alias</span><span class="p">,</span> <span class="n">mc</span><span class="o">-&gt;</span><span class="n">desc</span><span class="p">,</span> <span class="n">mc</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;%-20s %s%s%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">mc</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="n">mc</span><span class="o">-&gt;</span><span class="n">desc</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                   <span class="n">mc</span><span class="o">-&gt;</span><span class="n">is_default</span> <span class="o">?</span> <span class="s">&#34; (default)&#34;</span> <span class="o">:</span> <span class="s">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                   <span class="n">mc</span><span class="o">-&gt;</span><span class="n">deprecation_reason</span> <span class="o">?</span> <span class="s">&#34; (deprecated)&#34;</span> <span class="o">:</span> <span class="s">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nf">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">mc</span> <span class="o">=</span> <span class="nf">find_machine</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">machines</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">mc</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">error_report</span><span class="p">(</span><span class="s">&#34;unsupported machine type&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">error_printf</span><span class="p">(</span><span class="s">&#34;Use -machine help to list supported machines</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">mc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>object_class_get_list</code> 定义如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">GSList</span> <span class="o">*</span><span class="nf">object_class_get_list</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">implements_type</span><span class="p">,</span><span class="kt">bool</span> <span class="n">include_abstract</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GSList</span> <span class="o">*</span><span class="n">list</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">object_class_foreach</span><span class="p">(</span><span class="n">object_class_get_list_tramp</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                         <span class="n">implements_type</span><span class="p">,</span> <span class="n">include_abstract</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">list</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">list</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">object_class_foreach</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">fn</span><span class="p">)(</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="n">klass</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                          <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">implements_type</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">include_abstract</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                          <span class="kt">void</span> <span class="o">*</span><span class="n">opaque</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">OCFData</span> <span class="n">data</span> <span class="o">=</span> <span class="p">{</span> <span class="n">fn</span><span class="p">,</span> <span class="n">implements_type</span><span class="p">,</span> <span class="n">include_abstract</span><span class="p">,</span> <span class="n">opaque</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">enumerating_types</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">g_hash_table_foreach</span><span class="p">(</span><span class="nf">type_table_get</span><span class="p">(),</span> <span class="n">object_class_foreach_tramp</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">enumerating_types</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在全局表 <code>type_table_get()</code> 中，对于每一项 <code>TypeImpl</code>，我们都执行 <code>object_class_foreach_tramp</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span> <span class="nf">object_class_foreach_tramp</span><span class="p">(</span><span class="n">gpointer</span> <span class="n">key</span><span class="p">,</span> <span class="n">gpointer</span> <span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                       <span class="n">gpointer</span> <span class="n">opaque</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">OCFData</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="n">opaque</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TypeImpl</span> <span class="o">*</span><span class="n">type</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ObjectClass</span> <span class="o">*</span><span class="n">k</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">type_initialize</span><span class="p">(</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">k</span> <span class="o">=</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">class</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">include_abstract</span> <span class="o">&amp;&amp;</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">abstract</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">implements_type</span> <span class="o">&amp;&amp;</span> 
</span></span><span class="line"><span class="cl">        <span class="o">!</span><span class="nf">object_class_dynamic_cast</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">implements_type</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span><span class="o">-&gt;</span><span class="nf">fn</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">opaque</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在 <code>object_class_foreach_tramp</code> 中，会调用将 <code>type_initialize</code>，这里面会调用 <code>class_init</code> 将纸面上的 <code>class</code> 也即 <code>TypeImpl</code> 变为 <code>ObjectClass</code>，<code>ObjectClass</code> 是所有<code>Class</code> 类的祖先，<code>MachineClass</code> 是它的子类。</p>
<p>因为在 <code>machine</code> 的命令行里面，我们指定了名字为<code>spike</code>，就肯定能够找到我们注册过了的 <code>TypeImpl</code>，并调用它的 <code>class_init</code> 函数。</p>
<p>所以，当 <code>select_machine</code> 执行完毕后，就有一个 <code>MachineClass</code> 了。</p>
<p>接着，我们回到 <code>qemu_create_machine</code> 中的<code>object_new_with_class</code>。这就很好理解了，<code>MachineClass</code> 是一个 <code>Class</code> 类，接下来应该通过它生成一个 <code>Instance</code>，也即对象，这就是 <code>object_new_with_class</code> 的作用。</p>
<p><code>object_new_with_class</code> 中，<code>TypeImpl</code> 的 <code>instance_init</code> 会被调用，创建一个对象。<code>current_machine</code> 就是这个对象，它的类型是<code>MachineState</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">Object</span> <span class="o">*</span><span class="nf">object_new_with_class</span><span class="p">(</span><span class="n">ObjectClass</span> <span class="o">*</span><span class="n">klass</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nf">object_new_with_type</span><span class="p">(</span><span class="n">klass</span><span class="o">-&gt;</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">Object</span> <span class="o">*</span><span class="nf">object_new_with_type</span><span class="p">(</span><span class="n">Type</span> <span class="n">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Object</span> <span class="o">*</span><span class="n">obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">type_initialize</span><span class="p">(</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">obj</span> <span class="o">=</span> <span class="nf">g_malloc</span><span class="p">(</span><span class="n">type</span><span class="o">-&gt;</span><span class="n">instance_size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">object_initialize_with_type</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">type</span><span class="o">-&gt;</span><span class="n">instance_size</span><span class="p">,</span> <span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">obj</span><span class="o">-&gt;</span><span class="n">free</span> <span class="o">=</span> <span class="n">g_free</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>至此，绕了这么大一圈，有关体系结构的对象才创建完毕，接下来很多的设备的初始化，包括 CPU 和内存的初始化，都是围绕着体系结构的对象来的，后面我们会常常看到<code>current_machine</code>。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220308180036.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20220308180036.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<h2 id="参考">参考</h2>
<p><a href="https://www.cnblogs.com/nm90/p/15661202.html">Qemu CPU 虚拟化 - 人生一世，草木一秋。 - 博客园</a>
<a href="https://www.cnblogs.com/LoyenWang/p/13796537.html">【原创】Linux 虚拟化 KVM-Qemu 分析（四）之 CPU 虚拟化（2） - LoyenWang - 博客园</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>VSCode 单步调试 QEMU</title>
      <link>https://lifeislife.cn/posts/vscode%E5%8D%95%E6%AD%A5%E8%B0%83%E8%AF%95qemu/</link>
      <pubDate>Tue, 24 Aug 2021 19:24:08 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/vscode%E5%8D%95%E6%AD%A5%E8%B0%83%E8%AF%95qemu/</guid>
      <description>&lt;p&gt;了解了如何在&lt;a href=&#34;https://dunky-z.github.io/2021/08/23/VSCode%E8%B0%83%E8%AF%95%E7%A8%8B%E5%BA%8F/&#34;&gt;VSCode 中调试程序&lt;/a&gt;，接下来我们在 VSCode 中搭建调试 QEMU 的环境。&lt;/p&gt;
&lt;h2 id=&#34;配置&#34;&gt;配置&lt;/h2&gt;
&lt;p&gt;首先我们需要&lt;a href=&#34;https://dunky-z.github.io/2021/07/23/QEMU%E5%88%9D%E8%AF%86/&#34;&gt;下载和编译 QEMU 源码&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --enable-debug --target-list=riscv32-softmmu,riscv32-linux-user --enable-kvm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一定要加上&lt;code&gt;--enable-debug&lt;/code&gt;，编译出的程序才带有调试信息，不用设置安装路径，编译时会自动在 qemu 文件夹下自动创建一个&lt;code&gt;build&lt;/code&gt;文件夹，编译后的程序也在&lt;code&gt;build&lt;/code&gt;文件夹下。&lt;/p&gt;
&lt;p&gt;用 VSCode 打开&lt;code&gt;qemu-6.X.X&lt;/code&gt;文件夹，&lt;code&gt;Ctrl+Shift+D&lt;/code&gt;打开调试配置。如果参考过&lt;a href=&#34;https://dunky-z.github.io/2021/08/23/VSCode%E8%B0%83%E8%AF%95%E7%A8%8B%E5%BA%8F/&#34;&gt;VSCode 中调试程序&lt;/a&gt;这篇文章，接下来就很容易。我们只需要将&lt;code&gt;launch.jason&lt;/code&gt;文件中的&lt;code&gt;program&lt;/code&gt;属性改为&lt;code&gt;${workspaceFolder}/build/qemu-system-riscv32&lt;/code&gt;即可。&lt;/p&gt;
&lt;h2 id=&#34;调试&#34;&gt;调试&lt;/h2&gt;
&lt;p&gt;打开&lt;code&gt;qemu-6.X.X/softmmu/main.c&lt;/code&gt;文件，在&lt;code&gt;main&lt;/code&gt;函数入口处打上断点，即可开始调试。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210824194442.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210824194442.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;p&gt;现在只需要点击屏幕上的图标，就可以快速的进行单步调试。&lt;/p&gt;
&lt;p&gt;如果需要进行命令行操作，在屏幕下方打开&lt;code&gt;DEBUG CONSOLE&lt;/code&gt;，输入&lt;code&gt;-exec+正常命令行下的命令&lt;/code&gt;即可在命令行中进行更多的调试。如查看断点信息&lt;code&gt;-exec info breakpoints&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210824201427.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210824201427.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<p>了解了如何在<a href="https://dunky-z.github.io/2021/08/23/VSCode%E8%B0%83%E8%AF%95%E7%A8%8B%E5%BA%8F/">VSCode 中调试程序</a>，接下来我们在 VSCode 中搭建调试 QEMU 的环境。</p>
<h2 id="配置">配置</h2>
<p>首先我们需要<a href="https://dunky-z.github.io/2021/07/23/QEMU%E5%88%9D%E8%AF%86/">下载和编译 QEMU 源码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">./configure --enable-debug --target-list=riscv32-softmmu,riscv32-linux-user --enable-kvm
</span></span></code></pre></div><p>一定要加上<code>--enable-debug</code>，编译出的程序才带有调试信息，不用设置安装路径，编译时会自动在 qemu 文件夹下自动创建一个<code>build</code>文件夹，编译后的程序也在<code>build</code>文件夹下。</p>
<p>用 VSCode 打开<code>qemu-6.X.X</code>文件夹，<code>Ctrl+Shift+D</code>打开调试配置。如果参考过<a href="https://dunky-z.github.io/2021/08/23/VSCode%E8%B0%83%E8%AF%95%E7%A8%8B%E5%BA%8F/">VSCode 中调试程序</a>这篇文章，接下来就很容易。我们只需要将<code>launch.jason</code>文件中的<code>program</code>属性改为<code>${workspaceFolder}/build/qemu-system-riscv32</code>即可。</p>
<h2 id="调试">调试</h2>
<p>打开<code>qemu-6.X.X/softmmu/main.c</code>文件，在<code>main</code>函数入口处打上断点，即可开始调试。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210824194442.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210824194442.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<p>现在只需要点击屏幕上的图标，就可以快速的进行单步调试。</p>
<p>如果需要进行命令行操作，在屏幕下方打开<code>DEBUG CONSOLE</code>，输入<code>-exec+正常命令行下的命令</code>即可在命令行中进行更多的调试。如查看断点信息<code>-exec info breakpoints</code></p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210824201427.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210824201427.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
]]></content:encoded>
    </item>
    <item>
      <title>在 QEMU 上运行 64 位和 32 位 RISC-V-Linux</title>
      <link>https://lifeislife.cn/posts/qemu%E4%B8%8A%E8%BF%90%E8%A1%8C64%E4%BD%8D%E5%92%8C32%E4%BD%8Drisc-v-linux/</link>
      <pubDate>Wed, 28 Jul 2021 13:47:56 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E4%B8%8A%E8%BF%90%E8%A1%8C64%E4%BD%8D%E5%92%8C32%E4%BD%8Drisc-v-linux/</guid>
      <description>&lt;h2 id=&#34;制作交叉工具链-riscv-gnu-toolchain&#34;&gt;制作交叉工具链 riscv-gnu-toolchain&lt;/h2&gt;
&lt;h3 id=&#34;下载源码&#34;&gt;下载源码&lt;/h3&gt;
&lt;p&gt;这个仓库是我遇到的最难下载的一个仓库了，公司网慢和虚拟机性能差都脱不了干系。估计下载了五小时都不止，刚开始还指望一个命令所有子模块都下载完的，结果愣是等了半天中断了。试了两次后放弃了。如果各位看官能一次完成，那您是福大。&lt;/p&gt;
&lt;p&gt;国内的码云平台有个&lt;a href=&#34;https://gitee.com/organizations/mirrors/projects&#34;&gt;Gitee 极速下载&lt;/a&gt;项目，上面有 GitHub 的一些常用开源项目的镜像，可供加速下载。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# riscv-gnu-toolchain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;https&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;//&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gitee&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;com&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mirrors&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gnu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toolchain&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;git&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下载时问题出现了，如果下载子模块仍然会卡住，如果不加&lt;code&gt;--recursive&lt;/code&gt;就只能下载主体内容，子模块都没有。（&lt;strong&gt;以下内容为第一安装时的方法，后续又找到了&lt;a href=&#34;&#34;&gt;git clone 快速下载子模块&lt;/a&gt;的方法&lt;/strong&gt;）&lt;/p&gt;
&lt;p&gt;开始下载时不加&lt;code&gt;--recursive&lt;/code&gt;参数，只下载&lt;code&gt;riscv-gnu-toolchain&lt;/code&gt;的主体内容，然后进入到&lt;code&gt;riscv-gnu-toolchain&lt;/code&gt;文件夹下，手动下载子模块的内容。&lt;/p&gt;
&lt;p&gt;当下完&lt;code&gt;riscv-binutils&lt;/code&gt;继续下载&lt;code&gt;riscv-gdb&lt;/code&gt;时发现这两个项目是同一个项目，只是不同的分支。但是码云上并没有区分，但是我也没找到在码云上的对应分支。只能用油猴脚本了。&lt;/p&gt;
&lt;p&gt;如果你有油猴插件可以去&lt;a href=&#34;https://greasyfork.org/zh-CN&#34;&gt;greasyfork&lt;/a&gt;搜索安装&lt;strong&gt;GitHub 镜像访问，加速下载&lt;/strong&gt;这个脚本，刷新 GitHub 仓库界面就会多出几个镜像地址，一般下载都会快好几倍。如果不用油猴插件的可以用我复制好的链接。&lt;/p&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210728155417.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210728155417.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# riscv-binutils
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://gitee.com/mirrors/riscv-binutils-gdb.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# riscv-gcc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://gitee.com/mirrors/riscv-gcc.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# riscv-dejagnu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://gitee.com/mirrors/riscv-dejagnu.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# riscv-glibc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://gitee.com/mirrors/riscv-glibc.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# riscv-newlib
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://gitee.com/mirrors/riscv-newlib.git
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# riscv-gdb
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone --depth=1 https://hub.fastgit.org/riscv/riscv-binutils-gdb.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;编译-riscv-gnu-toolchain&#34;&gt;编译 riscv-gnu-toolchain&lt;/h3&gt;
&lt;p&gt;提前安装如下软件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;sudo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;apt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;install&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;autoconf&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;automake&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;autotools&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;curl&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;python3&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;libmpc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;libmpfr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;libgmp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gawk&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;essential&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bison&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flex&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;texinfo&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gperf&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;libtool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;patchutils&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zlib1g&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;libexpat&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不听老人言，吃亏在眼前呀，本以为这是可选项，很多库都安装了，就没有操作这一步，结果就是编译半天结果还错了。如果报&lt;code&gt;make 错误 127&lt;/code&gt;，那就老老实实把前置的这些库都装上。&lt;/p&gt;
&lt;p&gt;建立&lt;code&gt;riscv-gnu-toolchain&lt;/code&gt;安装目录&lt;code&gt;/opt/riscv64&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --prefix=/opt/riscv64 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo make linux -j8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;导出安装路径&#34;&gt;导出安装路径&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PATH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;$PATH:/opt/riscv64/bin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;出现一下信息表示安装成功。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Using&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;built&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;specs&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;COLLECT_GCC&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv64&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unknown&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linux&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gnu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gcc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;COLLECT_LTO_WRAPPER&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv64&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libexec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gcc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv64&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unknown&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linux&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gnu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;10.2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lto&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;wrapper&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;riscv64&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unknown&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linux&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gnu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Configured&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;home&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dominic&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv64&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linux&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gnu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toolchain&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gcc&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;configure&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv64&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unknown&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linux&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gnu&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prefix&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv64&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sysroot&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;opt&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv64&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sysroot&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;system&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;zlib&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shared&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tls&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;languages&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fortran&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libmudflap&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libssp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libquadmath&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;libsanitizer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nls&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bootstrap&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=.././&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;riscv&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;gcc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;disable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;multilib&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;abi&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lp64d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arch&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rv64imafdc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tune&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rocket&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;CFLAGS_FOR_TARGET=-O2   -mcmodel=medlow&amp;#39;&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;CXXFLAGS_FOR_TARGET=-O2   -mcmodel=medlow&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;ne&#34;&gt;Thread&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;model&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;posix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Supported&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;LTO&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;compression&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;algorithms&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;zlib&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;gcc&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;version&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;10.2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GCC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;制作内核&#34;&gt;制作内核&lt;/h2&gt;
&lt;h3 id=&#34;下载-linux-内核&#34;&gt;下载 Linux 内核&lt;/h3&gt;
&lt;p&gt;makefile&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="制作交叉工具链-riscv-gnu-toolchain">制作交叉工具链 riscv-gnu-toolchain</h2>
<h3 id="下载源码">下载源码</h3>
<p>这个仓库是我遇到的最难下载的一个仓库了，公司网慢和虚拟机性能差都脱不了干系。估计下载了五小时都不止，刚开始还指望一个命令所有子模块都下载完的，结果愣是等了半天中断了。试了两次后放弃了。如果各位看官能一次完成，那您是福大。</p>
<p>国内的码云平台有个<a href="https://gitee.com/organizations/mirrors/projects">Gitee 极速下载</a>项目，上面有 GitHub 的一些常用开源项目的镜像，可供加速下载。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># riscv-gnu-toolchain</span>
</span></span><span class="line"><span class="cl"><span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">gitee</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">mirrors</span><span class="o">/</span><span class="n">riscv</span><span class="o">-</span><span class="n">gnu</span><span class="o">-</span><span class="n">toolchain</span><span class="o">.</span><span class="n">git</span>
</span></span></code></pre></div><p>下载时问题出现了，如果下载子模块仍然会卡住，如果不加<code>--recursive</code>就只能下载主体内容，子模块都没有。（<strong>以下内容为第一安装时的方法，后续又找到了<a href="">git clone 快速下载子模块</a>的方法</strong>）</p>
<p>开始下载时不加<code>--recursive</code>参数，只下载<code>riscv-gnu-toolchain</code>的主体内容，然后进入到<code>riscv-gnu-toolchain</code>文件夹下，手动下载子模块的内容。</p>
<p>当下完<code>riscv-binutils</code>继续下载<code>riscv-gdb</code>时发现这两个项目是同一个项目，只是不同的分支。但是码云上并没有区分，但是我也没找到在码云上的对应分支。只能用油猴脚本了。</p>
<p>如果你有油猴插件可以去<a href="https://greasyfork.org/zh-CN">greasyfork</a>搜索安装<strong>GitHub 镜像访问，加速下载</strong>这个脚本，刷新 GitHub 仓库界面就会多出几个镜像地址，一般下载都会快好几倍。如果不用油猴插件的可以用我复制好的链接。</p>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210728155417.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210728155417.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># riscv-binutils
</span></span><span class="line"><span class="cl">git clone https://gitee.com/mirrors/riscv-binutils-gdb.git
</span></span><span class="line"><span class="cl"># riscv-gcc
</span></span><span class="line"><span class="cl">git clone https://gitee.com/mirrors/riscv-gcc.git
</span></span><span class="line"><span class="cl"># riscv-dejagnu
</span></span><span class="line"><span class="cl">git clone https://gitee.com/mirrors/riscv-dejagnu.git
</span></span><span class="line"><span class="cl"># riscv-glibc
</span></span><span class="line"><span class="cl">git clone https://gitee.com/mirrors/riscv-glibc.git
</span></span><span class="line"><span class="cl"># riscv-newlib
</span></span><span class="line"><span class="cl">git clone https://gitee.com/mirrors/riscv-newlib.git
</span></span><span class="line"><span class="cl"># riscv-gdb
</span></span><span class="line"><span class="cl">git clone --depth=1 https://hub.fastgit.org/riscv/riscv-binutils-gdb.git
</span></span></code></pre></div><h3 id="编译-riscv-gnu-toolchain">编译 riscv-gnu-toolchain</h3>
<p>提前安装如下软件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">install</span> <span class="n">autoconf</span> <span class="n">automake</span> <span class="n">autotools</span><span class="o">-</span><span class="n">dev</span> <span class="n">curl</span> <span class="n">python3</span> <span class="n">libmpc</span><span class="o">-</span><span class="n">dev</span> <span class="n">libmpfr</span><span class="o">-</span><span class="n">dev</span> <span class="n">libgmp</span><span class="o">-</span><span class="n">dev</span> <span class="n">gawk</span> <span class="n">build</span><span class="o">-</span><span class="n">essential</span> <span class="n">bison</span> <span class="n">flex</span> <span class="n">texinfo</span> <span class="n">gperf</span> <span class="n">libtool</span> <span class="n">patchutils</span> <span class="n">bc</span> <span class="n">zlib1g</span><span class="o">-</span><span class="n">dev</span> <span class="n">libexpat</span><span class="o">-</span><span class="n">dev</span>
</span></span></code></pre></div><p>不听老人言，吃亏在眼前呀，本以为这是可选项，很多库都安装了，就没有操作这一步，结果就是编译半天结果还错了。如果报<code>make 错误 127</code>，那就老老实实把前置的这些库都装上。</p>
<p>建立<code>riscv-gnu-toolchain</code>安装目录<code>/opt/riscv64</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">./configure --prefix=/opt/riscv64 
</span></span><span class="line"><span class="cl">sudo make linux -j8
</span></span></code></pre></div><h3 id="导出安装路径">导出安装路径</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="k">export</span> <span class="n">PATH</span><span class="o">=</span><span class="s2">&#34;$PATH:/opt/riscv64/bin&#34;</span>
</span></span></code></pre></div><p>出现一下信息表示安装成功。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">Using</span> <span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">specs</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="n">COLLECT_GCC</span><span class="o">=</span><span class="n">riscv64</span><span class="o">-</span><span class="n">unknown</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="o">-</span><span class="n">gcc</span>
</span></span><span class="line"><span class="cl"><span class="n">COLLECT_LTO_WRAPPER</span><span class="o">=/</span><span class="n">opt</span><span class="o">/</span><span class="n">riscv64</span><span class="o">/</span><span class="n">libexec</span><span class="o">/</span><span class="n">gcc</span><span class="o">/</span><span class="n">riscv64</span><span class="o">-</span><span class="n">unknown</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="o">/</span><span class="mf">10.2</span><span class="o">.</span><span class="mi">0</span><span class="o">/</span><span class="n">lto</span><span class="o">-</span><span class="n">wrapper</span>
</span></span><span class="line"><span class="cl"><span class="n">Target</span><span class="p">:</span> <span class="n">riscv64</span><span class="o">-</span><span class="n">unknown</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span>
</span></span><span class="line"><span class="cl"><span class="n">Configured</span> <span class="n">with</span><span class="p">:</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">dominic</span><span class="o">/</span><span class="n">riscv64</span><span class="o">-</span><span class="n">linux</span><span class="o">/</span><span class="n">riscv</span><span class="o">-</span><span class="n">gnu</span><span class="o">-</span><span class="n">toolchain</span><span class="o">/</span><span class="n">riscv</span><span class="o">-</span><span class="n">gcc</span><span class="o">/</span><span class="n">configure</span> <span class="o">--</span><span class="n">target</span><span class="o">=</span><span class="n">riscv64</span><span class="o">-</span><span class="n">unknown</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span> <span class="o">--</span><span class="n">prefix</span><span class="o">=/</span><span class="n">opt</span><span class="o">/</span><span class="n">riscv64</span> <span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">sysroot</span><span class="o">=/</span><span class="n">opt</span><span class="o">/</span><span class="n">riscv64</span><span class="o">/</span><span class="n">sysroot</span> <span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">system</span><span class="o">-</span><span class="n">zlib</span> <span class="o">--</span><span class="n">enable</span><span class="o">-</span><span class="n">shared</span> <span class="o">--</span><span class="n">enable</span><span class="o">-</span><span class="n">tls</span> <span class="o">--</span><span class="n">enable</span><span class="o">-</span><span class="n">languages</span><span class="o">=</span><span class="n">c</span><span class="p">,</span><span class="n">c</span><span class="o">++</span><span class="p">,</span><span class="n">fortran</span> <span class="o">--</span><span class="n">disable</span><span class="o">-</span><span class="n">libmudflap</span> <span class="o">--</span><span class="n">disable</span><span class="o">-</span><span class="n">libssp</span> <span class="o">--</span><span class="n">disable</span><span class="o">-</span><span class="n">libquadmath</span> <span class="o">--</span><span class="n">disable</span><span class="o">-</span><span class="n">libsanitizer</span> <span class="o">--</span><span class="n">disable</span><span class="o">-</span><span class="n">nls</span> <span class="o">--</span><span class="n">disable</span><span class="o">-</span><span class="n">bootstrap</span> <span class="o">--</span><span class="n">src</span><span class="o">=.././</span><span class="n">riscv</span><span class="o">-</span><span class="n">gcc</span> <span class="o">--</span><span class="n">disable</span><span class="o">-</span><span class="n">multilib</span> <span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">abi</span><span class="o">=</span><span class="n">lp64d</span> <span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">arch</span><span class="o">=</span><span class="n">rv64imafdc</span> <span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">tune</span><span class="o">=</span><span class="n">rocket</span> <span class="s1">&#39;CFLAGS_FOR_TARGET=-O2   -mcmodel=medlow&#39;</span> <span class="s1">&#39;CXXFLAGS_FOR_TARGET=-O2   -mcmodel=medlow&#39;</span>
</span></span><span class="line"><span class="cl"><span class="ne">Thread</span> <span class="n">model</span><span class="p">:</span> <span class="n">posix</span>
</span></span><span class="line"><span class="cl"><span class="n">Supported</span> <span class="n">LTO</span> <span class="n">compression</span> <span class="n">algorithms</span><span class="p">:</span> <span class="n">zlib</span>
</span></span><span class="line"><span class="cl"><span class="n">gcc</span> <span class="n">version</span> <span class="mf">10.2</span><span class="o">.</span><span class="mi">0</span> <span class="p">(</span><span class="n">GCC</span><span class="p">)</span> 
</span></span></code></pre></div><h2 id="制作内核">制作内核</h2>
<h3 id="下载-linux-内核">下载 Linux 内核</h3>
<p>makefile</p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 文档</title>
      <link>https://lifeislife.cn/posts/qemu%E6%96%87%E6%A1%A3/</link>
      <pubDate>Tue, 27 Jul 2021 11:12:01 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E6%96%87%E6%A1%A3/</guid>
      <description>&lt;h1 id=&#34;调用文档&#34;&gt;调用文档&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;qemu-system-x86_64 [options] [disk_image] &lt;/code&gt;
&lt;code&gt;disk_image&lt;/code&gt;是 IDE 硬盘 0 的原始硬盘映像。某些目标不需要磁盘映像。&lt;/p&gt;
&lt;h2 id=&#34;标准参数-standard-options&#34;&gt;标准参数 Standard options&lt;/h2&gt;
&lt;h3 id=&#34;-h&#34;&gt;&lt;code&gt;-h&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;功能
显示帮助信息并退出&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;子参数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调用实例&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-riscv32 -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-version&#34;&gt;&lt;code&gt;-version&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;功能
显示 qemu 版本信息并退出&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;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-riscv32 -version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-machine-typenamepropvalue&#34;&gt;&lt;code&gt;-machine [type=]name[,prop=value[,...]]&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;功能
通过名称选择模拟器。使用 &lt;code&gt;-machine help&lt;/code&gt; 可以查看可用的模拟器。
对于支持跨版本实时迁移兼容性的架构，每个版本都会引入一个新的版本化模拟器类型。例如，2.8.0 版本为 &lt;code&gt;x86_64/i686&lt;/code&gt; 架构引入了&lt;code&gt;“pc-i440fx-2.8”&lt;/code&gt;和&lt;code&gt;“pc-q35-2.8”&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;子参数
为了允许用户从 QEMU 2.8.0 版实时迁移到 QEMU 2.9.0 版，2.9.0 版也必须支持&lt;code&gt;“pc-i440fx-2.8”&lt;/code&gt;和&lt;code&gt;“pc-q35-2.8”&lt;/code&gt;机器。为了允许用户在升级时实时迁移 VMs 跳过多个中间版本，QEMU 的新版本将支持多个以前版本的机器类型。
支持的机器属性有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;accel=accels1[:accels2[:...]]&lt;/code&gt;
This is used to enable an accelerator. Depending on the target architecture, kvm, xen, hax, hvf, nvmm, whpx or tcg can be available. By default, tcg is used. If there is more than one accelerator specified, the next one is used if the previous one fails to initialize.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vmport=on|off|auto&lt;/code&gt;
Enables emulation of VMWare IO port, for vmmouse etc. auto says to select the value based on accel. For accel=xen the default is off otherwise the default is on.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dump-guest-core=on|off&lt;/code&gt;
Include guest memory in a core dump. The default is on.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mem-merge=on|off&lt;/code&gt;
Enables or disables memory merge support. This feature, when supported by the host, de-duplicates identical memory pages among VMs instances (enabled by default).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aes-key-wrap=on|off&lt;/code&gt;
Enables or disables AES key wrapping support on s390-ccw hosts. This feature controls whether AES wrapping keys will be created to allow execution of AES cryptographic functions. The default is on.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dea-key-wrap=on|off&lt;/code&gt;
Enables or disables DEA key wrapping support on s390-ccw hosts. This feature controls whether DEA wrapping keys will be created to allow execution of DEA cryptographic functions. The default is on.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nvdimm=on|off&lt;/code&gt;
Enables or disables NVDIMM support. The default is off.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;memory-encryption=&lt;/code&gt;
Memory encryption object to use. The default is none.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hmat=on|off&lt;/code&gt;
Enables or disables ACPI Heterogeneous Memory Attribute Table (HMAT) support. The default is off.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;memory-backend=&#39;id&#39;&lt;/code&gt;
An alternative to legacy -mem-path and mem-prealloc options. Allows to use a memory backend as main RAM.
For example: &lt;code&gt;:: -object memory-backend-file,id=pc.ram,size=512M,mem-path=/hugetlbfs,prealloc=on,share=on -machine memory-backend=pc.ram -m 512M&lt;/code&gt;
Migration compatibility note: a) as backend id one shall use value of ‘default-ram-id’, advertised by machine type (available via query-machines QMP command), if migration to/from old QEMU (&amp;lt;5.0) is expected. b) for machine types 4.0 and older, user shall use x-use-canonical-path-for-ramblock-id=off backend option if migration to/from old QEMU (&amp;lt;5.0) is expected. For example: :: -object memory-backend-ram,id=pc.ram,size=512M,x-use-canonical-path-for-ramblock-id=off -machine memory-backend=pc.ram -m 512M&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调用实例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-riscv32 -machine virt,mem-merge=on
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-cpu-model&#34;&gt;&lt;code&gt;-cpu model&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
选择 CPU 型号（&lt;code&gt;-cpu help&lt;/code&gt;显示帮助列表和附加功能的选项）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;默认情况会给客户机提供 qemu64 或 qemu32 的基本 CPU 模型。这样做可以对 CPU 特性提供一些高级的过滤功能，让客户机在同一组硬件平台上的动态迁移会更加平滑和安全。
在客户机中查看 CPU 信息 (cat /proc/cpuinfo)，model name 就是当前 CPU 模型的名称。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;调用实例&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-riscv32 -cpu rv32
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;accel-namepropvalue&#34;&gt;&lt;code&gt;accel name[,prop=value[,...]]&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
This is used to enable an accelerator. Depending on the target architecture, kvm, xen, hax, hvf, nvmm, whpx or tcg can be available. By default, tcg is used. If there is more than one accelerator specified, the next one is used if the previous one fails to initialize.&lt;/li&gt;
&lt;li&gt;子参数
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;igd-passthru=on|off&lt;/code&gt;
When Xen is in use, this option controls whether Intel integrated graphics devices can be passed through to the guest (default=off)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kernel-irqchip=on|off|split&lt;/code&gt;
Controls KVM in-kernel irqchip support. The default is full acceleration of the interrupt controllers. On x86, split irqchip reduces the kernel attack surface, at a performance cost for non-MSI interrupts. Disabling the in-kernel irqchip completely is not recommended except for debugging purposes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kvm-shadow-mem=size&lt;/code&gt;
Defines the size of the KVM shadow MMU.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;split-wx=on|off&lt;/code&gt;
Controls the use of split w^x mapping for the TCG code generation buffer. Some operating systems require this to be enabled, and in such a case this will default on. On other operating systems, this will default off, but one may enable this for testing or debugging.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tb-size=n&lt;/code&gt;
Controls the size (in MiB) of the TCG translation block cache.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;thread=single|multi&lt;/code&gt;
Controls number of TCG threads. When the TCG is multi-threaded there will be one thread per vCPU therefore taking advantage of additional host cores. The default is to enable multi-threading where both the back-end and front-ends support it and no incompatible TCG features have been enabled (e.g. icount/replay).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dirty-ring-size=n&lt;/code&gt;
When the KVM accelerator is used, it controls the size of the per-vCPU dirty page ring buffer (number of entries for each vCPU). It should be a value that is power of two, and it should be 1024 or bigger (but still less than the maximum value that the kernel supports). 4096 could be a good initial value if you have no idea which is the best. Set this value to 0 to disable the feature. By default, this feature is disabled (dirty-ring-size=0). When enabled, KVM will instead record dirty pages in a bitmap.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;smp-cpusnmaxcpusmaxcpussocketssocketsdiesdiescorescoresthreadsthreads&#34;&gt;&lt;code&gt;smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;功能
配置客户机的 SMP（Symmetric Multi-Processing），对称多处理机&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;子参数&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[cpus=]n&lt;/code&gt;
设置客户机中使用逻辑的 CPU 数量（默认值是 1）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,maxcpus=cpus]&lt;/code&gt;
设置客户机最大可能被使用的 CPU 数量（可以用热插拔 hot-plug 添加 CPU，不能超过 maxcpus 上限）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,cores=cores]&lt;/code&gt;
设置每个 CPU socket 上的 core 数量（默认值是 1）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,threads=threads]&lt;/code&gt;
设置每个 CPU core 上的线程数（默认值是 1）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,sockets=sockets]&lt;/code&gt;
设置客户机中总的 CPU socket 数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调用实例&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64  -smp 1,sockets=1,cores=2,threads=2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;-numa-nodememsizecpusfirstcpu-lastcpunodeidnodeinitiatorinitiator&#34;&gt;&lt;code&gt;-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node][,initiator=initiator]&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id=&#34;-numa-nodememdevidcpusfirstcpu-lastcpunodeidnodeinitiatorinitiator&#34;&gt;&lt;code&gt;-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node][,initiator=initiator]&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id=&#34;-numa-distsrcsourcedstdestinationvaldistance&#34;&gt;&lt;code&gt;-numa dist,src=source,dst=destination,val=distance&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id=&#34;-numa-cpunode-idnodesocket-idxcore-idythread-idz&#34;&gt;&lt;code&gt;-numa cpu,node-id=node[,socket-id=x][,core-id=y][,thread-id=z]&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id=&#34;-numa-hmat-lbinitiatornodetargetnodehierarchyhierarchydata-typetpyelatencylatbandwidthbw&#34;&gt;&lt;code&gt;-numa hmat-lb,initiator=node,target=node,hierarchy=hierarchy,data-type=tpye[,latency=lat][,bandwidth=bw]&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id=&#34;-numa-hmat-cachenode-idnodesizesizelevellevelassociativitystrpolicystrlinesize&#34;&gt;&lt;code&gt;-numa hmat-cache,node-id=node,size=size,level=level[,associativity=str][,policy=str][,line=size]&lt;/code&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;h3 id=&#34;-add-fd-fdfdsetsetopaqueopaque&#34;&gt;&lt;code&gt;-add-fd fd=fd,set=set[,opaque=opaque]&lt;/code&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;h3 id=&#34;-set-groupidargvalue&#34;&gt;&lt;code&gt;-set group.id.arg=value&lt;/code&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;h3 id=&#34;-global-driverpropvalue&#34;&gt;&lt;code&gt;-global driver.prop=value&lt;/code&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;h3 id=&#34;-global-driverdriverpropertypropertyvaluevalue&#34;&gt;&lt;code&gt;-global driver=driver,property=property,value=value&lt;/code&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;h3 id=&#34;-boot-orderdrivesoncedrivesmenuonoffsplashsp_namesplash-timesp_timereboot-timeoutrb_timeoutstrictonoff&#34;&gt;&lt;code&gt;-boot [order=drives][,once=drives][,menu=on|off][,splash=sp_name][,splash-time=sp_time][,reboot-timeout=rb_timeout][,strict=on|off]&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
设置客户机启动顺序&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;在 qemu 模拟的 x86 平台中，用&amp;quot;a&amp;quot;、&amp;ldquo;b&amp;quot;分别表示第一和第二软驱，用&amp;quot;c&amp;quot;表示第一个硬盘，用&amp;quot;d&amp;quot;表示 CD-ROM 光驱，用&amp;quot;n&amp;quot;表示从网络启动。
默认从硬盘启动。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;子参数&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[order=drives]&lt;/code&gt;
设置启动顺序。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,once=drives]&lt;/code&gt;
只设置下一次启动的顺序，再重启后无效。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,menu=on|off]&lt;/code&gt;
只要固件/BIOS 支持，就可以启用交互式引导菜单/提示。默认为非交互式引导。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,splash=sp_name]&lt;/code&gt;
如果固件/BIOS 支持选项 splash=sp_name 和 menu=on，则可以将启动画面传递给 bios，使用户能够将其显示为徽标。目前 Seabios for X86 系统支持它。限制：启动文件可以是 24 BPP 格式（真彩色）的 jpeg 文件或 BMP 文件。分辨率应该是 SVGA 模式支持的，推荐 320x240、640x480、800x640。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,splash-time=sp_time]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,reboot-timeout=rb_timeout]&lt;/code&gt;
引导失败时，客户机将暂停 &lt;code&gt;rb_timeout&lt;/code&gt; 毫秒，然后重新启动。如果 &lt;code&gt;rb_timeout&lt;/code&gt; 为 &amp;lsquo;-1&amp;rsquo;，客户机不会重启，qemu 默认将 &amp;lsquo;-1&amp;rsquo; 传递给 bios。目前 Seabios for X86 系统支持它。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,strict=on|off]&lt;/code&gt;
只要固件/BIOS 支持，就通过严格启动。这仅在 bootindex 选项更改引导优先级时有效。默认为非严格引导。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调用实例&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# 尝试先从网络启动，然后从硬盘启动
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -boot order=nc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# 先从光驱启动，重启后切换回默认顺序
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -boot once=d
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# 5 秒钟的启动画面。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -boot menu=on,splash=/root/boot.bmp,splash-time=5000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-m-sizemegsslotsnmaxmemsize&#34;&gt;&lt;code&gt;-m [size=]megs[,slots=n,maxmem=size]&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;功能
将客户机内存设置为 &lt;code&gt;megs&lt;/code&gt; M字节。默认值为 &lt;code&gt;128 MiB&lt;/code&gt;。或者，也可以使用“M”或“G”的后缀。齐。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;子参数&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[size=]megs&lt;/code&gt;
将客户机内存设置为 &lt;code&gt;megs&lt;/code&gt; M字节&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,slots=n,maxmem=size]&lt;/code&gt;
可用于设置可热插拔内存插槽的数量和最大内存数量。&lt;code&gt;maxmem&lt;/code&gt; 必须与页面大小对&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调用实例
以下命令行将客户机启动 RAM 大小设置为 1GB，创建 3 个插槽以热插拔额外内存，并将客户机可以达到的最大内存设置为 4GB：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 -m 1G,slots=3,maxmem=4G
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果未指定 &lt;code&gt;slot&lt;/code&gt; 和 &lt;code&gt;maxmem&lt;/code&gt;，则不会启用内存热插拔，并且客户机内存永远不会增加。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-mem-path-path&#34;&gt;&lt;code&gt;-mem-path path&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
使用huge page。对于内存访问密集型的应用，使用&lt;code&gt;huge page&lt;/code&gt;是可以比较明显地提高客户机性能。 使用&lt;code&gt;huge page&lt;/code&gt;的内存不能被换出（swap out），也不能使用&lt;code&gt;ballooning&lt;/code&gt;方式自动增长。&lt;/li&gt;
&lt;li&gt;子参数&lt;/li&gt;
&lt;li&gt;调用实例&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-mem-prealloc&#34;&gt;&lt;code&gt;-mem-prealloc&lt;/code&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;h3 id=&#34;-k-language&#34;&gt;&lt;code&gt;-k language&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;功能
设置键盘布局语言，默认为&lt;code&gt;en-us&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;子参数
可用布局：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ar  de-ch  es  fo     fr-ca  hu  ja  mk     no  pt-br  sv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;da  en-gb  et  fr     fr-ch  is  lt  nl     pl  ru     th
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;de  en-us  fi  fr-be  hr     it  lv  nl-be  pt  sl     tr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调用实例&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;块设备参数-block-device-options&#34;&gt;块设备参数 Block device options&lt;/h2&gt;
&lt;h3 id=&#34;fda-file&#34;&gt;&lt;code&gt;fda file&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
为客户机指定软盘设备，指定客户机的第一个软盘设备，在客户机中显示为&lt;code&gt;/dev/fd0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;子参数&lt;/li&gt;
&lt;li&gt;调用实例&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;fdb-file&#34;&gt;&lt;code&gt;fdb file&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
为客户机指定软盘设备，指定客户机的第一个软盘设备，在客户机中显示为&lt;code&gt;/dev/fd1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;子参数&lt;/li&gt;
&lt;li&gt;调用实例&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;hda-file&#34;&gt;&lt;code&gt;hda file&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id=&#34;hdb-file&#34;&gt;&lt;code&gt;hdb file&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id=&#34;hdc-file&#34;&gt;&lt;code&gt;hdc file&lt;/code&gt;&lt;/h3&gt;
&lt;h3 id=&#34;hdd-file&#34;&gt;&lt;code&gt;hdd file&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
为客户机指定块存储设备，指定客户机种的第一个 IDE 设备&lt;/li&gt;
&lt;li&gt;子参数
若客户机使用&lt;code&gt;PIIX_IDE&lt;/code&gt;驱动，显示为&lt;code&gt;/dev/hda&lt;/code&gt;设备；
若客户机使用&lt;code&gt;ata_piix&lt;/code&gt;驱动，显示为&lt;code&gt;/dev/sda&lt;/code&gt;设备。
若没有使用&lt;code&gt;-hdx&lt;/code&gt;的参数，则默认使用&lt;code&gt;-hda&lt;/code&gt;参数；
可以将宿主机的一块硬盘作为&lt;code&gt;-hda&lt;/code&gt;的参数使用；
若文件名包含逗号，应使用两个连续的逗号进行转义。&lt;/li&gt;
&lt;li&gt;调用实例&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-cdrom-file&#34;&gt;&lt;code&gt;-cdrom file&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
为客户机指定光盘 CD-ROM。可以将宿主机的光驱&lt;code&gt;/dev/cdrom&lt;/code&gt;设备作为&lt;code&gt;-cdrom&lt;/code&gt;参数使用。&lt;code&gt;-cdrom&lt;/code&gt;参数不能与&lt;code&gt;-hdc&lt;/code&gt;参数同时使用，因为&lt;code&gt;-cdrom&lt;/code&gt;就是客户机里的第三个 IDE 设备&lt;/li&gt;
&lt;li&gt;子参数&lt;/li&gt;
&lt;li&gt;调用实例&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-blockdev-optionoptionoption&#34;&gt;&lt;code&gt;-blockdev option[,option[,option[,...]]]&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;功能&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;子参数&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调用实例&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-drive-optionoptionoption&#34;&gt;&lt;code&gt;-drive option[,option[,option[,...]]]&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;功能
定义一个存储驱动器&lt;/li&gt;
&lt;li&gt;子参数
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[file=file]&lt;/code&gt;
加载&lt;code&gt;file&lt;/code&gt;镜像文件到客户机的驱动器中。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,if=type]&lt;/code&gt;
指定驱动器使用的接口类型：可用的类类型有：&lt;code&gt;ide&lt;/code&gt;、&lt;code&gt;scsi&lt;/code&gt;、&lt;code&gt;virtio&lt;/code&gt;、&lt;code&gt;sd&lt;/code&gt;、&lt;code&gt;floopy&lt;/code&gt;、&lt;code&gt;pflash&lt;/code&gt;等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,bus=n]&lt;/code&gt;
设置驱动器在客户机中的总线编号。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,unit=m]&lt;/code&gt;
设置驱动器在客户机中的单元编号。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,media=d]&lt;/code&gt;
设置驱动器中媒介的类型，值为 disk 或 cdrom。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,index=i]&lt;/code&gt;
设置在通一种接口的驱动器中的索引编号。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,snapshot=on|off]&lt;/code&gt;
当值为 on 时，qemu 不会将磁盘数据的更改写回到镜像文件中，而是写到临时文件中，可以在 qemu moinitor 中使用 commit 命令强制将磁盘数据保存回镜像文件中。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,cache=writethrough|writeback|none|directsync|unsafe]&lt;/code&gt;
设置宿主机对块设备数据访问的 cache 模式。，
&lt;code&gt;writethrough&lt;/code&gt;（直写模式）：调用 write 写入数据的同时将数据写入磁盘缓存和后端块设备中。
&lt;code&gt;writeback&lt;/code&gt;（回写模式）：调用 write 写入数据时只将数据写入到磁盘缓存中，当数据被换出缓存时才写入到后端存储中。优点写入数据块，缺点系统掉电数据无法恢复。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,aio=threads|native]&lt;/code&gt;
选择异步 IO 的方式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;threads&lt;/code&gt;
为 aio 参数的默认值，让一个线程池去处理异步 IO；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;native&lt;/code&gt;
只适用于 cache=none 的情况，使用的是 Linux 原生的 AIO。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,format=f]&lt;/code&gt;
指定使用的磁盘格式，默认是 QEMU 自动检测磁盘格式的。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,serial=s]&lt;/code&gt;
指定分配给设备的序列号。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,addr=A]&lt;/code&gt;
分配给驱动器控制器的 PCI 地址，只在使用 virtio 接口时适用。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[,id=name]&lt;/code&gt;
设置驱动器的 ID，可以在 QEMU monitor 中用 info block 看到。
&lt;code&gt;[,readonly=on|off]&lt;/code&gt;
设置驱动器是否只读。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;调用实例&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;usb-参数-usb-convenience-options&#34;&gt;USB 参数 USB convenience options&lt;/h2&gt;
&lt;h2 id=&#34;显示参数-display-options&#34;&gt;显示参数 Display options&lt;/h2&gt;
&lt;h2 id=&#34;仅限-i386-架构的参数-i386-target-only&#34;&gt;仅限 i386 架构的参数 i386 target only&lt;/h2&gt;
&lt;h2 id=&#34;网络参数-network-options&#34;&gt;网络参数 Network options&lt;/h2&gt;
&lt;h2 id=&#34;字符设备参数-character-device-options&#34;&gt;字符设备参数 Character device options&lt;/h2&gt;
&lt;h2 id=&#34;tpm-设备-tpm-device-options&#34;&gt;TPM 设备 TPM device options&lt;/h2&gt;
&lt;h2 id=&#34;指定启动指引-linuxmultiboot-boot-specific&#34;&gt;指定启动指引 Linux/Multiboot boot specific&lt;/h2&gt;
&lt;p&gt;当使用该调用参数时，你可以使用给定的 Linux 或者多重引导内核，而不需要安装内核到一个光盘中。这样可以更方便地测试不同内核。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-kernel bzImage&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;功能
用 bzImage 作为内核镜像，也可以使用其他启动格式。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-append cmdline&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;功能
用&lt;code&gt;cmd&lt;/code&gt;命令行，作为内核的命令行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-initrd file&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;功能
用文件作为初始化 ram&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-initrd &amp;quot;file1 arg=foo,file2&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;功能
此语法仅适用于多重引导
使用 &lt;code&gt;file1&lt;/code&gt; 和 &lt;code&gt;file2&lt;/code&gt; 作为模块并将 &lt;code&gt;arg=foo&lt;/code&gt; 作为参数传递给第一个模块&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-dtb file&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;功能
将文件用作设备树二进制 (dtb) 映像并在启动时将其传递给内核&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
      <content:encoded><![CDATA[<h1 id="调用文档">调用文档</h1>
<p><code>qemu-system-x86_64 [options] [disk_image] </code>
<code>disk_image</code>是 IDE 硬盘 0 的原始硬盘映像。某些目标不需要磁盘映像。</p>
<h2 id="标准参数-standard-options">标准参数 Standard options</h2>
<h3 id="-h"><code>-h</code></h3>
<ul>
<li>
<p>功能
显示帮助信息并退出</p>
</li>
<li>
<p>子参数</p>
</li>
<li>
<p>调用实例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-system-riscv32 -h
</span></span></code></pre></div></li>
</ul>
<h3 id="-version"><code>-version</code></h3>
<ul>
<li>
<p>功能
显示 qemu 版本信息并退出</p>
</li>
<li>
<p>子参数</p>
</li>
<li>
<p>调用实例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-system-riscv32 -version
</span></span></code></pre></div></li>
</ul>
<h3 id="-machine-typenamepropvalue"><code>-machine [type=]name[,prop=value[,...]]</code></h3>
<ul>
<li>
<p>功能
通过名称选择模拟器。使用 <code>-machine help</code> 可以查看可用的模拟器。
对于支持跨版本实时迁移兼容性的架构，每个版本都会引入一个新的版本化模拟器类型。例如，2.8.0 版本为 <code>x86_64/i686</code> 架构引入了<code>“pc-i440fx-2.8”</code>和<code>“pc-q35-2.8”</code>。</p>
</li>
<li>
<p>子参数
为了允许用户从 QEMU 2.8.0 版实时迁移到 QEMU 2.9.0 版，2.9.0 版也必须支持<code>“pc-i440fx-2.8”</code>和<code>“pc-q35-2.8”</code>机器。为了允许用户在升级时实时迁移 VMs 跳过多个中间版本，QEMU 的新版本将支持多个以前版本的机器类型。
支持的机器属性有：</p>
<ul>
<li><code>accel=accels1[:accels2[:...]]</code>
This is used to enable an accelerator. Depending on the target architecture, kvm, xen, hax, hvf, nvmm, whpx or tcg can be available. By default, tcg is used. If there is more than one accelerator specified, the next one is used if the previous one fails to initialize.</li>
<li><code>vmport=on|off|auto</code>
Enables emulation of VMWare IO port, for vmmouse etc. auto says to select the value based on accel. For accel=xen the default is off otherwise the default is on.</li>
<li><code>dump-guest-core=on|off</code>
Include guest memory in a core dump. The default is on.</li>
<li><code>mem-merge=on|off</code>
Enables or disables memory merge support. This feature, when supported by the host, de-duplicates identical memory pages among VMs instances (enabled by default).</li>
<li><code>aes-key-wrap=on|off</code>
Enables or disables AES key wrapping support on s390-ccw hosts. This feature controls whether AES wrapping keys will be created to allow execution of AES cryptographic functions. The default is on.</li>
<li><code>dea-key-wrap=on|off</code>
Enables or disables DEA key wrapping support on s390-ccw hosts. This feature controls whether DEA wrapping keys will be created to allow execution of DEA cryptographic functions. The default is on.</li>
<li><code>nvdimm=on|off</code>
Enables or disables NVDIMM support. The default is off.</li>
<li><code>memory-encryption=</code>
Memory encryption object to use. The default is none.</li>
<li><code>hmat=on|off</code>
Enables or disables ACPI Heterogeneous Memory Attribute Table (HMAT) support. The default is off.</li>
<li><code>memory-backend='id'</code>
An alternative to legacy -mem-path and mem-prealloc options. Allows to use a memory backend as main RAM.
For example: <code>:: -object memory-backend-file,id=pc.ram,size=512M,mem-path=/hugetlbfs,prealloc=on,share=on -machine memory-backend=pc.ram -m 512M</code>
Migration compatibility note: a) as backend id one shall use value of ‘default-ram-id’, advertised by machine type (available via query-machines QMP command), if migration to/from old QEMU (&lt;5.0) is expected. b) for machine types 4.0 and older, user shall use x-use-canonical-path-for-ramblock-id=off backend option if migration to/from old QEMU (&lt;5.0) is expected. For example: :: -object memory-backend-ram,id=pc.ram,size=512M,x-use-canonical-path-for-ramblock-id=off -machine memory-backend=pc.ram -m 512M</li>
</ul>
</li>
<li>
<p>调用实例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-system-riscv32 -machine virt,mem-merge=on
</span></span></code></pre></div></li>
</ul>
<h3 id="-cpu-model"><code>-cpu model</code></h3>
<ul>
<li>功能
选择 CPU 型号（<code>-cpu help</code>显示帮助列表和附加功能的选项）</li>
</ul>
<blockquote>
<p>默认情况会给客户机提供 qemu64 或 qemu32 的基本 CPU 模型。这样做可以对 CPU 特性提供一些高级的过滤功能，让客户机在同一组硬件平台上的动态迁移会更加平滑和安全。
在客户机中查看 CPU 信息 (cat /proc/cpuinfo)，model name 就是当前 CPU 模型的名称。</p>
</blockquote>
<ul>
<li>
<p>调用实例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-system-riscv32 -cpu rv32
</span></span></code></pre></div></li>
</ul>
<h3 id="accel-namepropvalue"><code>accel name[,prop=value[,...]]</code></h3>
<ul>
<li>功能
This is used to enable an accelerator. Depending on the target architecture, kvm, xen, hax, hvf, nvmm, whpx or tcg can be available. By default, tcg is used. If there is more than one accelerator specified, the next one is used if the previous one fails to initialize.</li>
<li>子参数
<ul>
<li><code>igd-passthru=on|off</code>
When Xen is in use, this option controls whether Intel integrated graphics devices can be passed through to the guest (default=off)</li>
<li><code>kernel-irqchip=on|off|split</code>
Controls KVM in-kernel irqchip support. The default is full acceleration of the interrupt controllers. On x86, split irqchip reduces the kernel attack surface, at a performance cost for non-MSI interrupts. Disabling the in-kernel irqchip completely is not recommended except for debugging purposes.</li>
<li><code>kvm-shadow-mem=size</code>
Defines the size of the KVM shadow MMU.</li>
<li><code>split-wx=on|off</code>
Controls the use of split w^x mapping for the TCG code generation buffer. Some operating systems require this to be enabled, and in such a case this will default on. On other operating systems, this will default off, but one may enable this for testing or debugging.</li>
<li><code>tb-size=n</code>
Controls the size (in MiB) of the TCG translation block cache.</li>
<li><code>thread=single|multi</code>
Controls number of TCG threads. When the TCG is multi-threaded there will be one thread per vCPU therefore taking advantage of additional host cores. The default is to enable multi-threading where both the back-end and front-ends support it and no incompatible TCG features have been enabled (e.g. icount/replay).</li>
<li><code>dirty-ring-size=n</code>
When the KVM accelerator is used, it controls the size of the per-vCPU dirty page ring buffer (number of entries for each vCPU). It should be a value that is power of two, and it should be 1024 or bigger (but still less than the maximum value that the kernel supports). 4096 could be a good initial value if you have no idea which is the best. Set this value to 0 to disable the feature. By default, this feature is disabled (dirty-ring-size=0). When enabled, KVM will instead record dirty pages in a bitmap.</li>
</ul>
</li>
</ul>
<h3 id="smp-cpusnmaxcpusmaxcpussocketssocketsdiesdiescorescoresthreadsthreads"><code>smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]</code></h3>
<ul>
<li>
<p>功能
配置客户机的 SMP（Symmetric Multi-Processing），对称多处理机</p>
</li>
<li>
<p>子参数</p>
<ul>
<li><code>[cpus=]n</code>
设置客户机中使用逻辑的 CPU 数量（默认值是 1）。</li>
<li><code>[,maxcpus=cpus]</code>
设置客户机最大可能被使用的 CPU 数量（可以用热插拔 hot-plug 添加 CPU，不能超过 maxcpus 上限）。</li>
<li><code>[,cores=cores]</code>
设置每个 CPU socket 上的 core 数量（默认值是 1）。</li>
<li><code>[,threads=threads]</code>
设置每个 CPU core 上的线程数（默认值是 1）。</li>
<li><code>[,sockets=sockets]</code>
设置客户机中总的 CPU socket 数量。</li>
</ul>
</li>
<li>
<p>调用实例</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-system-x86_64  -smp 1,sockets=1,cores=2,threads=2
</span></span></code></pre></div><h3 id="-numa-nodememsizecpusfirstcpu-lastcpunodeidnodeinitiatorinitiator"><code>-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node][,initiator=initiator]</code></h3>
<h3 id="-numa-nodememdevidcpusfirstcpu-lastcpunodeidnodeinitiatorinitiator"><code>-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node][,initiator=initiator]</code></h3>
<h3 id="-numa-distsrcsourcedstdestinationvaldistance"><code>-numa dist,src=source,dst=destination,val=distance</code></h3>
<h3 id="-numa-cpunode-idnodesocket-idxcore-idythread-idz"><code>-numa cpu,node-id=node[,socket-id=x][,core-id=y][,thread-id=z]</code></h3>
<h3 id="-numa-hmat-lbinitiatornodetargetnodehierarchyhierarchydata-typetpyelatencylatbandwidthbw"><code>-numa hmat-lb,initiator=node,target=node,hierarchy=hierarchy,data-type=tpye[,latency=lat][,bandwidth=bw]</code></h3>
<h3 id="-numa-hmat-cachenode-idnodesizesizelevellevelassociativitystrpolicystrlinesize"><code>-numa hmat-cache,node-id=node,size=size,level=level[,associativity=str][,policy=str][,line=size]</code></h3>
<ul>
<li>功能</li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="-add-fd-fdfdsetsetopaqueopaque"><code>-add-fd fd=fd,set=set[,opaque=opaque]</code></h3>
<ul>
<li>功能</li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="-set-groupidargvalue"><code>-set group.id.arg=value</code></h3>
<ul>
<li>功能</li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="-global-driverpropvalue"><code>-global driver.prop=value</code></h3>
<ul>
<li>功能</li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="-global-driverdriverpropertypropertyvaluevalue"><code>-global driver=driver,property=property,value=value</code></h3>
<ul>
<li>功能</li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="-boot-orderdrivesoncedrivesmenuonoffsplashsp_namesplash-timesp_timereboot-timeoutrb_timeoutstrictonoff"><code>-boot [order=drives][,once=drives][,menu=on|off][,splash=sp_name][,splash-time=sp_time][,reboot-timeout=rb_timeout][,strict=on|off]</code></h3>
<ul>
<li>功能
设置客户机启动顺序</li>
</ul>
<blockquote>
<p>在 qemu 模拟的 x86 平台中，用&quot;a&quot;、&ldquo;b&quot;分别表示第一和第二软驱，用&quot;c&quot;表示第一个硬盘，用&quot;d&quot;表示 CD-ROM 光驱，用&quot;n&quot;表示从网络启动。
默认从硬盘启动。</p>
</blockquote>
<ul>
<li>
<p>子参数</p>
<ul>
<li><code>[order=drives]</code>
设置启动顺序。</li>
<li><code>[,once=drives]</code>
只设置下一次启动的顺序，再重启后无效。</li>
<li><code>[,menu=on|off]</code>
只要固件/BIOS 支持，就可以启用交互式引导菜单/提示。默认为非交互式引导。</li>
<li><code>[,splash=sp_name]</code>
如果固件/BIOS 支持选项 splash=sp_name 和 menu=on，则可以将启动画面传递给 bios，使用户能够将其显示为徽标。目前 Seabios for X86 系统支持它。限制：启动文件可以是 24 BPP 格式（真彩色）的 jpeg 文件或 BMP 文件。分辨率应该是 SVGA 模式支持的，推荐 320x240、640x480、800x640。</li>
<li><code>[,splash-time=sp_time]</code></li>
<li><code>[,reboot-timeout=rb_timeout]</code>
引导失败时，客户机将暂停 <code>rb_timeout</code> 毫秒，然后重新启动。如果 <code>rb_timeout</code> 为 &lsquo;-1&rsquo;，客户机不会重启，qemu 默认将 &lsquo;-1&rsquo; 传递给 bios。目前 Seabios for X86 系统支持它。</li>
<li><code>[,strict=on|off]</code>
只要固件/BIOS 支持，就通过严格启动。这仅在 bootindex 选项更改引导优先级时有效。默认为非严格引导。</li>
</ul>
</li>
<li>
<p>调用实例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># 尝试先从网络启动，然后从硬盘启动
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -boot order=nc
</span></span><span class="line"><span class="cl"># 先从光驱启动，重启后切换回默认顺序
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -boot once=d
</span></span><span class="line"><span class="cl"># 5 秒钟的启动画面。
</span></span><span class="line"><span class="cl">qemu-system-x86_64 -boot menu=on,splash=/root/boot.bmp,splash-time=5000
</span></span></code></pre></div></li>
</ul>
<h3 id="-m-sizemegsslotsnmaxmemsize"><code>-m [size=]megs[,slots=n,maxmem=size]</code></h3>
<ul>
<li>
<p>功能
将客户机内存设置为 <code>megs</code> M字节。默认值为 <code>128 MiB</code>。或者，也可以使用“M”或“G”的后缀。齐。</p>
</li>
<li>
<p>子参数</p>
<ul>
<li><code>[size=]megs</code>
将客户机内存设置为 <code>megs</code> M字节</li>
<li><code>[,slots=n,maxmem=size]</code>
可用于设置可热插拔内存插槽的数量和最大内存数量。<code>maxmem</code> 必须与页面大小对</li>
</ul>
</li>
<li>
<p>调用实例
以下命令行将客户机启动 RAM 大小设置为 1GB，创建 3 个插槽以热插拔额外内存，并将客户机可以达到的最大内存设置为 4GB：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-system-x86_64 -m 1G,slots=3,maxmem=4G
</span></span></code></pre></div><p>如果未指定 <code>slot</code> 和 <code>maxmem</code>，则不会启用内存热插拔，并且客户机内存永远不会增加。</p>
</li>
</ul>
<h3 id="-mem-path-path"><code>-mem-path path</code></h3>
<ul>
<li>功能
使用huge page。对于内存访问密集型的应用，使用<code>huge page</code>是可以比较明显地提高客户机性能。 使用<code>huge page</code>的内存不能被换出（swap out），也不能使用<code>ballooning</code>方式自动增长。</li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="-mem-prealloc"><code>-mem-prealloc</code></h3>
<ul>
<li>功能
使宿主机在客户机启动时就全部分配好客户机的内存</li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="-k-language"><code>-k language</code></h3>
<ul>
<li>
<p>功能
设置键盘布局语言，默认为<code>en-us</code></p>
</li>
<li>
<p>子参数
可用布局：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ar  de-ch  es  fo     fr-ca  hu  ja  mk     no  pt-br  sv
</span></span><span class="line"><span class="cl">da  en-gb  et  fr     fr-ch  is  lt  nl     pl  ru     th
</span></span><span class="line"><span class="cl">de  en-us  fi  fr-be  hr     it  lv  nl-be  pt  sl     tr
</span></span></code></pre></div></li>
<li>
<p>调用实例</p>
</li>
</ul>
<h2 id="块设备参数-block-device-options">块设备参数 Block device options</h2>
<h3 id="fda-file"><code>fda file</code></h3>
<ul>
<li>功能
为客户机指定软盘设备，指定客户机的第一个软盘设备，在客户机中显示为<code>/dev/fd0</code></li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="fdb-file"><code>fdb file</code></h3>
<ul>
<li>功能
为客户机指定软盘设备，指定客户机的第一个软盘设备，在客户机中显示为<code>/dev/fd1</code></li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="hda-file"><code>hda file</code></h3>
<h3 id="hdb-file"><code>hdb file</code></h3>
<h3 id="hdc-file"><code>hdc file</code></h3>
<h3 id="hdd-file"><code>hdd file</code></h3>
<ul>
<li>功能
为客户机指定块存储设备，指定客户机种的第一个 IDE 设备</li>
<li>子参数
若客户机使用<code>PIIX_IDE</code>驱动，显示为<code>/dev/hda</code>设备；
若客户机使用<code>ata_piix</code>驱动，显示为<code>/dev/sda</code>设备。
若没有使用<code>-hdx</code>的参数，则默认使用<code>-hda</code>参数；
可以将宿主机的一块硬盘作为<code>-hda</code>的参数使用；
若文件名包含逗号，应使用两个连续的逗号进行转义。</li>
<li>调用实例</li>
</ul>
<h3 id="-cdrom-file"><code>-cdrom file</code></h3>
<ul>
<li>功能
为客户机指定光盘 CD-ROM。可以将宿主机的光驱<code>/dev/cdrom</code>设备作为<code>-cdrom</code>参数使用。<code>-cdrom</code>参数不能与<code>-hdc</code>参数同时使用，因为<code>-cdrom</code>就是客户机里的第三个 IDE 设备</li>
<li>子参数</li>
<li>调用实例</li>
</ul>
<h3 id="-blockdev-optionoptionoption"><code>-blockdev option[,option[,option[,...]]]</code></h3>
<ul>
<li>
<p>功能</p>
</li>
<li>
<p>子参数</p>
</li>
<li>
<p>调用实例</p>
</li>
</ul>
<h3 id="-drive-optionoptionoption"><code>-drive option[,option[,option[,...]]]</code></h3>
<ul>
<li>功能
定义一个存储驱动器</li>
<li>子参数
<ul>
<li><code>[file=file]</code>
加载<code>file</code>镜像文件到客户机的驱动器中。</li>
<li><code>[,if=type]</code>
指定驱动器使用的接口类型：可用的类类型有：<code>ide</code>、<code>scsi</code>、<code>virtio</code>、<code>sd</code>、<code>floopy</code>、<code>pflash</code>等。</li>
<li><code>[,bus=n]</code>
设置驱动器在客户机中的总线编号。</li>
<li><code>[,unit=m]</code>
设置驱动器在客户机中的单元编号。</li>
<li><code>[,media=d]</code>
设置驱动器中媒介的类型，值为 disk 或 cdrom。</li>
<li><code>[,index=i]</code>
设置在通一种接口的驱动器中的索引编号。</li>
<li><code>[,snapshot=on|off]</code>
当值为 on 时，qemu 不会将磁盘数据的更改写回到镜像文件中，而是写到临时文件中，可以在 qemu moinitor 中使用 commit 命令强制将磁盘数据保存回镜像文件中。</li>
<li><code>[,cache=writethrough|writeback|none|directsync|unsafe]</code>
设置宿主机对块设备数据访问的 cache 模式。，
<code>writethrough</code>（直写模式）：调用 write 写入数据的同时将数据写入磁盘缓存和后端块设备中。
<code>writeback</code>（回写模式）：调用 write 写入数据时只将数据写入到磁盘缓存中，当数据被换出缓存时才写入到后端存储中。优点写入数据块，缺点系统掉电数据无法恢复。</li>
<li><code>[,aio=threads|native]</code>
选择异步 IO 的方式</li>
<li><code>threads</code>
为 aio 参数的默认值，让一个线程池去处理异步 IO；</li>
<li><code>native</code>
只适用于 cache=none 的情况，使用的是 Linux 原生的 AIO。</li>
<li><code>[,format=f]</code>
指定使用的磁盘格式，默认是 QEMU 自动检测磁盘格式的。</li>
<li><code>[,serial=s]</code>
指定分配给设备的序列号。</li>
<li><code>[,addr=A]</code>
分配给驱动器控制器的 PCI 地址，只在使用 virtio 接口时适用。</li>
<li><code>[,id=name]</code>
设置驱动器的 ID，可以在 QEMU monitor 中用 info block 看到。
<code>[,readonly=on|off]</code>
设置驱动器是否只读。</li>
</ul>
</li>
<li>调用实例</li>
</ul>
<h2 id="usb-参数-usb-convenience-options">USB 参数 USB convenience options</h2>
<h2 id="显示参数-display-options">显示参数 Display options</h2>
<h2 id="仅限-i386-架构的参数-i386-target-only">仅限 i386 架构的参数 i386 target only</h2>
<h2 id="网络参数-network-options">网络参数 Network options</h2>
<h2 id="字符设备参数-character-device-options">字符设备参数 Character device options</h2>
<h2 id="tpm-设备-tpm-device-options">TPM 设备 TPM device options</h2>
<h2 id="指定启动指引-linuxmultiboot-boot-specific">指定启动指引 Linux/Multiboot boot specific</h2>
<p>当使用该调用参数时，你可以使用给定的 Linux 或者多重引导内核，而不需要安装内核到一个光盘中。这样可以更方便地测试不同内核。</p>
<ul>
<li>
<p><code>-kernel bzImage</code></p>
<ul>
<li>功能
用 bzImage 作为内核镜像，也可以使用其他启动格式。</li>
</ul>
</li>
<li>
<p><code>-append cmdline</code></p>
<ul>
<li>功能
用<code>cmd</code>命令行，作为内核的命令行</li>
</ul>
</li>
<li>
<p><code>-initrd file</code></p>
<ul>
<li>功能
用文件作为初始化 ram</li>
</ul>
</li>
<li>
<p><code>-initrd &quot;file1 arg=foo,file2&quot;</code></p>
<ul>
<li>功能
此语法仅适用于多重引导
使用 <code>file1</code> 和 <code>file2</code> 作为模块并将 <code>arg=foo</code> 作为参数传递给第一个模块</li>
</ul>
</li>
<li>
<p><code>-dtb file</code></p>
<ul>
<li>功能
将文件用作设备树二进制 (dtb) 映像并在启动时将其传递给内核</li>
</ul>
</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 初识</title>
      <link>https://lifeislife.cn/posts/qemu%E5%88%9D%E8%AF%86/</link>
      <pubDate>Fri, 23 Jul 2021 11:54:49 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E5%88%9D%E8%AF%86/</guid>
      <description>&lt;h2 id=&#34;简介&#34;&gt;简介&lt;/h2&gt;
&lt;p&gt;QEMU 是一款开源的模拟器及虚拟机监管器 (Virtual Machine Monitor, VMM)。QEMU 主要提供两种功能给用户使用。一是作为用户态模拟器，利用动态代码翻译机制来执行不同于主机架构的代码。二是作为虚拟机监管器，模拟全系统，利用其他 VMM(Xen, KVM, etc) 来使用硬件提供的虚拟化支持，创建接近于主机性能的虚拟机。&lt;/p&gt;
&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;
&lt;h3 id=&#34;使用包管理安装&#34;&gt;使用包管理安装&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get install qemu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;使用源码安装&#34;&gt;使用源码安装&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;wget&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;wget&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;https&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;//&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;download&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;org&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;6.1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rc3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xz&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;tar&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;xvJf&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;6.1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rc3&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tar&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;xz&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;cd&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;qemu&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;6.1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rc3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;安装相关库&#34;&gt;安装相关库&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get install libglib2.0-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get install ninja-build
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt install g++
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt install libpixman-1-dev
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt install libsdl2-dev -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;配置&#34;&gt;配置&lt;/h3&gt;
&lt;p&gt;通过&lt;code&gt;./configure --help&lt;/code&gt; 的查看编译时的选项，&lt;code&gt;--target-list&lt;/code&gt;选项为可选的模拟器，默认全选。
&lt;code&gt;--target-list&lt;/code&gt; 中的 &lt;code&gt;xxx-soft&lt;/code&gt; 和 &lt;code&gt;xxx-linux-user&lt;/code&gt; 分别指系统模拟器和应用程序模拟器，生成的二进制文件名字为&lt;code&gt;qemu-system-xxx&lt;/code&gt;和 &lt;code&gt;qemu-xxx&lt;/code&gt;
本文使用如下配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;./configure --prefix=XXX --enable-debug --target-list=riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu --enable-kvm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# --prefix 选项设置qemu的安装位置绝对路径，之后若要卸载删除qemu只要删除该文件夹即可，--enable-kvm开启kvm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# config完，可以在指定的qemu安装文件夹下面找到config-host.mak文件，
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;# 该文件记录着qemu配置的选项，可以和自己设置的进行对比，确保配置和自己已知
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接着进行编译&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make -j8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;直接&lt;code&gt;make&lt;/code&gt;会很慢，第一次编译时默认安装说有模拟器，编译了三四个小时。加上&lt;code&gt;-j8&lt;/code&gt;可以进行多线程编译&lt;/p&gt;
&lt;h2 id=&#34;创建与使用&#34;&gt;创建与使用&lt;/h2&gt;
&lt;h3 id=&#34;创建虚拟镜像&#34;&gt;创建虚拟镜像&lt;/h3&gt;
&lt;p&gt;使用虚拟镜像来模拟虚拟机的硬盘，在启动虚拟机之前需要创建一个镜像文件&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# qemu-img create -f qcow2 qmtest.img 10G
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Formatting &amp;#39;qmtest.img&amp;#39;, fmt=qcow2 size=10737418240 encryption=off cluster_size=65536 lazy_refcounts=off 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# ls
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qmtest.img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;-f&lt;/code&gt;选项用于指定镜像的格式，&lt;code&gt;qcow2&lt;/code&gt;格式是 QEMU 最常用的镜像格式，采用写时复制技术来优化性能。&lt;code&gt;qmtest.img&lt;/code&gt;是镜像文件的名字，&lt;code&gt;10G&lt;/code&gt;是镜像文件大小。&lt;/p&gt;
&lt;p&gt;镜像文件创建完成后，可使用&lt;code&gt;qemu-system-x86&lt;/code&gt;来启动&lt;code&gt;x86&lt;/code&gt;架构的虚拟机：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 qmtest.img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;qmtest.img 中还未安装操作系统，所以会提示“No bootable device”的错误。&lt;/p&gt;
&lt;h3 id=&#34;准备操作系统镜像&#34;&gt;准备操作系统镜像&lt;/h3&gt;
&lt;p&gt;下载需要的 Linux 发行版镜像文件，&lt;a href=&#34;https://launchpad.net/ubuntu/+cdmirrors&#34;&gt;https://launchpad.net/ubuntu/+cdmirrors&lt;/a&gt;，找到想要下载的镜像，这里以交通大学的镜像为例
右击链接复制地址：&lt;a href=&#34;https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso&#34;&gt;https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# wget https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;检查-kvm-是否可用&#34;&gt;检查 KVM 是否可用&lt;/h3&gt;
&lt;p&gt;QEMU 使用 KVM 来提升虚拟机性能，如果不启用 KVM 会导致性能损失。要使用 KVM，首先要检查硬件是否有虚拟化支持：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# grep -E &amp;#39;vmx|svm&amp;#39; /proc/cpuinfo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果有输出则表示硬件有虚拟化支持。其次要检查 kvm 模块是否已经加载：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# lsmod | grep kvm
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kvm_intel             142999  0 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;kvm                   444314  1 kvm_intel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果&lt;code&gt;kvm_intel/kvm_amd&lt;/code&gt;、&lt;code&gt;kvm&lt;/code&gt;模块被显示出来，则&lt;code&gt;kvm&lt;/code&gt;模块已经加载。最后要确保 qemu 在编译的时候使能了&lt;code&gt;KVM&lt;/code&gt;，即在执行&lt;code&gt;configure&lt;/code&gt;脚本的时候加入了&lt;code&gt;–enable-kvm&lt;/code&gt;选项。&lt;/p&gt;
&lt;h3 id=&#34;启动虚拟机安装操作系统&#34;&gt;启动虚拟机安装操作系统&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# qemu-system-x86_64 -m 2048 -enable-kvm qmtest.img -cdrom ./Fedora-Live-Desktop-x86_64-20-1.iso
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;-m&lt;/code&gt;指定虚拟机内存大小，默认单位是 MB，&lt;code&gt;-enable-kvm&lt;/code&gt;使用 KVM 进行加速，&lt;code&gt;-cdrom&lt;/code&gt;添加 fedora 的安装镜像。可在弹出的窗口中操作虚拟机，安装操作系统，安装完成后重起虚拟机便会从硬盘 (qmtest.img) 启动。之后再启动虚拟机只需要执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/#  qemu-system-x86_64 -m 2048 -enable-kvm qmtest.img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;退出-qemu&#34;&gt;退出 qemu&lt;/h2&gt;
&lt;p&gt;在运行 qemu 后，关闭图形界面但是终端仍然是处于 qemu 环境中，可以直接关闭终端退出。如果不想关闭终端，可以另外打开一个终端 kill 进程&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;killall qemu-system-riscv32
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果记不清全称，可以输入大概名称回车后会列出相关的进程&lt;/p&gt;
</description>
      <content:encoded><![CDATA[<h2 id="简介">简介</h2>
<p>QEMU 是一款开源的模拟器及虚拟机监管器 (Virtual Machine Monitor, VMM)。QEMU 主要提供两种功能给用户使用。一是作为用户态模拟器，利用动态代码翻译机制来执行不同于主机架构的代码。二是作为虚拟机监管器，模拟全系统，利用其他 VMM(Xen, KVM, etc) 来使用硬件提供的虚拟化支持，创建接近于主机性能的虚拟机。</p>
<h2 id="安装">安装</h2>
<h3 id="使用包管理安装">使用包管理安装</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sudo apt-get install qemu
</span></span></code></pre></div><h3 id="使用源码安装">使用源码安装</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">wget</span> <span class="n">wget</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">download</span><span class="o">.</span><span class="n">qemu</span><span class="o">.</span><span class="n">org</span><span class="o">/</span><span class="n">qemu</span><span class="o">-</span><span class="mf">6.1</span><span class="o">.</span><span class="mi">0</span><span class="o">-</span><span class="n">rc3</span><span class="o">.</span><span class="n">tar</span><span class="o">.</span><span class="n">xz</span>
</span></span><span class="line"><span class="cl"><span class="n">tar</span> <span class="n">xvJf</span> <span class="n">qemu</span><span class="o">-</span><span class="mf">6.1</span><span class="o">.</span><span class="mi">0</span><span class="o">-</span><span class="n">rc3</span><span class="o">.</span><span class="n">tar</span><span class="o">.</span><span class="n">xz</span>
</span></span><span class="line"><span class="cl"><span class="n">cd</span> <span class="n">qemu</span><span class="o">-</span><span class="mf">6.1</span><span class="o">.</span><span class="mi">0</span><span class="o">-</span><span class="n">rc3</span>
</span></span></code></pre></div><h3 id="安装相关库">安装相关库</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">apt-get install libglib2.0-dev
</span></span><span class="line"><span class="cl">apt-get install ninja-build
</span></span><span class="line"><span class="cl">apt install g++
</span></span><span class="line"><span class="cl">apt install libpixman-1-dev
</span></span><span class="line"><span class="cl">apt install libsdl2-dev -y
</span></span></code></pre></div><h3 id="配置">配置</h3>
<p>通过<code>./configure --help</code> 的查看编译时的选项，<code>--target-list</code>选项为可选的模拟器，默认全选。
<code>--target-list</code> 中的 <code>xxx-soft</code> 和 <code>xxx-linux-user</code> 分别指系统模拟器和应用程序模拟器，生成的二进制文件名字为<code>qemu-system-xxx</code>和 <code>qemu-xxx</code>
本文使用如下配置：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">./configure --prefix=XXX --enable-debug --target-list=riscv32-softmmu,riscv32-linux-user,riscv64-linux-user,riscv64-softmmu --enable-kvm
</span></span><span class="line"><span class="cl"># --prefix 选项设置qemu的安装位置绝对路径，之后若要卸载删除qemu只要删除该文件夹即可，--enable-kvm开启kvm
</span></span><span class="line"><span class="cl"># config完，可以在指定的qemu安装文件夹下面找到config-host.mak文件，
</span></span><span class="line"><span class="cl"># 该文件记录着qemu配置的选项，可以和自己设置的进行对比，确保配置和自己已知
</span></span></code></pre></div><p>接着进行编译</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">make -j8
</span></span></code></pre></div><p>直接<code>make</code>会很慢，第一次编译时默认安装说有模拟器，编译了三四个小时。加上<code>-j8</code>可以进行多线程编译</p>
<h2 id="创建与使用">创建与使用</h2>
<h3 id="创建虚拟镜像">创建虚拟镜像</h3>
<p>使用虚拟镜像来模拟虚拟机的硬盘，在启动虚拟机之前需要创建一个镜像文件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# qemu-img create -f qcow2 qmtest.img 10G
</span></span><span class="line"><span class="cl">Formatting &#39;qmtest.img&#39;, fmt=qcow2 size=10737418240 encryption=off cluster_size=65536 lazy_refcounts=off 
</span></span><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# ls
</span></span><span class="line"><span class="cl">qmtest.img
</span></span></code></pre></div><p><code>-f</code>选项用于指定镜像的格式，<code>qcow2</code>格式是 QEMU 最常用的镜像格式，采用写时复制技术来优化性能。<code>qmtest.img</code>是镜像文件的名字，<code>10G</code>是镜像文件大小。</p>
<p>镜像文件创建完成后，可使用<code>qemu-system-x86</code>来启动<code>x86</code>架构的虚拟机：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-system-x86_64 qmtest.img
</span></span></code></pre></div><p>qmtest.img 中还未安装操作系统，所以会提示“No bootable device”的错误。</p>
<h3 id="准备操作系统镜像">准备操作系统镜像</h3>
<p>下载需要的 Linux 发行版镜像文件，<a href="https://launchpad.net/ubuntu/+cdmirrors">https://launchpad.net/ubuntu/+cdmirrors</a>，找到想要下载的镜像，这里以交通大学的镜像为例
右击链接复制地址：<a href="https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso">https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# wget https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso
</span></span></code></pre></div><h3 id="检查-kvm-是否可用">检查 KVM 是否可用</h3>
<p>QEMU 使用 KVM 来提升虚拟机性能，如果不启用 KVM 会导致性能损失。要使用 KVM，首先要检查硬件是否有虚拟化支持：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# grep -E &#39;vmx|svm&#39; /proc/cpuinfo
</span></span></code></pre></div><p>如果有输出则表示硬件有虚拟化支持。其次要检查 kvm 模块是否已经加载：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# lsmod | grep kvm
</span></span><span class="line"><span class="cl">kvm_intel             142999  0 
</span></span><span class="line"><span class="cl">kvm                   444314  1 kvm_intel
</span></span></code></pre></div><p>如果<code>kvm_intel/kvm_amd</code>、<code>kvm</code>模块被显示出来，则<code>kvm</code>模块已经加载。最后要确保 qemu 在编译的时候使能了<code>KVM</code>，即在执行<code>configure</code>脚本的时候加入了<code>–enable-kvm</code>选项。</p>
<h3 id="启动虚拟机安装操作系统">启动虚拟机安装操作系统</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# qemu-system-x86_64 -m 2048 -enable-kvm qmtest.img -cdrom ./Fedora-Live-Desktop-x86_64-20-1.iso
</span></span></code></pre></div><p><code>-m</code>指定虚拟机内存大小，默认单位是 MB，<code>-enable-kvm</code>使用 KVM 进行加速，<code>-cdrom</code>添加 fedora 的安装镜像。可在弹出的窗口中操作虚拟机，安装操作系统，安装完成后重起虚拟机便会从硬盘 (qmtest.img) 启动。之后再启动虚拟机只需要执行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/#  qemu-system-x86_64 -m 2048 -enable-kvm qmtest.img
</span></span></code></pre></div><h2 id="退出-qemu">退出 qemu</h2>
<p>在运行 qemu 后，关闭图形界面但是终端仍然是处于 qemu 环境中，可以直接关闭终端退出。如果不想关闭终端，可以另外打开一个终端 kill 进程</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">killall qemu-system-riscv32
</span></span></code></pre></div><p>如果记不清全称，可以输入大概名称回车后会列出相关的进程</p>
]]></content:encoded>
    </item>
    <item>
      <title>QEMU 学习记录</title>
      <link>https://lifeislife.cn/posts/qemu%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</link>
      <pubDate>Tue, 20 Jul 2021 16:51:34 +0000</pubDate>
      <guid>https://lifeislife.cn/posts/qemu%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</guid>
      <description>&lt;h1 id=&#34;qemu-学习记录&#34;&gt;QEMU 学习记录&lt;/h1&gt;
&lt;h2 id=&#34;什么是-kvm&#34;&gt;什么是 KVM？&lt;/h2&gt;
&lt;p&gt;基于内核的虚拟机 &lt;code&gt;Kernel-based Virtual Machine（KVM）&lt;/code&gt;是一种内建于 Linux 中的开源虚拟化技术。具体而言，&lt;code&gt;KVM&lt;/code&gt; 可帮助用户将 Linux 转变为虚拟机监控程序，使主机计算机能够运行多个隔离的虚拟环境，即虚拟客户机或虚拟机（VM）。&lt;/p&gt;
&lt;h2 id=&#34;什么是-qemu&#34;&gt;什么是 QEMU？&lt;/h2&gt;
&lt;p&gt;Qemu 是一个完整的可以单独运行的软件，它可以用来模拟不同架构的机器，非常灵活和可移植。它主要通过一个特殊的&amp;rsquo;重编译器&amp;rsquo;将为特定处理器编写二进制代码转换为另一种。&lt;/p&gt;
&lt;h2 id=&#34;kvm-与-qemu-的关系&#34;&gt;KVM 与 QEMU 的关系&lt;/h2&gt;
&lt;p&gt;KVM 是 Linux 的一个模块。可以用&lt;code&gt;modprobe&lt;/code&gt;去加载 KVM 模块。加载了模块后，才能进一步通过其他工具创建虚拟机。但仅有 KVM 模块是 远远不够的，因为用户无法直接控制内核模块去作事情：还必须有一个用户空间的工具才行。这个用户空间的工具，开发者选择了已经成型的开源虚拟化软件 QEMU。KVM 使用了 QEMU 的一部分，并稍加改造，就成了可控制 KVM 的用户空间工具了。所以你会看到，官方提供的 KVM 下载有两 大部分三个文件，分别是 KVM 模块、QEMU 工具以及二者的合集。也就是说，你可以只升级 KVM 模块，也可以只升级 QEMU 工具。&lt;/p&gt;
&lt;h2 id=&#34;qemu-用户模式与系统模式&#34;&gt;QEMU 用户模式与系统模式&lt;/h2&gt;
&lt;p&gt;QEMU 属于应用层的仿真程序，它支持两种操作模式：&lt;strong&gt;用户模式&lt;/strong&gt;模拟和&lt;strong&gt;系统模式&lt;/strong&gt;模拟。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;用户模式仿真&lt;/strong&gt; 利用动态代码翻译机制，可以在当前 CPU 上执行被编译为支持其他 CPU 的程序，如可以在 x86 机器上执行一个 ARM 二进制可执行程序。（执行主机 CPU 指令的动态翻译并相应地转换 Linux 系统调用）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系统模式仿真&lt;/strong&gt; 利用其它 VMM(Xen, KVM) 来使用硬件提供的虚拟化支持，创建接近于主机性能的全功能虚拟机，包括处理器和配套的外围设备（磁盘，以太网等）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;用户模式&#34;&gt;用户模式&lt;/h3&gt;
&lt;p&gt;支持的 CPU：x86 (32 and 64 bit), PowerPC (32 and 64 bit), ARM, MIPS (32 bit only), Sparc (32 and 64 bit), Alpha, ColdFire(m68k), CRISv32 和 MicroBlaze
下列操作系统支持 QEMU 的用户模式模拟：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux (referred as qemu-linux-user)&lt;/li&gt;
&lt;li&gt;BSD (referred as qemu-bsd-user)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;调用（&lt;a href=&#34;https://qemu.readthedocs.io/en/latest/user/main.html&#34;&gt;具体参数含义&lt;/a&gt;）&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;用户模式模拟环境下运行速度要比系统模式模拟环境下快，但并不是完美模拟，比如程序读取&lt;code&gt;/proc/cpuinfo&lt;/code&gt;内容时，由主机内核返回，因此返回的信息是描述主机 CPU 的，而不是模拟的 CPU。&lt;/p&gt;
&lt;h3 id=&#34;系统模式&#34;&gt;系统模式&lt;/h3&gt;
&lt;p&gt;首先创建虚拟镜像，模拟硬盘空间：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# qemu-img create -f qcow2 qmtest.img 10G
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Formatting &amp;#39;qmtest.img&amp;#39;, fmt=qcow2 size=10737418240 encryption=off cluster_size=65536 lazy_refcounts=off 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# ls
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qmtest.img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;-f&lt;/code&gt;选项用于指定镜像的格式，&lt;code&gt;qcow2&lt;/code&gt;格式是 QEMU 最常用的镜像格式，采用写时复制技术来优化性能。&lt;code&gt;qmtest.img&lt;/code&gt;是镜像文件的名字，&lt;code&gt;10G&lt;/code&gt;是镜像文件大小。&lt;/p&gt;
&lt;p&gt;镜像文件创建完成后，可使用&lt;code&gt;qemu-system-x86&lt;/code&gt;来启动&lt;code&gt;x86&lt;/code&gt;架构的虚拟机：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;qemu-system-x86_64 qmtest.img
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;qmtest.img 中还未安装操作系统，所以会提示“No bootable device”的错误。&lt;/p&gt;
&lt;p&gt;其次，准备操作系统镜像
下载需要的 Linux 发行版镜像文件，&lt;a href=&#34;https://launchpad.net/ubuntu/+cdmirrors&#34;&gt;https://launchpad.net/ubuntu/+cdmirrors&lt;/a&gt;，找到想要下载的镜像，这里以交通大学的镜像为例
右击链接复制地址：&lt;a href=&#34;https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso&#34;&gt;https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# wget https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后，启动虚拟机安装操作系统&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;root@hanhan:/home/dominic/qemu/# qemu-system-x86_64 -m 2048 -enable-kvm qmtest.img -cdrom ./Fedora-Live-Desktop-x86_64-20-1.iso
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;-m&lt;/code&gt;指定虚拟机内存大小，默认单位是 MB，&lt;code&gt;-enable-kvm&lt;/code&gt;使用 KVM 进行加速，&lt;code&gt;-cdrom&lt;/code&gt;添加 fedora 的安装镜像。&lt;/p&gt;
&lt;p&gt;该模式下，要比用户模式模拟慢得多，因为模拟了目标内核，以及设备输入/输出、中断等。&lt;/p&gt;
&lt;h2 id=&#34;qemu-工作原理&#34;&gt;QEMU 工作原理&lt;/h2&gt;
&lt;p&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&#34;en&#34;&gt;
&lt;head&gt;
    &lt;meta charset=&#34;UTF-8&#34;&gt;
    &lt;meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34;&gt;
    &lt;title&gt;Responsive Image&lt;/title&gt;
    &lt;style&gt;
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    
    &lt;div class=&#34;post-img-view&#34;&gt;
        &lt;a data-fancybox=&#34;gallery&#34; href=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210721140349.png&#34;&gt;
            &lt;img class=&#34;responsive-image&#34; src=&#34;https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210721140349.png&#34; alt=&#34;&#34;  style=&#34;margin: 0 auto;&#34;/&gt;
        &lt;/a&gt;
    &lt;/div&gt;
    

    &lt;script&gt;
        document.addEventListener(&#34;DOMContentLoaded&#34;, function() {
            var images = document.querySelectorAll(&#34;.responsive-image&#34;);
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + &#34;px&#34;;
            });
        });
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
单纯使用 qemu，采用的是完全虚拟化的模式。qemu 向 Guest OS 模拟 CPU，也模拟其他的硬件，GuestOS 认为自己和硬件直接打交道，其实是同 qemu 模拟出来的硬件打交道，qemu 会将这些指令转译给真正的硬件。由于所有的指令都要从 qemu 里面过一手，因而性能就会比较差。&lt;/p&gt;
&lt;p&gt;完全虚拟化是非常慢的，所以要使用硬件辅助虚拟化技术 &lt;code&gt;Intel-VT&lt;/code&gt;，&lt;code&gt;AMD-V&lt;/code&gt;，所以需要 CPU 硬件开启这个标志位，一般在 BIOS 里面设置。当确认开始了标志位之后，通过&lt;code&gt;KVM&lt;/code&gt;，GuestOS 的 CPU 指令不用经过 Qemu 转译，直接运行，大大提高了速度。所以，&lt;code&gt;KVM&lt;/code&gt; 在内核里面需要有一个模块，来设置当前 CPU 是 Guest OS 在用，还是 Host OS 在用。&lt;/p&gt;
&lt;p&gt;可以通过如下命令查看内核模块中是否有 KVM&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lsmod | grep kvm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;KVM 内核模块通过 &lt;code&gt;/dev/kvm&lt;/code&gt; 暴露接口，用户态程序可以通过 &lt;code&gt;ioctl&lt;/code&gt;来访问这个接口。Qemu 将 KVM 整合进来，将有关 CPU 指令的部分交由内核模块来做，就是 &lt;code&gt;qemu-kvm (qemu-system-XXX)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;qemu 和 kvm 整合之后，CPU 的性能问题解决了。另外 Qemu 还会模拟其他的硬件，如网络和硬盘。同样，全虚拟化的方式也会影响这些设备的性能。&lt;/p&gt;
&lt;p&gt;于是，qemu 采取半虚拟化的方式，让 Guest OS 加载特殊的驱动来做这件事情。&lt;/p&gt;
&lt;p&gt;例如，网络需要加载 &lt;code&gt;virtio_net&lt;/code&gt;，存储需要加载 &lt;code&gt;virtio_blk&lt;/code&gt;，Guest 需要安装这些半虚拟化驱动，GuestOS 知道自己是虚拟机，所以数据会直接发送给半虚拟化设备，经过特殊处理（例如排队、缓存、批量处理等性能优化方式），最终发送给真正的硬件。这在一定程度上提高了性能。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Q : 系统模式和用户模式的区别？
系统模式 是 qemu 虚拟出一套完整的硬件环境，包含 CPU，内存，网卡，硬盘，对于虚拟机上运行的 OS 看到的和硬件和真实的是一样的。
用户模式是直接将可执行的文件进行指令翻译，只虚拟出 CPU。
假设有 KVM：host 是 x86，QEMU 虚拟出 x86 的系统模式 运行 Windows 系统。QEMU 会将 Windows 指令直接交给  host CPU 直接运行（这个功能是由 KVM 实现的，相当于直接调用 host CPU），性能损失小。内存，硬盘，网络等外设是由 qemu 虚拟出来的。
假设无 KVM：host 是 x86，QEMU 虚拟出 x86 的系统模式运行 Windows 系统。QEMU 会将 Windows 指令翻译成中间码，中间码再转成   host CPU 指令（这个功能是由 qemu TCG 实现的），性能损失大。内存，硬盘，网洛等外设是由 qemu 虚拟出来的。
假设有 KVM：host 是 x86，QEMU 虚拟出 RISC-V 的系统模式 运行 Linux 系统。QEMU 会将 Linux 指令翻译成中间码，中间码再转成 host CPU 指令（这个功能是由 qemu TCG 实现的），性能损失大。内存，硬盘，网洛等外设是由 qemu 虚拟出来的。
KVM 需要在虚拟机与宿主机架构相同时才生效。
此外，用户模式下调用 IO 硬件会报错。qemu 系统模式下会模拟出所有设备，但是模拟的 IO 设备效率低，所以后来有了半虚拟化。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      <content:encoded><![CDATA[<h1 id="qemu-学习记录">QEMU 学习记录</h1>
<h2 id="什么是-kvm">什么是 KVM？</h2>
<p>基于内核的虚拟机 <code>Kernel-based Virtual Machine（KVM）</code>是一种内建于 Linux 中的开源虚拟化技术。具体而言，<code>KVM</code> 可帮助用户将 Linux 转变为虚拟机监控程序，使主机计算机能够运行多个隔离的虚拟环境，即虚拟客户机或虚拟机（VM）。</p>
<h2 id="什么是-qemu">什么是 QEMU？</h2>
<p>Qemu 是一个完整的可以单独运行的软件，它可以用来模拟不同架构的机器，非常灵活和可移植。它主要通过一个特殊的&rsquo;重编译器&rsquo;将为特定处理器编写二进制代码转换为另一种。</p>
<h2 id="kvm-与-qemu-的关系">KVM 与 QEMU 的关系</h2>
<p>KVM 是 Linux 的一个模块。可以用<code>modprobe</code>去加载 KVM 模块。加载了模块后，才能进一步通过其他工具创建虚拟机。但仅有 KVM 模块是 远远不够的，因为用户无法直接控制内核模块去作事情：还必须有一个用户空间的工具才行。这个用户空间的工具，开发者选择了已经成型的开源虚拟化软件 QEMU。KVM 使用了 QEMU 的一部分，并稍加改造，就成了可控制 KVM 的用户空间工具了。所以你会看到，官方提供的 KVM 下载有两 大部分三个文件，分别是 KVM 模块、QEMU 工具以及二者的合集。也就是说，你可以只升级 KVM 模块，也可以只升级 QEMU 工具。</p>
<h2 id="qemu-用户模式与系统模式">QEMU 用户模式与系统模式</h2>
<p>QEMU 属于应用层的仿真程序，它支持两种操作模式：<strong>用户模式</strong>模拟和<strong>系统模式</strong>模拟。</p>
<ul>
<li><strong>用户模式仿真</strong> 利用动态代码翻译机制，可以在当前 CPU 上执行被编译为支持其他 CPU 的程序，如可以在 x86 机器上执行一个 ARM 二进制可执行程序。（执行主机 CPU 指令的动态翻译并相应地转换 Linux 系统调用）。</li>
<li><strong>系统模式仿真</strong> 利用其它 VMM(Xen, KVM) 来使用硬件提供的虚拟化支持，创建接近于主机性能的全功能虚拟机，包括处理器和配套的外围设备（磁盘，以太网等）。</li>
</ul>
<h3 id="用户模式">用户模式</h3>
<p>支持的 CPU：x86 (32 and 64 bit), PowerPC (32 and 64 bit), ARM, MIPS (32 bit only), Sparc (32 and 64 bit), Alpha, ColdFire(m68k), CRISv32 和 MicroBlaze
下列操作系统支持 QEMU 的用户模式模拟：</p>
<ul>
<li>Linux (referred as qemu-linux-user)</li>
<li>BSD (referred as qemu-bsd-user)</li>
</ul>
<p>调用（<a href="https://qemu.readthedocs.io/en/latest/user/main.html">具体参数含义</a>）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...]
</span></span></code></pre></div><p>用户模式模拟环境下运行速度要比系统模式模拟环境下快，但并不是完美模拟，比如程序读取<code>/proc/cpuinfo</code>内容时，由主机内核返回，因此返回的信息是描述主机 CPU 的，而不是模拟的 CPU。</p>
<h3 id="系统模式">系统模式</h3>
<p>首先创建虚拟镜像，模拟硬盘空间：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# qemu-img create -f qcow2 qmtest.img 10G
</span></span><span class="line"><span class="cl">Formatting &#39;qmtest.img&#39;, fmt=qcow2 size=10737418240 encryption=off cluster_size=65536 lazy_refcounts=off 
</span></span><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# ls
</span></span><span class="line"><span class="cl">qmtest.img
</span></span></code></pre></div><p><code>-f</code>选项用于指定镜像的格式，<code>qcow2</code>格式是 QEMU 最常用的镜像格式，采用写时复制技术来优化性能。<code>qmtest.img</code>是镜像文件的名字，<code>10G</code>是镜像文件大小。</p>
<p>镜像文件创建完成后，可使用<code>qemu-system-x86</code>来启动<code>x86</code>架构的虚拟机：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">qemu-system-x86_64 qmtest.img
</span></span></code></pre></div><p>qmtest.img 中还未安装操作系统，所以会提示“No bootable device”的错误。</p>
<p>其次，准备操作系统镜像
下载需要的 Linux 发行版镜像文件，<a href="https://launchpad.net/ubuntu/+cdmirrors">https://launchpad.net/ubuntu/+cdmirrors</a>，找到想要下载的镜像，这里以交通大学的镜像为例
右击链接复制地址：<a href="https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso">https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# wget https://ftp.sjtu.edu.cn/ubuntu-cd/20.10/ubuntu-20.10-live-server-amd64.iso
</span></span></code></pre></div><p>最后，启动虚拟机安装操作系统</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root@hanhan:/home/dominic/qemu/# qemu-system-x86_64 -m 2048 -enable-kvm qmtest.img -cdrom ./Fedora-Live-Desktop-x86_64-20-1.iso
</span></span></code></pre></div><p><code>-m</code>指定虚拟机内存大小，默认单位是 MB，<code>-enable-kvm</code>使用 KVM 进行加速，<code>-cdrom</code>添加 fedora 的安装镜像。</p>
<p>该模式下，要比用户模式模拟慢得多，因为模拟了目标内核，以及设备输入/输出、中断等。</p>
<h2 id="qemu-工作原理">QEMU 工作原理</h2>
<p>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Responsive Image</title>
    <style>
        .post-img-view {
            text-align: center;
        }
        .responsive-image {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    
    <div class="post-img-view">
        <a data-fancybox="gallery" href="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210721140349.png">
            <img class="responsive-image" src="https://picbed-1311007548.cos.ap-shanghai.myqcloud.com/markdown_picbed/img/20210721140349.png" alt=""  style="margin: 0 auto;"/>
        </a>
    </div>
    

    <script>
        document.addEventListener("DOMContentLoaded", function() {
            var images = document.querySelectorAll(".responsive-image");
            var maxHeight = window.innerHeight / 3;
            images.forEach(function(image) {
                image.style.maxHeight = maxHeight + "px";
            });
        });
    </script>
</body>
</html>
单纯使用 qemu，采用的是完全虚拟化的模式。qemu 向 Guest OS 模拟 CPU，也模拟其他的硬件，GuestOS 认为自己和硬件直接打交道，其实是同 qemu 模拟出来的硬件打交道，qemu 会将这些指令转译给真正的硬件。由于所有的指令都要从 qemu 里面过一手，因而性能就会比较差。</p>
<p>完全虚拟化是非常慢的，所以要使用硬件辅助虚拟化技术 <code>Intel-VT</code>，<code>AMD-V</code>，所以需要 CPU 硬件开启这个标志位，一般在 BIOS 里面设置。当确认开始了标志位之后，通过<code>KVM</code>，GuestOS 的 CPU 指令不用经过 Qemu 转译，直接运行，大大提高了速度。所以，<code>KVM</code> 在内核里面需要有一个模块，来设置当前 CPU 是 Guest OS 在用，还是 Host OS 在用。</p>
<p>可以通过如下命令查看内核模块中是否有 KVM</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">lsmod | grep kvm
</span></span></code></pre></div><p>KVM 内核模块通过 <code>/dev/kvm</code> 暴露接口，用户态程序可以通过 <code>ioctl</code>来访问这个接口。Qemu 将 KVM 整合进来，将有关 CPU 指令的部分交由内核模块来做，就是 <code>qemu-kvm (qemu-system-XXX)</code>。</p>
<p>qemu 和 kvm 整合之后，CPU 的性能问题解决了。另外 Qemu 还会模拟其他的硬件，如网络和硬盘。同样，全虚拟化的方式也会影响这些设备的性能。</p>
<p>于是，qemu 采取半虚拟化的方式，让 Guest OS 加载特殊的驱动来做这件事情。</p>
<p>例如，网络需要加载 <code>virtio_net</code>，存储需要加载 <code>virtio_blk</code>，Guest 需要安装这些半虚拟化驱动，GuestOS 知道自己是虚拟机，所以数据会直接发送给半虚拟化设备，经过特殊处理（例如排队、缓存、批量处理等性能优化方式），最终发送给真正的硬件。这在一定程度上提高了性能。</p>
<blockquote>
<p>Q : 系统模式和用户模式的区别？
系统模式 是 qemu 虚拟出一套完整的硬件环境，包含 CPU，内存，网卡，硬盘，对于虚拟机上运行的 OS 看到的和硬件和真实的是一样的。
用户模式是直接将可执行的文件进行指令翻译，只虚拟出 CPU。
假设有 KVM：host 是 x86，QEMU 虚拟出 x86 的系统模式 运行 Windows 系统。QEMU 会将 Windows 指令直接交给  host CPU 直接运行（这个功能是由 KVM 实现的，相当于直接调用 host CPU），性能损失小。内存，硬盘，网络等外设是由 qemu 虚拟出来的。
假设无 KVM：host 是 x86，QEMU 虚拟出 x86 的系统模式运行 Windows 系统。QEMU 会将 Windows 指令翻译成中间码，中间码再转成   host CPU 指令（这个功能是由 qemu TCG 实现的），性能损失大。内存，硬盘，网洛等外设是由 qemu 虚拟出来的。
假设有 KVM：host 是 x86，QEMU 虚拟出 RISC-V 的系统模式 运行 Linux 系统。QEMU 会将 Linux 指令翻译成中间码，中间码再转成 host CPU 指令（这个功能是由 qemu TCG 实现的），性能损失大。内存，硬盘，网洛等外设是由 qemu 虚拟出来的。
KVM 需要在虚拟机与宿主机架构相同时才生效。
此外，用户模式下调用 IO 硬件会报错。qemu 系统模式下会模拟出所有设备，但是模拟的 IO 设备效率低，所以后来有了半虚拟化。</p>
</blockquote>
]]></content:encoded>
    </item>
  </channel>
</rss>
