Windows 上的信号处理 [英] Signal handling on Windows

查看:32
本文介绍了Windows 上的信号处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个独立的 PHP 脚本,我会处理从 Windows 操作系统发送的信号,以便在发出终止信号"时正常关闭.

I have a standalone PHP script, and I would handle a signal sent from Windows OS to do a graceful shutdown when a "kill signal" is issued.

如何在 Windows 上执行此操作?

How can I do that on Windows?

推荐答案

2020 年 3 月更新:从 PHP 7.4 开始,有 sapi_windows_set_ctrl_handler()sapi_windows_generate_ctrl_event.这使您的脚本可以处理 Ctrl+C 和 Ctrl+Break 按键,并为同一进程组中的其他进程生成它们.proc_open() 还添加了 create_process_group 选项以允许子进程处理 CTRL 事件.就信号处理而言,这是您在 Windows 上可以做的最好的事情.请注意,这些函数仅适用于 PHP CLI SAPI(即 php.exe),并且仅在进程附加到控制台时才起作用,这在一定程度上限制了它们的实用性.

Update March 2020: As of PHP 7.4, there is sapi_windows_set_ctrl_handler() and sapi_windows_generate_ctrl_event. This lets your script handle Ctrl+C and Ctrl+Break keypresses as well as generate them for other processes in the same process group. proc_open() has also added the create_process_group option to allow the child process to handle CTRL events. This is the best you can do on Windows as far as signal handling goes. Note that these functions are only available to the PHP CLI SAPI (i.e. php.exe) and they only function when the process is attached to a console, which somewhat limits their usefulness.

原答案:

虽然这里唯一的其他答案是简洁准确的,但它缺乏关于 Windows 不支持信号的详细信息.

While the only other answer here is succinct and accurate, it lacks detail on why there isn't signal support on Windows.

首先,信号是一种相当有限且过时的与进程通信的方式.有许多更丰富的方法可以通知进程它需要放弃正在做的事情并做其他事情.即使在 POSIX 平台上,信号处理程序也应该是非常轻量级的 - 信号处理程序中的代码必须准备好/能够处理同时到达的多个信号.

First off, signals are a rather limited and outdated way of communicating with a process. There are many more richer ways to inform a process that it needs to drop what it is doing and do something else. Even on POSIX platforms, signal handlers are intended to be VERY lightweight - code in signal handlers has to be ready/capable of handling multiple signals arriving at the same time.

PHP 允许通过带有 pcntl_signal() 的信号处理程序大量的警告.在使用它们之前,代码必须调整在 PHP 传递到达处理程序的信号之前传递的滴答"数.一个刻度是 Zend(PHP 的核心)在检查信号处理程序状态和运行必要的回调之前将执行的指令数.它基本上是主执行循环中的一个繁忙循环.因此,如果遵循每刻度建议 1,调整刻度将显着减慢该过程.该函数的注释表明,在大多数系统上,刻度值 100 就足够了.了解处理程序如何工作的最简单方法是在幕后有一个实际的信号处理程序来收集信号信息,PHP 偶尔会查询该处理程序以查看是否调用了处理程序,发送了哪个信号等 - 如果是,那么信息传递给用户空间中的回调(即您的代码).这不是真正的信号处理,也永远不会因为真正的信号处理带来的危险和困难.

PHP allows for signal handlers via pcntl_signal() with a significant number of caveats. Before they can be used, the code has to adjust the number of "ticks" that pass before PHP will pass a signal that arrived onto a handler. A tick is the number of instructions that Zend (the core of PHP) will execute before checking the signal handler status and running the necessary callbacks. It's basically a busy loop within the main execution loop. As such, adjusting ticks will dramatically slow down the process if the per-tick recommendation of 1 is followed. The comments on the function suggest a tick value of 100 is sufficient on most systems. The easiest way to understand how the handler works is that there is an actual signal handler behind the scenes that collects signal information, which PHP occasionally queries to see if the handler was called, which signal was sent, etc. - if so, then the information is passed along to a callback in userland (i.e your code). It's not real signal handling and never will be due to the dangers and difficulties that real signal handling presents.

第二个问题是,即使使用 pcntl_signal() 提供的伪信号处理程序支持,PHP 也可能丢失有关发生的信号的信息.如果发生多信号场景,脚本将不会被通知某个信号发生了多次.刻度值越大,发生这种情况的可能性就越大,尤其是在繁忙的系统上.

The second problem is that, even with the pseudo-signal handler support that pcntl_signal() provides, PHP can lose information about signals that occurred. If the multiple signal scenario happens, the script will not be notified that some signal happened multiple times. The larger the tick value, the more likely this can happen, especially on a busy system.

在大多数情况下,Windows 并不真正使用信号.有 SetConsoleCtrlHandler() 用于捕获 Ctrl+C 和 Ctrl+Break 的函数,大致相当于 SIGINT.缺点是必须有一个附加到进程的控制台才能工作,并且不能由其他进程发送.TerminateProcess() 函数相当于 SIGKILL,在其他操作系统下无法阻塞/处理,并且 SIGKILL 信号实际上并未到达目标进程.除了这两个功能/信号之外,几乎没有共同之处.归根结底,Windows 是一个非常不同的引擎.

Windows, for the most part, doesn't really use signals. There is the SetConsoleCtrlHandler() function which exists to capture Ctrl+C and Ctrl+Break and is roughly equivalent to SIGINT. The downsides are that there has to be a console attached to the process for it to work and can't be sent by other processes. The TerminateProcess() function is equivalent to SIGKILL, which can't be blocked/handled under other OSes and the SIGKILL signal doesn't actually arrive at the target process. Other than those two functions/signals, there's very little in common. Ultimately, Windows is a very different beast under the hood.

查看信号的唯一原因是为了某种长期运行的 PHP 进程——似乎每个人都认为该语言不适合这项任务(我完全不同意,但这是一个不同的讨论).当我了解到即使在 POSIX 操作系统下 PHP 中信号支持的局限性时,我认为它们并不是我的情况的真正答案.正确的解决方案是完全忽略信号.对于 SIGINT,应该编写命令行脚本来处理过早终止的情况——也就是说,它们应该是幂等的.在 PHP 世界中,信号在很大程度上是无关紧要的,因为还有其他更灵活、更丰富的解决方案,包括命名互斥体/事件、套接字、文件、命名管道、共享内存、服务管理器

The only reason to look at signals is for some sort of long-running PHP process - a task that everyone seems to say that the language isn't suitable for (I completely disagree, but that's a different discussion). As I learned about the limitations of signal support in PHP even under POSIX OSes, I decided that they weren't really the answer to my situation. The correct solution is to ignore signals altogether. For SIGINT, command-line scripts should be written to handle the scenario of premature termination - that is, they should be idempotent. In the world of PHP, signals are largely irrelevant as there are other, more flexible, and richer solutions including named mutexes/events, sockets, files, named pipes, shared memory, Service Manager, etc.

这篇关于Windows 上的信号处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