Linux下共享内存 和 命名管道 的使用

文章目录

  • Linux共享内存:完整代码展示与剖析
    • 共享内存:原理、接口与应用实践
      • 引言
      • 一、共享内存核心原理 ⚙️
        • 1.1 共享内存的特点
        • 1.2 生命周期管理 ⏳
      • 二、关键系统接口解析
        • 2.1 生成唯一标识Key
        • 2.2 创建/获取共享内存 ️
        • 2.3 内存挂接与去关联
        • 2.4 控制操作
    • 完整代码展示
      • 1. 公用头文件 `common.hpp`
      • 2. 客户端代码 `client.cc`
      • 3. 服务端代码 `server.cc`
    • 编译与运行
      • 编译命令
      • 运行步骤 ▶️
    • 代码工作原理

Linux下共享内存 和 命名管道 的使用_第1张图片

Linux共享内存:完整代码展示与剖析

共享内存(Shared Memory)是Linux系统中一种高效的进程间通信(IPC)机制,允许多个进程共享同一块物理内存区域,从而实现快速的数据交换。本文将提供完整的C++代码示例,包括头文件、客户端和服务端实现,详细展示共享内存的创建、使用、同步及管理过程。✨


共享内存:原理、接口与应用实践

引言

共享内存是Linux系统进程间通信(IPC)中最快的一种方式,其核心思想是让多个进程通过映射同一块物理内存来实现数据共享。


一、共享内存核心原理 ⚙️

1.1 共享内存的特点
  • 零拷贝:数据直接映射到进程地址空间,无需内核中转
  • 高效性:适合传输大块数据(如视频流、大型矩阵)
  • 无同步机制:需结合其他IPC(如信号量、管道)实现同步
1.2 生命周期管理 ⏳
  • 随内核持续:即使进程退出,共享内存仍存在
  • 主动释放:需调用shmctl或使用ipcrm命令删除 ️

二、关键系统接口解析

