一.makefile编写
.PHONY:all
all:processa processbprocessa : processa.ccg++ -o $@ $^ -g -std=c++11
processb : processb.ccg++ -o $@ $^ -g -std=c++11.PHONY:clean
clean:rm -rf processa processb
二.创建system v共享内存
1.系统调用接口
key:a.key是一个数值,在内核中具有唯一性,可以让不同的进程标识唯一的共享内存。
b.第一个进程可以通过key创建共享内存,从此第二个进程在拿着相同的key就可以和第一个进程看到同一块共享内存了。
c.这个key保存在共享内存的描述结构体中。
d.需要强调的是key是由用户给出相应的信息然后,系统根据算法来返回一个计算到的key。
ftok函数调用成功返回key值,失败返回-1。
用户在程序中调用这个函数就可以得到一个key。然后将这个key传给shmget就可以得到一个又这个key在内核中唯一标识的共享内存。
size:所需要的共享内存的大小
shmflg:IPC_CREAT(可以单独使用)其作用是如果这个key在内核中没有就创建一个新的共享内存,如果有就直接获取它。
IPC_EXCL(不可以单独使用要配合IPC_CREAT一起使用)作用是如果申请的共享空间存在了就会出错返回,不存在就创建,确保我们每次的调用创建的都是新的共享内存。
返回值:成功返回共享内存的标识符shmid,识别返回-1。
shmid和key的对比:
shmid是在进程内用来表示共享内存唯一性的。
key是在内核中表示共享内存唯一性的。一般这个key只在创建共享内存的时候使用,其他的时候就不用了。
2.代码实现
#include <string>
#include <cerrno>
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>#include "log.hpp"using namespace std;//使用日志
Log log;const string pathname = "/home/cky";
const int proj_id = 0x6666;key_t GetKey()
{key_t key = ftok(pathname.c_str(), proj_id);if(key < 0){log(Fatal, "ftok error : %s", strerror(errno));exit(1);}log(Info, "ftok success, key is %d", key);return key;}#define SIZE 4096
int GetshareMemHelper(int flag)
{key_t key = GetKey();int shmid = shmget(key, SIZE, flag);if(shmid < 0){log(Fatal, " create share memory error : %s", strerror(errno));exit(1);}log(Info, " create share memory success, shmid is : %d", shmid);return shmid;
}int Greateshm()
{return GetshareMemHelper(IPC_CREAT | IPC_EXCL);
}int Getshm()
{return GetshareMemHelper(IPC_CREAT);
}
查看我们创建的共享内存:
创建的共享内存如果我们不手动释放掉的话,即使是使用的进程退出了,系统也不会主动的释放掉它。
perm:是共享内存的使用权限,此时我们的程序中并没有指定权限所以是0。
int Greateshm()
{return GetshareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}int Getshm()
{return GetshareMemHelper(IPC_CREAT);
}
nattch:是同这个共享内存关联的进程数,此时我们并没有为这个新创建的共享内存关联进程,所以是0。
在bash上删除我们创建的共享内存:
三.将创建好的共享内存同进程关联在一起 ,去除关联,释放共享内存资源
1.系统调用接口
shmaddr:将指定的共享内存关联到进程地址空间中的什么位置,如果是NULL是由操作系统决定,此时具体在什么位置通过返回值来确定。
shmflg:默认是0;
去关联函数。
cmd:IPC_RMID 是设置为删除。
buf:是类似于内核中管理共享内存的结构体,删除时设置为NULL就可以。
2.代码实现
//同共享内存关联char* shmaddr = nullptr;shmaddr = (char*)shmat(shmid, NULL, 0);//去除关联shmdt(shmaddr);//释放共享内存资源shmctl(shmid, IPC_RMID, NULL);
一但创建好的共享内存和进程所关联了,那么就可以直接使用了,不需要系统调用。
四.进行通信
processa.cc
#include "comm.hpp"int main(void)
{//创建共享内存int shmid = 0;shmid = Greateshm();//同共享内存关联char* shmaddr = nullptr;shmaddr = (char*)shmat(shmid, NULL, 0);//进行通信while(true){cout << "client say@ " << shmaddr << endl; //直接访问共享内存}//去除关联shmdt(shmaddr);//释放共享内存资源shmctl(shmid, IPC_RMID, NULL);return 0;
}
processb.cc
#include "comm.hpp"int main()
{int shmid = Getshm();char *shmaddr = (char*)shmat(shmid, nullptr, 0);// 一旦有了共享内存,挂接到自己的地址空间中,你直接把他当成你的内存空间来用即可!// 不需要调用系统调用// ipc codewhile(true){cout << "Please Enter@ ";fgets(shmaddr, 4096, stdin);}shmdt(shmaddr);return 0;
}
单纯的共享内存是没有同步互斥的,但是可以通过加管道的方式来实现。
五.通过管道实现共享内存的同步互斥
命名管道:
enum
{FIFO_CREATE_ERR = 1,FIFO_DELETE_ERR,FIFO_OPEN_ERR
};#define MODE 0664
#define FIFO_FILE "./myfifo"class Init
{
public:Init(){// 创建管道int n = mkfifo(FIFO_FILE, MODE);if (n == -1){perror("mkfifo");exit(FIFO_CREATE_ERR);}}~Init(){int m = unlink(FIFO_FILE);if (m == -1){perror("unlink");exit(FIFO_DELETE_ERR);}}
};
processa.cc
#include "comm.hpp"int main(void)
{//创建共享内存int shmid = 0;shmid = Greateshm();//同共享内存关联char* shmaddr = nullptr;shmaddr = (char*)shmat(shmid, NULL, 0);//创建管道Init init;//打开管道int fd = open(FIFO_FILE, O_RDONLY);if(fd < 0){log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);exit(FIFO_OPEN_ERR);}//进行通信while(true){char c;ssize_t s = read(fd, &c, 1);if(s == 0) break;else if(s < 0) break;cout << "client say@ " << shmaddr << endl; //直接访问共享内存}//去除关联shmdt(shmaddr);//释放共享内存资源shmctl(shmid, IPC_RMID, NULL);return 0;
}
processb.cc
#include "comm.hpp"int main()
{int shmid = Getshm();char *shmaddr = (char*)shmat(shmid, nullptr, 0);int fd = open(FIFO_FILE, O_WRONLY);if(fd < 0){log(Fatal, "error string: %s, error code: %d", strerror(errno), errno);exit(FIFO_OPEN_ERR);}// 一旦有了共享内存,挂接到自己的地址空间中,你直接把他当成你的内存空间来用即可!// 不需要调用系统调用// ipc codewhile(true){cout << "Please Enter@ ";fgets(shmaddr, 4096, stdin);write(fd, "c", 1); // 通知对方}shmdt(shmaddr);return 0;
}