网站备案 拉黑,门户网站欣赏,在线ppt网站,七牛云是干嘛的1.共享内存的原理
要理解共享内存的原理#xff0c;首先我们得记起进程间通信的前提#xff1a;必须让不同的进程看到同一份资源#xff08;必须由OS提供#xff09;
我们都知道进程都会有自己的进程地址空间#xff0c;然后都会通过页表与物理内存进行映射#xff0c;…1.共享内存的原理
要理解共享内存的原理首先我们得记起进程间通信的前提必须让不同的进程看到同一份资源必须由OS提供
我们都知道进程都会有自己的进程地址空间然后都会通过页表与物理内存进行映射如果要进行进程间通信的话那肯定不止一个进程最少也得两个进程所以我们就可以让这两个进程对应的进程地址空间都通过页表映射到同一块物理内存。如果细分步骤的话那就需要进行如下几步
①申请物理内存
②把申请的物理内存的地址填充到页表中然后把在进程地址空间里面对应这块空间的共享区的起始地址返回给上层通过我们前面学的动静态库的理解
此时两个进程都共享同一块物理内存这里就达到了我们进程间通信的前提条件了。然后就可以进程进程间通信了通信后。
③如果我们要释放这块共享内存的话就可以通过清空页表中映射到该处物理内存的内容也就是清除掉进程与这块物理内存之间的映射关系
④直接释放这块物理内存 可是系统里不仅仅只有这两个进程如果有非常多组的进程共享多块不同的共享内存也就是说操作系统里会有多组不同的进程同时使用相同的一块共享内存所以操作系统一定会允许系统同时存在多个共享内存那既然有这么多的共享内存操作系统要不要管理起来呢哪些进程在用哪些共享内存操作系统是不是得知道呢答案当然是要的既然要管理起来那么就需要先描述在组织。所以为了方便操作系统管理我们的共享内存会有一个struct shm这样的数据结构对象来代表这块共享内存这个结构体对象里面会有这块共享内存的属性比如什么时候创建的已经用了多少内存还剩多少内存到底有多少的指针指向这块内存呢等等。如果申请一块共享内存那就创建一个对应的struct shm结构体对象如果释放了就把这个结构体对象删除了所以对共享内存的管理就转变成了对struct shm的增删查改。
理解
1.共享内存也要被操作系统管理
2.如何保证第二个之后的参与通信的进程看到的就是同一个共享内存呢要保证这一点那么就需要要求共享内存必须要有唯一的标识那么如何做到呢怎么给另一个进程呢
2.快速认识系统接口 ①int shmget(key_t key, size_t size, int shmflg); 这里的这个key是什么意思呢我们如何理解呢那我们拿进程来说就是进程创建了共享内存另一个进程怎么会知道呢于是就约定了一个共享数字这个数字是多少并不重要这个数字只需要满足唯一性每一个共享内存对应的唯一的数字即可那么我们就可以规定好是多少规定好之后就相当于是共享内存的唯一标识当我们创建好共享内存的时候我们就可以把这个数字写到对应的共享内存的内核数据结构对象里面然后因为我们规定了所以写代码期间我们通信双方就都知道这个数字所以我们此时创建的时候就可以让系统帮我们去找共享内存对应的那个数字跟我们传进去的key是一样的就可以了比如说key是1234那么就可以保证通信双方看到的是同一块共享内存但是呢随意去创建数字是容易产生冲突的所以不建议去随便用一个数字所以我们建议用这个函数key_t ftok(const char *pathname, int proj_id); 这个函数的作用就是将pathname和一个proj_id转化成一个共享内存的key.这只是一个算法函数并不会对系统做任何操作比如说通过一些散列方式形成特定的key所以将来使用的是同一个pathname,同一个proj_id,再使用同样的一套算法我们就一定能形成同样的key,那么在代码中自然也就能通过形成同样的key找到同一块共享内存。所以为了我们想要判断是不是同一块共享内存我们就需要判断是不是同一个pathname,同一个proj_id,同一套算法.
下面我们来聊聊shmflag这个参数这个参数能够支撑我们既然创建又能获取共享内存shmflag可以传的两个重点选项是IPC_CREAT代表shm不存在就创建存在就获取并返回,IPC_EXCL不单独使用通常和IPC_CREAT一起使用,代表shm不存在就创建存在就出错返回保证创建的共享内存是全新的,很显然是两个宏我们前面学过可以通过位图来传递多个标志位除了传上面的参数还可以传权限通过传权限就能修改我们需要设置的权限。
而创建共享内存之后如果我们要删除共享内存就需要使用命令ipcrm -m shmid 删除指定的共享内存。 共享内存的大小强烈建议设置成为n*4096. ②void *shmat(int shmid, const void *shmaddr, int shmflg); ③int shmdt(const void *shmaddr); ④int shmctl(int shmid, int cmd, struct shmid_ds *buf); 3.直接编写代码
3.1创建key值
comm.hpp代码
#pragma once#includeiostream#includestringconst std::string pathname /home/sunwenchao/mylesson/lesson30;
const int proj_id 0x11223344; server.cc代码
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includecstring
#includecomm.hpp
int main()
{key_t key ftok(pathname.c_str(),proj_id);if(key0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;return 1;}std::coutkey: keystd::endl;return 0;
}
client.cc代码
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includecstring
#includecomm.hpp
int main()
{key_t key ftok(pathname.c_str(),proj_id);if(key0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;return 1;}std::coutkey: keystd::endl;return 0;
} Makefile代码
.PHONY:all
all:server clientclient:client.ccg -o $ $^ -stdc11server:server.ccg -o $ $^ -stdc11.PHONY:clean
clean:rm -f server client .fifo 运行结果 3.2创建共享内存
int shmget(key_t key, size_t size, int shmflg);接口测试
comm.hpp代码
#pragma once#includeiostream
#includecstdlib
#includestringconst std::string pathname /home/sunwenchao/mylesson/lesson30;
const int proj_id 0x11223344;
const int size 4096;key_t GetKey()
{key_t key ftok(pathname.c_str(),proj_id);if(key0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;exit(1);}std::coutkey: keystd::endl;return key;
}
client.cc代码
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includecstring
#includecomm.hpp
int main()
{key_t key GetKey();int shmid shmget(key,size,IPC_CREAT|IPC_EXCL);if(shmid0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;return 1;}std::coutshmid: shmidstd::endl;return 0;
}
server.cc代码
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includecstring
#includecomm.hpp
int main()
{key_t key GetKey();int shmid shmget(key,size,IPC_CREAT|IPC_EXCL);if(shmid0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;return 1;}std::coutshmid: shmidstd::endl;return 0;
}
Makefile代码
.PHONY:all
all:server clientclient:client.ccg -o $ $^ -stdc11server:server.ccg -o $ $^ -stdc11.PHONY:clean
clean:rm -f server client .fifo
运行结果 查看共享内存属性 此时进程已经退出了当我们再次运行时 发现他报错了。说明确实是不存在就创建了如果存在了就会报错返回。而这里的共享内存跟我们之前讲的文件并不一样文件是当进程退出了会直接关闭的但是这里的共享内存想要释放除了重启系统否则是需要我们用代码手动释放的。
所以我们就可以得出一个结论共享内存IPC资源的生命周期是随内核的
这些是标准是规定实现的时候与进程是没有关系的。
3.3进程与共享内存挂接
void *shmat(int shmid, const void *shmaddr, int shmflg);这个接口是为了将shm映射到进程的地址空间当中让该进程与共享内存挂接。
我们用如下代码来进行测试
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includeunistd.h
#includecstring
#includecomm.hpp
int main()
{key_t key GetKey();int shmid shmget(key,size,IPC_CREAT|IPC_EXCL|0644);if(shmid0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;return 1;}sleep(5);std::coutshmid: shmidstd::endl;std::cout开始将shm映射到进程的地址空间中 std::endl;char* s (char*)shmat(shmid,nullptr,0);sleep(5);return 0;
}
测试结果 与此同时我们在另一个SSH渠道用指令while :; do ipcs -m; sleep 1;done来进行检测发现 3.4进程与共享内存断开关联
当在我们的server可执行程序运行过程中从刚开始没有共享内存然后通过shmget创建共享内存然后再通过执行shmat函数让共享内存和当前进程产生关联至此nattch从0变成了1然后等到进程退出的时候nattch又从1变成了0所以我们这里验证了nattch是一个计数器用来对与共享内存产生关联的数量进行计数的。
当然除了进程退出的时候可以让nattch进行--我们也希望可以不让进程退出的时候实现进程与共享内存直接断开关联所以我们就可以使用函数int shmdt(const void *shmaddr);我们可以把shmat函数的返回值也就是共享内存的起始地址作为参数传给该接口也就是用如下代码
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includeunistd.h
#includecstring
#includecomm.hpp
int main()
{key_t key GetKey();sleep(3);int shmid shmget(key,size,IPC_CREAT|IPC_EXCL|0644);if(shmid0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;return 1;}sleep(5);std::coutshmid: shmidstd::endl;std::cout开始将shm映射到进程的地址空间中 std::endl;char* s (char*)shmat(shmid,nullptr,0);sleep(5);std::cout开始将shm从进程的地址空间中移除 std::endl;shmdt(s);sleep(10);return 0;
}
运行结果 我们发现当执行shmdt接口的时候进程还没退出的时候nattch从1变成了0.所以我们这里就实现了将shm从进程的地址空间中移除也就是让进程与共享内存进行断开关联。
3.5从操作系统删除共享内存 那么我们删除如何去删除我们的创建的共享内存呢因为我们创建之后每次如果不通过命令ipcrm -m shmid来删除共享内存的话就会引起 这样重复创建的报错所以我们需要将其用完了共享内存之后进行删除所以我们就需要使用shmctl这个接口来进行删除共享内存
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includeunistd.h
#includecstring
#includecomm.hpp
int main()
{key_t key GetKey();sleep(3);int shmid shmget(key,size,IPC_CREAT|IPC_EXCL|0644);if(shmid0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;return 1;}sleep(5);std::coutshmid: shmidstd::endl;std::cout开始将shm映射到进程的地址空间中 std::endl;char* s (char*)shmat(shmid,nullptr,0);sleep(5);std::cout开始将shm从进程的地址空间中移除 std::endl;shmdt(s);sleep(5);shmctl(shmid,IPC_RMID,nullptr);std::cout开始将shm从OS中删除 std::endl;sleep(10);return 0;
}
运行结果 4.总结4个接口整体完整代码
comm.hpp
#pragma once#includeiostream
#includecstdlib
#includestringconst std::string pathname /home/sunwenchao/mylesson/lesson30;
const int proj_id 0x11223344;
const int size 4096;key_t GetKey()
{key_t key ftok(pathname.c_str(),proj_id);if(key0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;exit(1);}std::coutkey: keystd::endl;return key;
}std::string ToHex(int id)
{char buffer[1024];snprintf(buffer,sizeof(buffer),0x%x,id);return buffer;
}int CreateShmHelper(key_t key,int flag)
{int shmid shmget(key,size,flag);if(shmid0){std::cerrerrno: errno , errstring: strerror(errno)std::endl;exit(2);}return shmid;
}int CreateShm(key_t key)
{return CreateShmHelper(key,IPC_CREAT|IPC_EXCL|0644);
}int GetShm(key_t key)
{return CreateShmHelper(key,IPC_CREAT);}
Makefile
.PHONY:all
all:server clientclient:client.ccg -o $ $^ -stdc11server:server.ccg -o $ $^ -stdc11.PHONY:clean
clean:rm -f server client .fifo
client.cc
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includeunistd.h
#includecstring
#includecomm.hpp
int main()
{key_t key GetKey();int shmid GetShm(key);char *s (char*)shmat(shmid,nullptr,0);char c a;for(;cz;c){s[c-a] c;std::coutwrite: cdonestd::endl;sleep(6);}shmdt(s);std::coutdetach shm donestd::endl;return 0;
}
server.cc
#includeiostream
#includesys/ipc.h
#includesys/shm.h
#includeunistd.h
#includecstring
#includecomm.hpp
int main()
{key_t key GetKey();std::coutkey : ToHex(key)std::endl;// sleep(3);int shmid CreateShm(key);// sleep(5);std::coutshmid: shmidstd::endl;std::cout开始将shm映射到进程的地址空间中 std::endl;char* s (char*)shmat(shmid,nullptr,0);// sleep(5);while(true){//直接读取std::cout共享内存的内容sstd::endl;sleep(1);}std::cout开始将shm从进程的地址空间中移除 std::endl;shmdt(s);// sleep(5);shmctl(shmid,IPC_RMID,nullptr);std::cout开始将shm从OS中删除 std::endl;// sleep(10);return 0;
}
运行结果 可是我们发现一个进程写一次之后另一个进行会读很多次也就是没有看到有同步机制。但是如果我们想要让其有同步机制我们也可以通过管道来进行实现同步机制的。 5.总结共享内存的特点
1.共享内存的通信方式不会提供同步机制共享内存是直接裸露给所有的使用者的一定要注意共享内存的使用安全问题
2.共享内存是所有进程间通信速度最快的
3.共享内存可以提供较大的空间