【Linux系统编程】第三十六弹---深入探索进程间通信:封装共享内存类并实现进程间数据共享

✨个人主页:熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、共享内存的概念

2、创建共享内存

2.1、函数介绍

2.2、代码测试

3、封装成类

3.1、基本框架

3.2、析构函数

3.3、私有成员函数

3.4、创建共享内存函数

3.5、构造函数

3.6、测试

3.7、进行通信

3.8、查看共享内存属性

4、完整代码

4.1、Makefile

4.2、Shm.hpp

4.3、namedPipe.hpp

4.4、server.cc

4.5、client.cc


1、共享内存的概念

共享内存是指多个进程共享同一块物理内存区域的技术。在Linux操作系统中,通过系统调用和内核支持,不同的进程可以映射到同一块物理内存,并直接读写这块内存区域,避免了数据在进程间的拷贝和传递,提高了数据访问的效率。

1、OS创建共享内存空间。

2、通过页表建立物理内存与虚拟内存的映射。

如何理解上面的这个过程呢?

1、前面做的操作都是OS做的。

2、OS提供上面1,2的步骤的系统调用,供用户进程A,B来进行调用。

3、AB进程可以共享,那么CD,EF都可以共享内存 ==> 共享内存在系统中可以存在多份,供不同个数,不同对进程同时进行通信。

4、OS注定要对共享内存进行管理! ==> 先描述在组织 ==> 共享内存不是简单的一段内存空间,也要有描述并管理的数据结构和匹配的算法。

5、共享内存 = 内存空间(数据) + 共享内存的属性(结构体封装)

共享内存结构体中一定要有标识内存唯一性的字段,但是又不能让OS自己生成,那么应该怎么生成呢?

其实我们只要设计一套规则,让用户自己设置唯一的标识符编号即可,但是通常使用ftok()函数获取共享内存唯一标识符。

2、创建共享内存

会用到的头文件及全局变量

bash 复制代码
#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/root/linux-learning/Shm"; # 此路径自己设置
const int gproj_id = 0x66;
const int gShmSize = 4096; 

2.1、函数介绍

创建共享内存系统调用函数

bash 复制代码
shmget - allocates a System V shared memory segment 
# 分配 System V 共享内存段

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

参数:
    key:用户生成的共享内存唯一标识符
    size:共享内存大小
    shmflg:一个标志位集合,用于控制共享内存段的访问权限和创建行为
        IPC_CREAT:如果共享内存段不存在,则创建一个新的共享内存段;如果已存在,获取该共享内存并返回
        IPC_EXCL:单独使用没有意义,只有跟IPC_CREAT组合才有意义
        IPC_CREAT | IPC_EXCL:如果共享内存段不存在,则创建一个新的共享内存段;如果已存在,则返回错误

RETURN VALUE
       On success, a valid shared memory identifier is returned.  On error, 
-1 is returned, and er‐rno is set to indicate the error.
返回值:
    成功时,shmget函数返回一个非负整数,表示共享内存段的标识符(ID)
    失败时,返回-1,并设置相应的errno以指示错误原因

ftok()

bash 复制代码
ftok函数用于生成一个唯一的键值(key),这个键值随后可以用作shmget等函数的参数来创建或访问共享内存。

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
参数:
    pathname:一个已存在的文件路径名。这个函数通过结合这个路径名和proj_id来生成一个唯一的键值。
    proj_id:一个项目标识符,通常是一个小的非负整数。

RETURN VALUE
       On success, the generated key_t value is returned.  
       On failure -1 is  returned,  with  errno indicating the error as for the stat(2) system call.
返回值:
    成功时,返回一个唯一的键值(key_t类型)。
    失败时,返回-1,并设置errno以指示错误原因。

2.2、代码测试

获取唯一标识符

cpp 复制代码
key_t GetComKey(const std::string& pathname,int proj_id)
{
    key_t k = ftok(pathname.c_str(),proj_id);
    if(k < 0)
    {
        perror("ftok");
    }
    return k;
}

唯一标识符转为16进制数

cpp 复制代码
std::string ToHex(key_t key)
{
    char buffer[128];
    snprintf(buffer,sizeof(buffer),"0x%x",key);
    return buffer;
}

创建共享内存

cpp 复制代码
int GetShm(key_t key,size_t size)
{
    // 不存在则创建,存在则返回错误
    int shmid = shmget(key,size,IPC_CREAT | IPC_EXCL);
    if(shmid < 0)
    {
        perror("shmget");
    }
    return shmid;
}