2.1 生成唯一标识Key
#include 
key_t ftok(const char *pathname, int proj_id);
  • 参数
    • pathname:已存在的文件路径(如/home/user/config
    • proj_id:项目ID(0-255)
  • 返回值:唯一Key,用于后续操作
2.2 创建/获取共享内存 ️
int shmget(key_t key, size_t size, int shmflg);
  • 标志位
    • IPC_CREAT:不存在则创建
    • IPC_EXCL:配合IPC_CREAT使用,存在则报错 ❌
    • 权限位(如0666
2.3 内存挂接与去关联
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
  • 挂接策略
    • shmaddr设为NULL由系统选择地址
    • SHM_RDONLY以只读方式挂接
2.4 控制操作
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 控制命令
    • IPC_STAT:获取状态信息
    • IPC_RMID:标记删除 ️

完整代码展示

以下是实现共享内存通信的完整代码,分为三个文件:common.hpp(公用头文件)、client.cc(客户端)和server.cc(服务端)。

1. 公用头文件 common.hpp

#ifndef __COMMON_HPP__
#define __COMMON_HPP__

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const string pathname = "/home"; // ftok使用的路径名 
const int proj_id = 0x666;       // ftok的项目ID 
const string FIFO_FILE = "./myfifo"; // 命名管道文件路径 
const mode_t MODE = 0664;        // 命名管道权限 

enum Mistake
{
    FTOK_ERROR = 1,
    SHMGET_ERROR,
    SHMAT_ERROR,
    FIFO_CREATE_ERR,
    FIFO_DELETE_ERR
};

// 生成唯一的key_t键值 
key_t GetKey()
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if (key < 0)
    {
       perror("ftok error");
       exit(FTOK_ERROR);
    }
    return key;
}

// 共享内存辅助函数 ️
int GetShareMemHelper(int flag)
{
    key_t key = GetKey();
    int shmid = shmget(key, 4096, flag); // 创建或获取4096字节的共享内存 
    if (shmid < 0)
    {
       perror("shmget error");
       exit(SHMGET_ERROR);
    }
    return shmid;
}

// 创建共享内存 
int CreateShm()
{
    return GetShareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}

// 获取共享内存 
int GetShm()
{
    return GetShareMemHelper(IPC_CREAT);
}

// 初始化类:创建和销毁命名管道 
class Init
{
public:
    Init()
    {
        int n = mkfifo(FIFO_FILE.c_str(), MODE);
        if (n == -1 && errno != EEXIST) // 如果管道已存在则忽略错误 ⚠️
        {
            perror("mkfifo");
            exit(FIFO_CREATE_ERR);
        }
    }
    ~Init()
    {
        int m = unlink(FIFO_FILE.c_str());
        if (m == -1)
        {
            perror("unlink");
            exit(FIFO_DELETE_ERR);
        }
    }
};

#endif


2. 客户端代码 client.cc

#include "common.hpp"

int main()
{

    // 获取共享内存 
    int shmid = GetShm();
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if (shmaddr == (char *)-1)
    {
        perror("shmat error");
        exit(SHMAT_ERROR);
    }

    // 打开命名管道并发送信号 
    int fd = open(FIFO_FILE.c_str(), O_WRONLY);
    if (fd < 0)
    {
        perror("open");
        exit(1);
    }

    cout << "Please Enter :" << endl;
    // 从标准输入读取数据并写入共享内存 ⌨️
    while (true)
    {
        fgets(shmaddr, 4096, stdin);
        write(fd, "c", 1); // 通知服务端数据已写入 
    }

    // 分离共享内存 
    shmdt(shmaddr);
    close(fd);

    return 0;
}

3. 服务端代码 server.cc

#include "common.hpp"

int main()
{
    // 创建命名管道 
    Init init;

    // 创建共享内存 
    int shmid = CreateShm();
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if (shmaddr == (char *)-1)
    {
        perror("shmat error");
        exit(SHMAT_ERROR);
    }

    // 打开命名管道,等待客户端信号 
    int fd = open(FIFO_FILE.c_str(), O_RDONLY);
    if (fd < 0)
    {
        perror("open");
        exit(1);
    }

    std::cout << "server start sucess" << endl;
    
    while (true)
    {
        char c;
        ssize_t s = read(fd, &c, 1); // 读取信号 
        if (s <= 0)
            break; // 当s小于0说明写端关闭跳出循环关闭共享内存
        else if (s > 0)
        {
            cout << "client say@ " << shmaddr; // 输出共享内存内容                                                        // 获取共享内存状态 
            struct shmid_ds shmds;
            shmctl(shmid, IPC_STAT, &shmds);
            cout << "shm size: " << shmds.shm_segsz << endl;
            cout << "shm nattch: " << shmds.shm_nattch << endl;
            printf("shm __key: 0x%x\n", shmds.shm_perm.__key);
            cout << "shm shm_perm.mode: " << shmds.shm_perm.mode << endl;
            cout << "---------------------" << endl;
        }
    }

    // 分离并删除共享内存 ️
    shmdt(shmaddr);
    shmctl(shmid, IPC_RMID, nullptr);
    close(fd);

    return 0;
}

编译与运行

编译命令

g++ -o client client.cc
g++ -o server server.cc

运行步骤 ▶️

  1. 在一个终端运行服务端

    ./server
    
  2. 在另一个终端运行客户端

    ./client
    
  3. 在客户端终端输入消息(例如"Hello, shared memory!")并按回车 ↵

Linux下共享内存 和 命名管道 的使用_第2张图片


代码工作原理

  1. 共享内存创建与附加

    • 服务端使用shmget创建共享内存,客户端通过相同的键值获取。
    • shmat将共享内存映射到进程地址空间,返回指针供直接操作。
  2. 数据通信

    • 客户端将输入写入共享内存,服务端直接读取该内存区域的内容。
  3. 同步机制

    • 使用命名管道(FIFO)实现同步,客户端写入数据后通过管道发送信号,服务端接收信号后读取数据。
  4. 资源管理

    • 服务端在结束时使用shmctl(IPC_RMID)删除共享内存,Init类的析构函数清理命名管道。

你可能感兴趣的:(Linux应用,linux,服务器,c++,算法)