前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UNIX 高级环境编程 实验一 同步与异步write的效率比较

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

作者头像
glm233
发布2020-10-26 17:06:25
1.2K0
发布2020-10-26 17:06:25
举报

实验一 同步与异步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 %d\n",length);
    printf("%-8s\t %-8s\t %-8s\t %-8s\t \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 %d\n",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("%8d\t %8.2f\t %8.2f\t %8.2f\t \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 %d\n",length);
    printf("%-8s\t %-8s\t %-8s\t %-8s\t \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 %d\n",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("%8d\t %8.2f\t %8.2f\t %8.2f\t \n", buffSize, usertime, systime, clocktime);
    }
    close(fd);
    return 0;
}

四、运行结果 [

0UBrRS.png
0UBrRS.png
0UBDG8.md.png
0UBDG8.md.png

可以看到,在处理大文件时,同步读入比异步模式要慢,虽然两者在小文件读入情况下几乎相似,但可以近似说同步比异步读入慢,具体的原因已在上文分析过。

五、实验总结

本次实验学习了unix下文件的基本操作,比如lseek重定位文件读写头、open打开文件、read读文件、write写文件,并认识了基本的打开文件的O_FLAG参数,学习了异步和同步打开文件在运行时间上的差异,以及详细的区别(从原理上了解),虽说之前上过linux基础编程课,但面对第一次实验课,我在一开始还是束手无策的,只能翻书、上网查阅资料看看他人的思路,最终在自己复现一遍,最终还是感觉收获了不少(真话):一些文件操作,复习了C语言。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-10-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实验一 同步与异步write的效率比较
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档