利用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 );
}
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的介绍很详细