利用mmap實現(xiàn)的一個文件拷貝例子
/*
?*?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?);
????}
????
/*
?為了完成復制,必須包含讀打開,否則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手冊中的說明,mmap()不能用于擴展文件長度。所以這里必須事
?????*?先擴大目標文件長度,準備一個空架子等待復制。
?????
*/
????
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?);
????}?
????
????
/*
?讀的時候指定?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()好處是處理大文件時速度明顯快于標準文件I/O,無論讀寫,都少了一次用戶空間與內(nèi)核空間之間的復制過程。操作內(nèi)存還便于設計、優(yōu)化算法。
文件I/O操作/proc/self/mem不存在頁邊界對齊的問題,但至少Linux的mmap()的最后一個形參offset并未強制要求頁邊界對齊,如果提供的值未對齊,系統(tǒng)自動向上舍入到頁邊界上。malloc()分配得到的地址不見得對齊在頁邊界上。
???????
/proc/self/mem和/dev/kmem不同。root用戶打開/dev/kmem就可以在用戶空間訪問到內(nèi)核空間的數(shù)據(jù),包括偏移0處的數(shù)
據(jù),系統(tǒng)提供了這樣的支持。顯然代碼段經(jīng)過/proc/self/mem可寫映射后已經(jīng)可寫,無須mprotect()介入。
參考:
Linux環(huán)境進程間通信(五): 共享內(nèi)存(上) 對mmap的介紹很詳細