Tmux简明手册

screen类似,tmux也用来管理多个终端回话。常用的就是下面几个命令:

(1)创建新会话:

# tmux

创建时指定会话的名字(2是会话名字),多用于复用之前已经退出会话的名字:

# tmux new -s 2  

(2)离开(detach)会话:先输入Ctrl + b,然后输入d
(3)列出当前管理的所有会话(冒号前面的是会话名字):

# tmux ls
0: 1 windows (created Wed Jul  3 15:08:44 2019) [88x29]
1: 1 windows (created Wed Jul  3 16:29:19 2019) [120x39]

(4)附着(attach)某个会话(2是会话名字):

# tmux a -t 2

(5)杀死某个会话(2是会话名字):

# tmux kill-session -t 2

参考资料:
A Gentle Introduction to tmux
tmux cheatsheet

VAX

VAX是一种古老的32位计算机处理器,基于VAX的第一款计算机发布于1977年。在2000年左右,VAX计算机退出历史舞台。随着OpenBSD6.0版本放弃对VAX的支持,目前主流操作系统似乎只有NetBSD还继续支持VAX。但是即便有操作系统,许多主流软件和编程语言也无法在VAX上运行。那么除了怀旧,现在的VAX还有其它的意义吗?

实际上VAX可以做测试平台。VAX支持C语言以及一些还不算“太老”的C语言编译器。如果你是一名C程序员,并且你的C代码不依赖于32位或64位处理器,你又恰巧有一台VAX古董计算机。你可以尝试着让你的代码在VAX上运行一下,说不定可以找到一些隐藏很深的bug。:-)

如果你想更多地了解VAX,可以参考下列文档:
VAX wikipeia
Modern BSD Computing for Fun on a VAX! Trying to use a VAX in today’s world
My beautiful VAX 4000

如何度量代码运行了多少时钟周期(X86平台)

Daniel Lemirebenchmark代码展示了在X86平台上,如何度量一段代码运行了多少时钟周期:

......
#define RDTSC_START(cycles)                                                    \
  do {                                                                         \
    unsigned cyc_high, cyc_low;                                                \
    __asm volatile("cpuid\n\t"                                                 \
                   "rdtsc\n\t"                                                 \
                   "mov %%edx, %0\n\t"                                         \
                   "mov %%eax, %1\n\t"                                         \
                   : "=r"(cyc_high), "=r"(cyc_low)::"%rax", "%rbx", "%rcx",    \
                     "%rdx");                                                  \
    (cycles) = ((uint64_t)cyc_high << 32) | cyc_low;                           \
  } while (0)

#define RDTSC_FINAL(cycles)                                                    \
  do {                                                                         \
    unsigned cyc_high, cyc_low;                                                \
    __asm volatile("rdtscp\n\t"                                                \
                   "mov %%edx, %0\n\t"                                         \
                   "mov %%eax, %1\n\t"                                         \
                   "cpuid\n\t"                                                 \
                   : "=r"(cyc_high), "=r"(cyc_low)::"%rax", "%rbx", "%rcx",    \
                     "%rdx");                                                  \
    (cycles) = ((uint64_t)cyc_high << 32) | cyc_low;                           \
  } while (0)
......
RDTSC_START(cycles_start);                                               
......                                                                   
RDTSC_FINAL(cycles_final);                                               
cycles_diff = (cycles_final - cycles_start);
......

这段代码其实参考自How to Benchmark Code Execution Times on Intel® IA-32 and IA-64 Instruction Set Architectures,原理如下:

(1)测量开始和结束时的cpuid指令用来防止代码的乱序执行(out-of-order),即保证cpuid之前的指令不会调度到cpuid之后执行,因此两个cpuid指令之间只包含要度量的代码,没有掺杂其它的。

(2)rdtscrdtscp都是读取系统自启动以来的时钟周期数(cycles,高32位保存在edx寄存器,低32位保存在eax寄存器),并且rdtscp保证其之前的代码都已经完成。两次采样值相减就是我们需要的时钟周期数。

综上所述,通过cpuidrdtscrdtscp3条汇编指令,我们就可以计算出一段代码到底消耗了多少时钟周期。

P.S.,stackoverflow也有相关的讨论。

如何度量系统的时钟频率

Daniel LemireMeasuring the system clock frequency using loops (Intel and ARM)讲述了如何利用汇编指令来度量系统的时钟频率。以Intel X86处理器为例(ARM平台原理类似):

; initialize 'counter' with the desired number
label:
dec counter ; decrement counter
jnz label ; goes to label if counter is not zero

在实际执行时,现代的Intel X86处理器会把decjnz这两条指令“融合”成一条指令,并在一个时钟周期执行完毕。因此只要知道完成一定数量的循环花费了多长时间,就可以计算得出当前系统的时钟频率近似值。

代码中,Daniel Lemire使用了一种叫做“measure-twice-and-subtract”技巧:假设循环次数是65536,每次实验跑两次。第一次执行65536 * 2次,花费时间是nanoseconds1;第二次执行65536次,花费时间是nanoseconds2。那么我们就得到3个执行65536次数的时间:nanoseconds1 / 2nanoseconds1 - nanoseconds2nanoseconds2。这三个时间之间的误差必须小于一个值才认为此次实验结果是有效的:

