- URL: https://www.laruence.com/en/2011/10/19/2247.html
- Please include attribution when republishing.
In PHP 5.4, following an RFC submitted by Rasmus, a new signal-handling mechanism has been introduced, with the goal of making the signal-masking mechanism applicable to any SAPI, and of improving PHP's performance in that process.
The new mechanism is called zend signal. Its philosophy comes from Yahoo's "deferred signal handling" (Yahoo signal deferring mechanism). Later, Facebook brought this philosophy into PHP, to improve the performance of PHP calling ap_block/ap_unblock under PHP + Apache 1.X.
Before introducing the details, let me first introduce the background of why this new mechanism was introduced:
I previously wrote two blog posts, describing cases where a timeout signal caused PHP to crash: A deep dive into PHP memory management: analyzing a low-probability core and A low-probability PHP core dump . In them, I noted that in fact, during critical operations, PHP already reserves a signal-masking mechanism: HANDLE_BLOCK and UNBLOCK_INTERRUPTIONS. But these two macros are only hooks, requiring the SAPI to implement them itself. At present, only the Apache 1.x SAPI has implemented these two macros, i.e. using ap_block and ap_unblock.
And for the situation described in "A low-probability PHP core dump ", if we wanted to solve it by introducing a pair of mask/unmask system calls at every moment an error occurs, the performance loss would be quite noticeable, so this problem has never been solved well.
So what zend signal does is:
1. At zend engine startup, it registers signal handlers for the following signals: SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2, SIGPROF (on *nix). If these signals already have handlers, the old handlers are saved.
2. When a signal occurs, zend_signal_handler_defer first checks whether we are currently in a block region. If not, the signal's old handler is called. If so, the signal handler is not called immediately, but waits until after HANDLE_UNBLOCK_INTERRUPTIONS, once we exit the block region, before invoking the signal handler. If multiple signals occur, they are queued and wait.
3. zend signal uses zend_signal_globals_t.depth as a counter to determine whether we are in a block region: HANDLE_BLOCK increments it, HANDLE_UNBLOCK_INTERRUPTIONS decrements it. When zend_signal_globals_t.depth is greater than 0, it means we are in a block; otherwise we are not. This guarantees performance (avoiding the old approach of calling sigaction to mask signals).
In addition, zend signal provides PHP with a new signal-handling registration interface: zend_signal.
For more detailed information, see [RFC]Zend Singal
In the broader context of the introduction of zend signal, I finally solved the problem mentioned at the beginning of the article — the one where a timeout signal could cause a crash: #60038. (only in 5.4)
That said, a reminder: PHP 5.4 is still under development. Before the final release, any new feature may be adjusted or changed. If you have any suggestions, feedback is welcome, to help make PHP even better.
Thanks
For more updates, follow: Changelog
Be First to Comment