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

时间:2022-07-23
本文章向大家介绍shm进程间通信失败了!!!,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

先解决问题

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

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

那天,我和共享内存、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共享内存

创建或打开共享内存

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

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

参数不释义,后面有例子

挂载共享内存

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

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

分离共享内存

       #include <sys/shm.h>

       int shmdt(const void *shmaddr);

控制共享内存

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

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

示例

#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);					//增加可写资源
}