• Linux 系统编程学习笔记


    Linux系统为进程预定义了3个流:标准输入、标准输出、标准错误。进程启动时,会自动打开。
    3个流分别对应文件描述符(int):STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO;
    对应文件指针(FILE *):stdin、stdout、stderr;

    缓冲

    标准I/O库为标准输入、标准输出流提供了缓存。标准错误默认没有缓冲。
    提供缓冲的目的是尽可能减少read、write调用次数。

    下图是一个调用fprintf与缓冲区关系的示例:

    标准I/O和文件I/O

    文件I/O:不带缓冲的I/O(unbuffered I/O)。不带缓冲是指函数实现时不带库缓冲区,从而每个读、写操作,都直接调用系统调用。
    标准I/O:ANSI C建立的标准I/O模型,API包含在<stdio.h>中,不依赖内核,可移植性强。标准I/O库实现的缓存,统称简称库缓冲。

    3种标准I/O缓冲

    1. 全缓冲
      填满库缓冲后,才调用write将库缓冲内容写入内核高速缓存,由内核写入流。

    冲洗(flush)说明库缓冲的写操作。通常全缓冲写满以后,才会触发write系统调用,而冲洗操作会主动触发write,将所有用户控件输入write进内核高速缓存。对应系统调用fflush()。

    1. 行缓冲
      当输入和输出遇到换行符(CRLF for Win, LF for Unix)时,标准I/O库执行实际的I/O操作(read/write)。可以用fputc一次输出一个字符,但只有写了一行字符串或者写满库缓冲时,才会进行实际I/O操作。

    对行缓冲有2个限制:
    1)标准I/O库用来收集每行缓冲区长度固定,只要填满缓冲区,即使每写换行符,也进行I/O操作;
    2)任何时候,只要通过标准I/O库从 一个不带缓冲的流,或者一个行缓冲的流得到数据,那么就会flush库的所有行缓冲输出流;

    1. 不带缓冲
      指的是标准I/O库不对字符进行行缓冲存储。当然,这需要我们收到设置 禁用库缓冲,因为标准I/O输入、输出是默认行缓冲。

    PS:标准错误流默认不带库缓冲。

    ISO C要求下列缓冲特征:

    • 当前仅当标准输入、标准输出不指向交互式设备时,它们才是全缓冲的;
    • 标准错误绝不会是全缓冲的;

    很多系统默认实现:

    • 标准错误是不带缓冲的;
    • 若是指向终端设备的流,则是行缓冲的;否则,是全缓冲的;

    设置库缓冲

    • setbuf
      setbuf 使能、禁用长度为BUFSIZ的用户缓冲区buf。(BUFSIZ定义在<stdio.h>头文件中)。buf为NULL,代表关闭缓冲;buf指向一个长度为BUFSIZ的缓冲区,代表该流与全缓冲关联,如果该流与一个终端设备相关,那么有些系统可以将其设置为行缓冲。

    • setvbuf
      setvbuf 精确说明所需要的缓存类型,用mode参数实现:
      _IOFBF 全缓冲
      _IOLBF 行缓冲
      _IONBF 不带缓冲

    • setbuf与setvbuf关系

    1. setbuf(stream, buf) 相当于 setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
    2. setbuf 只能简单开启、关闭库缓冲,而不能指定其类型。setvbuf可以精确指定缓存类型;
    3. setbuf如果指定缓冲,其大小必须为固定的BUFSIZ,而setvbuf没有这个限制;
    4. 当setvbuf的mode指定_IOFBF(全缓冲)或者_IOLBF (行缓冲)时,如果buf为NULL,则由系统自动分配缓冲区;如果非NULL,则由用户提供缓冲区及其大小size;

    setbuf, setvbuf函数原型

    #include <stdio.h>
    
    void setbuf(FILE *stream, char *buf);
    int setvbuf(FILE *stream, char *buf, int mode, size_t size);
    

    冲洗流(库缓冲)

    fflush 函数可以强制冲洗一个流,使得库缓冲内容传送至内核(高速缓存)。
    如果stream为NULL,将导致所有输出流被冲洗。

    #include <stdio.h>
    
    int fflush(FILE *stream);
    

    PS:关于写磁盘,可以参考以下几点,不过要谨慎使用,可能导致效率很低。

    1. O_DIRECT选项
      如果想绕过内核高速缓存,直接访问磁盘而不使用内核缓存,可以在open文件的时候,加上选项O_DIRECT。对read、write都有效。

    2. O_SYNC选项
      如果想把内核缓存数据直接同步到磁盘,可以open文件的时候,加上O_SYNC选项。O_SYNC只对write有效。
      使用O_SYNC务必大块数据一次写,通常是 512B至4KB/次以上,避免频繁同步磁盘,导致效率极其低下。

    3. 缓存同步
      尽量保证缓存数据和写到磁盘的数据一致,有以下几种方法:

    • sync() 将所有修改过的块缓存提交到队列,然后返回,并不等待实际写磁盘操作结束(APUE描述,Linux环境编程 从应用到内核指出该点可能有误),并不能保证数据写入磁盘;

    • fsync() 将fd对应的文件的块缓存立即写入磁盘,并等待写磁盘操作结束返回;

    • fdatasync() 类似于fsync,但只影响文件的数据部分。而fsync除数据外,还会同步更新文件属性(如访问时间、修改时间、文件大小等);

  • 相关阅读:
    mybatis 控制台打印sql脚本
    删除git库中untracked files(未监控)的文件
    亚马逊云实例被攻击 一个月账单三百多美刀
    ubuntu下搜索文件
    django 富文本展示 以及 post提交出错
    亚马逊EC2弹性IP收费
    redis 相关命令
    使用XSHELL连接EC2虚拟机实例
    springMVC下集成active MQ发送邮件
    ubuntu下安装JDK并搭建activeMQ
  • 原文地址:https://www.cnblogs.com/fortunely/p/15467021.html
Copyright © 2020-2023  润新知