测试key值

server.cc

cpp 复制代码
int main()
{
    key_t key = GetComKey(gpathname,gproj_id);
    std::cout << "key: " << ToHex(key) << std::endl;

    return 0;
}

client.cc

cpp 复制代码
int main()
{
    key_t key = GetComKey(gpathname,gproj_id);
    std::cout << "key: " << ToHex(key) << std::endl;
    return 0;
}

两个文件创建的key值都相同!!!

测试共享内存创建

IPC_CREAT | IPC_EXCL:不存在则创建,存在则返回错误

**IPC_CREAT :**不存在则创建,存在则获取并返回

补充命令:

ipcs -m # 查看共享内存信息
ipcrm -m shmid # 删除共享内存

key vs shmid

**key :**属于用户形成,内核使用的一个字段,用户不能使用key来管理shm。内核来区分shm的唯一性(类比struct file*)。

**shmid :**内核给用户返回的一个标识符,用来进行用户级对共享内存进行管理的id值(类比fd)。

  • 在技术角度可以实现用key管理共享内存,但是为了能够更好的解耦,选择各自管理各自的。

3、封装成类

构造函数:创建共享内存。

析构函数:删除共享内存。

3.1、基本框架

cpp 复制代码
class Shm
{
private:
    key_t GetComKey(); // 获取key值
    int GetShmHelper(key_t key, size_t size, int flag); // 创建内存空间
    std::string RoleToString(int who); // 用户名转为字符串
    void *AttachShm(); // 连接进程
    void DetachShm(void *shmaddr); // 脱离进程 

public:
    Shm(const std::string &pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who),, _addrshm(nullptr)
    {}
    std::string ToHex(key_t key); // 将10进制数转化为16进制数
    bool GetShmUseCreate(); // Creater创建共享内存
    bool GetShmForUser();  // User创建共享内存
    void Zero(); // 清空共享内存数据
    void *Addr(); // 获取地址
    void DebugShm(); // 访问共享内存属性
    ~Shm()
    {}

private:
    key_t _key; // key值
    int _shmid; // 共享内存编号

    std::string _pathname; // 路径
    int _proj_id;

    int _who; // 用户名
};

删除共享内存

cpp 复制代码
shmctl函数用于控制共享内存段的某些操作,包括删除共享内存段。

#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:
    shmid:共享内存段的标识符,由shmget函数返回。
    cmd:要执行的操作命令。对于删除共享内存段,应使用IPC_RMID命令。
    buf:指向shmid_ds结构的指针,用于传递或接收共享内存段的属性。在删除共享内存段时,这个参数通常设置为NULL,因为不需要获取或设置共享内存的属性。

返回值:
    成功时,返回0。
    失败时,返回-1,并设置errno以指示错误原因。

3.2、析构函数

创建者才需要删除共享内存!!!

cpp 复制代码
~Shm()
{
    sleep(5); // 休眠5秒测试删除效果
    if (_who == gCreater)
    {
        int res = shmctl(_shmid, IPC_RMID, nullptr);
        if (res < 0)
        {
            perror("shmctl");
        }
    }
    std::cout << "shm remove done..." << std::endl;
}

3.3、私有成员函数

将获取key值和创建共享内存函数封装为私有

cpp 复制代码
private:
    key_t GetComKey()
    {
        key_t k = ftok(gpathname.c_str(), gproj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }
    int GetShmHelper(key_t key, size_t size, int flag)
    {
        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("shmget");
        }
        return shmid;
    }

3.4、创建共享内存函数

根据用户创建共享内存

cpp 复制代码
// Creater创建共享内存
bool GetShmUseCreate()
{
    if (_who == gCreater)
    {
        // 不存在则创建,存在则返回错误
        _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
        if (_shmid >= 0)
            return true;
        std::cout << "shm create done..." << std::endl;
    }
    return false;
}
// USer创建共享内存
bool GetShmForUser()
{
    if (_who == gUser)
    {
        // 不存在则创建,存在则获取并返回
        _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
        if (_shmid >= 0)
            return true;
        std::cout << "shm get done..." << std::endl;
    }
    return false;
}

3.5、构造函数

