Skip to content

dart-ffi-demo ​

1. 实验目标 ​

用最小 Dart CLI 验证 dart:ffi 零拷贝与原生函数调用(对应 docs/05-bridge-hybrid/06):

  • DynamicLibrary.process() 拿进程符号表,lookupFunction 绑定 C 函数
  • NativeFunction / Pointer / Int32 / IntPtr / Void 类型映射
  • malloc + asTypedList:Dart 直接读写 Native 内存(零拷贝)
  • 对照:FFI 无序列化开销,而 PlatformChannel 每次调用都要内存序列化 + 切主线程

2. 工程要点 ​

  • 纯 dart:ffi(无 package:ffi 依赖),macOS / Linux 直接可跑
  • C 函数签名声明(typedef ... Native / Dart 成对)是 FFI 绑定核心
  • sizeOf<IntPtr>() 获取原生指针尺寸;asTypedList(n) 把 Native 内存包装成 Dart 视图
  • 手写字符串时注意 null terminator;本例用 malloc 分配数值数组简化

3. 关键实现速览 ​

3.1 绑定 C 函数 ​

为什么看这段:Dart 调用 C 函数必须声明签名对(Native 类型 → Dart 类型)。

dart
typedef CGetpidNative = Int32 Function();
typedef CGetpidDart = int Function();

final libc = DynamicLibrary.process();                       // 当前进程符号表
final getpid = libc.lookupFunction<CGetpidNative, CGetpidDart>('getpid');
print('getpid() = ${getpid()}');                             // 直接调用

3.2 零拷贝读写 Native 内存 ​

为什么看这段:asTypedList 不复制数据,Dart 视图与 C 堆是同一块内存。

dart
final ptr = malloc(sizeOf<IntPtr>() * count).cast<Uint8>();
final view = ptr.asTypedList(count);   // 视图,无拷贝
view[0] = 255;                         // Dart 写
print(ptr.asTypedList(count)[0]);      // C 侧同址读到 255
free(ptr.cast());                      // 必须释放

4. 运行方式 ​

bash
cd labs/dart/host
dart run bin/runtime_concurrency/ffi_demo.dart

5. 预期现象 ​

  • getpid() = <pid>(Dart 调用真实 C 函数)
  • 通过 Dart 视图写入 [0,10,...,70] 并读回;改 view[0]=255 后同址读回 255(零拷贝证明)
  • 打印 C 堆指针地址(0x...)

6. 常见误区 ​

误区实际情况
FFI 和 PlatformChannel 差不多FFI 无序列化/无主线程限制;PlatformChannel 必须序列化 + 切主线程
Pointer 用完会自动释放不会——Native 内存要手动 free,否则泄漏
C++ 线程可以直接改 Dart 对象不行——必须 Dart_PostCObject 投递消息回 Dart 事件循环

7. 对应知识库文档 ​

8. 完整源码 ​

站点构建时间:2026/8/24 23:43:17