代码:

#include <atomic>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <cstring>
#include <cerrno>
#include <signal.h>
#include <cstdio>

const char* SHM_NAME = "/myclass_test6_instance";
const char* SEM_NAME = "/myclass_test6_sem";

struct SharedData {
    bool     active;
    pid_t    owner_pid;
    uint64_t generation;
};

class ScopedSemLock {
public:
    explicit ScopedSemLock(sem_t* sem)
        : mSem(sem), mLocked(false), mErrno(0) {
        if (!mSem) {
            mErrno = EINVAL;
            return;
        }
        while (true) {
            if (sem_wait(mSem) == 0) {
                mLocked = true;
                return;
            }
            if (errno == EINTR) {
                continue;
            }
            mErrno = errno;
            break;
        }
    }

    ~ScopedSemLock() {
        if (mLocked && sem_post(mSem) == -1) {
            std::perror("sem_post");
        }
    }

    bool locked() const { return mLocked; }
    int  error()  const { return mErrno; }

private:
    sem_t* mSem;
    bool   mLocked;
    int    mErrno;
};

class SPMyClass;
class MyClass;

class SPMyClass {
public:
    SPMyClass() = default;
    virtual ~SPMyClass() = default;

    static SPMyClass* create();
    static void destroy(void* instance);
};

class MyClass : public SPMyClass {
public:
    static std::atomic<bool> mInstanceExists;

    MyClass();
    ~MyClass() override;

    void create();
    void destroy();
    bool isCreated() const { return mIsCreated; }

private:
    bool mIsCreated;
    bool mDestroyed;

    static bool initSharedResources();
    static void releaseSharedResources();
    static bool isProcessAlive(pid_t pid);

    static SharedData*       mSharedData;
    static sem_t*            mSemaphore;
    static int               mShmFd;
    static bool              mIsInitialized;
    static std::atomic<int>  mObjectCounter;
};

std::atomic<bool> MyClass::mInstanceExists{false};
std::atomic<int>  MyClass::mObjectCounter{0};
SharedData*       MyClass::mSharedData    = nullptr;
sem_t*            MyClass::mSemaphore     = nullptr;
int               MyClass::mShmFd         = -1;
bool              MyClass::mIsInitialized = false;

MyClass::MyClass()
    : mIsCreated(false), mDestroyed(false) {
    mObjectCounter.fetch_add(1, std::memory_order_relaxed);
}

MyClass::~MyClass() {
    destroy();
    if (mObjectCounter.fetch_sub(1, std::memory_order_acq_rel) == 1) {
        releaseSharedResources();
    }
}

bool MyClass::isProcessAlive(pid_t pid) {
    if (pid <= 0) {
        return false;
    }
    if (::kill(pid, 0) == 0) {
        return true;
    }
    return errno != ESRCH;
}

bool MyClass::initSharedResources() {
    if (mIsInitialized) {
        return true;
    }

    sem_t* semaphore = sem_open(SEM_NAME, O_CREAT, 0666, 1);
    if (semaphore == SEM_FAILED) {
        std::perror("sem_open");
        return false;
    }

    int shmFd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shmFd == -1) {
        std::perror("shm_open");
        sem_close(semaphore);
        return false;
    }

    struct stat st {};
    if (fstat(shmFd, &st) == -1) {
        std::perror("fstat");
        ::close(shmFd);
        sem_close(semaphore);
        return false;
    }

    bool needInit = st.st_size < static_cast<off_t>(sizeof(SharedData));
    if (needInit) {
        if (ftruncate(shmFd, sizeof(SharedData)) == -1) {
            std::perror("ftruncate");
            ::close(shmFd);
            sem_close(semaphore);
            return false;
        }
    }

    void* addr = mmap(nullptr, sizeof(SharedData),
                      PROT_READ | PROT_WRITE,
                      MAP_SHARED,
                      shmFd, 0);
    if (addr == MAP_FAILED) {
        std::perror("mmap");
        ::close(shmFd);
        sem_close(semaphore);
        return false;
    }

    mSemaphore  = semaphore;
    mShmFd      = shmFd;
    mSharedData = static_cast<SharedData*>(addr);

    if (needInit) {
        std::memset(mSharedData, 0, sizeof(SharedData));
    }

    mIsInitialized = true;
    return true;
}

void MyClass::releaseSharedResources() {
    if (mSharedData) {
        munmap(mSharedData, sizeof(SharedData));
        mSharedData = nullptr;
    }
    if (mShmFd != -1) {
        ::close(mShmFd);
        mShmFd = -1;
    }
    if (mSemaphore) {
        sem_close(mSemaphore);
        mSemaphore = nullptr;
    }
    mIsInitialized = false;
}

