linuxsocket优化提升网络速度

linuxsocket优化提升网络速度

2023年7月23日发(作者:)

linuxsocket优化提升⽹络速度在开发 socket 应⽤程序时,⾸要任务通常是确保可靠性并满⾜⼀些特定的需求。利⽤本⽂中给出的 4 个提⽰,您就可以从头开始为实现最佳性能来设计并开发 socket 程序。本⽂内容包括对于 Sockets API 的使⽤、两个可以提⾼性能的 socket 选项以及 GNU/Linux 优化。为了能够开发性能卓越的应⽤程序,请遵循以下技巧:最⼩化报⽂传输的延时。最⼩化系统调⽤的负载。为 Bandwidth Delay Product 调节 TCP 窗⼝。动态优化 GNU/Linux TCP/IP 栈。在通过 TCP socket 进⾏通信时,数据都拆分成了数据块,这样它们就可以封装到给定连接的 TCP payload(指 TCP 数据包中的有效负荷)中了。TCP payload 的⼤⼩取决于⼏个因素(例如最⼤报⽂长度和路径),但是这些因素在连接发起时都是已知的。为了达到最好的性能,我们的⽬标是使⽤尽可能多的可⽤数据来填充 每个报⽂。当没有⾜够的数据来填充 payload 时(也称为最⼤报⽂段长度(maximum segment size) 或 MSS),TCP 就会采⽤ Nagle 算法⾃动将⼀些⼩的缓冲区连接到⼀个报⽂段中。这样可以通过最⼩化所发送的报⽂的数量来提⾼应⽤程序的效率,并减轻整体的⽹络拥塞问题。尽管 John Nagle 的算法可以通过将这些数据连接成更⼤的报⽂来最⼩化所发送的报⽂的数量,但是有时您可能希望只发送⼀些较⼩的报⽂。⼀个简单的例⼦是 telnet 程序,它让⽤户可以与远程系统进⾏交互,这通常都是通过⼀个 shell 来进⾏的。如果⽤户被要求⽤发送报⽂之前输⼊的字符来填充某个报⽂段,那么这种⽅法就绝对不能满⾜我们的需要。另外⼀个例⼦是 HTTP 协议。通常,客户机浏览器会产⽣⼀个⼩请求(⼀条 HTTP 请求消息),然后 Web 服务器就会返回⼀个更⼤的响应(Web 页⾯)。您应该考虑的第⼀件事情是 Nagle 算法满⾜⼀种需求。由于这种算法对数据进⾏合并,试图构成⼀个完整的 TCP 报⽂段,因此它会引⼊⼀些延时。但是这种算法可以最⼩化在线路上发送的报⽂的数量,因此可以最⼩化⽹络拥塞的问题。但是在需要最⼩化传输延时的情况中,Sockets API 可以提供⼀种解决⽅案。要禁⽤ Nagle 算法,您可以设置 TCP_NODELAY socket选项,如清单 1 所⽰。int sock, flag, ret;/* Create new stream socket */sock = socket( AF_INET, SOCK_STREAM, 0 );/* Disable the Nagle (TCP No Delay) algorithm */flag = 1;ret = setsockopt( sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) );if (ret == -1) { printf("Couldn't setsockopt(TCP_NODELAY)n"); exit(-1);}

提⽰:使⽤ Samba 的实验表明,在从 Microsoft® Windows® 服务器上的 Samba 驱动器上读取数据时,禁⽤ Nagle 算法⼏乎可以加倍提⾼读性能。

任何时候通过⼀个 socket 来读写数据时,您都是在使⽤⼀个系统调⽤(system call)。这个调⽤(例如 read 或 write)跨越了⽤户空间应⽤程序与内核的边界。另外,在进⼊内核之前,您的调⽤会通过 C 库来进⼊内核中的⼀个通⽤函数(system_call())。从 system_call() 中,这个调⽤会进⼊⽂件系统层,内核会在这⼉确定正在处理的是哪种类型的设备。最后,调⽤会进⼊ socket 层,数据就是在这⾥进⾏读取或进⾏排队从⽽通过 socket 进⾏传输的(这涉及数据的副本)。这个过程说明系统调⽤不仅仅是在应⽤程序和内核中进⾏操作的,⽽且还要经过应⽤程序和内核中的很多层次。这个过程耗费的资源很⾼,因此调⽤次数越多,通过这个调⽤链进⾏的⼯作所需要的时间就越长,应⽤程序的性能也就越低。由于我们⽆法避免这些系统调⽤,因此惟⼀的选择是最⼩化使⽤这些调⽤的次数。幸运的是,我们可以对这个过程进⾏控制。在将数据写⼊⼀个 socket 时,尽量⼀次写⼊所有的数据,⽽不是执⾏多次写数据的操作。对于读操作来说,最好传⼊可以⽀持的最⼤缓冲区,因为如果没有⾜够多的数据,内核也会试图填充 整个缓冲区(另外还需要保持 TCP 的通告窗⼝为打开状态)。这样,您就可以最⼩化调⽤的次数,并可以实现更好的整体性能。

