信號量是什么?
AI解釋:信號量(Semaphore)是操作系統中用于 進程同步與互斥 的經典工具,由荷蘭計算機科學家 Edsger Dijkstra 在 1965 年提出。它本質上是一個 非負整數變量,通過原子操作(P 操作和 V 操作)實現對共享資源的訪問控制。
System-V版本的信號量相關API:
Ftok
函數定義:
key_t ftok(const char *pathname, int proj_id);
函數作用:
獲取唯一的key值標識符。
參數解釋:
- 傳入一個有效的文件路徑和一個
<255
的整數數字,返回一個具有唯一性的key
。
semget
函數定義:
int semget(key_t key, int nsems, int semflg);
函數作用:
獲取或者創建信號量集的文件描述符 fd
。
參數解釋:
key
:ftok
調用成功返回的key
。nsems
:要創建/獲取的信號量集合中的信號量數量cnt
。semflg
:- 傳入
(IPC_CREATE | IPC_EXCL | 文件權限)
表示創建信號量集合,并返回fd
。 - 傳入
IPC_CREATE
表示獲取指定的信號量集合fd
。
- 傳入
semctl
函數定義:
int semctl(int semid, int semnum, int cmd, ...);
函數作用:
控制指定的信號量集合,刪除或者修改。
參數解釋:
semid
:指定的信號量集的文件描述符fd
。semnum
:要控制的信號量集合中的下標(數組下標從0開始)。cmd
:- 設置為
IPC_RMID
時,表示刪除指定信號量集,可忽略semnum
(設為0)和可變參數。 - 設置為
SETVAL
時,表示設置信號量的值,需手動創建聯合體union semun
并設置val
字段:union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO */ } sem_un; sem_un.val = val; // 設置val字段 semctl(fd, i, SETVAL, sem_un); // 傳入可變參數
- 設置為
semop
函數定義:
int semop(int semid, struct sembuf *sops, unsigned nsops);
函數作用:
對信號量進行 PV 操作。
PV操作:
使用系統提供的 struct sembuf
結構體:
struct sembuf {unsigned short sem_num; /* 信號量下標 */short sem_op; /* 操作值(-1為P操作,1為V操作) */short sem_flg; /* 標志,一般設為SEM_UNDO */
};
參數解釋:
sem_num
:信號量集合中信號量的下標。sem_op
:-1
表示 P 操作,1
表示 V 操作(本質是對信號量值進行加減)。sem_flg
:一般設置為SEM_UNDO
,表示異常時銷毀信號量。semid
:信號量集描述符。sops
:struct sembuf
數組地址,支持批量 PV 操作。nsops
:數組大小。
實現基于簡單建造者模式的信號量封裝
日志模塊 gitee:https://gitee.com/LOG_C/log
#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include "Log.hpp"
using namespace ns_log;#define CREATE_SEM (IPC_CREAT | IPC_EXCL | 0666)
#define GET_SEM (IPC_CREAT)
#define P_OP (-1) /* P操作 */
#define V_OP (1) /* V操作 */
const std::string SEM_PATH = "../tmp"; // 需要先創建有效目錄
const int proj_id = 123;
const int default_sem_nums = 1;std::string ToHex(int num) {char buf[64];sprintf(buf, "0x%x", num);return buf;
}class Semaphore {
private:int _fd;void PV(int op) {struct sembuf sem_buf;sem_buf.sem_num = 0;sem_buf.sem_op = op;sem_buf.sem_flg = SEM_UNDO;int n = semop(_fd, &sem_buf, 1);if (n < 0) {LOG(DEBUG, "op = %d失敗\n", op);return;}}public:Semaphore(int fd) : _fd(fd) {}void P() { PV(P_OP); LOG(DEBUG, "P操作done\n"); }void V() { PV(V_OP); LOG(DEBUG, "V操作done\n"); }~Semaphore() {if (_fd > 0) {int n = semctl(_fd, 0, IPC_RMID);if (n < 0) {LOG(ERROR, "semctl的IPC_RMID操作異常!,異常信息:%s\n", strerror(errno));}LOG(DEBUG, "銷毀信號量done\n");}}
};
using SemPtr = std::shared_ptr<Semaphore>;class SemaphoreBuilder {
private:int _val;bool Init(int fd, int num, int val) {union semun {int val;struct semid_ds *buf;unsigned short *array;struct seminfo *__buf;} sem_un;sem_un.val = val;for (int i = 0; i < num; i++) {int ret = semctl(fd, i, SETVAL, sem_un);if (ret < 0) {LOG(ERROR, "semctl的SETVAL操作異常!,異常信息:%s\n", strerror(errno));return false;}}return true;}public:SemaphoreBuilder() {}SemaphoreBuilder& SetVal(int val) { _val = val; return *this; }SemPtr Build(int flag, int num = default_sem_nums) {LOG(DEBUG, "開始build信號量\n");key_t key = ftok(SEM_PATH.c_str(), proj_id);if (key == -1) {LOG(ERROR, "ftok操作異常!,異常信息:%s\n", strerror(errno));return nullptr;}LOG(INFO, "frok的key為:%s\n", ToHex(key).c_str());int sem_fd = semget(key, num, flag);if (sem_fd == -1) {LOG(ERROR, "semget操作異常!,異常信息:%s\n", strerror(errno));return nullptr;}if (flag == CREATE_SEM) {Init(sem_fd, num, _val);}return std::make_shared<Semaphore>(sem_fd);}
};
理解建造者模式
定義:
將一個復雜對象的構建過程與其表示分離,使得同樣的構建過程可以創建不同的表示。通過分解復雜對象的構建步驟,每一步創建一部分,最后組合成完整對象。
角色:
- 產品(Product):
- 被構建的復雜對象,包含多個組成部分(屬性和行為)。
- 抽象建造者(Builder):
- 定義構建復雜對象各部分的接口,以及返回最終產品的方法。
- 具體建造者(Concrete Builder):
- 實現抽象建造者接口,具體構建對象的各部分,生成不同類型或形式的產品。
- 指揮者(Director):
- 負責調用具體建造者構建對象,不依賴具體類,僅通過建造者接口操作。
重構后的建造者模式(信號量場景)
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include "Log.hpp"
using namespace ns_log;#define CREATE_SEM (IPC_CREAT | IPC_EXCL | 0666)
#define GET_SEM (IPC_CREAT)
#define P_OP (-1) /* P操作 */
#define V_OP (1) /* V操作 */
const std::string SEM_PATH = "../tmp";
const int proj_id = 123;
const int default_sem_nums = 1;enum { FTOK_ERROR = 1, SEMGET_ERROR, SEMCTL_ERROR };std::string ToHex(int num) {char buf[64];sprintf(buf, "0x%x", num);return buf;
}// 產品:信號量
class Semaphore {
private:int _fd;void PV(int who, int op) {struct sembuf sem_buf;sem_buf.sem_num = who;sem_buf.sem_op = op;sem_buf.sem_flg = SEM_UNDO;int n = semop(_fd, &sem_buf, 1);if (n < 0) {LOG(DEBUG, "op = %d失敗\n", op);return;}}public:Semaphore(int fd) : _fd(fd) {}void P(int who) { PV(who, P_OP); LOG(DEBUG, "P操作done\n"); }void V(int who) { PV(who, V_OP); LOG(DEBUG, "V操作done\n"); }int Fd() const { return _fd; }~Semaphore() {if (_fd > 0) {int n = semctl(_fd, 0, IPC_RMID);if (n < 0) {LOG(ERROR, "semctl的IPC_RMID操作異常!,異常信息:%s\n", strerror(errno));}LOG(DEBUG, "銷毀信號量done\n");}}
};// 抽象建造者
class Builder {
public:virtual ~Builder() {}virtual void BuildKey() = 0; // 獲取鍵值virtual void SetPerm(int perm) = 0; // 設置權限virtual void SetSemNum(int num) = 0; // 設置信號量數量virtual void SetVal(const std::vector<int>& init_vals) = 0; // 初始化值virtual void Init() = 0; // 初始化信號量virtual void Build(int flag) = 0; // 構建信號量
};// 具體建造者
class SemaphoreBuilder : public Builder {
private:bool init(int fd, int index, int val) {union semun {int val;struct semid_ds *buf;unsigned short *array;struct seminfo *__buf;} sem_un;sem_un.val = val;int ret = semctl(fd, index, SETVAL, sem_un);if (ret < 0) {LOG(ERROR, "semctl的SETVAL操作異常!,異常信息:%s\n", strerror(errno));return false;}return true;}public:std::shared_ptr<Semaphore> GetSemaphore() { return _sem; }void BuildKey() override {_key = ftok(SEM_PATH.c_str(), proj_id);if (_key == -1) {LOG(ERROR, "ftok操作異常!,異常信息:%s\n", strerror(errno));exit(FTOK_ERROR);}LOG(INFO, "frok的key為:%s\n", ToHex(_key).c_str());}void SetPerm(int perm) override { _perm = perm; }void SetSemNum(int num) override { _sem_cnts = num; }void SetVal(const std::vector<int>& init_vals) override { _init_vals = init_vals; }void Init() override {if (_sem_cnts > 0 && _sem_cnts == _init_vals.size()) {for (int i = 0; i < _sem_cnts; i++) {if (!init(_sem->Fd(), i, _init_vals[i])) {exit(SEMCTL_ERROR);}}}}void Build(int flag) override {int sem_fd = semget(_key, _sem_cnts, flag);if (sem_fd == -1) {LOG(ERROR, "semget操作異常!,異常信息:%s\n", strerror(errno));exit(SEMGET_ERROR);}_sem = std::make_shared<Semaphore>(sem_fd);}private:std::shared_ptr<Semaphore> _sem;key_t _key;int _perm;int _sem_cnts;std::vector<int> _init_vals;
};// 指揮者
class Director {
public:void construct(std::shared_ptr<SemaphoreBuilder>& sem_builder,int flag,int num,const std::vector<int>& vals,int perm) {sem_builder->BuildKey();sem_builder->SetPerm(perm);sem_builder->SetVal(vals);sem_builder->SetSemNum(num);sem_builder->Build(flag);if (flag == CREATE_SEM) {sem_builder->Init();}}
};