Linux标准IO与文件IO的区别与联系

前言:

      本文主要内容是从网上摘录的,将查到的零散信息消化后归纳在一起,方便以后查阅。

      参考以下文章,谢谢作者的分享:

      Linux探秘之I/O效率

      https://cloud.tencent.com/developer/article/1018033

      linux系统编程之基础必备(四):C 标准库IO缓冲区和内核缓冲区的区别     

      https://cloud.tencent.com/developer/article/1012299

概念:


       标准I/O:是指标准I/O库,又称带缓存的I/O。它由ANSI C标准说明,标准I/O库代替用户处理很多细节,比如缓存分配、以优化长度执行I/O等,提供缓存的目的是为了尽量减少read和write的调用次数。
常用函数:fopen、fread、fwrite、fclose、printf、fprintf、scanf、sscanf等。
文件I/O:又被称为不带缓冲的I/O,指的是每个read和write都是调用内核中的一个系统调用。这些不带缓冲的文件I/O函数不是ISO C的组成部分。它们是POSIX.1和Single UNIX Specification的组成部分。常用函数:open、read、write、lseek、close等。

联系:


      用户程序调用C标准I/O库函数读写普通文件或设备,而这些库函数要通过系统调用把读写请求传给内核 ,最终由内核驱动磁盘或设备完成I/O操作。C标准库为每个打开的文件分配一个I/O缓冲区以加速读写操作,通过文件的FILE 结构体可以找到这个缓冲区,用户调用读写函数大多数时候都在I/O缓冲区中读写,只有少数时候需要把读写请求传给内核。以fgetc  / fputc 为例,当用户程序第一次调用fgetc 读一个字节时,fgetc 函数可能通过系统调用 进入内核读1K字节到I/O缓冲区中,然后返回I/O缓冲区中的第一个字节给用户,把读写位置指 向I/O缓冲区中的第二个字符,以后用户再调fgetc ,就直接从I/O缓冲区中读取,而不需要进内核 了,当用户把这1K字节都读完之后,再次调用fgetc 时,fgetc 函数会再次进入内核读1K字节 到I/O缓冲区中。在这个场景中用户程序、C标准库和内核之间的关系就像在“Memory Hierarchy”中 CPU、Cache和内存之间的关系一样,C标准库之所以会从内核预读一些数据放  在I/O缓冲区中,是希望用户程序随后要用到这些数据,C标准库的I/O缓冲区也在用户空间,直接 从用户空间读取数据比进内核读数据要快得多。另一方面,用户程序调用fputc 通常只是写到I/O缓 冲区中,这样fputc 函数可以很快地返回,如果I/O缓冲区写满了,fputc 就通过系统调用把I/O缓冲 区中的数据传给内核,内核最终把数据写回磁盘或设备。有时候用户程序希望把I/O缓冲区中的数据立刻  传给内核,让内核写回设备或磁盘,这称为Flush操作,对应的库函数是fflush,fclose函数在关闭文件 之前也会做Flush操作。


区别:


1、缓冲机制区别:
      众所周知,CPU和内存的数据交换要远大于磁盘操作,通过缓存机制,可以减少磁盘读写的次数,提高并发处理程序的效率,因此,缓存是一种提高任务存储和处理效率的有效方法。
从宏观上看,Linux操作系统分为用户态和内核态,在处理I/O操作的时候,两者都提供了缓存。用户态的称为标准I/O缓存,也称为用户空间缓存,而内核态的称为缓冲区高速缓存,也叫页面高速缓存。既然都提供了缓存,那为什么这本书上却分不带I/O的缓存和带I/O的缓存,原因其实是“不带I/O缓存”指的是用户空间中不为这些I/O操作设有缓冲,而内核是带缓冲的。

2、I/O操作的流程区别:


  如上图所示,用户进程空间和内核进程空间读写磁盘的操作都要经过缓冲区缓存,缓存的作用前面也提到过,是为了减少磁盘读写的次数,提高I/O的效率。当读写一个文件时,首先看系统I/O的操作流程。
2.1 文件I/O:

属于内核系统调用,没有涉及用户态的参与。以图中标号为例:
      (3) 调用write函数向文件中写数据,buf中存放的就是要写入的数据,如write(fd, 'abc', 3)。调用前需要先设置BUFFSIZE。不同的BUFFSIZE会影响I/O效率,下面再来说这个问题。
      (5) 延迟写:当缓存区高速缓存满或者内核要重写缓冲区的时候,才将数据写入输出队列,等数据到队列首部的时候,才真正触发磁盘的写操作。
      (6) 预读:当检测到正进行顺序读取时,内核就试图读入比应用程序所要求更多的数据,并假想应用程序很快就会读到这些数据。这样,当缓冲区没有数据时,能够快速填充下次要读取的数据。
      (4) 调用read从缓冲区高速缓存读取所需数据到逻辑单元中进行处理。
以上,就是系统I/O所涉及到的四步操作。

2.2 标准I/O:

      属于ISO C实现的标准库函数,调用的是底层的系统调用。
      (1) 将逻辑单元中的数据写入文件,根据需求,有三种函数类型可以调用,以fputc、fputs、fwrite为例,这些函数不用人为去控制缓冲区的大小,而是系统自动申请的,当用户定义了相应的I/O函数之后,根据不同的缓存类型(是全缓冲、行缓冲还是无缓冲),系统自动调用malloc等函数申请缓冲区,即标准I/O缓存。
      (3)(5) 当用户缓冲区满了之后,如系统I/O操作一般,此时调用write从标准I/O缓存中复制数据到内核缓冲区,再写入磁盘。
      (4)(6) 同系统I/O操作,从内核缓冲区调用read读入到用户缓冲区。
      (2) 同样有三种函数类型可以调用,以fgetc、fgets、fread为例,读入逻辑单元进行后续的处理。
可见,标准I/O实现的机制就是基于系统I/O,这样看来,标准I/O在效率上肯定不如系统I/O,但事实是标准I/O与系统I/O相比并不慢很多,而且还有很多其他的优点。

2.3 用数据流来形容两者的差异:
      无缓存I/O操作的数据流:数据->内核缓存区->磁盘
      标准I/O操作的数据流:数据->流缓存区->内核缓存区->磁盘

2.4 标准I/O的优点和缺点:
      1)使用标准I / O例程的一个优点是无需考虑缓存及最佳I / O长度的选择,并且它并不比直接调用read、write慢多少
      2)在标准I / O库中,一个效率不高的不足之处是需要复制的数据量。当使用每次一行函数fgets和fputs时,通常需要复制两次数据:一次是在内核和标准I / O缓存之间(当调用read和write时),第二次是在标准I / O缓存(通常系统分配和管理)和用户程序中的行缓存(fgets的参数就需要一个用户行缓存指针)之间。

3、经典回答:
      前者属于低级IO,后者是高级IO。
      前者返回一个文件描述符(用户程序区的),后者返回一个文件指针。
      前者无缓冲,后者有缓冲。
      前者与 read, write 等配合使用, 后者与 fread, fwrite等配合使用。
      后者是在前者的基础上扩充而来的,在大多数情况下,用后者。
     上面就是open和fopen的区别介绍了,两者的区别主要是缓冲的区别,fopen有缓冲而open没有,还有它们的层次也有所不同,fopen可移植而open不能。
 

猜你喜欢

转载自blog.csdn.net/the_wan/article/details/108309703