操作系统-虚拟文件系统
虚拟文件系统
为什么需要虚拟文件系统?
应用程序是需要与io设备打交道,io设备主要分为块设备和字符设备,块设备为了以目录结构持久化保存数据,通常还需要文件系统来管理,为了兼容各种设备的文件系统,需要一层VFS(虚拟文件系统)通过file_operations数据结构的成员函数来与它下层的文件系统、设备文件进行交互file_operations数据结构的成员函数包含对文件的打开、关闭、读写、控制等功能。

虚拟文件系统结构
VFS 定义了一组所有文件系统都支持的数据结构和标准接口,这样程序员不需要了解文件系统的工作原理,只需要了解 VFS 提供的统一接口即可。
在 Linux 文件系统中,用户空间、系统调用、虚拟机文件系统、缓存、文件系统以及存储之间的关系如下图:

Linux 支持的文件系统也不少,根据存储位置的不同,可以把文件系统分为三类:
- 磁盘的文件系统,它是直接把数据存储在磁盘中,比如 Ext 2/3/4、XFS 等都是这类文件系统。
- 内存的文件系统,这类文件系统的数据不是存储在硬盘的,而是占用内存空间,我们经常用到的 /proc 和 /sys 文件系统都属于这一类,读写这类文件,实际上是读写内核中相关的数据。
- 网络的文件系统,用来访问其他计算机主机数据的文件系统,比如 NFS、SMB 等等。
文件的使用
我们从用户角度来看文件的话,就是我们要怎么使用文件?首先,我们得通过系统调用来打开一个文件。

