Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

アプリケーションへのRubyインタープリターの組み込み

1,268 views

Published on

milter managerとGroongaを例にして、アプリケーションへのCRubyの組み込み・mrubyの組み込みの実装について説明します。

  • Be the first to comment

アプリケーションへのRubyインタープリターの組み込み

  1. 1. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Embed Ruby アプリケーションへの Rubyインタープリターの組み込み 須藤功平 株式会社クリアコード 東京Ruby会議11 2016-05-28
  2. 2. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Speaker's award Continuous development award
  3. 3. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 受賞者 cedlemo Continuous development award
  4. 4. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 受賞理由 2015年1月から継続的に Ruby-GNOME2の開発に 参加しているから 一発すごい改善をした人よりも 地味でも継続的に改善している人を評価したい Continuous development award cedlemo
  5. 5. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 宣伝 OSS Gate
  6. 6. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 OSS Gate OSS開発に 参加する人を 増やす取り組み
  7. 7. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 背景 OSS利用は当たり前になった →OSS利用者増加✓ ✓ 開発参加者も増えるといいな →OSS増加✓ ✓ OSS Gate
  8. 8. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 OSS開発参加 すごい改善じゃなくていい✓ バグレポートとかでいい typo見つけました!とかでもいい✓ サンプルを更新とかでもいい✓ ✓ OSS Gate
  9. 9. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 OSS Gate参加の動機 人それぞれでいい OSS Gate
  10. 10. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 私の動機 ユーザーが自由に使える ソフトウェアが増えるといいな ✓ 自由に使える例: コードを読んで学習できる✓ 今日聞いた話の実装を確認できる!✓ ✓ OSS Gate
  11. 11. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 興味ある? 興味?(重要:動機不問) OSS開発に参加したい!✓ OSS開発参加者を増やしたい!✓ ✓ 5階でワークショップ開催中 説明や見学は私に一声かけて✓ ✓ OSS Gate
  12. 12. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 本題 Rubyの組み込み (CアプリケーションへのRubyインタープリターの組み込み)
  13. 13. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 動機 柔軟な記述力が欲しい✓ Cの速さが欲しい✓ Ruby以外の言語とも連携したい Pythonも組み込む✓ ✓ なんかカッコいい✓ Rubyの組み込み
  14. 14. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 別の実現方法 拡張ライブラリー Rubyの組み込み
  15. 15. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 組み込みと拡張ライブラリー C Ruby Ruby C Ruby組み込み 拡張 ライブラリー Rubyの組み込み
  16. 16. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 拡張ライブラリー 実現可能: 柔軟な記述力が欲しい✓ Cの速さが欲しい✓ ✓ 実現不可能: Ruby以外の言語とも連携したい✓ なんかカッコいい感✓ ✓ Rubyの組み込み
  17. 17. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 実現方法の選び方 基本は拡張ライブラリー✓ すでにあるアプリなら組み込み✓ 選びたい方があるならそっち✓ Rubyの組み込み
  18. 18. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 組み込みを選ぶ時の注意 それなりの覚悟が必要 利用例があまりない 問題遭遇確率が高い✓ ✓ 問題遭遇時: 自分でソースを読んで調べる✓ 詳しい人に相談 ささださん<できればサポートを強化したい ✓ ✓ Rubyの組み込み
  19. 19. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 組み込み方を紹介 実例 milter manager Since 2008
  20. 20. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 milter manager メール サーバー milter manager メール フィルター メール フィルター メール フィルターmilter Ruby milterを管理するmilter サーバープロセス
  21. 21. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込みの実装 初期化✓ fork対応✓ イベントループとシグナル✓ milter managerへのRubyの組み込み
  22. 22. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 初期化:GC関連 { /* スタックの底を設定 */ /* GC時にCのローカル変数に代入されている Rubyのオブジェクトをマークするため */ RUBY_INIT_STACK; /* ... */ } milter managerへのRubyの組み込み
  23. 23. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 スタックとマーク対象 { RUBY_INIT_STACK; /* ... */ { VALUE object = rb_ary_new(); /* マーク対象 */ } } { VALUE object = rb_ary_new(); /* マーク対象外 */ } milter managerへのRubyの組み込み
  24. 24. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 確認例 #define MARKED_P(object) rb_objspace_marked_object_p(object) { RUBY_INIT_STACK; { VALUE object = rb_ary_new(); /* GC.start(immediate_sweep: false) */ printf("%dn", MARKED_P(object)); /* => 1 */ } } { VALUE object = rb_ary_new(); /* GC.start(immediate_sweep: false) */ printf("%dn", MARKED_P(object)); /* => 0 */ } スライドのリポジトリー:examples/gc.c
  25. 25. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 GC関連の注意 { RUBY_INIT_STACK; /* Rubyのオブジェクトを触る Cのコードはこのブロック内でだけ使うこと */ /* Cからのコールバックで Rubyのコードを呼び出すときは注意 */ } milter managerへのRubyの組み込み
  26. 26. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 初期化:シグナル関連 { RUBY_INIT_STACK; /* シグナルハンドラーを保存 */ ruby_init(); /* Rubyがシグナルハンドラーを登録 */ /* シグナルハンドラーを復帰 */ /* シグナルはアプリで処理したいから */ } milter managerへのRubyの組み込み
  27. 27. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 シグナル復帰例 { /* 他のシグナルも同様に復帰 */ void (*sigint_handler)(int); sigint_handler = signal(SIGINT, SIG_DFL); ruby_init(); signal(SIGINT, sigint_handler); } milter managerへのRubyの組み込み
  28. 28. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 初期化:引数の処理 { /* ...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); } milter managerへのRubyの組み込み
  29. 29. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 初期化:アプリの初期化 { /* ...ruby_process_options()... */ /* require中に例外が発生してもここで止める */ /* ここで止めないと例外を受け取る人がいなくて クラッシュ */ rb_protect(/* rb_require("milter/manager") */); } milter managerへのRubyの組み込み
  30. 30. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 milter managerとRuby 組み込み処理系の1つ Pythonも使えるようにしたかった 結局Ruby必須でPython対応はしなかった ✓ ✓ 起動後にdlopen()で動的にsoを 読み込んで組み込み ✓ milter managerへのRubyの組み込み
  31. 31. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 起動時に動的に組み込み milter manager Ruby milter manager Ruby 起動前 起動 起動後 milter manager Ruby dlopen() milter managerへのRubyの組み込み
  32. 32. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 起動時にso読んで組み込み { /* ↓GC用 */ RUBY_INIT_STACK; /* 動的にsoを読んで初期化関数を呼ぶ */ /* dlopen(); init = dlsym(); init(); ←の中でruby_init();とか */ /* アプリの処理 */ } milter managerへのRubyの組み込み
  33. 33. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 RUBY_INIT_STACK!? { /* ↓アプリ側で呼ぶの!? */ RUBY_INIT_STACK; /* 動的にsoを読んで初期化関数を呼ぶ */ /* dlopen(); init = dlsym(); init(); ←の中でruby_init();とか */ /* アプリの処理 */ } milter managerへのRubyの組み込み
  34. 34. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 アプリにーlruby… milter manager Ruby milter manager Ruby 起動前 起動 起動後 milter manager Ruby dlopen() -lruby カッコわるい。。。 milter managerへのRubyの組み込み
  35. 35. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込み時の意気込み 本体に組み込む✓ 動的に組み込もうとしない✓
  36. 36. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込みの実装 初期化✓ fork対応✓ イベントループとシグナル✓ milter managerへのRubyの組み込み
  37. 37. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 milter manager利用例 大学・企業 ユーザー数:数百〜数万人✓ ✓ プロバイダー ユーザー数:数千〜数十万人✓ ✓ それなりの性能が必要 milter managerへのRubyの組み込み
  38. 38. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 性能向上方法 CPU マルチプロセス1択✓ ✓ 通信・多同時接続 いろいろ✓ ✓ milter managerへのRubyの組み込み
  39. 39. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 マルチプロセス マスタープロセス listen()a. fork()b. ✓ ワーカープロセス accept()a. ↑したクライアントの処理b. ✓ milter managerへのRubyの組み込み
  40. 40. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込みとfork() fork()すると ワーカープロセスがクラッシュ プロセス終了時とか✓ ✓ ヒント:fork()とスレッド✓ milter managerへのRubyの組み込み
  41. 41. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 fork()とスレッド 混ぜるな危険✓ Rubyはスレッドを動かしている 例:タイマースレッド✓ ✓ fork時にスレッドのケアが必要✓ milter managerへのRubyの組み込み
  42. 42. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 スレッドのケア VALUE rb_pid; /* タイマースレッドの後始末とか した上でfork */ rb_pid = rb_funcall(rb_mKernel, rb_intern("fork"), 0); return NUM2INT(rb_pid); milter managerへのRubyの組み込み
  43. 43. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込みの実装 初期化✓ fork対応✓ イベントループとシグナル✓ milter managerへのRubyの組み込み
  44. 44. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 イベントループとシグナル 気にしなくてよい アプリがシグナルを処理するから✓ ✓ 拡張ライブラリーなら対応必要 イベントループ中にシグナル発生✓ →すぐにイベントループを抜ける✓ ✓ milter managerへのRubyの組み込み
  45. 45. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Rubyの組み込みのまとめ Rubyを組み込む実装方法を紹介✓ 動的組み込みは諦めろ✓ fork時はRubyのforkを使う✓ Rubyの組み込み
  46. 46. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 mrubyの組み込み 実例 Groonga Since 2013
  47. 47. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Groongaとmruby Groonga 全文検索エンジン(mruby組み込み)✓ 高速に検索結果を返し続けたい✓ リソース消費は波がない方がよい 例:いらなくなったメモリーはすぐに解放 ✓ ✓ mrubyの組み込み
  48. 48. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 リソース消費 時間 メモリー使用量 こっちが望ましい mrubyの組み込み
  49. 49. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 メモリー管理 Groonga 必要なときに確保✓ いらなくなったら解放✓ ✓ mruby GC✓ メモリーが足りなくなったら解放✓ ✓ mrubyの組み込み
  50. 50. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 GroongaとmrubyのGC mrubyのGCにGroongaのリソース 管理を任せない リソース管理: mrubyのオブジェクトのsweep時に Groongaのリソースを解放 ✓ ✓ mrubyの組み込み
  51. 51. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 mrubyのGCとリソース mrubyのGC Groongaリソースのサイズを知らない✓ 適切なタイミングでsweepできない✓ RubyのGCも同じ✓ ✓ mrubyの組み込み
  52. 52. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 実例1: 明示的な解放 # 検索 result = table.search(condition) begin output_result(result) # 出力 ensure result.close # 明示的な解放 end mrubyの組み込み
  53. 53. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 実例2: 所有権を渡さない /* 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); mrubyの組み込み
  54. 54. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 mruby組み込みのまとめ GCに任せないという選択 使用メモリー量を安定させるため✓ 安定した性能を出すため✓ ✓ mrubyの組み込み
  55. 55. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 まとめ Rubyの組み込み ガッツリ連携するつもりで設計 拡張ライブラリーで十分じゃないかよく検討すること ✓ ✓ mrubyの組み込み アプリの大事な事を忘れないで設計✓ ✓ OSS Gateもよろしく✓

×