不管是哪种操作系统,要实现文件拷贝,必须陷入内核,从磁盘读取文件内容,然后存储到另一个文件。实现文件拷贝最通常的做法是:读取文件用系统调用read()函数,读取到一定长度的连续的用户层缓冲区,然后使用write()函数将缓冲区内容写入文件。也可以用标准库函数fread()和fwrite(),但这两个函数最终还是通过系统调用read()和write()实现拷贝的,因此可以归为一类(不过效率肯定没有直接进行系统调用的高)。一个更高级的做法是使用虚拟存储映射技术进行,这种方法将源文件以共享方式映射到虚拟存储器中,目的文件也以共享方式映射到虚拟地址空间中,然后使用memcpy高效地将源文件内容复制到目的文件中。- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/mman.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <string.h>
- #include <sys/times.h>
- #define error(fmt,args...) \
- printf(fmt, ##args) ; \
- printf(":%s\n",strerror(errno))
- inline int cp_rw(int srcfd,int dstfd,char *buf,int len);
- inline int cp_map(int srcfd,int dstfd,size_t len);
- int main(int argc,char **argv)
- {
- char buf[8192];
- int srcfd,dstfd;
- clock_t start,end;
- struct tms stm,ntm;
- struct stat filestat;
- int tck;
- char cmdline[30];
-
- if(argc!=3)
- printf("usage: cmd ");
-
- tck=sysconf(_SC_CLK_TCK);
- start = times(&stm);
- if((srcfd=open(argv[1],O_RDONLY))==-1)
- {
- error("open %s error",argv[1]);
- exit(0);
- }
- if((dstfd=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666))==-1)
- {
- error("creat %s error",argv[2]);
- exit(0);
- }
- fstat(srcfd,&filestat);
- if(lseek(dstfd,filestat.st_size,SEEK_SET)==-1)
- {
- error("lseek error");
- exit(0);
- }
- if(write(dstfd," ",1)!=1)
- {
- error("write error");
- exit(0);
- }
- cp_map(srcfd,dstfd,filestat.st_size);
- close(srcfd);
- close(dstfd);
- end = times(&ntm);
- printf("copying %s to %s using cp_map:filesize=%lu MBytes Using %f seconds\n"
- ,argv[1],argv[2],filestat.st_size>>20,(end-start)/(double)tck);
-
- sprintf(cmdline,"rm -f %s",argv[2]);
- system(cmdline);
-
- start = times(&stm);
- if((srcfd=open(argv[1],O_RDONLY))==-1)
- {
- error("open %s error",argv[1]);
- }
- if((dstfd=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666))==-1)
- {
- error("creat %s error",argv[2]);
- }
- cp_rw(srcfd,dstfd,buf,sizeof(buf));
- fstat(srcfd,&filestat);
- close(srcfd);
- close(dstfd);
- end = times(&ntm);
- printf("copying %s to %s using cp_rw:filesize=%lu MBytes Using %f seconds\n"
- ,argv[1],argv[2],filestat.st_size>>20,(end-start)/(double)tck);
-
- return 0;
- }
- inline int cp_rw(int srcfd,int dstfd,char *buf,int len)
- {
- int nread;
- while((nread=read(srcfd,buf,len))>0)
- {
- if(write(dstfd,buf,nread)!=nread)
- {
- error("write error");
- return -1;
- }
- }
- if(nread ==-1)
- {
- error("read error");
- return -1;
- }
- return 0;
- }
- inline int cp_map(int srcfd,int dstfd,size_t len)
- {
- char *src,*dst;
- if((src=mmap(0,len,PROT_READ,MAP_SHARED,srcfd,0))==MAP_FAILED)
- {
- error("mmap src error");
- return -1;
- }
- if((dst=mmap(0,len,PROT_WRITE,MAP_SHARED,dstfd,0))==MAP_FAILED)
- {
- error("mmap dst error");
- return -1;
- }
- if(memcpy(dst,src,len)==NULL)
- {
- error("memcpy error");
- return -1;
- }
- munmap(src,len);
- munmap(dst,len);
- return 0;
- }
运行,拷贝一个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则需要通过系统调用把内核空间的缓存复制到用户空间,再将用户空间缓存复制到内核空间,拷贝次数明显多了一个呢?速度为什么于理论预测的不一致呢?
阅读(12536) | 评论(5) | 转发(3) |