原来 8 张图,就可以搞懂「零拷贝」了

原来 8 张图,就可以搞懂「零拷贝」了

前言 磁盘可以说是计算机系统最慢的硬件之一,读写速度相差内存 10 倍以上,所以针对优化磁盘的技术非常的多,比如零拷贝、直接 I/O、异步 I/O 等等,这些优化的目的就是为了提高系统的吞吐量,另外操作系统内核中的磁盘高速缓存区,可以有效的减少磁盘的访问次数。 这次,我们就以「文件传输」作为切入点,来分析 I/O 工作方式,以及如何优化传输文件的性能。 正文 为什么要有 DMA 技术? 在没有 DMA 技术前,I/O 的过程是这样的: CPU 发出对应的指令给磁盘控制器,然后返回; 磁盘控制器收到指令后,于是就开始准备数据,会把数据放入到磁盘控制器的内部缓冲区中,然后产生一个中断; CPU 收到中断信号后,停下手头的工作,接着把磁盘控制器的缓冲区的数据一次一个字节地读进自己的寄存器,然后再把寄存器里的数据写入到内存,而在数据传输的期间 CPU 是无法执行其他任务的。 为了方便你理解,我画了一副图: 可以看到,整个数据的传输过程,都要需要 CPU 亲自参与搬运数据的过程,而且这个过程,CPU 是不能做其他事情的。 简单的搬运几个字符数据那没问题,但是如果我们用千兆网卡或者硬盘传输大量数据的时候,都用 CPU 来搬运的话,肯定忙不过来。 计算机科学家们发现了事情的严重性后,于是就发明了 DMA 技术,也就是直接内存访问(Direct Memory Access) 技术。 什么是 DMA 技术?简单理解就是,在进行 I/O 设备和内存的数据传输的时候,数据搬运的工作全部交给 DMA 控制器,而 CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务。 那使用 DMA 控制器进行数据传输的过程究竟是什么样的呢?下面我们来具体看看。 具体过程: 用户进程调用 read 方法,向操作系统发出 I/O 请求,请求读取数据到自己的内存缓冲区中,进程进入阻塞状态; 操作系统收到请求后,进一步将 I/O 请求发送 DMA,然后让 CPU 执行其他任务; DMA 进一步将 I/O 请求发送给磁盘; 磁盘收到 DMA 的 I/O 请求,把数据从磁盘读取到磁盘控制器的缓冲区中,当磁盘控制器的缓冲区被读满后,向 DMA 发起中断信号,告知自己缓冲区已满; DMA 收到磁盘的信号,将磁盘控制器缓冲区中的数据拷贝到内核缓冲区中,此时不占用 CPU,CPU 可以执行其他任务; 当 DMA 读取了足够多的数据,就会发送中断信号给 CPU; CPU 收到 DMA 的信号,知道数据已经准备好,于是将数据从内核拷贝到用户空间,系统调用返回; 可以看到, 整个数据传输的过程,CPU 不再参与数据搬运的工作

计算机中内存、cache和寄存器之间的关系及区别

计算机中内存、cache和寄存器之间的关系及区别

最近在看一些高并发相关的内容,总感觉有些地方理解的不对,于是再去看看硬件架构方面的知识,补了补基础知识。 以下纯转载,这篇文章讲的挺好的。解决了我的很多疑惑。 寄存器是中央处理器内的组成部份。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和位址。在中央处理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序计数器(PC)。在中央处理器的算术及逻辑部件中,包含的寄存器有累加器(ACC)。 内存包含的范围非常广,一般分为只读存储器(ROM)、随机存储器(RAM)和高速缓存存储器(cache)。 寄存器是CPU内部的元件,寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快。 Cache :即高速缓冲存储器,是位于CPU与主内存间的一种容量较小但速度很高的存储器。由于CPU的速度远高于主内存,CPU直接从内存中存取数据要等待一定时间周期,Cache中保存着CPU刚用过或循环使用的一部分数据,当CPU再次使用该部分数据时可从Cache中直接调用,这样就减少了CPU的等待时间,提高了系统的效率。Cache又分为一级Cache(L1 Cache)和二级Cache(L2 Cache),L1 Cache集成在CPU内部,L2 Cache早期一般是焊在主板上,现在也都集成在CPU内部,常见的容量有256KB或512KB L2 Cache。 总结:大致来说数据是通过内存-Cache-寄存器,Cache缓存则是为了弥补CPU与内存之间运算速度的差异而设置的的部件。 首先看一下计算机的存储体系(Memory hierarchy)金字塔: 图片.png 其次我们看看一个计算机的存储体系 图片.png Register 寄存器是CPU的内部组成单元,是CPU运算时取指令和数据的地方,速度很快,寄存器可以用来暂存指令、数据和地址。在CPU中,通常有通用寄存器,如指令寄存器IR;特殊功能寄存器,如程序计数器PC、sp等。 Cache 缓存即就是用于暂时存放内存中的数据,若果寄存器要取内存中的一部分数据时,可直接从缓存中取到,这样可以调高速度。高速缓存是内存的部分拷贝。 CPU <--- > 寄存器<--- > 缓存<--- >内存 寄存器的工作方式很简单,只有两步:(1)找到相关的位,(2)读取这些位。 内存的工作方式就要复杂得多: (1)找到数据的指针。(指针可能存放在寄存器内,所以这一步就已经包括寄存器的全部工作了。) (2)将指针送往内存管理单元(MMU),由MMU将虚拟的内存地址翻译成实际的物理地址。 (3)将物理地址送往内存控制器(memory controller),由内存控制器找出该地址在哪一根内存插槽(bank)上。 (4)确定数据在哪

