Linux simple source policy routing

Dual network connections


Routing policy
* Transmit via eth0 when source address is
* Transmit via eth1 when source address is


# eth0
ifconfig eth0 up
ip rule add from table 251
ip route add default via dev eth0 src table 251
# eth1
ifconfig eth1 up
ip rule add from table 252
ip route add default via dev eth1 src table 252


Configuring Bonding Manually via Sysfs

Configuring Bonding Manually via Sysfs

	Starting with version 3.0.0, Channel Bonding may be configured
via the sysfs interface.  This interface allows dynamic configuration
of all bonds in the system without unloading the module.  It also
allows for adding and removing bonds at runtime.  Ifenslave is no
longer required, though it is still supported.

	Use of the sysfs interface allows you to use multiple bonds
with different configurations without having to reload the module.
It also allows you to use multiple, differently configured bonds when
bonding is compiled into the kernel.

	You must have the sysfs filesystem mounted to configure
bonding this way.  The examples in this document assume that you
are using the standard mount point for sysfs, e.g. /sys.  If your
sysfs filesystem is mounted elsewhere, you will need to adjust the
example paths accordingly.

Creating and Destroying Bonds
To add a new bond foo:
# echo +foo > /sys/class/net/bonding_masters

To remove an existing bond bar:
# echo -bar > /sys/class/net/bonding_masters

To show all existing bonds:
# cat /sys/class/net/bonding_masters

NOTE: due to 4K size limitation of sysfs files, this list may be
truncated if you have more than a few hundred bonds.  This is unlikely
to occur under normal operating conditions.

Adding and Removing Slaves
	Interfaces may be enslaved to a bond using the file
/sys/class/net//bonding/slaves.  The semantics for this file
are the same as for the bonding_masters file.

To enslave interface eth0 to bond bond0:
# ifconfig bond0 up
# echo +eth0 > /sys/class/net/bond0/bonding/slaves

To free slave eth0 from bond bond0:
# echo -eth0 > /sys/class/net/bond0/bonding/slaves

	When an interface is enslaved to a bond, symlinks between the
two are created in the sysfs filesystem.  In this case, you would get
/sys/class/net/bond0/slave_eth0 pointing to /sys/class/net/eth0, and
/sys/class/net/eth0/master pointing to /sys/class/net/bond0.

	This means that you can tell quickly whether or not an
interface is enslaved by looking for the master symlink.  Thus:
# echo -eth0 > /sys/class/net/eth0/master/bonding/slaves
will free eth0 from whatever bond it is enslaved to, regardless of
the name of the bond interface.

Changing a Bond's Configuration
	Each bond may be configured individually by manipulating the
files located in /sys/class/net//bonding

	The names of these files correspond directly with the command-
line parameters described elsewhere in this file, and, with the
exception of arp_ip_target, they accept the same values.  To see the
current setting, simply cat the appropriate file.

	A few examples will be given here; for specific usage
guidelines for each parameter, see the appropriate section in this

To configure bond0 for balance-alb mode:
# ifconfig bond0 down
# echo 6 > /sys/class/net/bond0/bonding/mode
 - or -
# echo balance-alb > /sys/class/net/bond0/bonding/mode
	NOTE: The bond interface must be down before the mode can be

To enable MII monitoring on bond0 with a 1 second interval:
# echo 1000 > /sys/class/net/bond0/bonding/miimon
	NOTE: If ARP monitoring is enabled, it will disabled when MII
monitoring is enabled, and vice-versa.

To add ARP targets:
# echo + > /sys/class/net/bond0/bonding/arp_ip_target
# echo + > /sys/class/net/bond0/bonding/arp_ip_target
	NOTE:  up to 16 target addresses may be specified.

To remove an ARP target:
# echo - > /sys/class/net/bond0/bonding/arp_ip_target

To configure the interval between learning packet transmits:
# echo 12 > /sys/class/net/bond0/bonding/lp_interval
	NOTE: the lp_inteval is the number of seconds between instances where
the bonding driver sends learning packets to each slaves peer switch.  The
default interval is 1 second.

Example Configuration
	We begin with the same example that is shown in section 3.3,
executed with sysfs, and without using ifenslave.

	To make a simple bond of two e100 devices (presumed to be eth0
and eth1), and have it persist across reboots, edit the appropriate
file (/etc/init.d/boot.local or /etc/rc.d/rc.local), and add the

