mmap详谈
簡(jiǎn)述:
mmap函數(shù)將文件系統(tǒng)內(nèi)的文件或者是Posix共享內(nèi)存對(duì)象映射到調(diào)用進(jìn)程的地址空間。
用途:
1.對(duì)普通文件使用mmap提供內(nèi)存映射I/O,以避免系統(tǒng)調(diào)用(read、write、lseek)帶來(lái)的性能開(kāi)銷(xiāo)。同時(shí)減少了數(shù)據(jù)在內(nèi)核緩沖區(qū)和進(jìn)程地址空間的拷貝次數(shù)。
2.使用特殊文件提供匿名內(nèi)存映射。
3.使用shm_open以提供無(wú)親緣關(guān)系進(jìn)程間的Posix共享內(nèi)存區(qū)。
接口說(shuō)明:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
mmap返回成功后,fd文件描述符可以關(guān)閉且不會(huì)對(duì)內(nèi)存映射產(chǎn)生影響。
對(duì)內(nèi)存區(qū)的保護(hù)由prot參數(shù)指定:
PROT_EXEC:數(shù)據(jù)可執(zhí)行
PROT_READ:數(shù)據(jù)可讀
PROT_WRITE:數(shù)據(jù)可寫(xiě)
PROT_NONE:數(shù)據(jù)不可訪(fǎng)問(wèn)
對(duì)映射內(nèi)存區(qū)的操作標(biāo)志由flags指定:
MAP_SHARED:對(duì)內(nèi)存區(qū)數(shù)據(jù)操作的變動(dòng)是共享的。即所有映射到該內(nèi)存地址的進(jìn)程都能收到該數(shù)據(jù)的變動(dòng)。
MAP_PRIVATE:對(duì)內(nèi)存區(qū)數(shù)據(jù)的操作進(jìn)行寫(xiě)時(shí)復(fù)制。(注:僅僅在內(nèi)存中有多一份拷貝,文件并不受到影響)
mmap函數(shù)通過(guò)文件描述符定位到文件位置再到用戶(hù)地址空間:
int munmap(void *addr, size_t len);
從某個(gè)進(jìn)程的地址空間刪除一個(gè)內(nèi)存映射關(guān)系。len是映射區(qū)的大小。
如果被映射區(qū)是使用MAP_PRIVATE標(biāo)志映射的,那么調(diào)用進(jìn)程對(duì)它的所有變動(dòng)都不會(huì)寫(xiě)回文件,并且被丟棄。
細(xì)節(jié):
1.考慮不同的進(jìn)程調(diào)用mmap內(nèi)存映射同一個(gè)文件(前提設(shè)置了MAP_SHARED),那么某個(gè)進(jìn)程對(duì)這個(gè)內(nèi)存映射的修改是否會(huì)影響其他進(jìn)程的內(nèi)存映射。
//orgin.c用于打印內(nèi)存映射區(qū)內(nèi)的值
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
int main()
{
int fd = open("./testfile", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
int num = 0;
write(fd, &num, sizeof(int));
int *ptr = mmap(NULL, sizeof(int), PROT_WRITE, MAP_PRIVATE, fd, 0);
sleep(10);
printf("%d
", *ptr);
}
//mod.c用于修改內(nèi)存映射區(qū)內(nèi)的數(shù)據(jù)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
int main()
{
int fd = open("./testfile", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
int num = 0;
write(fd, &num, sizeof(int));
int *ptr = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
printf("%d
", ++(*ptr));
}
結(jié)論:
當(dāng)不同的進(jìn)程映射同一個(gè)文件的時(shí)候,內(nèi)存映射區(qū)其實(shí)變?yōu)閮?nèi)核為各個(gè)進(jìn)程維護(hù)的共享內(nèi)存區(qū)。各個(gè)進(jìn)程對(duì)內(nèi)存映射區(qū)的修改都會(huì)寫(xiě)回文件,并影響其他進(jìn)程在該共享內(nèi)存上的值。
2.內(nèi)存分配策略以頁(yè)為單位(假設(shè)一頁(yè)為4096個(gè)字節(jié))。考慮內(nèi)存映射空間和文件大小都為5000個(gè)字節(jié)的時(shí)候,文件映射的讀寫(xiě)會(huì)如何。
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
int main()
{
int fileSize = 5000;
int mapSize = 5000;
long pageSize;
int fd = open("./testfile", O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IRUSR);
lseek(fd, fileSize-1, SEEK_SET);
write(fd, "", 1);
char *ptr = mmap(NULL, mapSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
pageSize = sysconf(_SC_PAGESIZE);
int i;
for (i = 0; i < 5000; i+=pageSize)
{
//打印每個(gè)頁(yè)的首字節(jié)內(nèi)容,并置為1
printf("ptr[%d] = %d
", i, ptr[i]);
ptr[i] = 1;
//打印每個(gè)頁(yè)的尾字節(jié)內(nèi)容,并置為1
printf("ptr[%d] = %d
", i + pageSize - 1, ptr[i+pageSize-1]);
ptr[i+pageSize-1] = 1;
}
return 0;
}
輸出結(jié)果:
ptr[0] = 0 ptr[4095] = 0 ptr[4096] = 0 ptr[8191] = 0
使用命令:od -b(以八進(jìn)制形式查看內(nèi)容) -A b(以十進(jìn)制輸出地址) testfile 查看地址對(duì)應(yīng)的內(nèi)容。
結(jié)果:
內(nèi)存映射其實(shí)分配了2個(gè)頁(yè)的大小(第一個(gè)頁(yè)0~4095字節(jié),第二個(gè)頁(yè)4096~8191字節(jié)),對(duì)于在這兩個(gè)頁(yè)范圍內(nèi)的讀和寫(xiě)操作都不會(huì)導(dǎo)致“Segmentation Fault”,但對(duì)在5000~8191字節(jié)范圍內(nèi)的寫(xiě)操作不會(huì)寫(xiě)回文件中(因?yàn)槲募笮?000字節(jié))。對(duì)于超過(guò)8191字節(jié)(即第三個(gè)頁(yè))的讀寫(xiě)將導(dǎo)致段錯(cuò)誤。
結(jié)論:
在內(nèi)存分配的頁(yè)范圍內(nèi)的讀寫(xiě)不會(huì)導(dǎo)致段錯(cuò)誤,在文件大小范圍內(nèi)的寫(xiě)操作才會(huì)寫(xiě)回文件。
3.進(jìn)程采用mmap操作文件內(nèi)容一定比readwrite快嗎?
討論這個(gè)問(wèn)題前先看進(jìn)程采用系統(tǒng)條用read/write方式讀寫(xiě)文件過(guò)程。
數(shù)據(jù)先從文件系統(tǒng)復(fù)制到內(nèi)核緩沖區(qū)(最小單位為一個(gè)頁(yè)大小)---->復(fù)制數(shù)據(jù)到用戶(hù)的進(jìn)程空間;此過(guò)程中如果進(jìn)程的下一次讀操作的數(shù)據(jù)依然在內(nèi)核緩沖區(qū)的話(huà)內(nèi)核不會(huì)再?gòu)奈募到y(tǒng)中讀取,而是將數(shù)據(jù)從內(nèi)核緩沖區(qū)直接復(fù)制給進(jìn)程。
后續(xù)對(duì)文件的操作還需要其他的系統(tǒng)調(diào)用如lseekwrite。進(jìn)程將多次在內(nèi)核態(tài)和用戶(hù)態(tài)之間切換。而mmap則只需一次系統(tǒng)調(diào)用,其后操作都在用戶(hù)態(tài)上。
由此可知:
對(duì)于小文件的讀寫(xiě)操作,內(nèi)核完全有可能直接從內(nèi)核緩沖區(qū)讀寫(xiě)數(shù)據(jù)并不見(jiàn)得會(huì)比mmap慢,但是對(duì)于大文件且頻繁讀寫(xiě)的操作,mmap會(huì)比readwrite要快。
完。
you knon i am not leaving you.right?
總結(jié)
- 上一篇: 安装国际版firefox(火狐浏览器)并
- 下一篇: AIMLBot (中文自动回复)文本自动