linux 系统 UDP 丢包问题分析思路

linux 系统 UDP 丢包问题分析思路

在开始之前,我们先用一张图解释 linux 系统接收网络报文的过程。 首先网络报文通过物理网线发送到网卡 网络驱动程序会把网络中的报文读出来放到 ring buffer 中,这个过程使用 DMA(Direct Memory Access),不需要 CPU 参与 内核从 ring buffer 中读取报文进行处理,执行 IP 和 TCP/UDP 层的逻辑,最后把报文放到应用程序的 socket buffer 中 应用程序从 socket buffer 中读取报文进行处理 在接收 UDP 报文的过程中,图中任何一个过程都可能会主动或者被动地把报文丢弃,因此丢包可能发生在网卡和驱动,也可能发生在系统和应用。 之所以没有分析发送数据流程,一是因为发送流程和接收类似,只是方向相反;另外发送流程报文丢失的概率比接收小,只有在应用程序发送的报文速率大于内核和网卡处理速率时才会发生。 本篇文章假定机器只有一个名字为 eth0 的 interface,如果有多个 interface 或者 interface 的名字不是 eth0,请按照实际情况进行分析。 NOTE:文中出现的 RX(receive) 表示接收报文,TX(transmit) 表示发送报文。 确认有 UDP 丢包发生 要查看网卡是否有丢包,可以使用 ethtool -S eth0 查看,在输出中查找 bad 或者 drop 对应的字段是否有数据,在正常情况下,这些字段对应的数字应该都是 0。如果看到对应的数字在不断增长,就说明网卡有丢包。 另外一个查看网卡丢包数据的命令是 ifconfig,它的输出中会有 RX(receive 接收报文)和 TX(transmit 发送报文)的统计数据: ~# ifconfig eth0 ... RX packets 3553389376 bytes 2599862532475 (2.3 TiB) RX errors 0 dropped 1353 overruns 0 frame 0 TX packets 3479495131 bytes 3205366800850 (2.9 TiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 ... 此外,linux 系统也提供了各个网络协议的丢包信息,可以使用 netstat -s 命令查看,加上 --udp 可以只看 UDP 相关的报文数据: [root@holodesk02 GOD]# netstat -s -u IcmpMsg: InType0: 3 InType3: 1719356 InType8: 13 InType11: 59 OutType0: 13 OutType3: 1737641 OutType8: 10 OutType11: 263 Udp: 517488890 packets received 2487375 packets to unknown port received. 47533568 packet receive errors 147264581 packets sent 12851135 receive buffer errors 0 send buffer errors UdpLite: IpExt:

面试官:Netty这些我必问

面试官:Netty这些我必问

