zoukankan      html  css  js  c++  java
  • 2021-2022-1 20191315《信息安全系统设计与实现(上)》学习笔记2

    I/O库函数

    本章讨论了I/O库函数解释了I/O库函数的作用及其相对于系统调用的优势;使用示例程序来说明I/O库函数和系统调用之间的关系,并解释了它们之间的相似性和基本区别;详细介绍了I/O库函数的算法,包括fread、fwrite和 fclose 的算法,重点介绍了它们与 read、write 和 close 系统调用的交互;介绍了I/O库函数的不同模式,包括字符模式、行模、结构化记录模式和格式化I/O操作;阐述了文件流缓冲方案,并通过示例程序说明了不同缓冲方案的效果;阐释了有不同参数的函数以及如何使用stdarg 宏访问参数。


    I/O库函数

    • 系统调用是文件操作的基础,但它们只支持数据块的读/写。
    • 实际上,用户程序可能望以最适合应用程序的逻辑单元读/写文件,如行、字符、结构化记录等,而系统调用不持这些逻辑单元。

    I/O库函数和系统调用

    在Unix/Linux中,I/O库函数建立在系统调用的基础上

    • 系统调用函数:open() read() write() lseek() close()
    • I/O库函数:fopen() fread() fwrite() fseek() fclose()

    I/O库函数的算法

    fread算法

    • 在第一次调用freadO时,FILE结构体的缓冲区是空的,freadO使用保存的文件描述符fd发出一个
      n = read(fd, fbuffer, BLKSIZE);
      系统调用,用数据块填充内部的fbuf[]。然后,它会初始化fbuf[]的指针、计数器和状态变量,以表明内部缓冲区中有一个数据块。接着,通过将数据复制到程序的缓冲区,尝试满足来自内部缓冲区的fread调用。如果内部缓冲区没有足够的数据,则会再发出一个 read()系统调用来填充内部缓冲区,将数据从内部缓冲区传输到程序缓冲区,直到满足所需的字节数(或者文件无更多数据)。将数据复制到程序的缓冲区之后,它会更新内部缓冲区的指针、计数器等,为下一个fread()请求做好准备。然后,它会返回实际读取的数据对象数量。
    • 在随后的每次 fread() 调用中,它都尝试满足来自FILE 结构体内部缓冲区的调用。当缓冲区变为空时,它就会发出read()系统调用来重新填充内部缓冲区。因此,fread()一方面接受来自用户程序的调用,另一方面向操作系统内核发出read()系统调用。除了read()系统调用之外,所有fread()处理都在用户模式映像中执行。它只在需要时才会进入操作系统内核,并且以一种最高效匹配文件的方式进入。它会提供自动缓冲机制,因此用户程序不必担心这些具体操作。

    fwrite算法

    • fwrite()算法与fread()算法相似,只是数据传输方向不同。最开始,FILE结构体的内部缓冲区是空的。在每次调用fwrite()时,它将数据写入内部缓冲区,并调整缓冲区的指针、计数器和状态变量,以跟踪缓冲区中的字节数。如果缓冲区已满,则发出 write()系统调用,将整个缓冲区写入操作系统内核。

    fclose算法

    • 若文件以写的方式被打开,fcloseO会先关闭文件流的局部缓冲区。然后,它会发出一个close(fd)系统调用来关闭FILE结构体中的文件描述符。最后,它会释放FILE结构体,并将 FILE 指针重置为 NULL。

    使用I/O库函数或系统调用

    什么时候使用系统调用或库函数进行文件I/O?

    fread()依赖read将数据从内核复制到内部缓冲区,然后从内部缓冲区将数据复制到程序的缓冲区。所以,它传输了两次数据。相反,read()将数据从内核直接复制到程序的缓冲区,只复制了一次。因此,对于以BLKSIZE为单位的读/写数据来说,read()本来就比fread()更高效,因为它只需要一个而不是两个复制操作。类似表述也适用于write()和 fwrite()。

    需要注意,在fread()和 fwrite()的一些实现中,例如在GNU libc库中,如果请求的大小以 BLKSIZE为单位,它们可以使用系统调用将以BLKSIZE为单位的数据直接从内核传输到用户指定的缓冲区。即便如此,使用I/O函数仍然需要其他的函数调用。因此,在上面的例子中,使用系统调用的程序实际上比使用 1/O库函数的程序更高效。但是,如果不是以BLKSIZE为单位进行读/写,那么fread()和fwrite()可能更高效。例如,如果我们坚持一次读/写一个字节,fread()和fwrite()会好得多,因为它们进人操作系统内核只是为了填充或清除内部缓冲区,并不是逐字节输入。这里假设进人内核模式比停留在用户模式的代价更高,事实的确如此。

    I/O库模式

    fopen()中模式参数可指定为:r、w、a,分别为读 写 追加。每个模式字符串可包含一个+号,表示同时读写,或者在写写入、追加情况下,如果文件不存在则创建文件。

    • "r+":表示读/写,不会截断文件。
    • "w+":表示读/写,但是会先截断文件;如果文件不存在,会创建文件。
    • "a+":表示通过追加进行读/写;如果文件不存在,会创建文件。

    字符模式I/O

       int fgetc(FILE *fp):	      // get a char from fp, cast to int.	
       int ungetc(int c, fILE *fpl;  // push a previously char got by fgetc() back to stream
       int fputc(int c, FILE *fp);   // put a char to fp
    
    • fgetc()返回的是整数,而不是字符。这是因为它必须在文件结束时返回文件结束符。文件结束符通常是一个整数-1,将它与文件流中的任何字符区分开。
    • 对于fp=stdin或stdout,可能会使用c=getchar();putchar(c);来代替。对于运行时效说,getchar()和putchar()通常不是getc()和putc()的缩小版本。相反,可以将它们实现为宏,以避免额外的函数调用。

    字符模式I/O

    /* file copy using getc(), putc() */
      #include <stdio.h> 
      FILE*fp,*gp; 
      int main(){
         int c; /* for testing EoF */ 
         fp=fopen("source","r"); 
         gp=fopen("target","w");
         while((c=getc(fp)) != EOF )
             putc(c,gp);
         fclose(fp);
         fclose(gp);
      }
    

    行模式I/O

    char *fgets(char *buf, int size,FILE*fp):从fp中读取最多为一行(以 结尾)的字符。
    int fputs(char *buf,FILE*fp):将buf中的一行写人fp中。

    #include <stdio.h> 
    FILE *fp,*gp;
    char buf[256]; 
    char *s="this is a string";
    int main(){
        fp = fopen("sre","r"); 
        gp = fopen("dest","w");
        fgets(buf,256,fp); // read a line of up to 255 chars to buf
        fputs(buf,gp);	// write line to destination file
    }
    
    • 当 fp 是 stdin 或 stdout 时,也可以使用以下函数,但它们并非fgets()和fputs()的缩减版本。
    gets(char *buf);	// input line from stdin but without checking length	
    puts(char *buf);	// write line to stdout
    

    格式化

    格式化输入:(FMT=格式字符串)
    scanf(char*FMT, &items); // from stdin
    fscanf(fp,char *FMT, &items); // from file stream
    格式化输出:
    printf(char *FMT, items); // to stdout
    fprintf(fp,char *FMT, items); // to file stream

    内存中的转换函数

    sscanf(buf,FMT,&items); // input from buf[ ] in memory
    sprintf(buf,FMT,items); // print to buf[ ] in memroy

    • sscanf和sprintf并非I/O函数,而是内存中的数据转换函数

    其他I/O库函数

    • fseek()、ftell()、rewind():更改文件流中的读/写字节位置。
    • feofO)、ferr()、fileno():测试文件流状态。
    • fdopen():用文件描述符打开文件流。
    • freopen():以新名称重新打开现有的流。
    • setbuf0、setvbuf():设置缓冲方案。
    • popen():创建管道,复刻子进程来调用sh。

    限制混合 fread-fwrite

    当某文件流同时用于读/写时,就会限制使用混合 fread() 和 fwrite() 调用。规范要求每对 fread()和 fwrite()之间至少有一个 fseek()或 ftell()。

    文件流缓冲

    • 无缓冲
    • 行缓冲
    • 全缓冲
      通过fopen()创建文件流之后,在对其执行任何操作之前,用户均可发出一个
      setvbuf(FILE *stream, char *buf, int node, int size)
      调用来设置缓冲区(buf)、缓冲区大小(size)和缓冲方案(mode),它们必须是以下一个宏:
    • IONBUF:无缓冲
    • IOLBUF:行缓冲
    • IOFBUF:全缓冲
      此外,还有其他的setbuf()函数,是setvbuf()的变体。对于行缓冲流或全缓冲流,可用fflush(stream)立即清除流的缓冲区。

    变参函数

    printf()多种不同类型的可变数量参数可以调用它。C语言仍然允许参数数量可变的函数。这些函数必须至少使用一个参数进行声明,后跟3个点,如
    int funclint m, int n...) // n = last specified parameter
    在函数内部,可以通过C语言库宏访问参数:
    void va_start(va_list ap,last); // start param list from last parameter
    type va_arg(va_list ap, type);//type = next parameter type
    va_end(va_1ist ap); // clear parameter list

  • 相关阅读:
    java Activiti 工作流引擎 SSM 框架模块设计方案
    自定义表单 Flowable 工作流 Springboot vue.js 前后分离 跨域 有代码生成器
    数据库设计的十个最佳实践
    activiti 汉化 stencilset.json 文件内容
    JAVA oa 办公系统模块 设计方案
    java 考试系统 在线学习 视频直播 人脸识别 springboot框架 前后分离 PC和手机端
    集成 nacos注册中心配置使用
    “感恩节 ”怼记
    仓颉编程语言的一点期望
    关于System.out.println()与System.out.print("\n")的区别
  • 原文地址:https://www.cnblogs.com/harperhjl/p/15311284.html
Copyright © 2011-2022 走看看