kernel笔记:TCP参数

http://blog.chinaunix.net/uid-27119491-id-3346430.html

本文将介绍网络连接建立的过程、收发包流程,以及其中应用层、tcp层、ip层、设备层和驱动层各层发挥的作用。

应用层

对于使用socket进行网络连接的服务器端程序,我们会先调用socket函数创建一个套接字:

  1. fd = socket(AF_INET, SOCK_STREAM, 0);

以上指定了连接协议,socket调用返回一个文件句柄,与socket文件对应的inode不在磁盘上,而是存在于内存。

之后我们指定监听的端口、允许与哪些ip建立连接,并调用bind完成端口绑定:

  1. server_addr.sin_family = AF_INET;
  2. server_addr.sin_port   = htons(PORT);
  3. server_addr.sin_addr.s_addr = INADDR_ANY;
  4. bind(fd, (struct sockaddr_in *)&server_addr, sizeof(struct sockaddr_in));

端口作为进程的标识,客户端根据服务器ip和端口号就能找到相应进程。

接着我们调用listen函数,对端口进行监听:

  1. listen(fd, backlog);

backlog值指定了监听队列的长度,以下内核参数限制了backlog可设定的最大值:

  1. linux # sysctl -a | grep somaxconn
  2. net.core.somaxconn = 128

监听端口在listen调用后变为LISTEN状态:

  1. linux # netstat -antp | grep 9999
  2. Proto  Recv-Q Send-Q Local Address  Foreign Address  State  PID/Program name
  3. tcp         0      0  0.0.0.0:9999        0.0.0.0:* LISTEN       8709/server

相应地,客户端调用connect进行连接,tcp三次握手在connect调用返回之前完成:

如果服务器端向客户端发送SYN+ACK后,客户端不返回ACK,则服务器保持半连接(SYN_RECV)状态:

  1. linux # netstat -np | grep SYN_RECV
  2. tcp      0        0     0.0.0.0:9999  127.0.0.0.1:5334   SYN_RECV  -

若队列中的连接均处于半连接状态,服务器将不能处理正常的请求,syn泛洪攻击(syn flood)就是利用这个特点完成DoS(拒绝服务攻击)。

当连接数超过队列长度backlog时,超出的连接也保持为半连接状态,直到数量达到内核参数tcp_max_syn_backlog值,超出该值的连接请求将被丢弃:

  1. linux # sysctl -a | grep tcp_max_syn
  2. net.ipv4.tcp_max_syn_backlog = 1024

accept调用用于处理新到来的连接:

  1. new_fd = accept(fd, (struct sockaddr*)&client_addr, &sin_size);

其返回一个文件描述符,后续我们可以对该文件描述符调用write、read等操作函数,原监听端口仍处于LISTEN状态:

  1. linux # netstat -antp | grep 9999
  2. tcp     0    0    0.0.0.0:9999       0.0.0.0:*      LISTEN  8709/server
  3. tcp     0    0  127.0.0.1:9999 127.0.0.1:52274 ESTABLISHED  -

以上为网络连接建立过程中,应用层所做的工作,server端完成了socket创建、端口绑定、端口监听、连接和收发包任务,而client端相对简单,只需包含连接和收发包。

tcp层

内核代码中,tcp_sendmsg是tcp发包的主入口函数,该函数中struct sk_buff结构用于描述一个数据包。

对于超过MTU(maximum transmission unit, 最大传输单元)的数据包,tcp层会对数据包进行拆分,若开启了网口的tcp segmentation offload功能,则拆分工作由网卡完成:

  1. linux # ethtool -k ether
  2. Offload parameters for eth1:
  3. rx-checksumming: on
  4. tx-checksumming: on
  5. scatter-gather: on
  6. tcp segmentation offload: on

以下内核参数是内核为tcp socket预留的用于发送数据包的缓冲区大小,单位为byte:

  1. linux # sysctl -a | grep tcp_wmem
  2. net.ipv4.tcp_wmem = 4096 16384 131072

默认的用于包发送的缓冲区大小为16M。

除了用于缓冲收发数据包,对于每个socket,内核还要分配一些数据结构用于保持连接状态,内核对tcp层可使用的内存大小进行了限制:

  1. linux # sysctl -a | grep tcp_mem
  2. net.ipv4.tcp_mem = 196608 262144 393216

