当前位置: 首页 > article >正文

Linux 基于共享内存的循环队列实现

Linux 基于共享内存的循环队列实现

  • Linux 基于共享内存的循环队列实现
    • 一、共享内存与循环队列基础
      • 1.1 共享内存特性
      • 1.2 循环队列优势
    • 二、系统关键技术分析
      • 2.1 共享内存操作API
        • shmget() 创建共享内存
        • shmat() 映射共享内存
      • 2.2 模板类设计要点
    • 三、循环队列核心方法实现
      • 3.1 初始化方法
      • 3.2 入队操作
      • 3.3 出队操作
    • 四、共享内存实践要点
      • 4.1 使用流程
      • 4.2 关键注意事项
    • 五、进程同步问题解决方案
      • 5.1 信号量同步示例
      • 5.2 同步策略建议
    • 六、扩展应用场景
      • 6.1 高性能日志系统
      • 6.2 实时数据采集
      • 6.3 分布式计算中间件
    • 七、完整代码示例与测试结果
      • 7.1 完整代码示例
        • _public.h
        • demo.cpp

Linux 基于共享内存的循环队列实现

一、共享内存与循环队列基础

1.1 共享内存特性

共享内存是Linux系统最高效的进程间通信(IPC)方式,其特点包括:

  • 直接映射到进程地址空间
  • 数据无需在进程间复制
  • 访问速度接近内存访问
  • 需要手动处理同步问题

与其他IPC方式对比:

通信方式传输速度数据持久性使用复杂度
共享内存最快系统重启前有效
管道进程结束失效
消息队列中等系统重启前有效

1.2 循环队列优势

循环队列(Circular Queue)相比普通队列:

  • 有效利用预分配内存空间
  • 时间复杂度均为O(1)
  • 避免假溢出问题
  • 固定大小的特性适合共享内存场景

二、系统关键技术分析

2.1 共享内存操作API

shmget() 创建共享内存
int shmid = shmget(key_t key, size_t size, int shmflg);
  • key:0x5005为自定义标识
  • size:使用sizeof计算模板类大小
  • shmflg:0640权限+IPC_CREAT创建标志
shmat() 映射共享内存
void* shmat(int shmid, const void* shmaddr, int shmflg);
  • shmaddr设为0由系统选择映射地址
  • 返回类型强转为队列指针

2.2 模板类设计要点

template<class TT, int MaxLength>
class squeue {
    // 禁止拷贝构造和赋值
    squeue(const squeue &) = delete;
    squeue &operator=(const squeue &) = delete;
    
    TT m_data[MaxLength]; // 固定大小数组
    // 队列指针和状态管理
};

设计特点:

  • 模板化支持任意数据类型
  • 禁止拷贝防止浅复制问题
  • 内置初始化方法解决共享内存构造问题
  • 环形索引计算:(pos+1)%MaxLength

三、循环队列核心方法实现

3.1 初始化方法

void init() {
    if(!m_inited) {
        m_head = 0;
        m_tail = MaxLength-1; // 初始指向末尾
        m_length = 0;
        memset(m_data, 0, sizeof(m_data));
        m_inited = true;
    }
}

特殊处理原因:共享内存对象不会自动调用构造函数

3.2 入队操作

bool push(const TT &ee) {
    if(full()) return false;
    m_tail = (m_tail+1)%MaxLength; // 先移动尾指针
    m_data[m_tail] = ee;          // 后写入数据
    m_length++;
    return true;
}

3.3 出队操作

bool pop() {
    if(empty()) return false;
    m_head = (m_head+1)%MaxLength; // 移动头指针
    m_length--;
    return true;
}

四、共享内存实践要点

4.1 使用流程

  1. 创建共享内存:shmget()
  2. 映射到进程空间:shmat()
  3. 手动初始化队列:init()
  4. 正常队列操作
  5. 分离映射:shmdt()
  6. (可选)删除共享内存:shmctl()

4.2 关键注意事项

  • 数据持久性:共享内存会一直存在直到系统重启或主动删除
  • 多进程同步:必须使用信号量等同步机制
  • 内存对齐:确保数据结构在共享内存中正确对齐
  • 数据类型限制:建议使用POD(Plain Old Data)类型

五、进程同步问题解决方案

5.1 信号量同步示例

// 创建信号量集
int semid = semget(0x5005, 2, 0640|IPC_CREAT);

// P操作函数
void P(int semid, int num) {
    struct sembuf sb = {num, -1, SEM_UNDO};
    semop(semid, &sb, 1);
}

// V操作函数
void V(int semid, int num) {
    struct sembuf sb = {num, 1, SEM_UNDO};
    semop(semid, &sb, 1);
}

// 使用示例
P(semid, 0); // 申请队列访问权
QQ->push(ee);
V(semid, 0); // 释放队列访问权

5.2 同步策略建议

  1. 互斥信号量:保护队列操作原子性
  2. 空/满信号量:实现生产者-消费者模型
  3. 超时机制:避免死锁
  4. 错误恢复:处理进程异常退出

六、扩展应用场景

6.1 高性能日志系统

  • 多生产者单消费者模式
  • 批量写入磁盘设计
  • 日志分级存储

6.2 实时数据采集

  • 传感器数据缓存
  • 多采集节点汇聚
  • 数据预处理流水线

6.3 分布式计算中间件

  • 任务分配队列
  • 结果收集缓冲区
  • 负载均衡控制器

