公司有线网是电信的,访问境外的服务器会间歇性得无法访问,但有一个无线网是移动的,用手机测试发现一直没什么问题,所以搞了个无线网卡连无线网。

但电脑的数据默认只走有线网,只有关了有线网才能用无线网,但是公司内部的服务只能通过有线网访问,所以就尝试了以下方案:

  • 192.168.1.0/24 网段走有线网,其他走无线网
  • 只在访问境外服务器的时候走无线网

经过测试确定这两个方案都能满足我的需求,但是由于无线不如有限稳定,所以最后确定使用第二种方案。

调整路由的工具使用 ip 命令的子命令 route,除了路由子命令还有很多其他网络相关的功能,具体可以查看 ip 命令的 man 手册。

linux 下当连接无线网和有线网之后使用 ip route,可以简写为 ip r 可以列出当前所有的路由规则:

1
2
3
4
5
6
default via 192.168.1.1 dev enp2s0 proto static metric 100
default via 192.168.1.1 dev wlx200db012a6be proto dhcp metric 600
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.1.0/24 dev enp2s0 proto kernel scope link src 192.168.1.208 metric 100
192.168.1.0/24 dev wlx200db012a6be proto kernel scope link src 192.168.1.74 metric 600
192.168.252.0/24 dev xdroid0 proto kernel scope link src 192.168.252.1

其中 enp2s0 是有线网相关的路由,wlx200db012a6be 是无线网相关的路由,此外还有 docker 和 xdroid 相关的,后面这两个不用管,没有安装这两个程序的机器也不会有这两种路由。

从上面的路由规则可以看到 enp2s0 相关的 metric 值为 100 而 wlx200db012a6be 相关的 metric 值为 600,这就表示 enp2s0 相关的路由规则比 wlx200db012a6be 相关的路由规则优先级高。在相同的条件下,如相同的 prefix(IP 地址段)时系统将优先使用 enp2s0。

例如以下这两条规则,就表示访问 192.168.1.* 就会走 enp2s0:

1
2
192.168.1.0/24 dev enp2s0 proto kernel scope link src 192.168.1.208 metric 100
192.168.1.0/24 dev wlx200db012a6be proto kernel scope link src 192.168.1.74 metric 600

此外要注意调整路由规则需要 root 权限。

阅读全文 »

klib 的项目地址:https://github.com/attractivechaos/klib/

klib 官方文档:http://attractivechaos.github.io/klib/

总的来说整个 klib 库小巧且功能强大,各种实现之间没有依赖,大多只需要 include 头文件即可使用,目前已经使用过的有 khash 和 klist,这里先说我个人在使用 khash 的过程中遇到的问题和使用过程中需要注意的点。

上面的 klib 官方文档链接中有不少入门的小例子,但也只是简单的使用方法,很多接口并没有提及,不过也有可能作者没有及时更新文档中的例子。

在使用过程中要时刻谨记 khash 中给出的接口都是宏,这也意味着在调试的时候很不方便,如果搞不懂一个接口的意思或者想知道这个接口都做了什么,最好还是看一下 khash.h 中的实现。

就我个人来说,最想提出要注意的一点是 内存释放,在过去一段时间内,我手上依赖 klib 的程序一直都有内存泄漏,不过我始终没有找到原因,直到昨天我才终于定位到是 khash 相关的数据没有被彻底释放。导致这一问题出现的重要原因也在于我接手这个项目时,khash 相关的释放代码已经大量存在了,以至于先入为主,令我以为正确的释放操作就应该就那样,昨天定位到这个问题后我仔细分析了一下 khash 的源码,发现一直以来程序里关于销毁释放内存的操作,一直都是错误的。下面只会使用正确的方法(至少目前我认为是正确的:)),以免误导了其他人。

khash 里面提供了两种数据结构:hashmap 和 hashset,两种结构的使用方法大致是一样的,可以把 hashset 结构看作是没有值的 hashmap。

demo1

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
// 声明 hashmap 名字和键值的类型
// 这个宏还定义了大量的函数,不过一般都不直接使用那些函数,而是使用通用的接口宏
// 这里声明了一个键值对是 int:int 的 map 类型,注意这里只是声明了一个 类型
// 这个 map 类型的名字是 MAP_int2int,map 的名字后面会经常用到
// 此外,这个宏是对宏 KHASH_INIT 的一个简单封装
// 如果有复杂需求要使用 KHASH_INIT,则需要提供两个 hash 函数,我没有用到就不举例了
// hash 函数的实现可以参考 khash 里已有的来实现
KHASH_MAP_INIT_INT(MAP_int2int, int);

