【lesson46】进程通信之system V(共享内存)

文章目录

  • 共享内存通信原理
  • 用共享内存通信
    • shmServer.cc
    • shmClient.cc
  • 完整通信代码
    • common.hpp
    • Log.hpp
    • shmServer.cc
    • shmClient.cc
    • 通信测试
  • 共享内存借助管道添加访问控制
    • common.hpp
    • shmServer.cc
    • shmClient.cc

共享内存通信原理

两个进程将一块system V的物理地址通过页表映射到自己的进程地址空间中。
【lesson46】进程通信之system V(共享内存)_第1张图片
具体点:
【lesson46】进程通信之system V(共享内存)_第2张图片
共享内存的建立
共享内存提供者是OS
那么OS要不要管理共享内存?
当然要—>先描述,再组织----->重新理解共享内存:
共享内存 = 共享内存块 + 共享内存的内核数据结构

用共享内存通信

创建两个文件:
shmServer.cc:读取数据
shmClient.cc:写入数据

shmServer.cc

1.创建key值
【lesson46】进程通信之system V(共享内存)_第3张图片
在这里插入图片描述
fotk参数:
pathname:随便写一个固定的路径,主要是为了生成唯一key值标识共享内存。
proj_id:也是随便写一个值,目的pathname一样。
在这里插入图片描述
2.创建共享内存
【lesson46】进程通信之system V(共享内存)_第4张图片
参数key:通过唯一key值创建内存并且共享内存以key编号
参数size:要申请的字节共享内存大小
参数shmflg
在这里插入图片描述
返回值
在这里插入图片描述
成功返回用户层用来唯一表示共享内存的shmid
【lesson46】进程通信之system V(共享内存)_第5张图片
3.挂接共享内存到进程地址空间的共享区
【lesson46】进程通信之system V(共享内存)_第6张图片
参数shmid:用户层共享内存的唯一标识
参数shmaddr
在这里插入图片描述
参数shmflg
在这里插入图片描述
返回值
在这里插入图片描述
【lesson46】进程通信之system V(共享内存)_第7张图片
4.通信逻辑之后实现,先把其它逻辑走通。
5.通信结束,共享内存去挂接
在这里插入图片描述
参数shmaddr:之前挂接的共享内存地址
返回值
在这里插入图片描述
【lesson46】进程通信之system V(共享内存)_第8张图片
6.删除共享内存
【lesson46】进程通信之system V(共享内存)_第9张图片
参数shmid:共享内存用户层编号
参数cmd
【lesson46】进程通信之system V(共享内存)_第10张图片
参数buf
【lesson46】进程通信之system V(共享内存)_第11张图片
返回值
在这里插入图片描述
【lesson46】进程通信之system V(共享内存)_第12张图片
通信逻辑的实现:
【lesson46】进程通信之system V(共享内存)_第13张图片
我们直接输出共享内存中的数据即可。

shmClient.cc

1.获取key值
【lesson46】进程通信之system V(共享内存)_第14张图片
2.获取共享内存
【lesson46】进程通信之system V(共享内存)_第15张图片
3.挂接共享内存到进程地址空间的共享区
【lesson46】进程通信之system V(共享内存)_第16张图片
4.开始通信,通信逻辑之后再实现
5.共享内存去挂接
【lesson46】进程通信之system V(共享内存)_第17张图片
这里不用删除共享空间,因为共享空间不是这个进程创建的。

通信逻辑
【lesson46】进程通信之system V(共享内存)_第18张图片
我们直接向共享内存写数据。
在这里插入图片描述
【lesson46】进程通信之system V(共享内存)_第19张图片
【lesson46】进程通信之system V(共享内存)_第20张图片

完整通信代码

common.hpp

#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "Log.hpp"

#define PATH_NAME "/home/xiaolin/"
#define PROJ_ID 0x66
#define SHM_SIZE 4096

Log.hpp

#pragma once

#include 
#include 

#define DEBUG 0
#define NOTICE 1
#define WARNING 2
#define ERROR 3

const std::string msg[] = {
    "DEBUGE",
    "NOTICE",
    "WARNING",
    "ERROR"};

std::ostream &Log(std::string message, int level)
{
    std::cout << "|" << (unsigned)time(nullptr) << "|" << msg[level] << "|" << message;
    return std::cout;
}

shmServer.cc

#include "common.hpp"

std::string TransToHex(key_t k)
{
    char buffer[32];
    snprintf(buffer, sizeof buffer, "0x%x", k);
    return buffer;
}

