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