通常程序执行 IO 操作时,需要涉及用户空间和内核空间的两个缓冲区。
只有内核才能跟磁盘等硬件进行操作,因此数据在流向程序时,必定会先存在于内核缓冲区,内核缓冲区又称为 PageCache,不同操作系统的 PageCache 机制不一样。
内核缓冲区:系统内核在内核空间中定义的内核缓冲区,即 PageCache。
用户缓冲区:通过 C 语言或其它语言提供的标准 IO 读写函数在用户空间定义的缓冲区。
从上图中可以看到,IO 操作有两种方式:
- 缓冲IO:同时使用用户缓冲区和内核缓冲区。需要 3 次数据复制以及 1 次上下文切换(用户态、内核态切换)。
- 直接IO:只使用内核缓冲区。需要 2 次数据复制和 1 次上下文切换。
明显,直接 IO 比缓冲 IO 少了一个缓冲区,也就是少了一个数据复制操作。
为了提高 IO 读写效率,操作系统提供了 mmap 和 sendfile 两个机制,帮助我们实现内存 ”零拷贝“,以及减少用户态和内核态上下文切换次数,不过要注意,它们的特点都是针对文件而言的。
mmap 是内存映射机制,直接将文件从内核缓冲区映射到应用程序内存地址空间。
文件从内核缓冲区中映射到应用程序内存中时,是逻辑地址。当程序操作这个逻辑地址,进行读写时,实际上是在内核缓冲区中操作,数据并不需要复制到应用程序的内存中或经过用户空间。
使用 mmap 后,仅内核缓冲区跟磁盘之间出现数据复制。但因为内核空间跟应用程序之间需要经过用户态和内核态的转换,一次还需要一次上下文切换。
sendfile 则是为 Socket 编程服务的。在网络通讯中,操作系统内核会为 Socket 提供一个缓冲区,要传输的网络数据要经过缓冲区。
使用 sendfile 处理文件,数据可以不经过用户空间,只需要通过内核空间完成操作,没有上下文切换。
在一些新版本的系统中,数据发送时可以不经过 Socket 缓冲区,减少一次数据复制。
使用 sendfile 有个缺点是,因为数据没有经过用户空间,因此应用程序不能读取或修改这些数据。
文章评论