TCP 的性能取决于⼏个⽅⾯的因素。两个最重要的因素是链接带宽(link bandwidth)(报⽂在⽹络上传输的速率)和

往返时间(round-trip time) 或 RTT(发送报⽂与接收到另⼀端的响应之间的延时)。这两个值确定了称为

Bandwidth Delay Product(BDP)的内容。给定链接带宽和 RTT 之后,您就可以计算出 BDP 的值了,不过这代表什么意义呢?BDP 给出了⼀种简单的⽅法来计算理论上最优的 TCPsocket 缓冲区⼤⼩(其中保存了排队等待传输和等待应⽤程序接收的数据)。如果缓冲区太⼩,那么 TCP 窗⼝就不能完全打开,这会对性能造成限制。如果缓冲区太⼤,那么宝贵的内存资源就会造成浪费。如果您设置的缓冲区⼤⼩正好合适,那么就可以完全利⽤可⽤的 带宽。下⾯我们来看⼀个例⼦:BDP = link_bandwidth * RTT如果应⽤程序是通过⼀个 100Mbps 的局域⽹进⾏通信,其 RRT 为 50 ms,那么 BDP 就是:100MBps * 0.050 sec / 8 = 0.625MB = 625KB注意:此处除以 8 是将位转换成通信使⽤的字节。因此,我们可以将 TCP 窗⼝设置为 BDP 或 1.25MB。但是在 Linux 2.6 上默认的 TCP 窗⼝⼤⼩是 110KB,这会将连接的带宽限制为2.2MBps,计算⽅法如下:throughput = window_size / RTT110KB / 0.050 = 2.2MBps如果使⽤上⾯计算的窗⼝⼤⼩,我们得到的带宽就是 12.5MBps,计算⽅法如下:625KB / 0.050 = 12.5MBps差别的确很⼤,并且可以为 socket 提供更⼤的吞吐量。因此现在您就知道如何为您的 socket 计算最优的缓冲区⼤⼩了。但是⼜该如何来改变呢?Sockets API 提供了⼏个 socket 选项,其中两个可以⽤于修改 socket 的发送和接收缓冲区的⼤⼩。清单 2 展⽰了如何使⽤ SO_SNDBUF 和 SO_RCVBUF 选项来调整发送和接收缓冲区的⼤⼩。注意:尽管 socket 缓冲区的⼤⼩确定了通告 TCP 窗⼝的⼤⼩,但是 TCP 还在通告窗⼝内维护了⼀个拥塞窗⼝。因此,由于这个拥塞窗⼝的存在,给定的 socket 可能永远都不会利⽤最⼤的通告窗⼝。int ret, sock, sock_buf_size;sock = socket( AF_INET, SOCK_STREAM, 0 );sock_buf_size = BDP;ret = setsockopt( sock, SOL_SOCKET, SO_SNDBUF, (char *)&sock_buf_size, sizeof(sock_buf_size) );ret = setsockopt( sock, SOL_SOCKET, SO_RCVBUF, (char *)&sock_buf_size, sizeof(sock_buf_size) ); 在 Linux 2.6 内核中,发送缓冲区的⼤⼩是由调⽤⽤户来定义的,但是接收缓冲区会⾃动加倍。您可以进⾏ getsockopt 调⽤来验证每个缓冲区的⼤⼩。我们还可以考虑将包的⼤⼩从 1,500 字节修改为 9,000 字节(称为巨帧)。在本地⽹络中可以通过设置最⼤传输单元(Maximum Transmit Unit,MTU)来设置巨帧,这可以极⼤地提⾼性能。提⽰:Linux 内核还包括了⾃动对这些 socket 缓冲区进⾏优化的能⼒(请参阅下⾯ 中的 tcp_rmem 和 tcp_wmem),不过这些选项会对整个栈造成影响。如果您只需要为⼀个连接或⼀类连接调节窗⼝的⼤⼩,那么这种机制也许不能满⾜您的需要了。

