0
点赞
收藏
分享

微信扫一扫

Rust FFI 编程 - Rust导出共享库05

夕颜合欢落 2022-06-29 阅读 41

本章,我们继续来玩耍跨Rust 和 C FFI边界的指针。

设计

本节,我们设计这样一个示例:

示例 1

  1. Rust 导出一个 so 库
  2. main 函数在 C 这边,链接 Rust 的 so 库
  3. C 中分配栈内存,交由 Rust 端填充
  4. Rust 端打印
  5. C 端打印

示例 2

  1. 同样的示例,C 中分配堆内存,交由 Rust 端填充,并且两边分别打印。

下面我们直接看示例1的代码。

示例 1 代码

Rust 端。

// src/lib.rs

use std::os::raw::c_int;
use std::slice;

#[repr(C)]
#[derive(Debug)]
pub struct Student {
pub num: c_int,
pub total: c_int,
}

#[no_mangle]
pub extern "C" fn fill_students(p_stu: *mut Student, n: c_int) {
assert!(!p_stu.is_null());
let s: &mut [Student] = unsafe { slice::from_raw_parts_mut(p_stu, n as usize) };
for elem in s.iter_mut() {
// fill any valid values
elem.num = 1 as c_int;
elem.total = 100 as c_int;
}
}

#[no_mangle]
pub extern "C" fn print_students(p_stu: *mut Student, n: c_int) {
assert!(!p_stu.is_null());
let s: &[Student] = unsafe { slice::from_raw_parts(p_stu, n as usize) };
for elem in s.iter() {
println!("print in rust side: {:?}", elem);
}
}

记得 Cargo.toml 加上:

[lib]
crate-type = ["cdylib"]

C 端

// csrc/cfoo1.c

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>


typedef struct Students {
int num; // serial number
int total; // total score
} Student;

extern void fill_students(Student *stu, int);
extern void print_students(Student *stu, int);

void print_students_c(Student *stu, int n) {
int i;
for (i=0; i<n; i++) {
printf("C side print: %d %d\n", stu[i].num, stu[i].total);
}
}

void main() {
int len = 10;
Student students[len];

// call rust fill and print functions
fill_students(students, len);
print_students(students, len);

// call c print function
print_students_c(students, len);
}

C 端代码这样编译:

gcc -o ./cfoo1 ./cfoo1.c -L ./ -lrustffi4

(注意,我已经将 cargo build 生成的 librustffi4.so 文件从 target/debug/ 目录拷贝至 C 代码所在目录)

C 端二进制运行:

LD_LIBRARY_PATH=. ./cfoo1

结果如下:

print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100

可以看到,C的栈空间上分配的结构体数组,已经被Rust这边成功填充了。

感觉已经没什么可讲的了。看过本教程之前内容的同学,应该会秒懂。

接着来看示例2的代码。

示例2的代码

示例2的代码,Rust 这边没有变化。下面直接看 C 这边的代码:

// csrc/cfoo2.c

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>

typedef struct Students {
int num; // serial number
int total; // total score
} Student;

extern void fill_students(Student *stu, int);
extern void print_students(Student *stu, int);

Student* create_students(int n) {
if (n <= 0) return NULL;

Student *stu = NULL;
stu = (Student*) malloc(sizeof(Student)*n);

return stu;
}

void release_students(Student *stu) {
if (stu != NULL)
free(stu);
}

void print_students_c(Student *stu, int n) {
int i;
for (i=0; i<n; i++) {
printf("C side print: %d %d\n", stu[i].num, stu[i].total);
}
}

void main() {
int len = 10;
Student* students = create_students(len);

// call rust fill and print functions
fill_students(students, len);
print_students(students, len);

// call c print function
print_students_c(students, len);

release_students(students);
}

C 端代码这样编译:

gcc -o ./cfoo2 ./cfoo2.c -L ./ -lrustffi4

(注意,我已经将 cargo build 生成的 librustffi4.so 文件从 target/debug/ 目录拷贝至 C 代码所在目录)

C 端二进制运行:

LD_LIBRARY_PATH=. ./cfoo2

结果如下:

print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
print in rust side: Student { num: 1, total: 100 }
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100
C side print: 1 100

可以看到,两个示例打印结果完全一致。

示例2的C语言这边是在堆上 malloc 了一块内存,所以程序结束的时候,要记得 free 掉。

我们从两个示例的对比可以看到,C 这边栈和堆的指针,都可以用相同的 Rust 的代码。也就是说,Rust 这边,它就认 C 的指针,而不管这个指针是从哪里来,栈也好,堆也好,甚至其它地址的指针也好,对 Rust 来说,其实都一样(本质上都是内存指针)。

结论

本章通过构造两个示例,演示了 Rust导出共享库 的一个操作场景,例子清晰明了,可细品。

本章示例的所有代码,皆可在:​​https://github.com/daogangtang/learn-rust/tree/master/11rustffi4​​ 找到。


举报

相关推荐

0 条评论