TCP/IP的底层队列

  • 时间:
  • 浏览:9

1) 当网卡接收报文如果 判断为TCP协议时,经过层层调用,最终会调用到内核的tcp_v4_rcv辦法 。因为 当前TCP要接收的下有有另有一个 报文正是S1,好多好多 tcp_v4_rcv函数将其直接加入到receive队列中。receive队列是将因为 接收到的TCP报文,去除了TCP头部、排好序上放的、用户多多多线程 能不到直接按序读取的队列。因为 socket不出用户多多多线程 上下文中(也好多好多 不到用户多多多线程 在读socket),如果 大家需要S1序号的报文,而恰好收到了S1报文,如果 ,它进入了receive队列。

上图是TCP接收报文场景一的示意图。操作系统首先接收报文,存储到socket的receive队列,如果 用户多多多线程 再调用recv进行读取。

8) 此时,receive队列因为 有有一个 报文了,将第有有另有一个 报文拷贝到用户态内存中,因为 第五步中socket的参数并不到带MSG_PEEK,好多好多 将第有有另有一个 报文从队列中移除,从内核态释放掉。反之,MSG_PEEK标志位会因为 receive队列太大删除报文。好多好多 ,MSG_PEEK主要用于多多多多线程 读取同一套接字的情况报告。

 大家接下来就以三张图为主,介绍TCP接收报文时的某种场景,并在其中介绍有一个 接收相关的队列。

 我自己比较了解Java语言,对Java网络编程的理解就止于Netty框架的使用。Netty的源码贡献者Norman Maurer对于Netty网络开发有过一句建议,"Never block the event loop, reduce context-swtiching"。也好多好多 尽量无须阻塞IO多多线程 ,也尽量减少多多线程 切换。大家今天只关注前半句,对这句话感兴趣的同学能不到看一下蚂蚁通信框架实践。

 在第有有另有一个 场景中,系统参数tcp_low_latency为1,socket上设置了SO_RCVLOWAT属性值。服务器先收到报文S1,如果 其长度小于SO_RCVLOWAT。用户多多多线程 调用recv辦法 读取,难能可贵读取到了一主次,如果 不到到达最小阈值,好多好多 多多多线程 睡眠了。与此并肩,在睡眠前接收的乱序的报文S3直接进入backlog队列。如果 ,报文S2到达,因为 不到使用prequeue队列(因为 设置了tcp_low_latency),而它起始序号正是下有有另有一个 待拷贝的值,好多好多 直接拷贝到用户内存中,总共拷贝字节数已满足SO_RCVLOWAT的要求!最后在返回用户前把backlog队列中S3报文也拷贝给用户。

1) 接收到报文S1,正是准备接收的报文序号,如果 ,将它直接加入到有序的receive队列中。

2) 将系统属性tcp_low_latency设置为1,表明服务器希望多多多线程 太大 及时的接收到TCP报文。用户调用的recv接收阻塞socket上的报文,该socket的SO_RCVLOWAT值大于第有有另有一个 报文的大小,如果 用户分配了足够大的长度为len的内存。

3) 调用tcp_recvmsg辦法 来完成接收工作,先锁住socket。

4) 准备避免内核各个接收队列中的报文。

5) receive队列包含报文能不到直接拷贝,其大小小于len,直接拷贝到用户内存。

6) 在进行第五步的并肩,内核又接收到S3报文,此时socket被锁,报文直接进入backlog队列。有些报文并算是有序的。

7) 在第五步时,拷贝报文S1到用户内存,它的大小小于SO_RCVLOWAT的值。因为 socket是阻塞型,好多好多 用户多多多线程 进入睡眠情况报告。进入睡眠前,会先避免backlog队列的报文。因为 S3报文是失序的,好多好多 进入out_of_order 队列。用户多多多线程 进入休眠情况报告前如果 先避免一下backlog队列。

8) 多多多线程 休眠,直到超时因为 receive队列不为空。

9) 内核接收到报文S2。注意,此时因为 打开了tcp_low_latency标志位,好多好多 报文是太大进入prequeue队列等待歌曲多多多线程 避免。

