comparing ubuntu with centos in package management

yum

1
2
3
4
5
6
7
8
9
10
11
12
# find which package the cmd 'a.out' belongs to
yum provides */bin/a.out
# check if 'wget' package is installed
yum list installed | grep wget
# download 'wget' package to /tmp/rpm/
yum install yum-plugin-downloadonly
yum download --downloaddir=/tmp/rpm/ --downloadonly wget
# list all files in 'wget' package
rpm -qlp /tmp/rpm/wget*.rpm
# another method listing files without downloading
yum install yum-utils
repoquery -l wget

apt

1
2
3
4
5
6
# find which package 'Python.h' belongs to
apt install apt-file
apt-file update
apt-file search Python.h
# list all files in 'wget' package
dpkg -L wget

package names

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  ubuntu			  centos
install build-essential		groupinstall "Development tools"
xfce4				@xfce-desktop
ssh				openssh-clients
udev				systemd-udev
libc6-dev			glibc-devel
libglib2.0-dev			glib2-devel
libffi-dev			libffi-devel
libncurses5			ncurses
libncurses5-dev			ncurses-devel
libsqlite3-dev			sqlite-devel
libnet1-dev			libnet-devel
libreadline6			readline
bridge-utils			bridge-utils
libpcre3-dev			pcre-devel
libyaml-dev			libyaml-devel
libmagic-dev			file-devel
liblua5.1-0-dev			lua-devel
libssl-dev			openssl-devel
zlib1g-dev			zlib-devel
lm-sensors			lm_sensors
ifenslave			iputils
ntpdate				ntp
redis-server			redis
libperl-dev			perl-devel
libjpeg-turbo8-dev		libjpeg-turbo-devel
libfreetype6-dev		freetype-devel
iproute2			iproute
iputils-ping			iputils
ifupdown			NetworkManager
net-tools			net-tools
libpython3.8-dev		python3-devel
mysql-server			mysql5-server
libmysqlclient-dev		mysql5-devel
tofrodos			dos2unix
				kernel-headers
				kernel-devel
qemu-user-static
initramfs-tools
gcc-5-plugin-dev
build-essential
libprelude-dev
libzmq3-dev
libhtp-dev
fbset				maybe in xorg-x11-server

deprecated in ubuntu

1
2
sysv-rc-conf
libjpeg-dev # why?

refer to:
https://blog.csdn.net/mimosa2008/article/details/102833837
https://forums.centos.org/viewtopic.php?t=14711
https://blog.csdn.net/xldwhj/article/details/72831674
https://www.cnblogs.com/orcl-2018/p/13276558.html
https://blog.csdn.net/HAOMCU/article/details/7268687
https://mirrors.tuna.tsinghua.edu.cn/help/fedora/

switch between shell and anaconda in serial console

as are indicated at the bottom of the console, there are 5 windows in anaconda, we can toggle any of them by press:

<Ctrl> + <b> + <window number>

in a tmux style.

refer to:
https://docs.centos.org/en-US/centos/install-guide/Trouble-x86/
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/installation_guide/sect-consoles-logs-during-installation-x86

automatic disk performance test

mkdir like test_case1, copy three files below in it:

test_case1/test.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/python3
import os
 
def shell_run(name, cmd):
	os.system('mkdir result')
	os.system(cmd + '> ./result/' + name)
 
def run_cmds(cmds):
	for name, cmd in cmds.items():
		shell_run(name, cmd)
 
