Rust-DPDK
Oct 6, 2017
Masaru OKI @masaru0714
Rust?
● ラスト
● システムプログラミング向けに新しく開発されたプログラミング言語。
● http://rust-lang.org/
● スクリプト言語ではなく、ネイティブコードにコンパイルする。
● Rustで書かれたOSというのがある模様。
○ RedoxOS https://www.redox-os.org/
○ intermezzOS https://intermezzos.github.io/
● ツール類の更新等にはrustupコマンドを用いる。
● C/C++ライブラリを呼び出すbinding libraryを生成するツールがある。
● 通常、パッケージ(crate)管理システム (cargo) と連動して動作する。
● 通常、ビルド等にはcargoコマンドを使う (コンパイラ本体はrustc)
● 立ち位置的にはGo言語に近い雰囲気
Rustのインストール
● curl https://sh.rustup.rs -sSf | sh
● $HOME/.cargo/binにインストールされる。
● 必要な環境変数は . $HOME/.cargo/env の実行で設定される。
● sudo不要。コンパイラとかユーザーごと。
● Jenkinsでビルドさせるときは~jenkins/.cargoが必要になる。
● 通常はstable(安定版)がインストールされる。
● beta(ベータ版)をインストールするにはrustup install beta
● nightly(開発版)をインストールするにはrustup install nightly
● RustでC用のTLSを扱うには現時点ではnightly buildが必要。
Rustのプログラム例
fn main () {
let mut a = 1; // 変更可能
let b = 2;
println!(“hello, world. a = {}, b = {}”, a, b),;
a = a + b;
println!(“a + b = {}”, a);
}
● ファンクションの宣言や定義は fn
● 処理ブロックの範囲は { … }
● 各行の区切りはセミコロン
● 通常の変数はimmutable(変更不可)、変更する変数には mut をつける
Rustプログラムのコンパイルと実行
% cd hello
% rustc ./src/main.rs # 実行バイナリ./mainが生成される
% cat Cargo.toml
[package]
name = “hello”
version = “0.0.1”
% cargo run # コンパイルして即実行
% cargo build # target/debug/helloが生成される(実行しない)
% cargo test # テストコードを実行
% cargo clean # target/を削除
言語の詳細
書き始めるときりがないので、下記を参照願います
● The Rust Programming Language
● Rust by Example
C言語用ライブラリをRustで使う
● Rust側で extern “C” で宣言しておけば呼び出せる。
○ Foeign Function Interface (FFI) と呼ばれる。
● 皆がよく使うライブラリはcrateになっていることもある。
extern crate libc;
…
let fp = libc::fopen(...);
● bindgenというツールが提供されていて、これを使えば自作できる
○ C言語ライブラリのヘッダファイルを読み、 structやプロトタイプ宣言等を Rustに変換する
○ 実行ファイルとしても提供されているが、 Rustのcrateとしても提供されている
○ 変換手順をRustで記述できる
○ 自作したライブラリは crateとして呼び出せるようになる
bindgenの変換例
C
typedef unsigned short int sa_family_t;
struct sockaddr
{
__SOCKADDR_COMMON (sa_);
char sa_data[14];
};
extern int bind (int __fd, __CONST_SOCKADDR_ARG
__addr, socklen_t __len)
__THROW;
Rust
pub type sa_family_t = ::std::os::raw::c_ushort;
#[repr(C)]
#[derive(Debug, Copy)]
pub struct sockaddr {
pub sa_family: sa_family_t,
pub sa_data: [::std::os::raw::c_char; 14usize],
}
extern "C" {
pub fn bind(__fd: ::std::os::raw::c_int,
__addr: *const sockaddr,
__len: socklen_t) ->
::std::os::raw::c_int;
}
bindgenの制限
● Cのヘッダで定義されるinline関数は対象外
○ inline関数は単純にスキップされる
○ --generate-inline-functions指定はあるがまともに動かない
○ DPDKはヘッダで定義する inline関数を多用しているので単純変換だけだとまず困る
○ inline関数を使わないようにして C libraryをrebuildしろとか言われる
○ きれいで手のかからない解決策 : なし
○ 自作というか改造板の Rust-DPDKでは、当該コード(の一部)を Rustで再実装した
○ Rustのfnの中でinline関数を呼ぶだけ、というコードを用意している実装もあった
● C static libraryはうまくbinding libraryにできない
○ 一見できていても、リンク時に「 -fPICで作り直せ」と怒られる
■ crate-type=”staticlib”もだめだった
○ Webで探しても、C側をstaticじゃなくする方法ばかりが見つかる
○ いろいろ試してみた限り、あきらめるしかないようだった
DPDK?
● Data Plane Development Kit
● http://dpdk.org/
● 高速なパケットI/O機能を提供する、C言語用ライブラリ。
● 基本的にLinux向け。FreeBSDでは一部機能が提供されないが動く。
DPDKのプログラム例(C言語)
int
hello(void *arg) {
printf(“hello lcore %dn”, rte_lcore_id());
return 0;
}
int
main(int argc, char *argv[]) {
rte_eal_init(argc, argv);
rte_eal_mp_remote_launch(hello, NULL, CALL_MASTER);
rte_eal_mp_wait_lcore();
return 0;
}
悪魔合体!
RustによるDPDKのバインディングはすでにいくつか実装がある。
● https://github.com/libpnet/rust-dpdk
● https://github.com/flier/rust-dpdk
● https://github.com/ANLAB-KAIST/rust-dpdk
● bindgenで変換しただけだったり
● 動くようだけどサンプルのl2fwdを見るとスレッド関数はCだったり
● サンプルがなくてどう書くのか謎だったり
● 更新が止まっていて新しいDPDKと組み合わせられるか謎だったり
しかたがないので、変換しただけのをベースに自分でいじくったものを用意。
● https://github.com/iMasaruOki/rust-dpdk
Rustで書くとこう
extern crate rust_dpdk;
use rust_dpdk::dpdk;
unsafe extern "C" fn hello_thread(_arg: *mut std::os::raw::c_void) -> i32 {
println!("Hello! lcore {}", dpdk::rte_lcore_id());
0
}
fn main() {
unsafe {
let _ = dpdk::eal_init(std::env::args());
let callback: dpdk::lcore_function_t = Some(hello_thread);
let callback_arg: *mut std::os::raw::c_void = std::mem::zeroed();
dpdk::rte_eal_mp_remote_launch(callback,
callback_arg,
dpdk::rte_rmt_call_master_t::CALL_MASTER);
dpdk::rte_eal_mp_wait_lcore();
}
}
まだRustらしくないので
もう少し手直しする予定
(APIを含めて)
手直し中のもの
extern crate dpdk;
use std::os::raw::c_void;
unsafe extern "C" fn hello_thread(_arg: *mut c_void) -> i32 {
println!("Hello! lcore {}", dpdk::lcore::id());
0
}
fn main() {
unsafe {
let _ = dpdk::eal::init(std::env::args());
let callback_arg: *mut c_void = std::mem::zeroed();
dpdk::eal::mp_remote_launch(hello_thread, callback_arg, true);
dpdk::eal::mp_wait_lcore();
}
}
Rust-DPDK(iMasaruOki版)現在のステータス
● DPDK 17.08対応
● マルチコア動作を確認
● l2fwd相当のサンプルを作成し、パケット転送できることを確認
● 動かせることが分かって、ひとまず満足

