前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >shm进程间通信失败了!!!

shm进程间通信失败了!!!

作者头像
看、未来
发布于 2020-08-26 03:10:50
发布于 2020-08-26 03:10:50
59000
代码可运行
举报
运行总次数:0
代码可运行

先解决问题

如果你是在网上辗转而不得其解,那就来我这儿吧。

之前那篇写的比较急,讲的还是蛮有条理的,就是东西少了点,这篇一次性写完。

那天,我和共享内存、shmid不眠不休只吃一点喝一点奋战了十个小时,只为了把我的项目进度赶在大家前面,却被进程间通信始终无法打通而拦住。解决问题之后,有感而作。

如果放在今天,我会选择采用TCP流协议的方式来进行进程间通信,详情:你会不会分布式系统进程间通信

不过我们现在讲的是shm,好。

以下内容基于在一个进程里至少准备挂两个共享内存,一个用来发,一个用来收

既然用到shm,那自然和key值要打交道。

key值有fotk函数生成,如果对ftok函数不熟,有空可以看一下这篇:ftok

讲的是极好的,不是我写的。

我遇到的第一个问题,是:不同参数的ftok生成同样的shmid值。

为什么呢?不知道。

但是我还不算傻,至少知道做个demo把key值打印出来看,全是-1。

ftok的第一个参数得是有效的文件路径。

看了上面那篇文章之后,我将代码进行了修改,接下来就遇到了第二个问题:同样参数的ftok函数生成了不同的key值

这个就不好找咯,上面那个还能在网上找到点蛛丝马迹,这个要是找到希望能在下面给我留个网址,感激不尽。

这个就不好找咯,上面那个还能在网上找到点蛛丝马迹,这个要是找到希望能在下面给我留个网址,感激不尽。

这个就要分两种情况了(我遇到两种),第一种就是代码的问题,刚开始我写的花里胡哨的,后面老实了,拿到key值之后直接就shm_get, 这下shmid也老实了,不过还是会差,因为key值会偏差一点。

第二种情况,

其实问题也很简单,就是目录的差别。如果你用的是绝对目录那就比较好,但是如果给ftok传参传的是相对目录,而你运行的两个执行文件所在的目录又不同,那么ftok计算key值时从当前进程所在目录出发,自然是会有偏差的。

怎么办?怎么办?

小事情,这里有两个方法:

1、将两个执行文件放在统一目录底下,方法是好方法,不过最好你得会写Makefile

2、使用绝对路径,其实这个方法也能另辟蹊径,什么呢, / ,就是这个斜杠,杠杠的绝对路径

shm共享内存

创建或打开共享内存

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  #include <sys/ipc.h>
  #include <sys/shm.h>

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

参数不释义,后面有例子

挂载共享内存

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     #include <sys/types.h>
     #include <sys/shm.h>

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

分离共享内存

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
       #include <sys/shm.h>

       int shmdt(const void *shmaddr);

控制共享内存

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
       #include <sys/ipc.h>
       #include <sys/shm.h>

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

示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "f_shm.h"

#include <sys/types.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>

typedef struct shmhead_st
{
    int shmid;			// 共享内存ID

    unsigned int blksize;		// 块大小
    unsigned int blocks;		// 总块数
    unsigned int rd_index;		// 读索引
    unsigned int wr_index;		// 写索引

        //必须放在共享内存内部才行
    sem_t sem_mutex;	// 用来互斥用的信号量
    sem_t sem_full;		// 用来控制共享内存是否满的信号量
    sem_t sem_empty;	// 用来控制共享内存是否空的信号量

}shmhead_t;

F_Shm::F_Shm(key_t key, int blksize, int blocks)
{
    this->open_shm(key, blksize, blocks);
}

F_Shm::F_Shm()
{
    shmhead = NULL;
    payload = NULL;
    open = false;
}

