第三篇文档:自助终端全自动生产线 —— 建造者模式封装 System V 信号量完整工程实现

文档定位

核心工程实现,结合设计模式思想,分步构建信号量对象,完整代码、流程拆解、类比、思维导图,可直接用于企业项目。

一、趣味类比:建造者 = 银行自助终端生产线

老式封装:一次性把所有材料塞进机器,无法自定义选配; 建造者模式生产线拆分 4 个独立工序,分步自由配置,最后一键生成完整银行(信号量集):

  1. 工序 1(Builder 初始化):新建空白终端;
  2. 工序 2:选配银行门牌号(key 配置:ftok 私有 / IPC_PRIVATE);
  3. 工序 3:设置窗口总数、每个窗口初始工位;
  4. 工序 4:选配创建权限、标志位(IPC_CREAT/IPC_EXCL);
  5. 工序 5(Director 构建):所有配置完成,一键生成可用银行;
  6. 配套工具:内置 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 # 无残留,代码自动销毁

三、建造者模式工作流程拆解(自助终端流水线)

  1. 初始化空白建造者(空终端);
  2. 自由分步配置 key、窗口数量、初始工位、权限标志;
  3. Director 指挥者封装标准化场景(互斥锁 / 私有计数信号量),业务层无需关心底层配置细节;
  4. 调用 build () 一次性创建内核信号量集并完成初始化,返回完整可用产品;
  5. 产品自带 P/V、销毁接口,业务代码极简。

四、本篇核心思维导图(建造者 + 信号量融合)

五、建造者模式适配 System V 信号量的独特优势

  1. 配置解耦:创建 key、设置信号量数量、初始化 val、权限标志完全分离,按需选配;
  2. 场景复用:Director 封装互斥锁、限流计数、私有 IPC 等通用场景,业务一行代码创建;
  3. 高扩展:后续新增超时 semop、批量操作、信号量查询功能,仅新增 Builder 配置接口,不改动原有业务代码;
  4. 容错统一:所有内核 API 错误捕获统一放在 build 函数内部,业务无需重复写 perror;
  5. 资源自动管理:产品销毁接口统一释放内核信号量 + 堆内存,杜绝 IPC 资源泄露。

六、三篇完整系列总结

  1. 第一篇:吃透 System V 信号量内核原理与原生 API,明白底层银行模型;
  2. 第二篇:发现原生 API 缺陷,实现简易封装,找到耦合痛点;
  3. 第三篇:引入建造者模式分层解耦,工程化封装,标准化生产线,适配企业多并发同步场景。

谢谢

相关推荐
小和尚同志12 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble13 小时前
c端系统,其实更像一个信息展示平台
前端
战族狼魂13 小时前
GPT-5.6与Grok 4.5重磅发布
人工智能·算法·大模型·大语言模型
李姆斯13 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
白日焰火114 小时前
基于 OpenSpec 实现规范驱动开发
算法·交互
jinyishu_14 小时前
C++ string使用方法
开发语言·c++
imuliuliang14 小时前
关于图搜索算法的性能建模与可预测性研究7
算法
名字还没想好☜14 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司14 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
乐观勇敢坚强的老彭14 小时前
C++浮点数使用注意事项
开发语言·c++