將最近網(wǎng)上搜索的資料統(tǒng)一整理下,方便后續(xù)復(fù)查。
一、什么是mmap
mmap是一種內(nèi)存映射文件的方法,即將一個(gè)文件或者其它對(duì)象映射到進(jìn)程的地址空間,實(shí)現(xiàn)文件磁盤地址和進(jìn)程虛擬地址空間中一段虛擬地址的一一對(duì)映關(guān)系,函數(shù)原型如下
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
實(shí)現(xiàn)這樣的映射關(guān)系后,進(jìn)程就可以采用指針的方式讀寫操作這一段內(nèi)存,而系統(tǒng)會(huì)自動(dòng)回寫臟頁(yè)面到對(duì)應(yīng)的文件磁盤上,即完成了對(duì)文件的操作而不必再調(diào)用read,write等系統(tǒng)調(diào)用函數(shù)。如下圖所示
mmap除了可以減少read,write等系統(tǒng)調(diào)用以外,還可以減少內(nèi)存的拷貝次數(shù),比如在read調(diào)用時(shí),一個(gè)完整的流程是操作系統(tǒng)讀磁盤文件到頁(yè)緩存,再?gòu)捻?yè)緩存將數(shù)據(jù)拷貝到read傳遞的buffer里,而如果使用mmap之后,操作系統(tǒng)只需要將磁盤讀到頁(yè)緩存,然后用戶就可以直接通過(guò)指針的方式操作mmap映射的內(nèi)存,減少了從內(nèi)核態(tài)到用戶態(tài)的數(shù)據(jù)拷貝。
mmap適合于對(duì)同一塊區(qū)域頻繁讀寫的情況,比如一個(gè)64M的文件存儲(chǔ)了一些索引信息,我們需要頻繁修改并持久化到磁盤,這樣可以將文件通過(guò)mmap映射到用戶虛擬內(nèi)存,然后通過(guò)指針的方式修改內(nèi)存區(qū)域,由操作系統(tǒng)自動(dòng)將修改的部分刷回磁盤,也可以自己調(diào)用msync手動(dòng)刷磁盤。
內(nèi)核角度分析mmap原理,這篇博客圖文并茂,直接參考就好了。
linux內(nèi)存映射mmap原理分析 - 魚(yú)思故淵的專欄 - CSDN博客 https://blog.csdn.net/yusiguyuan/article/details/23388771
映射只不過(guò)是映射到虛擬內(nèi)存,不用擔(dān)心映射的文件太大。
- 每個(gè)進(jìn)程的4G內(nèi)存空間只是虛擬內(nèi)存空間,每次訪問(wèn)內(nèi)存空間的某個(gè)地址,都需要把地址翻譯為實(shí)際物理內(nèi)存地址
- 所有進(jìn)程共享同一物理內(nèi)存,每個(gè)進(jìn)程只把自己目前需要的虛擬內(nèi)存空間映射并存儲(chǔ)到物理內(nèi)存上。
詳細(xì)虛擬內(nèi)存的內(nèi)容還是參考大神總結(jié)
linux 進(jìn)程的虛擬內(nèi)存 - fengxin的博客 - CSDN博客 https://blog.csdn.net/fengxinlinux/article/details/52071766
二、mmap參數(shù)說(shuō)明
映射文件或設(shè)備到內(nèi)存中,取消映射就是munmap函數(shù)。
語(yǔ)法如下:
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
該函數(shù)主要用途有三個(gè):
- 將普通文件映射到內(nèi)存中,通常在需要對(duì)文件進(jìn)行頻繁讀寫時(shí)使用,用內(nèi)存讀寫取代I/O讀寫,以獲得較高的性能;
- 將特殊文件進(jìn)行匿名內(nèi)存映射,為關(guān)聯(lián)進(jìn)程提供共享內(nèi)存空間;
- 為無(wú)關(guān)聯(lián)的進(jìn)程間的Posix共享內(nèi)存(SystemV的共享內(nèi)存操作是shmget/shmat)
我們來(lái)看下函數(shù)的入?yún)⑦x擇:
? 參數(shù)addr:
指向欲映射的內(nèi)存起始地址,通常設(shè)為 NULL,代表讓系統(tǒng)自動(dòng)選定地址,映射成功后返回該地址。
? 參數(shù)length:
代表將文件中多大的部分映射到內(nèi)存。
? 參數(shù)prot:
映射區(qū)域的保護(hù)方式。可以為以下幾種方式的組合:
PROT_EXEC 映射區(qū)域可被執(zhí)行
PROT_READ 映射區(qū)域可被讀取
PROT_WRITE 映射區(qū)域可被寫入
PROT_NONE 映射區(qū)域不能存取
? 參數(shù)flags:
影響映射區(qū)域的各種特性。在調(diào)用mmap()時(shí)必須要指定MAP_SHARED 或MAP_PRIVATE。
MAP_FIXED 如果參數(shù)start所指的地址無(wú)法成功建立映射時(shí),則放棄映射,不對(duì)地址做修正。通常不鼓勵(lì)用此。
MAP_SHARED對(duì)映射區(qū)域的寫入數(shù)據(jù)會(huì)復(fù)制回文件內(nèi),而且允許其他映射該文件的進(jìn)程共享。
MAP_PRIVATE 對(duì)映射區(qū)域的寫入操作會(huì)產(chǎn)生一個(gè)映射文件的復(fù)制,即私人的“寫入時(shí)復(fù)制”(copy on write)對(duì)此區(qū)域作的任何修改都不會(huì)寫回原來(lái)的文件內(nèi)容。
MAP_ANONYMOUS建立匿名映射。此時(shí)會(huì)忽略參數(shù)fd,不涉及文件,而且映射區(qū)域無(wú)法和其他進(jìn)程共享。
MAP_DENYWRITE只允許對(duì)映射區(qū)域的寫入操作,其他對(duì)文件直接寫入的操作將會(huì)被拒絕。
MAP_LOCKED 將映射區(qū)域鎖定住,這表示該區(qū)域不會(huì)被置換(swap)。
? 參數(shù)fd:
要映射到內(nèi)存中的文件描述符。如果使用匿名內(nèi)存映射時(shí),即flags中設(shè)置了MAP_ANONYMOUS,fd設(shè)為-1。
? 參數(shù)offset:
文件映射的偏移量,通常設(shè)置為0,代表從文件最前方開(kāi)始對(duì)應(yīng),offset必須是分頁(yè)大小的整數(shù)倍。
返回說(shuō)明
成功執(zhí)行時(shí),mmap()返回被映射區(qū)的指針,munmap()返回0。失敗時(shí),mmap()返回MAP_FAILED[其值為(void *)-1],munmap返回-1。errno被設(shè)為以下的某個(gè)值。
EACCES:訪問(wèn)出錯(cuò)
EAGAIN:文件已被鎖定,或者太多的內(nèi)存已被鎖定
EBADF:fd不是有效的文件描述詞
EINVAL:一個(gè)或者多個(gè)參數(shù)無(wú)效
ENFILE:已達(dá)到系統(tǒng)對(duì)打開(kāi)文件的限制
ENODEV:指定文件所在的文件系統(tǒng)不支持內(nèi)存映射
ENOMEM:內(nèi)存不足,或者進(jìn)程已超出最大內(nèi)存映射數(shù)量
EPERM:權(quán)能不足,操作不允許
ETXTBSY:已寫的方式打開(kāi)文件,同時(shí)指定MAP_DENYWRITE標(biāo)志
SIGSEGV:試著向只讀區(qū)寫入
SIGBUS:試著訪問(wèn)不屬于進(jìn)程的內(nèi)存區(qū)
三、mmap與直接IO(read、write)的效率比較
不能簡(jiǎn)單的說(shuō)哪個(gè)效率高,要看具體實(shí)現(xiàn)與具體應(yīng)用。
write read mmap實(shí)際流程如下:
無(wú)論是通過(guò)mmap方式或read/write方式訪問(wèn)文件在內(nèi)核中都必須經(jīng)過(guò)兩個(gè)緩存:一個(gè)是用address_space來(lái)組織的以頁(yè)為基礎(chǔ)的緩存;一個(gè)是以buffer來(lái)組織的緩存,但實(shí)際上這兩個(gè)緩存只是同一個(gè)緩沖池里內(nèi)容的不同組織方式。當(dāng)需要從文件讀寫內(nèi)容時(shí),都經(jīng)過(guò) address_space_operation中提供的函數(shù)也就是說(shuō)路徑是一致的。如果是用read/write方式,用戶須向內(nèi)核指定要讀多少,內(nèi)核再把得到的內(nèi)容從內(nèi)核緩沖池拷向用戶空間;寫也須要有一個(gè)大致如此的過(guò)程。
mmap的優(yōu)勢(shì)在于通過(guò)把文件的某一塊內(nèi)容映射到用戶空間上,用戶可以直接向內(nèi)核緩沖池讀寫這一塊內(nèi)容,這樣一來(lái)就少了內(nèi)核與用戶空間的來(lái)回拷貝所以通常更快。但 mmap方式只適用于更新、讀寫一塊固定大小的文件區(qū)域而不能做像諸如不斷的寫內(nèi)容進(jìn)入文件導(dǎo)到文件增長(zhǎng)這類的事。
二者的主要區(qū)別在于,與mmap和memcpy相比,read和write執(zhí)行了更多的系統(tǒng)調(diào)用,并做了更多的復(fù)制。read和write將數(shù)據(jù)從內(nèi)核緩沖區(qū)中復(fù)制到應(yīng)用緩沖區(qū),然后再把數(shù)據(jù)從應(yīng)用緩沖區(qū)復(fù)制到內(nèi)核緩沖區(qū)。而mmap和memcpy則直接把數(shù)據(jù)從映射到地址空間的一個(gè)內(nèi)核緩沖區(qū)復(fù)制到另一個(gè)內(nèi)核緩沖區(qū)。當(dāng)引用尚不存在的內(nèi)存頁(yè)時(shí),這樣的復(fù)制過(guò)程就會(huì)作為處理頁(yè)錯(cuò)誤的結(jié)果而出現(xiàn)(每次錯(cuò)頁(yè)讀發(fā)生一次錯(cuò)誤,每次錯(cuò)頁(yè)寫發(fā)生一次錯(cuò)誤)。
所以他們兩者的效率的比較就是系統(tǒng)調(diào)用和額外的復(fù)制操作的開(kāi)銷和頁(yè)錯(cuò)誤的開(kāi)銷之間的比較,哪一個(gè)開(kāi)銷少就是哪一個(gè)表現(xiàn)更好。用mmap可以避免與讀寫打交道,這樣可以簡(jiǎn)化程序邏輯,有利于編程實(shí)現(xiàn)。
系統(tǒng)調(diào)用mmap()可以將某文件映射至內(nèi)存(進(jìn)程空間),如此可以把對(duì)文件的操作轉(zhuǎn)為對(duì)內(nèi)存的操作,以此避免更多的lseek()與read()、write()操作,這點(diǎn)對(duì)于大文件或者頻繁訪問(wèn)的文件而言尤其受益。但有一點(diǎn)必須清楚:mmap的addr與offset必須對(duì)齊一個(gè)內(nèi)存頁(yè)面大小的邊界,即內(nèi)存映射往往是頁(yè)面大小的整數(shù)倍,否則maaped_file_size%page_size內(nèi)存空間將被閑置浪費(fèi)。映射時(shí),指定offset最好是內(nèi)存頁(yè)面大小的整數(shù)倍。
內(nèi)存文件映射的使用:
(1)大數(shù)據(jù)量文件的讀取,有效的提高磁盤和內(nèi)存間數(shù)據(jù)通信的性能;
(2)進(jìn)程間快速的共享內(nèi)存,實(shí)現(xiàn)進(jìn)程間高效的通信。
內(nèi)存映射文件性能高于普通IO的原因:
內(nèi)存文件映射和普通的文件IO都是要通過(guò)文件系統(tǒng)和硬盤驅(qū)動(dòng)拷貝數(shù)據(jù)到內(nèi)存中,內(nèi)存文件映射數(shù)據(jù)越大越快主要是:
(1)實(shí)際拷貝數(shù)據(jù)前,需要建立映射信息,內(nèi)存文件映射已經(jīng)提前準(zhǔn)備好了映射關(guān)系,內(nèi)核調(diào)度好了進(jìn)程內(nèi)的內(nèi)存塊,交付給內(nèi)核進(jìn)行了預(yù)先處理,內(nèi)存文件映射會(huì)消耗掉一些時(shí)間。
(2)實(shí)際拷貝時(shí)候,內(nèi)存文件映射將磁盤數(shù)據(jù)直接拷貝到用戶進(jìn)程內(nèi)存空間只進(jìn)行了一次拷貝,而普通的IO是先將文件拷貝到內(nèi)核緩存空間,然后才拷貝到用戶進(jìn)程內(nèi)存空間,進(jìn)行了兩次拷貝。
下面是一個(gè)使用普通的fread函數(shù)和內(nèi)存映射文件函數(shù),讀取不同大小的磁盤文件的性能分析表:
綜合:當(dāng)讀寫磁盤文件的數(shù)據(jù)較小(少于1MB)時(shí)候,使用內(nèi)存文件映射和普通IO是差異很小的,所以建議使用普通IO就可以了;當(dāng)很多文件的大小在幾十MB, 幾百M(fèi)B, 或者1GB以上的文件數(shù)據(jù)需要進(jìn)行較頻繁的訪問(wèn),或者一開(kāi)始需要全部加載這些大文件的時(shí)候,那么就需要考慮使用內(nèi)存文件映射了。
四、網(wǎng)上測(cè)試實(shí)例
(1)demo1
演示一下,將文件/tmp/file_mmap中的字符轉(zhuǎn)成大寫,分別使用mmap與read/write二種方法實(shí)現(xiàn)。
先創(chuàng)建/tmp/file_mmap文件,該文件寫入www.baidu.com,使用strace統(tǒng)計(jì)系統(tǒng)調(diào)用。
/*
* @file: t_mmap.c
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/mman.h> /*mmap munmap*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char *buf;
off_t len;
struct stat sb;
char *fname = "/tmp/file_mmap";
fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1)
{
perror("open");
return 1;
}
if (fstat(fd, &sb) == -1)
{
perror("fstat");
return 1;
}
buf = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED)
{
perror("mmap");
return 1;
}
if (close(fd) == -1)
{
perror("close");
return 1;
}
for (len = 0; len < sb.st_size; ++len)
{
buf[len] = toupper(buf[len]);
/*putchar(buf[len]);*/
}
if (munmap(buf, sb.st_size) == -1)
{
perror("munmap");
return 1;
}
return 0;
}
自己測(cè)試運(yùn)行結(jié)果:
root@chenwr-pc:/home/workspace/test# gcc tmp.c -o run
root@chenwr-pc:/home/workspace/test# strace ./run
execve("./run", ["./run"], [/* 22 vars */]) = 0
brk(0) = 0x1ffa000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106932, ...}) = 0
mmap(NULL, 106932, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcab05de000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P \2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1857312, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcab05dd000
mmap(NULL, 3965632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcab000e000
mprotect(0x7fcab01cc000, 2097152, PROT_NONE) = 0
mmap(0x7fcab03cc000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1be000) = 0x7fcab03cc000
mmap(0x7fcab03d2000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fcab03d2000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcab05db000
arch_prctl(ARCH_SET_FS, 0x7fcab05db740) = 0
mprotect(0x7fcab03cc000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7fcab05f9000, 4096, PROT_READ) = 0
munmap(0x7fcab05de000, 106932) = 0
open("/tmp/file_mmap", O_RDWR|O_CREAT, 0600) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=14, ...}) = 0
mmap(NULL, 14, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7fcab05f8000
close(3) = 0
munmap(0x7fcab05f8000, 14) = 0
exit_group(0) = ?
+++ exited with 0 +++
該文件已經(jīng)變成大寫。
root@chenwr-pc:/tmp# cat file_mmap
WWW.BAIDU.COM
網(wǎng)上該demo的說(shuō)明:
open("/tmp/file_mmap", O_RDWR|O_CREAT, 0600) = 3 //open,返回fd=3
fstat64(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0 //fstat, 即文件大小18
mmap2(NULL, 18, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0xb7867000 //mmap文件fd=3
close(3) = 0 //close文件fd=3
munmap(0xb7867000, 18)= 0 //munmap,移除0xb7867000這里的內(nèi)存映射
這里mmap的addr是0(NULL),offset是18,并不是一個(gè)內(nèi)存頁(yè)的整數(shù)倍,即有4078bytes(4kb-18)內(nèi)存空間被閑置浪費(fèi)了。
(2)demo2
read的方式
#include <stdio.h>
#include <ctype.h>
#include <sys/mman.h> /*mmap munmap*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd, len;
char *buf;
char *fname = "/tmp/file_mmap";
ssize_t ret;
struct stat sb;
fd = open(fname, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if (fd == -1)
{
perror("open");
return 1;
}
if (fstat(fd, &sb) == -1)
{
perror("stat");
return 1;
}
buf = malloc(sb.st_size);
if (buf == NULL)
{
perror("malloc");
return 1;
}
ret = read(fd, buf, sb.st_size);
for (len = 0; len < sb.st_size; ++len)
{
buf[len] = toupper(buf[len]);
/*putchar(buf[len]);*/
}
lseek(fd, 0, SEEK_SET);
ret = write(fd, buf, sb.st_size);
if (ret == -1)
{
perror("error");
return 1;
}
if (close(fd) == -1)
{
perror("close");
return 1;
}
free(buf);
return 0;
}
自己測(cè)試運(yùn)行的結(jié)果:
root@chenwr-pc:/home/workspace/test# strace ./run
execve("./run", ["./run"], [/* 22 vars */]) = 0
brk(0) = 0x13ac000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=106932, ...}) = 0
mmap(NULL, 106932, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb98f1d7000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P \2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1857312, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb98f1d6000
mmap(NULL, 3965632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb98ec07000
mprotect(0x7fb98edc5000, 2097152, PROT_NONE) = 0
mmap(0x7fb98efc5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1be000) = 0x7fb98efc5000
mmap(0x7fb98efcb000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb98efcb000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb98f1d4000
arch_prctl(ARCH_SET_FS, 0x7fb98f1d4740) = 0
mprotect(0x7fb98efc5000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7fb98f1f2000, 4096, PROT_READ) = 0
munmap(0x7fb98f1d7000, 106932) = 0
open("/tmp/file_mmap", O_RDWR|O_CREAT, 0600) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=14, ...}) = 0
brk(0) = 0x13ac000
brk(0x13cd000) = 0x13cd000
read(3, "www.baidu.com\n", 14) = 14
lseek(3, 0, SEEK_SET) = 0
write(3, "WWW.BAIDU.COM\n", 14) = 14
close(3) = 0
exit_group(0) = ?
+++ exited with 0 +++
網(wǎng)上該demo的說(shuō)明:
open("/tmp/file_mmap", O_RDWR|O_CREAT, 0600) = 3 //open, fd=3
fstat64(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0 //fstat, 其中文件大小18
brk(0) = 0x9845000 //brk, 返回當(dāng)前中斷點(diǎn)
brk(0x9866000) = 0x9866000 //malloc分配內(nèi)存,堆當(dāng)前最后地址
read(3, "www.perfgeeks.com\n", 18)= 18 //read
lseek(3, 0, SEEK_SET) = 0 //lseek
write(3, "WWW.PERFGEEKS.COM\n", 18) = 18 //write
close(3) = 0
這里通過(guò)read()讀取文件內(nèi)容,toupper()后,調(diào)用write()寫回文件。因?yàn)槲募。w現(xiàn)不出read()/write()的缺點(diǎn):頻繁訪問(wèn)大文件,需要多個(gè)lseek()來(lái)確定位置。每次編輯read()/write(),在物理內(nèi)存中的雙份數(shù)據(jù)。
當(dāng)然,不可以忽略創(chuàng)建與維護(hù)mmap()數(shù)據(jù)結(jié)構(gòu)的成本。需要注意:并沒(méi)有具體測(cè)試mmap vs read/write,即不能一語(yǔ)斷言誰(shuí)孰誰(shuí)劣,具體應(yīng)用場(chǎng)景具體評(píng)測(cè)分析。
你只是要記?。?strong>mmap內(nèi)存映射文件之后,操作內(nèi)存即是操作文件,可以省去不少系統(tǒng)內(nèi)核調(diào)用(lseek, read, write)。
五、自己寫的demo
#include <stdio.h>
#include <ctype.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#define INT64U unsigned long long
#define MSG_ERR 1
#define MSG_WARN 2
#define MSG_INFO 3
#define MSG_DBG 4
#define MSG_NOR 5
#define MSG_HEAD ("libfat->")
#define PRTMSG(level, fmt, args...)\
do {\
if (level <= MSG_NOR) {\
if (level <= MSG_NOR) {\
printf("%s, %s, line %d: " fmt,__FILE__,__FUNCTION__,__LINE__, ##args);\
} else {\
printf("%s:" fmt, MSG_HEAD, ##args);\
}\
}\
} while(0)
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef unsigned int INT16U;
typedef unsigned long INT32U;
typedef signed char INT8S;
typedef signed int INT16S;
typedef signed long INT32S;
char *filename = "./lt00001";
//char *filename = "/mnt/sdisk/video/lt00004";
char *data = "1111111111\
2222222222\
3333333333\
4444444444";
INT32S data_len = 40;//單次寫入的數(shù)據(jù)長(zhǎng)度
struct timeval t_start, t_end;
struct stat file_info;
long cost_time = 0;
int write_num = 1000;
INT32S mmap_write(INT32S fd, INT64U offset, void *data, INT32S data_len)
{
char *buf = NULL;
if (fstat(fd, &file_info) == -1) {
perror("fstat");
PRTMSG(MSG_ERR, "[cwr] Get file info failed\n");
return -1;
}
buf = mmap(0, file_info.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
perror("mmap");
PRTMSG(MSG_ERR, "[cwr] mmap failed\n");
return -1;
}
//offset = (INT64U)((order)*sizeof(FAT_FILE_LIST_T));
memcpy(buf+offset, data, data_len);
if (munmap(buf, file_info.st_size) == -1) {
perror("munmap");
PRTMSG(MSG_ERR, "[cwr] munmap failed\n");
return -1;
}
return data_len;
}
int write_test()
{
int fd, ret, i, data_size;
INT64U ret64, offset;
int ret_len = 0;
time_t starttime, endtime;
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("[cwr] open file faild\n");
}
gettimeofday(&t_start, NULL);
for (i=0; i<write_num; i++) {
offset = i*data_len;
ret64 = lseek64(fd, offset, SEEK_SET);
if (ret64 == -1LL) {
printf("lseek data fail\n");
return -1;
}
ret_len = write(fd, data, data_len);
if (ret_len != data_len) {
printf("[cwr] count = %d; write error\n", i);
close(fd);
return -1;
}
}
gettimeofday(&t_end, NULL);
printf("[cwr] test end, count = %d\n", i);
close(fd);
return 0;
}
int mmap_write_test()
{
int fd, ret, i, data_size;
INT64U ret64, offset;
int ret_len = 0;
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("[cwr] open file faild\n");
}
gettimeofday(&t_start, NULL);
for (i=0; i<write_num; i++) {
offset = i*data_len;
ret_len = mmap_write(fd, offset, data, data_len);
if (ret_len != data_len) {
printf("[cwr] count = %d; mmap write error\n", i);
close(fd);
return -1;
}
}
gettimeofday(&t_end, NULL);
printf("[cwr] mmap write test end, count = %d\n", i);
close(fd);
return 0;
}
void main()
{
int ret;
memset(&file_info, 0, sizeof(file_info));
#if 1
ret = write_test();
if (ret != 0) {
printf("[cwr] write_test failed\n");
}
#endif
#if 0
ret = mmap_write_test();
if (ret != 0) {
printf("[cwr] mmap_write_test failed\n");
}
#endif
cost_time = t_end.tv_usec - t_start.tv_usec;
printf("Start time: %ld us\n", t_start.tv_usec);
printf("End time: %ld us\n", t_end.tv_usec);
printf("Cost time: %ld us\n", cost_time);
while(1) {
sleep(1);
}
}
運(yùn)行結(jié)果:
write的方式獲取的時(shí)間
使用mmap的方式去操作
后續(xù)不映射整個(gè)文件的空間大小,而是映射要寫入數(shù)據(jù)的長(zhǎng)度40字節(jié)
buf = mmap(0, 40, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset*4*1024)
為何測(cè)試mmap效率并沒(méi)有更高效。
六、參考資料
mmap函數(shù)使用與實(shí)例詳解 - u013525455的博客 - CSDN博客 https://blog.csdn.net/u013525455/article/details/52582839
linux mmap 內(nèi)存映射 mmap() vs read()/write()/lseek()的實(shí)例演示 - Linux操作系統(tǒng):Ubuntu_Centos_Debian - 紅黑聯(lián)盟 https://www.2cto.com/kf/201806/753798.html
Linux文件讀寫機(jī)制及優(yōu)化方式 - Linux就該這么學(xué) - 博客園 https://www.cnblogs.com/linuxprobe/articles/5925397.html
在linux中使用內(nèi)存映射(mmap)操作文件 - 隨意的風(fēng)的專欄 - CSDN博客 https://blog.csdn.net/windgs_yf/article/details/80856458
linux內(nèi)存管理——mmap函數(shù)詳解 - badman250的專欄 - CSDN博客 https://blog.csdn.net/notbaron/article/details/80019134
函數(shù)sync、fsync與fdatasync總結(jié)整理 - pugu12的專欄 - CSDN博客 https://blog.csdn.net/pugu12/article/details/46860143
file - Why (ftruncate+mmap+memcpy) is faster than (write)? - Stack Overflow https://stackoverflow.com/questions/38194948/why-ftruncatemmapmemcpy-is-faster-than-write/38374266
mmap與直接IO(read、write)的效率比較 - 緋淺yousa的筆記 - CSDN博客 https://blog.csdn.net/qq_15437667/article/details/72084829
測(cè)試linux下 fprintf fwrite write mmap 等寫文件的速度 - penzchan的專欄 - CSDN博客 https://blog.csdn.net/penzchan/article/details/18660307
mmap和write性能對(duì)比-bjpiao-ChinaUnix博客 http://blog.chinaunix.net/uid-26575352-id-3761850.html
linux內(nèi)存映射mmap原理分析 - 魚(yú)思故淵的專欄 - CSDN博客 https://blog.csdn.net/yusiguyuan/article/details/23388771
linux 進(jìn)程的虛擬內(nèi)存 - fengxin的博客 - CSDN博客 https://blog.csdn.net/fengxinlinux/article/details/52071766
[原創(chuàng)] 深入剖析mmap-從三個(gè)關(guān)鍵問(wèn)題說(shuō)起 - 簡(jiǎn)書(shū) http://www.itdecent.cn/p/eece39beee20