图展示了文件IO的主要手段。

linux IO 概览_java


1 文件IO

图中红色箭头描述的部分。APP想要和文件系统的文件进行读写,主要通过内核的交互,主要通过open/close/read/write/lseek/fsync等系统调用交互。内核为了加速读写速度,需要在内核中维护cache加速读取效率和减少写入次数。缓冲区需要通过fsync等操作确认落盘。上述open、close、read、write等函数可能会调用出错,出错时可以通过查errno出错码,做出相应的补救措施。


  • 内核打开文件时可以指定文件不需要cache进行直接IO(O_DIRECT),这种绕过内核的IO方式叫做kernel bypass。bypass在高性能网络通信中应用很多,不用内核tcp栈,而在用户态实现通信的方式目前很流行。

  • 以及可以指定文件read/write时以非阻塞形式(读写受阻时直接返回)执行(O_NONBLOCK)。


标准输入、输出、出错的预定义是

STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO


linux提供了posix_fadvise()系统调用,可以预加载部分数据到内核缓冲区,优化读取性能。


2 裸盘IO

有些时候,读写需要透过文件系统,直接与磁盘读写操作。也就是和没格式化的文件打交道。这时,采用的是图中蓝色箭头部分。裸盘读写会在内核的另一个buffer中进行缓存。


注意区分内核的cache和buffer,是两块区域。

linux IO 概览_java_02


3 用户缓冲IO

图中黄色箭头描述的部分。磁盘是块设备,读写操作以块大小或整数倍进行操作时,性能表现更好。因此,在用户态设置缓冲区,有助于提高IO速度。标准库提供了一套很好的用户态缓冲区方案。


stdio.h中提供的FILE文件指针方案,函数包括fopen/fdopen/fclose/fcloseall/fgetc/fputc/fgets/fputs/fread/fwrite

/fseek/fsetpos/rewind/ftell/fgetpos/fflush


标准库提供了配置缓存的方法:setbuf()和setvbuf()。缓冲区一般可以配置成无缓冲、行缓冲、块缓冲方式。一般需要交互的文件(标准输入输出)等都是行缓冲,遇到\n会触发写盘;正常的磁盘文件是块缓存,缓冲区满后会自动写盘。


注意fflush只保证用户态缓冲区写入到内核,通常为了保证数据落盘,需要让fflush和fsync配合一起使用。


标准IO库,需要经历两个缓冲区才能访问到磁盘数据。


预定义:stdin, stdout, stderr


4 存储映射

除此之外,文件可以映射到进程的虚拟内存空间中,像操作内存那样操作文件。主要的函数:mmap/munmap/msync。mmap映射文件,munmap取消映射,msync保证数据落盘。



划重点:

  1.  app要进行文件IO既可以通过内核接口,也可以通过第三方库接口。内核里有两个缓冲区,分别对应文件系统读写和设备读写。库函数读写缓冲区是另一层缓冲区,一般都是磁盘块大小的整数倍,常见的就4K对齐。库函数IO交互时,数据要通过两层缓冲区。

  2. 内核可以直接进行IO,不用cache/buffer。

  3. 数据落盘操作是必须的。fsync、fflush、msync。

  4. 文件映射的方式操作文件很方便,并可以用作进程间通信的媒介。