稍安勿躁。
先解決問題
如果你是在網上輾轉而不得其解,那就來我這兒吧。
之前那篇寫的比較急,講的還是蠻有條理的,就是東西少了點,這篇一次性寫完。
那天,我和共享內存、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); //增加可寫資源
}