网络编程常见名词介绍

这篇文章总结了网络编程常见名词汇总,是博主自己的理解,如果和你的有出入,一切以你为准,博主欢迎交流。

BIO、NIO、AIO

这三个名词是网络编程中编程方式对名词,代表的是不同网络编程思想。

BIO 是阻塞 IO,也就是在内核读取数据阻塞用户线程,用户线程从内核态拷贝数据也会阻塞线程。对于这种编程方式,相当于所有读写操作都做了串行化。

NIO 是非阻塞 IO,当执行 IO 函数时用户线程不必等待内核态加载数据。

AIO 时异步 IO,用户态程序执行 IO 函数,当用户接收到读写完成的通知,它就可以用数据来,注意此时没有内核态到用户态的数据拷贝。

内核态/用户态

为了保护操作系统执行程序时的安全性,操作系统执行环境分为用户态和内核态。

所有系统级别的操作都是在内核态执行的。常见的系统级别的操作有:线程上下文切换、IO、虚拟内存管理等。

Unix IO

大名顶顶 Unix 或类 Unix 操作系统内核实现的 IO 函数。分为五类:

  • 阻塞 IO
  • 非阻塞 IO
  • 多路 IO 复用
  • 信号驱动 IO
  • 异步 IO

要理解 Unix IO 就要先知道 IO 的加载拷贝过程。大致可分为加载阶段、拷贝阶段。

已读取磁盘上的一个文件为例,假如读取 A 文件,第一个阶段,操作系统接收到读取信号,然后加载磁盘上的 A 文件,加载完毕到第二个阶段,用户线程从内核态拷贝 A 文件,读取完毕算完成了一次 IO。

在说 Unix IO 时经常也听到同步阻塞同步非阻塞…,那么其中的“同步”、“阻塞”、“非阻塞”是什么意思?

阻塞、非阻塞指的是在调用读取函数时,用户线程是否等待用户内核加载完毕数据,这个时间节点在加载完毕,从加载到这个时间点,如果用户线程等待就是“阻塞”,反之就是“非阻塞”。

同步、非同步指的是在内核加载完毕数据,用户线程从内核拷贝数据这个期间是否等待,等待就是“同步”,不等待就是“非同步”。不过,从内核态拷贝数据是内存间的数据拷贝,是非常快的,能达到磁盘读取的几百倍,时间几乎可以忽略。

到现在,当提到“同步非阻塞 IO”时,应改理解它节省了当前线程等待内核加载文件的时间,然而从内核拷贝文件仍然需要当前线程进行。

多路复用

多路复用指的是三个网络 IO 函数:select、poll、epoll。

多路复用理解是节省了用户线程等待内核准备数据的阶段,在网络 IO 中是等待读信号、写信号。

三者的区别:

select、poll 比较相似,都是通过一个 O(n)的遍历操作获取关系的文件描述符(fd,整型值,用于标识文件),不同的是,select 有文件描述符数量的限制,为 1024,超出就会报错,poll 则取消了这个限制。

epoll,于 Linux 2.5.44 首度登场,它设计目的旨在取代既有POSIX selectpoll系统函数,让需要大量操作文件描述符的程序得以发挥更优异的性能(举例来说:旧有的系统函数所花费的时间复杂度为 O(n),epoll的时间复杂度 O(log n))。epoll 实现的功能与 poll 类似,都是监听多个文件描述符上的事件。

epoll 水平触发和边缘触发,当检测 epoll 文件描述符时,有读或写事件就绪通知,水平触发时,会通知多次直到用户处理了事件,而边缘触发则仅通知一次。

参考:Epoll

零拷贝

网络 IO 中,除了内核态加载数据的时间开销,还有内核态到用户态间数据拷贝开销,另外用户线程读取内核态数据还伴有上下文切换到开销。为了优化这块的性能,人们想出了在内核态直接发送接收数据,这样用户态就不必拷贝数据了,节省了拷贝开销。Linux 中 sendfile 函数就是零拷贝操作。

内存映射

内存映射(Memory Map),是另一种解决从内核态向用户态拷贝数据开销的方法,从名字可以看出它是一种映射技术,是利用一块用户态的非堆内存与内核态加载好的数据映射,实现直接读取数据,避免了拷贝开销。

直接内存

这是独属于 Java 中的名词。在 JVM 中,直接内存属于非堆内存,非堆内存是 Java 程序不可见的内存,但很巧妙地通过 JVM 进行了管理,Java 中是通过“虚引用”类进行控制的。

直接内存相较于堆内存效率提升在了哪里呢?我们都知道,JVM 像一个沙盒,隔离了 Java 程序与操作系统,也就是说当 JVM 加载完数据此时对 Java 程序还不可见,只有再次拷贝到 Java 堆中才可见,这样一来,加载文件的数据就成了:操作系统内核加载磁盘文件->拷贝到用户态(JVM 可见)->拷贝到 Java 堆中(Java 可见),此时我们发现已经到用户态的数据被多拷贝了一次,为了避免这一次拷贝,Java 引入了直接内存来使用 JVM 可见的这块内存。