UNIX 高级环境编程 实验一 同步与异步write的效率比较

2020-10-26 17:06:25 浏览数 (2)

实验一 同步与异步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语言。

0 人点赞