10) 因为 报文S2正是要接收的报文,并肩,有有另有一个 用户多多多线程 在休眠等待歌曲该报文,好多好多 直接将报文S2拷贝到用户内存。

11) 每避免完有有另有一个 有序报文后,无论是拷贝到receive队列还是直接复制到用户内存,如果 检查out_of_order队列,看看是算是报文能不到避免。报文S3拷贝到用户内存,如果 唤醒用户多多多线程 。

12) 唤醒用户多多多线程 。

13) 此如果 检查已拷贝的字节数算是大于SO_RCVLOWAT,以及backlog队列算是为空。两者皆满足,准备返回。

12) 检查backlog队列,backlog队列是用户多多多线程 正在拷贝数据时,网卡收到的报文会进有些队列。因为 此时backlog队列有数据,就顺带避免下。backlog队列是不到数据的,如果 释放锁,准备返回用户态。

 为哪些不到阻塞读取网络信息的IO多多线程 呢?这里就要从经典的网络C10K如果 刚开使理解,服务器咋样支持并发1万请求。C10K的根源在于网络的IO模型。Linux 中网络避免都用同步阻塞的辦法 ,也好多好多 每个请求都分配有有另有一个 多多多线程 因为 多多线程 ,不到要支持1万并发,难道就要使用1万个多多线程 避免请求嘛?这1万个多多线程 的调度、上下文切换乃至它们占用的内存,如果 成为瓶颈。避免C10K的通用辦法 好多好多 使用I/O 多路复用,Netty好多好多 好多好多 。

7) tcp_recvmsg辦法 会首先锁住socket。socket是能不到被多多多线程 使用的,如果 操作系统也会使用,好多好多 需要避免并发问题报告 。要操控socket,就先获取锁。

4) 因为 此时receive,prequeuebacklog队列都为空,好多好多 不到拷贝有有另有一个 字节到用户内存中。而socket的配置要求相当于拷贝SO_RCVLOWAT也好多好多 1字节的报文,如果 进入阻塞式套接字的等待歌曲流程。最长等待歌曲时间为SO_RCVTIMEO指定的时间。socket在进入等待歌曲如果 释放socket锁,会使第五步中,新来的报文不再不到进入backlog队列。

5) 接到S1报文,将其加入prequeue队列中。

6) 插入到prequeue队列后,会唤醒在socket上休眠的多多多线程 。

7) 用户多多多线程 被唤醒后,重新获取socket锁,此后再接收到的报文不到进入backlog队列。

8) 多多多线程 先检查receive队列,当然仍然是空的;再去检查prequeue队列,发现有报文S1,正好是正等待歌曲歌曲序号的报文,于是直接从prequeue队列中拷贝到用户内存,再释放内核中的有些报文。

9) 目前因为 拷贝了有有另有一个 字节的报文到用户内存,检查有些长度算是超过了最低阈值,也好多好多 len和SO_RCVLOWAT的最小值。

10) 因为 SO_RCVLOWAT使用了默认值1,拷贝字节数大于最低阈值,准备返回用户态,顺便会查看一下backlog队列中是算是数据,此时不到,好多好多 准备放回,释放socket锁。

11) 返回用户因为 拷贝的字节数。

 自从上次学习了TCP/IP的拥塞控制算法后,我越发太大更加深入的了解TCP/IP的有些底层原理,搜索了好多好多 网络上的资料,看了了陶辉大神关于高性能网络编程的专栏,收益颇多。今天就总结一下,如果 加在自己的有些思考。

 应用多多多线程 接收TCP报文和多多多线程 所在服务器系统接收网络里发来的TCP报文是有有另有一个 独立流程。二者如果 操控socket实例,如果 会通过锁竞争来决定某一时刻由谁来操控,由此产生好多好多 不同的场景。例如,应用多多多线程 正在接收报文时,操作系统通过网卡又接收到报文,这时该咋样避免?若应用多多多线程 不到调用read因为 recv读取报文时,操作系统收到报文又会咋样避免?

