赞
踩
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
共享内存可以通过mmap()映射普通文件
使磁盘文件与内存的一个缓冲区进行映射,进程可以像访问普通内存一样访问磁盘文件,不必再调用write和read
上面图二就是利用了共享内存概念
void *mmap(void *addr, size_ t length, int prot, int flags, int fd, off t offset);
功能:创建共享内存映射
函数返回值:成功返回创建的映射区首地址,失败返回MAP_FAILED 即:((void*)-1),设
置errno值
参数说明:
start:指定要映射的内存地址,一般设置为NULL让操作系统自动选择合适的内存地址。
length:必须>0。映射地址空间的字节数,它从被映射文件开头offset个字节开始算起。
prot:指定共享内存的访问权限。可取如下几个值的可选: PROT_ READ (可读) , PROT _WRITE(可写),PROT_ _EXEC (可执行) , PROT_ NONE (不可访问)。
flags:由以下几个常值指定: MAP_ SHARED (共享的),MAP_ PRIVATE (私有的),MAP_ FIXED(表示必须使用start 参数作为开始地址,如果失败不进行修正),其中,MAP_ SHARED,MAP_ PRIVATE 必选其一,而MAP_ FIXED则不推荐使用。MAPANONYMOUS (匿名映射,用于血缘关系进程间通信)
fd:表示要映射的文件句柄。如果匿名映射写-1。
offset:表示映射文件的偏移量,一般设置为0表示从文件头部开始映射。
注意事项:
(1)创建映射区的过程中, 隐含着一次对映射文件的读操作,将文件内容读取到映射区。
(2) 当MAP_ SHARED时,要求:映射区的权限应<=文件打开的权限(出于对映射区的保
护),如果不满足报非法参数(Invalid argument)错误。
当MAP_ PRIVATE 时候,mmap中的权限是对内存的限制,只需要文件有读权限即可,
操作只在内存有效,不会写到物理磁盘,且不能在进程间共享。
(3) 映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。
(4) 用于映射的文件大小必须>0,当映射文件大小为0时,指定非0大小创建映射区,访
问映射地址会报总线错误,指定0大小创建映射区,报非法参数错误( Invalid argument)
(5) 文件偏移量必须为0或者4K的整数倍(不是会报非法参数Invalid argument错误).
(6) 映射大小可以大于文件大小,但只能访问文件page(4k)的内存地址,否则报总线错误,超出映射的内存大小报段错误图中从左到右:
阴影部分可以写入到磁盘,page2剩余部分只能写入到内存,page3-4总线错误,最后是段错误
- #include<stdio.h>
- #include <sys/mman.h>
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
- int main(int argc, const char *argv[])
- {
- void *addr;
- int fd;
- fd = open("1.txt",O_RDWR);
- if(fd < 0){
- perror("open:");
- return 0;
- }
- //根据定位函数,返回文件大小
- //int len = lseek(fd,0,SEEK_END);
- addr = mmap(NULL,16000,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
- if(addr == MAP_FAILED){
- perror("mmap:");
- }
- //close(fd);
- //映射区创建成功后,其释放与文件关闭无关
-
- //注意:写入的字节大小,必须<=1.txt文件的大小
- //addr指针运算,分成四段,对应上面的图描述
- //0-len,len-4000,4000-16000,>16000
- //memcpy(addr+16000,"abcdefg",7);
-
- memcpy(addr,"abcdefg",7);
- printf("%s\n",(char*)addr);
- return 0;
- }
//注意:写入的字节大小,必须 <= 1.txt文件的字节大小,不然写入的数据不全
举例完成进程间通信
- //写
- #include<stdio.h>
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
- int main(int argc, const char *argv[])
- {
- void *addr;
- int fd;
- fd = open("1.txt",O_RDWR);
- if(fd < 0){
- perror("open:");
- return 0;
- }
- //根据定位函数,返回文件大小
- //int len = lseek(fd,0,SEEK_END);
- addr = mmap(NULL,2048,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
- if(addr == MAP_FAILED){
- perror("mmap:");
- }
- close(fd);
- int i;
- for(i = 0;i<2048;i++){
- memcpy(addr+i,"a",1);
- sleep(1);
- }
- return 0;
- }
-
- //读
- #include<stdio.h>
- #include <sys/mman.h>
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
- int main(int argc, const char *argv[])
- {
- void *addr;
- int fd;
- fd = open("1.txt",O_RDWR);
- if(fd < 0){
- perror("open:");
- return 0;
- }
- //根据定位函数,返回文件大小
- //int len = lseek(fd,0,SEEK_END);
- printf("%s\n",(char*)(addr+5000));
- addr = mmap(NULL,2048,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0);
- if(addr == MAP_FAILED){
- perror("mmap:");
- }
- close(fd);
-
- while(1){
- printf("read=%s\n",(char*)(addr));
- sleep(1);
- }
- return 0;
- }
- #include<stdio.h>
- #include <sys/mman.h>
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
- int main(int argc, const char *argv[])
- {
- void *addr;
- pid_t pid;
- //根据定位函数,返回文件大小
- //int len = lseek(fd,0,SEEK_END);
- addr = mmap(NULL,2048,PROT_WRITE|PROT_READ,MAP_SHARED|MAP_ANONYMOUS,-1,0);
- if(addr == MAP_FAILED){
- perror("mmap:");
- }
- pid = fork();
- if(pid > 0){
- memcpy(addr,"hhhhh",5);
- wait(NULL);
- }else if(pid == 0){
- sleep(1);
- printf("result=%s\n",(char*)addr);
- }else{
- perror("fork:");
- }
- return 0;
- }
共享内存使用步骤
1.生成key
2.创建/打开共享内存
3.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
4.读写共享内存
5.撤销共享内存映射
6.删除共享内存对象
key = ftok("keytest",100);
ftok函数是根据fname和id来创建一个关键字(类型为 key_t),此关键字在创建信号量,创建消息队列的时候都需要使用。
其中fname必须是一个存在的可访问的路径或文件,id必须不得为0
共享内存创建- shmget
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_ t key, int size, int shmflg);
➢成功时返回共享内存的id,失败时返回EOF➢size是共享存储区字节数
➢key和共享内存关联的key, IPC PRIVATE或ftok生成
➢shmflg共享内存标志位IPC CREAT|0666
注意: IPCS指令 可以查看当前的共享内存,消息队列和信号量数组
共享内存映射一shmat
#include <sys/ipc.h>
#include < sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
➢成功时返回映射后的地址, 失败时返回(yoid *)-1
shmid要映射的共享内存id
shmaddr映射后的地址,NULL表示由系统自动映射
➢shmflg 标志位0表示可读写; SHM_ RDONLY表示只读
见代码
共享内存撤销映射- shmdt
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(void *shmaddr);
➢成功时返回0,失败时返回EOF
➢不使用共享内存时应撤销映射
➢进程结束时自动撤销
共享内存删除。shmctl
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid ds *buf);
成功时返回0,失败时返回EOF
shmid要操作的共享内存的id
➢cmd要执行的操作IPC STAT IPC SET IPC RMID
➢buf 保存或设置共享内存属性的地址
代码演示:进程间通信
- //**************写*****************
- #include <sys/types .h>
- #include <sys/ ipc.h>
- #include <stdio.h>
- #include <sys/shm. h>
- #include <string.h>
-
- int main(){
- key_ t key;
- int shmid;
- char *buf;
- //1.获得ipc对象id
- key = ftok("keytest",100);
- if (key<0){
- perror("ftok" );
- return 0 ;
- }
- printf("key=%&x\n",key);
- //2.创建内存,返回共享内存id
- shmid = shmget (key,512, IPC CREAT |0666) ;
- if(shmid<0){
- perror("shmget") ;
- return 0;
- }
- printf ("shmid=%d\n",shmid) ;
- //3.共享内存映射,返回映射后的地址
- buf = shmat( shmid,NULL,0);
- if(buf<0){
- perror("shmat");
- return 0;
- }
- //4.写内存
- strcpy(buf,"hello world");
-
-
- }
- //**************读*****************
- #include <sys/types .h>
- #include <sys/ ipc.h>
- #include <stdio.h>
- #include <sys/shm. h>
- #include <string.h>
-
- int main(){
- key_ t key;
- int shmid;
- char *buf;
- //1.获得ipc对象id
- key = ftok("keytest",100);
- if (key<0){
- perror("ftok" );
- return 0 ;
- }
- printf("key=%&x\n",key);
- //2.获得shmid
- shmid = shmget (key,512,0666) ;
- if(shmid<0){
- perror("shmget") ;
- return 0;
- }
- printf ("shmid=%d\n",shmid) ;
- //3.共享内存映射,返回映射后的地址
- buf = shmat(shmid,NULL,0);
- if(buf<0){
- perror("shmat");
- return 0;
- }
- //4.打印
- printf("share mem = %s\n",buf);
- //5.撤销映射==>多线程时,方便其他线程可以使用共享内存,且删除前必须线程全部撤销完
- //shmdt(buf);
- //6.删除共享内存空间
- //shmctl(shmid,IPC_REID,NULL);
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。