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.

OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた

11,830 views

Published on

第六回闇PHP勉強会発表資料

Published in: Technology
  • Be the first to comment

OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた

  1. 1. Reading implementation of OPcache’s file-based cache OPcacheの新機能
 ファイルベースキャッシュ の内部実装を読んでみた 第六回闇PHP勉強会(2015/11/22)
 発表資料
  2. 2. 自己紹介 ❖ @hnw ❖ 所属:KLab株式会社 ❖ カレーとバグが大好物 ❖ 最近PHP7のプレゼンばかりしています
  3. 3. アジェンダ ❖ ファイルベースキャッシュの紹介 ❖ キャッシュファイルを覗いてみる ❖ わきあがる疑問点 ❖ まとめ
  4. 4. ❖ ファイルベースキャッシュの紹介 ❖ キャッシュファイルを覗いてみる ❖ わきあがる疑問点 ❖ まとめ
  5. 5. OPcacheのおさらい ❖ OPcacheなしのPHP Parser Lexer OpcodeCompiler ZendVM PHP token AST opcode
  6. 6. OPcacheのおさらい ❖ OPcache拡張モジュールを使った場合 ❖ キャッシュヒットすると前段の処理を省略できる Parser Lexer OpcodeCompiler ZendVM Optimizer PHP token AST opcode opcode OpcodeCache opcode
  7. 7. ファイルベースキャッシュ ❖ OPcacheの新機能 ❖ PHP 7.0.0 alpha 1で実装 ❖ まだexperimental扱い ❖ PHPのコンパイル結果をファイルにキャッシュする ❖ 従来は共有メモリにキャッシュしていた
  8. 8. ファイルベースキャッシュ ❖ ファイルベースキャッシュを有効にしたOPcache Parser Lexer OpcodeCompiler ZendVM Optimizer PHP token AST opcode opcode FileCacheSHMCache
  9. 9. ファイルベースキャッシュの利点 ❖ 共有メモリとファイルの両キャッシュを併用する場合 ❖ 共有メモリにキャッシュが無いときの速度低下を最 低限に抑える(Webサーバ再起動直後など) ❖ ファイルキャッシュのみ使う場合 ❖ コマンドラインやCGIなどでも速度が稼げる
  10. 10. ファイルベースキャッシュの利点 http://talks.php.net/tokyo15#/php7pcache1
  11. 11. ❖ ファイルベースキャッシュの紹介 ❖ キャッシュファイルを覗いてみる ❖ わきあがる疑問点 ❖ まとめ
  12. 12. 実際に試してみる ❖ opcache.file_cache! ❖ 未設定: ファイルベースキャッシュ無効(デフォルト) ❖ パスを設定: 共有メモリ・ファイル両方有効 opcache.enable=1! opcache.enable_cli=1! opcache.file_cache=/var/tmp/php/opcache
  13. 13. キャッシュファイルのパス ❖ /tmp/foo.phpのキャッシュを探してみた /var/tmp/php/opcache/68359b54ec757c2697b328c10d7d44c2/ tmp/foo.php.bin ❖ フルパス+「.bin」にキャッシュされる
  14. 14. キャッシュファイルのパス ❖ /tmp/foo.phpのキャッシュを探してみた /var/tmp/php/opcache/68359b54ec757c2697b328c10d7d44c2/ tmp/foo.php.bin ❖ フルパス+「.bin」にキャッシュされる ❖ このハッシュ値は何だ???
  15. 15. パス中のハッシュ値の正体 ❖ このハッシュ値を作っているのは
 ext/opcache/ZendAccelerator.c の accel_gen_system_id() ❖ PHPバージョン・ZTSが有効か・int型やlong型のサイ ズなどを含んだ文字列のMD5値 ❖ PHPをバージョンアップすると別のパスになる /var/tmp/php/opcache/68359b54ec757c2697b328c10d7d44c2/
 tmp/foo.php.bin
  16. 16. キャッシュファイルの中身(1) <?php phpinfo(); 000000 4f 50 43 41 43 48 45 00 36 38 33 35 39 62 35 34 >OPCACHE.68359b54<! 000010 65 63 37 35 37 63 32 36 39 37 62 33 32 38 63 31 >ec757c2697b328c1<! 000020 30 64 37 64 34 34 63 32 e0 02 00 00 00 00 00 00 >0d7d44c2à.......<! 000030 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > ...............<! 000040 4f 48 50 56 00 00 00 00 12 19 ee 32 01 00 00 00 >OHPV......î2....<! 000050 c0 01 00 00 00 00 00 00 02 00 00 00 00 00 00 08 >À...............<! ……! 000330 01 00 00 00 06 06 00 00 f9 e0 f8 ab b5 d0 00 80 >........ùàø«µÐ..<! 000340 07 00 00 00 00 00 00 00 70 68 70 69 6e 66 6f 00 >........phpinfo.< phpinfo.php phpinfo.php.bin
  17. 17. なるほど、 わからん!
  18. 18. キャッシュファイルの中身(2) ❖ 意外とデカいぞ…?
 (.php 17bytes → .php.bin 848bytes) ❖ 中身を調べよう!
  19. 19. キャッシュファイルの中身(3) ❖ 内部的なデータ構造をそのまま書き出している ❖ zend_file_cache_metainfo構造体 ❖ zend_persistent_script構造体 ❖ 固定文字列(zend_string)の配列
  20. 20. zend_file_cache_metainfo構造体 typedef struct _zend_file_cache_metainfo {! char magic[8]; // "OPCACHE0"! char system_id[32]; // accel_gen_system_id()! size_t mem_size; // size of serialized script! size_t str_size; // size of interned string! size_t script_offset; // ?! accel_time_t timestamp; // script->timestamp! uint32_t checksum;! } zend_file_cache_metainfo; ❖ メンバ system_id には先ほど見たハッシュ値が入る! ❖ キャッシュを読む際、これをチェックする ❖ 異なるバージョン間でキャッシュの使い回しはできない
  21. 21. zend_persistent_script構造体 typedef struct _zend_persistent_script {! ! zend_string *full_path;! ! zend_op_array main_op_array;! ! HashTable function_table;! ! HashTable class_table;! ……! } zend_persistent_script; ❖ スクリプトのコンパイル結果を管理するOPcacheの構造体 ❖ 共有メモリ上に置かれているキャッシュそのもの
  22. 22. 固定文字列だけ別管理 ❖ OPcacheの管理上、プログラム中の固定文字列は zend_persistent_script構造体とは別管理になる ❖ 全プロセスで固定文字列を共有する仕組みがある
 (interned string & そのキャッシュ) ❖ 別途取り出してファイルに書き出す
  23. 23. ポインタのシリアライズ(1) ❖ ファイルキャッシュの概要 ❖ 構造体をそのままファイルに保存 ❖ ファイルから構造体を復元
  24. 24. ポインタのシリアライズ(1) ❖ ファイルキャッシュの概要 ❖ 構造体をそのままファイルに保存 ❖ ファイルから構造体を復元 ❖ 簡単そうに思えるが、自明でない点がある ❖ メモリアドレスは実行ごとに変わる ❖ 保存・復元のため「シリアライズ」する必要がある
  25. 25. ポインタのシリアライズ(2) ❖ 発想としては単純 #define SERIALIZE_PTR(ptr) do { ! ! ! if (ptr) { ! ! ! ! ZEND_ASSERT(IS_UNSERIALIZED(ptr)); ! ! ! ! (ptr) = (void*)((char*)(ptr) - (char*)script->mem); ! ! ! } ! ! } while (0)! #define UNSERIALIZE_PTR(ptr) do { ! ! ! if (ptr) { ! ! ! ! ZEND_ASSERT(IS_SERIALIZED(ptr)); ! ! ! ! (ptr) = (void*)((char*)buf + (size_t)(ptr)); ! ! ! } ! ! } while (0)
  26. 26. ポインタのシリアライズ(2) ❖ 発想としては単純 ❖ バッファ先頭からの相対値に変換する ❖ 全部のポインタ値が比較的小さい値になる ❖ ポインタ値が小さい=シリアライズ済と見なす
  27. 27. ポインタのシリアライズ(3) ❖ 仕組みは単純でも実際は面倒 ❖ PHP配列の中の配列→ポインタの先にポインタ ❖ 全部のポインタを変換することになる ❖ 関連する全データ構造のシリアライズ・アンシリアライ ズ関数が実装されている ❖ op_array、zval、配列、関数、AST ❖ 何か別の応用が可能かもしれない
  28. 28. ❖ ファイルベースキャッシュの紹介 ❖ キャッシュファイルを覗いてみる ❖ わきあがる疑問 ❖ まとめ
  29. 29. わきあがる疑問 ❖ Pythonでいう.pycを我々は手に入れたのか? ❖ 性能面でそこまで嬉しいのか? ❖ 他の応用はあるか? ❖ セキュリティ面は大丈夫か?
  30. 30. Pythonでいう.pycを我々は手に入れたのか? ❖ .pyc は互換性を考えて作られている ❖ 近いバージョンなら別サーバにもデプロイ可能 ❖ コンパイル済みバイナリに準ずる扱い ❖ .php.bin は少しでもバージョンが変わると使えない ❖ 構造体をベタに保存しているので、構造体メンバの順 序を入れ替えただけで作り直しになる ❖ あくまでキャッシュ
  31. 31. 性能面でそこまで嬉しいのか? ❖ CLIやCGIだとプロセス生成のコストの方が高い気が? ❖ 高負荷Webサービスでは、従来通りウォームアップ後 にロードバランサ配下に組み込む方が無難では? ❖ 個人的にはユースケースがイマイチ見えない…
  32. 32. 他の応用はあるか? ❖ オススメしないが、なんちゃって難読化に使える ❖ .php.bin だけをデプロイ又は納品するなど ❖ 理屈上は逆コンパイルも可能、難読化とは呼べない ❖ 運用で死ぬ未来が見える
  33. 33. セキュリティ面は大丈夫か? ❖ ダメかも… ❖ キャッシュファイルはWebサーバ権限で書き込める ❖ 他の脆弱性を利用してキャッシュファイルを上書き& 再起動を待てば任意スクリプトが実行可能
 (発動条件が厳しめではあるが…)
  34. 34. ❖ ファイルベースキャッシュの紹介 ❖ キャッシュファイルを覗いてみる ❖ 疑問点 ❖ まとめ
  35. 35. まとめ ❖ OPcacheのファイルベースキャッシュを調べました ❖ 共有メモリのキャッシュをシリアライズしてファイル 化するような仕組みでした ❖ 手元で生成してデプロイするにはあまり向いていない ❖ あくまでキャッシュと考えた方が良さそう ❖ 今後の動きを注視していきたい
  36. 36. ご静聴 ありがとう ございました
  37. 37.
  38. 38. 以降、 補足スライド
  39. 39. ファイルの更新チェック ❖ opcache.validate_timestamps! ❖ 1: タイプスタンプのチェックを行う(デフォルト) ❖ .php と .php.bin 内部のタイムスタンプを比較し、 一致しなかったらキャッシュを捨てる opcache.enable=1! opcache.enable_cli=1! opcache.file_cache=/var/tmp/php/opcache! opcache.validate_timestamps=1
  40. 40. 勘違いしやすい(?)設定 ❖ opcache.file_cache_only! ❖ 1: 共有メモリキャッシュ無効・ファイルのみ ❖ 0: 共有メモリキャッシュ有効(デフォルト) opcache.enable=1! opcache.enable_cli=1! opcache.file_cache=/var/tmp/php/opcache! opcache.file_cache_only=1

×