why wiki

博客通常是用来记录一些完整的文章,每篇文章有一个主题。但是我想把平日里的一些笔记也记录到我的博客里,但笔记是零散的,随时的,不是完整的一个主题。所以打算构建一个 wiki 页面,专门用来存放我的笔记,wiki 页面类似于 维基百科的形式。

我的博客采用的是 hexo 构建的,如果打算 DIY 一个类似于 维基百科 的 wiki 页面的话,对于我来说,也许有点难度,毕竟我只会写简单的网页。那么有没有现成的方案或者替代的方案呢?

答案是有的,那就是 mkdocs

mkdocs 使用

什么是 Mkdocs 呢?

MkDocs is a fast, simple and downright gorgeous static site generator that’s geared towards building project documentation. Documentation source files are written in Markdown, and configured with a single YAML configuration file. Start by reading the introduction below, then check the User Guide for more info.

mkdocs 是一个用 python 编写的静态站点生成工具,主要是用来编写项目文档,文档使用 Markdown 编写,只需要配合一个 YAML 配置文件,就能快速生成一个站点。

毫无疑问,对于我来说,它有以下几个优点:

  • 使用 python 编写(说明有 DIY 的可能)
  • 源文件使用 Markdown 编写
  • 只需要一个 Yaml 文件,非常简单了
  • 主题可选(当然目前来说不是特别多)

可以先看一下 我的wiki.

阅读全文 »

CDN

关于 CDN 是什么,我想应该不用做过多的介绍,毕竟现在是一个 “云” 的时代,你至少也听说过 阿里云 或者 腾讯云 吧,当然其中就包括 CDN 业务。

CDN 的作用有很多,比如可以用来加速网站的访问,可以用来防护网站等。本篇文章讨论的就是使用 cloudflare 作为 CDN 来加速博客网站,并让博客开启 https,提升博客安全等级。

  • 加速网站访问
  • 开启https

选择什么 CDN 呢?

选择 CDN,对于个人博客来说,主要考虑的还是访问速度以及价格,当然也有免费的 CDN。Cloudflare 就是一家提供免费 CDN 的公司,也是在 CDN 领域比较知名的公司。

话不多说,关于 cloudflare 的配置网上可以搜到很多文章,这里我就简单记录一下。

阅读全文 »

缘起

我的博客一直采用的是 github pages 来托管,中间断断续续的也没怎么管理过,偶尔写几篇博客,所以也就没怎么关心过访问速度,搜索引擎收录等问题。

不过我对博客一直还是情有独钟,我觉得像我一样的软件工程师,如果能有个人博客,并且保持一定程度的更新率还是很有必要的。

这次迁移主要考虑三个原因:

  • 访问速度较慢
  • 博客还不支持 https
  • 谷歌搜索引擎收录较少

github pages 服务器位于美国,对于中文博客来说,访问还是有一些慢的,且不说 github 未来在我国很有可能被 feng,所以打算迁移到国内来。之前博客其实是有部署过双线的,国外走 github,国内走 coding,但奈何 coding 不争气,后来我干脆停了 coding 的解析。现在打算找一个付费的香港虚拟主机,一年几十块钱搞定。

另外就是由于之前已经采用了 rebootcat.com 这个域名,所以无法在 github pages 上开启 https(当然方法是有的,比如使用 cloudflare 加速,这个详见我另外一篇博文),所以这次的迁移也打算开启全站 https。

阅读全文 »

前言

一直没有时间来整理下博客搭建的一些事情,现在补上一篇,给 Hexo Next 博客添加一个相册功能,使用瀑布流的方式。

原理说明

  • 使用 github 作为仓库存储图片文件(图床)
  • 使用 jsdelivr 进行图片 CDN 加速

优点

此种方式的优点是免费,不需要购买其他的对象存储产品;并且使用的是 github 作为图床,图片不会丢失。

早期的博文使用的是七牛云的免费存储,结果后来被他们删掉了。。。结果造成文中的一些图片链接都是 404,有兴趣的可以翻一翻我早期的博客。

缺点

由于采用的是 github 仓库存储图片,但是 github 对单仓库有 50MB 的大小限制,所以单仓库可能不能够存储太多的文件;

解决方法就是建立很多的图片仓库(稍微有点费劲,不过是行得通的);另外上传的单张图片大小最好不要太大。

还有个缺点就是得折腾啊,且看我后文。

各位可以参考下我的相册瀑布流: 摄影

开始搭建相册瀑布流

