Как обрабатывать readlink () "/proc/self/exe", когда исполняемый файл заменяется во время выполнения?

в моем приложении C++ мое приложение делает execv() на fork()Ed дочерний процесс для использования одного и того же исполняемого файла для обработки некоторой работы в новом дочернем процессе с различными аргументами, который взаимодействует с каналами родительского процесса. Чтобы получить путь к себе, я выполняю следующий код на порту Linux (у меня другой код на Macintosh):

  const size_t bufSize = PATH_MAX + 1;
  char dirNameBuffer[bufSize];
  // Read the symbolic link '/proc/self/exe'.
  const char *linkName = "/proc/self/exe";
  const int ret = int(readlink(linkName, dirNameBuffer, bufSize - 1));

однако, если во время выполнения исполняемого файла я заменяю исполняемый файл обновленной версией двоичного файла на диске readlink() результирующая строка: "/usr/local/bin/myExecutable (deleted)"

Я понимаю, что мой исполняемый файл был заменен более новой обновленной версией и оригиналом для /proc/self/exe теперь заменяется, однако, когда я иду в execv() теперь он терпит неудачу с errno 2 - No such file or directory. из-за дополнительного трейлинга " (deleted)" в результате.

Я бы execv() либо использовать старый исполняемый файл для себя, либо обновленный. Я мог просто обнаружить строку, заканчивающуюся на " (deleted)" и измените его на опустите это и разрешите обновленный исполняемый файл, но это кажется мне неуклюжим.

как я могу execv() текущий исполняемый файл (или его замена, если это проще) с новым набором аргументов, когда исходный исполняемый файл был заменен обновленным во время выполнения?

3 ответов


вместо readlink чтобы узнать путь к вашему исполняемому файлу, вы можете напрямую вызвать open on /proc/self/exe. Поскольку ядро уже имеет открытый fd для процессов, которые в настоящее время выполняются, это даст вам fd независимо от того, был ли путь заменен новым исполняемым файлом или нет.

Далее, вы можете использовать fexecve вместо execv, которая принимает


одно решение находится при запуске исполняемого файла (например, в начале main()), чтобы прочитать значение ссылки /proc/self/exe один раз и хранить его статически впрок:

  static string savedBinary;
  static bool initialized = false;

  // To deal with issue of long running executable having its binary replaced
  // with a newer one on disk, we compute the resolved binary once at startup.
  if (!initialized) {
    const size_t bufSize = PATH_MAX + 1;
    char dirNameBuffer[bufSize];
    // Read the symbolic link '/proc/self/exe'.
    const char *linkName = "/proc/self/exe";
    const int ret = int(readlink(linkName, dirNameBuffer, bufSize - 1));

    savedBinary = dirNameBuffer;

    // On at least Linux, if the executable is replaced, readlink() of
    // "/proc/self/exe" gives "/usr/local/bin/flume (deleted)".
    // Therefore, we just compute the binary location statically once at
    // startup, before it can possibly be replaced, but we leave this code
    // here as an extra precaution.
    const string deleted(" (deleted)");
    const size_t deletedSize = deleted.size();
    const size_t pathSize = savedBinary.size();

    if (pathSize > deletedSize) {
      const size_t matchPos = pathSize - deletedSize;

      if (0 == savedBinary.compare(matchPos, deletedSize, deleted)) {
        // Deleted original binary, Issue warning, throw an exception, or exit.
        // Or cludge the original path with: savedBinary.erase(matchPos);
      }
    }
    initialized = true;
  }

  // Use savedBinary value.

таким образом, очень маловероятно, что исходный исполняемый файл будет заменен в течение микросекунд main() кэширования пути к его двоичному файлу. Таким образом, длительное приложение (например, часы или дни) может быть заменено на диске, но в исходном вопросе оно может fork() и execv() к обновленный двоичный файл, который может исправить ошибку. Это имеет дополнительное преимущество работы на разных платформах, и, следовательно,отличающийся код Macintosh для чтения двоичного пути может быть также защищен от двоичной замены после запуска.

предупреждение примечание редакции: readlink не null завершает строку, поэтому вышеуказанная программа может работать или не работать случайно, если буфер не был заполнен нулями перед вызовом readlink


причина, по которой вы получите (deleted) часть символической ссылки заключается в том, что вы заменили файл правильным двоичным текстом программы другим файлом, и символическая ссылка на исполняемый файл никогда не будет действительной снова. Предположим, вы используете эту символическую ссылку, чтобы получить таблицу символов этой программы или загрузить некоторые данные, встроенные в нее, и изменяете программу... таблица будет неправильной, и вы даже можете аварийно завершить работу своей программы. Исполняемый файл для программы, которую вы выполняли, отсутствует больше доступно (вы удалили его), и программа, которую вы поставили на место, не соответствует двоичному файлу, который вы выполняете.

когда вы отсоединить(2) программа, которая выполняется, ядро отмечает эту символическую ссылку в /proc, поэтому программа может

  • обнаружить, что двоичный файл был удален и больше не доступен.
  • разрешить вам по-прежнему собирать некоторую информацию о фамилии, которую он имел (вместо удаления символической ссылки от /proc дерево)

вы не можете писать в файл, который выполняется ядром, но никто не мешает тебе стереть этот файл. Файл будет продолжать присутствовать в файловой системе до тех пор, пока вы его выполняете, но имя не указывает на него (это пространство будет освобождено после процесса выход(2)) ядро не стирает свое содержимое, пока количество индексов в памяти ядра не достигнет нуля, что происходит, когда все использования (ссылки) на этот файл надлежащий.