fd = open(name, flag); # 打开文件
...
write(fd,...); # 写数据
...
close(fd); # 关闭文件
上面简单的代码是读取一个文件的过程:
- 首先用 open 系统调用打开文件,open 的参数中包含文件的路径名和文件名。
- 使用 write 写数据,其中 write 使用 open 所返回的文件描述符,并不使用文件名作为参数。
- 使用完文件后,要用 close 系统调用关闭文件,避免资源的泄露。
文件描述符fd
我们打开了一个文件后,操作系统会跟踪进程打开的所有文件,所谓的跟踪呢,就是操作系统为每个进程维护一个打开文件表,文件表里的每一项代表「文件描述符」,所以说文件描述符是打开文件的标识。操作系统在打开文件表中维护着打开文件的状态和信息。
文件系统的基本操作单位是数据块
用户和操作系统对文件的读写操作是有差异的,用户习惯以字节的方式读写文件,而操作系统则是以数据块来读写文件,那屏蔽掉这种差异的工作就是文件系统了。
我们来分别看一下,读文件和写文件的过程:
- 当用户进程从文件读取 1 个字节大小的数据时,文件系统则需要获取字节所在的数据块,再返回数据块对应的用户进程所需的数据部分。
- 当用户进程把 1 个字节大小的数据写进文件时,文件系统则找到需要写入数据的数据块的位置,然后修改数据块中对应的部分,最后再把数据块写回磁盘。
所以,文件系统把多个扇区组成了一个逻辑块,每次读写的最小单位就是逻辑块(数据块),Linux 中的逻辑块大小为 4KB,也就是一次性读写 8 个扇区,这将大大提高了磁盘的读写的效率。
IO多路复用与epoll原理探究
五大IO模型
服务器端编程经常需要构造高性能的IO模型,常见的IO模型有五种:
-
阻塞IO
等待「内核数据准备好」和「数据从内核态拷贝到用户态」,直到完成。用户在发起IO请求后,不能做任何事情,对CPU的资源利用率不够。
-
非阻塞IO
判断「内核数据准备好」,没有立即返回,准备好则继续执行「数据从内核态拷贝到用户态」后再返回。但是为了判断内核数据是否准备好,仍需要不断地轮询、重复请求,消耗了大量的CPU的资源。一般很少直接使用这种模型。
-
IO多路复用
主要作用是可以避免同步非阻塞IO模型中轮询等待的问题,可达到在同一个线程内同时处理多个IO请求的目的
-
信号驱动IO:l
inux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO事件就绪,进程收到SIGIO信号。然后处理IO事件。
-
异步IO:
linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
实际上,无论是阻塞 I/O、非阻塞 I/O,还是基于非阻塞 I/O 的多路复用都是同步调用。因为它们在 read 调用时,内核将数据从内核空间拷贝到应用程序空间,过程都是需要等待的,也就是说这个过程是同步的,如果内核实现的拷贝效率不高,read 调用就会在这个同步过程中等待比较长的时间。(需要等待内核拷贝数据到用户缓存的过程)。
IO复用技术
-
I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(读就绪或写就绪),就通知程序进行相应的读写操作。
select函数
-
原理
-
以数组形式的监听文件描述符集并拷贝到内核中,分别监听读、写、异常动作。
-
内核态采用轮询方式,遍历查询所有fd的状态
-
返回所有fd,用户需要轮询所有fd,判断其触发事件。
-
-
优势
其最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。
-
缺点
- select通过线性表描述文件描述符集合,文件描述符有上限,一般是1024,每次调用select,都需要把fd_set集合从用户态拷贝到内核态,如果fd_set集合很大时,那这个开销也很大,比如百万连接却只有少数活跃连接时这样做就太没有效率。
- 需要遍历select返回结果,来判断监听事件的发生。
poll函数
poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,poll是链表结构来描述文件描述符,突破了文件描述符上限,最大可以打开文件的数目。
epoll函数
- 原理
- 监听文件描述符集合,并以红黑树形式放在内核里保存,减少了监听文件描述符在内核和用户态之间拷贝带来的开销,并且没有数量限制。
- epoll是基于事件机制的,每个fd的回调函数(中断函数),将发生事件描述符返回用于存储准备就绪的链表。所以在内核态不用轮询查询文件描述符集合的状态。
- 将就绪链表中文件描述符返回给数组,用户层不用轮询遍历。
应用场景分析
- 当监测的fd数目较小,且各个fd都比较活跃,建议使用select或者poll。
- 当监测的fd数目非常大,成千上万,且单位时间只有其中的一部分fd处于就绪状态,这个时候使用epoll能够明显提升性能。
-
ET、LT、EPOLLONESHOT模式
参考博客 https://www.cnblogs.com/my_life/articles/10910375.html
-
LT水平触发模式
-
- 若数据可读,epoll返回可读事件
- 若开发者没有把数据完全读完,epoll会不断通知数据可读,直到数据全部被读取。
- 若socket可写,epoll返回可写事件,而且是只要socket发送缓冲区未满,就一直通知可写事件。
- 优点是对于read操作比较简单,只要有read事件就读,读多读少都可以。
- 缺点是write相关操作较复杂,由于socket在空闲状态时发送缓冲区一定是不满的,故若socket一直在epoll wait列表中,则epoll会一直通知write事件,所以必须保证没有数据要发送的时候,要把socket的write事件从epoll wait列表中删除。而在需要的时候在加入回去,这就是LT模式的最复杂部分。
-
ET模式的特点是:
- 若socket可读,返回可读事件
- 若开发者没有把所有数据读取完毕,epoll不会再次通知epoll read事件,也就是说存在一种隐患,如果开发者在读到可读事件时,如果没有全部读取所有数据,那么可能导致epoll在也不会通知该socket的read事件。(其实这个问题并没有听上去难,参见下文)。
- 若发送缓冲区未满,epoll通知write事件,直到开发者填满发送缓冲区,epoll才会在下次发送缓冲区由满变成未满时通知write事件。 【应该:剩余可写缓冲区的大小高于低水平位时,通知可写】
- ET模式下,只有socket的状态发生变化时才会通知,也就是读取缓冲区由无数据到有数据时通知read事件,发送缓冲区由满变成未满通知write事件。
- 缺点是epoll read事件触发时,必须保证socket的读取缓冲区数据全部读完(事实上这个要求很容易达到)
- 优点:对于write事件,发送缓冲区由满到未满时才会通知【不会一直通知】,若无数据可写,忽略该事件,若有数据可写,直接写。Socket的write事件可以一直放在epoll的wait列表。Man epoll中我们知道,当向socket写数据,返回的值小于传入的buffer大小或者write系统调用返回EWouldBlock时,表示发送缓冲区已满。
-
EPOLLONESHO
- 一个线程读取某个socket上的数据后开始处理数据,在处理过程中该socket上又有新数据可读,此时另一个线程被唤醒读取,此时出现两个线程处理同一个socket
- 我们期望的是一个socket连接在任一时刻都只被一个线程处理,通过epoll_ctl对该文件描述符注册epolloneshot事件,一个线程处理socket时,其他线程将无法处理,当该线程处理完后,需要通过epoll_ctl重置epolloneshot事件
应用场景分析
- 当监测的fd数目较小,且各个fd都比较活跃,建议使用select或者poll
- 当监测的fd数目非常大,成千上万,且单位时间只有其中的一部分fd处于就绪状态,这个时候使用epoll能够明显提升性能。

浙公网安备 33010602011771号