开始之前,需要简单介绍一下,我参考的是 Hexo NexT 博客增加瀑布流相册页面 这篇文章,文中涉及到的脚本主要都是 js 实现;与他不同的是,由于我对 js 的掌握远远不及我对 Python 的掌握,故部分脚本我采用了 Python 实现。

所以在开始操作之前,你可以根据自己的技能,选择不同的方式。如果你擅长 python,那么跟着我来吧。

阅读全文 »

对行业现状的一点看法

2020 年,新冠肆虐。

最近对于区块链的想法有点消极。简单谈一下。

纵观整个区块链行业,公链项目死了很多,还活着的也在拖着,除了少数明星公链。也许未来的几年之内,这种状况还会持续下去,对于公链项目来说,要么死,要么拖下去再死,要么成为明星。无论哪一条路,都异常难!

区块链目前能解决的问题范围依然还是比较局限,一方面受限于技术层面,一方面受限于政治层面。技术方面的难点,在于架构,在于算法设计,在于安全,在于通信。

架构上目前行业普遍追求可扩展的架构,这样的目的在于提高 TPS,为未来可能存在的真实大量业务提供服务,当然必然为此牺牲去中心化属性,牺牲安全属性,抉择就在于追求什么目标?从比特币到以太坊,再到 EOS,再到各种分片的公链,可以理解为逐步为了高 TPS 改良,因为比特币的交易速度实在是太慢了,为此我们势必要有一条满足我们交易需求的公链,至于这个交易频率需要多快,也许可以对标中心化得出答案。

比如 visa 的 TPS 在 1000 ~ 2000,银联在 2000 左右, paypal 600 ~ 1000等等。这里就不得不提出一个疑问,对于公链来说,追求高 TPS 是不是一个伪需求?

算法层面,为了保证零信任网络内部达成一致共识,会涉及到大量的收发包以及加解密过程,然而至今没有一条公链的共识算法能称得上权威或者标杆,整个行业仍然处于研究阶段,不同的公链之间互相参考学习,然后进行创新以及试验。

这个过程可能很漫长,需要大量的人才投入贡献。这也就意味着现阶段的公链,至少在共识算法层面,不能达到一个质的飞越。也许在未来,还得依靠其他的手段做改良。

阅读全文 »

前言

我是一个 linux c++ 开发者,但是一直对 Makefile 的语法很是头痛,每次都记不住,所以每次写 Makefile 都很痛苦,Makefile 里需要你自己编写依赖和推导规则,这个过程能不能简单点呢?

对于编译一个 C++ 工程来说,也许需要的就是头文件路径、库路径、编译参数,剩下的东西基本也不重要,这三样足够去编译一个工程了。所以有没有一个工具能简单点的去实现 C++ 项目的构建呢?

答案是有的,上一篇博文 scons构建C++项目 介绍了 使用 scons 来构建 C++ 项目,大大提高了编写构建脚本的效率,使用起来也极为方便,对于熟悉 python 的童鞋来说真的是大大的福音;但 scons 的问题就是在大型项目的时候构建起来可能会很慢(听说的)。那么有没有其他的工具呢?

当然有,cmake 就是这样的一个工具,既能满足跨平台的编译,并且屏蔽了 Makefile 蛋疼的语法,使用一种更加简单的语法编写构建脚本,用在大型项目也毫无压力。

当然,对于我个人来说,cmake 的使用还是有门槛的,刚接触 cmake 可能还是会被它的语法搞的头疼(cmake 的语法也还是挺折腾的)。但是别急,沉下心来,本篇博文就带你从 cmake 入门到编写一个复杂工程的实战。

CMake

什么是 cmake

这里直接引用官网的解释:

CMake is an open-source, cross-platform family of tools designed to build, test and package software. CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of your choice. The suite of CMake tools were created by Kitware in response to the need for a powerful, cross-platform build environment for open-source projects such as ITK and VTK.

CMake 是一个开源的跨平台的构建工具,语法简单,编译独立,并且很多知名大型项目也在用 CMake,比如 KDE、Netflix 、ReactOS等。

阅读全文 »

前言

我是一个 linux c++ 开发者,但是一直对 Makefile 的语法很是头痛,每次都记不住,所以每次写 Makefile 都很痛苦,Makefile 里需要你自己编写依赖和推导规则,这个过程能不能简单点呢?

对于编译一个 C++ 工程来说,也许需要的就是头文件路径、库路径、编译参数,剩下的东西基本也不重要,这三样足够去编译一个工程了。所以有没有一个工具能简单点的去实现 C++ 项目的构建呢?

答案是有的,Scons 就是答案。

Scons

什么是 scons

