mmap()和fmemopen()的使用

本文详细介绍了mmap()和fmemopen()这两个系统调用的功能、参数、错误代码及示例用法。重点讨论了如何利用mmap()将文件映射到内存中进行高效操作,以及fmemopen()如何将内存视为文件进行读写。文章还阐述了mmap()与fmemopen()在实际开发中的应用,包括内存管理、文件共享等方面。
摘要由CSDN通过智能技术生成

转自:http://blog.csdn.net/achelloworld/article/details/41828033

 

先来看看mmap()函数,本函数的头文件为#include <unistd.h>#include <sys/mman.h>。函数原型如下

 

 

   

 

返回值:若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。

错误代码:

  • EBADF  参数fd 不是有效的文件描述词。
  • EACCES  存取权限有误。如果是MAP_PRIVATE 情况下文件必须可读,使用MAP_SHARED 则要有PROT_WRITE 以及该文件要能写入。
  • EINVAL  参数start、length 或offset 有一个不合法。
  • EAGAIN  文件被锁住,或是有太多内存被锁住。
  • ENOMEM  内存不足。

 

mmap()是用来将某个文件内容映射到内存中,对该内存区域的取值即是直接对该文件内容的读写。在内存中建立页表项,但mmap()调用并没有立即在页表中建立页表项,只有使用到某个地址空间时才会给此页面分配空间。由于涉及到页面置换,需要有一定的物理内存做支撑,如果内存太小,那么刚置换入内存中的页面又被置换出到磁盘上,mmap的性能大大降低。

 

mmap()的存在主要是为用户程序随机访问大文件提供了一个方便的操作方法。同时为不同进程共享大批量数据提供高效的手段。对特大文件的处理提供了一种有效的方法。

 

 

