zoukankan      html  css  js  c++  java
  • Linux系统编程19_标准I/O

    IO是计算机上最慢的部分,先不看磁盘IO,针对网络编程,自然是针对网络IO。
    网络协议对网络IO影响很大,当下,TCP/IP协议是毫无疑问的主流协议。

    文件IO中使用了文件描述符的概念。
    但是并不是所有操作系统都提供文件描述符这样抽象的机制。
    因此 文件IO 的那一套函数,可以在类unix系统下使用。但是换做别的系统,就没有了文件描述符的概念。

    而c标准库,为我们提供了一套函数叫做标准IO。无论在什么系统上,我们都可以使用标准IO 所提供的函数。
    标准IO相当于在所有的操作系统的实现上提供了一层封装,使用标准IO 函数 进行IO操作时,在不同的操作系统上是兼容的。

    标准IO提供了两方面的封装:
    操作对象 – FILE*: 封装了不同OS对文件的实现,
    封装了缓存支持和优化。

    在C中引入了流(stream)的概念。
    它将数据的输入输出看作是数据的流入和流出,这样不管是磁盘文件或者是物理设备(打印机、显示器、键盘等),都可看作一种流的源和目的,
    视他们为同一种东西,而不管其具体的物理结构,即对他们的操作,就是数据的流入和流出。
    这种把数据的输入输出操作对象,抽象化为一种流,而不管它的具体结构的方法很有利于编程,
    而涉及流的输出操作函数可用于各种对象,与其具体的实体无关,即具有通用性。

    数据是流入和流出的对象,加如数据是水。
    存储的目标(比如一个文件)当做水池,链接水池和外界的是水管,这个水管往水池里灌水,或者把水池里的水泵出来,形成“流”。
    往里灌水(写文件)时,对于水管(流)来说,是“将水管里的水输出到水池里”,此时对于流来说是输出流;
    往外排水(读文件)时,对于水管(流)来说,是“将水池里的水输入到水管里”,此时对于流来说是输入流。
    一开始总是站在水池的角度,总是想不明白,站在水管的角度就明白了。

    在C中流可分为两大类,即文本流(text stream)和二进制流(binary stream)。
    所谓文本流是指在流中流动的数据是以字符形式出现。在文本流中,’ ’被换成回车CR和换行LF的代码0DH和0AH。而当输出时,则0DH和0AH本换成’ ’。

    二进制流是指流动的是二进制数字序列,若流中有字符,则用一个字节的二进制ASCII码表示,若是数字,则用一个字节的二进制数表示。
    在流入流出时,对 符号不进行变换。例如2001这个数,在文本流中用其ASCII码表示为:‘2’ ‘0’ ‘0’ ‘1’,共占4字节。 但是在二进制流中为其二进制形式:00000111 11010001 用十六进制就是07D1。只占两字节。

    操作系统如何标识一个流:
    用一个结构体对象标识一个流,结构体对象里面记录了这个流的相关信息。 这个结构体的名字叫FILE。
    这样的结构体是操作系统帮我们创建的,而我们要操作这个结构体,就要知道这个结构体的位置,而FILE * 指针就是指向这个结构体的指针。
    当采用fopen 打开一个流时会返回一个指向流对象的指针,指针类型为FILE *
    每个被使用的文件都在内存中开辟一个区域,用来存放文件的有关信息,这些信息是保存在一个结构体类型的变量中
    该结构体类型是由标准IO库定义的,取名为FILE。
    FILE封装了系统调用中的文件描述符的概念,所以标准IO中的函数不会直接操作文件描述符。
    同理:FILE屏蔽了操作系统的差别(标准IO不用文件描述符也是为了屏蔽OS的概念,用FILE这个抽象的概念。而文件描述符绝对是个UNIX/LINUX的概念。

    一个进程启动的时候,会打开三个流
    标准输入 stdin
    标准输出 stdout
    标准出错 stderr

    标准I/O设置缓冲区:
    标准I/O的缓冲区实际上是用户态中的缓冲区;
    用户态是不能访问硬盘、键盘、终端这些外围(虚拟)设备的,因此需要切换到内核态。
    用户态和内核态的切换是会产生开销的。

    文件I/O——不带缓冲的I/O //其实本质上文件I/O也是带缓冲的。这句话实际上的意思是文件I/O不带用户态的缓冲区;
    所谓不带缓冲,即每调用一次文件IO(如write),就进行一次用户态与内核态的切换;
    标准I/O——带缓冲的I/O //带用户态下的缓冲区,减少系统调用次数;
    所谓带缓冲,其实就是在调用fwrite的时候,先把数据放在缓冲区。不直接使用系统调用,切换到内核态。
    而是等到缓冲区满或是调用fflush等条件满足时,再一次性调用write,把数据拷贝到内核空间。

    缓冲是标准IO库提供的,缓冲区存在于用户空间(不管是fread还是fwrite)。
    对于fwrite,根据上面的例子很好理解。
    对于fread而言,就是在调用fread的时候,切换到内核态,
    内核态读取缓冲去能够接受的大小的数据(一般会多余此次fread期望的数据), 然后返回给fread函数。
    下次再调用fread的时候,可能只需要从缓冲区读取,而不需要再到内核空间拷贝了。

    标准IO有三种缓冲类型:全缓冲、 行缓冲、 不带缓冲
    缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。
    1、全缓冲:在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
    2、行缓冲:在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。
    典型代表是键盘输入数据。
    3、不带缓冲:也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。

    文件IO与标准IO的应用场景:
    在多线程日志系统中,每个线程打开一个日志文件的描述符;
    如果使用带缓冲的IO,最终可能导致日志的顺序发生错乱,可能影响阅读。
    因此如果对多线程的日志顺序有要求的话,可能需要使用不带缓冲的IO。

    ========================================================================

    参考链接:

    标准I/O: https://www.cnblogs.com/lasnitch/p/12764120.html
    内核态与用户态、系统调用与库函数、文件IO与标准IO、缓冲区等概念介绍:https://blog.csdn.net/windeal3203/article/details/79083453 //博主的其他文章也很不错

    知行合一
  • 相关阅读:
    CTF-pwn-tips-zh_CN
    Linux 内核中 offset_of 和 container_of 宏的实现
    glibc2.26 -- tcache (2)
    glibc2.26 -- tcache (1)
    漏洞复现 -- 条件竞争 -- TOCTOU
    Linux 内核源码分析 -- read
    ospf lsa 4是不可替代的
    MPLS_Lab_3_AToM
    配置多链路捆绑PPP
    OSPF在转换LSA 5时的转发地址抑制 cyrus
  • 原文地址:https://www.cnblogs.com/grooovvve/p/14665186.html
Copyright © 2011-2022 走看看