七、完整代码示例与测试结果

7.1 完整代码示例

_public.h
#ifndef __PUBLIC_HH
#define __PUBLIC_HH

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>

using namespace std;

// 循环队列
template<class TT, int MaxLength>
class squeue
{
private:
    bool    m_inited;        // 队列初始化标志
    TT     m_data[MaxLength];    // 使用数组存储循环队列中的元素
    int    m_head;
    int    m_tail;
    int    m_length;
    squeue(const squeue &) = delete;        // 禁止拷贝构造函数
    squeue &operator=(const squeue &) = delete;    // 禁止赋值函数

public:
    squeue() { init(); }    // 构造函数
    
    // 循环队列的初始化操作
    // 注意:如果用于共享内存的队列 不会调用构造函数 则必须调用此函数初始化 ???
    void init()
    {
        cout << "init\n";
        if(m_inited!=true)
        {
            m_head=0;
            m_tail=MaxLength-1;
            m_length=0;
            memset(m_data, 0, sizeof(m_data));
            m_inited=true;
        }
    }

    // 元素入队
    bool push(const TT &ee)
    {
        if(full() == true)
        {
            cout << "循环队列已满,入队失败\n"; return false;
        }
        // 先移动队伍尾指针 再拷贝数据
        m_tail=(m_tail+1)%MaxLength;
        m_data[m_tail]=ee;
        m_length++;
        return true;
    }

    // 求循环队列的长度
    int size()
    {
        return m_length;
    }

    // 判断循环队列是否为空
    bool empty()
    {
        if(m_length == 0) return true;
        return false;
    }

    // 判断循环队列是否已满
    bool full()
    {
        if(m_length == MaxLength) return true;
        return false;
    }

    // 查看队头元素值,元素不出队
    TT& front()
    {
        return m_data[m_head];
    }

    // 元素出队
    bool pop()
    {
        if(empty() == true) return false;
        m_head=(m_head+1)%MaxLength;
        m_length--;
        return true;
    }

    // 显示村换队列的全部元素
    void printqueue()
    {
        for(int ii=0; ii<size(); ii++)
        {
            cout << "m_data ii=" << ii << " [" << (m_head+ii)%MaxLength << "], value=" \
                << m_data[(m_head+ii)%MaxLength] << endl;
        }
    }
};

#endif
demo.cpp
// 演示基于共享内存的循环队列
#include "_public.h"

int main()
{
    using ElemType=int;

    // 初始化共享内存
    int shmid=shmget(0x5005, sizeof(squeue<ElemType, 5>), 0640|IPC_CREAT);
    if(shmid == -1) {
        cout << "shmget(0x5005) failed\n";
        return -1;
    }

    // 将共享内存连接到当前进程的地址空间
    squeue<ElemType, 5>* QQ = (squeue<ElemType, 5>*)shmat(shmid, 0, 0);
    if(QQ == (void*)-1) {
        cout << "shmat() failed\n";
        return -1;
    }

    // 初始化循环队列 因为不会调用squeue的构造函数
    QQ->init();

    // 创建一个数据元素
    ElemType ee;
    cout << "元素(1 2 3)入队\n";
    ee=1; QQ->push(ee);
    ee=2; QQ->push(ee);
    ee=3; QQ->push(ee);
    cout << "队列长度为" << QQ->size() << endl;
    QQ->printqueue();

    ee=QQ->front(); QQ->pop(); cout << "出队伍元素值为" << ee << endl;
    ee=QQ->front(); QQ->pop(); cout << "出队伍元素值为" << ee << endl;
    cout << "队列长度为" << QQ->size() << endl;
    QQ->printqueue();
    
    cout << "元素(11 12 13 14 15)入队\n";
    ee=11; QQ->push(ee);
    ee=12; QQ->push(ee);
    ee=13; QQ->push(ee);
    ee=14; QQ->push(ee);
    ee=15; QQ->push(ee);
    cout << "队列长度为" << QQ->size() << endl;
    QQ->printqueue();

    // 将共享内存从进程中分离
    shmdt(QQ);
}

http://www.kler.cn/a/549541.html

相关文章:

  • 服务器中部署大模型DeepSeek-R1 | 本地部署DeepSeek-R1大模型 | deepseek-r1部署详细教程
  • Rocky Linux 9.4 安装 VirtualBox 7.1
  • 数据库索引使用 B+树和Java TreeMap 使用红黑树的原因
  • 硬件学习笔记--44 电磁兼容试验-8 振铃波试验介绍
  • 26. 未来一瞥:量子计算
  • HCIA项目实践--静态路由的综合实验
  • 【故障处理】- RMAN-06593: platform name ‘Linux x86 64-bitElapsed: 00:00:00.00‘
  • JDK1.8新特性面试题
  • 使用 Python 爬虫和 FFmpeg 爬取 B 站高清视频
  • 【limit 1000000,10 加载很慢该怎么优化?】
  • FPGA之​​​​​​​​​​​​​​HRBANK与HOBANK有什么区别?
  • HTTP入门
  • Go语言的错误处理error
  • Redis 09章——哨兵(sentinel)
  • 【Elasticsearch】多字段查询方式汇总
  • SQL CHECK 语句详解
  • 硬件学习笔记--43 电磁兼容试验-7 浪涌试验介绍
  • solidworks零件的绘制学习
  • 阿里云上线 DeepSeek,AI 领域再掀波澜
  • 微信小程序image组件mode属性详解