示例:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <sys/mman.h>  
  2. #include <sys/types.h>  
  3. #include <sys/stat.h>  
  4. #include <unistd.h>  
  5. #include <fcntl.h>  
  6. #include <stdio.h>  
  7.   
  8. int main(int argc, char **argv)  
  9. {  
  10.     struct stat st;  
  11.   
  12.     //打开文件  
  13.     int fd = -1;  
  14.     if((fd = open("File", O_RDONLY)) == -1)  
  15.     {  
  16.         printf("open File error!\n");  
  17.         return -1;  
  18.     }  
  19.   
  20.     //根据文件描述符取出文件状态  
  21.     if(fstat(fd, &st) == -1)  
  22.     {  
  23.         printf("get st error!\n");  
  24.         return -1;  
  25.     }  
  26.   
  27.     //进行映射  
  28.     void *start = NULL;  
  29.     if((start = mmap(start, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED)  
  30.     {  
  31.         printf("mmap error!\n");  
  32.         return -1;  
  33.     }  
  34.   
  35.     printf("%s\n", (char *)start);  
  36.   
  37.     //解除映射关系  
  38.     if(munmap(start, st.st_size) == -1)  
  39.     {  
  40.         printf("munmap error!\n");  
  41.         return -1;  
  42.     }  
  43.     close(fd);  
  44.   
  45.     return 0;  
  46. }  

 

上面有一个函数为fstat(fd, st),成功返回0,失败返回-1。它是根据文件描述符fd获取文件状态st的,而st是一个结构体,记录了文件的很多状态信息,这个结构体定义如下

 

 

其实还有一个函数的作用跟fstat()差不多,即stat()。不同的是stat()的第一个参数表示的是文件名,而不是文件描述符。

 

mmap()系统调用并不是完全为了共享内存而设计的,它本身提供了一种不同于普通文件的访问方式,进程可以像读取内存那样对普通文件进行操作,当然mmap()是共享内存最主要应用之一。

 

 

还有几个与mmap()系统调用有关的函数如下

 

(1)munmap()函数

 

    函数原型为 : 

 

    该调用在进程地址空间解除一个映射关系,addr是调用mmap()返回的地址,len是映射区的大小,当解除映

    射关系后,对原来映射地址的访问将会导致段错误。此函数解除成功返回0,否则返回-1

 

(2)msync()函数

 

    函数原型为 : 

 

    一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执

    行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。


    addr: 文件映射到进程空间的地址
    len:  映射空间的大小
    flags:刷新的参数设置,可以取值MS_ASYNC/ MS_SYNC/ MS_INVALIDATE

    其中:
   

    取值为MS_ASYNC(异步)时,调用会立即返回,不等到更新的完成;
    取值为MS_SYNC(同步)时,调用会等到更新完成之后返回;
    取MS_INVALIDATE(通知使用该共享区域的进程,数据已经改变)时,在共享内容更改之后,使得文件的其

    他映射失效,从而使得共享该文件的其他进程去重新获取最新值

    此函数成功则返回0,失败则返回-1

 

(3)madvise()函数

 

    在调用mmap()时内核只是建立了逻辑地址到物理地址的映射表,并没有映射任何数据到内存。 在你要访问数

    据时内核会检查数据所在分页是否在内存,如果不在,则发出一次缺页中断,linux默认分页为4K,可以想象

    读一个将近2G的电影文件要发生多少次中断。解决办法如下

 

    将madvise()mmap()搭配起来使用,在使用数据前告诉内核这一段数据我要用,将其一次读入内存,避免

    中断。madvise()这个函数可以对映射的内存提出使用建议,从而提高内存,大大改善性能。

 

    函数原型 : 

 

    参数说明: addr  为mmap()调用获得的映射地址空间首地址。

             len   为映射空间的大小。

             behav 关于这个参数,详情请点这里。

 

    执行成功返回0,否则返回-1。使用方法如下

 

代码:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <strong><span style="font-size:18px;">#include <sys/mman.h>  
  2. #include <sys/types.h>  
  3. #include <sys/stat.h>  
  4. #include <unistd.h>  
  5. #include <fcntl.h>  
  6. #include <stdio.h>  
  7.   
  8. int main(int argc, char **argv)  
  9. {  
  10.     struct stat st;  
  11.   
  12.     //打开文件  
  13.     int fd = -1;  
  14.     if((fd = open("File", O_RDONLY)) == -1)  
  15.     {  
  16.         printf("open File error!\n");  
  17.         return -1;  
  18.     }  
  19.   
  20.     //根据文件描述符取出文件状态  
  21.     if(fstat(fd, &st) == -1)  
  22.     {  
  23.         printf("get st error!\n");  
  24.         return -1;  
  25.     }  
  26.   
  27.     //进行映射  
  28.     void *start = NULL;  
  29.     if((start = mmap(start, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED)  
  30.     {  
  31.         printf("mmap error!\n");  
  32.         return -1;  
  33.     }  
  34.   
  35.     //使用advise()调用  
  36.     if(madvise(start, st.st_size, MADV_WILLNEED | MADV_SEQUENTIAL) == -1)  
  37.     {  
  38.         printf("madvise error!\n");  
  39.         return -1;  
  40.     }  
  41.   
  42.     printf("%s\n", (char *)start);  
  43.   
  44.     //解除映射关系  
  45.     if(munmap(start, st.st_size) == -1)  
  46.     {  
  47.         printf("munmap error!\n");  
  48.         return -1;  
  49.     }  
  50.     close(fd);  
  51.   
  52.     return 0;  
  53. }  
  54. </span></strong>  


 

关于mmap()调用Mayuyu已经介绍了很多了,接下来介绍另一个系统调用,即fmemopen()

 

mmap()调用是将文件映射到内存中,也就是说通过mmap()调用可以把文件当内存使用。而fmemopen()调用正好相反,通过fmemopen()调用可以把内存当文件使用。接下来将会详细介绍fmemopen()调用。

 

 

头文件:#include <stdio.h>

 

函数原型 : 

 

参数说明:这个比较明显,就不说了,后面用实例说明即可。

 

这个应用场合较多,比如有些文件不支持内存操作,但是支持文件操作的。

 

代码:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <string.h>  
  2. #include <stdio.h>  
  3.   
  4. static char buff[] = "Mayuyu is from Japan";  
  5.   
  6. int main(int argc, char **argv)  
  7. {  
  8.     int len = strlen(buff);  
  9.     FILE *fd = fmemopen(buff, len, "r");  
  10.     if(fd == NULL)  
  11.     {  
  12.         printf("get file error!\n");  
  13.         return -1;  
  14.     }  
  15.   
  16.     char ch;  
  17.     while((ch = fgetc(fd)) != EOF)  
  18.         printf("%c", ch);  
  19.     puts("");  
  20.   
  21.     fclose(fd);  
  22.   
  23.     return 0;  
  24. }  
mmap和sendfile是两种在文件处理中常用的技术。 mmap是一种将文件映射到进程的地址空间的方法,它通过建立文件磁盘地址和进程虚拟地址空间的对应关系,实现了文件的访问。在使用mmap时,并没有分配和映射到具体的物理内存空间,而是在第一次加载文件时,通过MMU将虚拟地址转换为物理地址,并将文件加载到物理地址中的内核空间的Page Cache中。这样,进程可以直接通过访问内存的方式来读写文件,而不需要进行繁琐的文件IO操作。\[1\] sendfile是一种在网络编程中常用的方法,它可以将一个文件的内容直接发送到网络连接中,而无需将文件内容先拷贝到用户缓冲区再发送。sendfile利用了操作系统的DMA引擎,将磁盘文件数据直接映射到内核缓冲区,并与用户缓冲区共享同一块映射数据。这样,在建立映射后,就可以直接从内核缓冲区发送数据,减少了一次拷贝操作,提高了性能。总共需要4次切换和3次拷贝。\[3\] 所以,mmap和sendfile都是在文件处理中提高性能的技术,但它们的应用场景和使用方式有所不同。mmap适用于需要频繁读写文件的场景,而sendfile适用于需要将文件内容直接发送到网络连接的场景。 #### 引用[.reference_title] - *1* [Java 两种zero-copy零拷贝技术mmap和sendfile的介绍](https://blog.csdn.net/djdjdxieuej/article/details/128683165)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [零拷贝技术:mmap和sendfile](https://blog.csdn.net/lonelymanontheway/article/details/105888792)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值