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

  • 相关阅读:
    整数参数求和
    《Objective-C 程序设计》读后感
    构建之法前三章精读读后感
    《java编程思想》读后感
    《JAVA核心技术》观后感
    构建之法读后感
    一、构建之法读后感
    开发MIS系统需要的技术及其含义、作用
    阅读笔记一
    Java课后练习9(异常处理)
  • 原文地址:https://www.cnblogs.com/harperhjl/p/15311284.html
Copyright © 2011-2022 走看看