mmap使用
利用mmap實現的一個文件拷貝例子
/*
?*?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?才可能真正改變靜態文件? */
????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?);
}?
?*?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?才可能真正改變靜態文件? */
????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,無論讀寫,都少了一次用戶空間與內核空間之間的復制過程。操作內存還便于設計、優化算法。
文件I/O操作/proc/self/mem不存在頁邊界對齊的問題,但至少Linux的mmap()的最后一個形參offset并未強制要求頁邊界對齊,如果提供的值未對齊,系統自動向上舍入到頁邊界上。malloc()分配得到的地址不見得對齊在頁邊界上。
???????
/proc/self/mem和/dev/kmem不同。root用戶打開/dev/kmem就可以在用戶空間訪問到內核空間的數據,包括偏移0處的數 據,系統提供了這樣的支持。顯然代碼段經過/proc/self/mem可寫映射后已經可寫,無須mprotect()介入。
參考:
Linux環境進程間通信(五): 共享內存(上) 對mmap的介紹很詳細