导致出现很多相同端口的UDP套接字的原因有两点:
1)在reload的时候,调用shutdown()而不是close()来关闭UDP套接字。shutdown()是用来关闭连接的,但是文件描述符还是会存在。UDP协议并没有提供shutdown接口(参见udp_prot),所以调用shutdown()并没有真的关闭套接字,而且reload的次数太多。
2)设置了SO_REUSEADDR选项。如果没有设置这个选项,在bind的时候就会报错,漏关或者使用shutdown()关闭的错误就很容易发现。UDP套接字没有TIME_WAIT状态,而且要使用的UDP套接字数量不多,所以没有必要设置该选项。
现在我们结合内核代码来分析一下为什么相同端口的UDP套接字数量过多导致软中断占用CPU利用率高。
UDP套接字在绑定端口后才会添加到udp_table哈希表中,解决冲突使用的是“拉链法”,放置的槽位是由绑定的端口决定的(参见不考虑CONFIG_NET_NS选项)。所以绑定到相同端口的套接字会放置到同一个槽位的链表中,这种套接字数量越多,链表就越长。
在接收到UDP协议的SKB包后,会调用__udp4_lib_lookup()函数来查找对应的UDP套接字,相关代码如下所示:
static struct sock *__udp4_lib_lookup(struct net *net,
__be32 saddr,
__be16 sport, __be32 daddr, __be16 dport,
int dif, struct udp_table *udptable)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
unsigned short hnum = ntohs(dport);
unsigned int hash = udp_hashfn(net, hnum);
struct udp_hslot *hslot = &udptable->hash[hash];
int score, badness;
rcu_read_lock();
begin:
result = NULL;
badness = -1;
sk_nulls_for_each_rcu(sk, node, &hslot->head) {
score = compute_score(sk, net, saddr, hnum, sport,
daddr, dport, dif);
if (score > badness) {
result = sk;
badness = score;
}
}
......
}
首先根据SKB包的目的端口号来查找要遍历的链表,然后通过sk_nulls_for_each_rcu宏来遍历链表上的所有套接字。我们可以看到,不管这个链表上有多少个套接字,都要从头到尾遍历一遍。如果遍历的链表越长,耗费的时间就越多,这个处理是在软中断的上下文中,因此会导致软中断占用CPU利用率高。
下面是使用perf top命令看到的结果(可以清楚地看到__udp4_lib_lookup会成为热点,在同等压力下是不会出现的):
相同端口的UDP套接字数量过多导致软中断占用CPU利用率高的原因分析,布布扣,bubuko.com
相同端口的UDP套接字数量过多导致软中断占用CPU利用率高的原因分析
原文:http://blog.csdn.net/justlinux2010/article/details/20727135