kk Blog —— 通用基础


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

DKMS简介

https://www.cnblogs.com/wwang/archive/2011/06/21/2085571.html

DKMS全称是Dynamic Kernel Module Support,在内核版本变动之后可以自动重新生成新的模块。

安装

1
sudo apt-get install dkms

流程

DKMS主要的命令分别是add、build、install、uninstall和remove,另外,还可以执行"dkms status"查看目前DKMS系统维护的模块的状态。

DKMS要求我们的代码目录必须以" -“的格式命名。

命令

以hello-0.1为例,代码copy到"/usr/src/hello-0.1"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 添加
sudo dkms add -m hello -v 0.1

# 编译
sudo dkms build -m hello -v 0.1
生成模块路径: /var/lib/dkms/hello/0.1/*/*/module/

# 安装
sudo dkms install -m hello -v 0.1

# 移除
sudo dkms uninstall -m hello -v 0.1

# 彻底删除,会把/var/lib/dkms下彻底删除
sudo dkms remove -m hello -v 0.1 --all

# 以上的每个步骤查看执行后的状态
dkms status

目录结构

1
2
3
4
/usr/src/hello-0.1/
├── dkms.conf
├── hello.c
└── Makefile

在Makefile中要使用变量$(KVERSION)指定内核版本号,这样我们在执行dkms时,就可以用“-k”选项来设定为哪个内核版本编译模块。

dkms.conf

1
2
3
4
5
6
7
PACKAGE_NAME="hello"
PACKAGE_VERSION="0.1"
CLEAN="make clean"
MAKE[0]="make all KVERSION=$kernelver"
BUILT_MODULE_NAME[0]="hello"
DEST_MODULE_LOCATION[0]="/updates"
AUTOINSTALL="yes"

PACKAGE_NAME和PACKAGE_VERSION和文件夹的命名是一致的。

CLEAN的命令是每次build的时候第一条执行的动作。

MAKE[0]用来设定编译的命令,一般情况下是不用设定的。在本例中,就可以把MAKE[0]这行删掉。但在下面这种情况下就需要设定了。比如,您的Makefile里有多个target,分别为all、debug、release等,不指定MAKE[0]时,编译会选择第一个target来执行,也就是make all,如果您想执行make release来编译,就需要在dkms.conf里明确设定。

BUILD_MODULE_NAME[0]用来指定模块的名称,一般情况下也可以不设定。

DEST_MODULE_LOCATION[0]用来设定模块安装的目的地址,本例是"/lib/module/$(KVERSION)/updates"。

AUTOINSTALL=“yes"表示在Linux引导之后DKMS会自动对这个模块执行Build和Install的动作,当然如果模块已经处于该状态的话,相应的动作是不用再执行的。

基于DKMS制作驱动程序的DEB安装包

制作DEB包依赖于dh-make,请首先执行 sudo apt-get install dh-make 安装。

在模块处于"Built State"的条件下,执行 sudo dkms mkdeb -m hello -v 0.1 可以在目录“/var/lib/dkms/hello/0.1/deb”下生成deb包。

DKMS还提供了mktarball和mkrpm来制作tarball和RPM安装包。

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