<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>RISC-V - 标签 - huxiang.bond</title><link>https://huxiang.bond/tags/risc-v/</link><description>RISC-V - 标签 - huxiang.bond</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><managingEditor>admin@huxiang.bond (huxiang)</managingEditor><webMaster>admin@huxiang.bond (huxiang)</webMaster><copyright>huxiang.bond</copyright><lastBuildDate>Fri, 30 Oct 2026 12:00:00 +0800</lastBuildDate><atom:link href="https://huxiang.bond/tags/risc-v/" rel="self" type="application/rss+xml"/><item><title>openEuler RISC-V 进阶实操：交叉编译、RVV 向量验证与自编译内核的踩坑实录</title><link>https://huxiang.bond/posts/openeuler-riscv-advanced/</link><pubDate>Fri, 30 Oct 2026 12:00:00 +0800</pubDate><author>admin@huxiang.bond (huxiang)</author><guid>https://huxiang.bond/posts/openeuler-riscv-advanced/</guid><description><![CDATA[<div class="featured-image">
                <img src="/welcome/card_digital.jpg" referrerpolicy="no-referrer">
            </div><blockquote>
<p>上一篇介绍了怎么在 QEMU 里跑起 openEuler RISC-V 虚拟机，并在 VM 里原生编译了第一个 hello world。但真正开始做 RISC-V 软件移植后你会发现：<strong>在 QEMU 模拟的 VM 里编译大软件实在太慢了</strong>（慢 10~100 倍），而且 openEuler 25.03 的发行版内核默认不开放向量扩展，跑向量程序直接 SIGILL。这篇文章记录我从&quot;交叉编译 → qemu-user 验证 → RVV 向量踩坑 → 自编译内核&quot;的完整进阶过程，每一步都有实测命令和踩坑记录，希望能帮你少走弯路。</p>
</blockquote>
<p>先说结论：这一套流程走下来，最大的收获不是学会了某条命令，而是建立了一条**&ldquo;宿主交叉编译 + qemu-user 先行验证 + VM 只跑产物&rdquo;**的标准化工作流。它让我在做软件包移植验证时，把&quot;改一次代码 → 跑一次验证&quot;的周期从小时级压缩到分钟级，而且绝大多数正确性问题根本不需要碰真机就能暴露出来。</p>
<h2 id="前置环境说明">前置环境说明</h2>
<p>我的环境（与入门篇相同）：</p>
<ul>
<li><strong>宿主</strong>：WSL2 Ubuntu（x86_64），Windows 侧有 Clash 代理</li>
<li><strong>目标机</strong>：openEuler 25.03 riscv64 虚拟机（QEMU），SSH 端口 12055，用户名 <code>linght153</code></li>
<li><strong>目标机 ISA</strong>：<code>rv64gc_zba_zbb_zbs</code>（<strong>没有 v 向量扩展</strong>，这一点后面坑惨了我）</li>
<li><strong>交叉工具链</strong>：<code>gcc-14-riscv64-linux-gnu</code>（GCC 14.3.0），sysroot 在 <code>/usr/riscv64-linux-gnu/</code></li>
</ul>
<p>核心工作流一句话：<strong>编译一律放宿主（WSL），VM 只跑编译产物</strong>。</p>
<hr>
<h2 id="一wsl-交叉编译把编译从-vm-搬回宿主">一、WSL 交叉编译：把&quot;编译&quot;从 VM 搬回宿主</h2>
<h3 id="11-安装交叉工具链">1.1 安装交叉工具链</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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"># WSL (Debian/Ubuntu) 一条命令装齐：编译器 + C++ + binutils + C 库 + 调试器</span>
</span></span><span class="line"><span class="cl">sudo apt install gcc-riscv64-linux-gnu g++-riscv64-linux-gnu <span class="se">\
</span></span></span><span class="line"><span class="cl">  binutils-riscv64-linux-gnu libc6-dev-riscv64-cross gdb-multiarch
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 验证版本和 sysroot</span>
</span></span><span class="line"><span class="cl">riscv64-linux-gnu-gcc --version        <span class="c1"># gcc (Ubuntu 14.x) 14.3.0</span>
</span></span><span class="line"><span class="cl">riscv64-linux-gnu-gcc -print-sysroot   <span class="c1"># /usr/riscv64-linux-gnu</span></span></span></code></pre></div></div>
<blockquote>
<p>⚠️ <strong>坑 1：GCC 15 的 libstdc++ 内嵌向量指令</strong>。我一开始图新装了 GCC 15 工具链，结果链接 C++ 程序时静态库里的 libstdc++ 编译进了向量指令，<code>-march</code> 参数根本覆盖不了——交叉编译出的程序在目标机上直接 SIGILL。<strong>结论：RISC-V 交叉编译老老实实用 GCC 14</strong>，这是当前最稳的版本。</p>
</blockquote>
<p>顺带一提：这套工具链是&quot;全家桶&quot;结构——编译器（gcc）、汇编器（as）、链接器（ld）、C 库（glibc）、二进制工具（readelf/objdump/nm/strip）缺一不可，任何一个环节缺失交叉编译都跑不通。装完后可以用 <code>riscv64-linux-gnu-gcc -v</code> 查看配置参数和 sysroot 路径，用 <code>-print-sysroot</code> 直接输出交叉根目录，后续 qemu-user 的 <code>-L</code> 参数和 CMake 的 <code>CMAKE_SYSROOT</code> 都指向它。</p>
<h3 id="12-第一个交叉编译-march-与--mabi-选型">1.2 第一个交叉编译：-march 与 -mabi 选型</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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"># hello.c 与入门篇一样，但这次用交叉编译器，目标平台 riscv64</span>
</span></span><span class="line"><span class="cl">riscv64-linux-gnu-gcc -march<span class="o">=</span>rv64gc -mabi<span class="o">=</span>lp64d -O2 hello.c -o hello
</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">file hello
</span></span><span class="line"><span class="cl"><span class="c1"># 预期输出：ELF 64-bit LSB executable, UCB RISC-V, RVC, double-float ABI</span></span></span></code></pre></div></div>
<p><strong>-march 和 -mabi 是交叉编译最容易踩坑的一对参数</strong>，我的理解如下：</p>
<table>
	<thead>
			<tr>
					<th>参数</th>
					<th>含义</th>
					<th>常见值</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>-march</code></td>
					<td>目标 CPU 支持哪些指令扩展</td>
					<td><code>rv64gc</code>（保守通用）、<code>rv64gc_zba_zbb_zbs</code>（RVA22 级）、<code>rv64gcv</code>（带向量）</td>
			</tr>
			<tr>
					<td><code>-mabi</code></td>
					<td>调用约定（参数怎么传、浮点怎么处理）</td>
					<td><code>lp64d</code>（64 位长整型+硬浮点，最常用）、<code>lp64</code>（无硬浮点）、<code>ilp32d</code>（32 位嵌入式）</td>
			</tr>
	</tbody>
