小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

mmap內(nèi)存映射操作

 cupid 2015-06-26

概述:

1.對于mmap的內(nèi)存映射,是將物理內(nèi)存映射到進(jìn)程的虛擬地址空間中去,那么進(jìn)程對文件的訪問就相當(dāng)于直接對內(nèi)存的訪問,從而加快了讀寫操作的效率。在這里,remap_pfn_range函數(shù)是一次性的建立頁表,而nopage函數(shù)是根據(jù)page fault產(chǎn)生的進(jìn)程虛擬地址去找到內(nèi)核相對應(yīng)的邏輯地址,再通過這個邏輯地址去找到page。完成映射過程。remap_pfn_range不能對常規(guī)內(nèi)存映射,只能對保留的內(nèi)存與物理內(nèi)存之外的進(jìn)行映射。

2.在這里,要分清幾個地址,一個是物理地址,這個很簡單,就是物理內(nèi)存的實際地址。第二個是內(nèi)核虛擬地址,即內(nèi)核可以直接訪問的地址,如kmalloc,vmalloc等內(nèi)核函數(shù)返回的地址,kmalloc返回的地址也稱為內(nèi)核邏輯地址。內(nèi)核虛擬地址與實際的物理地址只有一個偏移量。第三個是進(jìn)程虛擬地址,這個地址處于用戶空間。而對于mmap函數(shù)映射的是物理地址到進(jìn)程虛擬地址,而不是把物理地址映射到內(nèi)核虛擬地址。而ioremap函數(shù)是將物理地址映射為內(nèi)核虛擬地址。

3.用戶空間的進(jìn)程調(diào)用mmap函數(shù),首先進(jìn)行必要的處理,生成vma結(jié)構(gòu)體,然后調(diào)用remap_pfn_range函數(shù)建立頁表。而用戶空間的mmap函數(shù)返回的是映射到進(jìn)程地址空間的首地址。所以mmap函數(shù)與remap_pfn_range函數(shù)是不同的,前者只是生成mmap,而建立頁表通過remap_pfn_range函數(shù)來完成。

在應(yīng)用層:

#include <sys/mman.h>

prototype : void *mmap(void *start, size_t length, int prot, int flags,  int fd, off_t offset);

                 int munmap(void *start, size_t length);

parameter :

start : 映射區(qū)的開始地址。(一般建議為null,讓內(nèi)核幫我們自動尋找一個合適的地址)

length : 映射區(qū)的長度。

prot:期望的內(nèi)存保護(hù)標(biāo)志,不能與文件的打開模式?jīng)_突。是以下的某個值,可以通過or運算合理的組合在一起:
                         PROT_EXEC //頁內(nèi)容可以被執(zhí)行
                         PROT_READ  //頁內(nèi)容可以被讀取
                         PROT_WRITE //頁可以被寫入
                         PROT_NONE  //頁不可訪問

flags:指定映射對象的類型,映射選項和映射頁是否可以共享。它的值可以是一個或者多

                           個以下位的組合體。

                           MAP_SHARED //與其它所有映射這個對象的進(jìn)程共享映射空間。對共享區(qū)的寫

                                                 入,相當(dāng)于輸出到文件。直到msync()或者munmap()被調(diào)

                                                 用,文件實際上不會被更新。
                           MAP_PRIVATE //建立一個寫入時拷貝的私有映射。內(nèi)存區(qū)域的寫入不會影響到原

                                                 文件。這個標(biāo)志和以上標(biāo)志是互斥的,只能使用其中一個。

                           (還有更多可選參數(shù),具體網(wǎng)上易得)

fd:有效的文件描述詞。

offset:被映射對象內(nèi)容的起點。

return : 返回所映射的虛擬內(nèi)存首地址。

