Intereting Posts
Как создать каталог на сервере с помощью Java-сокета клиентской машины? Простая программа Java в 100 раз медленнее после подключения USB-точки доступа gitk “требуют Tk” Невозможно установить прокси для Android SDK Manager на Ubuntu 12.10 Сессия PHP не работает на серверах Понимание программы на C ++ Если сервер возвращает 404, proxy_pass на альтернативный сервер sSMTP больше не работает – ‘Неверный ответ: 501 5.5.4 HELO / EHLO аргумент MYEMAILADDRESS@gmail.com недействительный, закрывающее соединение.’ android sdk не указан в студии Android Как разрешить mod_rewrite помещать не буквенно-цифровые символы в строку запроса? Как защитить файлы от несанкционированной загрузки Включение / установка расширения Curl (OSX Server / PHP 5.3.3 / Apache 2.2) redirect вывода оболочки в файл с амперсандом возвращает некоторые дополнительные команды на выходе Как я могу поставить задержку с C #? EC2onRails + SSL + Apache: нет ответа на порт 443

Как создать одно экземплярное приложение на C или C ++

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

Хороший способ:

#include  #include  int pid_file = open("/var/run/whatever.pid", O_CREAT | O_RDWR, 0666); int rc = flock(pid_file, LOCK_EX | LOCK_NB); if(rc) { if(EWOULDBLOCK == errno) ; // another instance is running } else { // this is the first instance } 

Обратите внимание, что блокировка позволяет игнорировать устаревшие файлы pid (т. Е. Вам не нужно их удалять). Когда приложение прекращает свою работу по какой-либо причине, ОС освобождает для вас файловую блокировку.

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

Более продвинутый метод заключается в создании и привязке сокета домена unix с использованием предопределенного имени сокета. Привязка успешно применяется для первого экземпляра вашего приложения. Опять же, ОС отсоединяет сокет, когда приложение прекращается по какой-либо причине. Когда bind() завершает работу, другой экземпляр приложения может connect() и использовать этот сокет для передачи его аргументов командной строки в первый экземпляр.

Вот решение в C ++. Он использует рекомендации сокета Максима. Мне нравится это решение лучше, чем решение блокировки на основе файлов, потому что на основе файла происходит сбой, если процесс выходит из строя и не удаляет файл блокировки. Другой пользователь не сможет удалить файл и заблокировать его. Сокеты автоматически удаляются при выходе из процесса.

Применение:

 int main() { SingletonProcess singleton(5555); // pick a port number to use that is specific to this app if (!singleton()) { cerr << "process running already. See " << singleton.GetLockFileName() << endl; return 1; } ... rest of the app } 

Код:

 #include  class SingletonProcess { public: SingletonProcess(uint16_t port0) : socket_fd(-1) , rc(1) , port(port0) { } ~SingletonProcess() { if (socket_fd != -1) { close(socket_fd); } } bool operator()() { if (socket_fd == -1 || rc) { socket_fd = -1; rc = 1; if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { throw std::runtime_error(std::string("Could not create socket: ") + strerror(errno)); } else { struct sockaddr_in name; name.sin_family = AF_INET; name.sin_port = htons (port); name.sin_addr.s_addr = htonl (INADDR_ANY); rc = bind (socket_fd, (struct sockaddr *) &name, sizeof (name)); } } return (socket_fd != -1 && rc == 0); } std::string GetLockFileName() { return "port " + std::to_string(port); } private: int socket_fd = -1; int rc; uint16_t port; }; 

Избегайте блокировки на основе файлов

Всегда полезно избегать механизма блокировки на основе файлов для реализации экземпляра singleton приложения. Пользователь всегда может переименовать файл блокировки на другое имя и снова запустить приложение следующим образом:

 mv lockfile.pid lockfile1.pid 

Где lockfile.pid – файл блокировки, на основе которого проверяется наличие перед запуском приложения.

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

Таким образом, лучшим вариантом будет привязка к сокету inet. Обратите внимание, что сокеты домена unix находятся в файловой системе и не являются надежными.

Кроме того, вы также можете сделать это с помощью DBUS.

Для окон – именованный объект ядра (например, CreateEvent, CreateMutex). Для unix, pid-файл – создайте файл и напишите ему идентификатор процесса.

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

Для деама – обычным способом является файл /var/run/app.pid .

Для пользовательского приложения у меня было больше проблем с приложениями, которые мешали мне запускать их дважды, чем с возможностью запуска дважды приложения, которое не должно было выполняться. Таким образом, ответ на вопрос «почему и по какой сфере» очень важен и, вероятно, даст конкретный ответ на вопрос о причинах и предполагаемом объеме.

Это, кажется, не упоминается – можно создать мьютекс в общей памяти, но его необходимо пометить как разделяемый атрибутами (не тестировался):

 pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); pthread_mutex_t *mutex = shmat(SHARED_MEMORY_ID, NULL, 0); pthread_mutex_init(mutex, &attr); 