</table>
<p><strong>选型铁律（血泪总结）</strong>：</p>
<ol>
<li><code>-march</code> <strong>必须 ≤ 目标机真实 ISA</strong>（以目标机 <code>/proc/cpuinfo</code> 为准），写了目标没有的扩展 → 运行时报 <code>Illegal instruction (core dumped)</code>；</li>
<li><code>-mabi=lp64d</code> 需要目标有 D 扩展（双精度浮点）；</li>
<li>同一套 ABI 的二进制才能互相链接：<code>-mabi=lp64d</code> 编译的库只能被同样 ABI 的程序链接，报 <code>relocation truncated</code> 时先查 ABI 是否一致。</li>
</ol>
<p>另外记住一个容易混淆的点：<code>-march</code> 决定&quot;我能生成哪些指令&quot;，<code>-mabi</code> 决定&quot;调用约定和二进制兼容性&quot;。前者错了运行时崩，后者错了链接时就崩——两者作用阶段不同，排查方向也完全不同。我踩过最冤枉的一次，就是忘了目标机 ISA 里没有 <code>zbb</code>，编出来的程序在 VM 上一跑就 <code>Illegal instruction</code>，当时还以为是编译器坏了。</p>
<blockquote>
<p>⚠️ <strong>坑 2：GLIBCXX_3.4.32 not found</strong>。第一次交叉编译 C++ 程序，拷到 VM 里一跑就报这个错。原因：GCC 14 的 libstdc++ 需要 <code>GLIBCXX_3.4.32</code>，而 openEuler 25.03 只有 3.4.30。<strong>解法：链接时加 <code>-static-libstdc++ -static-libgcc</code></strong>（只静态 C++ 运行库，动态 glibc），之后用 <code>readelf -d</code> 检查 NEEDED 只剩 <code>libm/libc/ld-linux</code> 就对了。体积信号：静态版约 8.0M，动态版约 6.1M。</p>
<p>⚠️ <strong>坑 3：链接报 <code>R_RISCV_CALL_PLT ... may bind externally</code></strong>。这是 PIE 下的重定位问题，加 <code>-no-pie -Wl,-z,notext</code> 即可解决。</p>
</blockquote>
<hr>
<h2 id="二qemu-user在-x86-宿主上直接跑-riscv64-程序">二、qemu-user：在 x86 宿主上直接跑 riscv64 程序</h2>
<p>交叉编译完，下一步是在真机/VM 上验证吗？不用——<strong>qemu-user 能在宿主机上直接跑 riscv64 用户态程序</strong>，完全不需要 VM，也不需要真实硬件。</p>
<h3 id="21-qemu-user-与-qemu-system-的区别">2.1 qemu-user 与 qemu-system 的区别</h3>
<ul>
<li><strong>qemu-system</strong>：全系统模拟，模拟整台机器（CPU + 内存 + 外设），可以装发行版、跑内核——入门篇用的就是它；</li>
<li><strong>qemu-user</strong>：用户态模拟，只翻译执行单个 ELF 文件，<strong>不经过目标内核</strong>。速度快、启动秒级，适合跑单元测试和做正确性验证。</li>
</ul>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>qemu-user 相当于：给宿主加了一层&#34;指令翻译器&#34;，把 riscv64 的系统调用转发给宿主内核，
把 riscv64 的普通指令逐条翻译执行。所以程序眼里自己是 riscv64 环境，实际上跑在 x86 上。</code></pre></div>
<p>这也是为什么 qemu-user 能绕过&quot;内核不支持向量&quot;的限制——它根本不加载目标内核，RVV 指令由 QEMU 翻译层直接模拟执行（下文第三节细讲）。对于只需要验证程序逻辑正确性的场景，qemu-user 比起一个完整 VM 轻量太多了。</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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">sudo apt install qemu-user
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 在 WSL 上直接跑刚交叉编译的 riscv64 程序</span>
</span></span><span class="line"><span class="cl">qemu-riscv64 -L /usr/riscv64-linux-gnu ./hello
</span></span><span class="line"><span class="cl"><span class="c1"># 预期输出：Hello, RISC-V!</span></span></span></code></pre></div></div>
<blockquote>
<p>⚠️ <strong>坑 4：<code>-L &lt;sysroot&gt;</code> 不能省</strong>。不加 <code>-L</code> 直接跑会报：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>qemu-riscv64: Could not open &#39;/lib/ld-linux-riscv64-lp64d.so.1&#39;</code></pre></div>
<p>因为动态链接器 <code>ld-linux-riscv64-lp64d.so.1</code> 在交叉 sysroot 里（<code>/usr/riscv64-linux-gnu/lib/</code>），宿主机的 <code>/lib</code> 下根本没有。<code>-L</code> 就是告诉 qemu 去哪个根目录找动态链接器和库。</p>
<p>⚠️ <strong>坑 5：别直接在宿主 shell 里 <code>./hello</code></strong>。riscv64 ELF 在 x86 宿主上跑会报 <code>bash: ./hello: cannot execute binary file</code>，这是预期行为，走 qemu-user 就对了。</p>
</blockquote>
<h3 id="22-通用验证三步走强烈建议固化">2.2 通用验证三步走（强烈建议固化）</h3>
<p>踩了几次&quot;编译时没问题、上 VM 就崩&quot;的坑后，我总结出<strong>任何交叉编译产物交付前的三步体检</strong>，缺一步都可能把有问题的二进制送到目标机上：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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"># ① readelf：链接体检 —— 该依赖谁、不该依赖谁</span>
</span></span><span class="line"><span class="cl">riscv64-linux-gnu-readelf -d app <span class="p">|</span> grep NEEDED
</span></span><span class="line"><span class="cl"><span class="c1"># 预期：只剩 libm / libc / ld-linux；看到 libstdc++.so.6 就是漏了静态链接</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ② objdump：指令体检 —— 声称用了向量，真的编进去了吗</span>
</span></span><span class="line"><span class="cl">riscv64-linux-gnu-objdump -d app <span class="p">|</span> grep -c vsetvli
</span></span><span class="line"><span class="cl"><span class="c1"># 预期：&gt;0。为 0 说明自动向量化没生效或 -march 被覆盖</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ③ qemu-user：运行体检 —— 宿主机直接跑通（不经目标内核）</span>
</span></span><span class="line"><span class="cl">qemu-riscv64 -L /usr/riscv64-linux-gnu ./app <span class="o">&amp;&amp;</span> <span class="nb">echo</span> PASS</span></span></code></pre></div></div>
<p>我把这三步固化成了 <code>~/.bashrc</code> 里的一个函数 <code>riscv64-check</code>，之后每次交叉编译完一条命令完成体检。<strong>三步全绿才叫&quot;可交付&quot;</strong>。</p>
<hr>
<h2 id="三rvv-向量验证从-sigill-到柳暗花明">三、RVV 向量验证：从 SIGILL 到柳暗花明</h2>
<h3 id="31-写一个最小-rvv-向量程序">3.1 写一个最小 RVV 向量程序</h3>
<p>RVV（RISC-V 向量扩展）和 x86 SSE/AVX 最大的不同：<strong>硬件向量长度（VLEN）对编译器未知</strong>，软件必须写成&quot;长度无关&quot;的代码。先理解几个关键词：VLEN 是向量寄存器位宽（常见 128/256 位），ELEN 是单元素最大位宽（默认 64），SEW 是当前操作的元素位宽（e32=32 位元素），LMUL 是寄存器组倍数（m1/m2/m4/m8），而 <code>vsetvli</code> 这条指令负责在运行时设置 SEW/LMUL 并返回本次实际处理的元素数 <code>vl</code>。写向量代码不像写 SSE 那样&quot;假设寄存器是 128 位&quot;——你必须假设任何长度都可能，用运行时查询的结果来控制循环。先看最小示例：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-c">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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">// vec.c —— 查询向量寄存器长度（完整可编译）
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;riscv_vector.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">size_t</span> <span class="n">vlen</span> <span class="o">=</span> <span class="nf">__riscv_vsetvlmax_e8m8</span><span class="p">();</span>   <span class="c1">// e8m8 一次能装的最大字节数
</span></span></span><span class="line"><span class="cl">    <span class="nf">printf</span><span class="p">(</span><span class="s">&#34;VLEN bytes: %zu</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">vlen</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</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></div>
<p>编译（注意 <code>-march</code> 要带 <code>v</code>）：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">riscv64-linux-gnu-gcc -march<span class="o">=</span>rv64gcv -mabi<span class="o">=</span>lp64d -O2 vec.c -o vec
</span></span><span class="line"><span class="cl">riscv64-linux-gnu-objdump -d vec <span class="p">|</span> grep -c vsetvli   <span class="c1"># &gt;0 说明真的编进了向量指令</span></span></span></code></pre></div></div>
<h3 id="32-坑-6vm-里跑向量程序直接-sigill最大拦路虎">3.2 坑 6：VM 里跑向量程序直接 SIGILL（最大拦路虎）</h3>
<p>我在 QEMU 启动参数里特意加了 <code>-cpu rv64,v=true,vlen=256</code>，进 VM 后 <code>grep v /proc/cpuinfo</code> 也确实能看到 <code>v</code> 扩展——<strong>但一跑向量程序，直接崩溃</strong>：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>Illegal instruction (core dumped)   # 退出码 132 = SIGILL</code></pre></div>
<p>排查过程：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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"># ① 确认 CPU 模拟出 v 扩展（确实有）</span>
</span></span><span class="line"><span class="cl">grep -o <span class="s1">&#39;^isa[^,]*&#39;</span> /proc/cpuinfo        <span class="c1"># rv64imafdc_zba_zbb_zbs_v ...</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">zcat /proc/config.gz <span class="p">|</span> grep RISCV_ISA_V  <span class="c1"># CONFIG_RISCV_ISA_V is not set</span></span></span></code></pre></div></div>
<p><strong>根因</strong>：QEMU 模拟出 v 扩展 ≠ 内核允许用户态使用它。openEuler 25.03 发行版内核<strong>显式关闭了 <code>CONFIG_RISCV_ISA_V</code></strong>（尽管上游 Linux 6.6 默认是 y）。内核不开这项 = 不保存/恢复向量寄存器上下文，用户态任何 v 指令直接非法。<strong>换什么 <code>-cpu</code> 都没用，因为问题出在内核，不是 QEMU。</strong></p>
<blockquote>
<p>顺带说一句：Debian/Ubuntu 的 riscv64 内核默认开 RVV，同样的二进制在两边行为不同——<strong>这种&quot;发行版级差异&quot;正是验证工作的价值点</strong>，也是我给社区提交验证报告时发现的。</p>
</blockquote>
<h3 id="33-坑-7qemu-user-的--cpu-rv64vtrue-不生效">3.3 坑 7：qemu-user 的 <code>-cpu rv64,v=true</code> 不生效</h3>
<p>想用 qemu-user 绕开内核限制，结果发现 <code>-cpu rv64,v=true</code> 在 <strong>user 模式</strong>下根本不解析出 v 扩展（该 CPU 名解析不带 v）。<strong>正确姿势是用默认 CPU 或 <code>-cpu max</code></strong>：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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"># ✅ 正确：qemu-user 不经目标内核，RVV 由 QEMU 翻译层执行</span>
</span></span><span class="line"><span class="cl">qemu-riscv64 -L /usr/riscv64-linux-gnu -cpu max ./vec
</span></span><span class="line"><span class="cl"><span class="c1"># 预期输出：VLEN bytes: 32   （256 位向量 = 32 字节）</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 调向量长度：默认 128 位 → 指定 256 位</span>
</span></span><span class="line"><span class="cl">qemu-riscv64 -L /usr/riscv64-linux-gnu -cpu max,vlen<span class="o">=</span><span class="m">256</span> ./vec</span></span></code></pre></div></div>
<p>这就是<strong>绕过内核 RVV 限制的官方推荐路线</strong>：<code>qemu-riscv64 -L &lt;sysroot&gt; &lt;binary&gt;</code> 直接在 x86 宿主机翻译执行 riscv64 指令，根本不经过目标内核，RVV 由 QEMU 翻译层执行。我实测用它跑通了 RVV 向量加法（bad=0 PASS）、甚至完整的 MLAS RVV SGEMM kernel 11/11 单元测试（vlen=128 和 vlen=256 都过，maxdiff ≤ 0.000156）。<strong>结论：发行版缺 RVV 支持时，正确性验证完全不需要 VM/真机。</strong></p>
<h3 id="34-坑-8rvv-循环必须用分段循环stripmining">3.4 坑 8：RVV 循环必须用分段循环（stripmining）</h3>
<p>写向量代码时我还踩了一个隐蔽坑：循环如果不按 vlenmax 分段、而是假设一次装完所有元素，<strong>超过 vlenmax 时数值会静默错乱</strong>（qemu-user 下实测，不报错只给错答案）。RVV 的标准写法是分段循环：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-c">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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">size_t</span> <span class="n">vlmax</span> <span class="o">=</span> <span class="nf">__riscv_vsetvlmax_e32m1</span><span class="p">();</span>   <span class="c1">// 一次最多处理几个 f32
</span></span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kt">size_t</span> <span class="n">vl</span><span class="p">;</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">;</span> <span class="n">n</span> <span class="o">-=</span> <span class="n">vl</span><span class="p">,</span> <span class="n">a</span> <span class="o">+=</span> <span class="n">vl</span><span class="p">,</span> <span class="n">b</span> <span class="o">+=</span> <span class="n">vl</span><span class="p">,</span> <span class="n">c</span> <span class="o">+=</span> <span class="n">vl</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">vl</span> <span class="o">=</span> <span class="nf">__riscv_vsetvl_e32m1</span><span class="p">(</span><span class="n">n</span><span class="p">);</span>           <span class="c1">// 本次处理 min(n, vlenmax) 个
</span></span></span><span class="line"><span class="cl">    <span class="kt">vfloat32m1_t</span> <span class="n">va</span> <span class="o">=</span> <span class="nf">__riscv_vle32_v1</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">vl</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">vfloat32m1_t</span> <span class="n">vb</span> <span class="o">=</span> <span class="nf">__riscv_vle32_v1</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">vl</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">vfloat32m1_t</span> <span class="n">vc</span> <span class="o">=</span> <span class="nf">__riscv_vfadd_vv_f32m1</span><span class="p">(</span><span class="n">va</span><span class="p">,</span> <span class="n">vb</span><span class="p">,</span> <span class="n">vl</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">__riscv_vse32_v1</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">vc</span><span class="p">,</span> <span class="n">vl</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>每个循环迭代用 <code>vsetvl</code> 重新截断本次要处理的元素数，这是 RVV 与固定宽度 SIMD 最本质的思维差异。如果总元素数超过 vlenmax 而你只循环了一次，QEMU 不会报错——它只是默默地把超出部分留在寄存器里没写回内存，你拿到的是错乱的结果。这种&quot;静默出错&quot;比崩溃更可怕，排查起来极费时间。</p>
<hr>
<h2 id="四自编译内核从根上解决向量支持">四、自编译内核：从根上解决向量支持</h2>
<p>qemu-user 能验证正确性，但<strong>真要在 openEuler VM 里跑向量程序，还得自编一个开了 <code>CONFIG_RISCV_ISA_V</code> 的内核</strong>。这也是最完整的进阶闭环。</p>
<h3 id="41-获取-olk-源码">4.1 获取 OLK 源码</h3>
<p>OLK = Open Euler Kernel，openEuler 官方内核仓，全架构同一仓库多分支：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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"># 浅克隆 OLK-6.6（约 200+MB，--depth 1 省流量）</span>
</span></span><span class="line"><span class="cl">git clone --depth <span class="m">1</span> -b OLK-6.6 https://gitee.com/openeuler/kernel.git ~/riscv/oe/olk-6.6
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 确认版本（Makefile 前 5 行）</span>
</span></span><span class="line"><span class="cl">head -5 ~/riscv/oe/olk-6.6/Makefile
</span></span><span class="line"><span class="cl"><span class="c1"># VERSION = 6 / PATCHLEVEL = 6 / SUBLEVEL = 0 / EXTRAVERSION = -72.6.0.56.oe2503</span></span></span></code></pre></div></div>
<h3 id="42-config-配置三条路--一个大坑">4.2 config 配置：三条路 + 一个大坑</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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">export</span> <span class="nv">ARCH</span><span class="o">=</span>riscv
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CROSS_COMPILE</span><span class="o">=</span>riscv64-linux-gnu-
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ~/riscv/oe/olk-6.6
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 路线 A（推荐）：用 VM 现役内核的 config，保证与官方行为一致</span>
</span></span><span class="line"><span class="cl"><span class="c1"># VM 里执行：zcat /proc/config.gz &gt; /tmp/oe.config</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 然后 scp 回 WSL</span>
</span></span><span class="line"><span class="cl">cp ~/riscv/oe/current.config build/.config
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 路线 B：源码自带 defconfig（实测 7396 行）</span>
</span></span><span class="line"><span class="cl">make <span class="nv">O</span><span class="o">=</span>build openeuler_defconfig</span></span></code></pre></div></div>
<blockquote>
<p>⚠️ <strong>坑 9：defconfig ≠ 发行版实际 config</strong>。这是我最惊讶的发现：OLK 主仓的 <code>openeuler_defconfig</code> 实测 <strong><code>CONFIG_RISCV_ISA_V=y</code></strong>（RVA23 方向已默认开），但 25.03 <strong>发行版内核 config 显式关了它</strong>——官方发布内核的 config 在 <code>src-openeuler/kernel</code> 的 spec 里，跟源码树 defconfig 不一致。<strong>要复刻官方行为就用路线 A（现役 config），要新能力就用 defconfig 再微调。</strong></p>
</blockquote>
<h3 id="43-显式开启向量支持">4.3 显式开启向量支持</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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"># 在现役 config 基础上开向量 + IKCONFIG（/proc/config.gz 依赖它）</span>
</span></span><span class="line"><span class="cl">./scripts/config --file build/.config -e RISCV_ISA_V -e IKCONFIG
</span></span><span class="line"><span class="cl">make <span class="nv">O</span><span class="o">=</span>build olddefconfig
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 关键：验证开关真的生效（别信 menuconfig 显示）</span>
</span></span><span class="line"><span class="cl">grep RISCV_ISA_V build/.config    <span class="c1"># 必须看到 CONFIG_RISCV_ISA_V=y</span></span></span></code></pre></div></div>
<h3 id="44-交叉编译内核">4.4 交叉编译内核</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">make <span class="nv">O</span><span class="o">=</span>build -j6 Image dtbs modules
</span></span><span class="line"><span class="cl"><span class="c1"># 产物：</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   build/arch/riscv/boot/Image   —— 内核镜像（QEMU -kernel 用它）</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   vmlinux                       —— ELF（调试用）</span>
</span></span><span class="line"><span class="cl"><span class="c1">#   build/.config                 —— 最终生效 config</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">file build/arch/riscv/boot/Image  <span class="c1"># ELF 64-bit LSB executable, UCB RISC-V</span></span></span></code></pre></div></div>
<blockquote>
<p>⚠️ <strong>坑 10：WSL 内存雪崩</strong>。第一次我用 <code>-j$(nproc)</code>（8 核）并行编译 + 4G VM，直接把 WSL 的 32GB 内存打满，WSL 服务整个挂死（<code>0x8007274c</code>），只能 <code>wsl --shutdown</code> 重启。<strong>铁律：限 <code>-j6</code></strong>，这是本项目反复验证的教训。</p>
<p>⚠️ <strong>坑 11：报 <code>certs/openeuler-cert.pem</code> 不存在</strong>。openEuler 内核 spec 会带签名证书路径，源码树里没有。解法：<code>make menuconfig</code> 里清空 <code>Cryptographic API &gt; Certificates for signature checking &gt; Additional X.509 keys</code>，或 config 关 <code>CONFIG_MODULE_SIG</code>。</p>
</blockquote>
<h3 id="45-替换-vm-内核推荐先直启验证">4.5 替换 VM 内核（推荐先直启验证）</h3>
<p>替换内核有两条路线，<strong>先直启、再装盘</strong>是最稳的顺序：</p>
<p><strong>路线 1：QEMU <code>-kernel</code> 直启新内核（推荐做实验，不动 VM 磁盘）</strong></p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-riscv64 -machine virt -smp <span class="m">8</span> -m 4G <span class="se">\
</span></span></span><span class="line"><span class="cl">  -kernel ~/riscv/oe/olk-6.6/build/arch/riscv/boot/Image <span class="se">\
</span></span></span><span class="line"><span class="cl">  -append <span class="s1">&#39;root=/dev/vda1 rw console=ttyS0 swiotlb=1 selinux=0 highres=off earlycon&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -drive <span class="nv">file</span><span class="o">=</span>&lt;你的qcow2&gt;,format<span class="o">=</span>qcow2,id<span class="o">=</span>hd0 <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">  -netdev user,id<span class="o">=</span>n1,hostfwd<span class="o">=</span>tcp::12056-:22 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>n1 -nographic</span></span></code></pre></div></div>
<blockquote>
<p>⚠️ <strong>坑 12：<code>-kernel</code> 只认 Image，不认 vmlinuz</strong>。磁盘里 <code>/boot/vmlinuz-*</code> 是<strong>压缩过的内核</strong>（uImage 格式，带自解压头），QEMU <code>-kernel</code> 期望的是<strong>未压缩的 raw 内核 Image</strong>（<code>arch/riscv/boot/Image</code>）。格式不对，启动直接崩或没反应。</p>
</blockquote>
<p><strong>路线 2：完整安装进 VM（装盘，可重启生效）</strong></p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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">make <span class="nv">INSTALL_MOD_PATH</span><span class="o">=</span>./modroot modules_install
</span></span><span class="line"><span class="cl">tar czf ~/riscv/oe/kmods.tgz -C ./modroot lib
</span></span><span class="line"><span class="cl">scp -P <span class="m">12055</span> build/arch/riscv/boot/Image ~/riscv/oe/kmods.tgz linght153@127.0.0.1:~/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># VM 内：</span>
</span></span><span class="line"><span class="cl">sudo cp ~/Image /boot/vmlinuz-6.6.0-custom
</span></span><span class="line"><span class="cl">sudo tar xzf ~/kmods.tgz -C /
</span></span><span class="line"><span class="cl">sudo dracut --kver 6.6.0-custom -f /boot/initramfs-6.6.0-custom.img
</span></span><span class="line"><span class="cl">sudo grub2-mkconfig -o /boot/grub2/grub.cfg
</span></span><span class="line"><span class="cl">sudo grubby --set-default /boot/vmlinuz-6.6.0-custom
</span></span><span class="line"><span class="cl"><span class="c1"># 重启前先 grubby --info=ALL 确认新条目存在（VM 重启由你自己手动执行）</span></span></span></code></pre></div></div>
<h3 id="46-验证闭环向量真的可用了">4.6 验证闭环：向量真的可用了</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">uname -r                                   <span class="c1"># 你的自定义版本号</span>
</span></span><span class="line"><span class="cl">zcat /proc/config.gz <span class="p">|</span> grep RISCV_ISA_V    <span class="c1"># CONFIG_RISCV_ISA_V=y</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 编一个向量程序跑——之前必然 SIGILL，现在正常输出</span>
</span></span><span class="line"><span class="cl">gcc -march<span class="o">=</span>rv64gcv -O2 -o vec vec.c <span class="o">&amp;&amp;</span> ./vec
</span></span><span class="line"><span class="cl"><span class="c1"># 输出：VLEN bytes: 32（QEMU virt 默认 VLEN 256）</span></span></span></code></pre></div></div>
<p>那一刻看到向量程序第一次在 openEuler VM 里正常输出，之前所有的坑都值了。</p>
<hr>
<h2 id="结尾总结">结尾总结</h2>
<table>
	<thead>
			<tr>
					<th>环节</th>
					<th>核心结论</th>
					<th>关键坑</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>交叉编译</td>
					<td><code>-march</code> ≤ 目标 ISA，<code>-mabi=lp64d</code></td>
					<td>GCC 15 别用；GLIBCXX 3.4.32 加静态链接</td>
			</tr>
			<tr>
					<td>qemu-user</td>
					<td><code>-L &lt;sysroot&gt;</code> 不能省，秒级验证</td>
					<td>动态链接器找不到；<code>./hello</code> 二进制格式错</td>
			</tr>
			<tr>
					<td>验证三步走</td>
					<td>readelf 查链接、objdump 查指令、qemu-user 查运行</td>
					<td>三步全绿才可交付</td>
			</tr>
			<tr>
					<td>RVV 验证</td>
					<td>发行版内核可能不开 ISA_V → qemu-user <code>-cpu max</code> 绕过</td>
					<td>VM 里 SIGILL 是内核问题不是 QEMU；循环必须分段</td>
			</tr>
			<tr>
					<td>自编译内核</td>
					<td>现役 config 小改 + <code>scripts/config -e</code> 开开关</td>
					<td>defconfig≠发行版 config；<code>-j6</code>；<code>-kernel</code> 只认 Image</td>
			</tr>
	</tbody>
</table>
<p><strong>最大的认知升级</strong>：QEMU 模拟出扩展 ≠ 内核允许用户态使用。遇到 SIGILL 先查目标内核的 config，而不是怀疑编译器或 QEMU；而 qemu-user 这把&quot;绕过内核&quot;的钥匙，让 RISC-V 软件的正确性验证可以完全脱离真机和 VM 进行。这套&quot;交叉编译 + qemu-user 验证 + VM 只跑产物&quot;的工作流，现在已经成为我日常做 RISC-V 软件移植的标准姿势。</p>
<p><strong>给后来者的建议</strong>：按&quot;先交叉编译 → 再 qemu-user 跑通 → 需要真机性能/内核特性时再上 VM 或真机&quot;的顺序推进，每一步的验证手段都简单直接；遇到 Illegal instruction 先查 <code>/proc/cpuinfo</code> 和内核 config，别一头扎进编译参数里。RISC-V 生态还年轻，坑多但成长快，把这些实测记录沉淀下来，就是给社区最好的贡献。</p>
]]></description></item><item><title>openEuler RISC-V 零基础入门实操：从装 QEMU 到跑通第一个 RISC-V 程序</title><link>https://huxiang.bond/posts/openeuler-riscv-beginner/</link><pubDate>Fri, 30 Oct 2026 11:00:00 +0800</pubDate><author>admin@huxiang.bond (huxiang)</author><guid>https://huxiang.bond/posts/openeuler-riscv-beginner/</guid><description><![CDATA[<div class="featured-image">
                <img src="/welcome/card_futuristic.jpg" referrerpolicy="no-referrer">
            </div><blockquote>
