linux mmap 記憶體映射 mmap() vs read()/write()/lseek()__linux

來源:互聯網
上載者:User

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


通過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_mmapopen("/tmp/file_mmap", O_RDWR|O_CREAT, 0600) = 3 //open,返回fd=3fstat64(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0 //fstat, 即檔案大小18mmap2(NULL, 18, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0xb7867000 //mmap檔案fd=3close(3)                                = 0 //close檔案fd=3munmap(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.copen("/tmp/file_mmap", O_RDWR|O_CREAT, 0600) = 3 //open, fd=3fstat64(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0 //fstat, 其中檔案大小18brk(0)                                  = 0x9845000  //brk, 返回當前中斷點brk(0x9866000)                          = 0x9866000  //malloc分配記憶體,堆當前最後地址read(3, "www.perfgeeks.com\n", 18)      = 18 //readlseek(3, 0, SEEK_SET)                   = 0 //lseekwrite(3, "WWW.PERFGEEKS.COM\n", 18)     = 18 //writeclose(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_mallocwrite(1, "-----------------------\n", 24-----------------------) = 24brk(0)                                  = 0x85ee000brk(0x860f000)                          = 0x860f000   //malloc(100)mmap2(NULL, 516096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.