[C++]Linux之文件拷贝在系统调用和C库函数下的效率比较
声明:如需引用或者摘抄本博文源码或者其文章的,请在显著处注明,来源于本博文/作者,以示尊重劳动成果,助力开源精神。也欢迎大家一起探讨,交流,以共同进步~ 0.0
题目:
1. 分别利用文件的系统调用read、write和文件的库函数fread、fwrite实现文件复制功能,比较在每次读取一个字节和1024字节时两个程序的执行效率,并分析原因。
分析:
预先准备好一份已经存储数据的普通文件(data.txt)
设置两对照组:
对照组1(系统调用组):在执行系统调用实现文件拷贝功能时,分别对读取一个字节和1024个字节记时,读取的执行时间若分别记为t1和t1024,然后通过比较1024×t1与t1024二者的时间来比较执行效率(时间效率为主)。
对照组2(库函数组):在执行库函数实现文件拷贝功能时,分别对文件的系统调用与文件的库函数的执行时间形成对照,比较二者执行效率。
即 本次实验需要记录4个变量:
1)系统调用的读取1字节的时间:Syst1
2)系统调用的读取1024字节的时间:Syst1024
3)库函数的读取1字节的时间:Lib1
4)库函数的读取1024字节:Lib1024
猜想:
系统调用的性能较库函数高,所以,性能上:Syst > Lib;
读取字节数小的时间效率将高于读取字节数大的时间效率,所以,时间上:T(1) < T(1024)
则,猜想结论: Lib1024 > Syst1024 > Lib1 > Syst1 (耗时)
开始实施实验,验证猜想 Action!
实验
- 【系统调用组】
- gcc main-system.c -o main-system.out
- time ./main-system.out
- /*
- @author:johnny
- @description:系统调用
- */
- #include<stdio.h>
- #include<stdlib.h>//exit(num)
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<string.h>
- #include<fcntl.h>
- #include<time.h>
- #include<unistd.h>//dependency:close/write/open method
- #define LENGTH_1 1
- #define LENGTH_1024 1024
- //统计系统调用的读取1字节的时间效率,并实现文件复制功能
- double read1Byte(){
- double startTime;
- double endTime;
- double Syst1;//系统调用的读取1字节的时间:Syst1
- int len,fread,fwrite;
- off_t off;//记录文件游标偏移位置
- char readStr[LENGTH_1];
- fread = open("data.txt",O_RDWR | O_CREAT,S_IRUSR | S_IWUSR);//打开数据源文件
- fwrite = creat("copy-1-data.txt", 0700);//创建拷贝文件
- if(fwrite == -1){
- perror("\n[read1Byte] Fail to create file 'copy-1-data.txt'.\n\n");
- exit(1);
- } else {
- printf("\n[read1Byte] Create file 'copy-1-data.txt' OK.\n\n");
- }
- if(fwrite){//write data to file.
- off = lseek(fwrite,0,SEEK_SET); //获取并重置游标位置为文件开头处,SEEK_CUR当前读写位置后增加offset[0]个位移量
- if(off == -1){
- perror("\n[read1Byte] Fail to lseek.\n\n");
- exit(1);
- }
- startTime = clock();//从打开文件后,读取文件数据前开始记录时间
- len = read(fread, readStr, LENGTH_1);//从data.txt读取数据
- endTime = clock();
- while(len){//判断len是否为0,如果为0,则说明读取到了文件尾,则停止
- printf("%s", readStr);
- write(fwrite, readStr,strlen(readStr)); //一边读取源数据文件,一边写入新文件数据
- len = read(fread, readStr, LENGTH_1);
- }
- }
- close(fread);
- close(fwrite);
- Syst1 = (double)(endTime - startTime) / 1000; //(单位:毫秒)
- printf("\n[read1Byte] 系统调用的读取1字节的时间[Syst1]:%f ms\n\n", Syst1);
- return Syst1;
- }
- //统计系统调用的读取1024字节的时间效率,并实现文件复制功能
- double read1024Bytes(){
- double startTime;
- double endTime;
- double Syst1024;//系统调用的读取1024字节的时间:Syst1024
- int len,fread,fwrite;
- off_t off;//记录文件游标偏移位置
- char readStr[LENGTH_1024];
- fread = open("data.txt",O_RDWR | O_CREAT,S_IRUSR | S_IWUSR);//打开数据源文件
- fwrite = creat("copy-1024-data.txt", 0700);//创建拷贝文件
- if(fwrite == -1){
- perror("\n[read1024Byte] Fail to create file 'copy-1024-data.txt'.\n\n");
- exit(1);
- } else {
- printf("\n[read1024Bytes] Create file 'copy-1024-data.txt' OK.\n\n");
- }
- if(fwrite){//write data to file.
- off = lseek(fwrite,0,SEEK_SET); //获取并重置游标位置为文件开头处,SEEK_CUR当前读写位置后增加offset[0]个位移量
- if(off == -1){
- perror("\n[read1024Bytes] Fail to lseek.\n");
- exit(1);
- }
- startTime = clock();//从打开文件后,读取文件数据前开始记录时间
- len = read(fread, readStr, LENGTH_1024);//从data.txt读取数据
- endTime = clock();
- while(len){//判断len是否为0,如果为0,则说明读取到了文件尾,则停止
- printf("%s", readStr);
- write(fwrite, readStr,strlen(readStr)); //一边读取源数据文件,一边写入新文件数据
- len = read(fread, readStr, LENGTH_1024);
- }
- }
- close(fread);
- close(fwrite);
- Syst1024 = (double)(endTime - startTime) / 1000; //(单位:毫秒)
- printf("\n[read1024Bytes] 系统调用的读取1024字节的时间[Syst1024]:%f ms\n\n", Syst1024);
- return Syst1024;
- }
- int main(){
- read1Byte();//统计系统调用的读取1字节的时间效率,并实现文件复制功能
- read1024Bytes();//统计系统调用的读取1024字节的时间效率,并实现文件复制功能
- return 0;
- }
运行效果:
- 【库函数调用组】
- gcc main-lib.c -o main-lib.out
- time ./main-lib.out
- /*
- @author:johnny
- @description:库函数
- */
- #include<stdio.h>
- #include<stdlib.h>//exit(num)
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<string.h>
- #include<fcntl.h>
- #include<time.h>
- #include<unistd.h>//dependency:close/write/open method
- #define LENGTH_1 1
- #define LENGTH_1024 1024
- //统计库函数的读取1字节的时间效率,并实现文件复制功能
- double read1Byte(){
- double startTime;
- double endTime;
- double Lib1;//库函数的读取1字节的时间:Lib1
- FILE * readStream;//读取数据源文件data.txt的文件流
- FILE * writeStream;//拷贝数据源文件data.txt的文件流
- readStream = fopen("data.txt","r+");//只读方式打开可读写的且必须已经存在的文件
- writeStream = fopen("copy-1-data.txt","w+");//只写方式打开文件。若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
- if(readStream == NULL){
- fprintf(stderr,"\n[read1Byte] readStream can not open file 'data.txt'.\n\n");
- return 0;
- }
- if(writeStream == NULL){
- fprintf(stderr,"\n[read1Byte] writeStream can not create/open file 'copy-1-data.txt'.\n\n");
- return 0;
- } else {
- printf("\n[read1024Bytes] writeStream create/open file 'copy-1-data.txt' OK.\n\n");
- }
- size_t len;//记录每次实际读取到的缓冲数据长度,若为0,则可能是读取错误或者已读到文件尾。
- int off;//记录文件游标当前偏移位置
- char buffer[LENGTH_1];
- //write data to file.
- off = fseek(writeStream,0,SEEK_SET);//获取并重置游标位置为文件开头处,SEEK_CUR当前读写位置后增加offset[0]个位移量
- if(off == -1){
- perror("\n[read1Byte] Fail to fseek.\n\n");
- exit(1);
- }
- startTime = clock();//从打开文件后,读取文件数据前开始记录时间
- len = fread(buffer, LENGTH_1, LENGTH_1, readStream);//从data.txt读取数据,len为返回实际写入的nmemb数目
- endTime = clock();
- while(len){//判断len是否为0,如果为0,则说明读取到了文件尾,则停止
- printf("%s", buffer);
- fwrite(fwrite, LENGTH_1, len, writeStream); //一边读取源数据文件,一边写入新文件数据
- len = fread(buffer, LENGTH_1, LENGTH_1, readStream);
- }
- fclose(readStream);
- fclose(writeStream);
- Lib1 = (double)(endTime - startTime);
- printf("\n[read1Byte] 库函数的读取1字节的时间[Lib1]:%f ms\n\n", Lib1);
- return Lib1;
- }
- //统计库函数的读取1024字节的时间效率,并实现文件复制功能
- double read1024Bytes(){
- double startTime;
- double endTime;
- double Lib1024;//库函数的读取1024字节的时间:Lib1024
- FILE * readStream;//读取数据源文件data.txt的文件流
- FILE * writeStream;//拷贝数据源文件data.txt的文件流
- readStream = fopen("data.txt","r+");//只读方式打开可读写的且必须已经存在的文件
- writeStream = fopen("copy-1024-data.txt","w+");//只写方式打开文件。若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
- if(readStream == NULL){
- fprintf(stderr,"\n[read1024Bytes] readStream can not open file 'data.txt'.\n\n");
- return 0;
- }
- if(writeStream == NULL){
- fprintf(stderr,"\n[read1024Bytes] writeStream can not create/open file 'copy-1024-data.txt'.\n\n");
- return 0;
- } else {
- printf("\n[read1024Bytes] writeStream create/open file 'copy-1024-data.txt' OK.\n\n");
- }
- size_t len;//记录每次实际读取到的缓冲数据长度,若为0,则可能是读取错误或者已读到文件尾。
- int off;//记录文件游标当前偏移位置
- char buffer[LENGTH_1024];
- //write data to file.
- off = fseek(writeStream, 0, SEEK_SET);//获取并重置游标位置为文件开头处,SEEK_CUR当前读写位置后增加offset[0]个位移量
- if(off == -1){
- perror("\n[read1024Bytes] Fail to fseek.\n\n");
- exit(1);
- }
- startTime = clock();//从打开文件后,读取文件数据前开始记录时间
- len = fread(buffer, LENGTH_1, LENGTH_1024, readStream);//从data.txt读取数据,len为返回实际写入的nmemb数目
- endTime = clock();
- while(len){//判断len是否为0,如果为0,则说明读取到了文件尾,则停止
- printf("%s", buffer);
- fwrite(fwrite, LENGTH_1, len, writeStream); //一边读取源数据文件,一边写入新文件数据
- len = fread(buffer, LENGTH_1, LENGTH_1024, readStream);
- }
- fclose(readStream);
- fclose(writeStream);
- Lib1024 = (double)(endTime - startTime);
- printf("\n[read1024Bytes] 库函数的读取1字节的时间[Lib1024]:%f ms\n\n", Lib1024);
- return Lib1024;
- }
- int main(){
- read1Byte();//统计库函数的读取1字节的时间效率,并实现文件复制功能
- read1024Bytes();//统计库函数的读取1024字节的时间效率,并实现文件复制功能
- return 0;
- }
运行效果:
结果:
变量 | 读取字节数(byte) | API | 消耗时间(ms) |
Syst1 |
1 |
系统调用 | 0.061 |
Syst1024 | 1024 | 系统调用 | 0.002 |
Lib1 | 1 | 库函数 | 46 |
Lib024 | 1024 | 库函数 | 14 |
结论:
实验后,回顾最初的猜想:
系统调用的性能较库函数高,所以,性能上:Syst > Lib;
读取字节数小的时间效率将高于读取字节数大的时间效率,所以,时间上: T(1) < T(1024)
那么,猜想结论:
Lib1024 > Syst1024 > Lib1 > Syst1 (耗时)
然而,实际实验结论是:
Lib1 > Lib1024 >> Syst1 > Syst1024(耗时)
发现,猜想仅仅正确一半。
字节数小的时间效率反而远远高于读取字节数大的时间效率,且系统调用的效率远远高于之前对系统调用与库函数调用的想象。
实验数据显示,分别比较读取1字节和读取1024字节在系统调用组,库函数组的时间效率:
Lib1 ~= 754.098 * Syst1 (时间效率比较:754.098倍)
Lib1024 = 7000 * Syst1024 (时间效率比较:7000倍)
对于系统调用与库函数之间的效率差距,在查阅相关文献后,得知大致原因如下:
要实现文件拷贝,必须嵌入内核,从磁盘读取文件内容,然后存储到另一个文件。
实现文件拷贝最通常的做法是:
读取文件用系统调用read()函数,读取到一定长度的连续的用户层缓冲区,然后使用write()函数将缓冲区内容写入文件。也可以用标准库函数fread()和fwrite(),但这两个函数最终还是通过系统调用read()和write()实现拷贝的,因此可以归为一类(不过效率肯定没有直接进行系统调用的高)。一个更高级的做法是使用虚拟存储映射技术进行,这种方法将源文件以共享方式映射到虚拟存储器中,目的文件也以共享方式映射到虚拟地址空间中,然后使用memcpy高效地将源文件内容复制到目的文件中。
实验数据显示,分别比较系统调用组,库函数组在读取1字节和读取1024字节的时间效率:
Syst1 = 30.5 * Syst1024 (时间效率比较:30.5倍)
Lib1 ~= 3.286 * Lib1024 (时间效率比较:约3.3倍)
对于单(低)字节与多(更多)字节之间的时间效率差距,查阅相关资料无效后,个人揣测原因如下:(仍存在疑问)
本来系统读取数据的缓存区是较大的,可以读取多个字节数据,这样能够解释为何多(更多)字节数据的读取高的原因。又由于读取单(低)字节,需要系统额外地设置和读取缓存区,导致比读取更多字节更高的时间开销,所以单(低)字节的效率便低于多字节的时间效率了。
参考文献:
[原创]
[C++]Linux之文件拷贝在系统调用和C库函数下的效率比较的更多相关文章
- Linux C 文件操作,系统调用 -- open()、read() 和 标准I/O库 -- fopen()、fread()
函数汇总: open().write().read().close() fopen().fwrite().fread().fclose() 一.什么是文件 在讲述文件操作之前,我们首先要知道什么是文件 ...
- 【Linux】文件操作函数(系统调用函数)
重点在于学习--思路与方法 举一反三 一.文件描述符 系统分配给文件的数字编号 二.函数学习 P.S.Man命令使用方法 manual 前三个章节 命令:系统调用函数:库函数 man read //r ...
- 【Linux】文件拷贝-Linux当前目录所有文件移动到上一级目录(转)
Linux当前目录所有文件移动到上一级目录 mv * ../
- Linux 删除文件未释放空间问题处理,下清空或删除大文件
linux里的文件被删除后,空间没有被释放是因为在Linux系统中,通过rm或者文件管理器删除文件将会从文件系统的目录结构上解除链接(unlink).然而如果文件是被打开的(有一个进程正在使用),那么 ...
- Linux打开文件设置
在某些情况下会要求增加Linux的文件打开数,以增加服务器到处理效率,在Linux下查看最多能打开的文件数量为: cat /proc/sys/fs/file-max 然后设置ulimit -n 文件数 ...
- linux复制文件并修改文件名
#!/bin/bash #复制/casnw/backup/db203oradata/目录下的所有后缀名为dmp的文件拷贝到/casnw/backup/dbmonthbak 目录下cp -f /casn ...
- Linux C高级编程——文件操作之系统调用
Linux C高级编程文件操作之系统调用 宗旨:技术的学习是有限的,分享的精神是无限的. 库函数是一些完毕特定功能的函数.一般由某个标准组织制作公布,并形成一定的标准.使用库函数编 ...
- Linux系统下远程文件拷贝scp命令
在Linux系统下,不同机器上实现文件拷贝 一.将本地文件拷贝到远程机器: scp /home/administrator/news.txt root@192.168.6.129:/etc/squid ...
- Linux下不同机器之间的文件拷贝
通过 scp 命令实现不同机器之间的文件拷贝. (1)本机考到目标机器:scp 本机文件 目的地: 如:scp /home/odp-web.war root@192.168.6.137:/usr/ ...
随机推荐
- js 触摸的Event--获取触摸位置
继上一篇js原生拖拽之后,现在又来写一下移动端touch列表,获取触摸位置.pc端的event事件,鼠标的位置信息在上一篇,点此进入上一篇. touch有3个事件:touchstart,touchmo ...
- ElasticSearch启动错误处理方法
在配置完elasticsearch,启动程序会包如下错误: [elk@localhost bin]$ ./elasticsearch ... ... ERROR: [3] bootstrap chec ...
- luoguP4707 重返现世
收集邮票加强版,每个邮票不是等概率获得的了. 而且是获得K个,如果把一个全集S集合找出其获得时间集合(显然获得时间两两不同)的话,那么就是第n-k+1大的期望! %%%Sooke min-max容斥扩 ...
- 再谈 javascript 数组去重
前言 数组去重方法老生常谈,既然是常谈,我也来谈谈 双层循环 也许我们首先想到的是使用 indexOf 来循环判断一遍,但在这个方法之前,让我们先看看最原始的方法: var array = [1,1, ...
- 透彻掌握Promise的使用
Promise的重要性我认为我没有必要多讲,概括起来说就是必须得掌握,而且还要掌握透彻.这篇文章的开头,主要跟大家分析一下,为什么会有Promise出现. 在实际的使用当中,有非常多的应用场景我们不能 ...
- 第六篇-以隐式意图(Implicit Intent)呼叫系统服务
一.新建一个layout5.xml,同样换为constriant模式. 二.拖动两个Button到预览界面,第一个按钮名字改为DISPLAY WEBPAGE,第二个按钮名字改为MAKE A CALL. ...
- TensorFlow升级1.4:Cannot remove entries from nonexistent file \lib\site-pack
https://blog.csdn.net/wishchin/article/details/78559313 https://blog.csdn.net/fool_frog/article/deta ...
- Java 存储时间戳的几种方式
有时需要记录一下数据生成时间的时间戳,精确到秒,这里记录一下java存储时间戳字符串的几种方式 1.DateFormat private static final SimpleDateFormat s ...
- Installation failed with message Failed to finalize session: INSTALL_FAILED_TEST_ONLY:installPackageLI.
这样还不行的话,加 -t吧.
- springcloud的turbine集成zookeeper
1.turbine监控界面显示一直是loading的状态,如何解决 http://blog.didispace.com/spring-cloud-tips-4/ 2.通过追踪turbine的依赖可以看 ...