cmds = {
	'1_small_read'		: 'fio -name iops -rw=read -bs=4k -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1',
	'1_small_randread'	: 'fio -name iops -rw=randread -bs=4k -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1',
	'1_small_write'		: 'fio -name iops -rw=write -bs=4k -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1',
	'1_small_randwrite'	: 'fio -name iops -rw=randwrite -bs=4k -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1',
	'1_big_read'		: 'fio -name iops -rw=read -bs=10M -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1',
	'1_big_randread'	: 'fio -name iops -rw=randread -bs=10M -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1',
	'1_big_write'		: 'fio -name iops -rw=write -bs=10M -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1',
	'1_big_randwrite'	: 'fio -name iops -rw=randwrite -bs=10M -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1',
	'4_small_read'		: 'fio -name iops -rw=read -bs=4k -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1 -numjobs=4',
	'4_small_randread'	: 'fio -name iops -rw=randread -bs=4k -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1 -numjobs=4',
	'4_small_write'		: 'fio -name iops -rw=write -bs=4k -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1 -numjobs=4',
	'4_small_randwrite'	: 'fio -name iops -rw=randwrite -bs=4k -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1 -numjobs=4',
	'4_big_read'		: 'fio -name iops -rw=read -bs=10M -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1 -numjobs=4',
	'4_big_randread'	: 'fio -name iops -rw=randread -bs=10M -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1 -numjobs=4',
	'4_big_write'		: 'fio -name iops -rw=write -bs=10M -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1 -numjobs=4',
	'4_big_randwrite'	: 'fio -name iops -rw=randwrite -bs=10M -runtime=60 -iodepth 32 -filename /dev/sda6 -ioengine libaio -direct=1 -numjobs=4'
}
run_cmds(cmds)

test_case1/parse.cpp

Read more

罗马历史年表

罗马时期

罗马十二主神
	Diana/Dĭāna: 狄安娜/月亮与橡树女神/Artemis、赫卡忒、腓尼基神话的刻勒斯塔女神
Janus: 雅努斯/门神
Somnus: 索莫纳斯/睡神
Bellona: 贝罗纳/战争女神/Mars之妻/Enyo

Flora: 芙罗拉/克罗丽丝/花神
Zephyr: 西风之神

Gaul: 高卢

努米托/兄弟阿穆利乌斯
	雷亚西尔维亚/女祭司/维斯太贞女/被阿穆利乌斯丢弃

Mars: 马尔斯/战神
	Remus: 莱姆斯/雷亚西尔维亚之子
	Romulus: 罗慕路斯/雷亚西尔维亚之子/杀兄成战神奎里纳斯

Lupin: 狼人

Alexander the Great: 亚历山大/前356-前323

Carthage: 迦太基/前8世纪-前146年
	Hannibal Barca: 汉尼拔_巴卡/前247-前183

Gaius Julius Caesar: 恺撒/前100-前44
Spartacus: 斯巴达克斯/前69

Nero: 尼禄/37-68/
Decius: 德修/249-251/全国性地迫害基督教
Constantine the Great: 君士坦丁一世/272-337/第一位信仰基督教的皇帝
Theodosius I: 狄奥多西一世/347-395/330年将帝国一分为二/393年宣布基督教为国教
	西罗马/476年在蛮族的侵袭下灭亡
		800年罗马教宗里奥三世为查理曼大帝加冕
		814-858,Pope Joan/约翰娜/女教皇
	Byzantine Empire: 拜占廷帝国/395-1453/东罗马/东正教
		Julia: 尤利安/361-363/apostate
		希拉克略/620年定希腊语为官方语言
		726年利奥三世下令捣毁帝国境内所有圣象
		查士丁尼/将北非以西、意大利和西班牙的东南并入版图,554年击败法兰克王国
		1054年天主教和东正教大分裂
		1095年穆斯林再度兵临城下,东正教向天主教求救,罗马教宗乌尔班二世发起了第一次十字军东征
		1204年首都君士坦丁堡被第四次十字军东征洗劫

Ammonius Saccas: 阿摩尼阿斯 萨卡斯/175-242
	Plotinus: 普罗提诺/204-269/新柏拉图主义

Philo Judeaus: 斐洛尤迪厄斯/前15-40
Pantaenus: 潘代诺
	Titus Flavius Clemens: 革利免/150-215/
		Origenes Adamartius: 俄利根/185-254/

