posix_spawnp зависает до завершения дочернего процесса в Debian 9

Вы можете воспользоваться командой OBJECT IDLETIME , которая возвращает количество секунд, в течение которых объект, хранящийся на указанном ключе, бездействует (не запрашивается операциями чтения или записи).

Пример кода выглядит следующим образом:

#!/bin/sh

redis-cli -p 6379 keys "*" | while read LINE ;
do
val=`redis-cli -p 6379 object idletime $LINE`;
if [ $val -gt $((30 * 24 * 60 * 60)) ];
then
  echo "$LINE";
  # del=`redis-cli -p 6379 del $LINE`;  # be careful with del
  # echo $del;
fi
done;

В вашей ситуации вы можете заменить redis-cli -p 6379 на:

redis-cli -h redis_host -p redis_port -a redis_password

0
09.05.2019, 03:58
1 ответ

Я просмотрел glibc 2.24, который поставляется с Debian 9.

posix _spawnp (и posix _spawn )реализованы как пользовательский -код C режима, а не системный вызов. Он делает следующее:

  1. создает канал с флагом O_CLOEXEC.
  2. вызывает клон с флагом CLONE_VFORK. vfork ограничивает связь между дочерними и родительскими -здесь в игру вступает канал.
  3. Родитель закрывает конец канала для записи и пытается читать из конца для чтения.
  4. дочерний элемент закрывает конец канала для чтения и выполняет все действия с файлами.
  5. дочерний процесс вызывает execvp. В случае успеха труба должна быть закрыта . В случае неудачи дочерний элемент запишет в канал код ошибки.
  6. Чтение родителя возвращается. Если execvp в дочернем элементе завершился успешно, чтение должно завершиться ошибкой , поскольку конец записи канала должен был быть закрыт , а родитель устанавливает переменную ecв 0. Если чтение завершается успешно, ec— это код ошибки, отправленный ему потомком.
  7. posix _spawnp в родительском объекте возвращает ec.

Я выделил эти слова курсивом, потому что это ошибка.

Когда posix _spawnp выполняет все эти posix_spawn_file_actions_addcloseдействия, код glibc достаточно умен, чтобы дублировать конец записи канала, когда он видит действие файла, которое влияет на этот файловый дескриптор.

int p = args->pipe[1];
...
/* Dup the pipe fd onto an unoccupied one to avoid any file
   operation to clobber it.  */
if ((action->action.close_action.fd == p)
    || (action->action.open_action.fd == p)
    || (action->action.dup2_action.fd == p))
  {
    if ((ret = __dup (p)) < 0)
      goto fail;
    p = ret;
  }

Проблема в том, что дубликат не дублирует флаг O_CLOEXEC, поэтому fd передается процессу, который был запущен дочерним процессом, и не будет закрыт, пока этот процесс не завершится. Чтение в родительском элементе не вернется, пока это не произойдет.

Ошибка была исправлена ​​в этой фиксации . Теперь потомок сообщает об успехе или неудаче родителю, используя общую переменную вместо канала.

Если вы застряли с этой версией glibc,вы ничего не можете сделать, кроме как не сообщать posix _spawnp закрыть конец записи канала (вероятно, logfd+2 в вашем примере кода ).

1
28.01.2020, 02:40

Теги

Похожие вопросы