Linux-代码实现通过system v共享内存实现的进程间的通信

一.makefile编写

.PHONY:all
all:processa processb

processa : processa.cc
	g++ -o $@ $^ -g -std=c++11
processb : processb.cc
	g++ -o $@ $^ -g -std=c++11

.PHONY:clean
clean:
	rm -rf processa processb

二.创建system v共享内存

        1.系统调用接口          

                key:a.key是一个数值,在内核中具有唯一性,可以让不同的进程标识唯一的共享内存。

                        b.第一个进程可以通过key创建共享内存,从此第二个进程在拿着相同的key就可以和第一个进程看到同一块共享内存了。

                        c.这个key保存在共享内存的描述结构体中。

                        d.需要强调的是key是由用户给出相应的信息然后,系统根据算法来返回一个计算到的key。

                        ftok函数调用成功返回key值,失败返回-1。

                        用户在程序中调用这个函数就可以得到一个key。然后将这个key传给shmget就可以得到一个又这个key在内核中唯一标识的共享内存。

                size:所需要的共享内存的大小

            shmflg:IPC_CREAT(可以单独使用)其作用是如果这个key在内核中没有就创建一个新的共享内存,如果有就直接获取它。

                           IPC_EXCL(不可以单独使用要配合IPC_CREAT一起使用)作用是如果申请的共享空间存在了就会出错返回,不存在就创建,确保我们每次的调用创建的都是新的共享内存。

                返回值:成功返回共享内存的标识符shmid,识别返回-1。 

                shmid和key的对比:

                                shmid是在进程内用来表示共享内存唯一性的。

                                 key是在内核中表示共享内存唯一性的。一般这个key只在创建共享内存的时候使用,其他的时候就不用了。

        

        2.代码实现
#include <string>
#include <cerrno>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

#include "log.hpp"

using namespace std;

//使用日志
Log log;

const string pathname = "/home/cky";
const int proj_id     = 0x6666;

key_t GetKey()
{
    key_t key = ftok(pathname.c_str(), proj_id);
    if(key < 0)
    {
        log(Fatal, "ftok error : %s", strerror(errno));
        exit(1);
    }
    log(Info, "ftok success, key is %d", key);

    return key;

}

#define SIZE 4096
int GetshareMemHelper(int flag)
{
    key_t key = GetKey();

    int shmid = shmget(key, SIZE, flag);
    if(shmid < 0)
    {
        log(Fatal, " create share memory error : %s", strerror(errno));
        exit(1);
    }
    log(Info, " create share memory success, shmid is : %d", shmid);

    return shmid;
}

int Greateshm()
{
    return GetshareMemHelper(IPC_CREAT | IPC_EXCL);
}

int Getshm()
{
    return GetshareMemHelper(IPC_CREAT);
}

                查看我们创建的共享内存:

                创建的共享内存如果我们不手动释放掉的话,即使是使用的进程退出了,系统也不会主动的释放掉它。 

                perm:是共享内存的使用权限,此时我们的程序中并没有指定权限所以是0。 

int Greateshm()
{
    return GetshareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}

int Getshm()
{
    return GetshareMemHelper(IPC_CREAT);
}

 

                nattch:是同这个共享内存关联的进程数,此时我们并没有为这个新创建的共享内存关联进程,所以是0。 

                在bash上删除我们创建的共享内存:

        

三.将创建好的共享内存同进程关联在一起 ,去除关联,释放共享内存资源

        1.系统调用接口                

                shmaddr:将指定的共享内存关联到进程地址空间中的什么位置,如果是NULL是由操作系统决定,此时具体在什么位置通过返回值来确定。

                shmflg:默认是0;

         

                        去关联函数。

                        cmd:IPC_RMID 是设置为删除。

                        buf:是类似于内核中管理共享内存的结构体,删除时设置为NULL就可以。

        2.代码实现
    //同共享内存关联
    char* shmaddr = nullptr;
    shmaddr = (char*)shmat(shmid, NULL, 0);


    //去除关联
    shmdt(shmaddr);

    //释放共享内存资源
    shmctl(shmid, IPC_RMID, NULL);

          一但创建好的共享内存和进程所关联了,那么就可以直接使用了,不需要系统调用。

四.进行通信

        processa.cc

#include "comm.hpp"

