linux

Linux C++中如何使用信号量

小樊
61
2025-04-28 06:36:11
栏目: 编程语言

在 Linux 下使用 C++ 进行信号量(semaphore)编程,通常涉及 POSIX 信号量(POSIX semaphores)。POSIX 信号量提供了一种用于进程间同步或线程间同步的机制。以下是如何在 C++ 中使用 POSIX 信号量的基本指南,包括创建、操作和销毁信号量。

POSIX 信号量的基本概念

POSIX 信号量分为两种类型:

  1. 命名信号量(Named Semaphores):通过名称进行标识,可以在不同进程之间共享。
  2. 未命名信号量(Unnamed Semaphores):通常用于线程间同步,存储在内存中。

本文将重点介绍命名信号量的使用方法。

使用步骤

  1. 包含必要的头文件
  2. 创建或打开信号量
  3. 初始化信号量
  4. 执行 P 操作(等待)和 V 操作(释放)
  5. 销毁信号量

示例代码

下面是一个使用命名 POSIX 信号量的示例程序,演示了如何在多个进程之间同步对共享资源的访问。

1. 创建一个共享内存段和信号量

首先,创建一个共享内存段,并在其中放置一个信号量。这里我们使用 shm_open 创建命名共享内存,并使用 sem_init 初始化信号量。

// semaphore_example.cpp
#include <iostream>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <unistd.h>
#include <cstring>

// 定义信号量和共享内存的名称
const char* SEM_NAME = "/my_semaphore";
const char* SHM_NAME = "/my_shared_memory";

int main() {
    // 创建或打开信号量
    sem_t* sem = sem_open(SEM_NAME, O_CREAT, 0644, 1);
    if (sem == SEM_FAILED) {
        perror("sem_open");
        return EXIT_FAILURE;
    }

    // 创建或打开共享内存
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0644);
    if (shm_fd == -1) {
        perror("shm_open");
        sem_close(sem);
        return EXIT_FAILURE;
    }

    // 设置共享内存大小
    if (ftruncate(shm_fd, sizeof(int)) == -1) {
        perror("ftruncate");
        close(shm_fd);
        sem_close(sem);
        return EXIT_FAILURE;
    }

    // 映射共享内存
    int* shared_value = static_cast<int*>(mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0));
    if (shared_value == MAP_FAILED) {
        perror("mmap");
        close(shm_fd);
        sem_close(sem);
        return EXIT_FAILURE;
    }

    // 初始化共享资源
    *shared_value = 0;
    std::cout << "Shared resource initialized to " << *shared_value << std::endl;

    // 关闭共享内存映射
    munmap(shared_value, sizeof(int));

    // 示例操作:多个进程对共享资源进行增减操作
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        close(shm_fd);
        sem_close(sem);
        return EXIT_FAILURE;
    } else if (pid == 0) { // 子进程
        // 等待信号量
        if (sem_wait(sem) == -1) {
            perror("sem_wait");
            close(shm_fd);
            sem_close(sem);
            return EXIT_FAILURE;
        }

        // 访问共享资源
        shared_value = static_cast<int*>(mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0));
        if (shared_value == MAP_FAILED) {
            perror("mmap");
            close(shm_fd);
            sem_close(sem);
            return EXIT_FAILURE;
        }

        (*shared_value)++;
        std::cout << "Child process incremented shared value to " << *shared_value << std::endl;

        munmap(shared_value, sizeof(int));
        // 释放信号量
        if (sem_post(sem) == -1) {
            perror("sem_post");
        }

        close(shm_fd);
        sem_close(sem);
        return EXIT_SUCCESS;
    } else { // 父进程
        // 等待信号量
        if (sem_wait(sem) == -1) {
            perror("sem_wait");
            close(shm_fd);
            sem_close(sem);
            return EXIT_FAILURE;
        }

        // 访问共享资源
        shared_value = static_cast<int*>(mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0));
        if (shared_value == MAP_FAILED) {
            perror("mmap");
            close(shm_fd);
            sem_close(sem);
            return EXIT_FAILURE;
        }

        (*shared_value)--;
        std::cout << "Parent process decremented shared value to " << *shared_value << std::endl;

        munmap(shared_value, sizeof(int));
        // 释放信号量
        if (sem_post(sem) == -1) {
            perror("sem_post");
        }

        close(shm_fd);
        sem_close(sem);

        // 等待子进程结束
        wait(NULL);
    }

    return EXIT_SUCCESS;
}

2. 编译程序

使用 g++ 编译上述代码:

g++ -o semaphore_example semaphore_example.cpp

3. 运行程序

首先运行程序,它将创建共享内存和信号量:

./semaphore_example

输出示例:

Shared resource initialized to 0
Parent process decremented shared value to -1
Child process incremented shared value to 0

在这个示例中:

详细说明

1. 创建或打开信号量

sem_t* sem = sem_open(SEM_NAME, O_CREAT, 0644, 1);

2. 初始化共享内存

int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0644);
ftruncate(shm_fd, sizeof(int));
int* shared_value = static_cast<int*>(mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0));
*shared_value = 0;

3. 执行 P 操作和 V 操作

sem_wait(sem); // P 操作:等待信号量,计数减一
// 访问共享资源
sem_post(sem); // V 操作:释放信号量,计数加一

4. 销毁信号量

当不再需要信号量时,应该销毁它以释放资源:

sem_close(sem);
sem_unlink(SEM_NAME);

注意事项

  1. 同步机制选择

    • 如果仅在单个进程的多个线程之间同步,可以考虑使用 C++11 提供的线程同步机制,如 std::mutexstd::condition_variable 等,更加方便和安全。
    • POSIX 信号量适用于跨进程的同步。
  2. 错误处理

    • 始终检查系统调用的返回值,并进行适当的错误处理。
  3. 资源管理

    • 确保在不再需要时关闭信号量和共享内存,避免资源泄漏。
    • 使用 sem_unlink 删除命名信号量,防止僵尸信号量占用系统资源。
  4. 原子性操作

    • 信号量的 P 和 V 操作是原子的,确保了对共享资源的互斥访问。

使用 C++11 线程同步(可选)

如果你的应用仅涉及多线程同步,推荐使用 C++11 提供的同步机制,因为它们更易于使用且与 C++ 语言集成更好。例如:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int shared_value = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);
    ++shared_value;
    std::cout << "Incremented to " << shared_value << std::endl;
}

void decrement() {
    std::lock_guard<std::mutex> lock(mtx);
    --shared_value;
    std::cout << "Decremented to " << shared_value << std::endl;
}

int main() {
    std::thread t1(increment);
    std::thread t2(decrement);

    t1.join();
    t2.join();

    return 0;
}

使用 std::mutexstd::lock_guard 可以简化同步操作,避免手动管理锁的获取和释放。

总结

POSIX 信号量是一种强大的进程间和线程间同步机制,适用于需要跨进程访问共享资源的场景。通过正确地创建、操作和销毁信号量,可以有效地控制对共享资源的访问,防止竞态条件和数据不一致的问题。然而,对于仅涉及多线程同步的应用,C++11 提供的同步机制更加简洁和高效,推荐优先使用。

0
看了该问题的人还看了