kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp

static_key 机制

https://www.dazhuanlan.com/2019/10/10/5d9f4b6a20f82/

简单来说,如果你对代码性能很敏感,而且大多数情况下分支路径是确定的,可以考虑使用static keys。static keys可以代替普通的变量进行分支判断,目的是用来优化频繁使用if-else判断的问题,这里涉及到指令分支预取的一下问题。简单地说,现代cpu都有预测功能,变量的判断有可能会造成硬件预测失败,影响流水线性能。虽然有likely和unlikely,但还是会有小概率的预测失败。

定义一个static_key

1
struct static_key key = STATIC_KEY_INIT_FALSE;

注意:这个key及其初始值必须是静态存在的,不能定义为局部变量或者使用动态分配的内存。通常为全局变量或者静态变量。 其中的STATIC_KEY_INIT_FALSE表示这个key的默认值为false,对应的分支默认不进入,如果是需要默认进入的,用STATIC_KEY_INIT_TRUE,这里如果不赋值,系统默认为STATIC_KEY_INIT_FALSE,在代码运行中不能再用STATIC_KEY_INIT_FALSE/STATIC_KEY_INIT_TRUE进行赋值。 判断语句

对于默认为false(STATIC_KEY_INIT_FALSE)的,使用

1
2
3
4
if (static_key_false(&key))
	do unlikely code
else
	do likely code

对于默认为true(STATIC_KEY_INIT_TRUE)的,使用

1
2
3
4
if (static_key_true((&static_key)))
	do the likely work;
else
	do unlikely work

修改判断条件

使用static_key_slow_inc让分支条件变成true,使用static_key_slow_dec让分支条件变成false,与其初始的默认值无关。该接口是带计数的, 也就是:

初始值为STATIC_KEY_INIT_FALSE的,那么: static_key_slow_inc; static_key_slow_inc; static_key_slow_dec 那么 if (static_key_false((&static_key)))对应的分支会进入,而再次static_key_slow_dec后,该分支就不再进入了。

初始值为STATIC_KEY_INIT_TRUE的,那么: static_key_slow_dec; static_key_slow_dec; static_key_slow_inc 那么 if (static_key_true((&static_key)))对应的分支不会进入,而再次static_key_slow_inc后,该分支就进入了。

static-key的内核实现

static_key_false的实现:

对X86场景其实现如下,其它架构下的实现类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static __always_inline bool static_key_false(struct static_key *key)
{
	return arch_static_branch(key);
}

static __always_inline bool arch_static_branch(struct static_key *key)
{
	asm_volatile_goto("1:"
		".byte " __stringify(STATIC_KEY_INIT_NOP) "nt"
		".pushsection __jump_table,  "aw" nt"
		_ASM_ALIGN "nt"
		_ASM_PTR "1b, %l[l_yes], %c0 nt"
		".popsection nt"
		: :  "i" (key) : : l_yes);
	return false;
l_yes:
	return true;
}

其中的asm_volatile_goto宏 使用了asm goto,是gcc的特性,其允许在嵌入式汇编中jump到一个C语言的label,详见gcc的manual(https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html), 但是本处其作用只是将C语言的label “l_yes”传递到嵌入式汇编中。

STATIC_KEY_INITIAL_NOP其实就是NOP指令

.pushsection __jump_table 是通知编译器,以下的内容写入到段 __jump_table

_ASM_PTR “1b, %l[l_yes], %c0 ,是往段__jump_table中写入label “1b"、C label "l_yes"和输入参数struct static_key *key的地址,这些信息对应于struct jump_entry 中的code、target、key成员,在后续的处理中非常重要。

.popsection表示以下的内容回到之前的段,其实多半就是.text段。

可见,以上代码的作用就是:执行NOP指令后返回false,同时把NOP指令的地址、代码"return true"对应地址、struct static_key *key的地址写入到段__jump_table。由于固定返回为false且为always inline,编译器会把

1
2
3
4
if (static_key_false((&static_key)))
	do the unlikely work;
else
	do likely work

优化为:

1
2
3
4
5
nop
do likely work
retq
l_yes:
do the unlikely work;

正常场景,就没有判断了。

static_key_true的实现:

1
2
3
4
static __always_inline bool static_key_true(struct static_key *key)
{
	return !static_key_false(key);
}

执行static_key_slow_inc(&key)后,底层通过gcc提供的goto功能,再结合c代码编写的动态修改内存功能,就可以让使用key的代码从执行false分支变成执行true分支。当然这个更改代价时比较昂贵的,不是所有的情况都适用。

squid 开启cgi-bin/cachemgr.cgi

start

apache2 支持cgi

(可选)apache2 开启认证

sudo apt-get install squid-cgi

文件就在 /usr/lib/cgi-bin/ 下面,和 apache2 目录一致,不需要cp

1
2
3
4
vim /etc/squid/cachemgr.conf
localhost
换成
localhost:port
1
2
3
4
5
6
7
vim /etc/squid/squid.conf
注释掉这两行
#http_access allow localhost manager
#http_access deny manager
添加这两行
acl manager proto cache_object
http_access allow manager

squid -k reconfigure

cachemgr_passwd

vim /etc/squid/squid.conf

cachemgr_passwd none all # 所有用户开启所有权限

// cachemgr_passwd 123456 all 不生效 ???

squid -k reconfigure

web 打开 http://ip/cgi-bin/cachemgr.cgi 就能查看、操作一些squid功能了

用户名:manager 或 空

密码:空

ubuntu 18.04