cpp 复制代码
Shm(const std::string &pathname, int proj_id, int who)
    : _pathname(pathname), _proj_id(proj_id), _who(who)
{
    _key = GetComKey();
    if (_who == gCreater)
        GetShmUseCreate();
    else if (_who == gUser)
        GetShmForUser();

    std::cout << "shmid: " << _shmid << std::endl;
    std::cout << "_key: " << ToHex(_key) << std::endl;
}

3.6、测试

cpp 复制代码
while :; do ipcs -m;sleep 1;done # 每隔一秒查看一次共享内存属性信息

server.cc

cpp 复制代码
int main()
{
    Shm shm(gpathname,gproj_id,gCreater);
    
    return 0;
}

运行结果

client.cc

cpp 复制代码
int main()
{
    Shm shm(gpathname,gproj_id,gUser);
    
    return 0;
}

运行结果

1、共享内存不随进程结束而自动释放,即共享内存声明周期随内核==> 一直存在,直到系统重启

2、共享内存手动释放(指令或者其他系统调用)

运行结果

先执行服务端程序,再执行客户端程序,会看到一个现象,共享内存挂接进程数从没有变成1,再从1变成2,然后从2变成1,且状态变为dest(被删除但还有用户在使用),最后没有挂接数。

3.7、进行通信

shmat()

cpp 复制代码
shmat : 将共享内存区对象映射到调用进程的地址空间,使得进程能够像访问本地空间一样访问共享内存

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数
    shmid:共享内存标识符,由shmget函数返回。该标识符用于唯一标识一块共享内存。
    shmaddr:指定共享内存出现在进程内存地址的什么位置。如果此参数为NULL,则内核会自动选择一个合适的地址位置进行映射。
如果shmaddr不为NULL,且没有指定SHM_RND标志,则尝试将共享内存映射到shmaddr指定的地址。
如果指定了SHM_RND标志,则映射地址会向下调整为SHMLBA(低边界地址的倍数,总是2的乘方)的整数倍。
    shmflg:权限标志位。如果shmflg为0,则默认为读写模式;如果指定SHM_RDONLY,则以只读模式连接共享内存。
返回值
    成功时,shmat函数返回映射好的共享内存地址。
    失败时,返回-1,并设置errno以指示错误原因。

shmdt()

cpp 复制代码
shmdt : 将当前进程地址空间中附加的共享内存段分离

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数
    shmaddr:这是一个指向共享内存段起始地址的指针。这个地址通常是之前通过调用shmat函数获得的,
表示当前进程地址空间中共享内存段的起始位置。
返回值
    成功:函数执行成功时,返回0。
    失败:如果函数执行失败,则返回-1,并设置相应的errno以指示错误原因。

此处需要获取地址,因此可以增加一个存储地址的成员变量

cpp 复制代码
class Shm
{
private:
    key_t _key; // key值
    int _shmid; // 共享内存编号

    std::string _pathname; // 路径
    int _proj_id;

    int _who; // 用户名
    void* _addrshm; // 内存地址 
};

分离地址

因为挂接地址需要先判断是否需要分离地址,因此先实现分离地址。

// 分离地址
void DetachShm(void* shmaddr)
{
    // 为空无需分离地址
    if(shmaddr == nullptr)
        return;
    shmdt(shmaddr);
    std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;
}

挂接地址

void* AttachShm()
{
    // 当前地址不为空先分离地址
    if(_addrshm != nullptr)
    {
        DetachShm(_addrshm);
    }
    // 为空直接挂接地址
    void* addrshm = shmat(_shmid,nullptr,0);
    if(addrshm == nullptr)
    {
        perror("AttachShm");
    }
    std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;
    return addrshm;
}

构造函数

构造函数通过初始化列表将地址初始化为空,然后在函数内部挂接地址。

Shm(const std::string &pathname, int proj_id, int who)
    : _pathname(pathname), _proj_id(proj_id), _who(who),_addrshm(nullptr)
{
    _key = GetComKey();
    if (_who == gCreater)
        GetShmUseCreate();
    else if (_who == gUser)
        GetShmForUser();

    std::cout << "shmid: " << _shmid << std::endl;
    std::cout << "_key: " << ToHex(_key) << std::endl;
    _addrshm = AttachShm(); // 挂接地址
}

清理共享内存数据

// 清理数据
void Zero()
{
    if (_addrshm)
    {
        memset(_addrshm, 0, gShmSize);
    }
}

测试

客户端写数据,服务端读数据。

client.cc

