概念
共享内存
共享内存可以说是最高效的进程间通信方式。同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
临界资源:多个进程看到的同一个资源(公共资源)
临界区:访问临界资源的代码段。
原子操作:操作一旦开始执行,就一定要执行结束,中途不能被任何原因打断,要么全做,要么不做,不能只做一半。
信号量(用来协调进程对共享资源的访问),程序对其访问都是原子操作,且只允许对它进行等待(P)和释放(V)操作。
作用:控制多个进程对临界资源的访问,使程序在同一个时刻,只有一个进程访问临界资源(进行进程间同步控制)。
P(sv):如果信号量sv的值大于零,就给它减1;如果它的值为零,就挂起等待。
V(sv):释放操作,它使信号量变为可用,如有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
例子:两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。
当临界区资源可用时,信号量变量n的值是true(即二进制信号量),然后P(n)操作将它变成false以表示临界区正在被使用;当进程离开临界区时,使用V(n)操作将它加1,置为true,使临界区再次变为可用。
代码
sharedMemory.h
#pragma once
#include <string>
#include <vector>
#include <sys/shm.h>
#include "sem.h"
struct shmData
{
int status;//状态字段(1为可读,0为不可读)
char* data;
};
class SharedMemory
{
public:
SharedMemory();
SharedMemory(int type);
~SharedMemory();
char* createMemory(int size);
int destoryMemory();
void shmLock();
void shmUnLock();
private:
Sem sem;
int shmId;
void* shm;
int key;
};
sharedMemory.cpp:
#include "sharedMemory.h"
SharedMemory::SharedMemory(int type): key(type), sem(type)
{
}
SharedMemory::~SharedMemory()
{
cout<< "delete SharedMemory..." <<endl;
}
char* SharedMemory::createMemory(int size) {
//创建共享内存,指定共享内存名字key,大小size
shmId = shmget((key_t)key, size, 0666 | IPC_CREAT);
CHECK_DO_RTN_VAL(shmId == -1, cout<<"shmget failed\n", nullptr)
// 将共享内存连接到当前进程的地址空间,返回指向共享内存第一个字节的指针
shm = shmat(shmId, 0, 0);
CHECK_DO_RTN_VAL(shm == (void*)-1, cout<<"shmat failed\n", nullptr)
return (char*)shm;
}
int SharedMemory::destoryMemory() {
shmdt(shm); //共享内存从当前进程中分离
shmctl(shmId, IPC_RMID, 0); //删除共享内存
}
void SharedMemory::shmLock() {
sem.semP();
}
void SharedMemory::shmUnLock() {
sem.semV();
}
sem.h:
#pragma once
#include <sys/sem.h>
#include <unistd.h>
#include <iostream>
#include <string.h>
#define CHECK_RTN(condition) if(condition){return;}
#define CHECK_DO_RTN(condition, needDo) if(condition){needDo; return;}
#define CHECK_RTN_VAL(condition, val) if(condition){return val;}
#define CHECK_DO_RTN_VAL(condition, needDo, val) if(condition){needDo; return val;}
#define CHECK_DO(condition, needDo) if(condition){needDo;}
using namespace std;
class Sem
{
public:
Sem(int type);
~Sem();
void semP();
void semV();
private:
void init();
int key;
int semId;
};
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
sem.cpp:
#include "sem.h"
Sem::Sem(int type): key(type + 1)
{
init();
}
Sem::~Sem()
{
int rlt = semctl(semId, 0, IPC_RMID); //删除信号量
CHECK_DO(rlt == -1, cout<<"delete sem failed\n")
}
void Sem::init()
{
cout<< "semget key" << key << endl;
semId = semget((key_t)key, 1, IPC_CREAT | 0600);
CHECK_DO(semId == -1, cout<<"semget failed\n" << strerror(errno))
union semun initVal;
initVal.val = 1; //给信号量赋初值1
int rlt = semctl(semId, 0, SETVAL, initVal);
CHECK_DO(rlt == -1, cout<<"semctl failed\n"<< strerror(errno))
}
void Sem::semP()
{
struct sembuf buf;
buf.sem_num = 0; //信号量下标
buf.sem_op = -1; //p 减1操作
buf.sem_flg = SEM_UNDO; //设置在进程出现错误时信号量值自动恢复,防止一个进程占着信号量
int rlt = semop(semId, &buf, 1); //如果信号量sv的值大于零,就给它减1;如果它的值为零,就挂起等待
CHECK_DO(rlt == -1, cout<<"semop p failed\n" << strerror(errno))
}
void Sem::semV()
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;//v 加1操作
buf.sem_flg = SEM_UNDO;
int rlt = semop(semId, &buf, 1);
CHECK_DO(rlt == -1, cout<<"semop v failed\n")
}
#include "sharedMemory.h"
#include <sys/time.h>
//g++ sharedMemory.cpp sem.cpp write.cpp -o write -std=c++11
//g++ sharedMemory.cpp sem.cpp read.cpp -o read -std=c++11
const int CAMERA = 100001;
const int SIZE = 1920 * 1080;
uint64_t getSystemClock()
{
struct timespec stTime;
uint64_t ret ;
clock_gettime(CLOCK_MONOTONIC, &stTime);
ret = stTime.tv_sec * 1000000 + stTime.tv_nsec / 1000L;
return ret;
}
int main() {
char array[SIZE + 1];
memset(array, 0, SIZE + 1);
SharedMemory sharedMemory(CAMERA);
char* shm = sharedMemory.createMemory(SIZE);
cout<< "start read app.." <<endl;
for(int i = 0 ; i < 10000; ++i){
sharedMemory.shmLock();
memcpy(array, shm, SIZE);
sharedMemory.shmUnLock();
}
cout<< "read time: " << getSystemClock() << endl;
sharedMemory.destoryMemory();
}
#include "sharedMemory.h"
#include <sys/time.h>
const int CAMERA = 100001;
const int SIZE = 1920 * 1080;
uint64_t getSystemClock()
{
struct timespec stTime;
uint64_t ret ;
clock_gettime(CLOCK_MONOTONIC, &stTime);
ret = stTime.tv_sec * 1000000 + stTime.tv_nsec / 1000L;
return ret;
}
int main() {
char array[SIZE];
memset(array, 'a', SIZE);
SharedMemory sharedMemory(CAMERA);
char* shm = sharedMemory.createMemory(SIZE);
cout<< "start write app.." <<endl;
cout<< "write time: " << getSystemClock() << endl;
for(int i = 0 ;i < 10000; ++i){
sharedMemory.shmLock();
memcpy(static_cast<void*>(const_cast<char*>(shm)), array, SIZE);
sharedMemory.shmUnLock();
}
sharedMemory.destoryMemory();
}