大约不到两周前,我写了一个内核模块统计了TCP当前的半连接数量:
https://blog.csdn.net/dog250/article/details/105013772
该实现采用了扫描每一个Listener的方式,从而替代了扫描所有的连接再过滤的方式,以
O
(
1
)
O(1)
O(1)复杂度替代了
O
(
n
)
O(n)
O(n)复杂度。
随后,我又换了一种方式可以实时查看当前的半连接计数值:
https://blog.csdn.net/dog250/article/details/105022347
再之后,一发不可收拾,开始了二进制hook手艺之旅。
回到最开始的扫描每一个Listener的方法,虽然OK,但是需要维护一个procfs的文件,以提供数据的读取,若要支持半连接统计功能,一个内核模块就必须一直处在载入状态。
能不能改成oneshot模式呢?即输出完数值,模块即卸载。
其实很容易,直接在模块的init函数里进行计算,然后输出值后,将init函数return -1即可。但这又是逆经理…这不优雅。
这里采用了stap的Guru专家模式,oneshot输出数值到控制台后,即退出。代码如下:
// dump_halfconn_num.stp
%{
#include <linux/module.h>
#include <net/tcp.h>
%}
function dump_halfconn_num:long()
%{
unsigned int num = 0;
int i;
struct inet_hashinfo *hashinfo = &tcp_hashinfo;
for (i = 0; i < INET_LHTABLE_SIZE; i++) {
struct sock *sk;
struct hlist_nulls_node *node;
struct inet_listen_hashbucket *ilb;
ilb = &hashinfo->listening_hash[i];
spin_lock_bh(&ilb->lock);
sk_nulls_for_each(sk, node, &ilb->head) {
struct inet_connection_sock *icsk = inet_csk(sk);
struct listen_sock *lopt;
read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
lopt = icsk->icsk_accept_queue.listen_opt;
if (lopt && lopt->qlen)
num += lopt->qlen;
read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
}
spin_unlock_bh(&ilb->lock);
}
STAP_PRINTF("系统中当前的半连接数量为:%d\n", num);
STAP_RETVALUE = 0;
%}
probe begin
{
dump_halfconn_num();
exit(); // oneshot模式,dump完直接退出!
}
使用方法非常简单:
[root@localhost test]# stap -g ./dump_halfconn_num.stp
302
[root@localhost test]#
当然了,这是因为经理今天穿了皮鞋,而且还露着白袜子。
浙江温州皮鞋湿,下雨进水不会胖。