欢迎转载,版权所有,转载请保留文档的完整性!
大家对ss或者或者本篇文章有任何见解,欢迎随时沟通
还在用netstat吗?你OUT了
ss是iproute2工具包中的工具。
ss is used to dump socket statistics. It allows showing information similar to netstat. It can display more TCP and state informations than other tools.
1. 为什么用ss替换netstat?
这要从netstat的先天缺陷说起,netstat通过遍历proc来获取socket信息,当socket数据上万后,netstat的输出就非常耗时(用过的都知道)
而与之不同的ss使用netlink与内核tcp_diag模块通信获取socket信息。
2.ss命令输出解释之Recv-Q Send-Q
先来看一下ss的输出:
tingw:~ # ss -at
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 :::sunrpc :::*
LISTEN 0 128 *:sunrpc *:*
LISTEN 0 128 :::http :::*
LISTEN 0 128 127.0.0.1:ipp *:*
LISTEN 0 128 ::1:ipp :::*
LISTEN 0 100 ::1:smtp :::*
LISTEN 0 100 127.0.0.1:smtp *:*
LISTEN 0 64 :::59642 :::*
LISTEN 0 128 *:54107 *:*
LISTEN 0 128 :::xinupageserver :::*
LISTEN 0 128 *:xinupageserver *:*
LISTEN 0 64 *:50341 *:*
LISTEN 0 50 *:mysql *:*
ESTAB 0 52 192.168.0.1:xinupageserver 192.168.0.96:50599
输出所有的tcp socket信息。
第一列表示tcp socket的状态,第二列和第三列的内容与socket所处的状态有关,查看tcp_diag代码
-
static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
-
void *_info)
-
{
-
const struct tcp_sock *tp = tcp_sk(sk);
-
struct tcp_info *info = _info;
-
-
if (sk->sk_state == TCP_LISTEN) {
-
r->idiag_rqueue = sk->sk_ack_backlog;
-
r->idiag_wqueue = sk->sk_max_ack_backlog;
-
} else {
-
r->idiag_rqueue = tp->rcv_nxt - tp->copied_seq;
-
r->idiag_wqueue = tp->write_seq - tp->snd_una;
-
}
-
if (info != NULL)
-
tcp_get_info(sk, info);
-
}
处于LISTEN状态的socket,Recv-Q表示了current listen backlog队列元素数目(等待用户调用accept的完成3次握手的socket),而Send-Q表示了listen socket最大能容纳的backlog。这个数目由listen时指定,且不能大于 /proc/sys/net/ipv4/tcp_max_syn_backlog;对于非LISTEN socket,Recv-Q表示了receive queue中的字节数目(等待接收的下一个tcp段的序号-尚未从内核空间copy到用户空间的段最前面的一个序号);Send-Q表示发送queue中容纳的字节数(已加入发送队列中最后一个序号-输出段中最早一个未确认的序号)
3.ss命令输出解释之timer
linux-19:~ # ss -a -t -o -4
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:sunrpc *:*
LISTEN 0 128 *:47093 *:*
LISTEN 0 3 192.168.86.1:domain *:*
LISTEN 0 3 192.168.0.86:domain *:*
LISTEN 0 3 10.0.64.19:domain *:*
LISTEN 0 3 192.168.100.3:domain *:*
LISTEN 0 3 172.16.132.189:domain *:*
LISTEN 0 3 127.0.0.2:domain *:*
LISTEN 0 3 127.0.0.1:domain *:*
LISTEN 0 128 *:ssh *:*
LISTEN 0 128 127.0.0.1:ipp *:*
LISTEN 0 100 *:smtp *:*
LISTEN 0 128 127.0.0.1:953 *:*
LISTEN 0 64 *:33914 *:*
LISTEN 0 64 *:nfs *:*
LISTEN 0 128 *:35659 *:*
LISTEN 0 128 *:remotefs *:*
LISTEN 0 128 10.0.64.19:6380 *:*
LISTEN 0 128 *:openvms-sysipc *:*
ESTAB 0 0 172.16.132.189:ssh 172.16.132.93:hs-port timer:(keepalive,65min,0)
ESTAB 0 0 10.0.64.19:35225 10.0.64.129:61616
ESTAB 0 0 10.0.64.19:46617 10.0.64.107:61616
ESTAB 0 0 10.0.64.19:openvms-sysipc 10.0.64.2:videotex
ESTAB 0 0 10.0.64.19:49462 10.0.64.107:61616
ESTAB 0 0 172.16.132.189:ssh 172.16.132.85:63934 timer:(keepalive,38min,0)
ESTAB 0 0 10.0.64.19:60569 10.0.64.107:61616
ESTAB 0 0 10.0.64.19:52745 10.0.64.129:61616
ESTAB 0 52 172.16.132.189:ssh 172.16.132.92:50598 timer:(on,476ms,0)
ESTAB 0 0 10.0.64.19:56401 10.0.64.107:61616
ESTAB 0 0 10.0.64.19:54805 10.0.64.129:61616
ESTAB 0 0 10.0.64.19:60772 10.0.64.129:61616
ESTAB 0 0 10.0.64.19:55510 10.0.64.129:61616
ESTAB 0 0 10.0.64.19:45663 10.0.64.129:61616
ESTAB 0 0 10.0.64.19:39262 10.0.64.129:61616
ESTAB 0 0 10.0.64.19:57775 10.0.64.129:61616
ESTAB 0 0 10.0.64.19:52205 10.0.64.18:6379
ESTAB 0 0 10.0.64.19:remotefs 10.0.64.12:sentinel
这个输出更上次输出相比,多了一个timer输出。这个输出描述的是tcp socket上的定时器,在说明这个之前先了解一下linux对一个tcp socket可能设置的定时器。
tcp socket总共有7个定时器,通过4个timer实现。分别是
通过icsk_retransmit_timer实现的重传定时器、零窗口探测定时器;通过sk_timer实现的连接建立定时器、保活定时器和FIN_WAIT_2定时器;通过icsk_delack_timer实现的延时ack定时器;以及TIME_WAIT定时器。
我们看一下ss的代码
-
static const char *tmr_name[] = {
-
"off",
-
"on",
-
"keepalive",
-
"timewait",
-
"persist",
-
"unknown"
-
};
-
if (show_options) {
-
if (r->idiag_timer) {
-
if (r->idiag_timer > 4)
-
r->idiag_timer = 5;
-
printf(" timer:(%s,%s,%d)",
-
tmr_name[r->idiag_timer],
-
print_ms_timer(r->idiag_expires),
-
r->idiag_retrans);
-
}
-
}
对应的内核代码是
-
if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
-
r->idiag_timer = 1;
-
r->idiag_retrans = icsk->icsk_retransmits;
-
r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
-
} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
-
r->idiag_timer = 4;
-
r->idiag_retrans = icsk->icsk_probes_out;
-
r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout);
-
} else if (timer_pending(&sk->sk_timer)) {
-
r->idiag_timer = 2;
-
r->idiag_retrans = icsk->icsk_probes_out;
-
r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires);
-
} else {
-
r->idiag_timer = 0;
-
r->idiag_expires = 0;
-
}
-
static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
-
struct sk_buff *skb, int ext, u32 pid,
-
u32 seq, u16 nlmsg_flags,
-
const struct nlmsghdr *unlh)
-
{
-
long tmo;
-
struct inet_diag_msg *r;
-
const unsigned char *previous_tail = skb_tail_pointer(skb);
-
struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq,
-
unlh->nlmsg_type, sizeof(*r));
-
-
r = NLMSG_DATA(nlh);
-
BUG_ON(tw->tw_state != TCP_TIME_WAIT);
-
-
nlh->nlmsg_flags = nlmsg_flags;
-
-
tmo = tw->tw_ttd - jiffies;
-
if (tmo < 0)
-
tmo = 0;
-
-
r->idiag_family = tw->tw_family;
-
r->idiag_retrans = 0;
-
r->id.idiag_if = tw->tw_bound_dev_if;
-
r->id.idiag_cookie[0] = (u32)(unsigned long)tw;
-
r->id.idiag_cookie[1] = (u32)(((unsigned long)tw >> 31) >> 1);
-
r->id.idiag_sport = tw->tw_sport;
-
r->id.idiag_dport = tw->tw_dport;
-
r->id.idiag_src[0] = tw->tw_rcv_saddr;
-
r->id.idiag_dst[0] = tw->tw_daddr;
-
r->idiag_state = tw->tw_substate;
-
r->idiag_timer = 3;
-
r->idiag_expires = DIV_ROUND_UP(tmo * 1000, HZ);
-
r->idiag_rqueue = 0;
-
r->idiag_wqueue = 0;
-
r->idiag_uid = 0;
-
r->idiag_inode = 0;
这样timer的输出含义就是(类型,过期时间,重试次数),这里说一下类型的含义:
off: 当前socket没有timer
on: 重传timer
keepalive:连接建立timer or fin_wait_2 timer or 保活timer;具体是那个timer,可以根据连接的状态来确定。
timewait: TIME_WAITtimer
persist:零窗口探测timer
阅读(1873) | 评论(0) | 转发(0) |