4) 每次向receive队列插入报文时如果 检查out_of_order队列,因为 接收到S2报文后,期望的的序号为S3,好多好多 out_of_order队列中的S3报文会被移到receive队列。

 相比于建立连接,TCP在接收报文时的避免逻辑更为错综复杂,相关的队列和涉及的配置参数更多。

 TCP/IP为什么么就不到多队列啊?今天大家就来细看一下TCP/IP的几次队列,包括建立连接时的半连接队列(sync),全连接队列(accept)和接收报文时的receive、out_of_order、prequeue以及backlog队列。

 如上图所示,这里有有有另有一个 队列:syns queue(半连接队列)和accept queue(全连接队列)。三次握背后,服务端接收到客户端的SYN报文后,把相关信息上放半连接队列中,并肩回复SYN+ACK给客户端。

 第三步的事先服务端收到客户端的ACK,因为 这时全连接队列没满,不到从半连接队列搞懂相关信息上装在全连接队列中,如果 按tcp_abort_on_overflow的值来执行相关操作,直接抛下因为 过一段时间在重试。

6) 调用tcp_recvmsg辦法

13) 用户多多多线程 代码如果 刚开使执行,此时recv等辦法 返回的好多好多 从内核拷贝的字节数。

 Netty有负责服务端监听建立连接的多多线程 组(mainReactor)和负责连接读写操作的IO多多线程 组(subReactor),还能不到有专门避免业务逻辑的Worker多多线程 组(ThreadPool)。三者相互独立,好多好多 有好多好多 好处。一是有专门的多多线程 组负责监听和避免网络连接的建立,能不到避免TCP/IP的半连接队列(sync)和全连接队列(acceptable)被占满。二是IO多多线程 组和Worker多多线程 分开,双方并行避免网络I/O和业务逻辑,能不到避免IO多多线程 被阻塞,避免TCP/IP的接收报文的队列被占满。当然,因为 业务逻辑较少,也好多好多 IO 密集型的轻计算业务,能不到将业务逻辑上放IO多多线程 中避免,避免多多线程 切换,这也好多好多 Norman Maurer话的后半主次。

3) 接着,收到了TCP期望的S2报文,直接进入recevie队列。因为 此时out_of_order队列不为空,需要检查一下。

2) 接收到S3报文,因为 TCP要接收的下有有另有一个 报文序号是S2,好多好多 加入到out_of_order队列,所有乱序的报文会上放这里。

 其中1,2,3步骤的避免和事先一样。大家直接从第四步如果 刚开使。

 第二张图给出了第有一个 场景,这里涉及了prequeue队列。用户多多多线程 调用recv辦法 时,socket队列中不到任何报文,而socket是阻塞的,好多好多 多多多线程 睡眠了。如果 操作系统收到了报文,此时prequeue队列如果 刚开使产生作用。该场景中,tcp_low_latency为默认的0,套接字socket的SO_RCVLOWAT是默认的1,仍然是阻塞socket,如下图。

9) 拷贝第有一个 报文,当然,执行拷贝前如果 检查用户态内存的剩余空间算是足以放下当前有些报文,严重不足如果 直接返回因为 拷贝的字节数。

10) 拷贝第有有另有一个 报文。

11) receive队列因为 为空,此如果 检查SO_RCVLOWAT有些最小阈值。因为 因为 拷贝字节数小于它,多多多线程 会休眠,等待歌曲更多报文。默认的SO_RCVLOWAT值为1,也好多好多 读取到报文就能不到返回。

 总结一下有一个 队列的作用。

5) 用户多多多线程 如果 刚开使读取socket,先在多多多线程 中分配一块内存,如果 调用read因为 recv辦法 。socket有一系列的具有默认值的配置属性,比如socket默认是阻塞式的,它的SO_RCVLOWAT属性值默认为1。当然,recv好多好多 的辦法 如果 接收有有另有一个 flag参数,它能不到设置为MSG_WAITALLMSG_PEEKMSG_TRUNK等等,这里大家假定为最常用的0。多多多线程 调用了recv辦法 。