漫谈网络I/O——前置概念

114 阅读5分钟

概述

说到网络通信就不得不提TCP/IP协议,但笔者并不打算仔细的去谈论TCP/IP协议。但是笔者仍然建议大家好好学习TCP/IP,虽然TCP/IP已经存在了几十年。但是它的设计思想在如今看来仍然十分优秀(比如分层设计思想),以至于几十年过去它仍然互联网最重要的基石之一。虽然通信相关应用层在不断变化,但是底层一直都是TCP/IP那一套,这就是不变的东西,才是值得去学习和研究的东西。接下来笔者打算从TCP/IP进一步的抽象Socket讲起。

Ⓜ️ I/O模型

在真正开始讲解I/O模型之前我们需要引入几个新的概念:Socket中断DMA用户态和内核态

Socket

为了便于后续代码的理解,这里先引入Socket的概念后面我们大量使用。

网络套接字(英语:Network socket;又译网络套接字网络接口网络插槽)在计算机科学中是电脑网络中进程间资料流的端点。使用以网际协议(Internet Protocol)为通信基础的网络套接字,称为网际套接字(Internet socket)。因为网际协议的流行,现代绝大多数的网络套接字,都是属于网际套接字。socket是一种操作系统提供的进程间通信机制。在操作系统中,通常会为应用程序提供一组应用程序接口(API),称为套接字接口(英语:socket API)。应用程序可以通过套接字接口,来使用网络套接字,以进行资料交换。最早的套接字接口来自于4.2 BSD,因此现代常见的套接字接口大多源自Berkeley套接字(Berkeley sockets)标准。在套接字接口中,以IP地址端口组成套接字地址(socket address)。远程的套接字地址,以及本地的套接字地址完成连线后,再加上使用的协议(protocol),这个五元组(five-element tuple),作为套接字对(socket pairs),之后就可以彼此交换资料。例如,在同一台计算机上,TCP协议与UDP协议可以同时使用相同的port而互不干扰。 操作系统根据套接字地址,可以决定应该将资料送达特定的行程线程。这就像是电话系统中,以电话号码加上分机号码,来决定通话对象一般。

简单的说Socket(针对网络套接字)就是简化了TCP/IP使用,让我们可以以一种简单的方式去使用TCP/IP进行网络通信。

中断

粗略的来说中断就是打断你现在做的事然后去做另一件事,日常生活中中断非常常见。比如你在玩游戏的时候你对象给你发条消息,你就要抽空敷衍一下然后再继续游戏。计算机的中断大家这么去类比即可,这里不做过多详细的解释。此外大家也要知道中断也是一种资源,CPU响应中断就意味着这段时间CPU去做其他事情了,而此时肯定存在另外一个事情被暂时搁置等中断处理完再回来。所以中断不能太多,中断程序尽量简短。

DMA

DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。

用户态和内核态

这里笔者稍微解释一下什么是用户态,什么又是内核态,以及用户态到内核态的切换

用户态:

在CPU的设计中,用户态指非特权状态。在此状态下,执行的代码被硬件限定,不能进行某些操作,比如写入其他进程的存储空间,以防止给操作系统带来安全隐患。在操作系统的设计中,用户态也类似,指非特权的执行状态。内核禁止此状态下的代码进行潜在危险的操作,比如写入系统配置文件、杀掉其他用户的进程、重启系统等。

内核态:

内核态是操作系统的概念,CPU没有内核态一说。CPU有各种特权级,表示CPU的状态,一般来说操作系统内核态会在CPU的高特权级下执行。

内核态和用户态的切换:

用户态和内核态切换的开销大,但是它的开销大在那里呢?简单点来说有下面几点

  • 保留用户态现场(上下文、寄存器、用户栈等)
  • 复制用户态参数,用户栈切到内核栈,进入内核态
  • 额外的检查(因为内核代码对用户不信任)
  • 执行内核态代码
  • 复制内核态代码执行结果,回到用户态
  • 恢复用户态现场(上下文、寄存器、用户栈等)

实际上操作系统会比上述的更复杂,这里只是个大概,我们可以发现一次切换经历了「用户态 -> 内核态 -> 用户态」。

用户态要主动切换到内核态,那必须要有入口才行,实际上内核态是提供了统一的入口,下面是Linux整体架构图

image.png