Есть также семафоры разделяемой памяти (но мне не удалось выяснить, как заблокировать один):

 int sem_id = semget(SHARED_MEMORY_KEY, 1, 0); 

Вы можете создать «анонимное пространство имен» сокетов AF_UNIX. Это полностью зависит от Linux, но имеет то преимущество, что ни одна файловая система не должна существовать.

Прочтите страницу man для unix (7) для получения дополнительной информации.

Никто не упомянул об этом, но sem_open() создает реальный семафор в соответствии с современными POSIX-совместимыми операционными системами. Если вы даете семафору начальное значение 1, оно становится мьютексом (при условии, что оно строго освобождается только в том случае, если блокировка была успешно получена).

С несколькими sem_open() основе sem_open() вы можете создать все общие эквивалентные объекты с именем Windows – с именем mutexes, семафорами и именованными событиями. Именованные события с «ручным», установленным в true, несколько сложнее эмулировать (для правильного эмуляции CreateEvent() , SetEvent() и ResetEvent() требуется четыре объекта семафора). Во всяком случае, я отвлекся.

В качестве альтернативы, называется разделяемая память. Вы можете инициализировать мьютез pthread с атрибутом «shared process» в именованной общей памяти, а затем все процессы могут безопасно получить доступ к этому объекту mutex после открытия дескриптора разделяемой памяти с помощью shm_open() / mmap() . sem_open() проще, если он доступен для вашей платформы (если это не так, это должно быть разумно).

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

Не забудьте разблокировать и закрыть мьютексы при выходе из приложения.

Основываясь на подсказках в ответе Максима, это мое решение POSIX для двухпользовательского демона (т.е. одно приложение, которое может выступать в роли демона и как клиент, взаимодействующий с этим деmonoм). Эта схема имеет то преимущество, что обеспечивает элегантное решение проблемы, когда первым запущенным экземпляром должен быть демон, и все последующие исполнения должны просто загружать работу на этом демоне. Это полный пример, но не хватает большого количества материала, который должен сделать настоящий демон (например, используя syslog для ведения журнала и fork чтобы правильно помещать себя в фоновый режим , откатывать привилегии и т. Д.), Но он уже довольно длинный и полностью работает как есть. Я тестировал это только на Linux до сих пор, но IIRC он должен быть совместим с POSIX.