void MyClass::create() {
    if (mIsCreated) {
        std::cout << "当前对象已经拥有实例,无需重复创建。" << std::endl;
        return;
    }

    if (!initSharedResources()) {
        std::cerr << "初始化共享资源失败,无法创建实例。" << std::endl;
        return;
    }

    ScopedSemLock lock(mSemaphore);
    if (!lock.locked()) {
        std::cerr << "获取信号量失败: "
                  << std::strerror(lock.error()) << std::endl;
        return;
    }

    const pid_t myPid = ::getpid();

    if (!mSharedData->active) {
        mSharedData->active    = true;
        mSharedData->owner_pid = myPid;
        ++mSharedData->generation;
        mIsCreated = true;
        mInstanceExists.store(true, std::memory_order_release);
        std::cout << "成功创建实例,PID: " << myPid << std::endl;
        return;
    }

    const pid_t ownerPid = mSharedData->owner_pid;

    if (!isProcessAlive(ownerPid)) {
        std::cout << "检测到僵尸实例 (PID " << ownerPid
                  << "),接管实例。" << std::endl;
        mSharedData->active    = true;
        mSharedData->owner_pid = myPid;
        ++mSharedData->generation;
        mIsCreated = true;
        mInstanceExists.store(true, std::memory_order_release);
        std::cout << "成功创建实例,PID: " << myPid << std::endl;
        return;
    }

    if (ownerPid == myPid) {
        std::cout << "当前进程已经持有全局实例,禁止重复创建。" << std::endl;
    } else {
        std::cout << "已有其他进程在运行,持有者 PID: "
                  << ownerPid << std::endl;
    }
}

void MyClass::destroy() {
    if (mDestroyed) {
        return;
    }
    mDestroyed = true;

    if (mIsCreated && mSemaphore && mSharedData) {
        ScopedSemLock lock(mSemaphore);
        if (!lock.locked()) {
            std::cerr << "destroy: 获取信号量失败: "
                      << std::strerror(lock.error()) << std::endl;
        } else if (mSharedData->active &&
                   mSharedData->owner_pid == ::getpid()) {
            mSharedData->active    = false;
            mSharedData->owner_pid = 0;
            ++mSharedData->generation;
            mInstanceExists.store(false, std::memory_order_release);
            std::cout << "实例已销毁,PID: " << ::getpid() << std::endl;
        }
    }

    mIsCreated = false;
}

SPMyClass* SPMyClass::create() {
    MyClass* instance = new MyClass();
    instance->create();
    if (!instance->isCreated()) {
        delete instance;
        return nullptr;
    }
    return instance;
}

void SPMyClass::destroy(void* instance) {
    if (!instance) {
        return;
    }
    static_cast<MyClass*>(instance)->destroy();
}

int main() {
    std::cout << "Before instance: "
              << MyClass::mInstanceExists.load() << std::endl;

    SPMyClass* a = SPMyClass::create();
    std::cout << "After instance a: "
              << MyClass::mInstanceExists.load() << std::endl;

    SPMyClass* b = SPMyClass::create();
    std::cout << "After instance b: "
              << MyClass::mInstanceExists.load() << std::endl;

    std::cout << "等待100秒..." << std::endl;
    sleep(100);

    std::cout << "\n删除实例a..." << std::endl;
    delete a;
    std::cout << "After delete a: "
              << MyClass::mInstanceExists.load() << std::endl;

    std::cout << "\n删除实例b..." << std::endl;
    delete b;
    std::cout << "After delete b: "
              << MyClass::mInstanceExists.load() << std::endl;

    return 0;
}

主要原理解析

1. 共享内存 + 信号量

  • 共享内存(shm_open + mmap
    通过 POSIX 共享内存,所有进程都映射到同一块内存区域 SharedData,用来存储实例的状态信息。

  • 信号量(sem_open
    用信号量做进程间同步,保证对共享内存的访问是互斥的,避免竞态条件。


2. 共享数据结构 SharedData

struct SharedData {
    bool     active;      // 实例是否被占用
    pid_t    owner_pid;   // 拥有实例的进程ID
    uint64_t generation;  // 版本号,表示实例状态的变化次数
};
  • active 标记实例是否被占用。
  • owner_pid 记录当前拥有实例的进程ID。
  • generation 用于版本控制(可用于检测实例是否被重置等)。

3. 创建实例的流程(MyClass::create()

  • 先初始化共享资源(共享内存和信号量)。
  • 通过信号量加锁,保证对共享内存的独占访问。
  • 检查 SharedData 中的 active 标志:
    • 如果 false,说明没有进程持有实例,当前进程设置 active = trueowner_pid = 当前进程PID,成功创建实例。
    • 如果 true,说明已有进程持有实例,进一步检查持有者进程是否存活:
      • 如果持有者进程已死(僵尸进程),当前进程“接管”实例,更新 owner_pid 和 generation
      • 如果持有者进程仍然存活:
        • 如果是当前进程自己,拒绝重复创建。
        • 如果是其他进程,拒绝创建,提示已有其他进程持有实例。

4. 销毁实例的流程(MyClass::destroy()

  • 通过信号量加锁,保证对共享内存的独占访问。
  • 如果当前进程是实例拥有者,清除 active 标志和 owner_pid,表示实例释放。
  • 更新 generation,通知状态变化。
  • 其他进程检测到 active == false 后,可以创建新实例。

5. 进程存活检测(isProcessAlive(pid_t pid)

  • 通过 kill(pid, 0) 系统调用检测进程是否存在。
  • 如果进程不存在,允许其他进程接管实例。

如何保证多进程中只有一个真实实例?

  • 共享内存:所有进程共享同一份状态数据。
  • 信号量:保证对共享状态的访问是互斥的,避免竞态。
  • 进程存活检测:避免死锁或僵尸实例占用资源。
  • 原子变量 mInstanceExists:本地进程内标记实例是否存在,方便快速判断。

总结

  • 通过共享内存和信号量实现跨进程的“单例”状态管理。
  • 只有一个进程能成功将 SharedData::active 置为 true 并成为拥有者。
  • 其他进程要么检测到实例已被占用且持有者存活,拒绝创建;要么检测到持有者死掉,接管实例。
  • 进程退出时释放实例,允许其他进程创建。

更多推荐