自制直流电源

怎么想到要自制(就是自己组装的意思)直流电源的呢,说来话长。首先是我的手机支持QC2.0快速充电协议,只能用原装的充电器来快速充电。理论上,用普通的5V 2A充电器也可以以较快的速度充电,但事实是充电非常慢(事后证明其实是USB线的问题,唉)。于是我开始寻找问题的解决方案。

找啊找,慢慢地就偏离了原来的方向,先后买了一条QC2.0的升压板,一个台达5V 3A电源适配器,一个USB电压电流表。下面依次道来。

继续阅读 →

网速测试工具

testmy.net可以方便地用来进行网速测试。无需flash插件,在PC或者移动端都可以方便地进行。

这个测试反映的是连接的真实速度。据我的经验,无线网络信号的好坏对数据传输速率有影响。

下面是我在家里得到的测试结果。

下载(Mbps)上载(Mbps)
直接连接25.45
洋葱路由0.3650.603
某公共代理20.089
我的DigitalOcean服务器2.12.7

注:1 Mbps = 0.119 MiB/s = 122 KiB/s

继续阅读 →

复习:拉普拉斯变换和z变换

致EE120。

拉普拉斯变换和z变换分别是连续时间和离散时间的傅里叶变换的推广。一方面,运用这些变换可以处理种类更加多的函数;另一方面,它们是分析线性时不变系统和求解微分/差分方程的有力工具。

继续阅读 →

复习:傅里叶级数和傅里叶变换

致EE120。

傅里叶级数和傅里叶变换是工程学领域里的一个重要工具。其思想也非常深刻:从频率的角度分析问题;把复杂函数分解成具有不同频率的分量;将函数在时域和频域之间进行转换。

继续阅读 →

编译GTK+

GTK+是一个流行的图形界面工具包,它的结构比较复杂,有很多外部依赖:

GTK+ dependency graph

而我今天要尝试的是,在Windows上编译最新的GTK+ 3.19.1。所有外部依赖均从源代码开始编译。为何想到从源码编译GTK+,是因为市面上难以找到最新版的GTK+用于Windows的安装包。GTK+ for Windows Runtime Environment是一个很好的安装包,但是只更新到GTK+ 2.24,在2012年后就再没有更新。

由依赖关系图可知,这个编译过程会非常痛苦。而选择Windows平台无疑使得难度更高。我没有安装Visual Studio,所以我没有研究过使用Visual Studio的构建方案。这篇文章可能有用。

我选择以Msys2为构建平台,并且为此编写了一个自动化构建脚本。由Make来解析其中的依赖关系(其实写成shell脚本也可以,只要按照拓扑排序的顺序构建就可以了。)

继续阅读 →

使用OpenMP做并行计算

OpenMP是一套语言扩展规范,可以给C/C++和Fortran提供并行计算的功能。今天我用OpenMP写了N皇后问题的并行计算程序。该程序使用经典的回溯搜索算法。

程序如下:

#include <stdio.h>
#include <omp.h>

unsigned all;
unsigned count = 0;

void search(unsigned row, unsigned ld, unsigned rd)
{
	unsigned pos, p;

	if (row == all) {
#pragma omp critical
		++count;
		return;
	}
	pos = all & ~(row|ld|rd);
	while ((p = pos & -pos)) {
		pos ^= p;
		if (p)
			search(row | p, (ld | p) << 1, (rd | p) >> 1);
	}
}

int main(int argc, char *argv[])
{
	int i;
	int N = 8;

	if (argc == 2)
		sscanf(argv[1], "%d", &N);
	all = (1 << N) - 1;
#pragma omp parallel for
	for (i = 0; i < N/2; i++) {
		unsigned p = 1 << i;
		search(p, p << 1, p >> 1);
	}
	count <<= 1;
	if (N & 1) {
		unsigned p = 1 << (N/2);
		search(p, p << 1, p >> 1);
	}
	printf("%d\n", count);
	return 0;
}
继续阅读 →

免xauth进行X11转发

之前谈到如何通过SSH进行X11转发。其中有一个前提,即主机B上要装有xauth。事实上,X11的转发和普通的TCP端口转发并无差别。如果主机B中未安装xauth,则需要用户手动设置远程TCP端口转发,并设置主机B的DISPLAY变量:

hostA$ ssh -R6010:localhost:6000 user@hostB
hostB$ export DISPLAY=':10'

在图形界面客户端例如PuTTY或者Bitvise Tunnelier中,也都有远程TCP端口转发的设置。

在主机A中,仍然启动X服务器的TCP监听。不同的是,由于没有.Xauthority认证文件,需要用xhost允许本地主机(即SSH客户端)连接X服务器。在Cygwin中:

hostA$ startxwin -- :0 -multiwindow -listen tcp &
hostA$ xhost +localhost

