首页 文章

如何在传递int数组时修复分段错误?

提问于
浏览
2

我有一个Rust(1.2)库,我想使用Rust的FFI从Python(3.4)中使用它的函数 . 我在OSX 10.10优胜美地 .

几乎所有函数都将可变切片引用作为输入:

pub fn myfunction<T>(array: &mut [T]) { ... }

然后,我将此函数暴露在除锈之外:

#[no_mangle]
pub extern fn ffi_myfunction(array_pointer: *const libc::int8_t, n: libc::size_t) {
    assert!(!array_pointer.is_null());
    assert!(n != 0);
    let mut to_sort = unsafe {
        slice::from_raw_parts_mut(array_pointer as *mut i8, n as usize)
    };
    myfunction(&mut to_sort);
}

这很好用:使用python的ctypes模块我可以用numpy数组调用 ffi_myfunction()

#!/usr/bin/env python3

import ctypes
import numpy as np

rustlib = ctypes.CDLL("target/debug/libmylib.dylib")

array = np.arange(5, dtype=np.int8)

rustlib.ffi_myfunction(ctypes.c_void_p(array.ctypes.data), len(array))

我也有 libc::int16_tlibc::int32_tlibc::int64_t 的生锈实现,我可以用 np.int16np.int32np.int64 来调用它们 .

我有第二组Rust函数,我想从Python调用 . 这些函数略有不同,因为它们对向量(而不是切片)采用可变引用:

pub fn myotherfunction<T>(array: &mut Vec<T>) { ... }

然后我创建我的包装器:

#[no_mangle]
pub extern "C" fn ffi_myotherfunction(array_pointer: *const libc::int8_t, n: libc::size_t) {
    assert!(!array_pointer.is_null());
    assert!(n != 0);
    let mut to_sort = unsafe {
        Vec::from_raw_parts(array_pointer as *mut i8, n as usize, n as usize)
    };
    myotherfunction(&mut to_sort);
}

不幸的是,当从Python调用 ffi_myotherfunction() 时,我遇到了分段错误 .

经过一番调查,我可以说如下:

  • myotherfunction()ffi_myotherfunction() 中的任何位置添加 println!() 宏使该函数正常执行 . 输出符合预期 .

  • 使用任何整数大小的锈库segfaults(尝试了8,16,32和64位整数) .

  • segfault似乎不是来自 myotherfunction() ,而是来自 Vec::from_raw_parts() 的调用 . 例如,我可以从 ffi_myotherfunction() 注释掉对 myotherfunction() 的调用,只留下不安全的块,并且仍然会发生段错误 .

所以 slice::from_raw_parts_mut()Vec::from_raw_parts() 之间似乎有区别 .

但我无法理解导致该段错误的原因 . 我错过了什么吗?难道我做错了什么? numpy如何存储数据会有问题吗?或者也许是关于生命,借用或任何其他生锈概念的东西,我没有得到?

谢谢!

1 回答

  • 5

    您应该只使用 Vec::from_raw_parts 与Rust的分配器在Rust代码中分配的数据 . 其他任何事情都是不安全的 .

    我希望Python使用malloc,但Rust使用jemalloc . 如果jemalloc被指示释放一个未被jemalloc分配的地址,我相信它会崩溃 . 因此,如果向量被释放(即它超出范围,运行其析构函数)或者如果需要重新分配(例如,如果将元素推到其上),则会遇到崩溃 .

    因此,第一个问题是 Vec 正在运行析构函数;当你完成它时,可以通过调用 std::mem::forget(to_sort) 进行修改 . 另一个问题是任何重新分配都会崩溃,这个问题要危险得多;你基本上已经做到了你不能安全地访问你的向量,并且必须非常小心你用它做什么 . 真的,你应该假设 Vec 上的所有内容都会崩溃 . 你应该使用 &mut [T] ,如果你想要的话可以使用它 .

相关问题