= 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 manager\n (('note:Since 2008')) = milter manager # image # src = images/milter-manager-overview.svg # relative_width = 85 (('wait')) (('tag:center')) milterを管理するmilter\n (('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 Groonga\n (('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もよろしく