<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh">
    <title>bigfish的网络博客 - ABI</title>
    <subtitle>记录技术和生活</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blog.laofu.online/tags/abi/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.laofu.online"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-15T09:00:00+08:00</updated>
    <id>https://blog.laofu.online/tags/abi/atom.xml</id>
    <entry xml:lang="zh">
        <title>从一个 helloworld 动态库到 rpi package：ABI 插件是怎样被加载的</title>
        <published>2026-10-15T09:00:00+08:00</published>
        <updated>2026-10-15T09:00:00+08:00</updated>
        
        <author>
          <name>bigfish</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.laofu.online/blog/20261015-0900-rpicha-jian-abijia-zai-ji-zhi/"/>
        <id>https://blog.laofu.online/blog/20261015-0900-rpicha-jian-abijia-zai-ji-zhi/</id>
        
        <summary type="html">&lt;p&gt;很多人第一次看到 rpi 的 Rust 插件，会把它理解成“编译出一个动态库，然后让 rpi 找到它”。这句话方向没错，但省略了最关键的部分：动态库如何证明自己是一个 rpi 插件？注册的工具如何跨过 Rust 边界？一次调用结束后，插件里的内存由谁释放？源码目录里的 crate 又是怎样变成项目里的 package 的？&lt;/p&gt;
&lt;p&gt;这篇文章从 &lt;code&gt;examples/plugin-stub&lt;/code&gt; 这样的最小 demo 开始，沿着 &lt;code&gt;rpi-plugin-sdk&lt;/code&gt;、&lt;code&gt;rpi-extensions&lt;/code&gt; 和 &lt;code&gt;rpi-cli&lt;/code&gt; 的实现，解释这条链路：&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;mermaid&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;flowchart&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-6&quot;&gt; LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    A&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;插件源码\nRust crate&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt; --&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; B&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;cdylib\n.dll / .so / .dylib&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    B &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; C&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;rpi 加载器\nlibloading&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    C &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; D&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;rpi_plugin_register&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    D &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; E&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;HostApi + ExtensionRegistry&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    E &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; F&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;RegistrySnapshot&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt;    F &lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-3 z-d-10&quot;&gt; G&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;Agent 会话中的工具\n事件与其他扩展能力&lt;/span&gt;&lt;span class=&quot;z-l-13 z-d-8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的“插件”指 rpi 的 Rust 原生 ABI 扩展，不是 MCP Server，也不是一个通过 HTTP 或 stdio 运行的外部进程。它和宿主运行在同一个进程里，因此能力强、开销低，但也意味着插件是本机可信代码，ABI、内存所有权和生命周期必须非常明确。&lt;/p&gt;
&lt;p&gt;本文讨论的是 rpi 的原生 ABI 插件机制。由于 ABI、loader 和 package 命令会随仓库版本变化，下面的文件名、符号名和命令都应与所读源码版本一起核对；代码块中明确标注为“示意”的部分，不等于可直接复制运行的最小插件。文章关注一条主线：插件如何从动态库进入注册表，再被会话驱动；不把它扩展成完整的 Rust FFI 教程。&lt;/p&gt;</summary>
        
    </entry>
</feed>
