日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux mmap 内存映射

發布時間:2024/9/5 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux mmap 内存映射 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://www.perfgeeks.com/?p=723


mmap() vs read()/write()/lseek()

通過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.h>int 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,返回fd=3 fstat64(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0 //fstat, 即文件大小18 mmap2(NULL, 18, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0xb7867000 //mmap文件fd=3 close(3) = 0 //close文件fd=3 munmap(0xb7867000, 18) = 0 //munmap,移除0xb7867000這里的內存映射

雖然沒有看到read/write寫文件操作,但此時文件/tmp/file_mmap中的內容已由www.perfgeeks.com改變成了WWW.PERFGEEKS.COM .這里mmap的addr是0(NULL),offset是18,并不是一個內存頁的整數倍,即有4078bytes(4kb-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.h>int 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, fd=3 fstat64(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 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.h>int 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和slave,slave根據master不同指令進行不同的操作。Master與slave就是通過映射同一個普通文件進行通信的。

/**@file master.c*/ root@liaowq:/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.h>void 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.h>void 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,輸出如下
root@liaowq:/data/tmp# echo “0″ > /tmp/shm_command
root@liaowq:/data/tmp# ./master
1320939445 good job [3]
1320939446 good job [5]
1320939447 good job [7]
root@liaowq:/data/tmp# ./slave
1320939440 slave is ready…
1320939444 hello world
1320939445 ^_^
1320939446 |<--

master向slave發出job指令2,4,6。slave收到指令后,執行相關邏輯操作,完成后告訴master,master知道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>>



總結

以上是生活随笔為你收集整理的linux mmap 内存映射的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。