int main()
{
    // 1.获取共享内存
    Shm shm(gpathname,gproj_id,gUser);
    // 2.清理内存数据
    shm.Zero();
    // 3.写数据
    char* shmaddr = (char*)shm.Addr(); // 获取地址

    char ch = 'A';
    while(ch <= 'Z')
    {
        shmaddr[ch - 'A'] = ch;
        sleep(2); // 两秒写一次数据
        ch++;
    }
    return 0;
}

server.cc

int main()
{
    // 1.创建共享内存
    Shm shm(gpathname,gproj_id,gCreater);
    // 2.获取共享内存地址
    char* shmaddr = (char*)shm.Addr();
    // 3.读取数据
    while(true)
    {
        std::cout << "shm memory content: " << shmaddr << std::endl;
    }
    return 0;
}

只启用服务端,会一直在读。

写端两秒写一条数据,读端一秒写一条数据,会发现出现重复数据。

  • 可以看出共享内存的一个优点:我们在访问共享内存的时候,没有使用任何系统调用,因此共享内存是所有IPC速度最快的,因为共享内存大大减少了数据的拷贝次数。
  • 通过上面测试可以看出共享内存一个缺点:共享内存不提供任何的保护机制 --- 数据不一致问题!!!

怎么解决这个缺点呢?

使用命名管道,因为命名管道是同步通信的!!!

server.cc

int main()
{
    // 1.创建共享内存
    Shm shm(gpathname,gproj_id,gCreater);
    // 2.获取共享内存地址
    char* shmaddr = (char*)shm.Addr();
    // 3.创建管道
    NamePiped fifo(comm_path,Creater);
    fifo.OpenForRead();
    // 4.读取数据
    while(true)
    {
        std::string temp;
        fifo.ReadNamedPipe(&temp);
        std::cout << "shm memory content: " << shmaddr << std::endl;
    }
    return 0;
}

client.cc

int main()
{
    // 1.获取共享内存
    Shm shm(gpathname,gproj_id,gUser);
    // 2.清理内存数据
    shm.Zero();
    // 3.创建管道
    NamePiped fifo(comm_path,User);
    fifo.OpenForWrite();

    // 4.写数据
    char* shmaddr = (char*)shm.Addr(); // 获取地址

    char ch = 'A';
    while(ch <= 'Z')
    {
        shmaddr[ch - 'A'] = ch;
        std::string temp = "wakeup";
        std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;
        fifo.WriteNamedPipe(temp);
        sleep(2); // 两秒写一次数据
        ch++;
    }
    return 0;
}

使用命名管道之后,执行服务端但是没有执行客户端会阻塞(根据管道原理)。

执行服务端之后,再执行客户端,会正常进行通信,且不出现数据不一致问题!!!

注意:我们使用ctrl + c关闭服务端进程都需要手动删除共享内存,否则会出问题!!!

运行结果

3.8、查看共享内存属性

struct shmid_ds ds;

shmctl(_shmid, IPC_STAT, &ds);

IPC_STAT:
    从关联的内核数据结构中复制信息与 shmid 一起进入 buf 指向的 shmid_ds 结构。
调用方必须对共享内存具有读取权限段。

测试函数

void DebugShm()
{
    struct shmid_ds ds;
    int n = shmctl(_shmid, IPC_STAT, &ds);
    if (n < 0)
        return;
    std::cout << "ds.shm_perm.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
    std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;
}

主函数

int main()
{
    // 1. 创建共享内存
    Shm shm(gpathname, gproj_id, gCreater);
    char *shmaddr = (char*)shm.Addr();

    shm.DebugShm();

    sleep(5);
    
    return 0;
}

运行结果

4、完整代码

4.1、Makefile

.PHONY:all
all:client server 

client:client.cc
	g++ -o $@ $^ -std=c++11
server:server.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -rf client server

4.2、Shm.hpp

#ifndef __SHM__HPP
#define __SHM_HPP

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>

const std::string gpathname = "/root/linux-learning/Shm";
const int gproj_id = 0x66;
const int gShmSize = 4096;
const int gCreater = 1;
const int gUser = 2;