成功執(zhí)行時,mmap()返回被映射區(qū)的指針,munmap()返回0。失敗時,mmap()返回MAP_FAILED[其值為(void *)-1],munmap返回-1。errno被設(shè)為以下的某個值   
EACCES:訪問出錯
EAGAIN:文件已被鎖定,或者太多的內(nèi)存已被鎖定
EBADF:fd不是有效的文件描述詞
EINVAL:一個或者多個參數(shù)無效
ENFILE:已達(dá)到系統(tǒng)對打開文件的限制
ENODEV:指定文件所在的文件系統(tǒng)不支持內(nèi)存映射
ENOMEM:內(nèi)存不足,或者進(jìn)程已超出最大內(nèi)存映射數(shù)量
EPERM:權(quán)能不足,操作不允許
ETXTBSY:已寫的方式打開文件,同時指定MAP_DENYWRITE標(biāo)志
SIGSEGV:試著向只讀區(qū)寫入
SIGBUS:試著訪問不屬于進(jìn)程的內(nèi)存區(qū)

  1. #include <stdio.h>  
  2. #include<sys/types.h>  
  3. #include<sys/stat.h>  
  4. #include<fcntl.h>  
  5. #include<unistd.h>  
  6. #include<sys/mman.h>  
  7.   
  8. int main()  
  9. {  
  10.     int fd;  
  11.     char *start;  
  12.     char buf[100];  
  13.   
  14.     /* open the device */  
  15.     fd = open("testfile",O_RDWR);  
  16.           
  17.     start=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);  
  18.       
  19.     /* read the data */  
  20.     strcpy(buf,start);  
  21.     printf("buf = %s/n",buf);      
  22.   
  23.     /* write data */  
  24.     strcpy(start,"Buf Is Not Null!");  
  25.       
  26.     munmap(start,100); /* unmap */  
  27.     close(fd);    
  28.       
  29.     return 0;      
  30. }  
