FFI(Foreign Function Interface)是什么?

4 阅读4分钟

一、FFI 的本质是什么?

FFI(Foreign Function Interface)的本质是:

让一种语言能够调用另一种语言的函数或使用其数据结构的机制。

换句话说,FFI 是跨语言通信的“共同语言”,是一套用来让不同编程语言互相理解、互相调用的接口规范。

核心目的只有一个:

让语言 A 能像调用自己本地函数一样,去调用语言 B 的函数。

FFI 是一种“技术能力”,而不是某种语言的专属特性。

任何语言都有可能支持 FFI。

二、用类比帮助理解:FFI 就像国际翻译官

假设:

  • Java = 中国人
  • C/C++ = 美国人
  • Python = 日本人
  • Rust = 德国人

这些语言各自有自己的语法、类型系统、运行时模型,就像不同国家有不同语言。

FFI 的角色就像:

一个同时懂多国语言的翻译官,让他们彼此沟通。

例如:

  • Java 想调用 C 函数:JNI = Java 世界的翻译官
  • Python 想使用 C 库:ctypes / cffi = Python 世界的翻译官
  • Rust 想调用 C 接口:Rust FFI = Rust 世界的翻译官

无论哪种语言,只要有一个 FFI,就可以与其他语言通信。

三、FFI 在技术层面是怎么运作的?

从操作系统的角度看:

编程语言最终都运行在操作系统和 CPU 上。

只要能调用动态库函数(.so / .dll),就能实现跨语言调用。

FFI 主要做三件事:


1. 函数调用协议统一(ABI 统一)

不同语言的函数,调用方式可能不一样,例如:

  • 参数如何入栈
  • 哪些寄存器保存什么数据
  • 返回值如何传递
  • 对齐方式
  • 字符串格式
  • 内存由谁释放

FFI 要解决的第一件事,就是让语言 A 调用语言 B 的函数时:

双方使用一致的 ABI(Application Binary Interface)。

ABI 就是机器级别的“通信协议”。

2. 数据类型转换

例如:

Java 的 int = 32 位

C 的 int = 在大多数平台上也是 32 位

→ 可以直接映射

但 Java 的 String ≠ C 的 char*

Java 的对象 ≠ C 的结构体

FFI 负责定义:

  • 哪些类型能直接映射
  • 哪些需要“包装”或“桥接”
  • 如何管理内存释放

3. 运行时隔离与安全

FFI 还需要保证:

  • 访问非法内存时能控制风险
  • 语言运行时(GC、异常机制)不被破坏
  • 跨线程、跨进程的调用是安全的
  • 不互相影响生命周期

这些都是跨语言调用的重点难题。

似乎类似于网络的远程接口交互,不同的语言,但是是通过共用的网络协议、接口协议来进行的。

四、FFI 在 JVM/JNI 中的体现

JNI 实际上就是 JVM 给 Java 提供的 专属 FFI。

它提供:

  1. 函数调用协议:如何从 Java 调到 C/C++
  2. 数据类型映射:jint、jstring、jobject
  3. 运行时交互:Java 对象如何从 native 世界被引用
  4. GC 保护机制:LocalRef / GlobalRef
  5. 线程管理:AttachCurrentThread
  6. 异常传递机制:ExceptionOccurred / ThrowNew

换句话说:

**JNI 是 JVM 定义的 FFI 标准,C/C++ 是只是实现语言。

JNI 定义规则 → JVM 实现规则 → Java 才能调用 C/C++。**

五、FFI 的典型用途(不限于 Java)

几乎所有现代语言都依赖 FFI:

Java

  • JNI = FFI
  • 用来调用 C/C++、OpenGL、音视频库、操作系统 API

Python

  • ctypes / cffi = FFI
  • 调用 C 的 TensorFlow/PyTorch 核心组件

Rust

  • Rust FFI ←→ C 是第一公民
  • 调用 C 库、或被 C/C++ 调用

Go

  • cgo = FFI
  • 调用 C 库、OpenSSL、系统调用

JavaScript(Node.js)

  • N-API / FFI
  • 调用 C++ addon

FFI 让语言能够复用不同语言的生态,避免重复造轮子。

六、常见误解:

❌ 误解 1:Java 能调用 C/C++ 是因为 JVM 用 C++ 写的

正确:Java 能调用 C/C++ 是因为 JVM 实现了 JNI(FFI 规范),与实现语言无关。

❌ 误解 2:FFI = 调 C/C++

正确:

FFI 可以连接任何语言:Python → Java、Rust → C、Go → C++、JS → Rust……

❌ 误解 3:FFI 很简单

正确:

FFI 是跨语言互操作中最复杂、最底层的机制之一,涉及:

  • ABI
  • GC
  • 线程模型
  • 异常机制
  • 生命周期管理

七、一句话总结

理解 FFI 的关键:

FFI 是跨语言调用的通用桥梁,不是某种语言自带的特权能力。 JNI 是 JVM 的 FFI 规范,而不是 JVM 使用了 C++ 才能调用 C++。

可以相关调用的本质:

建立起来可以信息交互的通路,只要可以相互发信息,便可以认为是一种调用方式。

所谓不同的语言并非是鸿沟,在操作系统维度,在硬件-cpu、寄存器、内存等角度上看,并没有什么区别,都是数据、信息、信号、01.

也就是有了彼此认识的通讯协议、彼此联通的通讯通路。