khash_t(MAP_int2int) *map_int2int = kh_init(MAP_int2int); // 使用上面声明的 map 类型声明并初始化一个变量

khiter_t iter = 0; // 访问键、值都需要使用 iter
iter = kh_get(MAP_int2int, map_int2int, 100); // 查看 100 这个键是否在 map 中存在

if (iter == kh_end(map_int2int)) { // 不存在
int ret = 0; // 将 100 这个键放入 map,ret 是一个额外的返回值,用来表示放入操作的结果
iter = kh_put(MAP_int2int, map_int2int, 100, &ret); // 放入成功后返回值 iter 即表示为 100 这个键在 map 中的位置
}
kh_value(map_int2int, iter) = 200; // 使用 iter 来存值,值为 200
/* 至此 map 中就有了 100:200 这一个数据 */

// 遍历 map
for (iter = kh_begin(map_int2int); iter != kh_end(map_int2int); iter++) {
if (!kh_exist(map_int2int, iter)) { // 这里的判断一定要有
continue;
}
do_something_foo(kh_key(map_int2int, iter));
do_something_bar(kh_val(map_int2int, iter));
}
/* khash 里还提供了更便利的遍历宏:kh_foreach 和 kh_foreach_value */

kh_del(MAP_int2int, map_int2int, iter); // 当确定一个 iter 之后,可以移除一个键值对
kh_clear(MAP_int2int, map_int2int); // 清空这个 map 的所有数据

do_something_zzz(map_int2int); // 清空后的 map 还可以继续使用,比如继续往里面存值

kh_destroy(MAP_int2int, map_int2int); // 销毁 map,销毁之后就不能再使用了

demo2

阅读全文 »

在 linux 下补全的配置一般发行版都给默认配了,原本以为在 OSX 下用 brew 装个 bash-completion 包,再在 bashrc 下贴两行配置也就搞定了,没想到不行,OSX 下 bash-completion 包有两个,另一个是 bash-completion@2,这两个包分别对应 bash 的两个版本,具体可以用 brew info bash-completion@2 来看。

而且 bash-completion@2bash-completion 相比还要多一行配置,总之就是需要下面两行配置才行,原因是大多软件包都只提供了旧版本的 completion 文件,新版的没有提供支持,所以下面的第一行就是声明要兼容下旧的补全文件:

