Неблокирующий режим файлов в PHP
Позже я внимательно прочитал документацию на теме. По всей видимости, это ничего не даст в отношении проверки системы хранения на зависание.
Смотрел недавно исходники языка ПХП, хотел изучить подробнее как реализованы там стримы. Пока читал, случайно натолкнулся на недокументированную возможность — у функции fopen, как оказалось, есть режим открытия в неблокирующем режиме (буква n).
#if defined(O_NONBLOCK)
if (strchr(mode, 'n')) {
flags |= O_NONBLOCK;
}
#endif
Доступно не на всех системах, только там, где есть константа O_NONBLOCK в fcntl.h, то есть в Линуксах, например.
Для меня это довольно полезное знание, так как я давно ищу возможность проверить из ПХП не зависла ли подмонтированная через NFS система хранения. Из-за таких зависаний виснет драйвер в ядре и все системные вызовы, которые к нему обращаются. В конечном счёте виснет процесс, который пытается работать с такой файловой системой, да так, что его не получается убить через SIGKILL.
В общем, надо будет попробовать при следующем таком инциденте — не спасает ли открытие в неблокирующем режиме. Код, кажется, должен выглядеть примерно так:
function tryToOpen(string $path, float $timeout): ?bool
{
$dio_fd = dio_open($path, O_RDONLY | O_NONBLOCK);
if ($dio_fd === false) {
return null;
}
foreach (glob('/proc/self/fd/*') as $file) {
if (is_link($file) && readlink($file) === $path) {
$fd = basename($file);
$read = [fopen("php://fd/$fd", 'rn')];
$write = $except = null;
$timeout_us = (int) ($timeout * 1e6);
return stream_select($read, $write, $except, 0, $timeout_us) === 1;
}
}
return null;
}
Тут используется модуль ПХП Direct IO, чтобы обратиться к системной функции open мимо ПХП. Приходится так делать, потому что функция fopen, которая есть в языке, сначала попытается проверить существование файла и в моей задаче сразу зависнет.
После этого я, пользуясь /proc, ищу среди своих файловых дескрипторов тот, который отвечает за только что открытый файл и переоткрываю его через fopen (в этом месте делается дубликат файлового дескриптора, а прежний закрывается — вызывается dup), так как мы тут работаем с номером файлового дескриптора, то проверка на существование файла не используется.
Переоткрытие файла нужно, так как в вызов stream_select, который я использую для ожидания данных из файла с таймаутом, можно передать результат fopen, но не dio_open.
Надеюсь случай испытать этот код представится нескоро.
А почему tryToOpen а не tryOpen ?
Ну я даже не знаю как ответить на этот вопрос. А почему должно быть tryOpen?