以上值以页为单位,分别对应最小值、压力值和最大值,并在系统启动、tcp栈初始化时根据内存总量设定。通过proc提供的接口,我们可以查到tcp已用的内存页数:

  1. linux # cat /proc/net/sockstat
  2. sockets : used 91
  3. TCP : inuse 8 orphan 0 tw 11 alloc 13 mem 2

ip层

内核代码中,ip_queue_xmit函数是ip层的主入口函数,注意ip层与tcp层操作的都是同一块内存(sk_buff结构),期间并没有发生数据包相关的内存拷贝。

ip层主要完成查找路由的任务,其根据路由表配置,决定数据包发往哪个网口,另外,该层实现netfilter的功能。

网络设备层

dev_queue_xmit是网络设备层的主入口函数,该层为每个网口维护一条数据包队列,由ip层下发的数据包放入对应网口的队列中。在该层中,数据包不是直接交给网卡,而是先缓冲起来,再通过软中断(NET_TX_SOFTIRQ)调用qdisc_run函数,该函数将数据包进一步交由网卡处理。我们执行ifconfig时,txqueuelen指示了网络设备层中,网口队列的长度。

驱动层

使用不同驱动的网卡,相应的驱动层代码就不一样,这里以e1000网卡为例。e1000_xmit_frame是该层的主入口函数,该层利用环形队列进行数据包管理,由两个指针负责维护环形队列。执行ethtool命令,我们可以查询网口驱动层环形队列长度:

  1. linux # ethtool -g eth1
  2. Ring parameters for ether
  3. Pre-set maximums:
  4. RX : 511
  5. RX Mini : 0
  6. RX Jumbo : 0
  7. TX : 511
  8. Current hardware settings:
  9. RX : 200
  10. RX Mini : 0
  11. RX Jumbo : 0
  12. TX : 511

以上RX与TX分别指示收包队列与发包队列长度,单位为包个数。

网卡接收到数据包时将产生中断,以通知cpu数据包到来的消息,而网卡接收包又非常繁忙,如果每次收发包都向cpu发送硬中断,那cpu将忙于处理网卡中断。

相应的优化方案是NAPI(New API)模式,其关闭网卡硬中断,使网卡不发送中断,而非使cpu不接收网卡中断。在e1000驱动代码中,由e1000_clean函数实现NAPI模式。

不像写文件的过程,磁盘设备层完成内存数据到磁盘拷贝后,会将消息层层上报,这里的网卡驱动层发包后不会往上层发送通知消息。

收包过程

以上为网络发包所需经过的层次结构,以及各层的大体功能,下面我们简单看下收包过程。

网卡接收到数据包后,通知上层,该过程不会发生拷贝,数据包丢给ip层。

内核代码中,ip_rcv是ip层收包的主入口函数,该函数由软中断调用。存放数据包的sk_buff结构包含有目的地ip和端口信息,此时ip层进行检查,如果目的地ip不是本机,则将包丢弃,如果配置了netfilter,则按照配置规则对包进行转发。

tcp_v4_rcv是tcp层收包的接收入口,其调用__inet_lookup_skb函数查到数据包需要往哪个socket传送,之后将数据包放入tcp层收包队列中,如果应用层有read之类的函数调用,队列中的包将被取出。

tcp层收包使用的内存同样有限制:

  1. linux # sysctl -a | grep rmem
  2. net.ipv4.tcp_rmem = 4096 16384 131072
时间: 2024-10-19 22:51:52

kernel笔记:TCP参数的相关文章

关于高负载服务器Kernel的TCP参数优化

net.ipv4.tcp_mem 内核分配给TCP连接的内存,单位是Page,1 Page = 4096 Bytes,可用命令查看: #getconf PAGESIZE 4096 net.ipv4.tcp_mem = 196608       262144  393216 第一个数字表示,当 tcp 使用的 page 少于 196608 时,kernel 不对其进行任何的干预 第二个数字表示,当 tcp 使用了超过 262144 的 pages 时,kernel 会进入 “memory pres

Swift学习笔记(二)参数类型