Rust-DPDK

  • 1.
  • 2.
    Rust? ● ラスト ● システムプログラミング向けに新しく開発されたプログラミング言語。 ●http://rust-lang.org/ ● スクリプト言語ではなく、ネイティブコードにコンパイルする。 ● Rustで書かれたOSというのがある模様。 ○ RedoxOS https://www.redox-os.org/ ○ intermezzOS https://intermezzos.github.io/ ● ツール類の更新等にはrustupコマンドを用いる。 ● C/C++ライブラリを呼び出すbinding libraryを生成するツールがある。 ● 通常、パッケージ(crate)管理システム (cargo) と連動して動作する。 ● 通常、ビルド等にはcargoコマンドを使う (コンパイラ本体はrustc) ● 立ち位置的にはGo言語に近い雰囲気
  • 3.
    Rustのインストール ● curl https://sh.rustup.rs-sSf | sh ● $HOME/.cargo/binにインストールされる。 ● 必要な環境変数は . $HOME/.cargo/env の実行で設定される。 ● sudo不要。コンパイラとかユーザーごと。 ● Jenkinsでビルドさせるときは~jenkins/.cargoが必要になる。 ● 通常はstable(安定版)がインストールされる。 ● beta(ベータ版)をインストールするにはrustup install beta ● nightly(開発版)をインストールするにはrustup install nightly ● RustでC用のTLSを扱うには現時点ではnightly buildが必要。
  • 4.
    Rustのプログラム例 fn main (){ let mut a = 1; // 変更可能 let b = 2; println!(“hello, world. a = {}, b = {}”, a, b),; a = a + b; println!(“a + b = {}”, a); } ● ファンクションの宣言や定義は fn ● 処理ブロックの範囲は { … } ● 各行の区切りはセミコロン ● 通常の変数はimmutable(変更不可)、変更する変数には mut をつける
  • 5.
    Rustプログラムのコンパイルと実行 % cd hello %rustc ./src/main.rs # 実行バイナリ./mainが生成される % cat Cargo.toml [package] name = “hello” version = “0.0.1” % cargo run # コンパイルして即実行 % cargo build # target/debug/helloが生成される(実行しない) % cargo test # テストコードを実行 % cargo clean # target/を削除
  • 6.
  • 7.
    C言語用ライブラリをRustで使う ● Rust側で extern“C” で宣言しておけば呼び出せる。 ○ Foeign Function Interface (FFI) と呼ばれる。 ● 皆がよく使うライブラリはcrateになっていることもある。 extern crate libc; … let fp = libc::fopen(...); ● bindgenというツールが提供されていて、これを使えば自作できる ○ C言語ライブラリのヘッダファイルを読み、 structやプロトタイプ宣言等を Rustに変換する ○ 実行ファイルとしても提供されているが、 Rustのcrateとしても提供されている ○ 変換手順をRustで記述できる ○ 自作したライブラリは crateとして呼び出せるようになる
  • 8.
    bindgenの変換例 C typedef unsigned shortint sa_family_t; struct sockaddr { __SOCKADDR_COMMON (sa_); char sa_data[14]; }; extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len) __THROW; Rust pub type sa_family_t = ::std::os::raw::c_ushort; #[repr(C)] #[derive(Debug, Copy)] pub struct sockaddr { pub sa_family: sa_family_t, pub sa_data: [::std::os::raw::c_char; 14usize], } extern "C" { pub fn bind(__fd: ::std::os::raw::c_int, __addr: *const sockaddr, __len: socklen_t) -> ::std::os::raw::c_int; }
  • 9.
    bindgenの制限 ● Cのヘッダで定義されるinline関数は対象外 ○ inline関数は単純にスキップされる ○--generate-inline-functions指定はあるがまともに動かない ○ DPDKはヘッダで定義する inline関数を多用しているので単純変換だけだとまず困る ○ inline関数を使わないようにして C libraryをrebuildしろとか言われる ○ きれいで手のかからない解決策 : なし ○ 自作というか改造板の Rust-DPDKでは、当該コード(の一部)を Rustで再実装した ○ Rustのfnの中でinline関数を呼ぶだけ、というコードを用意している実装もあった ● C static libraryはうまくbinding libraryにできない ○ 一見できていても、リンク時に「 -fPICで作り直せ」と怒られる ■ crate-type=”staticlib”もだめだった ○ Webで探しても、C側をstaticじゃなくする方法ばかりが見つかる ○ いろいろ試してみた限り、あきらめるしかないようだった
  • 10.
    DPDK? ● Data PlaneDevelopment Kit ● http://dpdk.org/ ● 高速なパケットI/O機能を提供する、C言語用ライブラリ。 ● 基本的にLinux向け。FreeBSDでは一部機能が提供されないが動く。
  • 11.
    DPDKのプログラム例(C言語) int hello(void *arg) { printf(“hellolcore %dn”, rte_lcore_id()); return 0; } int main(int argc, char *argv[]) { rte_eal_init(argc, argv); rte_eal_mp_remote_launch(hello, NULL, CALL_MASTER); rte_eal_mp_wait_lcore(); return 0; }
  • 12.
    悪魔合体! RustによるDPDKのバインディングはすでにいくつか実装がある。 ● https://github.com/libpnet/rust-dpdk ● https://github.com/flier/rust-dpdk ●https://github.com/ANLAB-KAIST/rust-dpdk ● bindgenで変換しただけだったり ● 動くようだけどサンプルのl2fwdを見るとスレッド関数はCだったり ● サンプルがなくてどう書くのか謎だったり ● 更新が止まっていて新しいDPDKと組み合わせられるか謎だったり しかたがないので、変換しただけのをベースに自分でいじくったものを用意。 ● https://github.com/iMasaruOki/rust-dpdk
  • 13.
    Rustで書くとこう extern crate rust_dpdk; userust_dpdk::dpdk; unsafe extern "C" fn hello_thread(_arg: *mut std::os::raw::c_void) -> i32 { println!("Hello! lcore {}", dpdk::rte_lcore_id()); 0 } fn main() { unsafe { let _ = dpdk::eal_init(std::env::args()); let callback: dpdk::lcore_function_t = Some(hello_thread); let callback_arg: *mut std::os::raw::c_void = std::mem::zeroed(); dpdk::rte_eal_mp_remote_launch(callback, callback_arg, dpdk::rte_rmt_call_master_t::CALL_MASTER); dpdk::rte_eal_mp_wait_lcore(); } } まだRustらしくないので もう少し手直しする予定 (APIを含めて)
  • 14.
    手直し中のもの extern crate dpdk; usestd::os::raw::c_void; unsafe extern "C" fn hello_thread(_arg: *mut c_void) -> i32 { println!("Hello! lcore {}", dpdk::lcore::id()); 0 } fn main() { unsafe { let _ = dpdk::eal::init(std::env::args()); let callback_arg: *mut c_void = std::mem::zeroed(); dpdk::eal::mp_remote_launch(hello_thread, callback_arg, true); dpdk::eal::mp_wait_lcore(); } }
  • 15.
    Rust-DPDK(iMasaruOki版)現在のステータス ● DPDK 17.08対応 ●マルチコア動作を確認 ● l2fwd相当のサンプルを作成し、パケット転送できることを確認 ● 動かせることが分かって、ひとまず満足