<p>作者：一名在 x86 电脑上&quot;白嫖&quot;到一台 RISC-V 电脑的学习者</p>
<p>关键词：openEuler、RISC-V、QEMU、虚拟机、gcc、hello world</p>
</blockquote>
<h2 id="一写在前面为什么我非要折腾一台假的-risc-v-电脑">一、写在前面：为什么我非要折腾一台&quot;假&quot;的 RISC-V 电脑</h2>
<p>最近在跟着 openEuler RISC-V 相关的学习资料做入门实践，第一道坎就是：<strong>我手上没有 RISC-V 开发板，怎么办？</strong></p>
<p>答案就是 QEMU——一个开源模拟器，可以在我的 x86 电脑上&quot;假装&quot;出一台完整的 RISC-V 电脑（CPU、内存、硬盘、网卡全都有），然后在里面装一个真正的 openEuler RISC-V 操作系统。更妙的是，<strong>QEMU 用的是纯软件模拟（TCG 技术），不需要 CPU 支持硬件虚拟化</strong>，也不用去 BIOS 里开 VT-x，装完就能用。</p>
<p>这篇文章就是我的完整实操记录：从装 QEMU、下载镜像、启动虚拟机，到在 riscv64 系统里编译运行我写的第一个 C 程序。全程第一人称踩坑实录，零基础可跟。</p>
<p><strong>我的环境</strong>：Windows 11 + WSL2（Ubuntu 22.04），所有命令都在 WSL 终端里执行。如果你用 Windows 原生 PowerShell 或 macOS，命令大同小异，我在文末踩坑区会标注差异点。</p>
<h2 id="二环境准备装-qemu-并完成验证三连">二、环境准备：装 QEMU 并完成&quot;验证三连&quot;</h2>
<h3 id="21-安装-wslwindows-用户">2.1 安装 WSL（Windows 用户）</h3>
<p>管理员身份打开 PowerShell，执行：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-powershell">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="n">wsl</span> <span class="p">-</span><span class="n">-install</span></span></span></code></pre></div></div>
<p>重启后按提示设置 Ubuntu 的用户名和密码（<strong>记好这个密码，sudo 要用</strong>）。装好后打开&quot;开始菜单 → Ubuntu&quot;进入 Linux 终端。</p>
<h3 id="22-安装-qemu">2.2 安装 QEMU</h3>
<p>在 WSL 终端里依次执行：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install -y qemu-system-misc qemu-utils</span></span></code></pre></div></div>
<p><code>qemu-system-misc</code> 这个包里就包含了 riscv64 等&quot;杂项架构&quot;的全系统模拟器，<code>qemu-utils</code> 提供 <code>qemu-img</code> 工具。装完 <code>qemu-system-riscv64</code> 就在系统里了。</p>
<h3 id="23-验证三连装完必做">2.3 验证三连（装完必做）</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qemu-system-riscv64 --version
</span></span><span class="line"><span class="cl">qemu-system-riscv64 -machine <span class="nb">help</span> <span class="p">|</span> grep virt
</span></span><span class="line"><span class="cl">qemu-img --version</span></span></code></pre></div></div>
<p>我的实际输出：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">QEMU emulator version 8.2.2 (Debian 1:8.2.2+dfsg-1~22.04.5)
</span></span><span class="line"><span class="cl">Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">virt              RISC-V VirtIO Board (default)</span></span></code></pre></div></div>
<p>三条全过 = QEMU 装好了。<strong>要求版本号 ≥ 7.0</strong>，因为 openEuler 24.03 LTS 的官方镜像和启动脚本是按新版 QEMU 写的；<code>-machine help</code> 里能看到 <code>virt</code> 行说明模拟器支持 openEuler 官方镜像默认的机器型号。</p>
<blockquote>
<p>💡 踩坑预警：如果你的 WSL 里 <code>apt update</code> 卡住不动，多半是 Ubuntu 官方源在大陆网络慢，把 <code>/etc/apt/sources.list</code> 里的 <code>archive.ubuntu.com</code> 换成清华或阿里镜像源再试。</p>
</blockquote>
<h2 id="三下载镜像三个文件一个都不能少">三、下载镜像：三个文件，一个都不能少</h2>
<p>打开 openEuler 官方下载页：<code>https://www.openeuler.org/zh/download/</code>，找到 <strong>24.03 LTS</strong> 版本 → <strong>RISC-V 架构</strong>分区 → 虚拟机镜像区，下载这四个文件到 <code>~/riscv-lab/</code> 目录：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">openEuler-24.03-LTS-SP3-riscv64.qcow2.xz   # 磁盘镜像压缩包，约 3-5GB，最大最慢
</span></span><span class="line"><span class="cl">RISCV_VIRT_CODE.fd                          # UEFI 固件-代码（几 MB）
</span></span><span class="line"><span class="cl">RISCV_VIRT_VARS.fd                          # UEFI 固件-变量（几 MB）
</span></span><span class="line"><span class="cl">start_vm.sh                                 # 官方启动脚本（几 KB）</span></span></code></pre></div></div>
<p>三个文件的角色：<code>.qcow2.xz</code> 是<strong>整块硬盘</strong>（装了 openEuler 系统，被 xz 压缩打包了）；两个 <code>.fd</code> 是<strong>UEFI 固件</strong>（电脑开机第一段代码，负责把系统从硬盘里叫醒）；<code>start_vm.sh</code> 是官方写好的启动说明书。</p>
<p>下载完做两件事：</p>
<ol>
<li><strong>看大小</strong>：qcow2.xz 应该有 3-5GB，如果只有几百 MB，多半是下载中断了，删掉重下。</li>
<li><strong>校验完整性</strong>：用 <code>sha256sum</code> 对比下载页给的校验值，整串一致才放心：</li>
</ol>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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> ~/riscv-lab
</span></span><span class="line"><span class="cl">sha256sum openEuler-24.03-LTS-SP3-riscv64.qcow2.xz</span></span></code></pre></div></div>
<blockquote>
<p>⏳ 踩坑预警：<strong>镜像下载慢</strong>是大陆网络的老大难。我的解法三件套：① 用支持断点续传的下载工具（IDM/迅雷），断了不用从头来；② 换清华镜像源 <code>https://mirrors.tuna.tsinghua.edu.cn/openeuler/</code>，路径结构和官方站一致；③ 实在不行就半夜下（说多了都是泪，但真的快）。</p>
</blockquote>
<h2 id="四解压--启动那条-200-字符的长命令逐参数看懂">四、解压 + 启动：那条 200 字符的长命令，逐参数看懂</h2>
<h3 id="41-解压镜像">4.1 解压镜像</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </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> ~/riscv-lab
</span></span><span class="line"><span class="cl">xz -dk openEuler-24.03-LTS-SP3-riscv64.qcow2.xz</span></span></code></pre></div></div>
<p><code>-d</code> 是解压（decompress），<code>-k</code> 是保留原压缩包。解压要等 1-5 分钟，终端没输出是正常的。完成后 <code>ls -lh</code> 应该能看到不带 <code>.xz</code> 后缀的 qcow2 文件（解压后约 5-7GB）。</p>
<h3 id="42-启动命令逐参数拆解">4.2 启动命令逐参数拆解</h3>
<p>第一次启动，我建议直接用官方 <code>start_vm.sh</code>，但<strong>一定要看懂它在干什么</strong>。它的核心就是下面这条命令（我整理成了经典写法，每个参数独立一行，行尾 <code>\</code> 只是续行符）：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><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">  -machine virt -m 4G -smp <span class="m">4</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -drive <span class="nv">file</span><span class="o">=</span>openEuler-24.03-LTS-SP3-riscv64.qcow2,format<span class="o">=</span>qcow2,id<span class="o">=</span>hd0 <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">  -netdev user,id<span class="o">=</span>n0,hostfwd<span class="o">=</span>tcp::12055-:22 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -device virtio-net-device,netdev<span class="o">=</span>n0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -nographic</span></span></code></pre></div></div>
<p>用人话翻译：<strong>&ldquo;QEMU，请模拟一台 RISC-V 电脑（virt 型号），给它 4GB 内存、4 个 CPU 核心；把 qcow2 文件当作硬盘接上；给它一张能上网的虚拟网卡，并把宿主机的 12055 端口转发到虚拟机的 22 号端口；不开图形窗口，所有输出走终端。&rdquo;</strong></p>
<p>各参数逐个说：</p>
<table>
	<thead>
			<tr>
					<th>参数</th>
					<th>含义</th>
					<th>去掉会怎样</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>-machine virt</code></td>
					<td>机器型号，<code>virt</code> 是 RISC-V 世界最常用的虚拟主板</td>
					<td>报错 <code>No machine specified</code></td>
			</tr>
			<tr>
					<td><code>-m 4G</code></td>
					<td>内存 4GB（从宿主机&quot;借&quot;走的，别开太大）</td>
					<td>内存太小起不来</td>
			</tr>
			<tr>
					<td><code>-smp 4</code></td>
					<td>4 个 CPU 核心（纯软件模拟，核给多了宿主机反而卡）</td>
					<td>默认 1 核，慢但能跑</td>
			</tr>
			<tr>
					<td><code>-drive ...</code></td>
					<td>定义硬盘：<code>file=</code> 指定 qcow2 文件，<code>format=qcow2</code> 声明格式，<code>id=hd0</code> 起名</td>
					<td>系统无盘可用</td>
			</tr>
			<tr>
					<td><code>-device virtio-blk-device,drive=hd0</code></td>
					<td>把硬盘 <code>hd0</code> 通过 virtio 高速总线接上主板</td>
					<td>硬盘&quot;看不见&quot;，Kernel panic</td>
			</tr>
			<tr>
					<td><code>-netdev user,...hostfwd=tcp::12055-:22</code></td>
					<td>用户模式网卡 + <strong>端口转发</strong>：访问宿主机 12055 端口 = 访问 VM 的 22 端口（SSH）</td>
					<td>VM 无法上网、无法 SSH</td>
			</tr>
			<tr>
					<td><code>-device virtio-net-device,netdev=n0</code></td>
					<td>把网卡接上主板</td>
					<td>网卡&quot;看不见&quot;</td>
			</tr>
			<tr>
					<td><code>-nographic</code></td>
					<td>不开图形窗口，虚拟机的&quot;屏幕&quot;直接接到终端</td>
					<td>尝试开图形窗口，无图形环境会报错</td>
			</tr>
	</tbody>
