共享内存解读
环境: linux (ubuntu server 64)
- Posix共享内存
- 来源:最初用读写同一个文件的方式实现管道、消息队列的机制,后来加以改进,将文件映射到内存来实现,完成高效的通信机制(这里的文件和共享内存区是“同步的”,即一致的,同时也不会是时时刻刻的一致,内核会完成这里的刷新工作。只是对于原来的读写文件,这里变成了读写内存。而这些通信由内核控制完成)。
- 进阶:使用最初的方式来打开共享内存,但是并不真的需要真的生成一个真实的文件,直接操作内存。同时内核会完成保存和更新与这块共享内存相关的伪文件信息(不包括文件里的内容)
- System V共享内存
直接类似于Posix的进阶版本
3. Bus Error(总线错误)
1. 内存字节对齐(本人也是初次听说,不太支持这个观点)
如int类型的未4字节,参见http://blog.sina.com.cn/s/blog_54f82cc201010zzh.html
本人反驳,是因为:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 5 int main() 6 { 7 char s[100] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 8 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00 }; 9 char *str = s+10; 10 int *pint; 11 for(int i=0;i<4;i++) 12 { 13 pint = (int *) (str+i); 14 printf("%X\n",*pint); 15 } 16 return 0; 17 } 18
运行结果:
2. 进程控制块(PCB)未知的内存访问错误
共享内存等,并不在PCB中有参考的内存访问。于是访问一块共享内存时,进程本身不能对访问的地址进行检查,越界时便触发Bus Error(总线错误)。
3. 其他。//了解得不多了
4. Posix内存映射文件的问题
甚至在一些书上都写着使用内存去映射一个文件时,可以不用去理会文件本身的大小,可以访问超越文件大小的地方。
这样的说法是不全对的。(这一点也确实害了我不浅)
因为使用内存映射就要向操作系统申请内存时,操作系统都是一个Page的大小为单位进行分配的。当文件大小不为整数个Page大小时,便会多得到一些映射的内存(即文件大小按Page单位取上整)。
于是内存映射文件就存在这样一个问题:像通过这样的方式,去申请一块大于被映射文件一个Page大小的内存是不成功的(不一定立即返回错误),但是实际申请到的内存是限制大小的。
这时访问的内存就可能不成功!参见下面代码
1 #include <unistd.h> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <fcntl.h> 8 #include <sys/mman.h> 9 10 #define handle_error(msg) \ 11 do { perror(msg); exit(EXIT_FAILURE); } while (0) 12 int main() 13 { 14 int fd = open("testfile",O_RDWR | O_CREAT, 0666); 15 if(fd == -1) 16 handle_error("testfile open error\n"); 17 18 //lseek(fd, 30, SEEK_SET); 19 ftruncate(fd, 10); 20 char *addr = (char *) mmap(NULL, 4096 + 30, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 21 if(addr == MAP_FAILED) 22 handle_error("mmap failed"); 23 setbuf(stdout, NULL); 24 const char *ss = "ss, Hello World\n"; 25 //write(fd,(void *)ss, strlen(ss)); 26 printf("%s",addr); 27 addr[0] = 'l'; 28 sprintf(addr, "source, Hello World\n"); 29 close(fd); //fd close之后访问addr的合法区任然成功,文件和内存的同步仍然有效 30 addr[0] = 'k'; 31 printf("%s",addr); 32 33 sprintf(addr+4095, "Dangerous Trial"); //注销这两行,程序便执行成功 34 printf("%s\n", addr+4095); 35 return 0; 36 } 37 38 //g++ main.cpp -lrt
总结: 使用Posix共享内存时第一步应该使用ftruncate将被映射的伪文件扩展到一定的大小!