2022-04-27

[php-src読書録]その17: opcacheの保存と取得

php-srcのコードリーディングした内容をコツコツ残すテスト。その17

opcache続き。コードを読む前にnikic大先生の記事を読むのが良さそう
https://www.npopov.com/2021/10/13/How-opcache-works.html

zend_なんちゃらの差し替え

main()
=> php_cli_startup()
=> php_module_startup()
=> zend_startup_extensions()
=> zend_llist_apply_with_del()
=> zend_extension_startup()
=> opcache.so!accel_startup()

という感じで accel_startup() が呼ばれる

accel_startup() では zend_post_startup_cbaccel_post_startup() をセットしている。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L3156:L3156

zend_post_startup_cb は以下の経路で呼ばれる

main()
=> php_cli_startup()
=> php_module_startup()
=> zend_post_startup()
=> opcache.so!accel_post_startup()

https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend.c#L1045

zend_compile_file zend_stream_open_function zend_resolve_path にそれぞれ、
persistent_compile_file persistent_stream_open_function persistent_zend_resolve_path を設定
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L3279-L3291

キャッシュの取得と保存

zend_compile_file 経由で persistent_compile_file() が呼ばれキャッシュを検索する。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L2000-L2007

ZCSG(hash) というのがファイル名から生成したキー => キャッシュの実態(oparrayとかclassやfunction tableなど) のハッシュテーブルになる(後述)

ファイルの場合はこの辺で判定
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L1961-L1965
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L2136-L2138

キャッシュがない場合 opcache_compile_file() を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L2160:L2160

opcache_compile_file()accelerator_orig_compile_file() を呼び出し、元のzend_compile_fileでop_arrayを生成する。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L1789:L1789

その後 cache_script_in_shared_memory() でキャッシュする
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L2169:L2169

cache_script_in_shared_memory()zend_accel_script_persist() を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L1600:L1600

zend_accel_script_persist() では共有メモリ内にopcacheを生成している。 例えば zend_shared_memdup_put() などで共有メモリ内に領域を確保する。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_persist.c#L525:L525
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_shared_alloc.c#L391-L393

zend_persistent_scriptはここで共有メモリ内に領域を確保している。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_persist.c#L1289:L1289

ので大丈夫と思いきや、 zend_persistent_script 内のフィールドでポインタになっているデータの実体は共有メモリ内に確保されていないので、それらも共有メモリ内に入れる必要がある。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_persist.c#L1315-L1322

scriptのfunction_table, class_table, main_op_arrayなど
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L3202-L3211

shmgetでメモリ確保した領域に ZCSG(hash) が存在する。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/shared_alloc_shm.c#L53-L125
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_shared_alloc.c#L127:L127
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L2862:L2862

zend_accel_hash_update()&ZCSG(hash) にキーがスクリプトの絶対パス、値に new_persistent_script が格納される。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L1620:L1620

validate_timestampsrevalidate_freq の設定はこのあたりでチェックされる。opcacheのハッシュキーがヒットした場合でも条件が合致した場合は再度コンパイルされる。
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L2092-L2108

last_usedやhitsはここで更新される。これによって opcache_get_status を使ってopcacheの統計情報を取得できる
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L2195:L2195
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/ZendAccelerator.c#L2227:L2227
https://github.com/php/php-src/blob/PHP-8.1.4/ext/opcache/zend_accelerator_module.c#L536-L564

このエントリーをはてなブックマークに追加