利用mmap實(shí)現(xiàn)的一個(gè)文件拷貝例子
/*
?*?gcc?-Wall?-O3?-o?copy_mmap?copy_mmap.c
?
*/
#include?
<
stdio.h
>
#include?
<
stdlib.h
>
#include?
<
string
.h
>
??
/*
?for?memcpy?
*/
#include?
<
strings.h
>
#include?
<
sys
/
mman.h
>
#include?
<
sys
/
types.h
>
#include?
<
sys
/
stat.h
>
#include?
<
fcntl.h
>
#include?
<
unistd.h
>
#define
?PERMS?0600
int
?main?(?
int
?argc,?
char
?
*
?argv[]?)
{
????
int
??????????src,?dst;
????
void
????????
*
sm,?
*
dm;?
????
struct
?stat??statbuf;
????
if
?(?argc?
!=
?
3
?)
????{
????????fprintf(?stderr,?
"
?Usage:?%s?<source>?<target>\n
"
,?argv[
0
]?);
????????exit(?EXIT_FAILURE?);
????}
????
if
?(?(?src?
=
?open(?argv[
1
],?O_RDONLY?)?)?
<
?
0
?)
????{
????????perror(?
"
open?source
"
?);
????????exit(?EXIT_FAILURE?);
????}
????
/*
?為了完成復(fù)制,必須包含讀打開(kāi),否則mmap()失敗?
*/
????
if
?(?(?dst?
=
?open(?argv[
2
],?O_RDWR?
|
?O_CREAT?
|
?O_TRUNC,?PERMS?)?)?
<
?
0
?)
????{
????????perror(?
"
open?target
"
?);
????????exit(?EXIT_FAILURE?);
????}
????
if
?(?fstat(?src,?
&
statbuf?)?
<
?
0
?)
????{
????????perror(?
"
fstat?source
"
?);
????????exit(?EXIT_FAILURE?);
????}
????
/*
?????*?參看前面man手冊(cè)中的說(shuō)明,mmap()不能用于擴(kuò)展文件長(zhǎng)度。所以這里必須事
?????*?先擴(kuò)大目標(biāo)文件長(zhǎng)度,準(zhǔn)備一個(gè)空架子等待復(fù)制。
?????
*/
????
if
?(?lseek(?dst,?statbuf.st_size?
-
?
1
,?SEEK_SET?)?
<
?
0
?)
????{
????????perror(?
"
lseek?target
"
?);
????????exit(?EXIT_FAILURE?);?
????}?
????
if
?(?write(?dst,?
&
statbuf,?
1
?)?
!=
?
1
?)
????{
????????perror(?
"
write?target
"
?);
????????exit(?EXIT_FAILURE?);
????}?
????
????
/*
?讀的時(shí)候指定?MAP_PRIVATE?即可?
*/
????sm?
=
?mmap(?
0
,?(?size_t?)statbuf.st_size,?PROT_READ,
???????????????MAP_PRIVATE?
|
?MAP_NORESERVE,?src,?
0
?);
????
if
?(?MAP_FAILED?
==
?sm?)
????{
????????perror(?
"
mmap?source
"
?);
????????exit(?EXIT_FAILURE?);
????}
????
/*
?這里必須指定?MAP_SHARED?才可能真正改變靜態(tài)文件?
*/
????dm?
=
?mmap(?
0
,?(?size_t?)statbuf.st_size,?PROT_WRITE,
???????????????MAP_SHARED,?dst,?
0
?);
????
if
?(?MAP_FAILED?
==
?dm?)
????{
????????perror(?
"
mmap?target
"
?);
????????exit(?EXIT_FAILURE?);
????}
????memcpy(?dm,?sm,?(?size_t?)statbuf.st_size?);
????
/*
?????*?可以不要這行代碼
?????*
?????*?msync(?dm,?(?size_t?)statbuf.st_size,?MS_SYNC?);
?????
*/
????
return
(?EXIT_SUCCESS?);
}?
mmap()好處是處理大文件時(shí)速度明顯快于標(biāo)準(zhǔn)文件I/O,無(wú)論讀寫(xiě),都少了一次用戶空間與內(nèi)核空間之間的復(fù)制過(guò)程。操作內(nèi)存還便于設(shè)計(jì)、優(yōu)化算法。
文件I/O操作/proc/self/mem不存在頁(yè)邊界對(duì)齊的問(wèn)題,但至少Linux的mmap()的最后一個(gè)形參offset并未強(qiáng)制要求頁(yè)邊界對(duì)齊,如果提供的值未對(duì)齊,系統(tǒng)自動(dòng)向上舍入到頁(yè)邊界上。malloc()分配得到的地址不見(jiàn)得對(duì)齊在頁(yè)邊界上。
???????
/proc/self/mem和/dev/kmem不同。root用戶打開(kāi)/dev/kmem就可以在用戶空間訪問(wèn)到內(nèi)核空間的數(shù)據(jù),包括偏移0處的數(shù)
據(jù),系統(tǒng)提供了這樣的支持。顯然代碼段經(jīng)過(guò)/proc/self/mem可寫(xiě)映射后已經(jīng)可寫(xiě),無(wú)須mprotect()介入。
參考:
Linux環(huán)境進(jìn)程間通信(五): 共享內(nèi)存(上) 對(duì)mmap的介紹很詳細(xì)