最近经常看到用户态以及内核态相关的内容,于是从这方面入手看了不少东西,现在简单地记录一下,如果有错误烦请指正。
什么是上下文切换?
在谈论什么是上下文切换前,我们先讨论一下什么是上下文。
上下文:进程的物理实体(代码和数据等)和支持运行的环境合称为进程的上下文。
直白的概括就是:当前进程运行的实体以及状态。
上下文切换:那么上下文切换也就呼之欲出了,上下文切换指的就是当前进程/线程在CPU中的运行暂时被中止,切换到另一个进程/线程的过程占用CPU,就称为上下文切换。
可见,上下文切换实际上是一个过程,它具体干了以下三件事:
1、当前进程时间片结束,系统保存其当前的运行状态。
2、加载新进程的运行状态
3、控制权转移到新进程。
这样就发生了一次上下文切换。在进程上下文切换中,由于每个进程都是独立空间,所以所有的系统依赖(如CPU 寄存器和程序计数器)都要进行切换;但是对于线程上下文切换来说,由于线程的大部分资源都是共享的,这些共享的资源就没必要动了,只需要对栈帧进行切换。个人认为这也是进程的切换资源消耗远大于线程的原因。
用户态和内核态
在Linux系统中,进程一般划分为4G的独立空间,其中高地址的1G左右的空间为内核空间,其余3G为用户空间。在这两种空间运行时的状态就被称为用户态和内核态。
内核态相对于用户态来说有着更高的权限,用户态不能直接访问内存等硬件设备,必须通过系统调用进入到内核中,才能访问这些特权资源。
而一次系统调用,往往需要从用户态->内核态,再从内核态->用户态,相当于要发生两次上下文的切换。
零拷贝
接下来我们谈谈零拷贝,先看一下普通的拷贝过程:
这是我在网上找的一个图,可以看到,如果我们想要将磁盘中的某一个文件拷贝到磁盘的另一处地方,实际上需要发生四次上下文切换、四次拷贝、四次用户态到内核态的切换。
零拷贝技术:是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域,这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。
说白了,零拷贝技术就是为了减少上下文切换、拷贝、用户态和内核态之间的切换的。
方法一:mmap
mmap主要实现方式是将内核读缓冲区的地址和用户缓冲区的地址进行映射,内核缓冲区和应用缓冲区共享,从而减少了从读缓冲区到用户缓冲区的一次CPU拷贝。
显然,该方法下不需要先将内核态的数据拷贝到用户区再拷贝到内核,二是直接建立了用户区与内核的映射(类似于共享内存?)内核态可直接将数据拷贝至另一个缓冲区(DMA控制器代替CPU去拷贝),相当于减少了一次拷贝。
方法二:sendfile
sendfile主要依赖于两个技术:
-
DMA 技术;
-
传递文件描述符代替数据拷贝。
DMA技术可以把它看成一个避免CPU参与拷贝的技术。对于sendfile来说,数据之间的拷贝可以直接在内核发生,相当于减少了两次用户态和内核态之间的切换。
可以看到在用户态发起sendfile请求后,内核态直接完成了拷贝过程再返回到用户态,该方式仅发生了2次用户态和内核态之间的上下文切换,3次拷贝。
其余的零拷贝技术还包括Direct i/o以及splice,这里不做过多介绍