</table>
<blockquote>
<p>📌 这里最重要、全书必考的是 <strong><code>hostfwd=tcp::12055-:22</code></strong>：它决定了你后面能不能 SSH 进虚拟机。22 是 SSH 默认端口，12055 是宿主机上的映射端口（高位端口，避免和宿主机自己的 22 冲突）。想换就换，但换了以后 SSH 命令的 <code>-p</code> 参数要跟着改。</p>
</blockquote>
<h3 id="43-启动你会看到四个阶段">4.3 启动！你会看到四个阶段</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./start_vm.sh</span></span></code></pre></div></div>
<blockquote>
<p>💡 踩坑预警：如果报 <code>Permission denied</code>，说明脚本没有执行权限，先 <code>chmod +x start_vm.sh</code> 再运行。如果报 <code>command not found</code>，检查 <code>pwd</code> 确认你在 <code>riscv-lab</code> 目录里。</p>
</blockquote>
<p>回车后耐心等待 1-3 分钟，启动日志按这个顺序刷屏：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">OpenSBI v1.4
</span></span><span class="line"><span class="cl">   ____                    _____ ____ _____
</span></span><span class="line"><span class="cl">  / __ \                  / ____|  _ \_   _|
</span></span><span class="line"><span class="cl">...（固件先醒来，确认自己在 QEMU 的 virt 机器上）
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[    0.000000] Linux version 6.6.0-76.0.0.76.oe2403.riscv64 (oe-build@localhost) ...
</span></span><span class="line"><span class="cl">[    0.000000] CPU: rv64imafdc_zicsr_zifencei_zba_zbb_zbc_zbs ...
</span></span><span class="line"><span class="cl">[    0.000000] 4 CPUs detected
</span></span><span class="line"><span class="cl">[    1.234567] virtio_blk virtio0: [vda] 62914560 512-byte logical blocks (32.2 GB)
</span></span><span class="line"><span class="cl">[    3.456789] systemd[1]: Running in virtual machine.
</span></span><span class="line"><span class="cl">[  OK  ] Started OpenSSH server daemon.
</span></span><span class="line"><span class="cl">[  OK  ] Reached target Multi-User System.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">openEuler Linux 24.03 (LTS)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Kernel 6.6.0-76.0.0.76.oe2403.riscv64 on an riscv64
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">localhost login:</span></span></code></pre></div></div>
<p>看到 <code>login:</code> 提示符，<strong>你的第一台 RISC-V 虚拟机就启动成功了！</strong> 重点看三处：<code>Linux version ...riscv64</code>（RISC-V 世界的内核）、<code>virtio_blk ... [vda]</code>（你的 qcow2 硬盘被识别了）、<code>on an riscv64</code>（架构实锤）。</p>
<h3 id="44-登录--立刻改密码">4.4 登录 + 立刻改密码</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">localhost login: root
</span></span><span class="line"><span class="cl">Password: openEuler12#$</span></span></code></pre></div></div>
<p><strong>openEuler RISC-V 官方镜像默认密码是 <code>openEuler12#$</code></strong>（<code>#$</code> 是密码的一部分，一共 13 个字符，别把 <code>#</code> 打成中文的）。输密码时屏幕什么都不显示——这是 Linux 的安全设计，不是键盘坏了，盲打回车即可。</p>
<p>登录成功提示符会变成 <code>[root@localhost ~]#</code>（<code>#</code> 是 root 用户的标志）。第一件事改密码：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">passwd</span></span></code></pre></div></div>
<blockquote>
<p>⚠️ 默认密码全世界都知道，<strong>登录后必须立刻改</strong>。密码至少 8 位，建议字母+数字+符号混合。忘了密码很麻烦（要进单用户模式重置），一定记牢。</p>
</blockquote>
<h2 id="五换个姿势进门ssh-远程登录">五、换个姿势进门：SSH 远程登录</h2>
<p>在 QEMU 黑窗口里操作只是第一步，真实服务器工作流全靠 SSH。回到<strong>宿主机</strong>终端：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh root@127.0.0.1 -p <span class="m">12055</span></span></span></code></pre></div></div>
<p>第一次连接会弹&quot;主机指纹确认&quot;提示（这是 SSH 的防伪机制，防止有人冒充你的 VM），输入完整的 <code>yes</code> 回车，然后输 VM 的 root 密码。登录后验证一下：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">uname -m</span></span></code></pre></div></div>
<p>输出 <code>riscv64</code>——<strong>你在 x86 宿主机上，手却伸进了 riscv64 的虚拟机</strong>，这就是 12055 端口转发的功劳。</p>
<blockquote>
<p>💡 进阶一步：<code>ssh-keygen -t ed25519</code> 生成密钥对，再 <code>ssh-copy-id root@127.0.0.1 -p 12055</code> 把公钥登记进 VM，以后 <code>ssh root@127.0.0.1 -p 12055</code> 免密直进，传文件用 <code>scp -P 12055 文件 root@127.0.0.1:/root/</code>（注意 <strong>scp 是大写 <code>-P</code>，ssh 是小写 <code>-p</code></strong>，这个坑坑过无数人）。</p>
</blockquote>
<h2 id="六第一个-risc-v-程序从装-gcc-到跑通-hello">六、第一个 RISC-V 程序：从装 gcc 到跑通 hello</h2>
<h3 id="61-装编译器">6.1 装编译器</h3>
<p>在 VM 里执行（注意提示符是 <code>[root@localhost ~]#</code>，别在宿主机敲）：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dnf install -y gcc</span></span></code></pre></div></div>
<p>注意输出里这行：<code>gcc riscv64 12.3.1-2.oe2403</code>——你装的是 <strong>riscv64 版的 gcc</strong>，它编译出来的机器码就是 RISC-V 方言，这就是&quot;原生编译&quot;。验证：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gcc --version</span></span></code></pre></div></div>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">gcc (GCC) 12.3.1 20230508 (openEuler 12.3.1-2.oe2403)</span></span></code></pre></div></div>
<h3 id="62-写-helloc">6.2 写 hello.c</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nano hello.c</span></span></code></pre></div></div>
<p>输入（强烈建议手敲一遍，别复制）：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-c">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</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">printf</span><span class="p">(</span><span class="s">&#34;Hello from RISC-V!</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="k">return</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></div>
<p><code>Ctrl+O</code> 保存，<code>Ctrl+X</code> 退出。逐行含义一句话版：<code>#include &lt;stdio.h&gt;</code> 是引入标准输入输出库的&quot;说明书&quot;；<code>int main(void)</code> 是程序唯一的大门，CPU 从这里开始执行；<code>printf</code> 把文字打到屏幕，<code>\n</code> 是换行，行尾分号 <code>;</code> 是 C 语言的&quot;句号&quot;（漏掉它是新手第一大报错来源）；<code>return 0</code> 表示程序正常结束。</p>
<h3 id="63-编译--运行--验证架构三连">6.3 编译 → 运行 → 验证架构（三连）</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gcc hello.c -o hello
</span></span><span class="line"><span class="cl">./hello
</span></span><span class="line"><span class="cl">file hello</span></span></code></pre></div></div>
<p>我的实际输出：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="复制到剪贴板"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Hello from RISC-V!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">hello: ELF 64-bit LSB executable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, for GNU/Linux 4.15.0, not stripped</span></span></code></pre></div></div>
<p>三个要点：</p>
<ol>
<li><strong><code>gcc hello.c -o hello</code> 编译成功是&quot;沉默&quot;的</strong>——没有任何输出就是最好的输出（注意 <code>-o</code> 是小写字母 o，不是数字 0）。</li>
<li><strong>运行必须写 <code>./hello</code></strong>——Linux 默认不在当前目录找命令，直接敲 <code>hello</code> 会报 <code>command not found</code>。</li>
<li><strong><code>file hello</code> 是本章最有成就感的时刻</strong>：输出里 <code>UCB RISC-V</code> 就是架构实锤（UCB = 加州大学伯克利分校，RISC-V 的诞生地，名字留在了 ELF 格式里）；<code>RVC</code> 表示用了压缩指令扩展；<code>/lib/ld-linux-riscv64-lp64d.so.1</code> 是 riscv64 的程序加载器。</li>
</ol>
<p><strong>强烈建议做个对照实验</strong>：把 hello.c 在 x86 宿主机上也编译一次，<code>file</code> 输出会变成 <code>ELF 64-bit ... x86-64 ...</code>——同一个源码，不同架构的编译器，产出不同&quot;方言&quot;的机器码。把 VM 里编的 <code>hello</code> 拷到宿主机运行，会报 <code>Exec format error</code>（架构不对，不是文件坏了）。<strong>架构的壁垒，这一刻你亲手摸到了。</strong></p>
<h2 id="七踩坑合集我替你踩过的坑">七、踩坑合集：我替你踩过的坑</h2>
<table>
	<thead>
			<tr>
					<th>现象</th>
					<th>原因</th>
					<th>解法</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>执行脚本报 <code>Permission denied</code></td>
					<td>脚本没有执行权限</td>
					<td><code>chmod +x start_oe.sh</code>，或改用 <code>bash start_oe.sh</code></td>
			</tr>
			<tr>
					<td>启动报 <code>Failed to bind socket: Address already in use</code></td>
					<td>12055 端口被占（上次 VM 没关干净 / 别的程序占用）</td>
					<td>换端口（改脚本里的 hostfwd 和 SSH 的 <code>-p</code>）；查占用：Windows <code>netstat -ano | findstr 12055</code>，Linux <code>ss -ltnp | grep 12055</code></td>
			</tr>
			<tr>
					<td>镜像下载慢 / 中断</td>
					<td>官方站在大陆网络慢</td>
					<td>清华镜像源 <code>mirrors.tuna.tsinghua.edu.cn/openeuler/</code>；IDM/迅雷断点续传；<code>sha256sum</code> 校验完整性</td>
			</tr>
			<tr>
					<td><code>ssh: connect to host 127.0.0.1 port 22: Connection refused</code></td>
					<td><strong>忘了写 <code>-p 12055</code></strong>，ssh 默认去敲 22 端口</td>
					<td>命令补上 <code>-p 12055</code></td>
			</tr>
			<tr>
					<td>SSH 报 <code>Connection refused</code>（端口写对了）</td>
					<td>VM 没启动 / 防火墙拦截 / VM 里 sshd 没运行</td>
					<td>确认 QEMU 窗口还开着；Windows 防火墙放行 qemu；VM 里 <code>systemctl start sshd</code></td>
			</tr>
			<tr>
					<td>输密码没反应</td>
					<td><strong>正常现象</strong>，终端故意不回显密码</td>
					<td>盲打回车即可</td>
			</tr>
			<tr>
					<td><code>bash: hello: command not found</code></td>
					<td>运行少了 <code>./</code> 前缀</td>
					<td>写 <code>./hello</code></td>
			</tr>
			<tr>
					<td><code>bash: ./hello: cannot execute binary file: Exec format error</code></td>
					<td>把 x86 编译的程序拷进 riscv64 VM 跑（反过来也一样）</td>
					<td>在目标机器上重新编译，或学交叉编译</td>
			</tr>
			<tr>
					<td>编译报 <code>stray '\...' in program</code></td>
					<td>代码里混入全角字符（中文引号/分号，输入法没切回）</td>
					<td>按 <code>文件名:行号:列号</code> 定位，改成英文半角</td>
			</tr>
			<tr>
					<td>杀毒软件把 QEMU 当病毒</td>
					<td>误报（模拟器常被误判为&quot;黑客工具&quot;）</td>
					<td>从官网 qemu.org 下载，加白名单（安装目录 + 工作目录）</td>
			</tr>
	</tbody>
</table>
<h2 id="八总结我这一路学到了什么">八、总结：我这一路学到了什么</h2>
<p>从零到跑通第一个 RISC-V 程序，我完成了三件有里程碑意义的事：</p>
<ol>
<li><strong>装好了 QEMU</strong>，用纯软件模拟在 x86 电脑上&quot;无中生有&quot;了一台 RISC-V 电脑——不需要真机，不需要硬件虚拟化。</li>
<li><strong>看懂了一条 200 字符的 QEMU 启动命令</strong>：<code>-machine</code> 选机型、<code>-m</code> 给内存、<code>-smp</code> 给核数、<code>-drive</code> 挂硬盘、<code>-netdev hostfwd</code> 做端口转发、<code>-nographic</code> 走终端。从此虚拟机在我眼里不再是&quot;黑魔法&quot;，而是一台参数明确的电脑。</li>
<li><strong>在 riscv64 上原生编译并运行了自己的程序</strong>，并且用 <code>file hello</code> 亲眼看到了 <code>UCB RISC-V</code> 的字样——那一刻我才真正理解了什么是&quot;架构&quot;：同一个 <code>printf</code>，x86 和 RISC-V 会翻译成完全不同的机器码，程序必须在目标架构上编译才能运行。</li>
</ol>
<p>下一步我准备挑战<strong>交叉编译</strong>：在 x86 宿主机上用 <code>riscv64-linux-gnu-gcc</code> 编出 riscv64 的程序，再用 <code>qemu-riscv64</code>（用户态模拟）直接验证——&ldquo;编译放宿主机、运行验证靠模拟器&rdquo;，这是 RISC-V 软件生态里最常用的生产工作流，也是我实习测评工作的标准姿势。等我把 RVV 向量扩展也玩明白了，再来分享第二篇。</p>
<p>如果这篇文章帮到了你，或者你在实操中遇到了其他坑，欢迎评论区交流。<strong>RISC-V 的世界，入门真的没有想象中那么难。</strong></p>
]]></description></item></channel></rss>