I/O模型前期知识

在这里插入图片描述

以此图中I\O阻塞模型为例

图中操作体指进程/线程/协程。用户运行程序要求访问文件,即创建操作体调用内核中开放的数据接口访问硬件空间的文件,通过文件名找到文件的inode号码,通过inode号码得到inode信息,通过inode信息找到文件所在的区域(即文件描述符),确定文件位置。

将文件从硬件空间拷贝至内核空间的缓冲区,然后cpu将数据从内核空间缓冲区拷贝到用户的地址空间。用户即可对文件进行读写操作。
若是写操作,写完之后会通过将cpu修改后的文件从用户缓冲区拷贝回内核缓冲区,再拷贝回硬件保存。
图中等待数据准备即等到找到文件所在区域即返回文件描述符。

下图为用户想访问磁盘文件顺序:
在这里插入图片描述

1.I\O操作是什么?

I\O模型是输入输出流。 不管socket、还是FIFO、管道、终端等,对于LInux来说、一切都是文件、一切都是流。 在信息交换的过程中、我们都是对这些流进行数据的收发操作、简称为I/O操作(input and output)、往流中读出数据系统调用read、写入数据系统调用write。

在计算机中有许许多多的流,因此在程序操作中,需要针对指定的流进行操作的时候,我们需要创建对应的文件描述符,即通常所说的fd(file descriptor)。对这个fd的操作、就是对这个文件(流)的操作,我们创建一个socket/读写一个文件,通过系统调用会返回一个文件描述符,那么接下来的操作就会转化为对这个描述符的操作,这就是IO操作。

2.文件描述符是什么?

文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux(一切皆文件)这样的操作系统。

在linux内核当中一个进程通过task_struct结构体描述,而打开的文件则用file结构体描述,打开文件的过程也就是对file结构体的初始化的过程。在打开文件的过程中会将inode部分关键信息填充到file中,特别是文件操作的函数指针。在task_struct中保存着一个file类型的数组,而用户态的文件描述符其实就是数组的下标。这样通过文件描述符就可以很容易到找到file,然后通过其中的函数指针访问数据。

例如我们以Ext2文件系统的写数据为例,在调用用户态的写数据接口的时候,需要传入文件描述符。内核根据文件描述符找到file,然后调用函数接口(file->f_op->write)文件磁盘数据。其中file结构体的f_op指针就是在打开文件的时候通过inode初始化的。

3.内核空间和用户空间是什么?

在这里插入图片描述
如图所示,Linux内核由系统内的所有进程共享,每个进程都有4G的虚拟地址空间。内核空间中存放的是内核代码和所有的内核模块以及内核所维护的数据。而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中。

整个linux内部结构可以分为三部分,从最底层到最上层依次是:硬件–>内核空间–>用户空间

用户运行一个程序,该程序创建的进程开始时运行自己的代码,处于用户态。如果要执行文件操作、网络数据发送等操作必须通过write、send等系统调用,这些系统调用会调用内核的代码。进程会切换到0级别,然后进入3G-4G中的内核地址空间去执行内核代码来完成相应的操作。内核态的进程执行完后又会切换到3级别,回到用户态。这样,用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。这说的保护模式是指通过内存页表操作等机制,保证进程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程地址空间中的数据。

4.I\O操作为什么需要调用内核?

Linux系统中内核权限最高为0级,用户权限低为3级。数据存放在硬件中,只有操作系统(内核)才有权限可以访问硬件,用户(应用程序)不能直接访问硬件,因此用户执行代码访问数据需要经过内核。

缓存I/O

缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

系统内核对磁盘的读写都会提供一个块缓冲,当用write函数对其写数据时,直接调用系统调用,将数据写入到块缓冲进行排队,当块缓冲达到一定的量时,才会把数据写入磁盘。因此所谓的不带缓冲的I/O是指进程不提供缓冲功能。每调用一次write或read函数,直接系统调用。
而带缓冲的I/O是指进程对输入输出流进行了改进,提供了一个流缓冲,当用fwrite函数网磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。
因此,带缓冲的I/O在往磁盘写入相同的数据量时,会比不带缓冲的I/O调用系统调用的次数要少。

用户空间没法直接访问内核空间的,内核态到用户态的数据拷贝 为什么存在缓存冲区?
因为频繁复制需要花费时间。

缓存I/O的优点:1)在一定程度上分离了内核空间和用户空间,保护系统本身的运行安全;2)可以减少读盘的次数,从而提高性能。

缓存I/O的缺点:数据在传输过程中需要在应用程序地址空间和缓存之间进行多次数据拷贝操作,这些数据拷贝操作所带来的CPU以及内存开销是非常大的。

参考文献

[1] IO模型前期准备理论
[2]用户态和内核态的理解和区别
[3]Linux虚拟文件系统(VFS)
[4]浅谈IO概念与IO模型
[5]Linux 虚拟文件系统(VFS)在文件系统中的作用
[6]I/O访问方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章