可爱的 Netty 之开篇
欢迎你和我一起学习 Netty
这款网络框架,之所以将这个系列的博文标题起名为《可爱的Netty》是希望大家面对 网络层的知识不要恐惧,保持一种好奇、探索的思路来学习他们,我会带着大家追随问题的本质,同时也需要动手实践去写一些代码。 目前我还未将所有的目录划分出来,因为这个系列不仅仅在讲 Netty
同时也在恶补我们计算机网络的知识点, 我尽量在每个章节控制一个合理的知识范围让各位不会感到枯燥,后期我会专门将整个系列做一个目录集合提供阅读。
这个系列都有什么内容?
在开始之前我们先向各位看官公布一下这里会讲什么内容?你可以学到什么?
Netty
是一个服务端网络层的框架,我们会结合 Unix
网络模型,Java NIO
来讲解Netty的使用和设计以及应用, 在前面的章节我们会花点时间来复习理论基础,然后来学习 Netty
中的一些核心概念, 最终带领大家一起编写很多应用型的示例,包括IM服务器、Web服务器、RPC服务器等企业中常用的技术。
所有的源码都会托管在 Github 平台上,你可以在 这里 进行查看,如果觉得不错可以点个 star
, 当然学习的过程中最重要的并不是代码,实践却总能得出真知,希望你同时做到。
我们开始吧
这是《可爱的Netty》的第一章,首先来复习一下计算机网络的一些基本概念,知道这些会让你对网络编程的种种现象会有更清晰的认识, 因为不论在学习任何技能的时候都会有一个基础点,我们无法罗列所有的概念,会挑出一些重点供大家学习。
名词解释
文件描述符 File Description
文件描述符(file descriptor,简称fd)在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
在 Linux
中,内核将所有的外部设备都当做一个文件来进行操作,而对一个文件的读写操作会调用内核提供的系统命令,返回一个 fd
,对一个 socket
的读写也会有相应的描述符,称为 socketfd
(socket 描述符),实际上描述符就是一个数字,它指向内核中的一个结构体(文件路径、数据区等一些属性)。
用户空间与内核空间、内核态与用户态
现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核,保证内核的安全,操作系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对 linux 操作系统而言(以32位操作系统为例)
- 将最高的
1G
字节(从虚拟地址0xC0000000
到0xFFFFFFFF
),供内核使用,称为内核空间; - 将较低的
3G
字节(从虚拟地址0x00000000
到0xBFFFFFFF
),供各个进程使用,称为用户空间。
每个进程可以通过系统调用进入内核,因此,Linux 内核由系统内的所有进程共享。于是,从具体进程的角度来看,每个进程可以拥有 4G 字节的虚拟空间。
- 当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈;
- 当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。此时处理器在特权级最低的(3级)用户代码中运行。当正在执行用户程序而突然被中断程序中断时,此时用户程序也可以象征性地称为处于进程的内核态。因为中断处理程序将使用当前进程的内核栈。
上下文切换
当一个进程在执行时,CPU
的所有寄存器中的值、进程的状态以及堆栈中的内容被称为该进程的上下文。
当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的上下文,以便在再次执行该进程时,能够必得到切换时的状态执行下去。在 Linux
中,当前进程上下文均保存在进程的任务数据结构中。在发生中断时,内核就在被中断进程的上下文中,在内核态下执行中断服务例程。但同时会保留所有需要用到的资源,以便中继服务结束时能恢复被中断进程的执行。
同步与异步
同步和异步关注的是消息通信机制,所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。
典型的异步编程模型比如 Node.js
。
举个通俗的例子:你打电话问书店老板有没有《如何与傻逼相处》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。 阻塞调用是指调用结果返回之前,当前线程会被挂起。 调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。 还是上面的例子,你打电话问书店老板有没有《如何与傻逼相处》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
其实本节什么也没讲,但是你可能要花一些时间来理解上面的词汇,关于同步、异步这块的理解我们会在下一个章节中来学习 Unix 网络IO模型 帮助你更好的理解。
参考资料