zero_copy
基本概念
用户态与内核态
机器的资源是固定的,应用程序进程却有很多,如果无节制的使用资源会导致系统崩溃。所以必须要对进程使用何种资源进行限制,由此权限的不同可以分为用户态和内核态。处于内核态中的程序可以说为所欲为。内核态像外管理硬件资源,像内管理操作系统中的进程,内存等资源。用户态和内核态的划分可以表示为
系统调用
上面也提到了一个系统的资源是有限的,但是系统中却存在很多很多的进程,进程不可能无限制自由使用这些资源,所有会由操作系统来统一管理。应用程序进程要想使用资源,就必须通过操作系统提供的入口,这个入口及 System Call 系统调用。举个例子,copy 文件就可能会用到 sys_read 系统调用。系统调用是运行于内核态的。
库函数
系统调用本身过于底层,所以封装了一些逻辑作为上层的库函数调用
Shell
外壳,什么的外壳,内核的外壳,一种比较特殊的应用程序,通过 shell 我们可以直接和操作系统进行交互。
用户态到内核态是如何切换的
上面提到了进程请求资源需要系统调用,而系统调用运行在内核态,所以可以说系统调用使得我们由用户态切换到内核态。异常和外设中断也会使得我们切换到内核态。系统调用是一种软件中断。内核态完成后,相应进程会在切换回用户态。简单来说用户态切换到内核态的方式有3种
- System Call
- Exception
- Interrupt
DMA
Direct Memory Access ,直接内存访问,一种常见的内存访问方式,允许部分硬件子系统直接读写系统内存,而不需要 CPU 的介入
用户进程缓冲区
用户进程要想访问系统资源,就要系统调用,切换到内核态,调用返回后还要切换回用户态,上下文切换耗费了大量的 CPU 资源,因此可以分配一个用户进程 buffer,就比如读取文件时,可以分配一个 buffer,每次 read 系统调用读到的数据都会放到 buffer 里面,供后面的程序使用,不用每次涉及到的 read 都调用。由此可见用户进程缓冲区的作用就是减少系统调用的次数,从而减少 CPU 消耗
内核缓冲区
当用户进程读取文件时,一般不会直接读取文件,而是先问一下内核缓冲区,我有的东西你有木有,如果有的话,直接复制,如果没有的话就把进程挂起,去处理其他的事情。等到数据准备完毕,在通知相应的进程
套接字缓冲区
数据在 write/send 的时候,不会立即发送数据,而是先放到套接字缓冲区里面。关闭套接字会丢失套接字输入缓冲区中的内容,输出缓冲区中的内容会继续发送
一般的 远程文件 copy(从磁盘读取文件的时候是以DMA方式)
上图最左侧,共经历了4次copy,4次上下文切换,数据的每次 copy 都涉及到了 CPU,4次 CPU 拷贝
- 用户从磁盘读取文件内容,触发 sys_read 调用,由用户态切换为内核态,经历了一次上下文切换。文件被copy 到内核缓冲区
- read 调用返回 ,数据从内核缓冲区 copy 到 用户缓冲区,又一次上下文切换(内核态切换为用户态)
- 触发 sys_send 系统调用,数据从用户缓冲区 copy 到内核套接字缓冲区,用户态切换为内核态,一次上下文切换
- send 调用返回,触发一次上下文切换,数据被copy 目标缓冲区
中间是一种 “zero copy”,共经历了3次copy,2次上下文切换,1次 CPU 拷贝
- 用户从磁盘读取文件内容,文件被 copy 到读取缓冲区,同时触发 sys_read ,sys_send 系统调用,2次上下文切换,数据直接被 copy 到 相应的套接字缓冲区
- 数据被copy 目标缓冲区
zero copy
上图最右侧,数据只经历了1次copy,0次上下文切换也就是0次 CPU 拷贝
- 用户读取文件,放到内核缓冲区,文件描述符(记录数据的位置信息等元数据)被发往套接字缓冲区,直接从本机内存读取数据发送到远程机器
- 零拷贝中的零是指涉及到的 CPU 拷贝,即用户态切换到内核态的上下文切换