(。・?・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~https://blog.csdn.net/ky233?type=blog
点个关注不迷路?'?'?
目录
共享内存顾名思义,就是在内存中堆区和栈区相对,中间还有一个共享内存区,两个进程同时访问,即可完成进程之间的通信!
这里要注意!
我们开辟的共享内存是在虚拟内存上开辟然后在,然后映射到真是的物理内存的,所以双方要进行进程通信直接用内存级别的读写就可以了,这是在堆栈之间开辟的,无需经过系统调用,而管道本质上还是经过文件,这是要有对应的内核数据结构,所以管道要用操作系统来调用!?
首先共享内存的提供者是操作系统,共享内存也是要经过OS管理的还是经过先描述在组织
所以共享内存=共享内存块+对应的共享内存的内核数据结构
int shmget(key_t key,size_t size,int shmflg)
key_t ftok(const char *pathname,int proi id)
用此函数来形成key值
只要两个进程使用同样的pathname,和id那么就会生成唯一key
Server:
int main()
{
key_t k = ftok(PATH_NAME,PROJ_ID);
Log("create key done",Debug)<<"server key:"<<k<<endl;
return 0;
}
Client:?
int main()
{
key_t k = ftok(PATH_NAME,PROJ_ID);
Log("create key done",Debug)<<"client key:"<<k<<endl;
return 0;
}
?由此我们可以看见两个进程获取的是同一个key值,而且是一个随机值
当进程运行结束,我们的共享内存还存在!system V IPC资源的生命周期是随我们内核的!
要想删除两种办法
手动删除太麻烦,所以我们可以用shmctl来删除
int shmctl(int shmid,int cmd,struct shmid_ds *buf)
我们创建完共享内存之后需要挂接到我们需要的进程的地址空间中,所以我们用这个函数
void *shmat(int shmid,const void *shmaddr,int shmflg)
7.shmdt的用法
int shmdt(const void *shmaddr)
shmServer:
#include "comm.hpp"
#include "Log.hpp"
string TransToHex(key_t &k)
{
char buffer[32];
snprintf(buffer, sizeof buffer, "0x%x", k);
return buffer;
}
int main()
{
// 创建key值
key_t k = ftok(PATH_NAME, PROJ_ID);
assert(k != -1);
Log("create key done", Debug) << "server key:" << TransToHex(k) << endl;
// 创建共享内存
int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
if (shmid == -1)
{
perror("shmget");
exit(1);
}
Log("create shm done", Debug) << "shmid:" << shmid << endl;
// 将指定的共享内存挂接到自己的地址空间
char *shmaddr = (char *)shmat(shmid, nullptr, 0);
Log("attach shm done", Debug) << " shmid : " << shmid << endl;
// 进行通信
for (;;)
{
cout << shmaddr << endl;
if(strcmp(shmaddr,"quit")==0)
break;
sleep(1);
}
// 将共享内存去关联
int n = shmdt(shmaddr);
assert(n != -1);
(void)n;
Log("detach shm done", Debug) << " shmid : " << shmid << endl;
// 删除共享内存
n = shmctl(shmid, IPC_RMID, nullptr);
assert(n != -1);
(void)n;
Log("delete shm done", Debug) << " shmid : " << shmid << endl;
return 0;
}
shmClient:
#include "comm.hpp"
#include "Log.hpp"
int main()
{
key_t k = ftok(PATH_NAME, PROJ_ID);
if (k < 0)
{
Log("create key failed", Error) << " client key : " << k << endl;
exit(1);
}
Log("create key done", Debug) << " client key : " << k << endl;
// 获取共享内存
int shmid = shmget(k, SHM_SIZE, 0);
if (shmid < 0)
{
Log("create shm failed", Error) << " client key : " << k << endl;
exit(2);
}
Log("create shm success", Error) << " client key : " << k << endl;
char *shmaddr = (char *)shmat(shmid, nullptr, 0);
if (shmaddr == nullptr)
{
Log("attach shm failed", Error) << " client key : " << k << endl;
exit(3);
}
Log("attach shm success", Error) << " client key : " << k << endl;
// 使用
// 看做char类型的buffer
char a = 'a';
for (; a < 'z'; a++)
{
snprintf(shmaddr, SHM_SIZE - 1, "你好,这是我的pid:%d,inc:%c", getpid(), a);
sleep(2);
}
strcpy(shmaddr,"quit");
// 去关联
int n = shmdt(shmaddr);
assert(n != -1);
Log("detach shm success", Error) << " client key : " << k << endl;
return 0;
}
由此可见,我们的两个进程利用共享内存来完成了通信!!!
我们可以封装一个管道,以此来进行控制!
comm.hpp:
#pragma once
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"
using namespace std;
#define PATH_NAME "/home/ky"
#define PROJ_ID 0x66
#define SHM_SIZE 4096
#define FIFO_NAME "./fifo"
class Init
{
public:
Init()
{
umask(0);
int n = mkfifo(FIFO_NAME, 0666);
assert(n == 0);
(void)n;
Log("create fifo success", Notice) << "\n";
}
~Init()
{
unlink(FIFO_NAME);
Log("remove fifo success", Notice) << "\n";
}
};
#define READ O_RDONLY
#define WRITE O_WRONLY
int OpenFIFO(std::string pathname, int flags)
{
int fd = open(pathname.c_str(), flags);
assert(fd >= 0);
return fd;
}
void Wait(int fd)
{
Log("等待中....", Notice) << "\n";
uint32_t temp = 0;
ssize_t s = read(fd, &temp, sizeof(uint32_t));
assert(s == sizeof(uint32_t));
(void)s;
}
void Signal(int fd)
{
uint32_t temp = 1;
ssize_t s = write(fd, &temp, sizeof(uint32_t));
assert(s == sizeof(uint32_t));
(void)s;
Log("唤醒中....", Notice) << "\n";
}
void CloseFifo(int fd)
{
close(fd);
}
shmClient:
#include "comm.hpp"
#include "Log.hpp"
int main()
{
key_t k = ftok(PATH_NAME, PROJ_ID);
if (k < 0)
{
Log("create key failed", Error) << " client key : " << k << endl;
exit(1);
}
Log("create key done", Debug) << " client key : " << k << endl;
// 获取共享内存
int shmid = shmget(k, SHM_SIZE, 0);
if (shmid < 0)
{
Log("create shm failed", Error) << " client key : " << k << endl;
exit(2);
}
Log("create shm success", Error) << " client key : " << k << endl;
char *shmaddr = (char *)shmat(shmid, nullptr, 0);
if (shmaddr == nullptr)
{
Log("attach shm failed", Error) << " client key : " << k << endl;
exit(3);
}
Log("attach shm success", Error) << " client key : " << k << endl;
//打开管道文件
int fd = OpenFIFO(FIFO_NAME, WRITE);
// 使用
// 看做char类型的buffer
// char a = 'a';
// for (; a < 'z'; a++)
// {
// snprintf(shmaddr, SHM_SIZE - 1, "你好,这是我的pid:%d,inc:%c", getpid(), a);
// sleep(2);
// }
// strcpy(shmaddr,"quit");
while(1)
{
ssize_t s = read(0, shmaddr, SHM_SIZE-1);
if(s > 0)
{
shmaddr[s-1] = 0;
Signal(fd);
if(strcmp(shmaddr,"quit") == 0) break;
}
}
CloseFifo(fd);
// 去关联
int n = shmdt(shmaddr);
assert(n != -1);
Log("detach shm success", Error) << " client key : " << k << endl;
return 0;
}
shmServer:
#include "comm.hpp"
#include "Log.hpp"
Init init;
string TransToHex(key_t &k)
{
char buffer[32];
snprintf(buffer, sizeof buffer, "0x%x", k);
return buffer;
}
int main()
{
// 创建key值
key_t k = ftok(PATH_NAME, PROJ_ID);
assert(k != -1);
Log("create key done", Debug) << "server key:" << TransToHex(k) << endl;
// 创建共享内存
int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);
if (shmid == -1)
{
perror("shmget");
exit(1);
}
Log("create shm done", Debug) << "shmid:" << shmid << endl;
// 将指定的共享内存挂接到自己的地址空间
char *shmaddr = (char *)shmat(shmid, nullptr, 0);
Log("attach shm done", Debug) << " shmid : " << shmid << endl;
//打开管道文件
int fd = OpenFIFO(FIFO_NAME, READ);
// 进行通信
for(;;)
{
//进行等待
Wait(fd);
// 临界区
printf("%s\n", shmaddr);
if(strcmp(shmaddr, "quit") == 0) break;
// sleep(1);
}
// 将共享内存去关联
int n = shmdt(shmaddr);
assert(n != -1);
(void)n;
Log("detach shm done", Debug) << " shmid : " << shmid << endl;
// 删除共享内存
n = shmctl(shmid, IPC_RMID, nullptr);
assert(n != -1);
(void)n;
Log("delete shm done", Debug) << " shmid : " << shmid << endl;
CloseFifo(fd);
return 0;
}
这样我们就利用管道完成了对内存共享的访问控制!!!
我们为了让进程间通信,本质上都是解决同一个问题,那就是让不同的进程看到同一份资源!
但这就会造成一些问题,比如共享内存带来了一些时序问题,造成数据不一致问题!
所以当我们的多个执行流运行互相干扰的时候,主要是我们不加保护的访问了同样的资源(临界资源),所以在非临界区我们的多个执行流是不影响的!!!?
我们的临界资源可能会分有好几个部分,当每一个进程想要进入临界资源的是偶,访问临界资源的一部分,不能让进程直接去使用临界资源,要先去申请信号量!
但是把信号量本质上是计数器但是当作一个计数器是不准确的,今日姑且这样理解,因为两个进程根本没办法看到同一个全局变量,即便看到了,也是不可以的
首先cpu执行指令的时候,会先将内存的数据加遭到cpu内的寄存器中,然后执行指令,将cpu修改完毕的数据写回内存。但是执行流在执行的时候,在任何时刻都可能被切换,寄存器只有一套,被所有的执行流共享,但是寄存器里面的数据属于每一个执行流,是属于该执行流的上下文数据!
那么即便可以看到同一个全局变量,但是因为时序、并发问题,n会有中间状态,会导致不一致,是不安全的。
所以,如果n--的汇编代码只有一行,我们就称之为原子的
所以,所以要想多个进程同时看到以及保证不会被切换,所以这个变量必须要放到内存空间中!也必须是原子的!!
信号计数器
申请信号量->计数器--?->p操作->必须是原子的
释放信号量->计数器++?->v操作->必须是原子
所以信号量是对临界资源的预定机制!!!