Embed Ruby¶ ↑
: subtitle
アプリケーションへの\n Rubyインタープリターの組み込み
: author
須藤功平
: institution
株式会社クリアコード
: content-source
東京Ruby会議11
: date
2016-05-28
: allotted-time
25m
: theme
clear-code
Speaker’s award¶ ↑
Continuous development award
受賞者¶ ↑
((‘tag:center’)) ((‘tag:x-large’)) cedlemo
((‘tag:center’)) ((‘note:Continuous development award’))
受賞理由¶ ↑
((‘tag:center’)) ((‘tag:large’)) 2015年1月から継続的にn Ruby-GNOME2の開発にn 参加しているから
((‘tag:center’)) ((‘note:一発すごい改善をした人よりも’))n ((‘note:地味でも継続的に改善している人を評価したい’))
((‘tag:center’)) ((‘note:Continuous development award’))n ((‘note:cedlemo’))
宣伝¶ ↑
OSS Gate
OSS Gate¶ ↑
OSS開発にn 参加する人をn 増やす取り組み
背景¶ ↑
* OSS利用は当たり前になった * →OSS利用者増加 * (('wait'))開発参加者も増えるといいな * →OSS増加
((‘tag:center’)) ((‘note:OSS Gate’))
OSS開発参加¶ ↑
* すごい改善じゃなくていい * (('wait'))バグレポートとかでいい * typo見つけました!とかでもいい * サンプルを更新とかでもいい
((‘tag:center’)) ((‘note:OSS Gate’))
OSS Gate参加の動機¶ ↑
((‘tag:center’)) ((‘tag:large’)) 人それぞれでいい
((‘tag:center’)) ((‘note:OSS Gate’))
私の動機¶ ↑
* ユーザーが自由に使える\n ソフトウェアが増えるといいな * 自由に使える例: * (('wait'))コードを読んで学習できる * (('wait'))今日聞いた話の実装を確認できる!
((‘tag:center’)) ((‘note:OSS Gate’))
興味ある?¶ ↑
* 興味?(重要:動機不問) * OSS開発に参加したい! * OSS開発参加者を増やしたい! * (('wait'))5階でワークショップ開催中 * 説明や見学は私に一声かけて
((‘tag:center’)) ((‘note:OSS Gate’))
本題¶ ↑
Rubyの組み込みn ((‘note:(CアプリケーションへのRubyインタープリターの組み込み)’))
動機¶ ↑
* 柔軟な記述力が欲しい * (('wait'))Cの速さが欲しい * (('wait'))Ruby以外の言語とも連携したい * Pythonも組み込む * (('wait'))なんかカッコいい
((‘tag:center’)) ((‘note:Rubyの組み込み’))
別の実現方法¶ ↑
((‘tag:center’)) ((‘tag:large’)) 拡張ライブラリー
((‘tag:center’)) ((‘note:Rubyの組み込み’))
組み込み((‘note:と’))拡張ライブラリー¶ ↑
# image # src = images/embed-and-extension.svg # relative_width = 90
((‘tag:center’)) ((‘note:Rubyの組み込み’))
拡張ライブラリー¶ ↑
* 実現可能: * (('wait'))柔軟な記述力が欲しい * (('wait'))Cの速さが欲しい * 実現不可能: * (('wait'))Ruby以外の言語とも連携したい * (('wait'))なんかカッコいい感
((‘tag:center’)) ((‘note:Rubyの組み込み’))
実現方法の選び方¶ ↑
* (('wait'))基本は拡張ライブラリー * (('wait'))すでにあるアプリなら組み込み * (('wait'))選びたい方があるならそっち
((‘tag:center’)) ((‘note:Rubyの組み込み’))
組み込みを選ぶ時の注意¶ ↑
((‘tag:center’)) ((‘wait’))それなりの覚悟が必要
* (('wait'))利用例があまりない * 問題遭遇確率が高い * (('wait'))問題遭遇時: * 自分でソースを読んで調べる * 詳しい人に相談\n (('note:ささださん<できればサポートを強化したい'))
((‘tag:center’)) ((‘note:Rubyの組み込み’))
組み込み方を紹介¶ ↑
実例n milter managern ((‘note:Since 2008’))
milter manager¶ ↑
# image # src = images/milter-manager-overview.svg # relative_width = 85
((‘wait’)) ((‘tag:center’)) milterを管理するmiltern ((‘note:サーバープロセス’))
Ruby組み込みの実装¶ ↑
* 初期化 * fork対応 * イベントループとシグナル
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
初期化:GC関連¶ ↑
# coderay c { /* スタックの底を設定 */ /* GC時にCのローカル変数に代入されている Rubyのオブジェクトをマークするため */ RUBY_INIT_STACK; /* ... */ }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
スタックとマーク対象¶ ↑
# coderay c { RUBY_INIT_STACK; /* ... */ { VALUE object = rb_ary_new(); /* マーク対象 */ } } { VALUE object = rb_ary_new(); /* マーク対象外 */ }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
確認例¶ ↑
# coderay c #define MARKED_P(object) rb_objspace_marked_object_p(object) { RUBY_INIT_STACK; { VALUE object = rb_ary_new(); /* GC.start(immediate_sweep: false) */ printf("%d\n", MARKED_P(object)); /* => 1 */ } } { VALUE object = rb_ary_new(); /* GC.start(immediate_sweep: false) */ printf("%d\n", MARKED_P(object)); /* => 0 */ }
((‘tag:center’)) ((‘note:スライドのリポジトリー:examples/gc.c’))
GC関連の注意¶ ↑
# coderay c { RUBY_INIT_STACK; /* Rubyのオブジェクトを触る Cのコードはこのブロック内でだけ使うこと */ /* Cからのコールバックで Rubyのコードを呼び出すときは注意 */ }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
初期化:シグナル関連¶ ↑
# coderay c { RUBY_INIT_STACK; /* シグナルハンドラーを保存 */ ruby_init(); /* Rubyがシグナルハンドラーを登録 */ /* シグナルハンドラーを復帰 */ /* シグナルはアプリで処理したいから */ }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
シグナル復帰例¶ ↑
# coderay c { /* 他のシグナルも同様に復帰 */ void (*sigint_handler)(int); sigint_handler = signal(SIGINT, SIG_DFL); ruby_init(); signal(SIGINT, sigint_handler); }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
初期化:引数の処理¶ ↑
# coderay c { /* ...ruby_init()... */ static char *argv_raw[] = {"milter-manager", "-e;"}; int argc; char **argv; argc = sizeof(argv_raw) / sizeof(char *); argv = argv_raw; ruby_incpush(/* ... */); /* $LOAD_PATHの設定 */ /* 中でいろいろ初期化するのでダミーの引数で呼ぶ */ ruby_process_options(argc, argv); }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
初期化:アプリの初期化¶ ↑
# coderay c { /* ...ruby_process_options()... */ /* require中に例外が発生してもここで止める */ /* ここで止めないと例外を受け取る人がいなくて クラッシュ */ rb_protect(/* rb_require("milter/manager") */); }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
milter managerとRuby¶ ↑
* 組み込み処理系の1つ * Pythonも使えるようにしたかった\n (('note:結局Ruby必須でPython対応はしなかった')) * (('wait'))起動後に(({dlopen()}))で動的にsoを読み込んで組み込み
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
起動時に動的に組み込み¶ ↑
# image # src = images/milter-manager-and-ruby.svg # relative_width = 90
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
起動時にso読んで組み込み¶ ↑
# coderay c { /* ↓GC用 */ RUBY_INIT_STACK; /* 動的にsoを読んで初期化関数を呼ぶ */ /* dlopen(); init = dlsym(); init(); ←の中でruby_init();とか */ /* アプリの処理 */ }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
RUBY_INIT_STACK!?¶ ↑
# coderay c { /* ↓アプリ側で呼ぶの!? */ RUBY_INIT_STACK; /* 動的にsoを読んで初期化関数を呼ぶ */ /* dlopen(); init = dlsym(); init(); ←の中でruby_init();とか */ /* アプリの処理 */ }
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
アプリにーlruby…¶ ↑
# image # src = images/milter-manager-and-ruby-real.svg # relative_width = 90
((‘tag:center’)) ((‘note:カッコわるい。。。’))n ((‘note:milter managerへのRubyの組み込み’))
Ruby組み込み時の意気込み¶ ↑
* 本体に組み込む * 動的に組み込もうとしない
Ruby組み込みの実装¶ ↑
* (('del:初期化')) * fork対応 * イベントループとシグナル
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
milter manager利用例¶ ↑
* (('wait'))大学・企業 * ユーザー数:数百〜数万人 * (('wait'))プロバイダー * ユーザー数:数千〜数十万人
((‘wait’)) ((‘tag:center’)) それなりの性能が必要
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
性能向上方法¶ ↑
* CPU * マルチプロセス1択 * 通信・多同時接続 * いろいろ
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
マルチプロセス¶ ↑
* (('wait'))マスタープロセス (1) (({listen()})) (2) (({fork()})) * (('wait'))ワーカープロセス (1) (({accept()})) (2) ↑したクライアントの処理
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
Ruby組み込みと(({fork()}))¶ ↑
* (({fork()}))すると\n ワーカープロセスがクラッシュ * プロセス終了時とか * (('wait'))ヒント:(({fork()}))とスレッド
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
(({fork()}))とスレッド¶ ↑
* 混ぜるな危険 * (('wait'))Rubyはスレッドを動かしている * 例:タイマースレッド * (('wait'))(({fork}))時にスレッドのケアが必要
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
スレッドのケア¶ ↑
# coderay c VALUE rb_pid; /* タイマースレッドの後始末とか した上でfork */ rb_pid = rb_funcall(rb_mKernel, rb_intern("fork"), 0); return NUM2INT(rb_pid);
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
Ruby組み込みの実装¶ ↑
* (('del:初期化')) * (('del:fork対応')) * イベントループとシグナル
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
イベントループとシグナル¶ ↑
* 気にしなくてよい * (('wait'))アプリがシグナルを処理するから * (('wait'))拡張ライブラリーなら対応必要 * イベントループ中にシグナル発生 * →すぐにイベントループを抜ける
((‘tag:center’)) ((‘note:milter managerへのRubyの組み込み’))
Rubyの組み込みのまとめ¶ ↑
* Rubyを組み込む実装方法を紹介 * (('wait'))動的組み込みは諦めろ * (('wait'))(({fork}))時はRubyの(({fork}))を使う
((‘tag:center’)) ((‘note:Rubyの組み込み’))
mrubyの組み込み¶ ↑
実例n Groongan ((‘note:Since 2013’))
Groongaとmruby¶ ↑
* Groonga * 全文検索エンジン(mruby組み込み) * (('wait'))高速に検索結果を返し続けたい * (('wait'))リソース消費は波がない方がよい\n (('note:例:いらなくなったメモリーはすぐに解放'))
((‘tag:center’)) ((‘note:mrubyの組み込み’))
リソース消費¶ ↑
# image # src = images/groonga-memory-usage.svg # relative_height = 90
((‘tag:center’)) ((‘note:mrubyの組み込み’))
メモリー管理¶ ↑
* Groonga * (('wait'))必要なときに確保 * (('wait'))いらなくなったら解放 * mruby * (('wait'))GC * (('wait'))メモリーが足りなくなったら解放
((‘tag:center’)) ((‘note:mrubyの組み込み’))
GroongaとmrubyのGC¶ ↑
* mrubyのGCにGroongaのリソース管理を任せない * リソース管理:\n mrubyのオブジェクトのsweep時にGroongaのリソースを解放
((‘tag:center’)) ((‘note:mrubyの組み込み’))
mrubyのGCとリソース¶ ↑
* mrubyのGC * Groongaリソースのサイズを知らない * 適切なタイミングでsweepできない * (('wait'))(('note:RubyのGCも同じ'))
((‘tag:center’)) ((‘note:mrubyの組み込み’))
実例1: 明示的な解放¶ ↑
# coderay ruby # 検索 result = table.search(condition) begin output_result(result) # 出力 ensure result.close # 明示的な解放 end
((‘tag:center’)) ((‘note:mrubyの組み込み’))
実例2: 所有権を渡さない¶ ↑
# coderay c /* Groonga側でリソース確保 */ expr = grn_expr_create(/* ... */); /* mrubyのオブジェクトとしてラップ */ mrb_expr = grn_mrb_value_from_grn_obj(mrb, expr); /* mruby側は参照して処理する */ mrb_size = mrb_funcall(mrb, mrb_expr, "estimate_size", 1, mrb_table); /* Groonga側でリソース解放 */ grn_expr_close(expr);
((‘tag:center’)) ((‘note:mrubyの組み込み’))
mruby組み込みのまとめ¶ ↑
* GCに任せないという選択 * 使用メモリー量を安定させるため * 安定した性能を出すため
((‘tag:center’)) ((‘note:mrubyの組み込み’))
まとめ¶ ↑
* Rubyの組み込み * (('wait'))ガッツリ連携するつもりで設計\n (('note:拡張ライブラリーで十分じゃないかよく検討すること')) * mrubyの組み込み * (('wait'))アプリの大事な事を忘れないで設計 * (('wait'))OSS Gateもよろしく