旅游网站规划说明,如何找网站建设客户,wordpress 单页模板,网站风格 颜色搭配From: http://www.perfgeeks.com/?p723 通过strace统计系统调用的时候#xff0c;经常可以看到mmap()与mmap2()。系统调用mmap()可以将某文件映射至内存(进程空间)#xff0c;如此可以把对文件的操作转为对内存的操作#xff0c;以此避免更多的lseek()与read()、write()操作…From: http://www.perfgeeks.com/?p723 通过strace统计系统调用的时候经常可以看到mmap()与mmap2()。系统调用mmap()可以将某文件映射至内存(进程空间)如此可以把对文件的操作转为对内存的操作以此避免更多的lseek()与read()、write()操作这点对于大文件或者频繁访问的文件而言尤其受益。但有一点必须清楚mmap的addr与offset必须对齐一个内存页面大小的边界即内存映射往往是页面大小的整数倍否则maaped_file_size%page_size内存空间将被闲置浪费。 演示一下,将文件/tmp/file_mmap中的字符转成大写分别使用mmap与read/write二种方法实现。 /*
* file: t_mmap.c
*/
#include stdio.h
#include ctype.h
#include sys/mman.h /*mmap munmap*/
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.hint main(int argc, char *argv[])
{int fd;char *buf;off_t len;struct stat sb;char *fname /tmp/file_mmap;fd open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);if (fd -1){perror(open);return 1;}if (fstat(fd, sb) -1){perror(fstat);return 1;}buf mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if (buf MAP_FAILED){perror(mmap);return 1;}if (close(fd) -1){perror(close);return 1;}for (len 0; len sb.st_size; len){buf[len] toupper(buf[len]);/*putchar(buf[len]);*/}if (munmap(buf, sb.st_size) -1){perror(munmap);return 1;}return 0;
}
#gcc –o t_mmap t_mmap.c
#strace ./t_mmap
open(/tmp/file_mmap, O_RDWR|O_CREAT, 0600) 3 //open返回fd3
fstat64(3, {st_modeS_IFREG|0644, st_size18, ...}) 0 //fstat, 即文件大小18
mmap2(NULL, 18, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) 0xb7867000 //mmap文件fd3
close(3) 0 //close文件fd3
munmap(0xb7867000, 18) 0 //munmap移除0xb7867000这里的内存映射 虽然没有看到read/write写文件操作但此时文件/tmp/file_mmap中的内容已由www.perfgeeks.com改变成了WWW.PERFGEEKS.COM .这里mmap的addr是0(NULL)offset是18并不是一个内存页的整数倍即有4078bytes4kb-18内存空间被闲置浪费了。 #include stdio.h
#include string.h
#include stdlib.h
#include ctype.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.hint main(int argc, char *argv[])
{int fd, len;char *buf;char *fname /tmp/file_mmap;ssize_t ret;struct stat sb;fd open(fname, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);if (fd -1){perror(open);return 1;}if (fstat(fd, sb) -1){perror(stat);return 1;}buf malloc(sb.st_size);if (buf NULL){perror(malloc);return 1;}ret read(fd, buf, sb.st_size);for (len 0; len sb.st_size; len){buf[len] toupper(buf[len]);/*putchar(buf[len]);*/}lseek(fd, 0, SEEK_SET);ret write(fd, buf, sb.st_size);if (ret -1){perror(error);return 1;}if (close(fd) -1){perror(close);return 1;
}
free(buf);return 0;
}
#gcc –o t_rw t_rw.c
open(/tmp/file_mmap, O_RDWR|O_CREAT, 0600) 3 //open, fd3
fstat64(3, {st_modeS_IFREG|0644, st_size18, ...}) 0 //fstat, 其中文件大小18
brk(0) 0x9845000 //brk, 返回当前中断点
brk(0x9866000) 0x9866000 //malloc分配内存,堆当前最后地址
read(3, www.perfgeeks.com\n, 18) 18 //read
lseek(3, 0, SEEK_SET) 0 //lseek
write(3, WWW.PERFGEEKS.COM\n, 18) 18 //write
close(3) 0 //close 这里通过read()读取文件内容,toupper()后调用write()写回文件。因为文件太小体现不出read()/write()的缺点频繁访问大文件需要多个lseek()来确定位置。每次编辑read()/write()在物理内存中的双份数据。当然不可以忽略创建与维护mmap()数据结构的成本。需要注意并没有具体测试mmap vs read/write即不能一语断言谁孰谁劣具体应用场景具体评测分析。你只是要记住mmap内存映射文件之后操作内存即是操作文件可以省去不少系统内核调用(lseek, read, write)。 mmap() vs malloc() 使用strace调试的时候通常可以看到通过mmap()创建匿名内存映射的身影。比如启用dl(‘apc.so’)的时候就可以看到如下语句。 mmap2(NULL, 31457280, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) 0xb5ce7000 //30M 通常使用mmap()进行匿名内存映射以此来获取内存满足一些特别需求。所谓匿名内存映射是指mmap()的时候,设置了一个特殊的标志MAP_ANONYMOUS且fd可以忽略(-1)。某些操作系统(像FreeBSD)不支持标志MAP_ANONYMOUS可以映射至设备文件/dev/zero来实现匿名内存映射。使用mmap()分配内存的好处是页面已经填满了0而malloc()分配内存后并没有初始化需要通过memset()初始化这块内存。另外malloc()分配内存的时候可能调用brk()也可能调用mmap2()。即分配一块小型内存(小于或等于128kb)malloc()会调用brk()调高断点分配的内存在堆区域当分配一块大型内存(大于128kb),malloc()会调用mmap2()分配一块内存与堆无关在堆之外。同样的free()内存映射方式分配的内存之后内存马上会被系统收回free()堆中的一块内存并不会马上被系统回收glibc会保留它以供下一次malloc()使用。 这里演示一下malloc()使用brk()和mmap2()。 /*
* file:t_malloc.c
*/
#include stdio.h
#include string.h
#include stdlib.hint main(int argc, char *argv)
{char *brk_mm, *mmap_mm;printf(-----------------------\n);brk_mm (char *)malloc(100);memset(brk_mm, \0, 100);mmap_mm (char *)malloc(500 * 1024);memset(mmap_mm, \0, 500*1024);free(brk_mm);free(mmap_mm);printf(-----------------------\n);return 1;
}#gcc –o t_malloc t_malloc.c
#strace ./t_malloc
write(1, -----------------------\n, 24-----------------------) 24
brk(0) 0x85ee000
brk(0x860f000) 0x860f000 //malloc(100)
mmap2(NULL, 516096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) 0xb7702000 //malloc(5kb)
munmap(0xb7702000, 516096) 0 //free(), 5kb
write(1, -----------------------\n, 24-----------------------) 24 通过malloc()分别分配100bytes和5kb的内存可以看出其实分别调用了brk()和mmap2()相应的free()也是不回收内存和通过munmap()系统回收内存。 mmap()共享内存,进程通信 内存映射mmap()的另一个外常见的用法是进程通信。相较于管道、消息队列方式而言这种通过内存映射的方式效率明显更高它不需要任务数据拷贝。这里我们通过一个例子来说明mmap()在进程通信方面的应用。我们编写二个程序分别是master和slaveslave根据master不同指令进行不同的操作。Master与slave就是通过映射同一个普通文件进行通信的。 /**file master.c*/
rootliaowq:/data/tmp# cat master.c
#include stdio.h
#include time.h
#include stdlib.h
#include sys/mman.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.hvoid listen();int main(int argc, char *argv[])
{listen();return 0;
}void listen()
{int fd;char *buf;char *fname /tmp/shm_command;char command;time_t now;fd open(fname, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);if (fd -1){perror(open);exit(1);}buf mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if (buf MAP_FAILED){perror(mmap);exit(1);}if (close(fd) -1){perror(close);exit(1);}*buf 0;sleep(2);for (;;){if (*buf 1 || *buf 3 || *buf 5 || *buf 7){if (*buf 1)printf(%ld\tgood job [%c]\n, (long)time(now), *buf);(*buf);}if (*buf 9){break;}sleep(1);}if (munmap(buf, 4096) -1){perror(munmap);exit(1);}
}/**file slave.c*/
#include stdio.h
#include time.h
#include stdlib.h
#include sys/mman.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.hvoid ready(unsigned int t);
void job_hello();
void job_smile();
void job_bye();
char get_command(char *buf);
void wait();int main(int argc, char *argv[])
{wait();return 0;
}void ready(unsigned int t)
{sleep(t);
}/* command 2 */
void job_hello()
{time_t now;printf(%ld\thello world\n, (long)time(now));
}/* command 4 */
void job_simle()
{time_t now;printf(%ld\t^_^\n, (long)time(now));
}/* command 6 */
void job_bye()
{time_t now;printf(%ld\t|--\n, (long)time(now));
}char get_command(char *buf)
{char *p;if (buf ! NULL){p buf;}else{return 0;}return *p;
}void wait()
{int fd;char *buf;char *fname /tmp/shm_command;char command;time_t now;fd open(fname, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);if (fd -1){perror(open);exit(1);}buf mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if (buf MAP_FAILED){perror(mmap);exit(1);}if (close(fd) -1){perror(close);exit(1);}for (;;){command get_command(buf);/*printf(%c\n, command);*/switch(command){case 0:printf(%ld\tslave is ready...\n, (long)time(now));ready(3);*buf 1;break;case 2:job_hello();*buf 3;break;case 4:job_simle();*buf 5;break;case 6:job_bye();*buf 7;break;default:break;}if (*buf 8){*buf 9;if (munmap(buf, 4096) -1){perror(munmap);exit(1);}return;}sleep(1);}if (munmap(buf, 4096) -1){perror(munmap);exit(1);}
} 执行master与slave输出如下 rootliaowq:/data/tmp# echo “0″ /tmp/shm_command rootliaowq:/data/tmp# ./master 1320939445 good job [3] 1320939446 good job [5] 1320939447 good job [7] rootliaowq:/data/tmp# ./slave 1320939440 slave is ready… 1320939444 hello world 1320939445 ^_^ 1320939446 |-- master向slave发出job指令2,4,6。slave收到指令后执行相关逻辑操作完成后告诉mastermaster知道slave完成工作后打印good job并且发送一下job指令。master与slave通信是通过mmap()共享内存实现的。 总结 1、 Linux采用了投机取巧的分配策略用到时才分配物理内存。也就是说进程调用brk()或mmap()时只是占用了虚拟地址空间并没有真正占用物理内存。这也正是free –m中used并不意味着消耗的全都是物理内存。 2、 mmap()通过指定标志(flag) MAP_ANONYMOUS来表明该映射是匿名内存映射此时可以忽略fd可将它设置为-1。如果不支持MAP_ANONYMOUS标志的类unix系统可以映射至特殊设备文件/dev/zero实现匿名内存映射。 3、 调用mmap()时就决定了映射大小不能再增加。换句话说映射不能改变文件的大小。反过来由文件被映射部分而不是由文件大小来决定进程可访问内存空间范围(映射时指定offset最好是内存页面大小的整数倍)。 4、通常使用mmap()的三种情况.提高I/O效率、匿名内存映射、共享内存进程通信。 相关链接 1.高性能网络编程 2.内存管理内幕 3.C语言指针与内存泄漏 4.read系统调用剖析 5. linux环境进程间通信:共享内存 6. Linux系统编程 unix网络编程2