2008-10-05 2 views
21

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

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

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

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

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

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

ответ

30

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

Метод 1: Global объект синхронизации или память

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

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

#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 вы будете использовать стадо.

Метод 3: Поиск по имени процесса:

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

+1

Если два человека скопировать код, который они могли бы в конечном итоге с тем же APPLICATION_INSTANCE_MUTEX_NAME: -> – 2008-10-05 04:05:50

+0

Я. они должны использовать свою собственную уникальную строку :) Я думаю, что изменить его из шахты, хотя :) – 2008-10-05 05:06:06

+0

является существует ли Linux C++ вариант метода 1? (в частности, C++ 11) Было бы замечательно видеть пример! – 2016-04-11 20:20:12

3

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

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

5

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

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

+0

Я думаю, что одна из причин, по которой многие UNIX-приложения по-прежнему используют файлы блокировки, заключается в том, что блокировка файлов в NFS может или не может стоить ничего: http://en.wikipedia.org/wiki/File_locking#Problems – bk1e 2008-10-05 08:14:26

0

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

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

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

0

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

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

Очевидно, что это зависит от 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 
1

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

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

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

4

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

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

5

Будьте осторожны, a single-instance program is its own denial of service.

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

Смежные вопросы