实验一 同步与异步write的效率比较
学院:信息学院
专业:计算机科学与技术
一、实验内容
计算 write 耗费的时间,来比较同步写和异步写的性能差异。显示的时间应当尽量接近write操作过程所花的时间。不要将从磁盘读文件的时间计入显示结果中。
实验要求程序必须指定输出的文件名,而该文件是否按同步方式打开,则是可以选择的。因此程序至少带一个、至多两个输入参数。程序默认从标准输入 STDIN_FILENO 读取输入文件,可以利用shell的输入定向功能选择具体的输入文件。
**调用原型如下: ** timewrite [sync]
不得变更程序的名字和使用方法。sync参数为可选,若有则输出文件用O_SYNC打开(见课本P51的解释)。
执行示例:
timewrite <f1 f2 表示输出文件f2不用O_SYNC 打开。
timewrite f1 sync <f2 表示输出文件f1用O_SYNC 打开
程序输出:计算write耗费的时间
二、实验思路分析及代码细节说明
这个实验其实粗看起来很麻烦,但是实际上我们把握了基本的要求,细细一分析就会发现其实挺简单的。
首先,我们要明确,这次实验让我们做什么,需要比较两种文件写入方式造成的时间差异,两种方式分别是采用同步和异步写入文件。那么从理论上来说,是异步写文件方式更快,具体如下:
同步写入文件时,加入O_SYNC参数。该参数是系统调用open的flag参数。通过指定open的flag参数,以特定的文件描述符打开某一文件。O_SYNC具体功能为:**以同步方式写入文件,强制刷新内核缓冲区到输出文件。一般采用此参数是为了数据安全,需要确保将数据真正写入磁盘或者磁盘的硬件告诉缓存中。**异步很简单,就是不用同步方式写入文件。很明显,同步的系统调用cpu时间会大大增加,关键在于每一次以buffsize读入都会强制刷新内核缓冲区,在耗时上是比异步写入更高的。(2020.10.4 upd:前提是大文件,如果小文件的话两者差不多,几乎分不出时间上的差别)
明白了实验原理和预期的实验结果之后,剩下的事情就很简单了,开始编写程序,这里有必要对某些地方进行分析:
1.头文件包含问题:
- #include “apue.h” //需要用到err_sys函数
- #include <fcntl.h> //文件open函数
- #include <unist.h> //文件读写、重定位read、write、lseek函数
- #include <string.h> //字符串比较strcmp函数
- #include <malloc.h>//开辟缓冲区malloc函数
- #include <sys/times.h>//times()、sysconf函数
2.编写一个合格健壮的程序,我们必须考虑到用户的所有异常输入,我们这个程序只支持用户输入两参数和三参数,对于异常的输入参数数目或位置应该要提前进行处理:
对于执行代码
代码语言:javascript复制 ./timewrite <f1 f2
来说,这个argc[0]即为函数名,重定向符后跟着的是不计入argc参数数目及argc[]数组,此时的argc[1]=f2
对于执行代码
代码语言:javascript复制 ./timewrite f1 sync <f2
来说,这个argc[0]即为函数名,重定向符连带之后跟着的是不计入argc参数数目及argc[]数组,此时的argc[1]=f1,agrc[2]=“sync”
因此,不合法参数数目及位置提前筛除代码可以如下编写:
代码语言:javascript复制 //parameter check
if(!(argc>=2&&argc<=3))
{
if(strcmp("sync",argv[2]))err_sys("usage error");
else err_sys("Parameter number error");
}
针对异步同步两种情况采用不同的open flag参数打开文件:
代码语言:javascript复制//Open file by case (asynchronous synchronization)
if(argc == 2)//Two parameter asynchronism
{
/* Asynchronous, determines that if the return file descriptor is less than 0, the file will fail to open
*/
if((fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, FILE_MODE)) < 0) {
err_sys("open file error");
}
}
else
{
/*
Specifies parameter synchronization, determines if the return file descriptor is less than 0, and fails to open the file
*/
if((fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC|O_SYNC, FILE_MODE)) < 0) {
err_sys("can't open file");
}
}
计算文件长度、开辟缓冲区、文件读写头重定位到文件首部:
代码语言:javascript复制// Calculate file length, return -1 if error
if((length=lseek(STDIN_FILENO,0,SEEK_END))==-1)
{
err_sys("lseek error");
}
//Create a buffer,whose size equals to the length of the file to be read
if (!(buff = (char*)malloc(sizeof(char)*length)))
{
err_sys("malloc error");
}
//The file read/write header is located to the beginning of the file and returns -1 if there is an error
if(lseek(STDIN_FILENO, 0, SEEK_SET)==-1)
{
err_sys("lseek error");
}
开始读写,设置初始读写为1kb,每次读写长度翻倍,查看在不同的一次读写长度下的总时间,每次以固定的 buffSize 写入,所以需要写入(file length / buff size)次。考虑到可能有剩余,那么就需要特殊判断一下,最后是否需要多一次读入,注意开始计时是选定一个buffSize之后,然后在所有文件内容全部读入之后结束计时,利用tms结构体获得进程用户、系统所使用的CPU时间,格式化输出
代码语言:javascript复制 printf("file length is %dn",length);
printf("%-8st %-8st %-8st %-8st n", "BUFFSIZE", "user", "system", "clock cpu");
//Write file with different buffSize
for(buffSize=1024;buffSize<=length;buffSize<<=1)
{
if(lseek(fd,0,SEEK_SET)==-1)err_sys("lseek error");
cnt=length/buffSize; // Number of writes
clockStart = times(&start);
for(i=0;i<cnt;i )
{ // 以 buffsize 大小写入
//printf("%d %dn",i,buffSize);
if(write(fd,buff i*buffSize,buffSize)!=buffSize)
{
err_sys("write error");
}
}
if (length%buffSize)
{
if (write(fd,buff cnt*buffSize,length%buffSize)!=length%buffSize)
{
err_sys("write error");
}
}
clockEnd = times(&end);
usertime = (double)(end.tms_utime - start.tms_utime)/(long double)tick;
systime = (double)(end.tms_stime - start.tms_stime)/(long double)tick;
clocktime = (double)(clockEnd - clockStart)/(long double)tick;
printf("�t %8.2ft %8.2ft %8.2ft n", buffSize, usertime, systime, clocktime);
}
最后不要忘了关闭文件,安全第一
代码语言:javascript复制close(fd);
三、实验代码(英文详细注释)
代码语言:javascript复制//
// linux1.c
// glmglm
//
// Created by apple on 2020/9/25.
//
//header files
#include "apue.h"
#include <fcntl.h>
#include <sys/times.h>
//varibles
int fd,cnt;
size_t length,buffSize;
//fd: File descriptor
//cnt: Number of writes
//buffSize: The length of a single write to the file
//The length of file
long long int tick;
//The number of ticks per second of the operating system
char *buff;
//Buffer character array
clock_t clockStart, clockEnd;
//clockStart, clockEnd: clockEnd-clockstart=Totaltime
struct tms start, end;
//A structure that stores the time the system and the user call the CPU
double usertime, systime,clocktime;
//main function
int main(int argc, char *argv[])
{
tick = sysconf(_SC_CLK_TCK);
int i;//Loop variables
//parameter check
if(!(argc>=2&&argc<=3))
{
if(strcmp("sync",argv[2]))err_sys("usage error");
else err_sys("Parameter number error");
}
//Open file by case (asynchronous synchronization)
if(argc == 2)//Two parameter asynchronism
{
/* Asynchronous, determines that if the return file descriptor is less than 0, the file will fail to open
*/
if((fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, FILE_MODE)) < 0) {
err_sys("open file error");
}
}
else
{
/*
Specifies parameter synchronization, determines if the return file descriptor is less than 0, and fails to open the file
*/
if((fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC|O_SYNC, FILE_MODE)) < 0) {
err_sys("can't open file");
}
}
// Calculate file length, return -1 if error
if((length=lseek(STDIN_FILENO,0,SEEK_END))==-1)
{
err_sys("lseek error");
}
//Create a buffer,whose size equals to the length of the file to be read
if (!(buff = (char*)malloc(sizeof(char)*length)))
{
err_sys("malloc error");
}
//The file read/write header is located to the beginning of the file and returns -1 if there is an error
if(lseek(STDIN_FILENO, 0, SEEK_SET)==-1)
{
err_sys("lseek error");
}
printf("file length is %dn",length);
printf("%-8st %-8st %-8st %-8st n", "BUFFSIZE", "user", "system", "clock cpu");
//Write file with different buffSize
for(buffSize=1024;buffSize<=length;buffSize<<=1)
{
if(lseek(fd,0,SEEK_SET)==-1)err_sys("lseek error");
cnt=length/buffSize; // Number of writes
clockStart = times(&start);
for(i=0;i<cnt;i )
{ // 以 buffsize 大小写入
//printf("%d %dn",i,buffSize);
if(write(fd,buff i*buffSize,buffSize)!=buffSize)
{
err_sys("write error");
}
}
if (length%buffSize)
{
if (write(fd,buff cnt*buffSize,length%buffSize)!=length%buffSize)
{
err_sys("write error");
}
}
clockEnd = times(&end);
usertime = (double)(end.tms_utime - start.tms_utime)/(long double)tick;
systime = (double)(end.tms_stime - start.tms_stime)/(long double)tick;
clocktime = (double)(clockEnd - clockStart)/(long double)tick;
printf("�t %8.2ft %8.2ft %8.2ft n", buffSize, usertime, systime, clocktime);
}
close(fd);
return 0;
}
四、运行结果 [
可以看到,在处理大文件时,同步读入比异步模式要慢,虽然两者在小文件读入情况下几乎相似,但可以近似说同步比异步读入慢,具体的原因已在上文分析过。
五、实验总结
本次实验学习了unix下文件的基本操作,比如lseek重定位文件读写头、open打开文件、read读文件、write写文件,并认识了基本的打开文件的O_FLAG参数,学习了异步和同步打开文件在运行时间上的差异,以及详细的区别(从原理上了解),虽说之前上过linux基础编程课,但面对第一次实验课,我在一开始还是束手无策的,只能翻书、上网查阅资料看看他人的思路,最终在自己复现一遍,最终还是感觉收获了不少(真话):一些文件操作,复习了C语言。