Post on 16-Apr-2017
signal の話或いは
Zend Signals とは何か
2016/12/11 第七回闇 PHP 勉強会
do_akiupdated 2016-12-13
@do_aki
@do_aki
http://do-aki.net/
signal とは
• UNIX 系 OS に古くからあるプロセス間通信の一つ
• プロセスがシグナル ( 実態は整数 ) を受信すると、あらかじめ設定した処理 ( シグナルハンドラ ) が実行される(乱暴に言えば割り込みが発生する)
• シグナルハンドラが設定されてないシグナルは、無視されるかあるいは既定の動きをする(その多くはプログラムの終了)
php においては
• pcntl 拡張を利用することで signal 処理を管理することが可能
• pcntl_signal(signo, callback)
pcntl_signal の利用例<?php
declare(ticks=1);
$signal_callback_function = function ($signo) { echo "receive signal: {$signo}\n"; exit;};
pcntl_signal(SIGINT, $signal_callback_function);
while(1) { sleep(1);}
// 通常の動きだけでは永久ループ// Ctrl + C 押下で、 "receive signal: 2" を出力して終了
pcntl による signal 処理
これは単純に
“ シグナルを受信したら callback が実行される”
というものではない
pcntl_signal が行うこと
• signal_table に シグナル番号とコールバック関数を登録
• 該当シグナル番号に対するシグナルハンドラとして pcntl_signal_handler を (実行環境に対して ) 登録
• 実際にコールバック関数を呼ぶのは pcntl_signal_dispatch
signal handling
pcntl_signal_handlersigno
pcntl_signal_dispatch
SIGHUP funcA
SIGINT funcB
SIGALRM funcC
……
signal table
lookup &
call
signo pending signal
queue
[receive signal]
[tick or vm_interrupt]
シグナルハンドラの制約
• どんなタイミングであっても呼ばれる可能性がある– 当然 php のスタックフレームは無視
• 呼び出し可能なシステムコールが限られる– man signal 7 を参照
最小限の処理 ( 受信したことの記録 ) だけ行い、あとから適切なタイミングでコールバックを実行 (dispatch) している
なので
tick による dispatch• declare(ticks = N); // (N >= 1)
– N個の tick 可能命令ごとに ZEND_TICKS opcode が発行される
– 詳しくは以前書いた記事を参照http://d.hatena.ne.jp/do_aki/20151204/1449197226
• ticks=1 ならば、だいたい php スクリプト 1行ごとに発行されてる– 1行ごとに pcntl_signal_dispatch が呼ばれるイ
メージ• ファイル単位で、コンパイル時に影響
– ticks 指定忘れるとシグナルコールバックされない
vm_interrupt による dispatch
• pcntl_async_signals(true); // 7.1~– declare(ticks=1) が不要になる– pcntl_signal_handler が EG(vm_interrupt) = 1 するようになる
• EG(vm_interrupt) が 1 の場合、 PHP VM は、zend_interrupt_function (関数ポインタ) を呼ぶ– Opcode ひとつ処理するたびにチェックしている– pcntl の MINIT で
zend_interrupt_function = pcntl_interrupt_functionしてて、このなかで pcntl_signal_dispatch を呼ぶ
– ZEND_TICKS Opcode で毎回呼ばれるよりも低コスト– (async_signals しなくても pcntl 以外が vm_interrupt を
セットすれば dispatch される気がする)
dispatch by vm_interrupt
pcntl_signal_handler
pcntl_signal_dispatch
[receive signal]
EG(vm_interrup
t)
PHP VM
zend_interrupt_function =
pcntl_interrupt_function
[each opcode]
set1
EG(vm_interrupt)• 元は Safe timeout handling により導入さ
れた仕組み
• EG(vm_interrupt) かつ EG(timed_out) ならば (zend_interrupt_function ではなく ) zend_timeout(0) を呼ぶ (おそらくこちらが本命 )
• EG(timed_out) は php スクリプトの実行時間が max_execution_time を超えたときに設定される
max_execution_time の実装
• setitimer (ITIMER_PROF)を利用 (!WIN32)– プログラムの実行時間が指定時間経過すると SIGPROF
が飛ぶ– php に kill –PROF すると終了する
• zend engine でシグナルハンドラ を設定してる– 7.0まで : signalシステムコールを利用
(直接 zend_timeout が呼ばれてた )– 7.1から : zend_signalを利用 (ZEND_SIGNALS)
(zend_timeout_handler)
この中で EG(timed_out) = 1
Safe timeout handling
zend_timeout_handler
PHP VM
zend_timeout
set1
zend_timeout
~7.0 (or !ZEND_SIGNALS)
7.1~ (with ZEND_SIGNALS)
EG(vm_interrup
t)EG(timed_out)
ZEND_SIGNALS• ZendEngineや SAPI,拡張 と 実行環境 の間に作ら
れた signal 処理の中間層– 環境依存な signal 処理を画一的に扱うための仕組み– 7.1 から デフォルトで有効
• php スクリプトにおける pcntl のようなものを、C言語で (コアや拡張向けに ) 提供した感じ– zend_signal によって実行環境に登録されるシグナル
ハンドラは zend_signal_handler_defer に統一される (シグナルハンドラとして指定した関数はここから間接的に呼ばれる )
Deferred Signals• ZEND_SIGNAL_BLOCK_INTERRUPTIONS (= HANDLE_BLOCK_INTERRUPTIONS) により、シグナルハンドラの実行を延期することが可能– 現状使ってるのは opcache のみ– 解放は ZEND_SIGNAL_UNBLOCK_INTERRUPTIONS– シグナルハンドラの実行タイミングを制御すること
で、共有メモリ操作時の不安定な動きを解消している(らしい)
– 以前はメモリ操作周りでも利用されていたが、 7.0 でのメモリマネージャ刷新によって不要になった?
ZEND SIGNALS
zend_signal_handler_defer
1 handler1
2 hanlder2
3 hanlder3
……SIGG(handlers)
zend signal queue
signo
signal blocked(HANDLE_BLOCK_INTERRUPTIONS)
signal unblocked
HANDLE_UNBLOCK_INTERRUPTIONS
signo
callzend_signal_handler
まとめ– pcntl signal – EG(vm_interrupt)– Safe timeout handling – ZEND_SIGNALS
• について話しました
• 7.1 で php における signal 処理はだいぶ大きく変容します– おそらく ふつーに php スクリプト書いている人には気づ
かれないような変化。けど、これにより安定化とパフォーマンス向上が図られているのです
参照• Zend Signal Handling
– https://wiki.php.net/rfc/zendsignals– https://
github.com/php/php-src/commit/939875133a2c389d621a9999a8ede3ddbc9b6637
• Asynchronous Signal Handling (without TICKs) – https://wiki.php.net/rfc/async_signals– https://
github.com/php/php-src/commit/c03ccfe78d6b13cab9546efb616a42a8f3e8a4e0
• Safe execution timeout handling– https://www.mail-archive.com/internals@lists.php.net/msg76907.html– https://github.com/php/php-src/pull/1876
• Enable Zend Signals by Default– https://www.mail-archive.com/internals@lists.php.net/msg86428.html