int main(void)
{
    //创建共享内存
    int shmid = 0;
    shmid = Greateshm();

    //同共享内存关联
    char* shmaddr = nullptr;
    shmaddr = (char*)shmat(shmid, NULL, 0);

    //进行通信
    while(true)
    {
        cout << "client say@ " << shmaddr << endl; //直接访问共享内存
    }

    //去除关联
    shmdt(shmaddr);

    //释放共享内存资源
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

        processb.cc

#include "comm.hpp"

int main()
{
    int shmid = Getshm();
    char *shmaddr = (char*)shmat(shmid, nullptr, 0);

    
    // 一旦有了共享内存,挂接到自己的地址空间中,你直接把他当成你的内存空间来用即可!
    // 不需要调用系统调用
    // ipc code
    while(true)
    {
        cout << "Please Enter@ ";
        fgets(shmaddr, 4096, stdin);

    }

    shmdt(shmaddr);

    return 0;
}

        单纯的共享内存是没有同步互斥的,但是可以通过加管道的方式来实现。

五.通过管道实现共享内存的同步互斥

        命名管道:

enum
{
    FIFO_CREATE_ERR = 1,
    FIFO_DELETE_ERR,
    FIFO_OPEN_ERR
};

#define MODE 0664
#define FIFO_FILE "./myfifo"

class Init
{
public:
    Init()
    {
        // 创建管道
        int n = mkfifo(FIFO_FILE, MODE);
        if (n == -1)
        {
            perror("mkfifo");
            exit(FIFO_CREATE_ERR);
        }
    }
    ~Init()
    {

        int m = unlink(FIFO_FILE);
        if (m == -1)
        {
            perror("unlink");
            exit(FIFO_DELETE_ERR);
        }
    }
};

        processa.cc

#include "comm.hpp"

int main(void)
{
    //创建共享内存
    int shmid = 0;
    shmid = Greateshm();

    //同共享内存关联
    char* shmaddr = nullptr;
    shmaddr = (char*)shmat(shmid, NULL, 0);

    //创建管道
    Init init;

    //打开管道
    int fd = open(FIFO_FILE, O_RDONLY);
    if(fd < 0)
    {
        log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);
        exit(FIFO_OPEN_ERR);
    }

    //进行通信
    while(true)
    {
        char c;
        ssize_t s = read(fd, &c, 1);
        if(s == 0) break;
        else if(s < 0) break;
        
        cout << "client say@ " << shmaddr << endl; //直接访问共享内存
    }

    //去除关联
    shmdt(shmaddr);

    //释放共享内存资源
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

        processb.cc

#include "comm.hpp"

int main()
{
    int shmid = Getshm();
    char *shmaddr = (char*)shmat(shmid, nullptr, 0);

    int fd = open(FIFO_FILE, O_WRONLY);
    if(fd < 0)
    {
        log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);
        exit(FIFO_OPEN_ERR);
    }
    
    // 一旦有了共享内存,挂接到自己的地址空间中,你直接把他当成你的内存空间来用即可!
    // 不需要调用系统调用
    // ipc code
    while(true)
    {
        cout << "Please Enter@ ";
        fgets(shmaddr, 4096, stdin);

        write(fd, "c", 1); // 通知对方
    }

    shmdt(shmaddr);

    return 0;
}

  • 24
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元(MTU)/路径MTU 以太网帧格式 ICMP ARP RARP 03TCPIP基础(三) IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06socket编程(一) 什么是socket IPv4套接口地址结构 网络字节序 字节序转换函数 地址转换函数 套接字类型 07socket编程(二) TCP客户/服务器模型 回射客户/服务器 socket、bind、listen、accept、connect 08socket编程(三) SO_REUSEADDR 处理多客户连接(process-per-conection) 点对点聊天程序实现 09socket编程(四) 流协议与粘包 粘包产生的原因 粘包处理方案 readn writen 回射客户/服务器 10socket编程(五) read、write与recv、send readline实现 用readline实现回射客户/服务器 getsockname、getpeername gethostname、gethostbyname、gethostbyaddr 11socket编程(六) TCP回射客户/服务器 TCP是个流协议 僵进程与SIGCHLD信号 12socket编程(七) TCP 11种状态 连接建立三次握手、连接终止四次握手 TIME_WAIT与SO_REUSEADDR SIGPIPE 13socket编程(八) 五种I/O模型 select 用select改进回射客户端程序 14socket编程(九) select 读、写、异常事件发生条件 用select改进回射服务器程序。 15socket编程(十) 用select改进第八章点对点聊天程序 16socket编程(十一) 套接字I/O超时设置方法 用select实现超时 read_timeout函数封装 write_timeout函数封装 accept_timeout函数封装 connect_timeout函数封装 17socket编程(十二) select限制 poll 18socket编程(十三) epoll使用 epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址结构 UNIX域字节流回射客户/服务 UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程通信篇 23进程通信介绍(一) 进程同步与进程互斥 进程通信目的 进程通信发展 进程通信分类 进程共享信息的三种方式 IPC对象的持续性 24进程通信介绍(二) 死锁 信号量 PV原语 用PV原语解决司机与售票员问题 用PV原语解决民航售票问题 用PV原语解决汽车租赁问题 25System V消息队列(一) 消息队列 IPC对象数据结构 消息队列结构 消息队列在内核中的表示 消息队列函数 26System V消息队列(二) msgsnd函数 msgrcv函数 27System V消息队列(三) 消息队列实现回射客户/服务器 28共享内存介绍 共享内存 共享内存示意图 管道、消息队列与共享内存传递数据对比 mmap函数 munmap函数 msync函数 29System V共享内存 共享内存数据结构 共享内存函数 共享内存示例 30System V信号量(一) 信号量 信号量集结构 信号量集函数 信号量示例 31System V信号量(二) 用信号量实现进程互斥示例 32System V信号量(三) 用信号集解决哲学家就餐问题 33System V共享内存与信号量综合 用信号量解决生产者消费者问题 实现shmfifo 34POSIX消息队列 POSIX消息队列相关函数 POSIX消息队列示例 35POSIX共享内存 POSIX共享内存相关函数 POSIX共享内存示例 Linux网络编程之线程篇 36线程介绍 什么是线程 进程与线程 线程优缺点 线程模型 N:1用户线程模型 1:1核心线程模型 N:M混合线程模型 37POSIX线程(一) POSIX线程库相关函数 用线程实现回射客户/服务器 38POSIX线程(二) 线程属性 线程特定数据 39POSIX信号量与互斥锁 POSIX信号量相关函数 POSIX互斥锁相关函数 生产者消费者问题 自旋锁与读写锁介绍 40POSIX条件变量 条件变量 条件变量函数 条件变量使用规范 使用条件变量解决生产者消费者问题 41一个简单的线程池实现 线程池性能分析 线程池实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值