【操作系统】文件 I/O

【操作系统】文件 I/O

⽂件的读写⽅式各有千秋,对于⽂件的 I/O 分类也⾮常多,常见的有:

  • 缓冲与⾮缓冲 I/O
  • 直接与⾮直接 I/O
  • 阻塞与⾮阻塞 I/O VS 同步与异步 I/O

一、缓冲与非缓冲 I/O

⽂件操作的标准库是可以实现数据的缓存,那么根据「是否利⽤标准库缓冲」,可以把⽂件 I/O 分为缓冲I/O 和⾮缓冲 I/O:

  • 缓冲 I/O,利⽤的是标准库的缓存实现⽂件的加速访问,⽽标准库再通过系统调⽤访问⽂件。
  • ⾮缓冲 I/O,直接通过系统调⽤访问⽂件,不经过标准库缓存。

这⾥所说的「缓冲」特指标准库内部实现的缓冲。

比如说,很多程序遇到换⾏时才真正输出,⽽换⾏前的内容,其实就是被标准库暂时缓存了起来,这样做的⽬的是,减少系统调⽤的次数,毕竟系统调⽤是有 CPU 上下⽂切换的开销的。

二、直接与非直接 I/O

磁盘 I/O 是⾮常慢的,所以 Linux 内核为了减少磁盘 I/O 次数,在系统调⽤后,会把⽤户数据
拷⻉到内核中缓存起来,这个内核缓存空间也就是「⻚缓存」,只有当缓存满⾜某些条件的时候,才发起磁盘 I/O 的请求。

那么,根据「是否利⽤操作系统的缓存」,可以把⽂件 I/O 分为直接 I/O 与⾮直接 I/O:

  • 直接 I/O,不会发⽣内核缓存和⽤户程序之间数据复制,⽽是直接经过⽂件系统访问磁盘。
  • ⾮直接 I/O,读操作时,数据从内核缓存中拷⻉给⽤户程序,写操作时,数据从⽤户程序拷⻉给内核缓存,再由内核决定什么时候写⼊数据到磁盘。

如果你在使⽤⽂件操作类的系统调⽤函数时,指定了 O_DIRECT 标志,则表示使⽤直接 I/O。如果没有设置过,默认使⽤的是⾮直接 I/O。

如果⽤了⾮直接 I/O 进⾏写数据操作,内核什么情况下才会把缓存数据写⼊到磁盘?

以下⼏种场景会触发内核缓存的数据写⼊磁盘:

  • 在调⽤ write 的最后,当发现内核缓存的数据太多的时候,内核会把数据写到磁盘上;
  • ⽤户主动调⽤ sync ,内核缓存会刷到磁盘上;
  • 当内存⼗分紧张,⽆法再分配⻚⾯时,也会把内核缓存的数据刷到磁盘上;
  • 内核缓存的数据的缓存时间超过某个时间时,也会把数据刷到磁盘上;

三、阻塞与非阻塞 I/O VS 同步与异步 I/O

先来看看阻塞 I/O,当⽤户程序执⾏ read ,线程会被阻塞,⼀直等到内核数据准备好,并把数据从内核缓冲区拷⻉到应⽤程序的缓冲区中,当拷⻉过程完成, read 才会返回。

【注意】阻塞等待的是「内核数据准备好」和「数据从内核态拷⻉到⽤户态」这两个过程。

过程如下图:

在这里插入图片描述

知道了阻塞 I/O ,来看看⾮阻塞 I/O,⾮阻塞的 read 请求在数据未准备好的情况下⽴即返回,可以继续往下执⾏,此时应⽤程序不断轮询内核,直到数据准备好,内核将数据拷⻉到应⽤程序缓冲区, read 调⽤才可以获取到结果。过程如下图:

在这里插入图片描述

【注意】这⾥最后⼀次 read 调⽤,获取数据的过程,是⼀个同步的过程,是需要等待的过程。这⾥的同步指的是内核态的数据拷⻉到⽤户程序的缓存区这个过程。

举个例⼦,访问管道或 socket 时,如果设置了 O_NONBLOCK 标志,那么就表示使⽤的是⾮阻塞 I/O 的⽅式访问,⽽不做任何设置的话,默认是阻塞 I/O。

应⽤程序每次轮询内核的 I/O 是否准备好,感觉有点傻乎乎,因为轮询的过程中,应⽤程序啥也做不了,只是在循环。

为了解决这种傻乎乎轮询⽅式,于是 I/O 多路复⽤技术就出来了,如 select、poll,它是通过 I/O 事件分发,当内核数据准备好时,再以事件通知应⽤程序进⾏操作。

这个做法⼤⼤改善了应⽤进程对 CPU 的利⽤率,在没有被通知的情况下,应⽤进程可以使⽤ CPU 做其他的事情。

下图是使⽤ select I/O 多路复⽤过程。注意, read 获取数据的过程(数据从内核态拷⻉到⽤户态的过程),也是⼀个同步的过程,需要等待:

在这里插入图片描述

实际上,⽆论是阻塞 I/O、⾮阻塞 I/O,还是基于⾮阻塞 I/O 的多路复⽤都是同步调⽤。因为它们在 read调⽤时,内核将数据从内核空间拷⻉到应⽤程序空间,过程都是需要等待的,也就是说这个过程是同步的,如果内核实现的拷⻉效率不⾼,read 调⽤就会在这个同步过程中等待⽐较⻓的时间。

真正的异步 I/O 是「内核数据准备好」和「数据从内核态拷⻉到⽤户态」这两个过程都不⽤等待。

当我们发起 aio_read 之后,就⽴即返回,内核⾃动将数据从内核空间拷⻉到应⽤程序空间,这个拷⻉过程同样是异步的,内核⾃动完成的,和前⾯的同步操作不⼀样,应⽤程序并不需要主动发起拷⻉动作。过程如下图:

在这里插入图片描述

四、总结

在这里插入图片描述

I/O 是分为两个过程的:

  • 数据准备的过程
  1. 数据从内核空间拷⻉到⽤户进程缓冲区的过程

阻塞 I/O 会阻塞在「过程 1 」和「过程 2」,⽽⾮阻塞 I/O 和基于⾮阻塞 I/O 的多路复⽤只会阻塞在「过程2」,所以这三个都可以认为是同步 I/O。

异步 I/O 则不同,「过程 1 」和「过程 2 」都不会阻塞。

学自小林coding所著的《图解系统》,侵删

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值