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

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

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

与异步、非阻塞式I/O模型类似,协程技术同样适用于处理海量的并发I/O任务,而且还不会像异步方式使业务代码逻辑支离破碎。

基本信息
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 <r@hev.cc>
 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);
        }
}
 
int
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;
}

Over!

Windows 7 有线局域网组播接收丢包调试

一有线局域网实时流媒体组播传输应用从 Windows 10 迁移至 Windows 7 平台后,迁移后传输质量下降明显。

对比实验发现在同一发送端的同一组播窗口中,运行在 Windows 7 系统上的接收端效果明显劣于 Windows 10 接收端。

分析接收端的收到的数据包发现,Windows 7 平台的接收端存在明显的丢包现象。于是排查了这两个方面:
1. Win7 网卡驱动较 Win10 较旧。
2. Socket 默认接收缓冲区是否太小。

针对第1点,在将 Win7 网卡驱动升级至最新后无明显改善。:(
针对第2点,显式设置了接收缓冲区为 1MB 后,接收质量得到明显改善。:)

Over!

用龙芯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
          nop
 1:
         mfc0    k0, CP0_EBASE
         ins     k0, zero, 0, 12
         addiu   k0, 0x480
         jr      k0
          dmfc0  k0, CP0_DESAVE
 2:
         la      k0, 0xdeadbeef
         dmtc0   k0, CP0_DEPC
         dmfc0   k0, CP0_DESAVE
         deret
         .set    pop

这段处理程序实现了两个功能:
1. 将来自用户态的sdbbp指令触发的异常路由至地址 0xdeadbeef。
2. 将来自内核态的sdbbp指令触发的异常或是任意态的非sdbbp触发的异常路由至 ebase+0x480。

接着,我们还需要修改内核,实现下列功能:
1. 实现 EJTAG watch 相关的 probe、install、read、clear 等操作,及合适的调试异常处理程序。
2. 实现 Linux ptrace watch 接口与 EJTAG watch 的对接。

See: https://github.com/heiher/linux-stable/commits/ejtag-watch-4.9

Over!

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

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

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

如何编译?

git clone git://github.com/heiher/hev-fsh
cd hev-fsh
git submodule init
git submodule update
make

如何使用?
公共转发服务:222.92.8.138:81

被控端

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

主控端

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

自建转发服务端

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

Over!

How to write SCGI applications in Python and JavaScript

Download & Install HevSCGIServerLibrary

git clone git://github.com/hev-scgi/hev-scgi-server-library
cd hev-scgi-server-library
make
 
sudo cp bin/libhev-scgi-server.so /usr/lib64/
sudo cp gir/HevSCGI-1.0.gir /usr/share/gir-1.0/
sudo cp gir/HevSCGI-1.0.typelib /usr/lib64/girepository-1.0/

Python Demo
https://github.com/hev-scgi/hev-scgi-server-python

git clone git://github.com/hev-scgi/hev-scgi-server-python
cd hev-scgi-server-python
 
python3 src/main.py

JavaScript Demo
https://github.com/hev-scgi/hev-scgi-server-gjs

git clone git://github.com/hev-scgi/hev-scgi-server-gjs
cd hev-scgi-server-gjs
 
gjs -I src src/main.js

Over!

优化 ibus-table 性能

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

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

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

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

已经合并至 1.9.18: https://github.com/kaio/ibus-table/releases/tag/1.9.18

Over!

解决小米4电信4G版刷LineageOS信号问题

小米4电信4G版 LineageOS 官方版本存在两类网络信号相关的问题:
1. 电信 CDMA 卡无信号。
2. 电信 CDMA 4G卡只能使用 LTE 网络,无法接打电话、收发短信。

解决方法
刷入官方 ROM 后,在 recovery 中挂载 /system,然后编辑 /system/build.prop 文件:

# 文件中原有此行配置,将值修改为 22
ro.telephony.default_network=22
# 额外增加以下三行
ril.subscription.types=NV,RUIM
persist.radio.force_on_dc=true
persist.omh.enabled=true

Over!

RPi2 远程控制PC电源开关

远程连接物理机调试、测试固件、内核级补丁时,因补丁功能异常导致死机是经常发生的,如果你有一个 Rpi,那就可以派上用场了,本文记录了使用 Rpi2 的 GPIO 远程控制 PC 电源开关的方法。

物理连接
rpi2 有数量众多的 pinout,将 rpi2 的 pin37(gpio26) 与 pc front panel 的 pwr_sw_p 连接,再将 rpi2 的 pin39(gnd) 与 pc front panel 的 pwr_sw_n 连接。如图:
RP2_Pinout
panel(1)

软件控制

# 先将 gpio 26 export,这步不必每次都做,当 /sys/class/gpio/gpio26 目录不存在时执行。
echo 26 > /sys/class/gpio/export
cd /sys/class/gpio/gpio26
# 开机
echo out > direction; echo 0 > value; sleep 1; echo in > direction
# 关机
echo out > direction; echo 0 > value; sleep 5; echo in > direction

Over!