在 Linux 下使用 C++ 進行信號量(semaphore)編程,通常涉及 POSIX 信號量(POSIX semaphores)。POSIX 信號量提供了一種用于進程間同步或線程間同步的機制。以下是如何在 C++ 中使用 POSIX 信號量的基本指南,包括創建、操作和銷毀信號量。
POSIX 信號量分為兩種類型:
本文將重點介紹命名信號量的使用方法。
下面是一個使用命名 POSIX 信號量的示例程序,演示了如何在多個進程之間同步對共享資源的訪問。
首先,創建一個共享內存段,并在其中放置一個信號量。這里我們使用 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;
}
使用 g++
編譯上述代碼:
g++ -o semaphore_example semaphore_example.cpp
首先運行程序,它將創建共享內存和信號量:
./semaphore_example
輸出示例:
Shared resource initialized to 0
Parent process decremented shared value to -1
Child process incremented shared value to 0
在這個示例中:
sem_wait
(相當于 P 操作)等待信號量,確?;コ庠L問。sem_post
(相當于 V 操作)釋放信號量,允許其他進程訪問共享資源。sem_t* sem = sem_open(SEM_NAME, O_CREAT, 0644, 1);
SEM_NAME
:信號量的名稱,所有需要訪問該信號量的進程必須使用相同的名稱。O_CREAT
:如果信號量不存在,則創建它。0644
:權限模式,類似于文件權限。1
:初始值,表示信號量的計數。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;
shm_open
創建或打開共享內存對象。ftruncate
設置共享內存的大小。mmap
將共享內存映射到進程的地址空間。sem_wait(sem); // P 操作:等待信號量,計數減一
// 訪問共享資源
sem_post(sem); // V 操作:釋放信號量,計數加一
sem_wait
:減少信號量的計數。如果計數為零,調用進程將被阻塞,直到有其他進程調用 sem_post
增加計數。sem_post
:增加信號量的計數,并喚醒一個等待的進程(如果有)。當不再需要信號量時,應該銷毀它以釋放資源:
sem_close(sem);
sem_unlink(SEM_NAME);
sem_close
:關閉信號量的描述符。sem_unlink
:刪除命名信號量,只有當引用計數為零時才會真正刪除。同步機制選擇:
std::mutex
、std::condition_variable
等,更加方便和安全。錯誤處理:
資源管理:
sem_unlink
刪除命名信號量,防止僵尸信號量占用系統資源。原子性操作:
如果你的應用僅涉及多線程同步,推薦使用 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::mutex
和 std::lock_guard
可以簡化同步操作,避免手動管理鎖的獲取和釋放。
POSIX 信號量是一種強大的進程間和線程間同步機制,適用于需要跨進程訪問共享資源的場景。通過正確地創建、操作和銷毀信號量,可以有效地控制對共享資源的訪問,防止競態條件和數據不一致的問題。然而,對于僅涉及多線程同步的應用,C++11 提供的同步機制更加簡潔和高效,推薦優先使用。