В этом примере клиенты могут отправлять целые числа, переданные им в качестве первого аргумента командной строки, и анализируются с помощью atoi через сокет демона, который печатает его на stdout . С такими сокетами можно также передавать массивы, структуры и даже файловые дескрипторы (см. man 7 unix ).

 #include  #include  #include  #include  #include  #include  #include  #include  #include  #define SOCKET_NAME "/tmp/exampled" static int socket_fd = -1; static bool isdaemon = false; static bool run = true; /* returns * -1 on errors * 0 on successful server bindings * 1 on successful client connects */ int singleton_connect(const char *name) { int len, tmpd; struct sockaddr_un addr = {0}; if ((tmpd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { printf("Could not create socket: '%s'.\n", strerror(errno)); return -1; } /* fill in socket address structure */ addr.sun_family = AF_UNIX; strcpy(addr.sun_path, name); len = offsetof(struct sockaddr_un, sun_path) + strlen(name); int ret; unsigned int retries = 1; do { /* bind the name to the descriptor */ ret = bind(tmpd, (struct sockaddr *)&addr, len); /* if this succeeds there was no daemon before */ if (ret == 0) { socket_fd = tmpd; isdaemon = true; return 0; } else { if (errno == EADDRINUSE) { ret = connect(tmpd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); if (ret != 0) { if (errno == ECONNREFUSED) { printf("Could not connect to socket - assuming daemon died.\n"); unlink(name); continue; } printf("Could not connect to socket: '%s'.\n", strerror(errno)); continue; } printf("Daemon is already running.\n"); socket_fd = tmpd; return 1; } printf("Could not bind to socket: '%s'.\n", strerror(errno)); continue; } } while (retries-- > 0); printf("Could neither connect to an existing daemon nor become one.\n"); close(tmpd); return -1; } static void cleanup(void) { if (socket_fd >= 0) { if (isdaemon) { if (unlink(SOCKET_NAME) < 0) printf("Could not remove FIFO.\n"); } else close(socket_fd); } } static void handler(int sig) { run = false; } int main(int argc, char **argv) { switch (singleton_connect(SOCKET_NAME)) { case 0: { /* Daemon */ struct sigaction sa; sa.sa_handler = &handler; sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) != 0 || sigaction(SIGQUIT, &sa, NULL) != 0 || sigaction(SIGTERM, &sa, NULL) != 0) { printf("Could not set up signal handlers!\n"); cleanup(); return EXIT_FAILURE; } struct msghdr msg = {0}; struct iovec iovec; int client_arg; iovec.iov_base = &client_arg; iovec.iov_len = sizeof(client_arg); msg.msg_iov = &iovec; msg.msg_iovlen = 1; while (run) { int ret = recvmsg(socket_fd, &msg, MSG_DONTWAIT); if (ret != sizeof(client_arg)) { if (errno != EAGAIN && errno != EWOULDBLOCK) { printf("Error while accessing socket: %s\n", strerror(errno)); exit(1); } printf("No further client_args in socket.\n"); } else { printf("received client_arg=%d\n", client_arg); } /* do daemon stuff */ sleep(1); } printf("Dropped out of daemon loop. Shutting down.\n"); cleanup(); return EXIT_FAILURE; } case 1: { /* Client */ if (argc < 2) { printf("Usage: %s \n", argv[0]); return EXIT_FAILURE; } struct iovec iovec; struct msghdr msg = {0}; int client_arg = atoi(argv[1]); iovec.iov_base = &client_arg; iovec.iov_len = sizeof(client_arg); msg.msg_iov = &iovec; msg.msg_iovlen = 1; int ret = sendmsg(socket_fd, &msg, 0); if (ret != sizeof(client_arg)) { if (ret < 0) printf("Could not send device address to daemon: '%s'!\n", strerror(errno)); else printf("Could not send device address to daemon completely!\n"); cleanup(); return EXIT_FAILURE; } printf("Sent client_arg (%d) to daemon.\n", client_arg); break; } default: cleanup(); return EXIT_FAILURE; } cleanup(); return EXIT_SUCCESS; } 

Я только что написал один и проверил.

 #define PID_FILE "/tmp/pidfile" static void create_pidfile(void) { int fd = open(PID_FILE, O_RDWR | O_CREAT | O_EXCL, 0); close(fd); } int main(void) { int fd = open(PID_FILE, O_RDONLY); if (fd > 0) { close(fd); return 0; } // make sure only one instance is running create_pidfile(); } 

Просто запустите этот код в отдельном streamе:

 void lock() { while(1) { ofstream closer("myapplock.locker", ios::trunc); closer << "locked"; closer.close(); } } 

Запустите это как свой основной код:

 int main() { ifstream reader("myapplock.locker"); string s; reader >> s; if (s != "locked") { //your code } return 0; } 

Вот решение, основанное на sem_open

 /* *compile with : *gcc single.c -o single -pthread */ /* * run multiple instance on 'single', and check the behavior */ #include  #include  #include  #include  #include  #include  #define SEM_NAME "/mysem_911" int main() { sem_t *sem; int rc; sem = sem_open(SEM_NAME, O_CREAT, S_IRWXU, 1); if(sem==SEM_FAILED){ printf("sem_open: failed errno:%d\n", errno); } rc=sem_trywait(sem); if(rc == 0){ printf("Obtained lock !!!\n"); sleep(10); //sem_post(sem); sem_unlink(SEM_NAME); }else{ printf("Lock not obtained\n"); } } 

Один из комментариев к другому ответу говорит: «Я обнаружил, что sem_open () скорее не хватает». Я не уверен в специфике того, чего не хватает