这里直接引用官网的解释:

What is SCons?

SCons is an Open Source software construction tool—that is, a next-generation build tool. Think of SCons as an improved, cross-platform substitute for the classic Make utility with integrated functionality similar to autoconf/automake and compiler caches such as ccache. In short, SCons is an easier, more reliable and faster way to build software.

What makes SCons better?

  • Configuration files are Python scripts–use the power of a real programming language to solve build problems.
  • Reliable, automatic dependency analysis built-in for C, C++ and Fortran–no more “make depend” or “make clean” to get all of the dependencies. Dependency analysis is easily extensible through user-defined dependency Scanners for other languages or file types.
  • Built-in support for C, C++, D, Java, Fortran, Yacc, Lex, Qt and SWIG, and building TeX and LaTeX documents. Easily extensible through user-defined Builders for other languages or file types.
  • Building from central repositories of source code and/or pre-built targets.
  • Built-in support for fetching source files from SCCS, RCS, CVS, BitKeeper and Perforce.
  • Built-in support for Microsoft Visual Studio .NET and past Visual Studio versions, including generation of .dsp, .dsw, .sln and .vcproj files.
  • Reliable detection of build changes using MD5 signatures; optional, configurable support for traditional timestamps.
  • Improved support for parallel builds–like make -j but keeps N jobs running simultaneously regardless of directory hierarchy.
  • Integrated Autoconf-like support for finding #include files, libraries, functions and typedefs.
  • Global view of all dependencies–no more multiple build passes or reordering targets to build everything.
  • Ability to share built files in a cache to speed up multiple builds–like ccache but for any type of target file, not just C/C++ compilation.
  • Designed from the ground up for cross-platform builds, and known to work on Linux, other POSIX systems (including AIX, BSD systems, HP/UX, IRIX and Solaris), Windows NT, Mac OS X, and OS/2.

最大特点就是使用 Python 语法来编写编译构建脚本,并且支持依赖自动推导,支持编译 C/C++/D/Java/Fortran等项目,并且是跨平台的(因为 python 是跨平台的)。

阅读全文 »

aliyun_spot

自动创建阿里云抢占式实例。

支持一下作者,购买阿里云

背景

阿里云抢占式实例应该属于阿里云的一种闲置资源利用,性价比非常高,每小时的价格在 0.01 ~ 0.05 每小时,具体根据不同的配置和地域有差别,流量价格小于 1元/G.

抢占式实例最高可以以一折的价格购买 ECS 实例,并能稳定持有该实例至少一个小时。一个小时后,当市场价格高于您的出价或资源供需关系变化时,抢占式实例会被自动释放,请做好数据备份工作。

非常适合爬虫

非常适合爬虫

非常适合爬虫

也适合程序员个人日常开发使用,上班来创建,下班释放,开销基本可以控制在在 1毛 ~ 2 毛。

对于我来说,最近在写一个爬虫,看了很多代理都很贵,免费的又不稳定,正好了解到阿里云的抢占式实例,所以非常满足我的需求。

但是要注意,这个实例是有可能被释放的,但是不用担心,比如香港地区的释放率最近(2020-08-19)小于 3%. 另外,每个人可以最大创建 100 个实例,所以还是不用太担心。

阅读全文 »

Valgrind Massif

valgrind 是什么,这里直接引用其他人的博客:

Valgrind是一套Linux下,开放源代码(GPL
V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。

内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。

Valgrind的体系结构如下图所示:

Massif 命令行选项

关于 massif 命令行选项,可以直接查看 valgrind 的 help 信息:

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

MASSIF OPTIONS
--heap=<yes|no> [default: yes]
Specifies whether heap profiling should be done.

--heap-admin=<size> [default: 8]
If heap profiling is enabled, gives the number of administrative bytes per block to use. This should be an estimate of the average, since it may vary. For example, the
allocator used by glibc on Linux requires somewhere between 4 to 15 bytes per block, depending on various factors. That allocator also requires admin space for freed blocks,
but Massif cannot account for this.

--stacks=<yes|no> [default: no]
Specifies whether stack profiling should be done. This option slows Massif down greatly, and so is off by default. Note that Massif assumes that the main stack has size zero
at start-up. This is not true, but doing otherwise accurately is difficult. Furthermore, starting at zero better indicates the size of the part of the main stack that a user
program actually has control over.

--pages-as-heap=<yes|no> [default: no]
Tells Massif to profile memory at the page level rather than at the malloc'd block level. See above for details.

--depth=<number> [default: 30]
Maximum depth of the allocation trees recorded for detailed snapshots. Increasing it will make Massif run somewhat more slowly, use more memory, and produce bigger output
files.

--alloc-fn=<name>
Functions specified with this option will be treated as though they were a heap allocation function such as malloc. This is useful for functions that are wrappers to malloc or
new, which can fill up the allocation trees with uninteresting information. This option can be specified multiple times on the command line, to name multiple functions.

Note that the named function will only be treated this way if it is the top entry in a stack trace, or just below another function treated this way. For example, if you have a
function malloc1 that wraps malloc, and malloc2 that wraps malloc1, just specifying --alloc-fn=malloc2 will have no effect. You need to specify --alloc-fn=malloc1 as well.
This is a little inconvenient, but the reason is that checking for allocation functions is slow, and it saves a lot of time if Massif can stop looking through the stack trace
entries as soon as it finds one that doesn't match rather than having to continue through all the entries.

Note that C++ names are demangled. Note also that overloaded C++ names must be written in full. Single quotes may be necessary to prevent the shell from breaking them up. For
example:

--alloc-fn='operator new(unsigned, std::nothrow_t const&)'

--ignore-fn=<name>
Any direct heap allocation (i.e. a call to malloc, new, etc, or a call to a function named by an --alloc-fn option) that occurs in a function specified by this option will be
ignored. This is mostly useful for testing purposes. This option can be specified multiple times on the command line, to name multiple functions.

Any realloc of an ignored block will also be ignored, even if the realloc call does not occur in an ignored function. This avoids the possibility of negative heap sizes if
ignored blocks are shrunk with realloc.

The rules for writing C++ function names are the same as for --alloc-fn above.

--threshold=<m.n> [default: 1.0]
The significance threshold for heap allocations, as a percentage of total memory size. Allocation tree entries that account for less than this will be aggregated. Note that
this should be specified in tandem with ms_print's option of the same name.

--peak-inaccuracy=<m.n> [default: 1.0]
Massif does not necessarily record the actual global memory allocation peak; by default it records a peak only when the global memory allocation size exceeds the previous peak
by at least 1.0%. This is because there can be many local allocation peaks along the way, and doing a detailed snapshot for every one would be expensive and wasteful, as all
but one of them will be later discarded. This inaccuracy can be changed (even to 0.0%) via this option, but Massif will run drastically slower as the number approaches zero.

--time-unit=<i|ms|B> [default: i]
The time unit used for the profiling. There are three possibilities: instructions executed (i), which is good for most cases; real (wallclock) time (ms, i.e. milliseconds),
which is sometimes useful; and bytes allocated/deallocated on the heap and/or stack (B), which is useful for very short-run programs, and for testing purposes, because it is
the most reproducible across different machines.

--detailed-freq=<n> [default: 10]
Frequency of detailed snapshots. With --detailed-freq=1, every snapshot is detailed.

--max-snapshots=<n> [default: 100]
The maximum number of snapshots recorded. If set to N, for all programs except very short-running ones, the final number of snapshots will be between N/2 and N.

--massif-out-file=<file> [default: massif.out.%p]
Write the profile data to file rather than to the default output file, massif.out.<pid>. The %p and %q format specifiers can be used to embed the process ID and/or the
contents of an environment variable in the name, as is the case for the core option --log-file.

对其中几个常用的选项做一个说明:

阅读全文 »

缘由

xxnode 是一个程序,执行 xxnode -v 出现卡住不动的情况。

正常情况下,xxnode -v 会打印出程序版本号等信息,而且只是简单的打印版本号就会推出,不涉及到复杂的业务逻辑,但本次碰到的情况是卡住不动了,马上怀疑肯定是出现死锁了。

排查死锁

1
2
$ ps -ef |grep xxnode
8987

上面先找到卡住的 xxnode 的进程号,然后使用 gdb attach 上去:

1
$ gdb attach 8987

然后使用 bt 查看堆栈情况,发现堆栈如下:

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
(gdb) bt
#0 0x00007f9d320df5cc in __lll_lock_wait_private () from /lib64/libc.so.6
#1 0x00007f9d3205bb12 in _L_lock_16654 () from /lib64/libc.so.6
#2 0x00007f9d32058753 in malloc () from /lib64/libc.so.6
#3 0x00007f9d32917ecd in operator new(unsigned long) () from /lib64/libstdc++.so.6
#4 0x00007f9d32976a19 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) () from /lib64/libstdc++.so.6
#5 0x00007f9d3297762b in std::string::_Rep::_M_clone(std::allocator<char> const&, unsigned long) () from /lib64/libstdc++.so.6
#6 0x00007f9d329776d4 in std::string::reserve(unsigned long) () from /lib64/libstdc++.so.6
#7 0x00007f9d3297793f in std::string::append(char const*, unsigned long) () from /lib64/libstdc++.so.6
#8 0x0000000001431d8d in xwrite_log2 (module=0x14d4c14 "xnetwork", file=0x14d7a0b "xmain.cpp", function=0x14db0c0 <get_child_status()::__FUNCTION__> "get_child_status", line=1865,
level=enum_xlog_level_warn, msg=0x14d8a4e "waitpid() failed: ECHILD") at ../../src/xlog.cpp:717
#9 0x0000000000dc4fd5 in get_child_status () at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1865
#10 0x0000000000dc490f in signal_handler (signo=17) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1763
#11 0x0000000000dc4cc8 in xnode_signal_handler (signo=17, siginfo=0x7fffdc448f70, ucontext=0x7fffdc448e40) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1798
#12 <signal handler called>
#13 0x00007f9d32054f41 in _int_malloc () from /lib64/libc.so.6
#14 0x00007f9d320586fc in malloc () from /lib64/libc.so.6
#15 0x00007f9d32917ecd in operator new(unsigned long) () from /lib64/libstdc++.so.6
#16 0x00007f9d32976a19 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) () from /lib64/libstdc++.so.6
#17 0x00000000013e9df1 in char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) ()
#18 0x00007f9d329786d8 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) () from /lib64/libstdc++.so.6
#19 0x00007f9d2fe2d5fa in top::get_md5 () at /home/xchano/src/programs/xtopchano/src/version.cpp:31
#20 0x00007f9d2fe2dc23 in top::print_version () at /home/xchano/src/programs/xtopchano/src/version.cpp:62
#21 0x00007f9d2fe2d24e in init_noparams_component (component_name=0x7f9d32bbf338 <std::string::_Rep::_S_empty_rep_storage+24> "",
pub_key=0x26532c8 "BEANYyQhGD0urjcRfsmqJLctMA5Hpe6DHW4pb6Asm50o/FgY2iU82bamk0WM0GtQXhNQykvy60DMmCHVWGhFR2c=", pub_len=88,
pri_key=0x2656858 "Lv1qWftE/8rQfMxufh+5ywy0maCdWl2ikdNun4wVjAM=", pri_len=44, node_id=0x2653378 "T-0-LWT5UjpGiSPzWs7Gt3Fc8ZmNFFwwA7Gpmt", datadir=0x26517a8 "/chano",
config_file_extra=0x26519e8 "/chano/extra_conf.json") at /home/xchano/src/programs/xtopchano/so.cpp:18
#22 0x0000000000dba3b5 in lib_load (cmd_type=0, config=...) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:176
#23 0x0000000000dc45c0 in child_load_so (cmd_type=0, config=...) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1666
#24 0x0000000000dc4349 in spawn_child (cmd_type=0, config=...) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1611
#25 0x0000000000dc8897 in xnode_master_process_cycle (cmd_type=0, config=...) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:2244
#26 0x0000000000dc092c in main (argc=7, argv=0x7fffdc44bce8) at /home/xchano/src/xtopcom/xnode/src/xmain.cpp:1155
(gdb) b version.cpp:31
Breakpoint 1 at 0x7f9d2fe2d5d4: file /home/xchano/src/programs/xtopchano/src/version.cpp, line 31.

注意看上面的第 2 帧和第 14 帧,都出现了 malloc 函数的调用,而 glibc malloc 内部的实现是有锁的,所以果然是出现了死锁。

上面的执行流程是这样的:

  1. 主进程启动
  2. fork 子进程,子进程会 load 一个 libxxxnode.so
  3. 子进程调用 so 里提供的 print_version() 函数,打印版本信息
  4. print_version() 函数内部有 string 的操作,也就是说会产生 malloc 的调用,正当在 malloc 内部的时候,不知道什么原因触发了某个信号(上面 signo=17)
  5. 程序执行转移到 signal_handler(),处理信号
  6. 但是不巧, signal_handler() 内部操作了日志,也就是也会调用到 malloc 函数
  7. 然后陷入死锁

所以,问题出现在了 signal_handler() 上,该信号处理函数调用了 malloc,而 malloc 是不可重入函数,造成最终的死锁

解决

要解决该死锁问题当然也简单,就是确保信号处理函数 signal_handler() 是可重入函数即可,这样即便在发生上面的场景,也就不会陷入死锁当中。

Blog:

2020-05-25 于杭州
By 史矛革