class Shm
{
private:
    key_t GetComKey()
    {
        key_t k = ftok(gpathname.c_str(), gproj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }
    int GetShmHelper(key_t key, size_t size, int flag)
    {
        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("shmget");
        }
        return shmid;
    }
    std::string RoleToString(int who)
    {
        if (who == gCreater)
            return "gCreater";
        else if (who == gUser)
            return "gUser";
        return "None";
    }
    // 挂接地址
    void *AttachShm()
    {
        // 当前地址不为空先分离地址
        if (_addrshm != nullptr)
        {
            DetachShm(_addrshm);
        }
        // 为空直接挂接地址
        void *addrshm = shmat(_shmid, nullptr, 0);
        if (addrshm == nullptr)
        {
            perror("AttachShm");
        }
        std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;
        return addrshm;
    }
    // 分离地址
    void DetachShm(void *shmaddr)
    {
        // 为空无需分离地址
        if (shmaddr == nullptr)
            return;
        shmdt(shmaddr);
        std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;
    }

public:
    Shm(const std::string &pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
    {
        _key = GetComKey(); // 初始化_key值
        if (_who == gCreater)
            GetShmUseCreate();
        else if (_who == gUser)
            GetShmForUser();

        std::cout << "shmid: " << _shmid << std::endl;
        std::cout << "_key: " << ToHex(_key) << std::endl;
        _addrshm = AttachShm(); // 挂接地址
    }
    std::string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }
    bool GetShmUseCreate()
    {
        if (_who == gCreater)
        {
            // 不存在则创建,存在则返回错误
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid >= 0)
                return true;
            std::cout << "shm create done..." << std::endl;
        }
        return false;
    }
    bool GetShmForUser()
    {
        if (_who == gUser)
        {
            // 不存在则创建,存在则获取并返回
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
            // _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT);
            if (_shmid >= 0)
                return true;
            std::cout << "shm get done..." << std::endl;
        }
        return false;
    }
    // 获取地址
    void *Addr()
    {
        return _addrshm;
    }
    // 清理数据
    void Zero()
    {
        if (_addrshm)
        {
            memset(_addrshm, 0, gShmSize);
        }
    }
    void DebugShm()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds);
        if (n < 0)
            return;
        std::cout << "ds.shm_perm.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
        std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;
    }

    ~Shm()
    {
        sleep(5);
        if (_who == gCreater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
            if (res < 0)
            {
                perror("shmctl");
            }
        }
        std::cout << "shm remove done..." << std::endl;
    }

private:
    key_t _key; // key值
    int _shmid; // 共享内存编号

    std::string _pathname; // 路径
    int _proj_id;

    int _who;       // 用户名
    void *_addrshm; // 内存地址
};

key_t GetComKey(const std::string &pathname, int proj_id)
{
    key_t k = ftok(pathname.c_str(), proj_id);
    if (k < 0)
    {
        perror("ftok");
    }
    return k;
}

int GetShm(key_t key, size_t size)
{
    // 不存在则创建,存在则返回错误
    // int shmid = shmget(key,size,IPC_CREAT | IPC_EXCL);
    // 不存在则创建,存在则获取并返回
    int shmid = shmget(key, size, IPC_CREAT);
    if (shmid < 0)
    {
        perror("shmget");
    }
    return shmid;
}

std::string ToHex(key_t key)
{
    char buffer[128];
    snprintf(buffer, sizeof(buffer), "0x%x", key);
    return buffer;
}

#endif

4.3、namedPipe.hpp

#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cerrno>
#include <cstdio>

#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

const std::string comm_path = "./myfifo";

class NamePiped
{
private:
    bool OpenNamedPipe(int mode)
    {
        // 返回值是文件描述符,打开失败返回-1,并设置错误码
        _fd = open(_fifo_path.c_str(), mode);
        if (_fd < 0)
            return false;
        return true;
    }

public:
    // 构造函数创建管道
    NamePiped(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        // 创建着才需要创建管道
        if (_id == Creater)
        {
            int res = mkfifo(_fifo_path.c_str(), 0666);
            // 返回值不等于0则创建失败
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater create named pipe" << std::endl;
        }
    }
    // 以读方式打开文件
    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }
    // 以写方式打开文件
    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }
    // 读文件
    int ReadNamedPipe(std::string *out)
    {
        char buffer[BaseSize];
        int n = read(_fd, buffer, sizeof(buffer));
        if (n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }
    // 写文件
    int WriteNamedPipe(const std::string& in)
    {
        return write(_fd,in.c_str(),in.size());
    }
    // 析构函数删除管道
    ~NamePiped()
    {
        // 析构之前休眠5秒,看删除效果
        sleep(5);
        // 创建者才需要删除管道
        if (_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if (res != 0)
            {
                perror("umlink");
            }
            std::cout << "creater free named pipe" << std::endl;
        }
        // 创建了文件则关闭文件描述符
        if(_fd != DefaultFd) close(_fd);
    }

private:
    const std::string _fifo_path; // 文件路径
    int _id;                      // 用户名
    int _fd;                      // 文件描述符
};