int main()
{
    // 1.创建key值
    key_t key = ftok(PATH_NAME, PROJ_ID);
    if (key == -1)
    {
        perror("ftok");
        exit(1);
    }
    Log("create key success", DEBUG) << " server key : " << TransToHex(key) << std::endl;
    //sleep(10);

    // 2.创建共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid == -1)
    {
        perror("shget");
        exit(2);
    }
    Log("create shm success", DEBUG) << " shm id : " << shmid << std::endl;
    //sleep(10);

    // 3.挂接共享内存的地址,到进程地址空间
    char *shmaddress = (char *)shmat(shmid, nullptr, 0);
    if (shmaddress == (char *)((void *)-1))
    {
        perror("shmat");
        exit(3);
    }
    Log("attach shm success", DEBUG) << " shm address : " << (int)*shmaddress << std::endl;
    //sleep(10);

    // 4.进行通信的逻辑
    //Server读取数据
    while(true)
    {
        printf("%s\n",shmaddress);
        if(strcmp(shmaddress,"quit") == 0)
        {
            break;
        }
        sleep(1);
    } 
    
    // 5.去挂接
    int shmdt_res = shmdt((void*)shmaddress);
    if(shmdt_res == -1)
    {
        perror("shmdt");
        exit(4);
    }
    Log("unattach shm success", DEBUG) << " shm res : " << shmdt_res << std::endl;
    //sleep(10);

    //6.删除共享内存,IPC_RMID表示即便是有进程和当下的shm挂接,依旧删除共享内存
    int shmctl_res = shmctl(shmid,IPC_RMID,nullptr);
    if(shmctl_res == -1)
    {
        perror("shmctl");
        exit(5);
    }
    Log("shmctl shm success", DEBUG) << " shmctl res : " << shmctl_res << std::endl;
    //sleep(10);

    return 0;
}

shmClient.cc

#include "common.hpp"
std::string TransToHex(key_t k)
{
    char buffer[32];
    snprintf(buffer, sizeof buffer, "0x%x", k);
    return buffer;
}

int main()
{
    // 1.获取key值
    key_t key = ftok(PATH_NAME, PROJ_ID);
    if (key == -1)
    {
        perror("ftok");
        exit(1);
    }
    Log("create key success", DEBUG) << " server key : " << TransToHex(key) << std::endl;
    //sleep(10);

    // 2.获取共享内存
    int shmid = shmget(key, SHM_SIZE, 0);
    if (shmid == -1)
    {
        perror("shget");
        exit(2);
    }
    Log("create shm success", DEBUG) << " shm id : " << shmid << std::endl;
    //sleep(10);

    // 3.挂接共享内存
    char *shmaddress = (char *)shmat(shmid, nullptr, 0);
    if (shmaddress == (char *)((void *)-1))
    {
        perror("shmat");
        exit(3);
    }
    Log("attach shm success", DEBUG) << " shm address : " << *shmaddress << std::endl;
    //sleep(10);

    // 4.通信逻辑
    // clinent写入数据
    char c = 'a';
    for (; c <= 'z'; c++)
    {
        snprintf(shmaddress, SHM_SIZE - 1,
                 "hello server, 我是其他进程,我的pid: %d, inc: %c\n",
                 getpid(), c);
        
        sleep(1);
    }

    snprintf(shmaddress,SHM_SIZE-1,"quit"); 

    // 5.去挂接
    int shmdt_res = shmdt((void *)shmaddress);
    if (shmdt_res == -1)
    {
        perror("shmdt");
        exit(4);
    }
    Log("unattach shm success", DEBUG) << " shm res : " << shmdt_res << std::endl;
    //sleep(10);
    return 0;
}

通信测试

【lesson46】进程通信之system V(共享内存)_第21张图片
我们可以看到server端在client还没有写入数据是就一直读取内容。
在client端运行起来后,也还在读取数据。
【lesson46】进程通信之system V(共享内存)_第22张图片
client端写入数据结束后,再写入quit指令,server端随之退出。
【lesson46】进程通信之system V(共享内存)_第23张图片
这里我们能看到system V缺乏访问控制。
那么我们能添加访问控制吗?可以

共享内存借助管道添加访问控制

common.hpp

#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "Log.hpp"

#define PATH_NAME "/home/xiaolin/"
#define PROJ_ID 0x66
#define SHM_SIZE 4096
#define FIFO_NAME "./fifo"
#define MODE 0666
#define READ O_RDONLY
#define WRITE O_WRONLY

class Init
{
public:
    Init()
    {
        umask(0);
        int n = mkfifo(FIFO_NAME,MODE);
        if(n == -1)
        {
            perror("mkfifo");
        }
        Log("create fifo success",NOTICE) << "\n";
    }
    ~Init()
    {
        int n = unlink(FIFO_NAME);
        if(n == -1)
        {
            perror("unlink");
        }
        Log("unlink fifo success",NOTICE) << "\n";
    }
};


int OpenFifo(std::string pathname,int flages) 
{
    int fd = open(pathname.c_str(),flages);
    if(fd < 0)
    {
        perror("open");
    }

    return fd;
}

void Wait(int fd)//阻塞等待
{
    Log("等待中....", NOTICE) << "\n";
    uint32_t temp = 0;
    ssize_t n = read(fd,&temp,sizeof(temp));
    assert(n == sizeof(uint32_t));
}

void Signal(int fd)//唤醒
{
    uint32_t temp = 1;
    ssize_t n = write(fd,&temp,sizeof(temp));
    assert(n == sizeof(uint32_t));
    Log("唤醒中....", NOTICE) << "\n";
}

void CloseFifo(int fd) 
{
    close(fd);
}

shmServer.cc

#include "common.hpp"