Athanasius: 296-373
Theophilus: 提阿非罗/391/
	Cyril: 西里尔/375-444/

Theon: 赛翁
	Hypatia/: 希帕蒂娅/370-415/
		Orestes: 奥瑞斯特斯/Alexandria总督
		Synesius of Cyrene: 辛乃西斯

希波的奥古斯丁/Augustine of Hippo/354-430

Damascius: /逃亡波斯

1265-1321	Dante Alighieri/但丁·阿利吉耶里

1479-1555	Juana/胡安娜/西班牙女王

?-1557		塔塔尼亚/口吃
			卡尔丹诺/三次方程一般解/令y = x + (a over 3)/给自己算死期不准,自杀
			费拉里/四次方程一般解

1776-1856	Ameldeo Avogadro/阿伏伽德罗

			开普勒/死在讨薪

1824		阿贝尔/Norway/无钱出论文/肺结核

Carthagia: 迦太基

Punic Wars: 布匿战争
巴卡家族
	汉尼拨

圣经

莎乐美和圣约翰
约阿施
	Gideon: 吉迪恩
印第安人

大航海

1452-1506	Columbus/哥伦布
1480-1521	Ferdinand Magellan/麦哲伦

refer to:
闲时乱翻书

德国历史年表

Prussia 普鲁士

1
2
3
4
5
1224		Deutscher Orden 条顿骑士团建国
1512		Albrecht von Brandenburg-Ansbach 阿尔布雷希特
1483-1546	Martin Luther 马丁_路德
 
1712-1786	Friedrich II 腓特烈二世/七年战争

Austria 奥地利
Hungary 匈牙利
Bohemia 波希米亚 = Czech 捷克
Bavaria 巴伐利亚 = Bayern 拜仁

1
2
3
4
5
1576-1612	Rudolf II/欧洲三十年战争
1717-1780	Maria Theresa 玛丽亚_特蕾莎/Franz Stephan 弗朗茨_斯蒂芬之妻
			Marie Antoinette 玛丽_安托瓦内特/路易十六王后
1837-1898	Elisabeth Amalie Eugenie 茜茜公主/被刺杀
1863-1914	Archduke Franz Ferdinand of Austria 弗朗茨·斐迪南大公/茜茜公主之侄/被Gavrilo Princip刺杀的萨拉热窝事件

二战

1
2
3
4
193910月		纳粹完全占领波兰/盖世太保搜杀波兰精英人士/卡廷惨案
1943年初		德军在斯大林格勒战役中惨败
1943419日		华沙犹太人起义
194481日		华沙起义/避免波兰被苏联赤化

沙俄

1
2
3
4
5
彼得一世
Catherine I 叶卡捷琳娜一世
安娜女皇
伊丽莎白女皇
叶卡捷琳娜二世

refer to:
杨藩讲艺术
黄娜老师
https://baike.baidu.com/item/%E5%8D%8E%E6%B2%99%E8%B5%B7%E4%B9%89/13013998

how to turn off fans in linux

after running 'sensors-detect' command, kernel modules controlling fan speed should be loaded.

here is an occasion with 'Nuvoton NCT6102D/NCT6104D/NCT6106D Super IO Sensors' detected.

as nct6775 document said:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#/sys/class/hwmon/hwmon4
pwm[1-7]
    - this file stores PWM duty cycle or DC value (fan speed) in range:
	   0 (lowest speed) to 255 (full)
 
pwm[1-7]_enable
    - this file controls mode of fan/temperature control:
	* 0 Fan control disabled (fans set to maximum speed)
	* 1 Manual mode, write to pwm[0-5] any value 0-255
	* 2 "Thermal Cruise" mode
	* 3 "Fan Speed Cruise" mode
	* 4 "Smart Fan III" mode (NCT6775F only)
	* 5 "Smart Fan IV" mode
...

ps1. if set pwm[1-7]_enable to 0, we will not be able to change it to other value, unless reboot linux.
ps2. I still can't find a common way to turn off fans using ipmitool.

