wordpress站内信插件,网站视频无法播放怎么办,网络广告营销的特性,泰安人才招聘网最新招聘2023Linux内核中连接的组织形式与全连接队列 文章目录 1.前言2.Linux内核中连接的组织形式2.1套接字和文件描述符2.2创建连接 获取连接 3.全连接队列3.1为什么有全连接队列#xff1f;3.2全连接队列的长度 1.前言
TCP是面向连接的#xff0c;TCP的各种可靠性机制实际都不… Linux内核中连接的组织形式与全连接队列 文章目录 1.前言2.Linux内核中连接的组织形式2.1套接字和文件描述符2.2创建连接 获取连接 3.全连接队列3.1为什么有全连接队列3.2全连接队列的长度 1.前言
TCP是面向连接的TCP的各种可靠性机制实际都不是从主机到主机的而是基于连接的。
比如一台服务器启动后可能有多个客户端前来访问如果TCP不是基于连接的也就意味着服务器端只有一个接收缓冲区此时各个客户端发来的数据都会拷贝到这个接收缓冲区当中此时这些数据就可能会互相干扰。
而我们在进行TCP通信之前需要先建立连接就是因为TCP的各种可靠性保证都是基于连接的要保证传输数据的可靠性的前提就是先建立好连接。
而一台机器上可能会存在大量的连接此时操作系统就不得不对这些连接进行管理。
操作系统在管理这些连接时需要“先描述再组织”在操作系统中一定有一个描述连接的结构体该结构体当中包含了连接的各种属性字段所有定义出来的连接结构体最终都会以某种数据结构组织起来此时操作系统对连接的管理就变成了对该数据结构的增删查改。建立连接实际就是在操作系统中用该结构体定义一个结构体变量然后填充连接的各种属性字段最后将其插入到管理连接的数据结构当中即可。断开连接实际就是将某个连接从管理连接的数据结构当中删除释放该连接曾经占用的各种资源。
以上都是理论层次的理解那我们今天就具体地探究Linux源码中连接是如何组织起来的
2.Linux内核中连接的组织形式
2.1套接字和文件描述符
网络通信本质上也是IO的过程而且之前我们也说过调用send、recv等函数本质上是向Tcp维护的发送缓冲区、接收缓冲区写入和读出数据既然是IO操作所以一个套接字的本质其实就是一个文件描述符对应的文件。Linux下一切皆文件。
一个服务器本质上就是一个进程而进程到文件的关系我们早已经在系统部分学习过 当我们创建套接字时Linux系统还会为我们创建一个新的结构体对象struct socket 我们观察到这个结构体内部包含了一个struct file类型的指针该指针指向的就是该套接字对应的文件对象所以我们此时可以通过该套接字找到对应的文件了但是更重要的是我们需要通过文件描述符找到对应的套接字呢呀现在只有一个单向的指针即我现在需要从文件找到对应的套接字对象。
所以在struct file结构体中还包含一个指针 这个指针指向的就是套接字socket结构所以我们现在就可以通过该文件描述符完成对套接字的操作了读取数据、获取连接等。
2.2创建连接 获取连接
我们上面提到过连接本质上是内核中的一种数据结构在Linux中实际就是struct tcp_sock结构体该结构体专门用于TCP协议。
它包含了TCP协议特有的字段和方法如TCP头部长度tcp_header_len、滑动窗口rcv_wnd、snd_wnd、拥塞控制算法相关字段如srtt_us、mdev_us等以及发送和接收队列等。
这个结构体是TCP连接在内核中的完整表示包含了TCP协议运行所需的所有状态信息和控制逻辑。
struct tcp_sock结构体的第一个字段是struct inet_connection_sock结构体该结构体增加了与连接管理相关的字段如连接状态icsk_state、重传机制等。这个结构体为TCP连接提供了必要的状态管理和控制机制。
struct inet_connection_sock结构体的第一个字段是struct inet_sock结构体该结构体增加了与IP层相关的字段和方法如IP地址sin_addr或sin6_addr、端口号sin_port或sin6_port等。这个结构体为TCP和UDP等基于IP的协议提供了更具体的支持。
struct inet_sock结构体的第一个字段是struct sock结构体该结构体包含了如套接字状态state、接收和发送缓冲区sk_buff队列、定时器timer等通用字段。
更重要的是你会发现与文件描述符相关的struct socket结构体中有一个字段就是struct sock类型的指针 sk 所以socket套接字可以通过这个 sk指针 获取tcp_sock结构体中的所有字段内容通过类型转换。 比如想要获取tcp_sock结构体中inet_connection_sock结构体中的字段内容就可以将sk指针转换成 inet_connection_sock类型获取。 这种通过一个指针获取不同结构体中属性的方式被称为“C风格的多态”。 以上是Tcp连接如果是Udp连接呢我们说Udp是无连接的通信协议所以对于Udp来说没有struct inet_connection_sock结构体因为该结构体内部维护的是与连接管理的相关字段但是同样的根据socket结构体中的 sk指针 指向udp_sock结构体来获取udp连接的各种属性内容。所以 该socket结构体 被称为 “BSD socket ”— 通用socket接口。
既然socket结构体既可以指向Tcp套接字又可以指向Udp套接字那是如何区分不同套接字类型的呢
int socket(int domain, int type, int protocol);参数type对应着socket结构体中的type字段 所以创建一个listen套接字的流程就是申请文件描述符获得文件结构体创建套接字socket和连接tcp_sock然后将他们关联起来。
那么调用accept()函数是从listen套接字监听的套接字中获取普通连接并返回这个过程又是怎样的呢
实际上在struct inet_connection_sock结构体中维护一个全连接队列当经历过三次握手后系统会自动创建一个连接tcp_sock然后将该连接加入到全连接队列中当调用accept()函数时操作系统会申请新的文件描述符和套接字socket然后从全连接队列中取出一个连接tcp_sock之后普通套接字socket中的 sk指针 指向该连接tcp_sock就完成了获取连接的操作。
3.全连接队列
实际TCP在进行连接管理时会用到两个连接队列
全连接队列accept队列。全连接队列用于保存处于ESTABLISHED状态但没有被上层调用accept取走的连接。半连接队列。半连接队列用于保存处于SYN_SENT和SYN_RCVD状态的连接也就是还未完成三次握手的连接维护时间比较短。
而全连接队列的长度实际会受到listen第二个参数的影响一般TCP全连接队列的长度就等于listen第二个参数backlog的值加一。
int listen(int sockfd, int backlog);如果将listen的第二个参数值设置为3此时服务器端最多就允许存在4个处于ESTABLISHED状态的连接。
在服务器端已经有4个ESTABLISHED状态的连接的情况下再有客户端发来建立连接请求此时服务器端就会新增状态为SYN_RCVD的连接该连接实际就是放在半连接队列当中的。
3.1为什么有全连接队列
一般当服务器压力较大时连接队列的作用才会体现出来如果服务器压力本身就不大那么一旦底层有连接建立成功上层就会立马将该连接读走并进行处理。
服务器端启动时一般会预先创建多个服务线程为客户端提供服务主线程从底层accept上来连接后就可以将其交给这些服务线程进行处理。
如果向服务器发起连接请求的客户端很少那么连接一旦在底层建立好就被主线程立马accept上来并交给服务线程处理了。
但如果向服务器发起连接请求的客户端非常多并且业务处理非常繁忙即当每个服务线程都在为某个连接提供服务时底层再建立好连接主线程就不能获取上来了此时底层这些已经建立好的连接就会被放到连接队列当中只有等某个服务线程空闲时主线程就会从这个连接队列当中获取建立好的连接。
如果没有这个连接队列那么当服务器端的服务线程都在提供服务时其他客户端发来的连接请求就会直接被拒绝。
但有可能正当这个连接请求被拒绝时某个服务线程提供服务完毕此时这个服务线程就无法立马得到一个连接为之提供服务所以一定有一段时间内这个服务线程是处于闲置状态的直到再有客户端发来连接请求。
而如果设置了连接队列当某个服务线程提供完服务后如果连接队列当中有建立好的连接那么主线程就可以立马从连接队列当中获取一个连接交给该服务线程进行处理此时就可以保证服务器几乎是满载工作的降低了服务器的闲置率。
3.2全连接队列的长度
虽然维护连接队列能让服务器处于几乎满载工作的状态但连接队列也不能设置得太长。
如果队列太长也就意味着在队列较尾部的连接需要等待较长时间才能得到服务此时客户端的请求也就迟迟得不到响应。此外服务器维护连接也是需要成本的连接队列设置的越长系统就要花费越多的成本去维护这个队列。但与其与其维护一个长连接造成客户端等待过久并且占用大量暂时用不到的资源还不如将部分资源节省出来给服务器使用让服务器更快的为客户端提供服务。
所以全连接队列要取一个合适的长度系统一般设置为5。 全连接队列的长度minbacklognet.core.somaxconn1 用户层调用listen时传入的第二个参数backlog。系统变量net.core.somaxconn在 Linux 系统中这个值默认可能因不同的发行版和内核版本而异但常见的默认值可能是 128。然而对于高负载的服务器特别是在处理大量并发连接时这个默认值可能太低导致新的连接被拒绝因为监听队列已满。 通过以下命令可以查看系统变量net.core.somaxconn的值。
sudo sysctl -a | grep net.core.somaxconnStay hungry, Stay foolish. —史蒂夫-乔布斯