// 对应的程序,在加载的时候,会自动构建全局变量,自动调用构造函数 -- 创建管道文件
// 程序退出的时候,全局变量会被析构,自动调用析构函数-----删除管道文件
Init init;

std::string TransToHex(key_t k)
{
    char buffer[32];
    snprintf(buffer, sizeof buffer, "0x%x", k);
    return buffer;
}

int main()
{
    // 1.创建key值
    key_t key = ftok(PATH_NAME, PROJ_ID);
    if (key == -1)
    {
        perror("ftok");
        exit(1);
    }
    Log("create key success", DEBUG) << " server key : " << TransToHex(key) << std::endl;
    //sleep(10);

    // 2.创建共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid == -1)
    {
        perror("shget");
        exit(2);
    }
    Log("create shm success", DEBUG) << " shm id : " << shmid << std::endl;
    //sleep(10);

    // 3.挂接共享内存的地址,到进程地址空间
    char *shmaddress = (char *)shmat(shmid, nullptr, 0);
    if (shmaddress == (char *)((void *)-1))
    {
        perror("shmat");
        exit(3);
    }
    Log("attach shm success", DEBUG) << " shm address : " << (int)*shmaddress << std::endl;
    //sleep(10);

    // 4.进行通信的逻辑
    //Server读取数据
    /* int fd = OpenFifo(FIFO_NAME,READ);
    while(true)
    {
        Wait(fd);
        printf("%s\n",shmaddress);
        if(strcmp(shmaddress,"quit") == 0)
        {
            break;
        }
        sleep(1);
    }

    CloseFifo(fd); */

    //shift alt a 注释指令
    while(true)
    {
        printf("%s\n",shmaddress);
        if(strcmp(shmaddress,"quit") == 0)
        {
            break;
        }
        sleep(1);
    }

    // 5.去挂接
    int shmdt_res = shmdt((void*)shmaddress);
    if(shmdt_res == -1)
    {
        perror("shmdt");
        exit(4);
    }
    Log("unattach shm success", DEBUG) << " shm res : " << shmdt_res << std::endl;
    //sleep(10);

    //6.删除共享内存,IPC_RMID表示即便是有进程和当下的shm挂接,依旧删除共享内存
    int shmctl_res = shmctl(shmid,IPC_RMID,nullptr);
    if(shmctl_res == -1)
    {
        perror("shmctl");
        exit(5);
    }
    Log("shmctl shm success", DEBUG) << " shmctl res : " << shmctl_res << std::endl;
    //sleep(10);

    return 0;
}

shmClient.cc

#include "common.hpp"
std::string TransToHex(key_t k)
{
    char buffer[32];
    snprintf(buffer, sizeof buffer, "0x%x", k);
    return buffer;
}

int main()
{
    // 1.获取key值
    key_t key = ftok(PATH_NAME, PROJ_ID);
    if (key == -1)
    {
        perror("ftok");
        exit(1);
    }
    Log("create key success", DEBUG) << " server key : " << TransToHex(key) << std::endl;
    //sleep(10);

    // 2.获取共享内存
    int shmid = shmget(key, SHM_SIZE, 0);
    if (shmid == -1)
    {
        perror("shget");
        exit(2);
    }
    Log("create shm success", DEBUG) << " shm id : " << shmid << std::endl;
    //sleep(10);

    // 3.挂接共享内存
    char *shmaddress = (char *)shmat(shmid, nullptr, 0);
    if (shmaddress == (char *)((void *)-1))
    {
        perror("shmat");
        exit(3);
    }
    Log("attach shm success", DEBUG) << " shm address : " << *shmaddress << std::endl;
    //sleep(10);

    // 4.通信逻辑
    // clinent写入数据
    /* int fd = OpenFifo(FIFO_NAME,WRITE);
    while(true)
    {
        ssize_t n = read(0,shmaddress,SHM_SIZE-1);
        if(n > 0)
        {
            shmaddress[n-1] = 0;
            Signal(fd);
            if(strcmp(shmaddress,"quit") == 0)
            {
                break;
            }
            
        }
        else if(n == 0)
        {
            break;
        }
        else
        {
            perror("read");
        }
    }

    CloseFifo(fd); */
    
    char c = 'a';
    for (; c <= 'z'; c++)
    {
        snprintf(shmaddress, SHM_SIZE - 1,
                 "hello server, 我是其他进程,我的pid: %d, inc: %c\n",
                 getpid(), c);
        
        sleep(1);
    }

    snprintf(shmaddress,SHM_SIZE-1,"quit");

    // 5.去挂接
    int shmdt_res = shmdt((void *)shmaddress);
    if (shmdt_res == -1)
    {
        perror("shmdt");
        exit(4);
    }
    Log("unattach shm success", DEBUG) << " shm res : " << shmdt_res << std::endl;
    //sleep(10);
    return 0;
}

Log.hpp和之前一样,这里就不再写一遍。

附加:
Linux下查看共享内存:

ipcs -m

Linux下手动删除共享内存:

ipcrm -m(shimd)

Linux下循环查看共享内存脚本:

while :; do ipcs -m; sleep 1; done

你可能感兴趣的:(linux,Linux,进程通信,vscode)