F_Shm::~F_Shm()
{
    this->close_shm();
}
//返回头地址
bool F_Shm::creat_shm(key_t key, int blksize, int blocks)
{
    int shmid = 0;

    //1. 查看是否已经存在共享内存,如果有则删除旧的
    shmid = shmget(key, 0, 0);
    if (shmid != -1)
    {
        shmctl(shmid, IPC_RMID, NULL); 	//	删除已经存在的共享内存
    }

    //2. 创建共享内存
    shmid = shmget(key, sizeof(shmhead_t) + blksize*blocks, 0666 | IPC_CREAT | IPC_EXCL);
    if(shmid == -1)
    {
        ERR_EXIT("shmget");
    }
    printf("Create shmid=%d size=%u \n", shmid, sizeof(shmhead_t) + blksize*blocks);

    //3.连接共享内存
    shmhead = shmat(shmid, (void*)0, 0);					//连接共享内存
    if(shmhead == (void*)-1)
    {
        ERR_EXIT("shmat");
    }
    memset(shmhead, 0, sizeof(shmhead_t) + blksize*blocks);		//初始化

    //4. 初始化共享内存信息
    shmhead_t * pHead = (shmhead_t *)(shmhead);
    pHead->shmid	= shmid;				//共享内存shmid
    pHead->blksize	= blksize;			//共享信息写入
    pHead->blocks	= blocks;				//写入每块大小
    pHead->rd_index = 0;					//一开始位置都是第一块
    pHead->wr_index = 0;					//
    sem_init(&pHead->sem_mutex, 1, 1);	// 第一个1表示可以跨进程共享,第二个1表示初始值
    sem_init(&pHead->sem_empty, 1, 0);	// 第一个1表示可以跨进程共享,第二个0表示初始值
    sem_init(&pHead->sem_full, 1, blocks);// 第一个1表示可以跨进程共享,第二个blocks表示初始值

    //5. 填充控制共享内存的信息
    payload = (char *)(pHead + 1);	//实际负载起始位置
    open = true;

    return true;
}

void F_Shm::dsy_shm()
{
    shmhead_t *pHead = (shmhead_t *)shmhead;
    int shmid = pHead->shmid;

    //删除信号量
    sem_destroy (&pHead->sem_full);
    sem_destroy (&pHead->sem_empty);
    sem_destroy (&pHead->sem_mutex);
    shmdt(shmhead); //共享内存脱离

    //销毁共享内存
    if(shmctl(shmid, IPC_RMID, 0) == -1)		//删除共享内存
    {
        printf("Delete shmid=%d \n", shmid);
        ERR_EXIT("shmctl rm");
    }

    shmhead = NULL;
    payload = NULL;
    open = false;
}

void F_Shm::Destroy(key_t key)
{
    int shmid = 0;

    //1. 查看是否已经存在共享内存,如果有则删除旧的
    shmid = shmget(key, 0, 0);
    if (shmid != -1)
    {
        printf("Delete shmid=%d \n", shmid);
        shmctl(shmid, IPC_RMID, NULL); 	//	删除已经存在的共享内存
    }
}

//返回头地址
bool F_Shm::open_shm(key_t key, int blksize, int blocks)
{
    int shmid;

    this->close_shm();

    //1. 查看是否已经存在共享内存,如果有则删除旧的
    shmid = shmget(key, 0, 0);
    if (shmid == -1)
    {
        return this->creat_shm(key, blksize, blocks);
    }

    //2.连接共享内存
    shmhead = shmat(shmid, (void*)0, 0);					//连接共享内存
    if(shmhead == (void*)-1)
    {
        ERR_EXIT("shmat");
    }
    printf("Open shmid=%d size=%u \n", shmid, sizeof(shmhead_t) + blksize*blocks);

    //3. 填充控制共享内存的信息
    payload = (char *)((shmhead_t *)shmhead + 1);	//实际负载起始位置
    open = true;

    return true;
}


//关闭共享内存
void F_Shm::close_shm(void)
{
    if(open)
    {
        shmdt(shmhead); //共享内存脱离
        shmhead = NULL;
        payload = NULL;
        open = false;
    }
}

void F_Shm::write_into_shm(const void *buf)
{

    shmhead_t *pHead = (shmhead_t *)shmhead;

    sem_wait(&pHead->sem_full);				//是否有资源写?	可用写资源-1
    sem_wait(&pHead->sem_mutex);				//是否有人正在写?

    printf("write to shm[%d] index %d \n", pHead->shmid, pHead->rd_index);
    memcpy(payload + (pHead->wr_index) * (pHead->blksize), buf, pHead->blksize);
    pHead->wr_index = (pHead->wr_index+1) % (pHead->blocks);	//写位置偏移
    sem_post(&pHead->sem_mutex);				//解除互斥
    sem_post(&pHead->sem_empty);				//可用读资源+1
}