标准的 GNU/Linux 发⾏版试图对各种部署情况都进⾏优化。这意味着标准的发⾏版可能并没有对您的环境进⾏特殊的优化。GNU/Linux 提供了很多可调节的内核参数,您可以使⽤这些参数为您⾃⼰的⽤途对操作系统进⾏动态配置。下⾯我们来了解⼀下影响socket 性能的⼀些更重要的选项。在 /proc 虚拟⽂件系统中存在⼀些可调节的内核参数。这个⽂件系统中的每个⽂件都表⽰⼀个或多个参数,它们可以通过 cat ⼯具进⾏读取,或使⽤ echo 命令进⾏修改。清单 3 展⽰了如何查询或启⽤⼀个可调节的参数(在这种情况中,可以在 TCP/IP 栈中启⽤ IP 转发)。[root@camus]# cat /proc/sys/net/ipv4/ip_forward0[root@camus]# echo "1" > /poc/sys/net/ipv4/ip_forward[root@camus]# cat /proc/sys/net/ipv4/ip_forward1[root@camus]# 给出了⼏个可调节的参数,它们可以帮助您提⾼ Linux TCP/IP 栈的性能。表 1. TCP/IP 栈性能使⽤的可调节内核参数可调节的参数/proc/sys/net/core/rmem_default/proc/sys/net/core/rmem_max/proc/sys/net/core/wmem_default/proc/sys/net/core/wmem_max/proc/sys/net/ipv4/tcp_window_scaling/proc/sys/net/ipv4/tcp_sack/proc/sys/net/ipv4/tcp_fack/proc/sys/net/ipv4/tcp_timestamps/proc/sys/net/ipv4/tcp_mem/proc/sys/net/ipv4/tcp_wmem/proc/sys/net/ipv4/tcp_rmem/proc/sys/net/ipv4/tcp_low_latency/proc/sys/net/ipv4/tcp_westwood/proc/sys/net/ipv4/tcp_bic"1""0"启⽤发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利⽤情况进⾏优化;对于 WAN 通信来说应该启⽤这个选项。"245763276849152""472""460""0"允许 TCP/IP 栈适应在⾼吞吐量情况下低延时的情况;这个选项应该禁⽤。与 tcp_wmem 类似,不过它表⽰的是为⾃动调优所使⽤的接收缓冲区的值。确 定 TCP 栈应该如何反映内存使⽤;每个值的单位都是内存页(通常是 4KB)。第⼀个值是内存使⽤的下限。第⼆个值是内存压⼒模式开始对缓冲区使⽤应⽤压⼒的上限。第三个值是内存上限。在这个层次上可以将报⽂丢弃,从⽽减 少对内存的使⽤。对于较⼤的 BDP 可以增⼤这些值(但是要记住,其单位是内存页,⽽不是字节)。为⾃动调优定义每个 socket 使⽤的内存。第⼀个值是为 socket 的发送缓冲区分配的最少字节数。第⼆个值是默认值(该值会被 wmem_default 覆盖),缓冲区在系统负载不重的情况下可以增长到这个值。第三个值是发送缓冲区空间的最⼤字节数(该值会被 wmem_max 覆盖)。"1"以⼀种⽐重发超时更精确的⽅法(请参阅 RFC 1323)来启⽤对 RTT 的计算;为了实现更好的性能应该启⽤这个选项。"1""1"启⽤有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报⽂来提⾼性能(这样可以让发送者只发送丢失的报⽂段);(对于⼴域⽹通信来说)这个选项应该启⽤,但是这会增加对 CPU 的占⽤。"1"启⽤ RFC 1323 定义的 window scaling;要⽀持超过 64KB 的窗⼝,必须启⽤该值。"110592"定义发送窗⼝的最⼤⼤⼩;对于更⼤的 BDP 来说,这个⼤⼩也应该更⼤。"110592"定义默认的发送窗⼝⼤⼩;对于更⼤的 BDP 来说,这个⼤⼩也应该更⼤。"110592"定义接收窗⼝的最⼤⼤⼩;对于更⼤的 BDP 来说,这个⼤⼩也应该更⼤。默认值选项说明"110592"定义默认的接收窗⼝⼤⼩;对于更⼤的 BDP 来说,这个⼤⼩也应该更⼤。启⽤转发应答(Forward Acknowledgment),这可以进⾏有选择应答(SACK)从⽽减少拥塞情况的发⽣;这个选项也应该启⽤。为快速长距离⽹络启⽤ Binary Increase Congestion;这样可以更好地利⽤以 GB 速度进⾏操作的链接;对于 WAN 通信应该启⽤这个选项。 与任何调优努⼒⼀样,最好的⽅法实际上就是不断进⾏实验。您的应⽤程序的⾏为、处理器的速度以及可⽤内存的多少都会影响到这些参数影响性能的⽅式。 在某些情况中,您认为有益的操作可能恰恰是有害的(反之亦然)。因此,我们需要逐⼀试验各个选项,然后检查每个选项的结果。换⽽⾔之,我们需要相信⾃⼰的 经验,但是对每次修改都要进⾏验证。提⽰:下⾯介绍⼀个有关永久性配置的问题。注意,如果您重新启动了 GNU/Linux 系统,那么您所需要的任何可调节的内核参数都会恢复成默认值。为了将您所设置的值作为这些参数的默认值,可以使⽤ /etc/ 在系统启动时将这些参数配置成您所设置的值。