IPMI: Intelligent Platform Management Interface
BMC: Baseboard Management Controller
FRU: Field Replaceable Unit

refer to:
[linux-kernel-src-root]/Documentation/hwmon/nct6775.rst
https://bbs.archlinux.org/viewtopic.php?id=225349

怀念故乡吕巷街

魏征丞相上书唐太宗《十思疏》开篇所云:知木之所长者,必固其根本,欲流之远者,必浚其泉源。

吕巷街,我之故乡,生我养我的地方。

公元一九四七年(丁亥九月十九日),我出生于吕巷街一谢姓穷苦人家。从孩童时起经历了少年,青年,直至结婚生子。自读书启蒙,中途辍学,跟父学艺,经四清运动,文化大革命,直至下放务农,从教,最终返迁城镇。于一九八六年我从吕巷街举家迁入汉川城关镇,至今已三十余载,人逾暮年,我尝尽了人间的苦辣酸甜。

吕巷街上记录了我成长的历程,已逝的青春,似流水。重回故地,仿佛在昨天。为了不能忘却此记忆,在同乡律师李晓云贤弟的倡议下,特撰此文以示怀念之!

此房产属原吕巷粮店原址原貌

古老的吕巷街,她有数百年的厚重历史。沿吕巷街之南有一条婉延曲折的中支河,从西向东依傍着流淌而过。西接天门河上游,东临汉川汈汊湖,穿越新沟闸,途经长江可直达省府大武汉。

忆往昔,吕巷街素有“小汉口”之美誉。由于水路交通之便利,商贾舟楫由此经过停泊,外地客商纷至沓来。白天,随州,鹤峰,钟祥,天门等至武汉的客轮往返如梭。吕巷河上汽笛长鸣悦耳,碧波荡漾,鹅鸭游嬉,鱼翔浅底,两岸翠柳迎风招展,万顷稻香,沁人心脾。正所谓:船在水上走,人在画中游。夜晚,水天一色,灯火闪烁,舟船停靠,桅杆林立,繁星璀璨,美不胜收。由此,带来了吕巷街上各行各业日益繁荣兴旺。由商家自发出资,从东至西修建了一条上千米长,五米多宽,全用青石板铺成的街道。街心石板年深月久,被过往行人踩踏得锃光发亮。街道两旁南北朝向,店门相对而建,密簇而立。多为木制式砖瓦结构的住房,矮小而拥挤,似乎显得简陋而压抑。街上餐馆旅店有左恒茂,吕祥兴,谢永发,黄同顺等,勤行白案有曾万发,谢川记,周义成,蒋正发等。百杂货店有广昌和,黄太和,方柱兴,刘裕泰等。药铺有仁寿堂,熊裕德等。糟坊有聂成兴,廖太兴等。铁木竹缝纫理发,各行各业应有尽有。昔时的吕巷街商业之繁荣,历史之悠久,借此可见一斑。各行业的招牌颇具韵味和内涵,或以人名,或以业名,或寄希望而命名。更重要的是这为远近十里八乡的老百姓生活生产提供了必需之便利。

此处是原吕巷卫生所旧址原貌

吕巷街上及周边的人们在忙碌之余,特别在冬春闲时,如雨天,夜晚及传统节日,听善书讲故事,唱皮影,划龙船,耍龙舞狮,划彩船,搭台唱戏。时常有听善书,流泪伤心,替古人担忧的,有看皮影熬夜,挨老婆责骂拌嘴的,有看花鼓戏入迷者忧闷不乐与媳妇分居呕气的,有饮酒品茶呼朋唤友,眉飞色舞谈天侃地,逸兴遄飞的,有贪杯醉,不辨杨柳,指桑骂槐的。凡此种轶人趣事,引人记忆犹新,历历在目。吕巷街一年四季过往商人,纤夫渔翁,相士游医,三教九流,来来往往热闹非凡。每逢夏季夜晚纳凉时,有兴趣者邀朋约友从四面八方凑集到沿河树下,或围聚在房前屋后的场地上说书讲笑话,唱小曲小调,谈笑风生或打情骂俏。真可谓乡亲邻里其乐融融!

