一文读懂计算机内核态、用户态和零拷贝技术

存储介质的性能话不多说,先看一张图,下图左边是磁盘到内存的不同介质,右边形象地描述了每种介质的读写速率 。一句话总结就是越靠近cpu,读写性能越快 。了解了不同硬件介质的读写速率后,你会发现零拷贝技术是多么的香,对于追求极致性能的读写系统而言,掌握这个技术是多么的优秀~

一文读懂计算机内核态、用户态和零拷贝技术

文章插图
 
上图是当前主流存储介质的读写性能,从磁盘到内存、内存到缓存、缓存到寄存器,每上一个台阶,性能就提升10倍 。如果我们打开一个文件去读里面的内容,你会发现时间读取的时间是远大于磁盘提供的这个时延的,这是为什么呢?问题就在内核态和用户态这2个概念后面深藏的I/O逻辑作怪 。
内核态和用户态内核态:也称为内核空间 。cpu可以访问内存的所有数据,还控制着外围设备的访问,例如硬盘、网卡、鼠标、键盘等 。cpu也可以将自己从一个程序切换到另一个程序 。
用户态:也称为用户空间 。只能受限的访问内存地址,cpu资源可以被其他程序获取 。
一文读懂计算机内核态、用户态和零拷贝技术

文章插图
计算机资源的管控范围
坦白地说内核态就是一个高级管理员,它可以控制整个资源的权限,用户态就是一个业务,每个人都可以使用它 。那计算机为啥要这么分呢?且看下文......
由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络 。CPU划分出两个权限等级:用户态和内核态 。
32 位操作系统和 64 位操作系统的虚拟地址空间大小是不同的,在 linux 操作系统中,虚拟地址空间的内部又被分为内核空间和用户空间两部分,如下所示:
一文读懂计算机内核态、用户态和零拷贝技术

文章插图
 
通过这里可以看出:
  • 32 位系统的内核空间占用 1G,位于最高处,剩下的 3G 是用户空间;
  • 64 位系统的内核空间和用户空间都是 128T,分别占据整个内存空间的最高和最低处,剩下的中间部分是未定义的 。
内核态控制的是内核空间的资源管理,用户态访问的是用户空间内的资源 。
从用户态到内核态切换可以通过三种方式:
  1. 系统调用,其实系统调用本身就是中断,但是软件中断,跟硬中断不同 。
  2. 异常:如果当前进程运行在用户态,如果这个时候发生了异常事件,就会触发切换 。例如:缺页异常 。
  3. 外设中断:当外设完成用户的请求时,会向CPU发送中断信号 。
内核态和用户态是怎么控制数据传输的?举个例子:当计算机A上a进程要把一个文件传送到计算机B上的b进程空间里面去,它是怎么做的呢?在当前的计算机系统架构下,它的I/O路径如下图所示:
一文读懂计算机内核态、用户态和零拷贝技术

文章插图
 
  1. 计算机A的进程a先要通过系统调用Read(内核态)打开一个磁盘上的文件,这个时候就要把数据copy一次到内核态的PageCache中,进入了内核态;
  2. 进程a负责将数据从内核空间的 Page Cache 搬运到用户空间的缓冲区,进入用户态;
  3. 进程a负责将数据从用户空间的缓冲区搬运到内核空间的 Socket(资源由内核管控) 缓冲区中,进入内核态 。
  4. 进程a负责将数据从内核空间的 Socket 缓冲区搬运到的网络中,进入用户态;
从以上4个步骤我们可以发现,正是因为用户态没法控制磁盘和网络资源,所以需要来回的在内核态切换 。这样一个发送文件的过程就产生了4 次上下文切换:
  1. read 系统调用读磁盘上的文件时:用户态切换到内核态;
  2. read 系统调用完毕:内核态切换回用户态;
  3. write 系统调用写到socket时:用户态切换到内核态;
  4. write 系统调用完毕:内核态切换回用户态 。
如此笨拙的设计,我们觉得计算机是不是太幼稚了,为啥要来回切换不能直接在用户态做数据传输吗?
  1. CPU 全程负责内存内的数据拷贝,参考磁盘介质的读写性能,这个操作是可以接受的,但是如果要让内存的数据和磁盘来回拷贝,这个时间消耗就非常的难看,因为磁盘、网卡的速度远小于内存,内存又远远小于 CPU;
  2. 4 次 copy + 4 次上下文切换,代价太高 。
所以计算机体系结构的大佬们就想到了能不能单独地做一个模块来专职负责这个数据的传输,不因为占用cpu而降低系统的吞吐呢?方案就是引入了DMA(Direct memory access)


推荐阅读