今天Mayuyu遇到了两个比较有意思的函数,即mmap()和fmemeopen()函数。
先来看看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()的存在主要是为用户程序随机访问大文件提供了一个方便的操作方法。同时为不同进程共享大批量数据提供高效的手段。对特大文件的处理提供了一种有效的方法。
示例:
#include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <stdio.h> int main(int argc, char **argv) { struct stat st; //打开文件 int fd = -1; if((fd = open("File", O_RDONLY)) == -1) { printf("open File error!\n"); return -1; } //根据文件描述符取出文件状态 if(fstat(fd, &st) == -1) { printf("get st error!\n"); return -1; } //进行映射 void *start = NULL; if((start = mmap(start, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { printf("mmap error!\n"); return -1; } printf("%s\n", (char *)start); //解除映射关系 if(munmap(start, st.st_size) == -1) { printf("munmap error!\n"); return -1; } close(fd); return 0; }
上面有一个函数为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。使用方法如下
代码:
<strong><span style="font-size:18px;">#include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <stdio.h> int main(int argc, char **argv) { struct stat st; //打开文件 int fd = -1; if((fd = open("File", O_RDONLY)) == -1) { printf("open File error!\n"); return -1; } //根据文件描述符取出文件状态 if(fstat(fd, &st) == -1) { printf("get st error!\n"); return -1; } //进行映射 void *start = NULL; if((start = mmap(start, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { printf("mmap error!\n"); return -1; } //使用advise()调用 if(madvise(start, st.st_size, MADV_WILLNEED | MADV_SEQUENTIAL) == -1) { printf("madvise error!\n"); return -1; } printf("%s\n", (char *)start); //解除映射关系 if(munmap(start, st.st_size) == -1) { printf("munmap error!\n"); return -1; } close(fd); return 0; } </span></strong>
关于mmap()调用Mayuyu已经介绍了很多了,接下来介绍另一个系统调用,即fmemopen()。
mmap()调用是将文件映射到内存中,也就是说通过mmap()调用可以把文件当内存使用。而fmemopen()调用正好相反,通过fmemopen()调用可以把内存当文件使用。接下来将会详细介绍fmemopen()调用。
头文件:#include <stdio.h>
函数原型 :
参数说明:这个比较明显,就不说了,后面用实例说明即可。
这个应用场合较多,比如有些文件不支持内存操作,但是支持文件操作的。
代码:
#include <string.h> #include <stdio.h> static char buff[] = "Mayuyu is from Japan"; int main(int argc, char **argv) { int len = strlen(buff); FILE *fd = fmemopen(buff, len, "r"); if(fd == NULL) { printf("get file error!\n"); return -1; } char ch; while((ch = fgetc(fd)) != EOF) printf("%c", ch); puts(""); fclose(fd); return 0; }