2020 年 3 月更新:从 PHP 7.4 开始,有sapi_windows_set_ctrl_handler() and sapi_windows_generate_ctrl_event。这使您的脚本可以处理 Ctrl+C 和 Ctrl+Break 按键,并为同一进程组中的其他进程生成它们。proc_open()还添加了create_process_group
选项允许子进程处理 CTRL 事件。就信号处理而言,这是您在 Windows 上可以做的最好的事情。请注意,这些函数仅适用于 PHP CLI SAPI(即 php.exe),并且仅当进程附加到控制台时它们才起作用,这在一定程度上限制了它们的实用性。
原答案:
虽然这里唯一的其他答案是简洁和准确的,但它缺乏关于为什么 Windows 上不支持信号的详细信息。
首先,信号是一种相当有限且过时的与进程通信的方式。有许多更丰富的方法可以通知进程需要放弃正在执行的操作并执行其他操作。即使在 POSIX 平台上,信号处理程序也旨在非常轻量级 - 信号处理程序中的代码必须准备好/能够处理同时到达的多个信号。
PHP 允许信号处理程序通过pcntl_signal()有大量警告。在使用它们之前,代码必须调整 PHP 将到达处理程序的信号传递之前经过的“滴答”数量。一个刻度是 Zend(PHP 的核心)在检查信号处理程序状态并运行必要的回调之前将执行的指令数。它基本上是主执行循环中的一个繁忙循环。因此,如果遵循每报价 1 的建议,调整报价将大大减慢该过程。该函数的注释表明,对于大多数系统,刻度值 100 就足够了。了解处理程序如何工作的最简单方法是,在幕后有一个实际的信号处理程序来收集信号信息,PHP 偶尔会查询该处理程序以查看处理程序是否被调用、发送了哪个信号等。如果是这样,则信息被传递到用户空间(即您的代码)中的回调。这不是真正的信号处理,也永远不会因为真正的信号处理所带来的危险和困难而产生。
第二个问题是,即使使用 pcntl_signal() 提供的伪信号处理程序支持,PHP 也可能会丢失有关发生的信号的信息。如果发生多个信号场景,脚本将不会收到某个信号多次发生的通知。刻度值越大,发生这种情况的可能性就越大,尤其是在繁忙的系统上。
Windows 在大多数情况下并不真正使用信号。有的是设置ConsoleCtrlHandler()该函数用于捕获 Ctrl+C 和 Ctrl+Break,大致相当于 SIGINT。缺点是必须有一个控制台附加到进程才能工作,并且不能由其他进程发送。这终止进程()功能相当于SIGKILL,在其他操作系统下无法被阻止/处理,并且SIGKILL信号实际上并没有到达目标进程。除了这两个功能/信号之外,几乎没有什么共同点。归根结底,Windows 的本质是一个非常不同的野兽。
查看信号的唯一原因是为了某种长时间运行的 PHP 进程 - 每个人似乎都说该语言不适合这项任务(我完全不同意,但这是一个不同的讨论)。当我了解到 PHP 中信号支持的局限性(即使在 POSIX 操作系统下)时,我认为它们并不能真正解决我的情况。正确的解决方案是完全忽略信号。对于 SIGINT,应该编写命令行脚本来处理提前终止的情况 - 也就是说,它们应该是幂等的。在 PHP 的世界中,信号在很大程度上是无关紧要的,因为还有其他更灵活、更丰富的解决方案,包括命名互斥体/事件、套接字、文件、命名管道、共享内存、服务经理, etc.