Zig 动态库教程
1. 动态库基础概念
Section titled “1. 动态库基础概念”1.1 什么是动态库?
Section titled “1.1 什么是动态库?”动态库(Dynamic Library,也称共享库)是在运行时加载的库文件,允许多个程序共享相同的代码段,减少内存占用。
主要特点:
- 运行时加载:程序启动或运行时加载
- 代码共享:多个进程可共享同一份库代码
- 独立更新:可单独更新库而不重新编译程序
- 符号解析:运行时解析函数地址
2. 创建动态库
Section titled “2. 创建动态库”2.1 基础动态库示例
Section titled “2.1 基础动态库示例”// fizzbuzz.zig - 动态库源码const std = @import("std");
// 导出函数:会被其他程序调用export fn fizzbuzz(n: u32) bool { return n % 3 == 0 or n % 5 == 0;}
// 内部函数:不会被导出fn internal_helper() void { // 内部实现细节}
// 导出带 C ABI 的函数export fn fibonacci(n: u32) u32 { if (n < 2) return n; return fibonacci(n - 1) + fibonacci(n - 2);}2.2 构建脚本详解
Section titled “2.2 构建脚本详解”// build.zig - 动态库构建脚本const std = @import("std");
pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{});
// 创建动态库 const libfizzbuzz = b.addLibrary(.{ .name = "fizzbuzz", // 库名称 .linkage = .dynamic, // 关键:设置为动态链接 .version = .{ // 版本号 .major = 1, .minor = 2, .patch = 3, }, .root_module = b.createModule(.{ .root_source_file = b.path("fizzbuzz.zig"), .target = target, .optimize = optimize, }), });
// 可选:设置 soname(ELF 系统中) libfizzbuzz.link_system_libc = false; libfizzbuzz.install();}2.3 构建结果
Section titled “2.3 构建结果”运行 zig build 后生成:
zig-out/lib/├── libfizzbuzz.so -> libfizzbuzz.so.1 # 主符号链接├── libfizzbuzz.so.1 -> libfizzbuzz.so.1.2.3 # 主版本号链接└── libfizzbuzz.so.1.2.3 # 实际库文件3. 使用动态库的程序
Section titled “3. 使用动态库的程序”3.1 链接动态库的程序
Section titled “3.1 链接动态库的程序”// main.zig - 使用动态库的程序const std = @import("std");
// 声明外部函数(来自动态库)extern fn fizzbuzz(n: u32) bool;extern fn fibonacci(n: u32) u32;
pub fn main() void { // 调用动态库中的函数 std.debug.print("测试 FizzBuzz:\n", .{}); for (1..21) |n| { if (fizzbuzz(@intCast(n))) { std.debug.print("{}, ", .{n}); } } std.debug.print("\n", .{});
// 调用另一个函数 const fib_10 = fibonacci(10); std.debug.print("fibonacci(10) = {}\n", .{fib_10});}3.2 构建使用动态库的可执行文件
Section titled “3.2 构建使用动态库的可执行文件”// build.zig - 链接动态库的程序const std = @import("std");
pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{});
// 创建可执行文件 const exe = b.addExecutable(.{ .name = "fizzbuzz_app", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, });
// 链接动态库 exe.linkSystemLibrary("fizzbuzz");
// 设置库搜索路径(如果不在标准路径) exe.addLibraryPath(b.path("zig-out/lib"));
// 运行时库搜索路径(可选) if (target.result.os.tag == .linux) { exe.addRPath(b.path("zig-out/lib")); }
b.installArtifact(exe);}4. 高级动态库特性
Section titled “4. 高级动态库特性”4.1 导出结构体和类型
Section titled “4.1 导出结构体和类型”// complex.zig - 导出复杂类型const std = @import("std");
// 导出结构体(需要 C 兼容布局)pub const Complex = extern struct { real: f64, imag: f64,
pub fn init(real: f64, imag: f64) Complex { return Complex{ .real = real, .imag = imag }; }
pub fn add(self: Complex, other: Complex) Complex { return Complex{ .real = self.real + other.real, .imag = self.imag + other.imag, }; }};
// 导出使用结构体的函数export fn complex_add(a: Complex, b: Complex) Complex { return a.add(b);}4.2 版本控制和符号可见性
Section titled “4.2 版本控制和符号可见性”// 使用版本控制来管理 ABI 兼容性pub const fizzbuzz_v1 = struct { pub export fn fizzbuzz(n: u32) bool { return n % 3 == 0 or n % 5 == 0; }};
pub const fizzbuzz_v2 = struct { // 新版本添加额外功能 pub export fn fizzbuzz_ext(n: u32) struct { is_fizz: bool, is_buzz: bool } { return .{ .is_fizz = n % 3 == 0, .is_buzz = n % 5 == 0, }; }};
// 控制符号可见性comptime { @export(fizzbuzz_v1.fizzbuzz, .{ .name = "fizzbuzz" }); @export(fizzbuzz_v2.fizzbuzz_ext, .{ .name = "fizzbuzz_ext", .linkage = .Strong, // 强符号 });}4.3 初始化函数和清理函数
Section titled “4.3 初始化函数和清理函数”// 库初始化函数(构造函数)export fn __attribute__((constructor)) init_library() void { std.debug.print("动态库初始化\n", .{}); // 初始化全局状态、分配资源等}
// 库清理函数(析构函数)export fn __attribute__((destructor)) cleanup_library() void { std.debug.print("动态库清理\n", .{}); // 释放资源、清理状态}
// 线程本地存储初始化threadlocal var thread_counter: u32 = 0;
export fn get_thread_counter() u32 { return thread_counter;}
export fn increment_thread_counter() void { thread_counter += 1;}5. 运行时动态加载
Section titled “5. 运行时动态加载”5.1 使用 dlopen 动态加载
Section titled “5.1 使用 dlopen 动态加载”// dynamic_loader.zig - 运行时加载动态库const std = @import("std");
pub fn main() !void { // 加载动态库 const lib = try std.DynLib.open("libfizzbuzz.so"); defer lib.close();
// 获取函数指针 const fizzbuzz_fn = try lib.lookup(fn (u32) bool, "fizzbuzz"); const fibonacci_fn = try lib.lookup(fn (u32) u32, "fibonacci");
// 调用函数 const result1 = fizzbuzz_fn(15); const result2 = fibonacci_fn(10);
std.debug.print("fizzbuzz(15) = {}, fibonacci(10) = {}\n", .{ result1, result2 });}5.2 平台无关的动态加载封装
Section titled “5.2 平台无关的动态加载封装”const Platform = struct { const DynLib = switch (@import("builtin").os.tag) { .windows => struct { handle: std.os.windows.HMODULE,
pub fn open(path: []const u8) !@This() { const handle = std.os.windows.LoadLibraryA(path.ptr); if (handle == null) return error.LoadFailed; return @This(){ .handle = handle.? }; }
pub fn lookup(self: @This(), comptime T: type, name: []const u8) !T { const addr = std.os.windows.GetProcAddress(self.handle, name.ptr); if (addr == null) return error.SymbolNotFound; return @ptrCast(addr.?); } }, else => struct { // Unix-like handle: *anyopaque,
pub fn open(path: []const u8) !@This() { const handle = std.c.dlopen(path.ptr, std.c.RTLD_LAZY); if (handle == null) return error.LoadFailed; return @This(){ .handle = handle.? }; }
pub fn lookup(self: @This(), comptime T: type, name: []const u8) !T { const addr = std.c.dlsym(self.handle, name.ptr); if (addr == null) return error.SymbolNotFound; return @ptrCast(addr.?); } }, };};6. 跨语言调用动态库
Section titled “6. 跨语言调用动态库”6.1 C 语言调用 Zig 动态库
Section titled “6.1 C 语言调用 Zig 动态库”// ziglib.zig - 供 C 调用的动态库const std = @import("std");
// 导出 C 兼容函数export fn add_numbers(a: c_int, b: c_int) c_int { return a + b;}
export fn reverse_string(str: [*:0]const u8) [*:0]u8 { const len = std.mem.len(str); const result = std.heap.c_allocator.alloc(u8, len + 1) catch return null;
for (0..len) |i| { result[i] = str[len - i - 1]; } result[len] = 0;
return @ptrCast(result.ptr);}
export fn free_string(ptr: [*:0]u8) void { const len = std.mem.len(ptr); const slice = ptr[0..len:0]; std.heap.c_allocator.free(slice);}C 语言调用代码:
#include <stdio.h>#include <stdlib.h>
// 声明 Zig 函数extern int add_numbers(int a, int b);extern char* reverse_string(const char* str);extern void free_string(char* ptr);
int main() { // 调用加法函数 int sum = add_numbers(10, 20); printf("Sum: %d\n", sum);
// 调用字符串反转函数 char* reversed = reverse_string("Hello World"); printf("Reversed