码迷,mamicode.com
首页 > 系统相关 > 详细

Linux系统编程19_标准I/O

时间:2021-04-16 12:18:46      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:span   内存   用户态   打印   https   为我   实体   snitch   不同   

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)。
所谓文本流是指在流中流动的数据是以字符形式出现。在文本流中,’\n’被换成回车CR和换行LF的代码0DH和0AH。而当输出时,则0DH和0AH本换成’\n’。

二进制流是指流动的是二进制数字序列,若流中有字符,则用一个字节的二进制ASCII码表示,若是数字,则用一个字节的二进制数表示。
在流入流出时,对\n符号不进行变换。例如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 //博主的其他文章也很不错

Linux系统编程19_标准I/O

标签:span   内存   用户态   打印   https   为我   实体   snitch   不同   

原文地址:https://www.cnblogs.com/grooovvve/p/14665186.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!