此乃是五、六十年代建的民房原貌

吕巷街人素有文化底蕴和内涵,每年春节期间,一进入腊月,忙着熬糖打豆腐做糍粑,腌制腊肉和腊鱼。自发组织耍狮舞龙划彩船,办置年货写春联。直到除夕,挨门挨户悬挂各式各样彩灯,照得门上对联通红,一片火红,熠熠生辉,喜气洋洋。鞭炮声此起彼伏,团年饭过后,成群接队的街坊邻里,一路沿街欣赏各家门前对联的寄托寓意及书法功底,并相互道贺新禧。印象深刻的是中药店仁寿堂门前有一幅联语:“活法法中生活法;奇方方内有奇方”,写尽了祖国传统中医神奇与奥妙;熊裕德家对联用墨汁兑蓝汞药水而书写金光耀眼;还有我家弟忠慧春联由其本人书写,简直龙飞凤舞,大气磅礡。至今,我记忆犹新。

吕巷街之南的中支河现状

吕巷街人对戏曲艺术鉴赏品味颇高,以至对举手投足,媚态眼神,台步水袖要求极高,否则就哄台。那时经常有各地马戏班,河南豫剧团,京山楚剧班来吕巷街安营扎寨,一演就是十天半月,甚至时日更长。演出时人山人海,来往络绎不绝,场面蔚为热闹壮观。然而,吕巷街附近较大村庄,几乎每个族氏都有自发的业余剧团。在人们中曾流传“谢家的戏,周家的衣。某家闻骚气。诙谐幽默调侃之说。

吕巷中支河边现今的景象

吕巷街杂货店老者胡炳官前辈,酷爱唱戏,与地方名角何守宽私交甚密。我孩童时,有爱好者相邀,常到老人家学唱演练,《白扇记》、《翠花女捡过》、《绣荷包》、《赶子放羊》、《辞店》、《站花墙》等剧目。斯地锣鼓琴声不绝入耳,热闹不凡。因我手聪字写得较好,经常受别人之托为其抄写剧本。吕巷街设有公立小学,十里八乡在该校学生近千名。可见吕巷人尚学重教古来已然,五十年代我上小学时,在六一儿童节,上级组织各校在倪集小学公演时,吕巷小学由周长年妆花旦,周年青扮小生,我演丑角,一曲《绣荷包》花鼓戏一举夺魁获奖,令我终生难以忘怀!

此处是五十年代之前竹业社的发源地

吕巷街西南方不远处名叫泥头咀及周家河湾。小街之东中途几米处未建房屋,名曰寡堤。再延伸数十米住着几十户吕姓人家。街东北不远处名曰红陂湖,红陂山是谢家西份的藕湖和旱田。六十年代初,为抗旱排涝之需,在此兴建了吕巷闸。斯地逐渐形成了吕巷人民公社,吕巷供电站,供销合作社,食品水产,卫生所,农具厂等单位所在地。吕巷街随着历史的变迁,延伸发展依堤而建。这里地势较好,又有水陆交通之便利。真可谓人杰地灵,物华天宝,商家云集,百业兴隆。因此吕巷街很早就成为了经济文化政治中心一一吕巷第七区公所,吕巷乡人民政府,吕巷人民公社所在地。

这是如今的吕巷河,此图律师李晓云摄