具体采用哪一种办法见仁见智。建议在主机B中安装xauth,这样可以免去手工设置远程端口转发和DISPLAY变量。

继续阅读 →

通过SSH进行X11转发

          Host A                                         Host B           
+--------------------+------+                +------+--------------------+
| +------------+     |      |                |      |     +------------+ |
| |            |     | SSH  |    Internet    | SSH  |     |            | |
| |            +-----> Port +----------------> Port +----->            | |
| |            <-----+      <----------------+      <-----+            | |
| |    SSH     |     |      |                | 22   |     |    SSH     | |
| |   Client   |     +------+                +------+     |   Server   | |
| |            <-----+      |                |      <-----+            | |
| |            +----->      |                |      +----->            | |
| +------------+     | X11  |                | X11  |     +------------+ |
| +------------+     | Port |                | Port |     +------------+ |
| | X11 Server +----->      |                |      +-----> X11 Client | |
| |            <-----+ 6000 |                | 6010 <-----+            | |
| +------------+     |      |                |      |     +------------+ |
+--------------------+------+                +------+--------------------+
继续阅读 →

诡异的OpenSSH行为

最近几天在FreeBSD上尝试搭建SSH服务器,主要是想利用SSH的端口转发功能。然而却遇到了一个诡异的问题,耗费了我许多时间。

我希望限制每个用户的登录数,即,同一用户在同一时间只能在一个主机上连接SSH服务器进行端口转发。网上查了老半天,基本上没有什么有用的信息。

据说Linux中可以使用/etc/security/limits.conf来限制用户的登录数量。也有资料说这个办法只能限制shell的登录数量,对不需要shell的端口转发连接无效。

limits.conf通过pam_limits模块产生作用,FreeBSD无此模块,因此我无法尝试。受此启发,我找到了pam_exec模块,允许我在开启/关闭会话的时候执行任意的脚本。这样我只需要自己编写一个脚本来检测是否存在重复登录即可。

#!/bin/sh
# file: pam_session.sh
# prevent multiple ssh connections from a single user

USER_SESSION="/var/run/${PAM_USER}.ssh_session"
LOG="/var/log/ssh_session.log"

echo `date` "$PAM_USER" "$PAM_RHOST" "$PAM_SM_FUNC" >> $LOG

if [ "$PAM_SM_FUNC" = "pam_sm_open_session" ]; then
        if [ -f $USER_SESSION ]; then
                echo BLOCKED by `cat $USER_SESSION` >> $LOG
                exit 1
        fi
        echo "$PAM_RHOST" > $USER_SESSION
elif [ "$PAM_SM_FUNC" = "pam_sm_close_session" ]; then
        rm -f $USER_SESSION
fi

保存为/etc/ssh/pam_session.sh,然后在/etc/pam.d/sshd中添加一行

session	required	pam_exec.so	/etc/ssh/pam_session.sh

即告成功。诡异的事现在发生了:我在一个主机上登录SSH,然后在另一个主机上也登录SSH,但后者只进行端口转发,不登录shell。结果后者也登录成功了!

我在Windows上用Bitvise的SSH客户端,连接并没有中断。

我在Android上用ConnectBot连接,在不登录shell的情况下,连接也没有中断。

我的脚本工作正常,PAM的Session应该是认证失败的(系统日志中有Permission Denied的记录)。也就是说,OpenSSH忽略了PAM会话认证失败而继续保持着连接!

沿着这样的思路,我尝试了其他各种方法,试图阻断重复的SSH连接,均告失败。绝望的我去查看了OpenSSH的源代码,想搞清楚作者为什么指定了这样的行为。

看到第1052行

	sshpam_err = pam_open_session(sshpam_handle, 0);
	if (sshpam_err == PAM_SUCCESS)
		sshpam_session_open = 1;
	else {
		sshpam_session_open = 0;
/*1052*/	disable_forwarding();
		error("PAM: pam_open_session(): %s",
		    pam_strerror(sshpam_handle, sshpam_err));
	}

瞬间释然,其实OpenSSH在PAM会话认证失败的情况下是会禁用端口转发的。至于连接为什么没有中断,我不知道。

且不提OpenSSH的行为是否合适,仅仅因为连接没有中断就判定端口转发仍然可以进行,显然我陷入了惯性思维的陷阱,为此还浪费了大量的时间。应当引以为戒。

继续阅读 →

玩具垃圾收集器

今天我写了一个玩具垃圾收集器,采用_标记-清除(Mark-Sweep)_ 算法。

Wikipedia上的一张图片解释了该过程。

我写的垃圾收集器之所以称作玩具,是因为它采用了朴素实现。每次垃圾收集会打断其他例程的运行,进行一次完整的标记-清除周期。其缺点是容易造成周期性的延迟,影响程序运行的平顺度。

继续阅读 →