......
double nanoseconds = (nanoseconds1 - nanoseconds2);
if ((fabs(nanoseconds - nanoseconds1 / 2) > 0.05 * nanoseconds) or
    (fabs(nanoseconds - nanoseconds2) > 0.05 * nanoseconds)) {
  return 0;
}
......

最后把有效的测量值排序取中位数(median):

......
std::cout << "Got " << freqs.size() << " measures." << std::endl;
std::sort(freqs.begin(),freqs.end());
std::cout << "Median frequency detected: " << freqs[freqs.size() / 2] << " GHz" << std::endl;
......

在我的系统上,lscpu显示的CPU时钟频率:

$ lscpu
......
CPU MHz:             1000.007
CPU max MHz:         3700.0000
CPU min MHz:         1000.0000
......

实际测量结果:

$ ./loop.sh
g++ -O2 -o reportfreq reportfreq.cpp  -std=c++11 -Wall -lm
measure using a tight loop:
Got 9544 measures.
Median frequency detected: 3.39196 GHz

measure using an unrolled loop:
Got 9591 measures.
Median frequency detected: 3.39231 GHz

measure using a tight loop:
Got 9553 measures.
Median frequency detected: 3.39196 GHz

measure using an unrolled loop:
Got 9511 measures.
Median frequency detected: 3.39231 GHz

measure using a tight loop:
Got 9589 measures.
Median frequency detected: 3.39213 GHz

measure using an unrolled loop:
Got 9540 measures.
Median frequency detected: 3.39196 GHz
.......

 

Void Linux 初体验

最近要做一些Linux kernel相关的测试工作,为了避免影响到其它同事,决定使用虚拟机。过去的3年一直使用Arch Linux,有点烦了,所以打算尝试一个新的发行版(前提必须也是“滚动发行”(rolling-release)的),这次我选择了Void Linux

Void Linux吸引我的地方在于它的“特立独行”:

(1)没有使用现在主流的systemd,而是用runit来作为初始化系统,这更接近传统的Unix方式;

(2)使用OpenBSD团队开发的LibreSSL而不是OpenSSL

另外,Void LinuxArch Linux的一个重要区别在于内核的版本:Arch Linux选择stable版本(现在是5.1.5),而Void Linux则是long-term4.19.46)。

经过一周的使用,总体来说Void Linux感觉不错,能够满足我的开发需求。感兴趣的朋友也可以尝试一下。

 

如何实现一个Linux性能监控工具

NmonLinux系统下一个简单但强大的性能监控工具。在新年的头一个月里,我花时间阅读了nmon的代码,总结了如何实现一个简单的Linux性能监控工具:

(1)获取性能数据。/proc文件系统是个宝库,你想要的信息几乎都可以从这里得到:
a)CPU利用率:/proc/stat
b)内存利用率:/proc/meminfo/proc/vmstat
c)磁盘利用率:/proc/diskstats
d)网络利用率:/proc/net/dev
e)单独进程的状态:/proc/[pid]目录;
f)其余感兴趣的信息:比如关于系统的负载状况,可以读取/proc/loadavg

(2)理解和解析数据。参考man手册,了解每一项数据的含义,必要时候可以阅读内核代码和学习相关的硬件知识。

(3)展示数据。Nmon使用的是“原始”的ncurses库,当然也可以使用“现代化”的GUI工具以达到更好的用户体验。

如果想进一步了解nmon内部的原理,也可参考我写的这本剖析nmon代码的小册子

使用uptime命令检查Unix系统的负载状况

Unix系统的uptime命令可以用来检查系统的负载状况。以Linux为例:

$ uptime
 01:32:50 up 40 days,  3:09, 56 users,  load average: 11.72, 11.67, 11.51

load average后面的3个值分别是系统在过去1515分钟负载的平均值(这里的负载包含3种进程:当前正在被CPU执行的,一切条件就绪等待CPU调度的,和等待磁盘读取结果的)。衡量当前系统是否“过载”,需要把load averageCPU的数量结合起来考虑。如果load average的值是1,并且当前系统上只有一个CPU(需要注意,这里的CPU指一个“逻辑CPU”,即需要考虑物理CPU有多个core,每个core支持hyper-thread的情况),那么系统在过去的时间就是“满负荷”运转的。但是如果系统上有4CPU,那么系统就只有1 / 4 = 25%的时间是忙碌的,其余75%是空闲的。

Linux系统的uptime读取/proc/loadavg文件:

$ cat /proc/loadavg
12.97 11.53 11.33 12/3958 7094

前三项对应uptimeload average的输出。第四项中斜线前面的是活跃的kernel进程(线程)数,后面则是系统所有的kernel进程(线程)数。最后一项是系统最新产生的进程ID

对于OpenBSD来说,由于其没有/proc文件系统。它的uptime实现则是通过sysctl系统调用读取vm.loadavg的值。