站長資訊網
        最全最豐富的資訊網站

        linux mmap 內存映射 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   #include   #include  /*mmap munmap*/  #include   #include   #include   #include     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   #include   #include   #include   #include   #include   #include   #include     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.comn", 18)= 18 //read  lseek(3, 0, SEEK_SET) = 0 //lseek  write(3, "WWW.PERFGEEKS.COMn", 18)  = 18 //write  close(3)  = 0 //close

        這里通過read()讀取文件內容,toupper()后,調用write()寫回文件。因為文件太小,體現不出read()/write()的缺點:頻繁訪問大文件,需要多個lseek()來確定位置。每次編輯read()/write(),在物理內存中的雙份數據。當然,不可以忽略創建與維護mmap()數據結構的成本。需要注意:并沒有具體測試mmap vs read/write,即不能一語斷言誰孰誰劣,具體應用場景具體評測分析。你只是要記?。簃map內存映射文件之后,操作內存即是操作文件,可以省去不少系統內核調用(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   #include   #include     int main(int argc, char *argv)  {   char *brk_mm, *mmap_mm;     printf("-----------------------n");   brk_mm = (char *)malloc(100);   memset(brk_mm, '', 100);   mmap_mm = (char *)malloc(500 * 1024);   memset(mmap_mm, '', 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   #include   #include   #include   #include   #include   #include   #include     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("%ldtgood 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   #include   #include   #include   #include   #include   #include   #include     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("%ldthello worldn", (long)time(&now));  }    /* command 4 */  void job_simle()  {   time_t now;   printf("%ldt^_^n", (long)time(&now));  }    /* command 6 */  void job_bye()  {   time_t now;   printf("%ldt|<--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("%cn", command);*/    switch(command)    {  case '0':   printf("%ldtslave 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>></unix網絡編程2></linux系統編程>

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 合区精品久久久中文字幕一区| 国产午夜精品一区二区三区漫画| 欧美精品中文字幕亚洲专区| 国产精品v欧美精品v日韩| 久久99热只有频精品8| 无码人妻精品一区二区三区99不卡| 97精品伊人久久久大香线蕉| 精品国产一区二区三区久久久狼| 天天爽夜夜爽8888视频精品| 国产精品玖玖美女张开腿让男人桶爽免费看 | 日本免费精品一区二区三区| 999精品在线| 亚洲国产精品一区二区久久hs| 精品久久久久久久久久久久久久久| 精品亚洲综合在线第一区| 国产精品拍天天在线| 亚洲国产另类久久久精品| 亚洲精品无码久久久| 久久精品国产亚洲7777| 国产精品手机在线观看你懂的| 99久久国产热无码精品免费久久久久 | 久久国产精品成人片免费| 最新在线精品国自av| 无码精品蜜桃一区二区三区WW| 久久精品国产一区二区| 黑人无码精品又粗又大又长| 国产成人无码精品久久久久免费 | 亚洲精品无码av人在线观看 | 久久91精品久久91综合| 精品999在线| 九九在线精品视频专区| 奇米精品一区二区三区在线观看| 精品久久久久久久| 热99re久久国超精品首页| 亚洲欧美日韩精品永久在线| 亚洲精品欧美日韩| 99精品欧美一区二区三区| 国产高清在线精品一本大道国产| 国产精品理论片在线观看| 国产综合精品久久亚洲| 国产亚洲精品精品国产亚洲综合|