modprobe bonding
modprobe e100
echo balance-alb > /sys/class/net/bond0/bonding/mode
ifconfig bond0 netmask up
echo 100 > /sys/class/net/bond0/bonding/miimon
echo +eth0 > /sys/class/net/bond0/bonding/slaves
echo +eth1 > /sys/class/net/bond0/bonding/slaves

	To add a second bond, with two e1000 interfaces in
active-backup mode, using ARP monitoring, add the following lines to
your init script:

modprobe e1000
echo +bond1 > /sys/class/net/bonding_masters
echo active-backup > /sys/class/net/bond1/bonding/mode
ifconfig bond1 netmask up
echo + /sys/class/net/bond1/bonding/arp_ip_target
echo 2000 > /sys/class/net/bond1/bonding/arp_interval
echo +eth2 > /sys/class/net/bond1/bonding/slaves
echo +eth3 > /sys/class/net/bond1/bonding/slaves

See also:

一个简单、轻量的 Linux 协程实现

HevTaskSystem 是一个简单的、轻量的多任务系统(或称协程),它工作于 Linux 平台,I/O event poll 基于 Epoll。

1. 协程是一个并发运行的多任务系统,一般由一个操作系统线程驱动。
2. 协程任务元数据资源占用比操作系统线程更低,且任务切换开销小。
3. 协程是任务间协作式调度,即某一任务主动放弃执行后进而调度另外一任务投入运行。


HevTaskSystem 目前开放了四个类:HevTaskSystem、HevTask、HevTaskPoll 和 HevMemoryAllocator。
HevTaskSystem 是协程任务系统,管理、调度众多的 HevTask 实例运行。由单一操作系统线程驱动,多个线程可并行驱动多套任务系统。
HevTask 是协程任务,实例可加入某一 HevTaskSystem 中调度运行。
HevTaskPoll 是提供了 poll 兼容的系统调用。
HevMemoryAllocator 是一个内存分配器接口,其后端有两套实现:
* 原始分配器,等价于 malloc/free。
* Slice 分配器,按分配大小限量缓存的分配器,缓存替换算法是 LRU。

Public API
TaskSystem – hev-task-system.h
Task – hev-task.h
TaskPoll – hev-task-poll.h
MemoryAllocator – hev-memory-allocator.h

该示例演示了在主线程上运行一个协程任务系统,并创建两个独立的协程任务,分别以不同的优先级运行各自的入口函数。各自的入口函数中各循环2次,每次打印一个字符串并 yield 释放CPU 触发调度切换。

 Name        : simple.c
 Author      : Heiher <>
 Copyright   : Copyright (c) 2017 everyone.
 Description :