在我測試時,strcpy總是會跑飛,后在網(wǎng)上找到程序:
  1. //read.c 讀取共享內(nèi)存中的內(nèi)容,讀取了,內(nèi)容也存在,跟普通文件一樣。  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <unistd.h>  
  5. #include <sys/types.h>  
  6. #include <sys/stat.h>  
  7. #include <fcntl.h>  
  8. #include <sys/mman.h>  
  9. #include <string.h>  
  10. #include <errno.h>  
  11.   
  12. #define MAPPED_FILENAME "/tmp/test.mmap.1"  
  13. #define BUFFER_SIZE 1024  
  14.   
  15. int main(int argc, char **argv)  
  16. {  
  17.  int fd;  
  18.    
  19.  if (argc < 2)  
  20.  {  
  21.   fprintf(stdout, "Usage:%s <filename>\n", argv[0]);  
  22.   exit(-1);  
  23.  }  
  24.    
  25.  //step 1, open a file and get a fd  
  26.  //int open(const char *pathname, int flags, mode_t mode);  
  27.  if ((fd = open(argv[1], O_RDWR | O_CREAT, 0644)) <0 )  
  28.  {  
  29.   if (errno == EEXIST)  
  30.   {  
  31.    fprintf(stderr, "Fatal error: The target mapped file existed, exit.\n");  
  32.   }  
  33.   else  
  34.   {  
  35.    fprintf(stderr, "Error: open file failed: (errno = %d) %s\n", errno, strerror(errno));  
  36.   }  
  37.   exit(-2);  
  38.  }  
  39.  off_t offset;  
  40.  offset = 1024;  
  41.  //step2, create a hole file  
  42.  //off_t lseek(int fildes, off_t offset, int whence);  
  43.  if (lseek(fd, offset, SEEK_SET) == (off_t) - 1)  
  44.  {  
  45.   fprintf(stderr, "lseek() failed:%s\n", strerror(errno));  
  46.   //FIXME:unlink the file  
  47.   close(fd);  
  48.   exit(-3);  
  49.  }  
  50.    
  51.  size_t n;  
  52.    
  53.  //size_t write(int fd, const void *buf, size_t count);  
  54.  if ((n = write(fd, "", 1)) < 0)  
  55.  {  
  56.   fprintf(stderr, "write() failed:%s\n", strerror(errno));  
  57.   exit(-4);  
  58.  }  
  59.    
  60.  void *p;  
  61.  //step 3, mmap(), get a pointer  
  62.  //void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);  
  63.  if ((p = mmap(NULL, 1024, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)  
  64.  {  
  65.   fprintf(stderr, "mmap() failed:%s\n", strerror(errno));  
  66.   close(fd);  
  67.   exit(-4);   
  68.  }  
  69.    
  70.  close (fd);  
  71.  fprintf(stdout, "mapped file to memory, size=%d\n", 1024);  
  72.  char buffer[BUFFER_SIZE];  
  73.    
  74.  //step 4, read/write on shared memory  
  75.  //void *memcpy(void *dest, const void *src, size_t n);  
  76.  memcpy(buffer, p+256, 32);    //與read.c不同的地方,反向拷貝  
  77.  fprintf(stdout, "%s\n", buffer);  
  78.    
  79.  //step 5, munmap();  
  80.  //int munmap(void *stat, size_t length);  
  81.  munmap(p, 1024);  
  82.    
  83.    
  84.  //close(fd);  
  85.    
  86.  return 0;  
  87. }  
  88.   
  89.    
  90.   
  91. //write.c 創(chuàng)建共享內(nèi)存,并寫入數(shù)據(jù)。然后退出程序。  
  92. #include <stdio.h>  
  93. #include <stdlib.h>  
  94. #include <unistd.h>  
  95. #include <sys/types.h>  
  96. #include <sys/stat.h>  
  97. #include <fcntl.h>  
  98. #include <sys/mman.h>  
  99. #include <string.h>  
  100. #include <errno.h>  
  101.   
  102. #define MAPPED_FILENAME "/tmp/test.mmap.1"  
  103.   
  104. int main(int argc, char **argv)  
  105. {  
  106.  int fd;  
  107.    
  108.  if (argc < 2)  
  109.  {  
  110.   fprintf(stdout, "Usage:%s <filename>\n", argv[0]);  
  111.   exit(-1);  
  112.  }  
  113.    
  114.  //step 1, open a file and get a fd  
  115.  //int open(const char *pathname, int flags, mode_t mode);  
  116.  if ((fd = open(argv[1], O_RDWR | O_CREAT | O_EXCL, 0644)) <0 )  
  117.  {  
  118.   if (errno == EEXIST)  
  119.   {  
  120.    fprintf(stderr, "Fatal error: The target mapped file existed, exit.\n");  
  121.   }  
  122.   else  
  123.   {  
  124.    fprintf(stderr, "Error: open file failed: (errno = %d) %s\n", errno, strerror(errno));  
  125.   }  
  126.   exit(-2);  
  127.  }  
  128.  off_t offset;  
  129.  offset = 1024;  
  130.  //step2, create a hole file  
  131.  //off_t lseek(int fildes, off_t offset, int whence);  
  132.  if (lseek(fd, offset, SEEK_SET) == (off_t) - 1)  
  133.  {  
  134.   fprintf(stderr, "lseek() failed:%s\n", strerror(errno));  
  135.   //FIXME:unlink the file  
  136.   close(fd);  
  137.   exit(-3);  
  138.  }  
  139.    
  140.  size_t n;  
  141.    
  142.  //size_t write(int fd, const void *buf, size_t count);  
  143.  if ((n = write(fd, "", 1)) < 0)  
  144.  {  
  145.   fprintf(stderr, "write() failed:%s\n", strerror(errno));  
  146.   exit(-4);  
  147.  }  
  148.    
  149.  void *p;  
  150.  //step 3, mmap(), get a pointer  
  151.  //void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);  
  152.  if ((p = mmap(NULL, 1024, PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)  
  153.  {  
  154.   fprintf(stderr, "mmap() failed:%s\n", strerror(errno));  
  155.   close(fd);  
  156.   exit(-4);   
  157.  }  
  158.   
  159.  close (fd);  
  160.  fprintf(stdout, "mapped file to memory, size=%d\n", 1024);  
  161.    
  162.    
  163.  //step 4, read/write on shared memory  
  164.  char *banner = "hello world.";  
  165.    
  166.  //void *memcpy(void *dest, const void *src, size_t n);  
  167.  memset(p, '\0', 1024);  
  168.  memcpy(p+256, banner, strlen(banner));  
  169.    
  170.  //step 5, munmap();  
  171.  //int munmap(void *stat, size_t length);  
  172.    
  173.  return 0;  
  174. }  

雖然取出數(shù)據(jù),但全是零。

表頭文件: #include <string.h>
定義函數(shù): void *memcpy(void *dest, const void *src, size_t n)
函數(shù)說明: memcpy()用來拷貝src所指的內(nèi)存內(nèi)容前n個字節(jié)到dest所指的內(nèi)存地址上。與strcpy()不同的是,memcpy()會完整的復(fù)制n個字節(jié),不會因為遇到字符串結(jié)束'\0'而結(jié)束
返回值:   返回指向dest的指針
附加說明: 指針src和dest所指的內(nèi)存區(qū)域不可重疊。


在內(nèi)核中:

mmap設(shè)備方法是file_operations結(jié)構(gòu)的成員,在Mmap系統(tǒng)調(diào)用發(fā)出時被調(diào)用。在此之前,內(nèi)核已經(jīng)完成了很多工作。mmap設(shè)備方法所需要做的就是建立虛擬地址到物理地址的頁表。

prototype : int (*mmap)(struct file *, struct vm_area_struct *);

parameter: struct file * : 需要操作的文件

                struct vm_area_struct * : 內(nèi)核自動幫我們找到的一個虛擬內(nèi)存區(qū)域。

return : 虛擬內(nèi)存區(qū)域起始地址


Linux內(nèi)核使用結(jié)構(gòu)vm_area_struct 來描述虛擬虛擬內(nèi)存區(qū)域,其中幾個主要成員如下:

unsigned long vm_start : 虛擬內(nèi)存區(qū)域起始地址

unsinged long vm_end  : 虛擬內(nèi)存區(qū)域結(jié)束地址

unsigned long vm_flags : 該區(qū)域的標(biāo)記(能否直接把信息通過虛擬地址存入物理地址等)

 

通過上面的介紹,其實mmap是如何完成頁表的建立呢?

方法有兩種:

1.使用remap_pfn_range一次建立所有頁表

2.使用nopage VMA方法每次建立一個頁表

 

我們這里詳細(xì)介紹方法一。

prototype : int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,

                                              unsigned long pfn, unsigned long size, pgprot_t prot)

parameter: vma : 虛擬內(nèi)存區(qū)域指針

                addr : 虛擬地址的起始值

                pfn  : 要映射的物理地址的頁幀號,即將物理地址右移PAGE_SHIFT(12位),至于為什

                         么是12位,敬請查看《深入理解LINUX內(nèi)核》內(nèi)存管理部分

                size : 要映射的區(qū)域的大小。

                prot : VMA的保護(hù)屬性。

return : 返回虛擬內(nèi)存起始地址。

  1. int memdev_mmap(struct file * filp, struct vm_area_struct * vma)  
  2. {  
  3.     vma -> vm_flags |= VM_IO;  
  4.     vma -> vm_flags |= VM_RESERVED;  //設(shè)置保護(hù)屬性  
  5.   
  6.     if (remap_pfn_range(vma, vma -> vm_start, virt_to_phys(dev -> data) >> PAGE_SHIFT, size, vma -> vm_page_prot))  
  7.     {  
  8.         return -EAGAIN;  
  9.     }  
  10.   
  11.     return 0;  
  12. }  
注意這里的remap_pfn_range函數(shù)里的virt_to_phys(dev -> data),因為例子采用的“設(shè)備”其實就是內(nèi)存,所以需要先轉(zhuǎn)化成物理地址再移位。如果以后我們操作實際的硬件,這里就不用轉(zhuǎn)化了,直接填入物理地址即可。


mmap執(zhí)行的順序
a.在用戶進(jìn)程創(chuàng)建一個vma區(qū)域
b.驅(qū)動程序獲得頁
c.將獲得的頁分配給vma區(qū)域

內(nèi)存映射的步驟:
* 用open系統(tǒng)調(diào)用打開文件, 并返回描述符fd.
* 用mmap建立內(nèi)存映射, 并返回映射首地址指針start.
* 對映射(文件)進(jìn)行各種操作, 顯示(printf), 修改(sprintf).
* 用munmap(void *start, size_t lenght)關(guān)閉內(nèi)存映射.
* 用close系統(tǒng)調(diào)用關(guān)閉文件fd.







    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多