futex

futex (fast userspace mutex) 是Linux的一个基础构件,可以用来构建各种更高级别的同步机制,比如锁或者信号量等等,POSIX信号量就是基于futex构建的。大多数时候编写应用程序并不需要直接使用futex,一般用基于它所实现的系统库就够了。

futex的性能非常优异,它是怎样做到的呢?这要从它的设计思想谈起。传统的SystemV IPC(inter process communication)进程间同步机制都是通过内核对象来实现的,以 semaphore 为例,当进程间要同步的时候,必须通过系统调用semop(2)进入内核进行PV操作。系统调用的缺点是开销很大,需要从user mode切换到kernel mode、保存寄存器状态、从user stack切换到kernel stack、等等,通常要消耗上百条指令。事实上,有一部分系统调用是可以避免的,因为现实中很多同步操作进行的时候根本不存在竞争,即某个进程从持有semaphore直至释放semaphore的这段时间内,常常没有其它进程对同一semaphore有需求,在这种情况下,内核的参与本来是不必要的,可是在传统机制下,持有semaphore必须先调用semop(2)进入内核去看看有没有人和它竞争,释放semaphore也必须调用semop(2)进入内核去看看有没有人在等待同一semaphore,这些不必要的系统调用造成了大量的性能损耗。futex就为了解决这个问题而生的,它的办法是:在无竞争的情况下,futex的操作完全在user space进行,不需要系统调用,仅在发生竞争的时候进入内核去完成相应的处理(wait 或者 wake up)。所以说,futex是一种user mode和kernel mode混合的同步机制,需要两种模式合作才能完成,futex变量必须位于user space,而不是内核对象,futex的代码也分为user mode和kernel mode两部分,无竞争的情况下在user mode,发生竞争时则通过sys_futex系统调用进入kernel mode进行处理,具体来说:

futex变量是位于user space的一个整数,支持原子操作。futex同步操作都是从user space开始的:

  • 当要求持有futex的时候,对futex变量执行”down”操作,即原子递减,如果变量变为0,则意味着没有竞争发生,进程成功持有futex并继续在user mode运行;如果变量变为负数,则意味着有竞争发生,需要通过sys_futex系统调用进入内核执行futex_wait操作,让进程进入休眠等待。
  • 当释放futex的时候,对futex变量进行”up”操作,即原子递增,如果变量变成1,则意味着没有竞争发生,进程成功释放futex并继续在user mode执行;否则意味着有竞争,需要通过sys_futex系统调用进入内核执行futex_wake操作,唤醒正在等待的进程。

如果需要在多个进程之间共享futex,那就必须把futex变量放在共享内存中,并确保这些进程都有访问共享内存的权限;如果仅需在线程之间使用futex的话,那么futex变量可以位于进程的私有内存中,比如普通的全局变量即可。

更详细的信息请参阅futex作者的论文:
Fuss, Futexes and Furwocks: Fast Userlevel Locking in Linux

观察网络流量的工具:iptraf

想知道你的Linux系统上网络流量有多大吗?想知道是哪一块网卡承载着网络流量吗?想知道哪一个进程产生了网络流量吗?iptraf可以帮你做到。在最新的Linux release上,比如CentOS 7.0,采用了衍生版本iptraf-ng

通常我们会先看看总体状况,“iptraf -g” 显示每一个网卡上的流量:

iptraf -g

找到感兴趣的网卡之后,再看看那个网卡的总体状况,“iptraf -d eth0” 显示指定网卡上的流量统计,总体流量、流入量、流出量、以及按协议分类的流量统计:

iptraf-d

以上我们看到大部分流量来自TCP协议,需要进一步找出这些流量通过哪一个TCP port,“iptraf -s eth0” 统计各port的流量::

iptraf-s

很明显是TCP port 22,即SSH端口。如果我们还想进一步看看是哪些远程主机在跟我们的SSH端口通信,“iptraf -i eth0” 可以帮忙:

iptraf_i

“iptraf -i eth0” 的输出分为两个窗口,上面是TCP socket pairs,下面是UDP。这里我们看到,连接我们SSH端口的远程IP是16.29.48.9。如果你愿意,根据这个socket pair的信息,还可以利用 lsof 工具找出进程号。

顺便介绍一个类似的工具:iftop ,它与 “iptraf -i” 有点像,显示每一对主机之间的动态流速,如果加上 “-P” 选项,就与 “iptraf -i” 一样可以显示每一对socket pair的动态流速,而且它还统计UDP端口,不像iptraf那样把UDP流量放在单独的窗口中显示。

观察网络性能时如何选择工具

Linux系统上的网络工具甚多,如何根据实际需要选择称手的工具呢?在此作一个简单介绍:

观察网络流量:

  • sar -n DEV 1 5” 可以统计每个网卡上的网络流速:

  • iptraf 是观察网络流速的强力工具,它可以让你的观察逐步深入,从硬件层(网卡),到网络层(IPv4,IPv6),到传输层(TCP,UDP etc.),一直到每一对socket pair。

iptraf-d

观察网络连接的状态:

  • netstat -a
    这是传统的工具,但是它无力处理海量的网络连接。所以在大规模网络连接的主机上,建议使用ss

  • ss -a” 列出所有的网络连接。ss特别适合海量连接的主机。
    如果加上”-p”选项,还可以显示对应的进程号。

观察静态统计值:

  • netstat -i

  • ifconfig 可以看到网卡层面的少量统计值,packet数量,collision,errors等:

  • ip -s link” 看到的信息基本类似:

  • netstat -s” 提供了各个协议下的统计信息,有些统计值比如retransmit是很有用的,只有 “netstat -s” 能看到: