linux 内核连接跟踪,Linux内核连接跟踪锁的优化分析(1)
Linux内核连接跟踪锁的优化分析(1)
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
微博:weibo.com/glinuxer
QQ技术群:4367710
简介
很多网络设备都使用Linux作为自己的OS,但是由于Linux本身是一个通用的操作系统,因此各家厂商都会根据自己的技术水平和需求,对内核进行或多或少的改动。而Linux随着版本的迭代,也在不断的改善自己的代码,吸收各厂商的patch,如google提交的著名的RPS/RFS补丁。
本文将对最近内核提交的一个commit 93bb0ceb75be2fdfa9fc0dd1fb522d9ada515d9c 进行分析。该改动是用于改善多CPU环境下多全局的连接跟踪锁nfconntracklock的优化。优化的手段也是常见的方法,将锁的粒度变细,由一个全局的自旋锁,改为1024个自旋锁。其中还会涉及几个之前的内核优化。
优化分析
功能简介
连接跟踪是大多数网络设备的基础功能,其它高级功能如NAT,Firewall等都是建立在连接跟踪之上的。Linux内核要负责创建连接,匹配连接,过期连接,销毁连接等等工作。可以说,连接跟踪的性能要在相当大的程度上影响数据包的转发性能。
2.6内核代码分析
在Linux 2.6内核中,nf_conntrack_lock是一个全局的自旋锁,用于保护内核的连接跟踪表。但是,实际上连接跟踪表并不是简单的一个表。而老代码只是使用nf_conntrack_lock来保护,这样就相当于给一个锁赋予了太多太宽泛的职责。
内核的连接跟踪依赖于net命名空间struct netns_ct的成员变量。一个连接的完整生命过程要涉及多个表的操作,如下所示:
struct netns_ct {
struct hlist_nulls_head *hash;
struct hlist_head *expect_hash;
struct hlist_nulls_head unconfirmed;
struct hlist_nulls_head dying;
其中hash是真正的连接跟踪表,expect_hash用于expectation连接,unconfirmed用于未确定的连接,而dying用于即将销毁的连接。
在老的内核中对这四个表的访问,都依赖于这个全局的自旋锁nf_conntrack_lock的保护。很自然,这已经充分影响了多核的并行处理。
3.16 内核代码分析
内核虽然对连接跟踪锁一直在做优化,但是直到今年3月才彻底抛弃nf_conntrack_lock(说实话,我没想到内核这么晚才对这个锁动手。对于设备厂商来说,这样的锁早就给扔了)。下面我们就内核对于2.6中的各个连接表的优化进行分析。
后文的代码以最新稳定版本3.16的代码为准
expectation连接
内核引入了一个新的全局自旋锁nf_conntrack_expect_lock专门用于保护expectation连接。具体的代码在此就不罗列了。需要注意的是,为了避免死锁,这两个锁要么不能同时上锁,要么必须按照一定的顺序上锁。
unconfirmed和dying连接
这两种情况的表,在新代码中有了比较大的改动。struct netns_ct中的unconfirmed和dying被变更为动态per cpu变量struct ct_pcpu __percpu *pcpu_lists;。而struct ct_pcpu的结构如下:
struct ct_pcpu {
spinlock_t lock;
struct hlist_nulls_head unconfirmed;
struct hlist_nulls_head dying;
struct hlist_nulls_head tmpl;
};
这里的ct_pcpu中的lock是用于用户空间与内核空间的同步。而多cpu间,即使仍然执行spinlock的上锁解锁动作,但实际上由于它们都是per cpu变量。因此并不会引起cpu之间的竞争。
一般的连接跟踪
真正的连接跟踪表netns_ct->hash实际上是一个hash表。之前的nf_conntrack_lock是对整个儿的hash表进行保护,那么我们可以减小锁的粒度来降低cpu之间的锁竞争。新内核去掉了独立的nf_conntrack_lock,取而代之的是1024个自旋锁。
-extern spinlock_t nf_conntrack_lock ;
+#ifdef CONFIG_LOCKDEP
+# define CONNTRACK_LOCKS 8
+#else
+# define CONNTRACK_LOCKS 1024
+#endif
+extern spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS];
之所以在CONFIG_LOCKDEP下,将CONNTRACK_LOCKS的数量缩小为8,因为这种情况下spinlock占用内存过大。
1024个自旋锁依然小于连接hash表的桶的数量,但在cpu不多,且hash算法良好的情况下,依然可以取得相当不错的效果。如何确定使用哪个自旋锁呢?是根据original和reply两个方向的tuple计算hash值来确定使用哪个所。
由于同时需要两个锁,所以这时候引入了一个新问题。如何确定这两个锁的使用顺序呢?最直接的想法是,先上original方向的锁,再上reply方向的锁。然而这第一个念头,无疑是错误的。假设两个连接A和B,那么极有可能A的original方向的hash值和B的reply方向的hash值一样,并且A的reply方向的hash值与B的original方向的hash值也一样。这时,就出现了两个CPU需要使用两个锁a和b,结果CPU1已经拥有了b锁期望a锁,CPU2拥有了a锁期望b锁,因此出现了死锁。
内核采用一种小技巧。不考虑方向,只看hash值,永远让cpu先拥有hash值小的锁,再尝试另外一个。代码如下:
+/* return true if we need to recompute hashes (in case hash table was resized) */
+static bool nf_conntrack_double_lock(struct net *net, unsigned int h1,
+ unsigned int h2, unsigned int sequence)
+{
+ h1 %= CONNTRACK_LOCKS;
+ h2 %= CONNTRACK_LOCKS;
+ if (h1 <= h2) {
+ spin_lock(&nf_conntrack_locks[h1]);
+ if (h1 != h2)
+ spin_lock_nested(&nf_conntrack_locks[h2],
+ SINGLE_DEPTH_NESTING);
+ } else {
+ spin_lock(&nf_conntrack_locks[h2]);
+ spin_lock_nested(&nf_conntrack_locks[h1],
+ SINGLE_DEPTH_NESTING);
+ }
+ if (read_seqcount_retry(&net->ct.generation, sequence)) {
+ nf_conntrack_double_unlock(h1, h2);
+ return true;
+ }
+ return false;
+}
这个函数通过比较h1和h2,先获得小hash值的锁,再尝试大hash值锁,最终同时拥有两个锁。
(最近由于工作太忙,已经大半年没有更新了。今天觉得实在是过意不去了,特意写了一篇,这是我最近两天看内核改动的所得。还没有写完,未完待续。貌似我又挖了一个坑)
linux 内核连接跟踪,Linux内核连接跟踪锁的优化分析(1)相关推荐
- 基于linux的千兆网卡驱动程序实现及数据传输效率优化,嵌入式Linux下网卡驱动的实现与数据转发性能优化分析...
摘要: 伴随着互联网的快速发展和后PC时代的到来,嵌入式系统已逐步成为当今IT产业的焦点之一,广阔的市场前景使嵌入式系统获得了空前的发展机遇.由于Linux操作系统具有代码开放.内核可裁减.网络功能强 ...
- 实验三 Linux的启动与关闭,实验三:跟踪分析Linux内核的启动过程
Ubuntu 16.04下搭建MenuOS的过程: 1.下载内核源代码编译内核 1 # 下载内核源代码编译内核 2 cd ~/LinuxKernel/ 3 wget https://www.kerne ...
- linux ip转发 丢包,关于ip_conntrack跟踪连接满导致网络丢包问题的分析
我们的线上web服务器在访问量很大时,就会出现网络连接丢包的问题,通过dmesg命令查看日志,发现如下信息: kernel: ip_conntrack: table full, dropping pa ...
- 使用BPF跟踪Linux内核
1. 前言 我们可以使用BPF对Linux内核进行跟踪,收集我们想要的内核数据,从而对Linux中的程序进行分析和调试.与其它的跟踪技术相比,使用BPF的主要优点是几乎可以访问Linux内核和应用程序 ...
- 跟踪 linux 内核调用_Linux用户和内核空间中的动态跟踪
跟踪 linux 内核调用 您是否曾经遇到过这样的情况,即您意识到没有在代码中的某些点插入调试打印 ,所以现在您将不知道您的CPU是否命中了特定的代码行来执行,直到您重新编译该代码为止.调试语句? 不 ...
- linux netstat Netstat是在内核中访问网络连接状态及其相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告。
在Internet RFC标准中,Netstat的定义是: Netstat是在内核中访问网络连接状态及其相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告. Netstat ...
- 嵌入式Linux设备驱动程序:编写内核设备驱动程序
嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...
- linux单步跟踪命令,dbx调试跟踪的常用子命令
dbx是UNIX下基于命令行界面的程序调试器. dbx是通过交互执行dbx子命令来达到调试的目的的.在调试程序前,必须先将-g选项包含在编译信息中,编译生成带调试信息的文件,即:cc -o filen ...
- Linux 内核详解以及内核缓冲区技术
Linux 内核简介 现在让我们从一个比较高的高度来审视一下 GNU/Linux 操作系统的体系结构.您可以从两个层次上来考虑操作系统,如图 2 所示. 图 2. GNU/Linux 操作系统的基本体 ...
最新文章
- curl调用WEB API
- Vim之代码异步检测插件 ALE -- 实时检查verilog等代码的正确性
- Mybatis Generator的使用
- 【腾讯通服务器的消息集成解决方案】之与勤哲Excel服务器的集成
- SqlServer索引的原理与应用
- C#中的DBNull、Null、和String.Empty解释
- sass webpack_如何在Visual Studio和Webpack中编译Sass文件
- 从0开始学习自动化测试框架cypress(五)案例
- 大数据平台分析发挥哪些作用
- tecplot360的宏命令
- Python实战教程 | 轻松批量识别数百个快递单号
- Linux线程详解(概念、原理、实现方法、优缺点)
- 查看redis安装路径
- win7、10无法修改mac地址_教一招如何修改MAC地址
- 10道经典java面试题_实习生必问(java基础)
- cad填充密度怎么调整_CAD填充比例调好了,填充物数量怎么调,就是密度怎么调?...
- 锁存器,触发器,寄存器关系及区别
- 20140925百度校园招聘一面
- 笑脸检测笑脸识别微笑识别大笑识别
- windows下使用route添加路由
热门文章
- yii2 java_YII2 自定义日志路径
- 输入法注入源码_将注入进行到底:利用Mono注入C#游戏脚本
- HTTPS和HTTPS证书
- idea中启动RunDashboard
- LeetCode177 第N高的薪水
- CTF Geek Challenge——第十一届极客大挑战Web Write Up
- 华为平板matepad支持鸿蒙2.0,首款鸿蒙OS 2.0平板,华为正式公布Matepad Pro2,搭载麒麟9000...
- Android 绿豆通讯录【 SQLite数据库(增删改查、展示数据) + ListView数据展示控件(展示所有数据) 】
- Springboot 常见请求方式
- python学习(函数)