#include <stdio.h>
#include <hev-task.h>
#include <hev-task-system.h>
static void
task_entry1 (void *data)
        int i;
        for (i=0; i<2; i++) {
                printf ("hello 1\n");
                /* 主动放弃执行,yield 函数会触发重新调度选取另一任务投入执行 */
                hev_task_yield (HEV_TASK_YIELD);
static void
task_entry2 (void *data)
        int i;
        for (i=0; i<2; i++) {
                printf ("hello 2\n");
                hev_task_yield (HEV_TASK_YIELD);
main (int argc, char *argv[])
        HevTask *task;
        /* 在当前线程上初始化 task system */
        hev_task_system_init ();
        /* 创建一个新的 task,栈空间采用默认大小 */
        task = hev_task_new (-1);
        /* 设置该 task 的优先级为 1 */
        hev_task_set_priority (task, 1);
        /* 将该 task 放入当前线程的 task system中,任务人口函数为 task_entry1
         * task_entry1 并不会在 hev_task_run 执行后立即调用,需等到该 task 被调度。
        hev_task_run (task, task_entry1, NULL);
        task = hev_task_new (-1);
        hev_task_set_priority (task, 0);
        hev_task_run (task, task_entry2, NULL);
        /* 运行当前线程上相关的 task system,当无任务可调度时该函数返回 */
        hev_task_system_run ();
        /* 销毁当前线程上相关的 task system */
        hev_task_system_fini ();
        return 0;


用龙芯EJTAG硬件断点优化Linux ptrace watch性能

在MIPS标准的协处理器0(CP0)中定义一组硬件watchpoints接口,由于某些原因,龙芯3系列处理器并未实现,这就导致了在该架构Linux系统中用gdb watch只能使用软件断点,真心非常、非常慢。:(

好消息是龙芯3系列处理器是实现了MIPS EJTAG的,兼容2.61标准,那么能否利用MIPS EJTAG的硬件断点功能部件实现Linux ptrace的watchpoints功能呢?答案是肯定的。让我们一起看看具体的方法吧。

首先,我们需要更改BIOS中的异常处理函数,将EJTAG调试异常重新路由至Linux内核中处理,因为MIPS EJTAG异常处理程序的入口地址固定为0xbfc00480

         /* Debug exception */
         .align  7           /* bfc00480 */
         .set    push
         .set    noreorder
         .set    arch=mips64r2
         dmtc0   k0, CP0_DESAVE
         mfc0    k0, CP0_DEBUG
         andi    k0, 0x2
         beqz    k0, 1f
          mfc0   k0, CP0_STATUS
         andi    k0, 0x18
         bnez    k0, 2f
         mfc0    k0, CP0_EBASE
         ins     k0, zero, 0, 12
         addiu   k0, 0x480
         jr      k0
          dmfc0  k0, CP0_DESAVE
         la      k0, 0xdeadbeef
         dmtc0   k0, CP0_DEPC
         dmfc0   k0, CP0_DESAVE
         .set    pop

1. 将来自用户态的sdbbp指令触发的异常路由至地址 0xdeadbeef。
2. 将来自内核态的sdbbp指令触发的异常或是任意态的非sdbbp触发的异常路由至 ebase+0x480。

1. 实现 EJTAG watch 相关的 probe、install、read、clear 等操作,及合适的调试异常处理程序。
2. 实现 Linux ptrace watch 接口与 EJTAG watch 的对接。



FSH – 助你接入私有网络中的 Linux 终端

Linuxer! 同事、朋友有技术问题需要你远程协助,可是双方计算机都连接在私有网络中怎么办?FSH 能助你接入私有网络中的 Linux 终端。

FSH 是采用服务器中转来穿透 NAT 的一种连接远程 Linux 终端的方案,服务端、客户端的源代码都是开放的,托管在 GitHub 上。


git clone git://
cd hev-fsh
git submodule init
git submodule update



# 登录模式(root 用户运行)
bin/hev-fsh -s -p 81
# 当前用户模式
bin/hev-fsh -s -p 81
# 指定用户模式,如 nobody 用户(root 用户运行)
bin/hev-fsh -s -p 81 -u nobody


bin/hev-fsh -s -p 81 -c TOKEN


bin/hev-fsh -a -p 端口
# 指定 LOG 文件
bin/hev-fsh -a -p 端口 -l /var/log/fsh.log


优化 ibus-table 性能

ibus-table 是 Python 语言实现的 ibus 输入法框架的码表引擎,ibus 的绝大多数形码输入法使用该引擎,如郑码、五笔等等。

使用过该引擎的用户或多或少有这样的感觉,就是反应慢,尤其在低性能的计算机上感觉格外明显。无需复杂的性能分析工具,仅用 top 命令就不难发现,用一个约有20万条记录的郑码码表,在连续输入中文时,ibus-table 进程的CPU使用率几乎100%,这还是在一个 i3 2.5GHz 的 PC 平台上。那么在一个更低性能的平台上,输入体验可以想象。

再通过 pref 工具跟踪可以发现,在连续输入时,大部分CPU使用主要来自于 sqlite 数据库查询操作和候选词排序。在阅读引擎代码后,我做了一个实验,在 的 select_words 中删除掉 sqlite 查询与排序,返回静态记录。结果CPU使用率降低到了只有5%左右。

因此,给 select_words 加个缓存应该是个不错的选择,就用 HashMap 来实现这个缓存,按键输入作为 key,排序后的结果作为 value。结果很明显,连接输入中文时的进程CPU使用率从几乎100%下降到了10%。

已经合并至 1.9.18:


ArchLinux 部署 SSD 缓存

在 Linux 系统上使用带宽更高、延迟更小的 SSD 作为 HDD 的缓存来打造软件实现的“混合硬盘”是一种容量和性能折中的方案。在 Linux 系统中使用高速外存作低速外存的缓存有两个成熟的方案:1. lvmcache 2. bcache。本文记录的是基于 lvmcache 在 Arch Linux 系统上的部署方法。

1. 一块120GB容量的固态硬盘 (/dev/sda)。
2. 一块1TB容量的机械硬盘 (/dev/sdb)。


/dev/sda1     20GB     lvm
/dev/sda2     100GB    lvm
/dev/sdb      1TB      lvm

LVM 规划

VG (system) -> { PV (/dev/sda1) }
VG (data) -> { PV (/dev/sdb), PV (/dev/sda2) }
LV (system/arch) 19.9GB
LV (data/home) 917GB
LV (data/home_cache) 91GB

操作系统完全安装在 SSD 中, home 存放于 HDD 中,但使用 SSD 的剩余空间作为 cache。为什么不把所有数据存放于 HDD 中,仅用 SSD 作为 cache 呢?测试结果是当 cache 加入后就影响了系统的启动,所有只能应用于数据区。

1. 使用 ArchLinux iso 启动盘启动系统。
2. fdisk /dev/sda 先创建一个类型为 lvm 容量为 20GB 的标准主分区,再创建一个类型为 lvm 容量为 100GB 的标准主分区。
3. pvcreate /dev/sda1
4. pvcreate /dev/sda2
5. pvcreate /dev/sdb
6. vgcreate system /dev/sda1
7. vgcreate data /dev/sdb
8. lvcreate -L 19.9G system -n arch
9. mkfs.ext4 -E discard /dev/mapper/system-arch
10. mount /dev/mapper/system-arch /mnt
11. pacstrap /mnt 按照需要安装系统
12. arch-chroot /mnt
13. lvcreate –type cache –cachemode wirteback -L 91G -n home_cache data/home /dev/sda2
14. 另开一个 tty, mount -o bind /run /mnt/run
15. 回到原 tty, vim /etc/mkinitcpio.conf,找到 HOOkS,在 block 和 filesystem 之间增加个 lvm2
16. mkinitcpio -p linux
17. vim /etc/default/grub,找到 GRUB_PRELOAD_MODULES,增加 lvm。
18. grub-mkconfig -o /boot/grub/grub.cfg
19. grub-install /dev/sda
20. 退出,重启。

查看 cache 状态

sudo lvs -o cache_read_hits,cache_read_misses,cache_write_hits,cache_write_misses data/home


优化 Linux 系统 IBus 郑码输入法

偶然的机会体验了一下 Windows 平台的“东方制作”郑码6.6,默认为4键自动提交,不动态调频等等觉得很好用,用了这么长时间的 IBus 郑码都不知道这些都应该是形码输入法的福利了。查找下来发现 IBus 郑码不好用的原因其实主要为配置不当和对5码郑码编码方案支持的不成熟。

在原来 ibus-table-zhengma 的基础上优化了郑码的使用体验。主要包括启用自动提交、关闭自动调词频,另外还将5码编码方案退回4码编码方案。


ArchLinux 安装方法:
1. 从 AUR 下载源代码包
2. 通过 makepkg 命令编译二进制包并安装。

如原来使用过旧版的郑码输入法,请在安装新版本后,删除 ~/.local/share/ibus-table 目录,并执行 ibus restart,最后进入郑码输入法的配置界面恢复默认配置(否则自动提交功能不会正常工作)。


Linux 不使用 chroot 临时替换C运行时库

有些时候我们需要在老旧的 Linux 系统上运行一些依赖于较新版本 C 库的应用程序或库,应用程序会因为系统中安装的C库缺少符号还启动失败。解决方法之一就是临时替换使用非系统安装的C运行时库。使用临时C库需要做些什么配置及会带来哪些问题呢?

1. 下载与目标应用程序版本相匹配的临时C库,解压缩到临时位置 A。
2. 需要设置 LD_LIBRARY_PATH 环境变量指向目标临时C库的存储位置 A。
3. 需要通过与临时C库匹配的 启用应用程序。因为应用程序默认是链接了一个绝对路径的,如 x86_64 是 /lib64/

使用临时C库的 启动的应用程序执行系统标准命令的子进程出错,原因是因为环境变量 LD_LIBRARY_PATH 被子进程继承,从而导致子进程在执行系统C库的ld.so中加载了版本不匹配的临时C库。

在合适的时机清除环境变量 LD_LIBRARY_PATH,最合适的时机应用就是执行目标应用程序 main 函数之前啦。这里又要用到了之前写过的方法 => Linux 平台一种进程代码注入方法

/* fakemain.c
 * Heiher <>
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU
#include <dlfcn.h>
__libc_start_main(int (*main)(int, char **, char **),
			int argc, char **ubp_av, void (*init)(void),
			void (*fini)(void), void (*rtld_fini)(void),
			void (*stack_end))
	int (*__libc_start_main_real)(int (*main) (int, char **, char **),
				int argc, char **ubp_av, void (*init)(void),
				void (*fini)(void), void (*rtld_fini)(void),
				void (*stack_end));
	unsetenv ("LD_PRELOAD");
	unsetenv ("LD_LIBRARY_PATH");
	__libc_start_main_real = dlsym(RTLD_NEXT, "__libc_start_main");
	return __libc_start_main_real(main, argc, ubp_av, init, fini,
				rtld_fini, stack_end);
gcc -fPIC -O3 -shared -o fakemain.c -ldl

设置环境变量 LD_PRELOAD=/xxx/,运行目标应用程序在执行 main 之前即会清除 LD_PRELOAD 和 LD_LIBRARY_PATH 变量。

为了方便使用我还写了个 wrapper,使用方法是将真实的目标应用程序 xxx 重命令为 xxx.bin,然后创建个符号链接 xxx 指向 wrapper,执行时直接执行 xxx,wrapper 会自动设置所需要的环境变量。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
main (int argc, char *argv[])
	int i;
	char buf[1024], path[1024];
	char *str, *root, *args[512];
	/* get FAKE_ROOT */
	root = getenv ("FAKE_ROOT");
	if (!root) {
		fprintf (stderr, "Please set environment FAKE_ROOT!\n");
		return -1;
	/* export PATH */
	str = getenv ("PATH");
	if (!str) {
		fprintf (stderr, "Get environment PATH failed!\n");
		return -2;
	if (NULL == strstr (str, root)) {
		strcpy (buf, root);
		strcat (buf, "/bin:");
		strcat (buf, str);
		if (0 != setenv ("PATH", buf, 1)) {
			fprintf (stderr, "Set environment PATH failed!\n");
			return -3;
	/* export LD_PRELOAD */
	strcpy (buf, root);
	strcat (buf, "/lib64/");
	if (0 != setenv ("LD_PRELOAD", buf, 1)) {
		fprintf (stderr, "Set environment LD_PRELOAD failed!\n");
		return -4;
	/* export LD_LIBRARY_PATH */
	strcpy (buf, root);
	strcat (buf, "/lib64");
	if (0 != setenv ("LD_LIBRARY_PATH", buf, 1)) {
		fprintf (stderr, "Set environment LD_LIBRARY_PATH failed!\n");
		return -5;
	/* set new path */
	strcpy (path, root);
	strcat (path, "/lib64/");
	args[0] = path;
	/* set real program path */
	strcpy (buf, root);
	strcat (buf, "/bin/");
	strcat (buf, argv[0]);
	strcat (buf, ".bin");
	args[1] = buf;
	/* copy arguments */
	for (i=1; i<argc; i++)
	      args[i+1] = argv[i];
	args[i+1] = NULL;
	/* run real program */
	return execv (path, args);;
gcc -O3 -o wrapper wrapper.c


mips64el toolchain for x86_64

mips64el toolchain 是用于在 x86_64 平台交叉编译 mips64el 目标程序的工具集,该工具集分为两种大版本:odd-spreg 和 no-odd-spreg,其中龙芯仅适用 no-odd-spreg 版本。系统库包含 mips64el o32, n32 和 n64 多种版本的库,分别有依赖于 Linux 2.6 内核和 Linux 3.4 内核的两种版本。另外还有支持 x86_64 交叉编译 Mozilla JS 引擎的支持包。

Source: mips64el-toolchain-2.src.tar.xz
toolchain: mips64el-toolchain-2.x64.tar.xz
system libaries (Linux 2.6): mips64el-toolchain-linux-2.6-2.x64.tar.xz
system libaries (Linux 3.4): mips64el-toolchain-linux-3.4-2.x64.tar.xz
system libaries (Linux 3.4 MozJS): mips64el-toolchain-linux-3.4-mozjs-2.x64.tar.xz
toolchain: mips64el-toolchain-2.x64.tar.xz
system libaries (Linux 2.6): mips64el-toolchain-linux-2.6-2.x64.tar.xz
system libaries (Linux 3.4): mips64el-toolchain-linux-3.4-2.x64.tar.xz


sudo tar --numeric-owner -xf xxxx -C /


export PATH=${PATH}:/opt/mips64el-toolchain/bin


sudo ln -s -f linux-2.6 /opt/mips64el-toolchain/platforms/current


# MIPS32 o32
mips64el-unknown-linux-gnu-gcc -march=mips32r2 -mabi=32 -o test test.c
# MIPS64 n32
mips64el-unknown-linux-gnu-gcc -march=mips64r2 -mabi=n32 -o test test.c
# MIPS64 n64
mips64el-unknown-linux-gnu-gcc -march=mips64r2 -mabi=64 -o test test.c