- 浏览: 209713 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (391)
- java (18)
- python (3)
- ruby (4)
- linux (48)
- 网络 (9)
- 前端 (2)
- 社会、文化、哲学、人生、百态 (0)
- 工具 (10)
- 下载 (0)
- 常用地址 (0)
- tracert (0)
- mysql (8)
- 开源相关收藏 (1)
- 模块查看依懒 (1)
- watch使用 (1)
- Tcpdump (2)
- easy_install安装 (1)
- 构造redis批量删除脚本 (1)
- MYSQL 性能测试 (1)
- JAVA code encode utf-8 (1)
- linux nginx awk 实时 每妙 (1)
- mkpasswd (1)
- spring security oauth (1)
- jmap dump java memory Analyzer (1)
- JAVA DUMP (1)
- swap linux 过高 解决 (1)
- SWAP (1)
- jmap jstat jstack dump (1)
- java jconsole 的使用 (1)
- git 常用 (1)
- MYSQL 索引 动态 唯一 (1)
- TCP 三次握手 四次挥手 (1)
- linux date (1)
- 删除 空行 注释行 (1)
- maven3 yum linux install repository (1)
- linux git 搭建 (1)
- linux sar eth1 查看 流量 (1)
- sar (1)
- netstat ip 过滤 常用脚本 (1)
- Tcpdump 包分析网络连接过程 (1)
- net ipv4 tcp time wait tw recycle (0)
- /etc/sysctl.conf linux 网络 配置 (1)
- ss 网络连接查看 (比netstat 快很多,实时性牺牲) (1)
- MYSQL 关键字 (1)
- Linux 下多核CPU知识 (1)
- top (1)
- 令牌 证书 (1)
- mysql unix timestamp (1)
- 端口扫描 nc nmap (1)
- 204 http code 状态码 (1)
- ss -s ss -l (1)
- linux 常用 curl (1)
- linux sed 替换 换行 (1)
- centos yum install rpm install (1)
- spring-mvc源码解读 (1)
- 使用iftop查看实时的网络流量 (0)
- linux 命令 expect (1)
- HTTP (1)
- openssl ddif 加密 (1)
- iptables 详解 (1)
- python 虚拟化 VirtualEnv virtualenvwrapper (1)
- nginx (2)
- more less 实用技巧 (1)
- linux nginx (2)
- linux curl https ssl 证书 ca (1)
- openssl (1)
- php mysql linux (1)
- linux 虚拟机 虚拟 xen (0)
- linux 虚拟机 虚拟 xen kvm (1)
- linux perl 单行执行技巧 (1)
- mysql 查看库占用空间 表查用空间 (1)
- linux tcpdump (1)
- maven (1)
- sun.misc.Unsafe (1)
- OpenSSL生成证书 (1)
- http://blog.csdn.net/zzulp/article/details/8018751 (1)
- maven 本地 jar dependency (1)
- 计算JAVA代码行数最简单命令 sed (1)
- 常用的证书格式转换 rsa eg (1)
- 加密 解密 签名 (1)
- 分析jar包冲突 (1)
- 使用JMockit编写java单元测试 (1)
- Linux 技巧:让进程在后台可靠运行的几种方法 (1)
- 环境变量控制 (1)
- 5+ 个 tar 命令的用法,附示例 (1)
- scp自动输入密码 (1)
- ps axo pid (1)
- ppid (1)
- comm (1)
- pmem (1)
- lstart|grep mysql (0)
- lstart (1)
- etime|grep mysql (1)
- UML类图字少好理解 (1)
- HTTP经典文章 (1)
- git (1)
- Git常用命令 (1)
- LINUX 系统被攻击的分析过程 (1)
- NIO (1)
- LINUX 操作快捷键使用 (1)
- openSSL命令、PKI、CA、SSL证书原理 (1)
- shell (2)
- 转载 (1)
- mysqldump 可以直接dump->xml (1)
- VIM比较全面的文章 (1)
- eclipse regex 正则表达式 (1)
- synchronized (1)
- 锁 (1)
- java 正则表达式 regex (1)
- Reference Queue 引用 源码 (1)
- spring aop 源码 分析 (1)
- java @Cache @Transaction 注解 (1)
- spring aop (1)
- spring jdk proxy cglib 动态代理 性能比较 (1)
- spring proxy private public 代理限制 (1)
- spring transaction aop 事务 (1)
- spring autowire 注解注入 (1)
- 桥接 NAT NAT地址转换 内部网络 虚拟网络 (1)
- spring-web-mvc 源码解读 之 RequestMappingHandlerMapping (1)
- find atime mtime ctime -n n +n (1)
- android studio 快捷键初探 (1)
- android 源码阅读的计划 (1)
- 计算机网络学习-VLAN (1)
- sed 高级 合并行 (1)
- CAP 一致性 可用性 分布式容错性 (1)
- android lib so 库文件 (0)
- android lib so 库文件 移植 (1)
- android 不错的博文 (1)
- sourceinsight 源码 阅读 (1)
- Android Tab UI (1)
- 诗 (1)
- mysql 批处理 (0)
- netty 堆外内存 DirectByteBuffer (1)
- netty 并发 百万 推送 (1)
- Linux操作系统中内存buffer和cache的区别 (1)
- maven intellij target bytecode version (1)
- linux sleep()的实现原理 (1)
- android (2)
- javadoc 代码注释规范 (1)
- spring 自动注入bean auto (1)
- Photoshop CS6常用快捷键 (1)
- 股票 数据 机器 分析 (1)
- 批处理 (1)
- mysql -e (1)
- char (1)
- Unicode (1)
- 编码 (1)
- utf8 (1)
- utf-8 (1)
- utf16 (1)
- utf-16 (1)
- IntelliJ IDEA (1)
- ide (1)
- idea (1)
- intellij (1)
- 文件 (1)
- 目录 (1)
- 源代码 (1)
- CountDownLatch (1)
- CyclicBarrier (1)
- Semaphore (1)
- spring (1)
- linux 查看不同进制文件 (1)
- WebMvcConfigurationSupport (1)
- sdkman工具的使用 (1)
- http header (1)
- LINUX系统优化 (1)
最新评论
-
gelongmei:
威武我大酒神
shell脚本不换行刷新数据
不要盲目增加ip_conntrack_max-理解Linux内核内存
http://blog.csdn.net/dog250/article/details/7107537
1.由ip_conntrack引出的Linux内存映射
有很多文章在讨论关于ip_conntrack表爆满之后丢弃数据包的问题,对此研究深入一些的知道Linux有个内核参数ip_conntrack_max,在拥有较大内存的机器中默认65536,于是疯狂的增加这个参数,比如设置成10000…00,只要不报设置方面的错误,就一定要设置成最大值。这种方式实在是将软件看成大神了,殊不知软件的技术含量还不如锅炉呢!
如果考虑的再全面一些,比如经验丰富的程序员或者网管,可能会想到内存的问题,他们知道所有的连接跟踪信息都是保存于内存中的,因此会考虑单纯放大这个ip_conntrack_max参数会占据多少内存,会权衡内存的占用,如果系统没有太大的内存,就不会将此值设置的太高。
但是如果你的系统有很大的内存呢?比如有8G的内存,分个1G给连接跟踪也不算什么啊,这是合理的,然而在传统的32位架构Linux中是做不到,为什么?因为你可能根本不懂Linux内核的内存管理方式。
内存越来越便宜的今天,linux的内存映射方式确实有点过时了。然而事实就摆在那里,ip_conntrack处于内核空间,它所需的内存必须映射到内核空间,而传统的32位Linux内存映射方式只有1G属于内核,这1G的地址空间中,前896M是和物理内存一一线性映射的,后面的若干空洞之后,有若干vmalloc的空间,这些vmalloc空间和一一映射空间相比,很小很小,算上4G封顶下面的很小的映射空间,一共可以让内核使用的地址空间不超过1G。对于ip_conntrack来讲,由于其使用slab分配器,因此它还必须使用一一映射的地址空间,这就是说,它最多只能使用不到896M的内存!
为何Linux使用如此“落后”的内存映射机制这么多年还不改进?其实这种对内核空间内存十分苛刻的设计在64位架构下有了很大的改观,然而问题依然存在,即使64位架构,内核也无法做到透明访问所有的物理内存,它同样需要把物理内存映射到内核地址空间后才能访问,对于一一映射,这种映射是事先确定的,对于大小有限(实际上很小)非一一映射空间,需要动态创建页表,页目录等。另外还有一个解释,那就是“内核本来就不该做ip_conntrack这种事”,那是协议栈的事,而不巧的是,Liunx的协议栈完全在内核中实现,可能在skb接收软中断中处理的ip_conntrack不能睡眠,因此也就不能将此任务交给进程,也就不能利用进程地址空间(进程地址空间[用户态+内核态]可以访问所有的物理内存)。
Linux之所以对内核内存要求如此苛刻,目的就是不想让你随意使用,因为它宝贵,你才更要珍惜它们。
2.在32位架构Linux系统上的实验
以下是为了证明以上的事实所作的实验,可能实验中使用的一些手段仍然不符合常识,然而我觉得成一家之言即可,毕竟这种方案永远不会也不可能出现在公司的标准文档上,那样会让人学会投机取巧或者称偷懒,但是为了备忘,还得有个地方留着,那就写成博客吧。
还有一个参数会影响查找连接跟踪的时间复杂度和空间复杂度,那就是ip_conntrack_buckets。该值描述了哈希桶的数量,理论上,这个值越大,哈希碰撞就会越小,查找时间就会越快,但是需要为每一个桶预分配一块不是很大的内存,如果桶数量很大,就会占用很大的内存,并且这些内存还都是宝贵的“仅有1G空间内的内核内存”,和ip_conntrack结构体的分配策略不同,这个哈希桶可以分配在vmalloc空间而不一定非要在一一线性映射空间。
2.1.快速压满ip_conntrack的方法
使用loadrunner绝对是一种方式,然而术业有专攻,工作之余我又很讨厌windows上的一切,因此需要采用其它方式,下班在家,只身一人,也不想使用netcat之类的“瑞士军刀”,我怕端口占满,又怕我的macbook狂热,因此需要再想办法。由于目的只是想测试ip_conntrack最多能占用多少内存,其实这个我早就知道了,只是想证实一下子,那么办法也就有了,那就是增加ip_conntrack结构体的大小,而这很容易,只需要在结构体后面增加一个很大的字段即可。下面的修改基于Red Hat Enterprise 5的2.6.18内核
2.2.测试前对ip_conntrack内核模块的修改
编辑$build/include/linux/netfilter_ipv4/ip_conntrack.h文件,在结构体ip_conntrack的最后加上下面一句:
char aaa[102400]; //这个102400是通过二分法得到的,如果设置成2xxxxx则在加载的时候就会使内核crash,因为这个数组是直接分配(类似栈上分配)的而不是动态分配的,它载入的时候很可能会冲掉内核的关键数据,因此还是选取一个可行的数值,然后慢慢加连接吧,毕竟扩大了起码100000倍呢~~
进入$src/net/ipv4/netfilter,执行:
make –C /lib/modules/2.6.18-92.e15/build SUBDIRS=`pwd` modules
如此一来加载ip_conntrack.ko之后,内核日志将打印出:
ip_conntrack version 2.4 (8192 buckets, 65536 max) - 102628 bytes per conntrack
由此看出ip_conntrack结构体已经增大了,这样撑满整个可用内存所需的网络连接压力就大大减小了,也就不用什么loadrunner之类的东西了。为了尽快撑满可以使用的内存,还要将关于ip_conntrack的所有timeout设置的比较长,相当长:
sysctl -w net.ipv4.netfilter.ip_conntrack_generic_timeout=600000
sysctl -w net.ipv4.netfilter.ip_conntrack_icmp_timeout=300000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_close=1000000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack=300000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=60000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=120000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=432000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_recv=600000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent=120000
这样既有的一个流就会“永久保持”了,一直占着ip_conntrack结构体不放,直到可用的内存溢出。
在加载了ip_conntrack模块之后,所有过往的数据包就会自动被追踪,下面编写以下脚本:
for (( i=1; i<255; i++));
do
for (( j=1; j<255; j++));
do
ping 192.168.$i.$j -c 1 -W 1
curl --connect-timeout 1 http://138.$i.$j.80/
curl --connect-timeout 1 http://38.$i.$j.80/
curl --connect-timeout 1 http://$i.1.$j.80/
curl --connect-timeout 1 http://$j.$i.9.8/
done
done
2.3.测试过程
本机配置:
内存:3032M,free命令识别3003M
执行上述脚本,抽根烟,拉一脬(pao),得到下列的数据:
封顶连接数:6149个
使用内存:886M
此时在本机ping 127.0.0.1也不通了,说明ip_conntrack已经达到了极限,同时由于在alloc ip_conntrack的地方插入了打印语句(肯定是一堆#号),内核打印了内存分配失败的信息。一共3G的内存,仅仅使用了886M(而且我不断使用sysctl –w vm.drop_caches=3清楚cache),剩余的都无法给ip_conntrack使用。为了使结果更有说服力,我在ip_conntrack模块的初始化函数中插入了下列代码:
for (j=0; I < 400; j++)
__get_free_pages(GFP_KERNEL,;
意思是我先占去内核空间的400M内存,看看最终总的连接跟踪数量是否也会减少相应的,得到数据如下:
封顶连接数:3421个
使用内存:879M
可见,内核内存被额外占据了,能分给ip_conntrack的就少了。更进一步,保持上述的__get_free_pages不变,再增加下列的代码:
for (j=0; I < 400; j++)
__get_free_pages(GFP_HIGHUSER,;
最终的结果如下:
封顶连接数:3394个
使用内存:1203M
可见,HIGHUSER内存并不会怎么影响内核内存,要知道用户进程的内存几乎都是使用这个HIGHUSER标识分配的。如果去掉GFP_KERNEL的分配,仅仅保留GFP_HIGHUSER的分配,得到下列结果:
封顶连接数:6449个
使用内存:1312M
可见,HIGHUSER内存的分配尽力在高端进行,不会怎么影响内核的一一映射空间。
2.4.测试结果
综上所述,32位架构上Linux的ip_conntrack使用的内存只能在内核地址空间的一一映射区,换句话说,它只能使用物理内存的前896M,除掉ip_conntrack结构体的添加的char aaa[],也是这个结果,只不过要想压满所有的可用内存,不是很容易,需要动用几台机器以及loadrunner之类的压力工具。
3.在64位架构Linux系统上做的实验
MD!由于我大动了内核数据结构,加载模块没有成功,至今仍在调试,排错,已几个时辰有余…
4.结论
最后,需要说明的是,ip_conntrack_max的初始值是内核根据你机器的内存计算出来的,包括ip_conntrack_buckets也是这样算出来的,内核之所以设置这样的初始值,那是经过精心测试的经验值,因此除了非要改不可,不要去提高这个值。如果你的机器面临大量连接,你提高了ip_conntrack_max的值,那么代价就是占用了大量可贵的内核内存,可能会引起其它的内核内存分配失败,并且,一旦内核内存使用超过了内核内存空间映射的阀值,那么系统会默默的丢弃你的数据包,而不会报出:
table full, dropping packet error and solution
之类的错误,这是可悲的一件事。
操作系统内核影响协议栈行为带来了一种错觉:我有这么多内存,为何不让我使用?!事实上,不是你要使用,而是内核要使用,你可以控制的只是进程,对于内核,程序员是没法控制的。当然你可以重新编译,甚至修改内核,甚至修改ip_conntrack的内存分配方式,不再使用伙伴系统的slab内存,而是重定向一个userspace,然则,然则这个开发需要成本,一个人日几百块,没有几个公司愿意花这笔钱。因此,最终的结论:不要盲目增加ip_conntrack_max。呼应了题目。
附:
1.关于GFP_XXX
GFP_ATOMIC:设置这个标识,在内核映射区域的紧急池分配,不成功则简单返回NULL,不释放其它内存,也就不kill进程,分配路径不睡眠。ip_conntrack结构体就是使用这个标识分配的(它大多数处于软中断路径)
GFP_KERNEL:设置这个标识,在内核映射区域分配,不成功则尝试释放可以释放的内存,尝试调用oom_killer,可以睡眠
GFP_HIGHUSER:设置这个标识,可以分配到所有的物理内存(排除很小一部分固定内存以及用于总线IO的内存,这个在x86上很明显,具体参见/proc/iomem获得详细物理内存映射信息)。一般而言,用户态进程所需的内存都使用这个标识来分配,尽量使用内核“很难”使用的高端内存(1G以后的物理内存,内核还要动态映射,这要操作页表),从而将前896M(32位架构)尽量留给内核使用
2.关于ip_conntrack的哈希
Linux的ip_conntrack使用了 jhash函数来进行哈希计算,关于该函数的实现可以参考下面这个网址:
http://burtleburtle.net/bob/hash/doobs.html
作者的解释很清晰
1.由ip_conntrack引出的Linux内存映射
有很多文章在讨论关于ip_conntrack表爆满之后丢弃数据包的问题,对此研究深入一些的知道Linux有个内核参数ip_conntrack_max,在拥有较大内存的机器中默认65536,于是疯狂的增加这个参数,比如设置成10000…00,只要不报设置方面的错误,就一定要设置成最大值。这种方式实在是将软件看成大神了,殊不知软件的技术含量还不如锅炉呢!
如果考虑的再全面一些,比如经验丰富的程序员或者网管,可能会想到内存的问题,他们知道所有的连接跟踪信息都是保存于内存中的,因此会考虑单纯放大这个ip_conntrack_max参数会占据多少内存,会权衡内存的占用,如果系统没有太大的内存,就不会将此值设置的太高。
但是如果你的系统有很大的内存呢?比如有8G的内存,分个1G给连接跟踪也不算什么啊,这是合理的,然而在传统的32位架构Linux中是做不到,为什么?因为你可能根本不懂Linux内核的内存管理方式。
内存越来越便宜的今天,linux的内存映射方式确实有点过时了。然而事实就摆在那里,ip_conntrack处于内核空间,它所需的内存必须映射到内核空间,而传统的32位Linux内存映射方式只有1G属于内核,这1G的地址空间中,前896M是和物理内存一一线性映射的,后面的若干空洞之后,有若干vmalloc的空间,这些vmalloc空间和一一映射空间相比,很小很小,算上4G封顶下面的很小的映射空间,一共可以让内核使用的地址空间不超过1G。对于ip_conntrack来讲,由于其使用slab分配器,因此它还必须使用一一映射的地址空间,这就是说,它最多只能使用不到896M的内存!
为何Linux使用如此“落后”的内存映射机制这么多年还不改进?其实这种对内核空间内存十分苛刻的设计在64位架构下有了很大的改观,然而问题依然存在,即使64位架构,内核也无法做到透明访问所有的物理内存,它同样需要把物理内存映射到内核地址空间后才能访问,对于一一映射,这种映射是事先确定的,对于大小有限(实际上很小)非一一映射空间,需要动态创建页表,页目录等。另外还有一个解释,那就是“内核本来就不该做ip_conntrack这种事”,那是协议栈的事,而不巧的是,Liunx的协议栈完全在内核中实现,可能在skb接收软中断中处理的ip_conntrack不能睡眠,因此也就不能将此任务交给进程,也就不能利用进程地址空间(进程地址空间[用户态+内核态]可以访问所有的物理内存)。
Linux之所以对内核内存要求如此苛刻,目的就是不想让你随意使用,因为它宝贵,你才更要珍惜它们。
2.在32位架构Linux系统上的实验
以下是为了证明以上的事实所作的实验,可能实验中使用的一些手段仍然不符合常识,然而我觉得成一家之言即可,毕竟这种方案永远不会也不可能出现在公司的标准文档上,那样会让人学会投机取巧或者称偷懒,但是为了备忘,还得有个地方留着,那就写成博客吧。
还有一个参数会影响查找连接跟踪的时间复杂度和空间复杂度,那就是ip_conntrack_buckets。该值描述了哈希桶的数量,理论上,这个值越大,哈希碰撞就会越小,查找时间就会越快,但是需要为每一个桶预分配一块不是很大的内存,如果桶数量很大,就会占用很大的内存,并且这些内存还都是宝贵的“仅有1G空间内的内核内存”,和ip_conntrack结构体的分配策略不同,这个哈希桶可以分配在vmalloc空间而不一定非要在一一线性映射空间。
2.1.快速压满ip_conntrack的方法
使用loadrunner绝对是一种方式,然而术业有专攻,工作之余我又很讨厌windows上的一切,因此需要采用其它方式,下班在家,只身一人,也不想使用netcat之类的“瑞士军刀”,我怕端口占满,又怕我的macbook狂热,因此需要再想办法。由于目的只是想测试ip_conntrack最多能占用多少内存,其实这个我早就知道了,只是想证实一下子,那么办法也就有了,那就是增加ip_conntrack结构体的大小,而这很容易,只需要在结构体后面增加一个很大的字段即可。下面的修改基于Red Hat Enterprise 5的2.6.18内核
2.2.测试前对ip_conntrack内核模块的修改
编辑$build/include/linux/netfilter_ipv4/ip_conntrack.h文件,在结构体ip_conntrack的最后加上下面一句:
char aaa[102400]; //这个102400是通过二分法得到的,如果设置成2xxxxx则在加载的时候就会使内核crash,因为这个数组是直接分配(类似栈上分配)的而不是动态分配的,它载入的时候很可能会冲掉内核的关键数据,因此还是选取一个可行的数值,然后慢慢加连接吧,毕竟扩大了起码100000倍呢~~
进入$src/net/ipv4/netfilter,执行:
make –C /lib/modules/2.6.18-92.e15/build SUBDIRS=`pwd` modules
如此一来加载ip_conntrack.ko之后,内核日志将打印出:
ip_conntrack version 2.4 (8192 buckets, 65536 max) - 102628 bytes per conntrack
由此看出ip_conntrack结构体已经增大了,这样撑满整个可用内存所需的网络连接压力就大大减小了,也就不用什么loadrunner之类的东西了。为了尽快撑满可以使用的内存,还要将关于ip_conntrack的所有timeout设置的比较长,相当长:
sysctl -w net.ipv4.netfilter.ip_conntrack_generic_timeout=600000
sysctl -w net.ipv4.netfilter.ip_conntrack_icmp_timeout=300000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_close=1000000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack=300000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait=60000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait=120000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_established=432000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_recv=600000
sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent=120000
这样既有的一个流就会“永久保持”了,一直占着ip_conntrack结构体不放,直到可用的内存溢出。
在加载了ip_conntrack模块之后,所有过往的数据包就会自动被追踪,下面编写以下脚本:
for (( i=1; i<255; i++));
do
for (( j=1; j<255; j++));
do
ping 192.168.$i.$j -c 1 -W 1
curl --connect-timeout 1 http://138.$i.$j.80/
curl --connect-timeout 1 http://38.$i.$j.80/
curl --connect-timeout 1 http://$i.1.$j.80/
curl --connect-timeout 1 http://$j.$i.9.8/
done
done
2.3.测试过程
本机配置:
内存:3032M,free命令识别3003M
执行上述脚本,抽根烟,拉一脬(pao),得到下列的数据:
封顶连接数:6149个
使用内存:886M
此时在本机ping 127.0.0.1也不通了,说明ip_conntrack已经达到了极限,同时由于在alloc ip_conntrack的地方插入了打印语句(肯定是一堆#号),内核打印了内存分配失败的信息。一共3G的内存,仅仅使用了886M(而且我不断使用sysctl –w vm.drop_caches=3清楚cache),剩余的都无法给ip_conntrack使用。为了使结果更有说服力,我在ip_conntrack模块的初始化函数中插入了下列代码:
for (j=0; I < 400; j++)
__get_free_pages(GFP_KERNEL,;
意思是我先占去内核空间的400M内存,看看最终总的连接跟踪数量是否也会减少相应的,得到数据如下:
封顶连接数:3421个
使用内存:879M
可见,内核内存被额外占据了,能分给ip_conntrack的就少了。更进一步,保持上述的__get_free_pages不变,再增加下列的代码:
for (j=0; I < 400; j++)
__get_free_pages(GFP_HIGHUSER,;
最终的结果如下:
封顶连接数:3394个
使用内存:1203M
可见,HIGHUSER内存并不会怎么影响内核内存,要知道用户进程的内存几乎都是使用这个HIGHUSER标识分配的。如果去掉GFP_KERNEL的分配,仅仅保留GFP_HIGHUSER的分配,得到下列结果:
封顶连接数:6449个
使用内存:1312M
可见,HIGHUSER内存的分配尽力在高端进行,不会怎么影响内核的一一映射空间。
2.4.测试结果
综上所述,32位架构上Linux的ip_conntrack使用的内存只能在内核地址空间的一一映射区,换句话说,它只能使用物理内存的前896M,除掉ip_conntrack结构体的添加的char aaa[],也是这个结果,只不过要想压满所有的可用内存,不是很容易,需要动用几台机器以及loadrunner之类的压力工具。
3.在64位架构Linux系统上做的实验
MD!由于我大动了内核数据结构,加载模块没有成功,至今仍在调试,排错,已几个时辰有余…
4.结论
最后,需要说明的是,ip_conntrack_max的初始值是内核根据你机器的内存计算出来的,包括ip_conntrack_buckets也是这样算出来的,内核之所以设置这样的初始值,那是经过精心测试的经验值,因此除了非要改不可,不要去提高这个值。如果你的机器面临大量连接,你提高了ip_conntrack_max的值,那么代价就是占用了大量可贵的内核内存,可能会引起其它的内核内存分配失败,并且,一旦内核内存使用超过了内核内存空间映射的阀值,那么系统会默默的丢弃你的数据包,而不会报出:
table full, dropping packet error and solution
之类的错误,这是可悲的一件事。
操作系统内核影响协议栈行为带来了一种错觉:我有这么多内存,为何不让我使用?!事实上,不是你要使用,而是内核要使用,你可以控制的只是进程,对于内核,程序员是没法控制的。当然你可以重新编译,甚至修改内核,甚至修改ip_conntrack的内存分配方式,不再使用伙伴系统的slab内存,而是重定向一个userspace,然则,然则这个开发需要成本,一个人日几百块,没有几个公司愿意花这笔钱。因此,最终的结论:不要盲目增加ip_conntrack_max。呼应了题目。
附:
1.关于GFP_XXX
GFP_ATOMIC:设置这个标识,在内核映射区域的紧急池分配,不成功则简单返回NULL,不释放其它内存,也就不kill进程,分配路径不睡眠。ip_conntrack结构体就是使用这个标识分配的(它大多数处于软中断路径)
GFP_KERNEL:设置这个标识,在内核映射区域分配,不成功则尝试释放可以释放的内存,尝试调用oom_killer,可以睡眠
GFP_HIGHUSER:设置这个标识,可以分配到所有的物理内存(排除很小一部分固定内存以及用于总线IO的内存,这个在x86上很明显,具体参见/proc/iomem获得详细物理内存映射信息)。一般而言,用户态进程所需的内存都使用这个标识来分配,尽量使用内核“很难”使用的高端内存(1G以后的物理内存,内核还要动态映射,这要操作页表),从而将前896M(32位架构)尽量留给内核使用
2.关于ip_conntrack的哈希
Linux的ip_conntrack使用了 jhash函数来进行哈希计算,关于该函数的实现可以参考下面这个网址:
http://burtleburtle.net/bob/hash/doobs.html
作者的解释很清晰
相关推荐
离线安装包,亲测可用
官方离线安装包,亲测可用
官方离线安装包,亲测可用。使用rpm -ivh [rpm完整包名] 进行安装
conntrack-tools-1.4.4-7.el7.x86_64。这个包适合centos 7使用,使用rpm -ivh 安装就行。
官方离线安装包,亲测可用
官方离线安装包,亲测可用。使用rpm -ivh [rpm完整包名] 进行安装
conntrack将信息存在内存结构中,包括IP,端口,协议类型,状态以及超时时间。 而且conntrack不仅可以对TCP这种有状态的会话进行状态跟踪,还可以对UDP进行状态跟踪。 conntrack本身并不会对包进行过滤,而是...
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
官方离线安装包,亲测可用
官方离线安装包,亲测可用
官方离线安装包,亲测可用
官方离线安装包,亲测可用
官方离线安装包,亲测可用
- `CONFIG_IP_NF_CONNTRACK` - `CONFIG_IP_NF_FTP` - `CONFIG_IP_NF_IPTABLES` - `CONFIG_IP_NF_MATCH_LIMIT` - `CONFIG_IP_NF_MATCH_MAC` - `CONFIG_IP_NF_MATCH_MARK` - `CONFIG_IP_NF_MATCH_MULTIPORT` -...
官方离线安装包,亲测可用
- **优先级**:这些钩子点的优先级分别为 NF_IP_PRI_CONNTRACK、NF_IP_PRI_CONNTRACK、NF_IP_PRI_LAST 和 NF_IP_PRI_LAST-1。这意味着当数据包进入或离开 IP 层时,将会被处理以进行连接跟踪。这主要是通过调用回调...
在Linux内核中,一个连接记录由`ip_conntrack`结构表示。该结构包含了以下主要字段: - **nf_conntrack**:记录连接被使用的次数,便于其他组件引用。 - **timeout**:指向一个函数指针,用于处理超时的连接记录。 ...
NF Conntrack是Linux内核的一个核心组件,它负责跟踪网络连接的状态,如TCP、UDP和其他协议的连接。"Common"一词意味着这部分代码或数据结构适用于多种协议,不局限于特定的协议层。 描述中提到的"protocol-...
官方离线安装包,亲测可用。使用rpm -ivh [rpm完整包名] 进行安装
综上所述,"nf_conntrack_proto_udp.rar_out"涉及的是Linux内核中的网络连接跟踪模块,特别是针对UDP协议的部分,其主要任务是管理和输出与UDP连接相关的跟踪信息。这个模块通过解析和处理UDP数据包,在网络层提供...