文档定位
核心工程实现,结合设计模式思想,分步构建信号量对象,完整代码、流程拆解、类比、思维导图,可直接用于企业项目。
一、趣味类比:建造者 = 银行自助终端生产线
老式封装:一次性把所有材料塞进机器,无法自定义选配; 建造者模式生产线拆分 4 个独立工序,分步自由配置,最后一键生成完整银行(信号量集):
- 工序 1(Builder 初始化):新建空白终端;
- 工序 2:选配银行门牌号(key 配置:ftok 私有 / IPC_PRIVATE);
- 工序 3:设置窗口总数、每个窗口初始工位;
- 工序 4:选配创建权限、标志位(IPC_CREAT/IPC_EXCL);
- 工序 5(Director 构建):所有配置完成,一键生成可用银行;
- 配套工具:内置 P/V、自动销毁、参数校验、错误捕获。
建造者模式四大角色对应信号量场景
表格
| 建造者角色 | 对应代码模块 | 银行类比 |
|---|---|---|
| Product 产品 | SemProduct | 完整配置好的银行(信号量实例) |
| Builder 抽象建造者 | SemBuilder | 自助终端标准操作规范(配置接口) |
| ConcreteBuilder 具体建造者 | SemConcreteBuilder | 实际自助终端,实现所有配置步骤 |
| Director 指挥者 | SemDirector | 大堂经理,按流程调度配置,生成产品 |
二、完整可运行建造者模式代码
c
运行
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
// ====================== 1. Product 产品:完整信号量实例 ======================
typedef struct {
key_t key;
int semid;
int sem_cnt;
int* init_vals;
int mode; // 权限 0666
int flag; // IPC_CREAT / IPC_EXCL
} SemProduct;
// 产品对外操作接口
void sem_product_p(SemProduct* prod, int idx) {
struct sembuf op = {idx, -1, 0};
if(semop(prod->semid, &op, 1) == -1) perror("P op fail");
}
void sem_product_v(SemProduct* prod, int idx) {
struct sembuf op = {idx, 1, 0};
if(semop(prod->semid, &op, 1) == -1) perror("V op fail");
}
// 销毁产品,释放内核+堆内存
void sem_product_destroy(SemProduct* prod) {
semctl(prod->semid, 0, IPC_RMID);
free(prod->init_vals);
free(prod);
}
// ====================== 2. 抽象建造者 Builder ======================
typedef struct SemBuilder SemBuilder;
struct SemBuilder {
// 分步配置接口
void (*set_key_ftok)(SemBuilder*, const char*, int proj_id);
void (*set_key_private)(SemBuilder*);
void (*set_sem_count)(SemBuilder*, int cnt);
void (*set_single_init)(SemBuilder*, int idx, int val);
void (*set_mode_flag)(SemBuilder*, int mode, int flag);
// 构建产品
SemProduct* (*build)(SemBuilder*);
// 私有缓存配置
SemProduct* temp_prod;
};
// ====================== 3. 具体建造者 ConcreteBuilder ======================
void set_key_ftok_impl(SemBuilder* b, const char* path, int proj) {
b->temp_prod->key = ftok(path, proj);
}
void set_key_private_impl(SemBuilder* b) {
b->temp_prod->key = IPC_PRIVATE;
}
void set_sem_count_impl(SemBuilder* b, int cnt) {
b->temp_prod->sem_cnt = cnt;
free(b->temp_prod->init_vals);
b->temp_prod->init_vals = calloc(cnt, sizeof(int));
}
void set_single_init_impl(SemBuilder* b, int idx, int val) {
if(idx >=0 && idx < b->temp_prod->sem_cnt)
b->temp_prod->init_vals[idx] = val;
}
void set_mode_flag_impl(SemBuilder* b, int mode, int flag) {
b->temp_prod->mode = mode;
b->temp_prod->flag = flag;
}
// 核心构建方法:生成可用信号量集
SemProduct* build_impl(SemBuilder* b) {
SemProduct* prod = b->temp_prod;
// 创建信号量集
prod->semid = semget(prod->key, prod->sem_cnt, prod->flag | prod->mode);
if(prod->semid == -1) {
perror("semget create fail");
return NULL;
}
// 逐个初始化信号量初始值
for(int i=0; i<prod->sem_cnt; i++) {
semctl(prod->semid, i, SETVAL, prod->init_vals[i]);
}
// 重置临时缓存,返回成品
SemProduct* ret = malloc(sizeof(SemProduct));
memcpy(ret, prod, sizeof(SemProduct));
ret->init_vals = malloc(sizeof(int)*prod->sem_cnt);
memcpy(ret->init_vals, prod->init_vals, sizeof(int)*prod->sem_cnt);
return ret;
}
// 创建具体建造者实例
SemBuilder* create_sem_builder() {
SemBuilder* b = malloc(sizeof(SemBuilder));
b->temp_prod = malloc(sizeof(SemProduct));
memset(b->temp_prod, 0, sizeof(SemProduct));
b->temp_prod->init_vals = NULL;
// 绑定接口实现
b->set_key_ftok = set_key_ftok_impl;
b->set_key_private = set_key_private_impl;
b->set_sem_count = set_sem_count_impl;
b->set_single_init = set_single_init_impl;
b->set_mode_flag = set_mode_flag_impl;
b->build = build_impl;
return b;
}
// ====================== 4. Director 指挥者(标准化构建流程) ======================
// 场景1:构建二元互斥信号量(单窗口,初始值1)
SemProduct* director_build_mutex(SemBuilder* b, const char* path, int proj) {
b->set_key_ftok(b, path, proj);
b->set_sem_count(b, 1);
b->set_single_init(b, 0, 1);
b->set_mode_flag(b, 0666, IPC_CREAT);
return b->build(b);
}
// 场景2:构建私有多窗口计数信号量
SemProduct* director_build_private_sem(SemBuilder* b, int window_num, int init_val) {
b->set_key_private(b);
b->set_sem_count(b, window_num);
for(int i=0; i<window_num; i++) {
b->set_single_init(b, i, init_val);
}
b->set_mode_flag(b, 0600, IPC_CREAT);
return b->build(b);
}
// ====================== 主函数测试演示 ======================
int main() {
// 1. 创建建造者生产线
SemBuilder* builder = create_sem_builder();
// 2. 指挥者生成互斥二元信号量
SemProduct* mutex_sem = director_build_mutex(builder, "./test.txt", 0x99);
if(!mutex_sem) return -1;
printf("建造者生成信号量semid = %d\n", mutex_sem->semid);
// 临界区测试
sem_product_p(mutex_sem, 0);
printf("获取互斥锁,进入临界区\n");
sleep(2);
sem_product_v(mutex_sem, 0);
printf("释放互斥锁\n");
// 销毁资源
sem_product_destroy(mutex_sem);
free(builder->temp_prod);
free(builder);
return 0;
}
编译运行指令:
bash
运行
cpp
touch test.txt
gcc sem_builder.c -o sem_builder
./sem_builder
ipcs -s # 无残留,代码自动销毁
三、建造者模式工作流程拆解(自助终端流水线)
- 初始化空白建造者(空终端);
- 自由分步配置 key、窗口数量、初始工位、权限标志;
- Director 指挥者封装标准化场景(互斥锁 / 私有计数信号量),业务层无需关心底层配置细节;
- 调用 build () 一次性创建内核信号量集并完成初始化,返回完整可用产品;
- 产品自带 P/V、销毁接口,业务代码极简。
四、本篇核心思维导图(建造者 + 信号量融合)

五、建造者模式适配 System V 信号量的独特优势
- 配置解耦:创建 key、设置信号量数量、初始化 val、权限标志完全分离,按需选配;
- 场景复用:Director 封装互斥锁、限流计数、私有 IPC 等通用场景,业务一行代码创建;
- 高扩展:后续新增超时 semop、批量操作、信号量查询功能,仅新增 Builder 配置接口,不改动原有业务代码;
- 容错统一:所有内核 API 错误捕获统一放在 build 函数内部,业务无需重复写 perror;
- 资源自动管理:产品销毁接口统一释放内核信号量 + 堆内存,杜绝 IPC 资源泄露。
六、三篇完整系列总结
- 第一篇:吃透 System V 信号量内核原理与原生 API,明白底层银行模型;
- 第二篇:发现原生 API 缺陷,实现简易封装,找到耦合痛点;
- 第三篇:引入建造者模式分层解耦,工程化封装,标准化生产线,适配企业多并发同步场景。
谢谢