18.04 的 squid 版本3.5.27-1ubuntu1.8 好像有问题,点击 Current Squid Configuration squid就重启。

换成 16.04 的 3.5.12-1ubuntu7.13 就没问题了。

http://security.ubuntu.com/ubuntu/pool/main/s/squid3/squid_3.5.12-1ubuntu7.13_amd64.deb

http://security.ubuntu.com/ubuntu/pool/universe/s/squid3/squid-cgi_3.5.12-1ubuntu7.13_amd64.deb

http://security.ubuntu.com/ubuntu/pool/main/s/squid3/squid-common_3.5.12-1ubuntu7.13_all.deb

apache2 支持cgi

http://blog.chinaunix.net/uid-26824563-id-5769678.html

ln

1
2
3
4
5
ls -l /etc/apache2/*/*cgi*

ln -s /etc/apache2/mods-available/cgid.conf /etc/apache2/mods-enabled/cgid.conf
ln -s /etc/apache2/mods-available/cgid.load /etc/apache2/mods-enabled/cgid.load
ln -s /etc/apache2/mods-available/cgi.load /etc/apache2/mods-enabled/cgi.load 

restart

service apache2 restart

CGI目录为 /usr/lib/cgi-bin

apache2 访问认证

创建密码

1
2
3
htpasswd -c squid.pwd admin

chown www-data:www-data squid.pwd

修改 /etc/apache2/apache2.conf

1
2
3
4
5
6
<Location /cgi-bin/cachemgr.cgi>
	AuthType Basic
	AuthName "admin"
	AuthUserFile  /etc/squid/squid.pwd
	require valid-user
</Location>

TCP_NEW_SYN_RECV

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=10feb428a5045d5eb18a5d755fbb8f0cc9645626

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=d34ac51b76e8c7de6094cfb11780ef9c2b93469f

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=4e9a578e5b6bdfa8b7fed7a41f28a86a7cffc85f

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=079096f103faca2dd87342cca6f23d4b34da8871

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=2215089b224412bfb28c5ae823b2a5d4e28a49d7

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=26e3736090e1037ac929787df21c05497479b77f

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=85645bab57bfc6b0b43bb96a301c4ef83925c07d

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=a9407000038805e5215a49c0a50c9e2b2ff38220

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=8b5801477926a2b018afc84a53c0b8818843fe73

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=a8399231f0b6e72bc140bcc4fecb0c622298a6bd

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=caf3f2676aaad395903d24a54e22f8ac4bc4823d

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=4bdc3d66147b3a623b32216a45431d0cff005f50

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=c2f34a65a61cd1ace3b53c93e8b38d2f79f4ff0d

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=f03f2e154f52fdaa982de7e2c386737679963dc9

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=fff1f3001cc58b5064a0f1154a7ac09b76f29c44

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=aac065c50aba0c534a929aeb687eb68c58e523b8

结合以上patch,在 kernel-3.10.0-693.11.1.el7.src.rpm 内核上引入 TCP_NEW_SYN_RECV patch

好处:mptcp和4.15.0的基本一样。

不再需要spin_lock(listen_sk),最大的互斥变成atomic。(去除atomic看 tcp连接查找

ipv6_addr_v4mapped

sk_ehashfn 被 ipv4, ipv6 共用,req hash 的时候可用的变量不多,用的是 ipv6_addr_v4mapped(&sk->sk_v6_daddr) 判断是否mapped,所以原先的sk->sk_daddr = addr; sk->sk_rcv_saddr = addr; 换成下面两个函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static inline void sk_daddr_set(struct sock *sk, __be32 addr)
{
	sk->sk_daddr = addr; /* alias of inet_daddr */
#if IS_ENABLED(CONFIG_IPV6)
	ipv6_addr_set_v4mapped(addr, &sk->sk_v6_daddr);
#endif
}

static inline void sk_rcv_saddr_set(struct sock *sk, __be32 addr)
{
	sk->sk_rcv_saddr = addr; /* alias of inet_rcv_saddr */
#if IS_ENABLED(CONFIG_IPV6)
	ipv6_addr_set_v4mapped(addr, &sk->sk_v6_rcv_saddr);
#endif
}

ir_iif, ireq_net, ireq_state

ir_iif, ireq_net, ireq_state 需要在 req 创建时赋值,因为插入ehash表后的查找需要用到这些变量。

reqsk_put

原先部分reqsk_free需要换成reqsk_put,因为req已经和sk一样,靠自己的refcnt维护

backlog

1
2
3
4
if (sk->sk_state == TCP_LISTEN) {
	ret = tcp_v4_do_rcv(sk, skb);
	goto put_and_return;
}

listen_sk 的包要在tcp_v4_rcv里处理完,不能再加入listen_sk的backlog处理,因为req已经不在listen_sk->icsk_accept_queue.listen_opt.syn_table里,而backlog(=tcp_v4_do_rcv)又不会再lookup_sk,导致无法找到req。

原来的处理是:按listen_sk的收到包的顺序处理,并且需要spin_lock。按照下面的顺序,即使syn、ack、GET包都在backlog里也能处理(GET包查不到req,能查到establish)。TCP_NEW_SYN_RECV 主要是优化调spin_lock(listen_sk)

1
2
3
4
5
6
7
8
9
10
tcp_v4_hnd_req() {
	req = inet_csk_search_req()

	if (req)
		return tcp_check_req()

	nsk = inet_lookup_established()
	if (nsk && nsk->sk_state != TCP_TIME_WAIT)
		return nsk;
}