MiddleКодИногдаЕщё не отвечали
Напишите кроссплатформенную программу, гарантирующую запуск только одного экземпляра.
Напишите кроссплатформенную защиту единственного экземпляра: когда вторая копия программы запускается при уже работающей первой, вторая должна это обнаружить и завершиться.
Ограничения:
- На POSIX используйте атомарную файловую блокировку (избегайте гонки TOCTOU с чтением PID и проверкой живости).
- На Windows используйте именованный объект ядра, чтобы проверка работала между процессами.
- Блокировка/handle должны освобождаться автоматически при выходе, включая обычное завершение.
class SingleInstance {
public:
explicit SingleInstance(const std::string& lock_path); // POSIX
~SingleInstance();
SingleInstance(const SingleInstance&) = delete;
// ваш код здесь
};
Допишите реализацию.
Два канонических подхода: на POSIX — эксклюзивный flock(LOCK_EX | LOCK_NB) на PID-файле; на Windows — именованный мьютекс через CreateMutex с проверкой ERROR_ALREADY_EXISTS. Оба освобождают блокировку при выходе.
- ✗Читать PID из файла и проверять, жив ли этот процесс — содержит гонку TOCTOU; используйте файловую блокировку, которая атомарна
- ✗Не записывать текущий PID в файл блокировки — затрудняет отладку; нельзя определить, какой экземпляр держит блокировку
- ✗Забывать закрывать fd перед exec() в модели fork/exec — блокировка будет освобождена; используйте O_CLOEXEC или fcntl(F_SETFD, FD_CLOEXEC)
- →Как передать сигнал уже запущенному экземпляру для вывода его окна на передний план в Windows?
- →Что происходит с файловой блокировкой, если процесс убивается SIGKILL?
Оглавление
Единственный экземпляр программы
POSIX (Linux/macOS)
#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
#include <string>
class SingleInstance {
public:
explicit SingleInstance(const std::string& lock_path)
: path_(lock_path) {
fd_ = ::open(path_.c_str(), O_CREAT | O_RDWR | O_CLOEXEC, 0644);
if (fd_ < 0) {
perror("open");
std::exit(1);
}
// Попытка неблокирующей эксклюзивной блокировки
if (::flock(fd_, LOCK_EX | LOCK_NB) != 0) {
std::fprintf(stderr, "Another instance is already running.\n");
::close(fd_);
std::exit(1);
}
// Записываем PID для удобства отладки
::ftruncate(fd_, 0);
char buf[32];
int n = std::snprintf(buf, sizeof(buf), "%d\n", (int)::getpid());
::write(fd_, buf, n);
}
~SingleInstance() {
::flock(fd_, LOCK_UN);
::close(fd_);
// Не удаляем файл — другой экземпляр создаст его заново
}
SingleInstance(const SingleInstance&) = delete;
SingleInstance& operator=(const SingleInstance&) = delete;
private:
std::string path_;
int fd_ = -1;
};
int main() {
SingleInstance guard("/tmp/myapp.lock");
// Основная логика...
std::printf("Running as PID %d\n", (int)::getpid());
// Блокировка освобождается при уничтожении guard или завершении процесса
}
Windows
#ifdef _WIN32
#include <windows.h>
#include <cstdio>
#include <cstdlib>
class SingleInstance {
public:
explicit SingleInstance(const wchar_t* mutex_name) {
handle_ = ::CreateMutexW(nullptr, TRUE, mutex_name);
if (handle_ == nullptr || ::GetLastError() == ERROR_ALREADY_EXISTS) {
std::fprintf(stderr, "Another instance is already running.\n");
if (handle_) ::CloseHandle(handle_);
std::exit(1);
}
}
~SingleInstance() {
if (handle_) {
::ReleaseMutex(handle_);
::CloseHandle(handle_);
}
}
SingleInstance(const SingleInstance&) = delete;
private:
HANDLE handle_ = nullptr;
};
int main() {
SingleInstance guard(L"Global\\MyApp_SingleInstance");
// Основная логика...
}
#endif
Оглавление