Chinaunix首页 | 论坛 | 博客
  • 博客访问: 272333
  • 博文数量: 21
  • 博客积分: 510
  • 博客等级: 下士
  • 技术积分: 545
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-05 12:32
文章分类
文章存档

2013年(3)

2012年(13)

2011年(5)

分类: LINUX

2012-06-03 13:27:15

不管是哪种操作系统,要实现文件拷贝,必须陷入内核,从磁盘读取文件内容,然后存储到另一个文件。实现文件拷贝最通常的做法是:读取文件用系统调用read()函数,读取到一定长度的连续的用户层缓冲区,然后使用write()函数将缓冲区内容写入文件。也可以用标准库函数fread()和fwrite(),但这两个函数最终还是通过系统调用read()和write()实现拷贝的,因此可以归为一类(不过效率肯定没有直接进行系统调用的高)。一个更高级的做法是使用虚拟存储映射技术进行,这种方法将源文件以共享方式映射到虚拟存储器中,目的文件也以共享方式映射到虚拟地址空间中,然后使用memcpy高效地将源文件内容复制到目的文件中。

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/mman.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <errno.h>
  9. #include <string.h>
  10. #include <sys/times.h>

  11. #define error(fmt,args...) \
  12.     printf(fmt, ##args) ; \
  13.     printf(":%s\n",strerror(errno))

  14. inline int cp_rw(int srcfd,int dstfd,char *buf,int len);
  15. inline int cp_map(int srcfd,int dstfd,size_t len);

  16. int main(int argc,char **argv)
  17. {
  18.     char buf[8192];
  19.     int srcfd,dstfd;
  20.     clock_t start,end;
  21.     struct tms stm,ntm;
  22.     struct stat filestat;
  23.     int tck;
  24.     char cmdline[30];
  25.     
  26.     if(argc!=3)
  27.         printf("usage: cmd ");
  28.     
  29.     tck=sysconf(_SC_CLK_TCK);


  30.     start = times(&stm);
  31.     if((srcfd=open(argv[1],O_RDONLY))==-1)
  32.     {
  33.         error("open %s error",argv[1]);
  34.     exit(0);
  35.     }
  36.     if((dstfd=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666))==-1)
  37.     {
  38.         error("creat %s error",argv[2]);
  39.     exit(0);
  40.     }

  41.     fstat(srcfd,&filestat);
  42.     if(lseek(dstfd,filestat.st_size,SEEK_SET)==-1)
  43.     {
  44.         error("lseek error");
  45.     exit(0);
  46.     }
  47.     if(write(dstfd," ",1)!=1)
  48.     {
  49.         error("write error");
  50.     exit(0);
  51.     }
  52.     cp_map(srcfd,dstfd,filestat.st_size);
  53.     close(srcfd);
  54.     close(dstfd);
  55.     end = times(&ntm);
  56.     printf("copying %s to %s using cp_map:filesize=%lu MBytes Using %f seconds\n"
  57.            ,argv[1],argv[2],filestat.st_size>>20,(end-start)/(double)tck);
  58.     
  59.     sprintf(cmdline,"rm -f %s",argv[2]);
  60.     system(cmdline);
  61.    

  62.     start = times(&stm);
  63.     if((srcfd=open(argv[1],O_RDONLY))==-1)
  64.     {
  65.         error("open %s error",argv[1]);
  66.     }
  67.     if((dstfd=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666))==-1)
  68.     {
  69.         error("creat %s error",argv[2]);
  70.     }
  71.     cp_rw(srcfd,dstfd,buf,sizeof(buf));
  72.     fstat(srcfd,&filestat);
  73.     close(srcfd);
  74.     close(dstfd);
  75.     end = times(&ntm);
  76.     printf("copying %s to %s using cp_rw:filesize=%lu MBytes Using %f seconds\n"
  77.            ,argv[1],argv[2],filestat.st_size>>20,(end-start)/(double)tck);


  78.     
  79.     return 0;
  80. }

  81. inline int cp_rw(int srcfd,int dstfd,char *buf,int len)
  82. {
  83.     int nread;
  84.     while((nread=read(srcfd,buf,len))>0)
  85.     {
  86.         if(write(dstfd,buf,nread)!=nread)
  87.     {
  88.      error("write error");
  89.      return -1;
  90.     }
  91.     }
  92.     if(nread ==-1)
  93.     {
  94.        error("read error");
  95.        return -1;
  96.     }
  97.     return 0;
  98. }

  99. inline int cp_map(int srcfd,int dstfd,size_t len)
  100. {
  101.     char *src,*dst;
  102.     if((src=mmap(0,len,PROT_READ,MAP_SHARED,srcfd,0))==MAP_FAILED)
  103.     {
  104.         error("mmap src error");
  105.     return -1;
  106.     }
  107.     if((dst=mmap(0,len,PROT_WRITE,MAP_SHARED,dstfd,0))==MAP_FAILED)
  108.     {
  109.         error("mmap dst error");
  110.     return -1;
  111.     }
  112.     if(memcpy(dst,src,len)==NULL)
  113.     {
  114.         error("memcpy error");
  115.     return -1;
  116.     }
  117.     munmap(src,len);
  118.     munmap(dst,len);
  119.     return 0;
  120. }
运行,拷贝一个1.1G的文件,得到如下结果
[root@garden copy]# ./copy /home/ker.tgz ./ker.tgz
copying /home/ker.tgz to ./ker.tgz using cp_map:filesize=1030 MBytes Using 61.900000 seconds
copying /home/ker.tgz to ./ker.tgz using cp_rw:filesize=1030 MBytes Using 34.330000 seconds

使用read/write的方法居然比mmap的快一倍,这是怎么回事呢?理论上mmap系统调用只进行了一次,而且拷贝文件是直接在内核空间进行的,read/write则需要通过系统调用把内核空间的缓存复制到用户空间,再将用户空间缓存复制到内核空间,拷贝次数明显多了一个呢?速度为什么于理论预测的不一致呢?

阅读(12409) | 评论(5) | 转发(3) |
给主人留下些什么吧!~~

jimgle2012-06-04 16:10:35

GFree_Wind: 忘了上次在哪看到了。因为mmap涉及到内存管理的问题,导致其效率反而不如read write快。具体的解释记不清了。

另外,sendfile应该更快吧。.....
sendfile()没有包含在POSIX标准中,会影响程序的可移植性,而且sendfile()的实现也是通过mmap()进行的

Aquester2012-06-04 13:16:54

建议以测试为准,mmap可能涉及到了swap。

GFree_Wind2012-06-04 11:59:27

忘了上次在哪看到了。因为mmap涉及到内存管理的问题,导致其效率反而不如read write快。具体的解释记不清了。

另外,sendfile应该更快吧。

jimgle2012-06-04 10:57:17

Aquester: 试试100M的文件,或者试试相对小块小块的mmap复制.....
mmap函数应该对大文件的复制应该更有效率的啊,拷贝了一个110M的文件,mmap大约7.4s,read/write约4s,困惑

Aquester2012-06-04 10:25:10

试试100M的文件,或者试试相对小块小块的mmap复制