实验一 同步与异步write的效率比较
一、程序要求
实验要求程序必须指定输出的文件名,而该文件是否按同步方式打开,则是可以选择的。因此程序至少带一个、至多两个输入参数。程序默认从标准输入STDIN_FILENO读取输入文件,可以利用shell的输入定向功能选择具体的输入文件。
timewrite < outfile > [sync]
不得变更程序的名字和使用方法。sync参数为可选,若有则输出文件用O_SYNC打开(见课本P51的解释)。
例:
timewrite <f1 f2 表示输出文件f2不用O_SYNC 打开。
timewrite f1 sync <f2 表示输出文件f1用O_SYNC 打开。
程序输出write耗费的时间,注意避免计入read的时间。
二、解决方案
缓冲区:
我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。缓冲区就是一块内存区, 它用在输入输出设备和CPU之间,用来缓存数据 。它 使得低速的输入输出设备和高速的CPU能够协调工作 ,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。 为了准确计算write耗费的时间,很重要的就是要避免将read的时间计入,因为I/O操作的时间通常是毫秒级的,不可以忽略。一种有效的方法是,设置一个与输入文件长度相同的缓冲区,一次性地将输入文件读入缓冲区,而后就不必再读输入文件。这样就可以有效避免计入read的时间。
设置输入缓冲区需要知道文件的长度,我们用lseek的返回值来获取长度。在按每一个给定大小的输出缓冲区计算写文件时间时,应当在开始写之前调用times(),记录下开始时间,然后在整个输入缓冲区都复制到输出文件之后,再调用times(),两次调用times()的时间间隔,就是在这个给定大小的输出缓冲区的限制下,复制整个输入文件所耗费的写时间。至于在每一次写的时候所执行的其他语句,它们相较于I/O操作,所花费的时间极小,可以忽略不计。
步骤一、打开我们要写入的文件,如果文件不存在则创建一个。设置sync参数为可选项,若有则输出文件用O_SYNC打开。
步骤二、利用lseek函数获取文件的长度,利用文件重定向,即运行参数 <f1
的作用,以STDIN_FILENO 和 STDOUT_FILENO 。
步骤三、开辟文件缓冲区。
步骤四、利用STDIN_FILENO把文件读入buff中。
步骤五、也是最关键的一步,写入。每次以固定的 buffsize 写入,所以需要写入(file length / buff size)次。考虑到最后一部分的剩余内容,所以还需要再写一次,考虑到用不同的buffsize写入的时候,需要用lseek设置文件偏移量到文件头,这样就可以重新写入了。
步骤六、运行时间比较。每次写入的时候调用times(),结束的时候再次调用times(),时间之差即为写入时间。
*系统调用times()的说明\****
\#include <sys/times.h>
clock_t times(struct tms *buf);
struct tms {
clock_t tms_utime; /* 记录进程除系统调用外所使用的CPU时间 */
clock_t tms_stime; /* 记录进程的系统调用所使用的CPU时间 */
clock_t tms_cutime; /* 记录子进程除系统调用外所使用的CPU时间 */
clock_t tms_cstime; /* 记录子进程的系统调用所使用的CPU时间 */
};
times函数的返回值是进程迄今为止的存活时间。所有时间都是以“滴答”为单位的,函数 sysconf(_SC_CLK_TCK)可获得所运行系统每秒的滴答数
三、代码
#include<stdio.h>
#include<fcntl.h>
#include<sys/times.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<errno.h>
#include<stdarg.h>
int main(int argc,char* argv[])
{
//参数检查
if(argc<2||argc>3){
printf("argv error: ./timewrite<f1 f2 or ./timewrite f1 sync < f2");
}
if(argc==3 && strcmp(argv[2],"sync")!=0){
printf("argv[2] error: ./timewrite f1 sync<f2");
}
int fd;
//打开文件
if(argc==2) //异步打开文件
{
if((fd=open(argv[1],O_RDWR|O_CREAT,0777))<0){
printf("can't open");
exit(0);
}
}else //同步打开文件
{
if((fd=open(argv[1],O_RDWR|O_CREAT|O_TRUNC|O_SYNC,0777))<0){
printf("can't open");
exit(0);
}
}
//获取文件长度
long int length;
if((length = lseek(STDIN_FILENO,0,SEEK_END))<0){
printf("lseek error");
exit(0);
}
//开辟文件长度大小的缓冲区
char * buff;
buff = (char*)malloc(sizeof(char)*length);
//设置文件偏移量为开头
if(lseek(STDIN_FILENO,0,SEEK_SET)==-1){
printf("lseek error");
exit(0);
}
//把文件读入到buff中
if(read(STDIN_FILENO,buff,length)<0){
printf("read error");
exit(0);
}
//返回每秒时钟的滴答数
int ticks = sysconf(_SC_CLK_TCK);
//写文件,并计算效率
clock_t clockstart,clockend;
struct tms tmsStart,tmsEnd;
int buffsize;
for(buffsize=1024;buffsize<=131072;buffsize *= 2){
printf("\n\nBuffsize:%d\n",buffsize);
//每次写入前,定位到文件头
if(lseek(fd,0,SEEK_SET)==-1) printf("lseek error");
clockstart = times(&tmsStart);
int count = length/buffsize; //写入次数
for(int i=0;i<count;i++){
if(write(fd,buff+i*buffsize,buffsize)!=buffsize){
printf("write error");
exit(0);
}
}
int remain = length - buffsize*count; //剩余内容
//写入剩余内容
if(remain){
if(write(fd,buff+count*buffsize,remain)!=remain){
printf("write error");
exit(0);
}
count++;
}
clockend = times(&tmsEnd);
printf("clock used = %d\n",clockend-clockstart);
printf("tms_utime = %d\n",tmsEnd.tms_utime-tmsStart.tms_utime);
printf("tms_stime = %d\n",tmsEnd.tms_stime-tmsStart.tms_stime);
printf("tms_cutime = %d\n",tmsEnd.tms_cutime-tmsStart.tms_cutime);
printf("tms_cstime = %d\n",tmsEnd.tms_cstime-tmsStart.tms_cstime);
}
return 0;
}