Подтвердить что ты не робот

Как заблокировать запуск двух экземпляров одной и той же программы?

Мне нужно убедиться, что пользователь может одновременно запускать только один экземпляр моей программы.
Это означает, что я должен проверять программно, работает ли одна и та же программа и выходить в таком случае.

Первое, что мне пришло в голову, это создать файл где-нибудь, когда программа запустится. Затем каждый другой экземпляр программы будет проверять этот файл и выйти, если он его найдет.
Проблема в том, что программа должна всегда изящно выйти и иметь возможность удалить созданный файл, чтобы это работало. В случае, скажем, отключения питания, файл блокировки остается на месте, и программа не может быть запущена снова.

Чтобы решить эту проблему, я решил сохранить первый идентификатор процесса программы в файле блокировки, и когда запускается другой экземпляр, он проверяет, прикреплен ли PID к файлу некоторому запущенному процессу.
Если файл не существует, он пуст или PID не соответствует существующему процессу, программа продолжает работать и записывает свой собственный PID в файл.

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

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

Итак, есть ли другой (более чистый и безопасный) способ сделать это? В идеале это будет работать со стандартом ISO 98 С++ и на Windows и * nix. Если это невозможно, независимо от платформы, Linux/Unix является для меня приоритетом.

4b9b3361

Ответ 1

Есть несколько методов, которые вы можете использовать, чтобы разрешить только один экземпляр вашего приложения:

Способ 1. Глобальный объект синхронизации или память

Обычно это делается путем создания именованного глобального мьютекса или события. Если он уже создан, значит, программа уже запущена.

Например, в Windows вы можете сделать:

    #define APPLICATION_INSTANCE_MUTEX_NAME "{BA49C45E-B29A-4359-A07C-51B65B5571AD}"

    //Make sure at most one instance of the tool is running
    HANDLE hMutexOneInstance(::CreateMutex( NULL, TRUE, APPLICATION_INSTANCE_MUTEX_NAME));
    bool bAlreadyRunning((::GetLastError() == ERROR_ALREADY_EXISTS));
    if (hMutexOneInstance == NULL || bAlreadyRunning)
    {
        if(hMutexOneInstance)
        {
            ::ReleaseMutex(hMutexOneInstance);
            ::CloseHandle(hMutexOneInstance);
        }
        throw std::exception("The application is already running");
    }

Метод 2: блокировка файла, вторая программа не может открыть файл, поэтому он открывает

Вы также можете открыть файл исключительно, заблокировав его при открытии приложения. Если файл уже открыт исключительно, и ваше приложение не может получить дескриптор файла, то это означает, что программа уже запущена. В Windows вы бы просто не указали флаги общего доступа FILE_SHARE_WRITE для файла, который вы открываете с помощью API CreateFile. В Linux вы бы использовали flock.

Способ 3: поиск имени процесса:

Вы можете перечислить активные процессы и искать один с вашим именем процесса.

Ответ 2

Ваш метод записи процесса pid в файл является общим, который используется во многих различных установленных приложениях. Фактически, если вы посмотрите в свой каталог /var/run прямо сейчас, я уверен, вы найдете несколько файлов *.pid.

Как вы говорите, он не на 100% надежный, потому что есть вероятность того, что pids запутаются. Я слышал о программах, использующих flock(), чтобы заблокировать файл приложения, который будет автоматически разблокирован ОС при выходе из процесса, но этот метод более специфичен для платформы и менее прозрачен.

Ответ 3

Будьте осторожны, программа с одним экземпляром - это собственный отказ в обслуживании.

Ссылка описывает проблему безопасности обнаружения одного экземпляра и предлагает использовать защищенный метод, поэтому, по крайней мере, процессы с более низкими правами не могут использовать проблему.

Ответ 4

Очень un-unix запрещает запуск нескольких экземпляров программы.

Если программа является, скажем, сетевым демоном, не нужно активно запрещать несколько экземпляров - только первый экземпляр получает прослушивание сокета, поэтому последующие экземпляры автоматически запускаются. Если это, скажем, РСУБД, нет необходимости активно запрещать несколько экземпляров - только первый экземпляр открывает и блокирует файлы. и др.

Ответ 5

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

"Правильный" способ сделать это, вероятно, использовать общую память: http://www.cs.cf.ac.uk/Dave/C/node27.html

Ответ 6

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

Этот метод используется довольно многими приложениями, но единственное, что я могу вспомнить, это VMware. И да, есть моменты, когда вам нужно войти и удалить "*.lck", когда все вклинится.

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

Ответ 7

У меня нет хорошего решения, но две мысли:

  • Вы можете добавить возможность ping для запроса другого процесса и убедиться, что это не несвязанный процесс. Firefox делает что-то подобное в Linux и не запускает новый экземпляр, если он уже запущен.

  • Если вы используете обработчик сигнала, вы можете убедиться, что файл pid удален на всех, кроме kill -9

Ответ 8

Я просматриваю список процессов, ища имя исполняемого файла моих приложений с соответствующими параметрами командной строки, затем выходим, если есть совпадение.

Мое приложение может запускаться несколько раз, но я не хочу, чтобы он одновременно запускал один и тот же файл конфигурации.

Очевидно, что это зависит от Windows, но одна и та же концепция довольно проста в любой системе * NIX даже без определенных библиотек, просто открыв команду оболочки ps -ef или изменив ее и ищет ваше приложение.

   '*************************************************************************
   '     Sub: CheckForProcess()
   '  Author: Ron Savage
   '    Date: 10/31/2007
   '
   ' This routine checks for a running process of this app with the same
   ' command line parameters.
   '*************************************************************************
   Private Function CheckForProcess(ByVal processText As String) As Boolean
      Dim isRunning As Boolean = False
      Dim search As New ManagementObjectSearcher("SELECT * FROM Win32_process")
      Dim info As ManagementObject
      Dim procName As String = ""
      Dim procId As String = ""
      Dim procCommandLine As String = ""

      For Each info In search.Get()
         If (IsNothing(info.Properties("Name").Value)) Then procName = "NULL" Else procName = Split(info.Properties("Name").Value.ToString, ".")(0)
         If (IsNothing(info.Properties("ProcessId").Value)) Then procId = "NULL" Else procId = info.Properties("ProcessId").Value.ToString
         If (IsNothing(info.Properties("CommandLine").Value)) Then procCommandLine = "NULL" Else procCommandLine = info.Properties("CommandLine").Value.ToString

         If (Not procId.Equals(Me.processId) And procName.Equals(processName) And procCommandLine.Contains(processText)) Then
            isRunning = True
         End If
      Next

      Return (isRunning)
   End Function

Ответ 9

Я нашел кросс-платформенный способ сделать это, используя boost/interprocess/sync/named_mutex. Пожалуйста, обратитесь к этому ответу fooobar.com/info/241152/...