php-srcのコードリーディングした内容をコツコツ残すテスト。その18
今日はopcacheのファイルキャッシュ
キャッシュをファイルに保存
cache_script_in_shared_memory()
で zend_file_cache_script_store()
が呼ばれる
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L1650:L1650
zend_file_cache_script_store()
で zend_file_cache_get_bin_file_path()
が呼ばれキャッシュファイルのパスを生成する
/tmp/095a04392b125a7d278d14ce75956467/root/php-src-php-8.1.4/1.php.bin
こんな感じで opcache.file_cache
にファイルの絶対パスを追加したファイルパスになる
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L990:L990
そのファイルパスのディレクトリを再帰的に作成する
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L992:L992
そのファイルパスで zend_file_cache_open()
でファイルを開き、writev()
でキャッシュを書き込んでいる
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L998:L998
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L1052:L1052
キャッシュを書き込む前に構造体をごにょる必要がある。なんでかというと構造体に含まれるポインタはその実行環境におけるポインタになるので、次にロードされるときには不正なポインタになってしまう。 各ポインタをメモリの先頭アドレスからの差分にして保存、利用するときはメモリの先頭アドレスにその差分を足せば正しいポインタになるので、ポインタの変換をキャッシュの保存・取得時に行っている。
ファイルキャッシュに保存する際の変換は zend_file_cache_serialize
で行われる。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L908-L913
このあたりでファイルキャッシュに保存する構造体に値をセットしている
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L896-L905
その後、 SERIALIZE_STR
や zend_file_cache_serialize_hash
などでポインタ変換を行う。
SERIALIZE_STR
はこんな感じで (ptr) = (void*)((char*)(ptr) - (char*)script->mem);
によって先頭アドレスからの差分に変換している。
#define SERIALIZE_STR(ptr) do { \
if (ptr) { \
if (IS_ACCEL_INTERNED(ptr)) { \
(ptr) = zend_file_cache_serialize_interned((zend_string*)(ptr), info); \
} else { \
ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
/* script->corrupted shows if the script in SHM or not */ \
if (EXPECTED(script->corrupted)) { \
GC_ADD_FLAGS(ptr, IS_STR_INTERNED); \
GC_DEL_FLAGS(ptr, IS_STR_PERMANENT); \
} \
(ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
} \
} \
} while (0)
キャッシュをファイルから取得
read()
でファイルからキャッシュを読み取り
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L1715:L1715
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L1763:L1763
ファイルキャッシュにはポインタの差分を保存しているので、読み取った結果をそのままでは利用できない。そのため、 zend_file_cache_unserialize()
でポインタを正しい位置に戻していたりする。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L1843:L1843
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_file_cache.c#L1675-L1682
例えば、UNSERIALIZE_STR
は以下のように定義されていて、 (ptr) = (void*)((char*)buf + (size_t)(ptr))
が変換している箇所になっている。
#define UNSERIALIZE_STR(ptr) do { \
if (ptr) { \
if (IS_SERIALIZED_INTERNED(ptr)) { \
(ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr), !script->corrupted); \
} else { \
ZEND_ASSERT(IS_SERIALIZED(ptr)); \
(ptr) = (void*)((char*)buf + (size_t)(ptr)); \
/* script->corrupted shows if the script in SHM or not */ \
if (EXPECTED(!script->corrupted)) { \
GC_ADD_FLAGS(ptr, IS_STR_INTERNED | IS_STR_PERMANENT); \
} else { \
GC_ADD_FLAGS(ptr, IS_STR_INTERNED); \
GC_DEL_FLAGS(ptr, IS_STR_PERMANENT); \
} \
} \
} \
} while (0)
bufは展開されたメモリの先頭アドレスでptrに先頭アドレスからの差分が入っている。
zend_file_cache_unserialize_hash
などの関数も良い感じにポインタを変換している。