void F_Shm::read_from_shm(void *buf)
{
    shmhead_t *pHead = (shmhead_t *)shmhead;

    sem_wait(&pHead->sem_empty);				//检测写资源是否可用

    printf("read from shm[%d] index %d \n", pHead->shmid, pHead->rd_index);
    memcpy(buf, payload + (pHead->rd_index) * (pHead->blksize), pHead->blksize);

    //读位置偏移
    pHead->rd_index = (pHead->rd_index+1) % (pHead->blocks);
    sem_post(&pHead->sem_full);					//增加可写资源
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/06/11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
开发成长之路(14)-- 小项目:视频点播器服务端(放码过来)
开发成长之路(1)-- C语言从入门到开发(入门篇一) 开发成长之路(2)-- C语言从入门到开发(函数与定制输入输出控制函数) 开发成长之路(3)-- C语言从入门到开发(讲明白指针和引用,链表很难吗?) 开发成长之路(4)-- C语言从入门到开发(距离开发,还差这一篇) 开发成长之路(5)-- C语言从入门到开发(仿ATM机项目,我写的第一个项目) 开发成长之路(6)-- C++从入门到开发(C++入门不难) 开发成长之路(6)-- C++从入门到开发(C++知名库:STL入门·容器(一)) 开发成长之路(7)-- C++从入门到开发(C++知名库:STL入门·容器(二)) 开发成长之路(8)-- C++从入门到开发(C++知名库:STL入门·容器(三)) 开发成长之路(9)-- C++从入门到开发(C++知名库:STL入门·空间配置器) 开发成长之路(10)-- C++从入门到开发(C++知名库:STL入门·算法) 开发成长之路(11)-- STL常用函数大集合 开发成长之路(12)-- Linux网络服务端编程(通识篇之熟悉操作环境) 开发成长之路(13)-- Linux网络服务端编程(通识篇)
看、未来
2021/09/18
4970
【Linux】进程间通信——System V共享内存
  System V是一种在Linux系统中用于进程间通信(IPC)的机制。它提供了几种不同的通信方式,包括共享内存、消息队列和信号量。以下是关于Linux进程间通信System V共享内存的详细解释:
大耳朵土土垚
2024/12/09
2200
【Linux】进程间通信——System V共享内存
Linux进程间通信之共享内存
一,共享内存   内核管理一片物理内存,允许不同的进程同时映射,多个进程可以映射同一块内存,被多个进程同时映射的物理内存,即共享内存。   映射物理内存叫挂接,用完以后解除映射叫脱接。 1,共享内存的特点: 优点:是最快的IPC。 缺点:要编程者自己实现对共享内存互斥访问。如何实现? 2,编程模型:具体函数的用法可以用man手册查看(强力推荐) 进程A: writeshm.c      1) 获得key, ftok()      2) 使用key来创建一个共享内存 shmget()      3)
xcywt
2018/01/11
4.5K0
Linux进程间通信之共享内存
【Linux】从零开始认识进程间通信 —— 共享内存
吃苦受难绝不是乐事一桩,但是如果您恰好陷入困境,我很想告诉您:“尽管眼前十分困难,可日后这段经历说不定就会开花结果。”请您这样换位思考、奋力前行。
叫我龙翔
2024/06/02
3830
【Linux】从零开始认识进程间通信 —— 共享内存
Linux进程间通信之System V
对于进程间通信,想必管道大家再熟悉不过了,对于管道这种通信方式,其实是对底层代码的一种复用,linux工程师借助类似文件缓冲区的内存空间实现了管道,其实也算偷了一个小懒,随着linux的发展,linux正式推出了System V来专门进行进程间通信,它和管道的本质都是一样的,都是让不同的进程看到同一份资源。
咬咬
2024/06/12
1620
Linux进程间通信之System V
进程间通信学习小结(共享内存)
要使用共享内存,应该有如下步骤:1.开辟一块共享内存shmget()2.允许本进程使用共某块共享内运维
Java架构师必看
2021/03/22
9280
Linux进程间通信【共享内存】
共享内存出自 System V 标准,是众多 IPC 解决方案中最快的一种,使用共享内存进行通信时,不需要借助函数进入内核传递数据,而是直接对同一块空间进行数据访问,至于共享内存是如何使用的、通信原理是怎么实现的、以及共享内存+命名管道的组合通信程序该如何实现,都将在本文中解答
北 海
2023/07/01
5890
Linux进程间通信【共享内存】
Linux系统编程-进程间通信(共享内存)
前面陆续介绍了标准管道流、无名管道、命名管道、mmap内存映射,这篇文章介绍共享内存段。
DS小龙哥
2022/02/17
2.2K0
进程间通信(二)/共享内存
要实现进程间通信,其前提是让不同进程之间看到同一份资源。所谓共享内存,那就是不同进程之间,可以看到内存中同一块资源,这就是共享内存的概念。
二肥是只大懒蓝猫
2023/03/30
9620
进程间通信(二)/共享内存
【Linux】system V进程间通信——共享内存、消息队列、信号量
进程具有独立性:内核数据结构包括对应的代码、数据与页表都是独立的。OS系统为了让进程间进行通信:1.申请一块空间 2.将创建好的内存映射进进程的地址空间。共享内存让不同的进程看到同一份的资源就是在物理内存上申请一块内存空间,如何将创建好的内存分别与各个进程的页表之间建立映射,然后在虚拟地址空间中将虚拟地址填充到各自页表的对应位置,建立起物理地址与虚拟地址的联系。
平凡的人1
2023/10/15
4100
【Linux】system V进程间通信——共享内存、消息队列、信号量
【Linux】进程间通信「建议收藏」
进程之间可能会存在特定的协同工作的场景,而协同就必须要进行进程间通信,协同工作可能有以下场景。
全栈程序员站长
2022/11/10
1.5K0
【Linux】进程间通信「建议收藏」
【Linux】IPC 进程间通信(二)(共享内存)
🚀 共享内存是一种进程间通信(IPC)机制,它允许多个进程直接访问同一块内存区域,从而实现高效的数据交换。
IsLand1314
2024/11/19
2920
【Linux】IPC 进程间通信(二)(共享内存)
Linux进程间通信之《共享内存》入门
共享内存是Linux系统进程间通信常用的方式,通常用于数据量较大的情况,如果只是用于不同的进程间消息通知,那不如用消息队列或者socket。之前做的项目中,使用共享内存的其实只有一种情况:视频数据的共享。设备类似于DVR,视频采集编码在一个独立的程序中,另一个程序负责协议通信。
fensnote
2021/05/31
2.1K0
Linux进程间通信之《共享内存》入门
Linux 进程间通信 : 共享内存(上)
邹立巍
2017/07/26
11.5K3
进程间通信方式——共享内存「建议收藏」
共享内存就是允许两个或多个进程共享一定的存储区。就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。因为数据不需要在客户机和服务器端之间复制,数据直接写到内存,不用若干次数据拷贝,所以这是最快的一种IPC。
全栈程序员站长
2022/09/27
1.2K0
进程间通信 - 共享内存
在Linux系统中,每个进程都有独立的虚拟内存空间,也就是说不同的进程访问同一段虚拟内存地址所得到的数据是不一样的,这是因为不同进程相同的虚拟内存地址会映射到不同的物理内存地址上。
用户7686797
2020/08/25
1.5K0
【Linux篇章】进程通信黑科技:System V 共享内存,开启进程间通信的星际数据通道!
在计算机系统里,进程就像一个个独立工作的小员工,各自完成自己的任务。但有时候,这些小员工之间需要交流信息、共享数据,这就涉及到进程间通信(IPC)。System V 共享内存就是进程间通信的一种强大方式,下面就带大家了解一下它的奥秘。
羑悻的小杀马特.
2025/04/30
1240
【Linux篇章】进程通信黑科技:System V 共享内存,开启进程间通信的星际数据通道!
进程间通信 共享内存
取消共享地址映射 当共享内存使用完毕后,调此接口会结束共享内存与指定的系统地址的映射关系。这里并未从系统中删除标识符,该标识符一直存在直至某个进程带IPC_RMID命令调用shmctl特地删除它为止。
开源519
2021/09/16
1.1K0
进程间通信 共享内存
【操作系统】进程间的通信——共享内存
进程间的通信-共享内存 共享内存机制 共享内存机制是允许两个或多个进程(不相关或有亲缘关系)访问同一逻辑内存的机制。它是共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。 ---- 两种常用的共享内存方式 System V版本的共享内存 shmm 多进程直接共享内存 文件映射mmap 如果一个文件需要频繁进行读写,那么将它映射到内存中。 将特殊文件进行匿名内存映射,为有关联的进程提供共享内存空间。 为无关联的进程提供共享内存空间,将
半生瓜的blog
2023/05/13
9330
【操作系统】进程间的通信——共享内存
linux网络编程之System V 信号量(三):基于生产者-消费者模型实现先进先出的共享内存段
生产者消费者问题:该问题描述了两个共享固定大小缓冲区的进程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费
s1mba
2017/12/28
1.6K0
linux网络编程之System V 信号量(三):基于生产者-消费者模型实现先进先出的共享内存段
推荐阅读
相关推荐
开发成长之路(14)-- 小项目:视频点播器服务端(放码过来)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验