1
2
export BASH_COMPLETION_COMPAT_DIR="/usr/local/etc/bash_completion.d"
[[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh"

除此之外还有个问题,我自己设置了很多别名 alias,但是这些自定义的别名默认是不支持参数补全的,想要如丝般顺滑就要用另一个项目:https://github.com:cykerway/complete-alias,具体的使用方法就不赘述了,可以去项目里看看怎么配置。

经过实践发现这个项目也不支持旧版的 bash-completion 包,会提示 _completion_loader 找不到的错误,改装 bash-completion@2 就好了。

使用 let’s encrypt 证书颁发机构的免费证书,如果想看官方的文档访问下面的链接,官方文档提供了各种方案来启用 https,我使用是推荐的 Certbot 方案。

https://letsencrypt.org/zh-cn/getting-started/

本文摘自的 Certbot 官网的针对 Ubuntu+Nginx 方案的教程,原文链接如下。

https://certbot.eff.org/lets-encrypt/ubuntuxenial-nginx

如果你的系统不是 Ubuntu 16.04 或者使用的 web 服务不是 nginx 可以去下面的链接重新选择教程。

https://certbot.eff.org

正文开始:

使用 ssh 或任何方法登陆到运行 web 服务的服务器,注意账户需要有 root 权限。

使用下面的命令添加 Certbot PPA 到系统中:

1
2
3
4
5
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update

使用如下命令安装 Certbot:

1
sudo apt-get install certbot python-certbot-nginx

阅读全文 »

翻译自 clickhouse 官方入门教程第二节:https://clickhouse.yandex/tutorial.html

将 clickhouse 部署到集群

ClickHouse 集群是一个 homogenous 集群. 设置步骤是:

  1. 在集群内所有机器上安装 ClickHouse 服务端
  2. 在配置文件中设置集群相关的配置
  3. 在集群内每台机器上创建本地表
  4. 创建分布式表(Distributed table

在 ClickHouse 集群中分布式表事实上是一种关联到每台机器本地表的 view。对分布式表执行查询将使用集群中所有分片(译者注:一个分片即为集群中的一台机器)的资源。可以为多个集群指定配置,并创建多个分布式表以提供对不同集群的 view

下面的配置是一个有三个分片的集群,每个分片将数据保存到一个副本中(译者注:数据只有一份,没有副本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<remote_servers>
<perftest_3shards_1replicas>
<shard>
<replica>
<host>example-perftest01j.yandex.ru</host>
<port>9000</port>
</replica>
</shard>
<shard>
<replica>
<host>example-perftest02j.yandex.ru</host>
<port>9000</port>
</replica>
</shard>
<shard>
<replica>
<host>example-perftest03j.yandex.ru</host>
<port>9000</port>
</replica>
</shard>
</perftest_3shards_1replicas>
</remote_servers>

创建本地表(译者注:待确定是否在所有分片上都创建本地表):

1
CREATE TABLE ontime_local (...) ENGINE = MergeTree(FlightDate, (Year, FlightDate), 8192);

创建分布式表,提供集群中本地表的 view:

1
2
CREATE TABLE ontime_all AS ontime_local
ENGINE = Distributed(perftest_3shards_1replicas, default, ontime_local, rand());

可以在集群的每个机器上都创建间一个分布式表。这将允许在集群中的任何一个机器上都能执行分布式查询。除了分布式表还可以使用 remote 表函数。

接下来在分布式表上执行 INSERT SELECT,以将该表扩展到多个服务器。

1
INSERT INTO ontime_all SELECT * FROM ontime;

阅读全文 »

目前 vscode 的 vim 插件支持在不同输入模式下自动切换输入法,可是 vscode 的配置目前不支持判断操作系统, 所以关于输入法切换的相关配置不能在 mac osx 和 linux 下通用,但是又不想因为这个问题维护两份 vscode 的配置文件,所以就想办法在 linux 使用 shell 脚本模仿了一个 im-select 的命令。

关于 vscode vim 插件输入法相关的配置可以到下面的链接中查看:

https://github.com/VSCodeVim/Vim#input-method

按照上面的链接在 mac osx 下安装 im-select 并在 vscode 中进行如下配置:

1
2
3
4
"vim.autoSwitchInputMethod.enable": true,
"vim.autoSwitchInputMethod.defaultIM": "com.apple.keylayout.ABC",
"vim.autoSwitchInputMethod.obtainIMCmd": "/usr/local/bin/im-select",
"vim.autoSwitchInputMethod.switchIMCmd": "/usr/local/bin/im-select {im}"

关于 vim.autoSwitchInputMethod.defaultIM,我在 mac osx 下用的英文输入法是 com.apple.keylayout.ABC,也可能是其他选项如:com.apple.keylayout.US,这个要根据具体情况来设置。

需要注意的是这个选项的值关系到下面脚本的内容,需要保持一致。

上面的配置在 linux 下会报错,因为上面在 mac osx 下安装的 im-select 没有提供对 linux 的支持,im-select 项目里建议直接使用 fcitx-remote 命令,链接如下:

https://github.com/daipeihust/im-select#fcitx

鉴于不想维护两份 vscode 配置文件的原因,分析了一下 vscode 调用 im-select 的情况,在 linux 使用 shell 实现了一个仿冒 mac osx 下 im-select 的脚本,脚本内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env bash

# this script in order to fake the im-select in mac osx for fcitx in linux

IM_EN="com.apple.keylayout.ABC"
IM_CN="2"

FCITX_CMD=/usr/bin/fcitx-remote

# there is no arguments that means query the current im
if [[ -z $@ ]]; then
if [[ "1" == $($FCITX_CMD) ]]; then
echo "$IM_EN"
else
echo "$IM_CN"
fi
else
if [[ "$@" == "$IM_EN" ]]; then
$FCITX_CMD -c
elif [[ "$@" == "$IM_CN" ]]; then
$FCITX_CMD -o
fi
fi

注意根据实际情况修正脚本中 IM_EN 变量的值,将脚本内容复制并保存到 /usr/local/bin/im-select 文件中即可,记得给文件增加可执行权限。

阅读全文 »

在 c/cpp 中可以使用 typedef 来给一个类型搞个别名:

1
2
3
4
5
typedef int myint;

// 下面 a 和 b 的类型都可以说是 int
int a;
myint b;

而函数别名的语法有些不同:

1
2
3
4
5
6
7
8
int max(int, int);
typedef int (* max_func_t)(int, int);

max_func_t max_alias;

// 下面的两个调用是等价的
max(1, 2);
max_alias(1, 2);

将函数指针作为参数时,使用函数别名可以大大提高代码的可读性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int max(int, int);

// 不使用别名,接收一个函数指针并调用
void test(int (* max_func)(int, int)) {
max_func(1, 2);
}
test(max);

typedef int (* max_func_t)(int, int);

// 使用别名,接收一个函数指针并调用
void test(max_func_t max_func) {
max_func(1, 2);
}
test(max);

函数别名的语法一直让我很奇怪写着也很难受,今天又碰到要将函数作为参数传递的情况,于是谷歌了一下,其实可以这样理解:

1
2
3
4
5
6
7
// 正确语法
typedef int (*max_func_t)(int, int);
// ^ ^ ^
// 返回类型 别名 参数

// 错误语法,但有助于理解(强行理解)
typedef int (*) (int, int) max_func_t;

以前只知道素数的定义:质数又称素数。一个大于 1 的自然数,除了 1 和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数。今天在学习《编程之法》(github) 时,第一章第二节 “字符串包含”,其提到的一种算法用到了素数,在此仅摘取原文部分内容作为记录。需要注意的是,这种算法并不是这个问题的最优解,具体请查看原文。

以下为原文摘录:


字符串包含

题目描述:
给定两个分别由字母组成的字符串 A 和字符串 B,字符串 B 的长度比字符串 A 短。请问,如何最快地判断字符串 B 中所有字母是否都在字符串 A 里?

为了简单起见,我们规定输入的字符串只包含大写英文字母,请实现函数 bool StringContains(string &A, string &B)

比如,如果是下面两个字符串:

String 1:ABCD

String 2:BAD

答案是 true,即 String2 里的字母在 String1 里也都有,或者说 String2 是 String1 的真子集。

阅读全文 »

翻译自:https://www.tonymacx86.com/threads/guide-how-to-patch-dsdt-for-working-battery-status.116102/

转载请注明出处

背景

因为电脑中的电池硬件与苹果的 SMbus 硬件不兼容,所以在笔记本电脑上运行 OS X 时,我们使用 ACPI 来访问电池状态。一般来说,我建议你使用 ACPIBatteryManager.kext,可在这里找到: https://github.com/RehabMan/OS-X-ACPI-Battery-Driver

AppleACPIPlatform 的更高版本无法正确访问 EC(嵌入式控制器)中的字段。由于各种 ACPI 方法(_BIF,_STA,_BST等)失效,这会导致 ACPIBatteryManager 获取电池数据出现问题。尽管可以使用较旧版本的 AppleACPIPlatform(来自 Snow Leopard),但还是希望使用最新版本的AppleACPIPlatform,因为对于具有 Ivy Bridge CPU 的计算机,它可以为这些计算机启用本机电源管理。要使用最新版本,必须更改 DSDT 以符合 Apple 的 AppleACPIPlatform 的限制。

特别是,EC 中大于 8 位的任何字段都必须更改为 8 位。 这包括 16、32、64 甚至更大的字段。

你应该先熟悉 DSDT/SSDT 打补丁的基本知识: http://www.tonymacx86.com/yosemite-laptop-support/152573-guide-patching-laptop-dsdt-ssdts.html

已存在的补丁

首先,你的笔记本电脑可能已经有补丁了。请看我的补丁仓库: https://github.com/RehabMan/Laptop-DSDT-Patch

为了使 DSDT 与补丁程序匹配,通常首先需要了解补丁程序的制作方式,以便你知道在 DSDT 中要查找的内容,并且可以将所看到的内容与现有补丁程序进行匹配。补丁集的更改比率很高,不会产生错误,并且似乎在补丁所有需要补丁的字段,这很可能是匹配的(本句原文:A patch set that has a high ratio of changes to patches, creates no errors, and appears to patch all fields that need to be patched is likely a match)。

阅读全文 »