早期的总线系统
为了解决通信的问题、主板上铺设了一条公共线路、各个设备都连接到这条线路上、不管谁要和谁通信、都能使用它来传输、这条线路就是总线。
总线上有CPU、内存、鼠标、键盘、硬盘、网卡、声卡、显卡等…
说是一条总线、实际上是包含了传输数据的数据总线、传输地址都地址总线、进行控制管理的控制总线
因为这条线路是共用的、所以大家不能一起用、不然就乱套了、为了统一管理、还专门安排了一个叫作总线控制的芯片、由它来统一管理总线、大家要通信就找它申请、这就叫做总线仲裁。
有些 IO 设备很慢、长时间占用总线,但是因为 CPU 和内存是需要频繁大量的通信的、所以这个时候矛盾就非常明显了。
那怎么办?
后面将这个总线控制的芯片一分为二。
北桥
北桥芯片集成了内存控制器、总线控制器、图形控制器、CPU 访问内存和显卡都需要经过它。
南桥
南桥芯片集成了各种 IO 设备的控制器、负责和这些IO设备连接。
IO 设备之前速度差异也是很大的、而去它们之间的接口还都不一样、所以南桥对接他们的时候区分了不同的总线,像 PCU 总线、USB 总线、SATA 总线。
它俩一个负责连接高速设备、一个负责低速设备、加上 CPU 。这三个芯片成为主板上的三个重要芯片
消失的北桥
后面 CPU 这玩意将内存控制器、图形控制器都集成到 CPU 芯片内、所以北桥北干掉了。
没有了北桥、也就没有南桥的说法了、整个主办就只有一个桥了…
其他设备如何和 CPU 通信
IO 设备、比如键盘、鼠标、磁盘等想跟 CPU 通信、因为它们实在是太慢了、所以想了一个叫中断的方式来跟 CPU 通信。
这些外部设备想跟 CPU 进行通信、需要先向 CPU 发一个中断信号,CPU 每执行一条指令的时候就会看看这个信号量,一旦发现有中断信号、就将手上的活放下、去处理这个中断。
当然 CPU 如果正在处理一些重要的事情、那么它会设置一个标志值、代表不允许被打断,这个时候即使有中断信号、CPU 也不会去管。
当然还有一种 VIP 的中断信号、CPU 无论怎么样都去处理、比如电源断电、温度过高等。
如果 CPU 去处理中断、那么它需要将手上的活当前的信息保存下来、也就是保存上下文。会将上下文保存到线程的栈里面。
中断亲和性
线程的 CPU 亲和性。 CPU 上面有自己的缓存、如果经常换 CPU 、那么缓存就会失去意义
所以提出一个中断亲和性、可以指定哪些核来处理。
CPU 把数据搬运的工作 “外包” 出去
CPU 工作的时候需要通过总线和主板上的一些设备进行通信、进行数据传输。比如网卡、硬盘这些设备。
和这些外部设备通信、是通过 IO 端口进行的。CPU 提供了 in/out 两条指令。
通过执行这两条执行。可以对它们进行读写数据。这种通信方式叫做可编程输入输出(PIO)
随着主板上的新设备的介入、越来越多的程序需要我们去执行、工作量实在太大了。并且 CPU 和硬盘的读写速度差距越来越大。这种通信方式太浪费 CPU 了。
DMA 技术
没过多久、成立了一个外包团队、专门负责处理这个事情。它们和 CPU 一样、它们也有几个寄存器、传输数据的时候、只需要设置这些寄存器的内容、告诉它们要传输哪里的数据、从哪里到哪里、传输的长度是多少。接下来的事情就不用 CPU 操心了。等数据传输完了
DMA (Direct Memory Access) 直接内存访问
DMAC (Direct Memory Access Controller) DMA 控制器
DMA 全面推广
除了硬盘、主板上的网卡、显示器、摄像头纷纷集成了 DMAC。CPU 得到彻底的解放、不用再做枯燥的搬运工。
零拷贝技术
DMA 是用于 IO 设备与内存之间搬运数据。CPU 现在干的任务是内存之间的拷贝工作。这DMA控制器也帮不上忙、还得 CPU 亲自动手拷贝
从内核空间搬运到用户空间、一会又从用户空间搬回到内核空间。
程序这么写的
File.read(file,buff,len)
Socket.send(socket,buff,len)
数据最终从硬盘到网卡、有四次数据传输
1、硬盘到读缓存、DMA 拷贝
2、读缓存到用户空间的缓存 CPU 拷贝
3、用户空间的读缓存到 socket 缓存拷贝
4、socket 缓存到网卡 DMA 拷贝
Linux 推出了一个新的 API、sendfile
sendfile(out_fd,in_fd,offset,size)
只需要制定打开文件的描述符和要发送的网络接口描述符、就能直接把文件通过网络发出去。
直接把从硬盘读取到数据缓冲区的地址和长度给到网络 socket 描述符
还把这一技术推广到了文件数据拷贝上、增加了 splice 的 api
splice(fd_in,offset_in,fd_out,offset_out,length
)
网卡是如何工作的
集线器时代
以前网络中的各个计算机都是通过一个叫集线器的家伙相连的、通过集线器、我们可以在物理上构成一个星型网络、还起了一个名字、叫以太网。传输速度可以做到 10Mb/s
集线器不管数据是发给谁的、它只是一个没有感情的转发机器、工作在物理层、把收到的信号做一个增强处理后就一股脑地转发给所有端口。
这样一来、我们在逻辑上就变成了一个总线型网络。总线属于公共资源、由所有连接在上面的主机共享。有人传输数据的时候其他人就要等着、不然数据就会发生冲突、出现乱套了。
为了让大家能和平共处、不必为了争抢线路发生不愉快、制定了一套规则 CSMA/CD
每次发送数据之前、都要先监听线路是否空闲。如果别人在传输数据、那我就要等待、至于等待多久、没人知道、这个是随机值。
等到空闲的时候、我就可以发送数据、不过一边发送、我还得一边检测是否有冲突发生、因为说不定有别人和我一样以为现在空闲都在发送数据。
所以这就是 CSMA/CD 载波侦听多路访问/冲突检测名字的由来。
但是如果数据长度太短、我很快就发送完了、结果先头部队还在路上、这之后再遇到冲突那我也发现不了、为了应对这种情况、我们也需要在极端的情况下也能够检查出来。
我们这个网络能够支持最远的距离是2500m、极端的情况下、到达最远端的时候冲突才发生、冲突信号要赶在我发送完最后一比特之前传回来、这一来一回就是 5000m
线路上跑个来回需要57.6微秒、传输速度是 10Mb/s 一个来回时间我就能发送 576比特也就是 72字节B。抛开8字节的帧前导符和开始符、剩下的以太网帧不能低于 64 字节。这样就算在最远两端发生了碰撞冲突都能及时传递回去被检测到。
数据收发的过程
把操作系统给的数据按照以太网的格式、把数据封装称一个个以太网帧发出去
帧的头部有收件人和发件人的地址、我们叫它为MAC地址、这是我们每个网卡的身份证号码、在我们出生的那一刻就确定了。
发件人是我的 MAC地址、但是收件人地址我不知道、操作系统协议栈部门交给我们的数据包直邮IP地址、我们又不认识这个、我们收发数据帧只用 MAC 地址。
为了解决这个问题、我们制定了一套协议、APR、地址解析协议。来实现这两个地址转换。
在不知道IP对应的 MAC 地址时、发送一个广播、这个广播的发件人填我、然后收件人填 FF:FF:FF:FF:FF:FF 这是一个特殊的 MAC 地址、我们约定好每个人收到广播都要接收而不能丢弃
这个广播里面填了 IP 地址、谁收到以后发现和自己匹配上都要来应答我。这样我就能知道对方的MAC 地址。
不过这样做的风险、要是有人冒充真正的收件人给我回信、我也没办法分辨、这个叫 ARP 欺诈。
网卡时如何抓到别人的通信数据的?
因为集线器是闭着眼睛到处转发的、所以不管谁发的数据、所有人都可以看到。所以总线中每天都有大量数据在流动。但我通常都不会全部都抓下来交给 CPU 处理、不然 CPU 就会忙坏了。所以每次网卡拿到数据帧都要看看是不是发给我的、如果不是就丢弃、当然广播消息除外。
网卡提供咯一种混杂模式、在这种模式下、网卡就会把总线中看到的所有数据帧都全部抓下来交给 CPU 去处理、一些抓包软件才会这么做。
交换机时代
集线器退休后、即使开启混杂模式、网卡也不能抓取到别人的数据包了。
交换机不是简单的将大家连接在一起、它用一张表将大家的 MAC 地址和连接的端口记录下来、每次收到数据后、只转发给对应的端口、不会像集线器那样到处转发。
交换机不用到处转发数据占用线路、相当于把冲突域进行隔离、我连接的线路上只有我自己的数据、没有其他人的数据了。
我们连接的网线也进行了升级、可以全双工通信、一边收一边发、不用和交换机发给我的下行数据发生冲突。
不存在冲突了、所以不再用 CSMA/CD 协议了
网卡收到数据包之后会做啥
我是一个数据包、我在千里之外的一台计算机上被创建、经过一系列路由转发、终于来到了这里
在计算机的世界里面、一切都是 0 和 1、实际上我是一串二进制比特流。人类利用光、电、磁等物理信号来存储和传输我们。
这些物理信号在传输过程中免不了受到一些干扰、导致其中的一些比特位从1 变成0、或者从 0 变成 1.
为了知道我们的内容有没有被破坏、在我们的尾部有一个帧检验序列FCS、这是一个循环冗余码检验、网卡收到后会重新计算我们的 CRC、和这个值一比较、就知道数据出没出错。
检查完 FCS 还没完、网卡又取出我最外层的以太网帧格式中的目的 MAC 地址、和自己的 MAC 地址进行比较、看看是不是发给他的。一般情况下、网卡只处理收件人是自己或者广播的数据包、除了这两种情况、其他数据包都会被丢掉。
DMA 数据传输
检查完之后、网卡将我放置到网卡内部的接收队列缓冲区。依赖 DMA 将我们传输到内存。
网卡会给 CPU 发送中断信号、告诉 CPU 已经把数据搬到内存了、他直接去处理即可
软中断
在内存中有一个网卡的接收队列、所有的数据都会放到这个队列中。
网卡给 CPU 发送的中断是硬中断、硬中断的响应需要快速完成、而数据包的处理是个很费时间的过程、没办法在硬中断函数中完成、所以 CPU在英中断函数中创建了一个软中断、所以过不了多久、CPU 就会在软中断中的处理函数中去处理这些数据包。
https://book.douban.com/subject/36428782/