Reading implementation of OPcache’s file-based cache
OPcacheの新機能

ファイルベースキャッシュ
の内部実装を読んでみた
第六回闇PHP勉強会(2015/11/22)

発表資料
自己紹介
❖ @hnw
❖ 所属:KLab株式会社
❖ カレーとバグが大好物
❖ 最近PHP7のプレゼンばかりしています
アジェンダ
❖ ファイルベースキャッシュの紹介
❖ キャッシュファイルを覗いてみる
❖ わきあがる疑問点
❖ まとめ
❖ ファイルベースキャッシュの紹介
❖ キャッシュファイルを覗いてみる
❖ わきあがる疑問点
❖ まとめ
OPcacheのおさらい
❖ OPcacheなしのPHP
Parser
Lexer
OpcodeCompiler
ZendVM
PHP
token
AST
opcode
OPcacheのおさらい
❖ OPcache拡張モジュールを使った場合
❖ キャッシュヒットすると前段の処理を省略できる
Parser
Lexer
OpcodeCompiler
ZendVM
Optimizer
PHP
token
AST
op...
ファイルベースキャッシュ
❖ OPcacheの新機能
❖ PHP 7.0.0 alpha 1で実装
❖ まだexperimental扱い
❖ PHPのコンパイル結果をファイルにキャッシュする
❖ 従来は共有メモリにキャッシュしていた
ファイルベースキャッシュ
❖ ファイルベースキャッシュを有効にしたOPcache
Parser
Lexer
OpcodeCompiler
ZendVM
Optimizer
PHP
token
AST
opcode
opcode
FileCach...
ファイルベースキャッシュの利点
❖ 共有メモリとファイルの両キャッシュを併用する場合
❖ 共有メモリにキャッシュが無いときの速度低下を最
低限に抑える(Webサーバ再起動直後など)
❖ ファイルキャッシュのみ使う場合
❖ コマンドラインやCGI...
ファイルベースキャッシュの利点
http://talks.php.net/tokyo15#/php7pcache1
❖ ファイルベースキャッシュの紹介
❖ キャッシュファイルを覗いてみる
❖ わきあがる疑問点
❖ まとめ
実際に試してみる
❖ opcache.file_cache!
❖ 未設定: ファイルベースキャッシュ無効(デフォルト)
❖ パスを設定: 共有メモリ・ファイル両方有効
opcache.enable=1!
opcache.enable_cli=1...
キャッシュファイルのパス
❖ /tmp/foo.phpのキャッシュを探してみた
/var/tmp/php/opcache/68359b54ec757c2697b328c10d7d44c2/
tmp/foo.php.bin
❖ フルパス+「.bi...
キャッシュファイルのパス
❖ /tmp/foo.phpのキャッシュを探してみた
/var/tmp/php/opcache/68359b54ec757c2697b328c10d7d44c2/
tmp/foo.php.bin
❖ フルパス+「.bi...
パス中のハッシュ値の正体
❖ このハッシュ値を作っているのは

