乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

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

       cupid 2015-06-26

      概述:

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

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

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

      在應用層:

      #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)存保護標志,不能與文件的打開模式?jīng)_突。是以下的某個值,可以通過or運算合理的組合在一起:
                               PROT_EXEC //頁內(nèi)容可以被執(zhí)行
                               PROT_READ  //頁內(nèi)容可以被讀取
                               PROT_WRITE //頁可以被寫入
                               PROT_NONE  //頁不可訪問

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

                                 個以下位的組合體。

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

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

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

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

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

      fd:有效的文件描述詞。

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

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

      成功執(zhí)行時,mmap()返回被映射區(qū)的指針,munmap()返回0。失敗時,mmap()返回MAP_FAILED[其值為(void *)-1],munmap返回-1。errno被設為以下的某個值   
      EACCES:訪問出錯
      EAGAIN:文件已被鎖定,或者太多的內(nèi)存已被鎖定
      EBADF:fd不是有效的文件描述詞
      EINVAL:一個或者多個參數(shù)無效
      ENFILE:已達到系統(tǒng)對打開文件的限制
      ENODEV:指定文件所在的文件系統(tǒng)不支持內(nèi)存映射
      ENOMEM:內(nèi)存不足,或者進程已超出最大內(nèi)存映射數(shù)量
      EPERM:權能不足,操作不允許
      ETXTBSY:已寫的方式打開文件,同時指定MAP_DENYWRITE標志
      SIGSEGV:試著向只讀區(qū)寫入
      SIGBUS:試著訪問不屬于進程的內(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()會完整的復制n個字節(jié),不會因為遇到字符串結束'\0'而結束
      返回值:   返回指向dest的指針
      附加說明: 指針src和dest所指的內(nèi)存區(qū)域不可重疊。


      在內(nèi)核中:

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

      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)核使用結構vm_area_struct 來描述虛擬虛擬內(nèi)存區(qū)域,其中幾個主要成員如下:

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

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

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

       

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

      方法有兩種:

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

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

       

      我們這里詳細介紹方法一。

      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的保護屬性。

      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;  //設置保護屬性  
      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),因為例子采用的“設備”其實就是內(nèi)存,所以需要先轉(zhuǎn)化成物理地址再移位。如果以后我們操作實際的硬件,這里就不用轉(zhuǎn)化了,直接填入物理地址即可。


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

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







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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多