昂首极目,登高远眺,东观新堰韩集横堤,南望中州赤壁陡埠,西瞻蒋场芦市天门,北瞩天鹅丁集应城。置身于纵横交错湖汊沟渠的青纱帐之间,古老而神奇的吕巷街,桃花红似火,杨柳绿如烟,美轮美奂。每当夜幕降临,满街灯火通明,天上星月映照,可谓到处灿烂辉煌,仿佛进入天上人间亦幻亦真。身处其中,定然忘乎天地,超然物外。弹丸之地的吕巷街源通四海,气贯八方,人财两旺。不乏陶朱漪顿,智者蔚起。如我辈之前贤、后秀者层出不穷:刘正量曾任孝感地区邮政局局长,谢德先曾任北京市某厂处长,左延年曾任陕西省外事厅主任,王志成曾任河北保定监狱党委书记,谢贞玉曾任大悟县人民医院外科主任医师,熊德先曾任汉川市中医院主任医师,李原生曾任四川省某军工厂总务科长注册会计师,饶江波曾任长航办公室主任,胡圣斌曾任汉川戏剧团知名琴师,黄广明曾任《南方周末》知名记者,谢雄军供职于海南省高级人民法院,李碧清中科院留美博士,晏建华居美博士。一批批外出的金凤凰为吕巷街乡亲增光添彩!

此处是抗日侯排长三烈士之墓地,原来是一位名董启发石匠老师傅刻制的石碑,因年深月久无迹,后来由本街知名律师李晓云行文申报,由政府拨款近年修建此碑

斗转星移,岁月如歌。历史见证,孰能无情。一方水土养育一方人,吕巷街人的聪明才智,创造出了不平凡的业绩。吕巷街以她繁荣的经济,丰富的文化之底蕴,孕育并成就了一代代出类拔萃的吕巷人。也涌现出一辈辈可歌可泣的英雄豪杰。她不仅以其悠久的文化和经济基础,更因有前辈英雄们的不朽业绩和精神丰碑,血染的风采,激励着下一代昂首阔步,肩负着吕巷人光荣使命继续前行!

我以吕巷街辉煌的历史,吕巷人多彩的人生,而倍感骄傲和自豪!

还尚在的民居旧貌。门楣匾额是吕巷中支河之南对岸张家湾一位名何明哲的老先生擅写的魏碑体遗迹

岁在甲午初夏写于湖北省汉川市城关宝树书画斋

c++ move forward

lvalue: loactor value
rvalue: read value

here is an example of class definition with only one constructor using template and 'forward' technique to tackle varying parameter:

rvalue.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <functional>
 
using namespace std;
 
template <typename T>
constexpr bool is_lvalue(T&) {
	return true;
}
 
template <typename T>
constexpr bool is_lvalue(T&&) {
	return false;
}
 
class MyStr
{
public:
	template<class T>
	MyStr(T&& t)
	{
		set(forward<T>(t));
	}
	template<class T>
	void set(T&& t)
	{
		if constexpr (conjunction_v<is_convertible<T&, MyStr&>>)
		{
			if (!is_lvalue(forward<T>(t)))
			{
				str_ = forward<string>(t.str_);
				cout << "is rvalue, type MyStr." << endl;
			}
			else
			{
				str_ = t.str_;
				cout << "is lvalue, type MyStr." << endl;
			}
		}
		else
		{
			str_ = forward<T>(t);
			cout << "is string." << endl;
		}
	}
	template<class T=char>
	void test()
	{
		cout << "test T size is " << sizeof(T) << endl;
	}
	string str_;
};
 
int main()
{
	cout << "call constructor:" << endl;
 
	MyStr str1("str1");
	MyStr str2(str1);
	MyStr str3(move(str1));
 
	cout << "call member function:" << endl;
 
	str1.set("set1");
	str2.set(str1);
	str3.set(move(str1));
 
	str3.test();
 
	return 1;
}

make

1
g++ -o rvalue -g -O0 -std=c++17 rvalue.cpp

output

1
2
3
4
5
6
7
8
call constructor:
is string.
is lvalue, type MyStr.
call member function:
is string.
is lvalue, type MyStr.
is rvalue, type MyStr.
test T size is 1

refer to:
http://www.javashuo.com/article/p-olwxiggw-q.html
http://c.biancheng.net/view/7829.html
https://zhuanlan.zhihu.com/p/99524127