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

时间:2015-08-27 00:20:40

标签: python numpy segmentation-fault rust ffi

我有一个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来调用它们

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

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()时出现了分段错误。

经过一番调查后,我可以说以下内容:

  1. println!()myotherfunction()中的任意位置添加ffi_myotherfunction()宏,可使该功能正常运行。输出符合预期。
  2. 使用任何整数大小的锈库segfaults(尝试了8,16,32和64位整数)。
  3. 段错误似乎不是来自myotherfunction(),而是来自Vec::from_raw_parts()的来电。例如,我可以从myotherfunction()注释掉对ffi_myotherfunction()的调用,只留下不安全的块,并且仍会发生段错误。
  4. 因此slice::from_raw_parts_mut()Vec::from_raw_parts()之间似乎存在差异。

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

    谢谢!

1 个答案:

答案 0 :(得分:5)

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

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

因此,第一个问题是Vec正在运行析构函数;可以通过在完成后调用std::mem::forget(to_sort)进行修改。另一个问题是任何重新分配都会崩溃,这个问题要危险得多;你已经基本上做到了你不能安全地访问你的向量,并且必须非常谨慎对待你做的任何事情。实际上,您应该假设Vec上的所有都会导致崩溃。如果您想要的话可​​以使用&mut [T],那么您应该使用function send_download($file) { $basename = basename($file); $length = sprintf("%u", filesize($file)); header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $basename . '"'); header('Content-Transfer-Encoding: binary'); header('Connection: Keep-Alive'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); header('Content-Length: ' . $length); set_time_limit(0); readfile($file); }