CURL不能被具有自定义信号处理程序的PHP SIGINT杀死什么有效?

2022-08-30 23:30:16

我有一个带有自定义关闭处理程序的PHP命令行应用程序:

<?php
declare(ticks=1);

$shutdownHandler = function () {
    echo 'Exiting';
    exit();
};

pcntl_signal(SIGINT, $shutdownHandler); 

while (true) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
    curl_exec($ch);
    curl_close($ch);
}

如果我在进行 CURL 请求时用 + 终止脚本,则没有任何效果。该命令只是挂起。如果我删除自定义关闭处理程序,+ 会立即终止 CURL 请求。CtrlCCtrlC

为什么当我定义处理程序时,CURL 是不可杀死的?SIGINT


答案 1

什么有效?

真正起作用的似乎是给整个事情一些空间来发挥其信号处理魔力。这样的空间似乎是通过启用cURL的进度处理来提供的,同时还设置了一个“userland”进度回调:

解决方案 1

while (true) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_NOPROGRESS, false); // "true" by default
    curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
        usleep(100);
    });
    curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
    curl_exec($ch);
    curl_close($ch);
}

似乎在进度回调函数中需要有“某些东西”。空体似乎不起作用,因为它可能没有给PHP太多的时间来处理信号(硬核推测)。


解决方案 2

即使没有 打开,输入回调似乎也可以工作。pcntl_signal_dispatch()declare(ticks=1);PHP 7.1

...
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
    pcntl_signal_dispatch();
});
...

解决方案 3 ❤ (PHP 7.1+)

使用而不是工作,即使使用空的进度回调函数体pcntl_async_signals(true)declare(ticks=1);

这可能是我个人会使用的,所以我将把完整的代码放在这里:

<?php

pcntl_async_signals(true);

$shutdownHandler = function() {
    die("Exiting\n");
};

pcntl_signal(SIGINT, $shutdownHandler);

while (true) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_NOPROGRESS, false);
    curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {});
    curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
    curl_exec($ch);
    curl_close($ch);
}

所有这三种解决方案都会导致 在Ctrl+C 后几乎立即退出PHP 7.1


答案 2

发生了什么事情?

发送命令时, 尝试在退出之前完成当前操作。Ctrl + CPHP

为什么我(OP)的代码没有退出?

请参阅最后的“最后的想法”以获取更详细的解释

您的代码不会退出,因为不会完成,因此在完成当前操作之前无法退出。cURLPHP

您为本练习选择的网站永远不会加载。

如何修复

要修复此问题,请将 URL 替换为可加载的内容,例如 https://google.com

证明

我编写了自己的代码示例,以向我确切地显示何时/何地决定退出:PHP

<?php
declare(ticks=1);

$t = 1;
$shutdownHandler = function () {
    exit("EXITING NOW\n");
};

pcntl_signal(SIGINT, $shutdownHandler);

while (true) {
    print "$t\n";
    $t++;
}

在终端中运行它时,您可以更清楚地了解PHP的运行方式:enter image description here

在上图中,您可以看到,当我通过(由箭头显示)发出SIGINT命令时,它完成了它正在执行的操作,然后退出。Ctrl + C

这意味着,如果我的修复是正确的,那么在OP的代码中杀死curl所需要的只是一个简单的更改:URL

<?php

declare(ticks=1);
$t = 1;
$shutdownHandler = function () {
    exit("\nEXITING\n");
};

pcntl_signal(SIGINT, $shutdownHandler);


while (true) {
    echo "CURL$t\n";
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://google.com');
    curl_exec($ch);
    curl_close($ch);
}

然后运行:enter image description here

中提琴!正如预期的那样,脚本在当前进程完成后终止,就像它应该的那样。

最后的思考

您尝试卷曲的网站实际上是在没有尽头的旅行中发送您的代码。能够停止进程的唯一操作是 CTRL + X最大执行时间设置或CURLOPT_TIMEOUT cURL 选项。当您取出OUT pcntl_signal(SIGINT,$shutdownHandler)时,CTRL + C之所以有效,是因为PHP不再具有通过内部函数正常关闭的负担。由于PHP不是并发的,当你确实有处理程序时,它必须等待轮到它才能执行 - 它永远不会得到它,因为cURL任务永远不会完成,从而给你留下永无止境的结果。

我希望这有帮助!


推荐