Один экземпляр программы на C++, использующий boost::interprocess

У меня есть консольное приложение, которое я пытаюсь запустить только один раз за раз. Для этого я использовал библиотеку boost interprocess shared_memory_object. Смотрите фрагмент кода ниже,

  boost::scoped_ptr<shared_memory_object> sharedMem;

  try
  {
     sharedMem.reset(
       new shared_memory_object(create_only, "shared_memory", read_write));
  } catch(...)
  {
     // executable is already running
      cerr << "Another instance of this program is running!" << endl;
      return 1;
  }

  // do something

  shared_memory_object::remove("shared_memory");  // remove the shared memory before exiting the application

Дело в том, что этот метод не позволяет моему приложению запускаться более одного раза одновременно; однако, давайте предположим, что пользователь останавливает выполнение программы, тогда память не будет освобождена, и в следующий раз, когда пользователь попытается снова запустить программу, она не запустится. У вас есть какие-нибудь предложения?

Консольное приложение PS C++, ОС: Ubuntu (но решение, которое будет работать и на других платформах, будет идеальным). Спасибо

2 ответа

Что вам нужно сделать, так это перехватить неожиданные завершения программы и соответственно освободить объект общей памяти. Вы можете поймать SIGINT следующим образом, используя signal.h POSIX заголовок:

#include <signal.h>

void handleSIGINT(int param=0) {
    // Reset the shared memory object here
}

int main() {

   // Connect the signal handler to SIGINT
   signal(SIGINT, handleSIGINT);

   // Etc...

}

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

УВЕДОМЛЕНИЕ Ответ телепортирован из Как ограничить количество запущенных экземпляров в C++. Он здесь, поскольку он детально описывает портативное решение с использованием Boost Interprocess и Boost Asio.

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

В Linux (и, возможно, в других ОС?) Вы можете использовать идиому файла блокировки (но она не поддерживается некоторыми файловыми системами и старыми ядрами).

Я бы предложил использовать межпроцессные объекты синхронизации.

Например, используя Boost Interprocess с именем семафор:

#include <boost/interprocess/sync/named_semaphore.hpp>
#include <boost/thread.hpp>
#include <cassert>

int main()
{
    using namespace boost::interprocess;
    named_semaphore sem(open_or_create, "ffed38bd-f0fc-4f79-8838-5301c328268c", 0ul);

    if (sem.try_wait())
    {
        std::cout << "Oops, second instance\n";
    }
    else
    {
        sem.post();

        // feign hard work for 30s
        boost::this_thread::sleep_for(boost::chrono::seconds(30));

        if (sem.try_wait())
        {
            sem.remove("ffed38bd-f0fc-4f79-8838-5301c328268c");
        }
    }
}

Если вы запустите одну копию на заднем плане, новые копии будут "отказываться" запускаться ("Упс, второй экземпляр") в течение 30 секунд.

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

проходит некоторое время

Хехе. Это было сложнее, чем я думал.

Дело в том, что вы хотите убедиться, что блокировка не сохраняется, когда ваше приложение прерывается или уничтожается. В интересах совместного использования методов переносной обработки сигналов:

#include <boost/interprocess/sync/named_semaphore.hpp>
#include <boost/thread.hpp>
#include <cassert>
#include <boost/asio.hpp>

#define MAX_PROCESS_INSTANCES 3

boost::interprocess::named_semaphore sem(
        boost::interprocess::open_or_create, 
        "4de7ddfe-2bd5-428f-b74d-080970f980be",
        MAX_PROCESS_INSTANCES);

// to handle signals:
boost::asio::io_service service;
boost::asio::signal_set sig(service);

int main()
{

    if (sem.try_wait())
    {
        sig.add(SIGINT);
        sig.add(SIGTERM);
        sig.add(SIGABRT);
        sig.async_wait([](boost::system::error_code,int sig){ 
                std::cerr << "Exiting with signal " << sig << "...\n";
                sem.post();
            });
        boost::thread sig_listener([&] { service.run(); });

        boost::this_thread::sleep_for(boost::chrono::seconds(3));

        service.post([&] { sig.cancel(); });
        sig_listener.join();
    }
    else
    {
        std::cout << "More than " << MAX_PROCESS_INSTANCES << " instances not allowed\n";
    }
}

Там можно многое объяснить. Дайте мне знать, если вы заинтересованы.

ПРИМЕЧАНИЕ. Должно быть совершенно очевидно, что если kill -9 используется в вашем приложении (принудительное завершение), тогда все ставки отключены, и вам придется либо удалить объект "Семафор имени", либо явно разблокировать его (post()).

Вот тестовый прогон в моей системе:

sehe@desktop:/tmp$ (for a in {1..6}; do ./test& done; time wait)
More than 3 instances not allowed
More than 3 instances not allowed
More than 3 instances not allowed
Exiting with signal 0...
Exiting with signal 0...
Exiting with signal 0...

real    0m3.005s
user    0m0.013s
sys 0m0.012s
Другие вопросы по тегам