GNU/Linux 对我⾮常有吸引⼒,这是因为其中有很多⼯具可以使⽤。尽管其中⼤部分都是命令⾏⼯具,但是它们都⾮常有⽤,⽽且⾮常直观。GNU/Linux 提供了⼏个⼯具 —— 有些是 GNU/Linux ⾃⼰提供的,有些是开放源码软件 —— ⽤于调试⽹络应⽤程序,测量带宽/吞吐量,以及检查链接的使⽤情况。表 2 列出最有⽤的⼏个 GNU/Linux ⼯具,以及它们的⽤途。表 3 列出了 GNU/Linux 发⾏版没有提供的⼏个有⽤⼯具。有关表 3 中⼯具的更多信息请参阅 。表 2. 任何 GNU/Linux 发⾏版中都可以找到的⼯具GNU/Linux ⼯具pingtraceroutenetstattcpdump⽤途这是⽤于检查主机的可⽤性的最常⽤的⼯具,但是也可以⽤于识别带宽延时产品计算的 RTT。打印某个连接到⽹络主机所经过的包括⼀系列路由器和⽹关的路径(路由),从⽽确定每个 hop 之间的延时。确定有关⽹络⼦系统、协议和连接的各种统计信息。显⽰⼀个或多个连接的协议级的报⽂跟踪信息;其中还包括时间信息,您可以使⽤这些信息来研究不同协议服务的报⽂时间。

表 3. GNU/Linux 发⾏版中没有提供的有⽤性能⼯具GNU/Linux ⼯具netlognettimerEtherealiperf⽤途为应⽤程序提供⼀些有关⽹络性能⽅⾯的信息。为瓶颈链接带宽⽣成⼀个度量标准;可以⽤于协议的⾃动优化。以⼀个易于使⽤的图形化界⾯提供了 tcpump(报⽂跟踪)的特性。测量 TCP 和 UDP 的⽹络性能;测量最⼤带宽,并汇报延时和数据报的丢失情况。

尝试使⽤本⽂中介绍的技巧和技术来提⾼ socket 应⽤程序的性能,包括通过禁⽤ Nagle 算法来减少传输延时,通过设置缓冲区的⼤⼩来提⾼ socket 带宽的利⽤,通过最⼩化系统调⽤的个数来降低系统调⽤的负载,以及使⽤可调节的内核参数来优化 Linux 的 TCP/IP 栈。在进⾏优化时还需要考虑应⽤程序的特性。例如,您的应⽤程序是基于 LAN 的还是会通过 Internet 进⾏通信?如果您的应⽤程序仅仅会在LAN 内部进⾏操作,那么增⼤ socket 缓冲区的⼤⼩可能不会带来太⼤的改进,不过启⽤巨帧却⼀定会极⼤地改进性能!最后,还要使⽤ tcpdump 或 Ethereal 来检查优化之后的结果。在报⽂级看到的变化可以帮助展⽰使⽤这些技术进⾏优化之后所取得的成功效果。

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1690106797a306314.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信