赞
踩
Linux的系统I/O函数(read、write、open、close和 lseek等)与C语言的C库函数(libc.so库文件中)都是相对应的,它们都是动态库函数。如下图所示,C库函数有fopen、fclose、fwrite、fread和fseek等。这些C库函数都封装在libc.so库文件中,其中的fopen函数用于打开一个文件,其返回值为FILE *类型(指向FILE类型的一个指针),FILE类型为一个结构体,用于描述对打开的文件的一些操作,对于除了fopen以外的C库函数都会通过这个FILE *类型指针对打开的文件进行操作。
FILE类型为一个结构体,包括三个部分:文件描述符、文件读写指针和I/O缓冲区。
文件描述符(整型)用于索引到对应的磁盘文件,类似于FCB(文件控制块)和索引结点,包含了该文件在磁盘上的位置信息。对于每一个进程打开的所有文件在其PCB中都有记录相应的文件描述符,如0代表标准输入,其宏定义为STDIN_FILENO(#define STDIN_FILENO 0)。所有执行I/O操作的系统调用都以文件描述符,即一个非负整数来指代所打开的文件。文件描述符可以用来表示所有类型的已打开文件。同时,多个文件描述符可以指向同一个打开文件,因为有在不同进程中打开同一个文件的需求。
文件描述符是一个整型数,文件描述符表是一个整型数组,而在PCB中有一个指针,指向文件描述符表的首地址,因此根据PCB就可以找到对应的文件描述符,而每一个文件描述符都对应一个FILE *的指针,即可以根据文件描述符进一步找到FILE结构体,该结构体保存了所打开文件的属性信息(文件大小、I/O缓冲、读写指针、文件打开次数等等),而这样的结构体每个文件都只有一份,供所有打开该文件的进程共享,而这些进程所不同的只是文件描述符不一样。FILE结构体的内容只有一份。FILE结构体类似inode,而文件描述符类似简化的FCB。
每一个文件只有一个文件读写指针(读写文件过程中指针的实际位置),在文件读写时要时刻注意当前文件指针的位置。文件读写指针指向将要写入或读出的下一个地址。如果一个进程对文件正在进行写入,此时另一个进程要读取该文件的数据,需要利用fseek函数将文件读写指针置于文件的起始位置才能读取数据。
系统调用指操作系统提供给用户程序调用的一组接口(接口函数)来获得内核提供的服务。在实际中程序员使用的通常不是系统调用,而应用程序接口API,也称为系统调用编程接口,接口函数可能要一个或者几个系统调用才能完成函数功能,此函数通过c库(libc)实现,如fread,fopen等。
I/O缓冲区。每一个C库函数在对文件操作时都会有对应的I/O缓冲区,I/O缓冲区属于内存的一部分,位于用户空间,默认大小为8KB,即8192个Bytes。
以fgetc / fputc函数为例,当用户程序第一次调用fgetc 读一个字节时,fgetc函数可能通过系统调用进入内核读1K字节到I/O缓冲区中,然后返回I/O缓冲区中的第一个字节给用户,把读写位置指向I/O缓冲区中的第二个字符,以后用户再调fgetc ,就直接从I/O缓冲区中读取,而不需要进内核了,当用户把这1K字节都读完之后,再次调用fgetc时,fgetc 函数会再次进入内核读1K字节到I/O缓冲区中。C标准库之所以会从内核预读一些数据放 在I/O缓冲区中,是希望用户程序随后要用到这些数据,C标准库的I/O缓冲区也在用户空间,直接从用户空间读取数据比进内核读数据要快得多。另一方面,用户程序调用fputc通常只是写到I/O缓冲区中,这样fputc 函数可以很快地返回,如果I/O缓冲区写满了,fputc 就通过系统调用把I/O缓冲区中的数据传给内核,内核最终把数据写回磁盘或设备。有时候用户程序希望把I/O缓冲区中的数据立刻给内核,让内核写回设备或磁盘,这称为Flush操作,对应的库函数是fflush,fclose函数在关闭文件之前也会做Flush操作。
由上可以看出,fgetc和fputc库函数在完成工作的过程中可能需要几个系统调用,如将I/O缓冲区的数据写入内核缓冲,将内核缓冲数据刷到磁盘上等。因此,通过增设I/O缓冲区和内核缓冲可以减少进入内核的次数和对磁盘的操作次数(这些都需要系统调用来完成),从而提高了读写效率。相对于内存,机械硬盘的读写速度很慢。机械硬盘的读写寻道时间ms级,而内存的读写速度是ns级别。另外,从用户空间(I/O缓冲区)直接读写数据也会比进入内核(系统调用)要快很多。
Flush操作。指把I/O缓冲区的数据立即传送给内核,然后刷到磁盘上,分为强制性和非强制性两种。C库函数fflush是强制性把I/O缓冲区的数据立即写给内核的缓冲区;C库函数fsync是强制性把内核缓冲区的数据立即刷到磁盘上。main函数的return和调用main函数中的exit(退出当前进程),也会把缓冲区的数据立即写到磁盘上。I/O缓冲区已经写满或者关闭文件时,将会自动将其数据写到磁盘上。
内核缓冲区。人生三大错觉之中的一个:在调用函数write()时,我们觉得该函数一旦返回,数据便已经写到了文件里,可是这样的概念仅仅是宏观上的。实际上,操作系统实现某些文件I/O时(如磁盘文件),为了保证I/O的效率,在内核一般会用到一片专门的区域(内存或独立的I/O地址空间)作为I/O数据缓冲区.它用在输入输出设备和CPU之间,用来缓存数据,使得低速的设备和快速的CPU可以协调工作避免低速的输入输出设备长时间占用CPU,降低系统调用,提高了CPU的工作效率。
Linux系统I/O函数是没有I/O缓冲区的,其缓存是由用户提供的。C库函数都有对应的I/O缓冲区8KB(8196Bytes)。注意:内核缓冲和I/O缓冲的作用不同。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。