关于参数类型,在以前的编程过程中,很多时间都忽视了形参与实参的区别.通过这两天的学习,算是捡回了漏掉的知识. 在swift中,参数有形参和实参之分,形参即只能在函数内部调用的参数,默认是不能修改的,如果想要修改就需要在参数前添加var声明. 但这样的声明过后,仍旧不会改变实参的值,这样就要用到inout了,传递给inout的参数类型必须是var类型的,不能是let类型或者字面类型,(字面类型是在swift中常提的一个术语,个人认为就是赋值语句,也不能修改)而且在传递过程中,要用传值符号"&

linux网络编程笔记——TCP

1.TCP和UDP TCP是长连接像持续的打电话,UDP是短消息更像是发短信.TCP需要消耗相对较多的资源,但是传输质量有保障,UDP本身是不会考虑传输质量的问题. 2.网络传输内容 我习惯的做法是直接通过TCP传送结构体,当然前提是收发两端都在程序里对目标结构体有充分的定义.特别说明的一点是,要小心收发两端处理器的大小端问题!而且传输信息头里必须包含长度信息,而且通用的是大端.但是,这里的长度和结构体,我选择用小端进行传输. 3.TCPserver实现 参考了别人多线程的回调写法,看起来不错.

linux下如何配置TCP参数设置详解

设置tcp参数一定要小心谨慎,轻易不要更改线上环境,我贴一下我们线上环境中,sysctl.conf的内容,见文章底部 net.ipv4.tcp_tw_reuse    = 1 net.ipv4.tcp_tw_recycle  = 1 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_keepalive_time = 1800 net.ipv4.tcp_max_syn_backlog = 4096 net.ipv4.tcp_syncookies = 1 www.

linux kernel的cmdline参数解析原理分析

利用工作之便,今天研究了kernel下cmdline参数解析过程,记录在此,与大家共享,转载请注明出处,谢谢. Kernel 版本号:3.4.55 Kernel启动时会解析cmdline,然后根据这些参数如console root来进行配置运行. Cmdline是由bootloader传给kernel,如uboot,将需要传给kernel的参数做成一个tags链表放在ram中,将首地址传给kernel,kernel解析tags来获取cmdline等信息. Uboot传参给kernel以及kern

[转载]window系统下TCP参数优化

注:此文转载自红黑联盟,最近服务器遇到周期性down掉的问题,拖了两三周请教了前辈之后才知道无关应用和数据库的事情,是tcp设置的问题.从网上找个这篇文章,解释的很不错.有兴趣请至红黑联盟翻阅原文. TCP连接的状态与关闭方式及其对Server与Client的影响 http://www.2cto.com/net/201304/206071.html 通常会采用修改注册表的方式改进Windows的系统参数.下面将为大家介绍Windows系统下的TCP参数优化方式,适用于Windows 2003.W

Windows系统下的TCP参数优化

1. TCP连接的状态 首先介绍一下TCP连接建立与关闭过程中的状态.TCP连接过程是状态的转换,促使状态发生转换的因素包括用户调用.特定数据包以及超时等,具体状态如下所示: CLOSED:初始状态,表示没有任何连接. LISTEN:Server端的某个Socket正在监听来自远方的TCP端口的连接请求. SYN_SENT:发送连接请求后等待确认信息.当客户端Socket进行Connect连接时,会首先发送SYN包,随即进入SYN_SENT状态,然后等待Server端发送三次握手中的第2个包.

kernel.sem信号量参数调优,以及ipcs信号量队列查询

首先介绍下kernel.sem 这个参数 cat /proc/sys/kernel/sem 250     32000   32      128 或者 ipcs -ls ------ Semaphore Limits -------- max number of arrays = 128 max semaphores per array = 250 max semaphores system wide = 32000 max ops per semop call = 32 semaphore 

LINUX kernel笔记系列 :IO块参数 图

Linux下,I/O处理的层次可分为4层: 系统调用层,应用程序使用系统调用指定读写哪个文件,文件偏移是多少 文件系统层,写文件时将用户态中的buffer拷贝到内核态下,并由cache缓存该部分数据 块层,管理块设备I/O队列,对I/O请求进行合并.排序 设备层,通过DMA与内存直接交互,将数据写到磁盘 下图清晰地说明了Linux I/O层次结构: 写文件过程 写文件的过程包含了读的过程,文件先从磁盘载入内存,存到cache中,磁盘内容与物理内存页间建立起映射关系.用于写文件的write函数的声