【问题标题】:Accessing static C functions in DPDK from Rust FFI从 Rust FFI 访问 DPDK 中的静态 C 函数
【发布时间】:2016-10-19 08:59:56
【问题描述】:

我是一个相当新手的程序员,我遇到了一个我认为我理解但不知道如何解决的问题。我正在尝试使用 Rust FFI 与 C 中的 Intel's DPDK 进行交互。我的第一次尝试是重新创建 helloworld 示例应用程序。

我遇到了一个编译错误,我认为这是由于 DPDK 中的函数是静态的,不能直接从库中获得。我的 FFI 接口在这里:

use libc::{c_uint, c_int, c_void, c_char};

pub type LcoreFunctionT =
    extern "C" fn(arg1: *mut c_void) -> c_int;

extern {
    pub fn rte_eal_init(argc: c_int,
                        argv: *mut *mut c_char) -> c_int;
    pub fn rte_eal_remote_launch(f: *mut LcoreFunctionT,
                                    arg: *mut c_void,
                                    slave_id: c_uint) -> c_int;
    pub fn rte_eal_mp_wait_lcore() -> ();
    pub fn rte_lcore_id() -> c_uint;
    pub fn rte_get_next_lcore(i: c_uint,
                                skip_master: c_int,
                                wrap: c_int) -> c_uint;
}

我还有一个引用它并包装函数的库:

extern crate libc;

use libc::{c_uint, c_int, c_char, c_void};
use std::ffi::CString;
use std::ptr;

mod ffi_rte_eal;

pub fn dpdk_rte_eal_init(argc: i32, argv: Vec<String>) -> i32 {
    let mut args: Vec<*mut c_char> =
        argv.iter().map(|x| CString::new(x.clone()).unwrap().into_raw()).collect();
    let retc: c_int = unsafe {ffi_rte_eal::rte_eal_init(argc as c_int, args.as_mut_ptr())};
    let ret: i32 = retc as i32;
    ret
}

pub fn dpdk_rte_eal_remote_launch(f: extern "C" fn(*mut c_void) -> i32,
                                    slave_id: u32 ) -> i32 {
    let mut fc: ffi_rte_eal::LcoreFunctionT = f;
    let retc: c_int = unsafe {ffi_rte_eal::rte_eal_remote_launch(&mut fc,
                                                                ptr::null_mut() as *mut c_void,
                                                                slave_id as c_uint)};
    let ret: i32 = retc as i32;
    ret
}

pub fn dpdk_rte_eal_mp_wait_lcore() -> (){
    unsafe {
        ffi_rte_eal::rte_eal_mp_wait_lcore();
    }
}

pub fn dpdk_rte_lcore_id() -> u32 {
    let retc: c_uint = unsafe {ffi_rte_eal::rte_lcore_id()};
    let ret: u32 = retc as u32;
    ret
}

pub fn dpdk_rte_get_next_lcore(i: u32,
                                skip_master: i32,
                                wrap: i32) -> u32 {
    let retc: c_uint = unsafe {ffi_rte_eal::rte_get_next_lcore(i as c_uint,
                                                               skip_master as c_int,
                                                               wrap as c_int)};
    let ret: u32 = retc as u32;
    ret
}

还有一个用于链接库的 build.rs 文件 -

//build.rs

fn main() {
    println!("cargo:rustc-link-lib=static=rte_eal");
    println!("cargo:rustc-link-search=native=/usr/local/lib");
    println!("cargo:rustc-link-lib=static=rte_mempool");
    println!("cargo:rustc-link-search=native=/usr/local/lib");
    println!("cargo:rustc-link-lib=static=rte_ring");
    println!("cargo:rustc-link-search=native=/usr/local/lib");
}

当我尝试针对 FFI 接口编译我自己的应用程序时,我不断收到关于未定义引用 rte_lcore_idrte_get_next_lcore 的错误。根据 DPDK 的 API 文档,这些函数是 librte_eal 库的一部分,但属于 defined in rte_lcore.h as static functions。我假设这些是静态函数,我将无法从 Rust 中看到它们。

在与 DPDK 捆绑的 helloworld 示例应用程序中,它们直接导入 rte_lcore.h。我认为这就是为什么他们可以访问这些函数而无需仅引用 librte_eal?

是否有在 Rust 中访问它的方法,或者在 C 中是否必须有类似 shim 的东西让我包装这些函数并通过 FFI 使它们可用?

【问题讨论】:

  • 棘手。正如您所猜测的,static 函数的问题在于它们不应该在当前编译单元之外使用,因此即使没有内联它们的符号也可以是私有的(并且在库之外无法访问)。

标签: c rust ffi dpdk


【解决方案1】:

如您所见,如果您打开相应的头文件,这些函数会直接在那里声明。这意味着这些函数将包含在 每个 .c/.cpp 包含此标头的文件中,但由于它们是 static,链接器不会为它们创建符号,所以它们实际上并不存在于库的编译版本中。 Here 描述了为什么需要这样做,但不幸的是这样的设计对 FFI 不太友好。

您可以做的是创建一个存根 C 库,其中包含完全相同的函数,这些函数从标头委托给静态函数,但它们本身不是静态的。像这样:

#include <rte_lcore.h>

unsigned my_rte_lcore_count(void) {
    return rte_lcore_count();
}

// and so forth for every function you need

然后,您可以使用自定义编写的 Makefile 或 Cargo build script 将此存根文件编译为静态库,并将最终程序链接到它。然后,很自然地,您应该在 extern 块中编写这些函数,而不是原来的:

extern {
    fn my_rte_lcore_count() -> libc::c_uint;
}

我认为没有更简单更正确的方法了。

更新:哦,我没有注意到你在问题中的最后一句话。是的,你是对的,写这样一个 shim 才是正确的做法。

【讨论】:

  • 你认为函数调用开销/缺少内联可能会对性能产生负面影响,而不是重写 unsafe Rust 中的函数?
  • 这是有道理的。我虽然像你提到的那样编写存根库,但不知道这是否是“正确”的方法。还在学习中。
  • @MatthieuM.,这自然在很大程度上取决于如何使用这些函数。如果它们很少被调用,我希望这实际上是像 core_count() 这样的函数的情况,应该不会有任何明显的影响。
  • @MatthieuM.,我还看到这些函数,至少其中一些,调用其他 C 宏,所以我认为仍然可能需要一些 shim 来正确扩展这些宏,即使你在 Rust 中重新实现它们。
  • 非常感谢弗拉基米尔,我能够将一个简单的存根文件库放在一起,现在一切正常。感谢您的回复!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
  • 2021-08-29
相关资源
最近更新 更多