4.4、server.cc

#include "Shm.hpp"
#include "namedPipe.hpp"

// int main()
// {
//     key_t key = GetComKey(gpathname,gproj_id);
//     std::cout << "key: " << ToHex(key) << std::endl;
    
//     int shmid = GetShm(key,gShmSize);
//     std::cout << "shmid: " << shmid << std::endl;
//     return 0;
// }

// int main()
// {
//     // 1.创建共享内存
//     Shm shm(gpathname,gproj_id,gCreater);
//     // 2.获取共享内存地址
//     char* shmaddr = (char*)shm.Addr();
//     // 3.读取数据
//     while(true)
//     {
//         sleep(1); // 每隔一秒读取一次
//         std::cout << "shm memory content: " << shmaddr << std::endl;
//     }
//     return 0;
// }

int main()
{
    // 1.创建共享内存
    Shm shm(gpathname,gproj_id,gCreater);
    // 2.获取共享内存地址
    char* shmaddr = (char*)shm.Addr();
    // 3.创建管道
    NamePiped fifo(comm_path,Creater);
    fifo.OpenForRead();
    // 4.读取数据
    while(true)
    {
        std::string temp;
        fifo.ReadNamedPipe(&temp);
        std::cout << "shm memory content: " << shmaddr << std::endl;
    }
    return 0;
}

4.5、client.cc

#include "Shm.hpp"
#include "namedPipe.hpp"

// int main()
// {
//     key_t key = GetComKey(gpathname,gproj_id);
//     std::cout << "key: " << ToHex(key) << std::endl;
//     return 0;
// }

// int main()
// {
//     // 1.获取共享内存
//     Shm shm(gpathname,gproj_id,gUser);
//     // 2.清理内存数据
//     shm.Zero();
//     // 3.写数据
//     char* shmaddr = (char*)shm.Addr(); // 获取地址

//     char ch = 'A';
//     while(ch <= 'Z')
//     {
//         shmaddr[ch - 'A'] = ch;
//         sleep(2); // 两秒写一次数据
//         ch++;
//     }
//     return 0;
// }


// int main()
// {
//     // 1.获取共享内存
//     Shm shm(gpathname,gproj_id,gUser);
//     // 2.清理内存数据
//     shm.Zero();
//     // 3.创建管道
//     NamePiped fifo(comm_path,User);
//     fifo.OpenForWrite();

//     // 4.写数据
//     char* shmaddr = (char*)shm.Addr(); // 获取地址

//     char ch = 'A';
//     while(ch <= 'Z')
//     {
//         shmaddr[ch - 'A'] = ch;
//         std::string temp = "wakeup";
//         std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;
//         fifo.WriteNamedPipe(temp);
//         sleep(2); // 两秒写一次数据
//         ch++;
//     }
//     return 0;
// }

int main()
{
    // 1. 创建共享内存
    Shm shm(gpathname, gproj_id, gCreater);
    char *shmaddr = (char*)shm.Addr();

    shm.DebugShm();

    sleep(5);
    
    return 0;
}
相关推荐
加载中loading...16 分钟前
Linux线程安全(二)条件变量实现线程同步
linux·运维·服务器·c语言·1024程序员节
Wx120不知道取啥名19 分钟前
C语言之长整型有符号数与短整型有符号数转换
c语言·开发语言·单片机·mcu·算法·1024程序员节
开心工作室_kaic1 小时前
ssm010基于ssm的新能源汽车在线租赁管理系统(论文+源码)_kaic
java·前端·spring boot·后端·汽车
Python私教1 小时前
Flutter颜色和主题
开发语言·javascript·flutter
代码吐槽菌1 小时前
基于SSM的汽车客运站管理系统【附源码】
java·开发语言·数据库·spring boot·后端·汽车
Iareges1 小时前
美团2025校招 广告算法工程师 面经
算法·面试·求职招聘·笔试·秋招
Ws_1 小时前
蓝桥杯 python day01 第一题
开发语言·python·蓝桥杯
zdkdchao1 小时前
jdk,openjdk,oraclejdk
java·开发语言
神雕大侠mu2 小时前
函数式接口与回调函数实践
开发语言·python
大力水手~2 小时前
css之loading旋转加载
前端·javascript·css