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);					//增加可寫資源
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章