ext/opcache/ZendAccelerator.c の accel_gen_system_id()
❖ PHPバージョン・ZTSが有効か・int型やlong型のサイ
ズなど...
キャッシュファイルの中身(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 3...
なるほど、
わからん!
キャッシュファイルの中身(2)
❖ 意外とデカいぞ…?

(.php 17bytes → .php.bin 848bytes)
❖ 中身を調べよう!
キャッシュファイルの中身(3)
❖ 内部的なデータ構造をそのまま書き出している
❖ zend_file_cache_metainfo構造体
❖ zend_persistent_script構造体
❖ 固定文字列(zend_string)の配列
zend_file_cache_metainfo構造体
typedef struct _zend_file_cache_metainfo {!
char magic[8]; // "OPCACHE0"!
char system_id[32]; /...
zend_persistent_script構造体
typedef struct _zend_persistent_script {!
! zend_string *full_path;!
! zend_op_array main_op_arr...
固定文字列だけ別管理
❖ OPcacheの管理上、プログラム中の固定文字列は
zend_persistent_script構造体とは別管理になる
❖ 全プロセスで固定文字列を共有する仕組みがある

(interned string & そのキャ...
ポインタのシリアライズ(1)
❖ ファイルキャッシュの概要
❖ 構造体をそのままファイルに保存
❖ ファイルから構造体を復元
ポインタのシリアライズ(1)
❖ ファイルキャッシュの概要
❖ 構造体をそのままファイルに保存
❖ ファイルから構造体を復元
❖ 簡単そうに思えるが、自明でない点がある
❖ メモリアドレスは実行ごとに変わる
❖ 保存・復元のため「シリアライズ」...
ポインタのシリアライズ(2)
❖ 発想としては単純
#define SERIALIZE_PTR(ptr) do { !
! ! if (ptr) { !
! ! ! ZEND_ASSERT(IS_UNSERIALIZED(ptr)); !
! ...
ポインタのシリアライズ(2)
❖ 発想としては単純
❖ バッファ先頭からの相対値に変換する
❖ 全部のポインタ値が比較的小さい値になる
❖ ポインタ値が小さい=シリアライズ済と見なす
ポインタのシリアライズ(3)
❖ 仕組みは単純でも実際は面倒
❖ PHP配列の中の配列→ポインタの先にポインタ
❖ 全部のポインタを変換することになる
❖ 関連する全データ構造のシリアライズ・アンシリアライ
ズ関数が実装されている
❖ op_a...
❖ ファイルベースキャッシュの紹介
❖ キャッシュファイルを覗いてみる
❖ わきあがる疑問
❖ まとめ
わきあがる疑問
❖ Pythonでいう.pycを我々は手に入れたのか?
❖ 性能面でそこまで嬉しいのか?
❖ 他の応用はあるか?
❖ セキュリティ面は大丈夫か?
Pythonでいう.pycを我々は手に入れたのか?
❖ .pyc は互換性を考えて作られている
❖ 近いバージョンなら別サーバにもデプロイ可能
❖ コンパイル済みバイナリに準ずる扱い
❖ .php.bin は少しでもバージョンが変わると使えない...
性能面でそこまで嬉しいのか?
❖ CLIやCGIだとプロセス生成のコストの方が高い気が?
❖ 高負荷Webサービスでは、従来通りウォームアップ後
にロードバランサ配下に組み込む方が無難では?
❖ 個人的にはユースケースがイマイチ見えない…
他の応用はあるか?
❖ オススメしないが、なんちゃって難読化に使える
❖ .php.bin だけをデプロイ又は納品するなど
❖ 理屈上は逆コンパイルも可能、難読化とは呼べない
❖ 運用で死ぬ未来が見える
セキュリティ面は大丈夫か?
❖ ダメかも…
❖ キャッシュファイルはWebサーバ権限で書き込める
❖ 他の脆弱性を利用してキャッシュファイルを上書き&
再起動を待てば任意スクリプトが実行可能

(発動条件が厳しめではあるが…)
❖ ファイルベースキャッシュの紹介
❖ キャッシュファイルを覗いてみる
❖ 疑問点
❖ まとめ
まとめ
❖ OPcacheのファイルベースキャッシュを調べました
❖ 共有メモリのキャッシュをシリアライズしてファイル
化するような仕組みでした
❖ 手元で生成してデプロイするにはあまり向いていない
❖ あくまでキャッシュと考えた方が良さそう
...
ご静聴
ありがとう
ございました
…
以降、
補足スライド
ファイルの更新チェック
❖ opcache.validate_timestamps!
❖ 1: タイプスタンプのチェックを行う(デフォルト)
❖ .php と .php.bin 内部のタイムスタンプを比較し、
一致しなかったらキャッシュを捨てる...
勘違いしやすい(?)設定
❖ opcache.file_cache_only!
❖ 1: 共有メモリキャッシュ無効・ファイルのみ
❖ 0: 共有メモリキャッシュ有効(デフォルト)
opcache.enable=1!
opcache.enable...
Upcoming SlideShare
Loading in...5
×

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

151
-1

Published on

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

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
151
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

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
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×