Netty 最流行的 NIO 框架,由 JBOSS 提供的,整合了FTP,SMTP,HTTP协议 API 简单 成熟稳定 社区活跃· 经过大规模验证(互联网、大数据、网络游戏、电信通信) Elasticsearch、Hadoop 子项目 avro项目、阿里开源框架 Dubbo、使用 Netty BIO 优点:模型简单,编码简单 缺点:性能瓶颈,请求数和线程数 N:N 关系 高并发情况下 ,CPU 切换线程上下文损耗大 案例:Tomcat 7之前,都是 BIO,7 之后是 NIO 改进:伪 NIO,使用线程池去处理逻辑 IO 模式 同步阻塞:丢衣服->等洗衣机洗完->再去晾衣服 同步非阻塞:丢衣服->去做其他事情,定时去看衣服是否洗完->洗完后自己去晾衣服 异步非阻塞:丢衣服-> 去做其他事情不管了,衣服洗好会自动晾好,并且通知你晾好了 五种 I/O 模型 五种 I/O 模型: 阻塞 IO、非阻塞 IO、多路复用 IO、信号驱动 IO、异步 IO,前 4 种是同步 IO,在内核数据 copy 到用户空间时是阻塞的 阻塞 IO 非阻塞 IO IO 多路复用 核心:可以同时处理多个 connection,调用系统 select 和 recvfrom函数 每一个socket 设置为 non-blocking 阻塞是被 select 这个函数 block 而不是 socket阻塞 缺点:连接数不高的情况下,性能不一定比 多线程+ 阻塞 IO 好(多调用一个select 函数) 信号驱动 异步 IO 采用 Future-Listener机制 IO 操作分为 2 步: 发起 IO 请求,等待数据准备 实际的 IO 操作,将数据从内核拷贝到进程中 阻塞 IO、非阻塞 IO 区别在于发起 IO 请求是否被阻塞 同步 IO、异步 IO 在于实际的 IO 读写是否阻塞请求进程 阻塞非阻塞是线程的状态 同步和异步是消息的通知机制 同步需要主动读写数据,异步不需要主动读写数据 同步 IO 和异步 IO 是针对用户应用程序和内核的交互 IO 多路复用 I/O 是指网络 I /O ,多路指多个 TCP 连接,复用指一个或几个线程。 简单来说:就是使用一个或者几个线程处理多个 TCP 连接,最大优势是减少系统开销,不必创建过多的线程进程,也不必维护这些线程进程 select 文件描述符 writefds、readdfs、exceptfds 30w个连接会阻塞住,等数据可读、可写、出异常、或者超时返回 select 函数正常返回后,通过遍历 fdset整个数组才能发现哪些句柄发生了事件,来找到就绪的描述符fd,然后进行对应的 IO操作,几乎在所有的平台上支持,跨平台支持性好 缺点: select采用轮询的方式扫描文件描述符,全部扫描,随着文件描述符 FD 数量增多而性能下降。 每次调用 slect (),需要把 fd集合从用户态拷贝到内核态,并进行

十种JVM内存溢出的情况,你碰到过几种?

十种JVM内存溢出的情况,你碰到过几种?

导言: 对于java程序员来说,在虚拟机自动内存管理机制的帮助下,不需要自己实现释放内存,不容易出现内存泄漏和内存溢出的问题,由虚拟机管理内存这一切看起来非常美好,但是一旦出现内存溢出或者内存泄漏的问题,对于不熟悉jvm虚拟机是怎么使用内存的话,那么排查错误将会是一项非常艰巨的任务。所以在了解内存溢出之前先要搞明白JVM的内存模型。 JVM(Java虚拟机)是一个抽象的计算模型。就如同一台真实的机器,它有自己的指令集和执行引擎,可以在运行时操控内存区域。目的是为构建在其上运行的应用程序提供一个运行环境。JVM可以解读指令代码并与底层进行交互:包括操作系统平台和执行指令并管理资源的硬件体系结构。 JVM内存模型 根据 JVM8 规范,JVM 运行时内存共分为虚拟机栈、堆、元空间、程序计数器、本地方法栈五个部分。还有一部分内存叫直接内存,属于操作系统的本地内存,也是可以直接操作的。 元空间(Metaspace) 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。 2.虚拟机栈(JVM Stacks) 每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息。栈的大小可以固定也可以动态扩展。 本地方法栈(Native Method Stack) 与虚拟机栈类似,区别是虚拟机栈执行java方法,本地方法站执行native方法。在虚拟机规范中对本地方法栈中方法使用的语言、使用方法与数据结构没有强制规定,因此虚拟机可以自由实现它。 程序计数器(Program Counter Register) 程序计数器可以看成是当前线程所执行的字节码的行号指示器。在任何一个确定的时刻,一个处理器(对于多内核来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,我们称这类内存区域为“线程私有”内存。 5.堆内存(Heap) 堆内存是 JVM 所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象和数组都在堆上进行分配。这部分空间可通过 GC 进行回收。当申请不到空间时会抛出 OutOfMemoryError。堆是JVM内存占用最大,管理最复杂的一个区域。其唯一的用途就是存放对象实例:所有的对象实例及数组都在对上进行分配。jdk1.8后,字符串常量池从永久代中剥离出来,存放在队中。 6.

联系我们

联系电话

4000-640-466

联系邮箱

service@f-li.cn

办公地址

上海黄浦区外滩源1号

谢谢,您的信息已成功发送。
请填写信息。