谈谈Linux下的时间机制

最近同事在解决一个时区问题时花费了较多时间,但仍是一头雾水,通过查询Linux相关的文档,我给出了他一些修改建议,经过验证后问题得以修复,事后我总结来看,主要是该同事对于Linux下的时间机制的理解不够深入,因此这里我将相关的知识给记录下来,以解释相关的疑惑。

Linux下目前有三种主要的时钟,其名称及作用如下:

硬件时钟

此时钟一般由主板上的单独电子器件提供,它与CPU核心是独立的运行的,往往在电脑关机时仍能继续运行(想想我们的PC在装机重启后,时间仍然还保持着正确性就是因为这么个东西存在)。

此时钟还有一些其他名称,如RTC(实时时钟)、BIOS时钟或CMOS时钟,此时钟可以通过hwclock这样的小工具来设置。

需要注意的是,硬件时钟中是不感知时区的,其硬件实现记录了个初始值(如我司设备经常采用的1970.1.1等),然后用户可以通过hwclock写入一个时间来更新它,我们可以认为它是一个有初始值的计数器,用户可以更新当前的值,之后它将按照其计时精度来更新其值,供系统通过硬件接口读取。

系统时钟

此时钟是由Linux内核所维护的一个软件时钟,由时钟中断触发更新,其时间是相对于1970.1.1-00:00:00 UTC这个时间的秒数(UTC与GMT即格林威治时间意义相同,为世界标准时间),为方便这里的讲述,我们可以假定我们在格林威治这个地方讨论时间,这样我们可以先把时区放到一边不管了。

在Linux启动后,系统启动脚本(不管是sysvinit还是systemd都将有这样的处理)将通过hwclock读取硬件时钟,并将时间设置到内核中,此后用户获取时间均通过内核获取。

单调时钟

在Linux系统上,用户可以通过date命令更新系统时间,从而导致系统时钟出现跳变,这对于一些涉及到对调度单元(如协程)运行时间来记时以便切出去让其他进程执行的程序来说则会产生计时不准的问题,为此,Linux上支持了一个所谓的单调时钟的接口,此接口除支持单调时钟外,还支持对于系统时钟的获取,此接口属于较新的POSIX标准,如果Linux版本较老的话,可能将无法支持

此接口的定义如下:

int clock_gettime(clockid_t clk_id, struct timespec *tp); // 采用CLOCK_MONOTONIC这个clk id可以获取到单调时钟

上面讨论时,我刻意互略了关于时区的处理,为简单起见,我们这里可以认为Linux内核是不感知时区的(实际上由于种种原因比如VFAT文件系统的时间戳处理就需要内核需要感知到时区)。

在Linux下,由于大量程序均需要依赖于glibc,因此glibc会读取TZ这个环境变量来设置时区,下面是一些C库函数的简单介绍:

struct tm *gmtime(const time_t *timep); // 获取UTC时间,也就是说怎么改TZ变量或者通过tzset更改时区都不会影响此值
struct tm *localtime(const time_t *timep); // 获取本地时间,更改TZ变量或者通过tzset更改时区都会影响此值

当然当于时区还需要补充一点的是,环境变量这个东西只影响所属的进程,在子进程中修改并不会影响父进程,libc.so甚至提供了一个全局变量用来保存当前进程的时区设置(注意,虽然libc.so为动态库,但其在mmap映射是private的,所以会导致其中定义的全局变量每个进程都复制了一份),如果修改的话,需要通过signal类似的IPC机制通知相关进程予以刷新,这也是我同事改时区反复出问题的一个原因。

从一个编程者的角度来看,如果是涉及到对调度单元(如协程)运行时间记时的话,要尽可能采用单调时钟,下面是syslog-ng项目所采用的异步io库ivykis关于时间处理的代码片段,可以看出,它将尽可能去使用单调时钟或者clock_gettime接口,在最后才尝试使用gettimeofday来获取时间。

#ifdef HAVE_CLOCK_GETTIME
static int clock_source;
#endif

void iv_time_get(struct timespec *time)
{
struct timeval tv;

#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC_FAST)
if (clock_source < 1) {
if (clock_gettime(CLOCK_MONOTONIC_FAST, time) >= 0)
return;
clock_source = 1;
}
#endif

#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC)
if (clock_source < 2) {
if (clock_gettime(CLOCK_MONOTONIC, time) >= 0)
return;
clock_source = 2;
}
#endif

#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_REALTIME)
if (clock_source < 3) {
if (clock_gettime(CLOCK_REALTIME, time) >= 0)
return;
clock_source = 3;
}
#endif

gettimeofday(&tv, NULL);
time->tv_sec = tv.tv_sec;
time->tv_nsec = 1000L * tv.tv_usec;
}

Midonet聪明的Tunnel Key分配策略

拥抱开源如今成了一种潮流,既Juniper家的OpenContrail成为开源一揽子(之所以称为一揽子是用于区别现有OpenStack中由社区所维护的基于分散组件的松耦合方式)OpenStack网络虚拟化方案的首发明星后,Midukura这家公司也将自家的OpenStack网络虚拟化方案以开源方式来运作了,相比于OpenContrail的工程师的设备商研发背景,Midukura更具有IT运维背景(这点可以从其技术堆栈如scala编程语言、zookeeper/cassandra数据库、分布式架构推测出来,关于更细节的内容,后面我会陆续补充相关的分析),因此我个人还是更看好Midokura的方案,而从自己实际部署及验证的情况来看,Midokura要靠谱的多一点,当然这只是一家之言,也并非本文的重点,就不再展开。

在本文中我想谈的是,Midonet Tunnel Key的使用策略,与我们通常在基于OVS的方案中看到Tunnel Key(不论是NVGRE还是VXLAN)一般用做租户标识(即Virtual Network Identifier)不同,Midonet Tunnel Key则可以理解为按VIF分配的(实际上是基于Midonet的外部虚拟端口分配的,这里为了简化理解,可以简单认为就是按VIF分配的),而前者其实也是IETF相关标准文档所描述的用法。

Midonet之所以这样做的原因,与其架构实现有较大的关系,Midonet的架构总结起来,有如下的这几种特色:

  • 全分布式架构,每个节点上运行一个Midolman进程(跑在JVM上),用于负责从分布式数据库(zookeeper)同步并发布虚拟网络配置信息,因此实现了去中心化,每个节点可以独立计算出转发结果
  • 虚拟拓朴仿真机制,Midolman从VIF上收到报文后,通过拓朴转发仿真(与我们常规的Bridge->Router->Router->Bridge转发类似),就可以计算出目的VIF,从而得知目的虚拟机所在的主机,并在通过Underlay网络的IP以NVGRE/VXLAN的方式将报文Overlay传送给目的主机前就将报文编辑好
  • 内核转发采用OVS datapath,即采用exact match的flow转发(megaflow暂不支持),以实现次包(即首包之后的包)的内核转发,提高转发性能

结合上面的介绍,Midonet采用基于VIF分配的原因就比较清楚的:

  • 即然源端可以直接仿真出目的VIF且完成报文编辑,因此到对端唯一需要做的就是知道对端的VIF对应的OVS datapath接口是什么,NVGRE/VXLAN封装中的Tunnel Key则提供了一个简单方便的编码点,于是midonet就这么用了,另外一个Host上通过Zookeeper分配的Tunnel Key空间有10位数(非标准限制,而是Zookeeper的限制),对于一个cpu有限、memory有限的主机来说,10位数的VM已经远超能力极限的(咱们又不是天河一号,天河一号估计也不行),已经足足够用了
  • Midonet采用与OVS相似的机制,转件层面采用wildcard match流表,内核采用exact match流表,因此在主机上的Midolman扫描到VIF对应的TAP接口时,Midolman就可以为这个VIF生成一条匹配域为该VIF在该主机上所申请到的Tunnel Key为匹配项的wildcard match流表,其出接口即为该VIF所对应的OVS datapath接口,当收到含有该Tunnel Key的报文时,可以直接命中该wildcard match流表,提取报文字段生成exact match流表出接口继承该wildcard流表向OVS datapath下发即可,Wildcard match流表查询及exact match流表下发会非常迅速,也弥补了Java/Scala代码在未被JIT编译器优化时的性能损失,是一个非常优雅的方案

以上便是我个人对于Midonet Tunnel Key分配的理解,在软件定义网络这股风潮日近的今天,来自于互联网IT企业所带来的新思维,新用法,也许会继续改变设备商的开发模式,设备商要想办法适应并拥抱这种变化了。

AOSA之ZeroMQ阅读笔记

AOSA即《The Architecture of Open Source Application》是本不错的书,这本书的写成本本身也采源了开源社区的协作方式,目前已经出了两部,最新的版本为POSA即《The Performance of Open Source Application》,专注于开源软件的性能。

最近抽时间看了AOSA中关于ZeroMQ的章节,自己先前由于工作需求,简单了解到过ZeroMQ,这次可以借机会读读ZeroMQ创始人亲自写的章节,确实有不少的收获,记录在这里,在后续的项目实践中可以予以参考使用。

Library设计
The lesson here is pretty obvious: Don’t use global state in libraries. If you do, the library is likely to break when it happens to be instantiated twice in the same process.

ZeroMQ设计时经过分析和对比,最终采用了Library而非单一的消息服务器的方案,在设计Library时,得出了上述结论。

也即对于Library而言,最好避免全局状态,采用Context的方式较好,特别是存在library被额外的library依赖在同一程序中存在多份library实例时可以避免带来的竞争性问题。

了解真正的问题
There are many more pitfalls in benchmarking the messaging systems that we won’t go further into. The stress should rather be placed on the lesson learned: Make sure you understand the problem you are solving. Even a problem as simple as “make it fast” can take lot of work to understand properly. What’s more, if you don’t understand the problem, you are likely to build implicit assumptions and popular myths into your code, making the solution either flawed or at least much more complex or much less useful than it could possibly be.

原文附了一个很经典的双向消息交互的图片,解释了吞吐率和时延的评估上的思维误区,用户也许更关注的是从一个单点看到的吞吐率和时延而不是全局吞吐率和时延,这提醒我们要搞清楚到底我们面对的是什么样的问题,以便能够找出解决的办法。

写这个笔记时总让我想起自己这些年工作上的一些感悟,近来听到的一句很经典的话就网络设备商有时候是在自己发明方案,然后再去寻找问题,这样如何能做出真正满足客户需求的产品呢?

内存分配
Lesson learned: optimize where it makes difference. Optimizing pieces of code that are not on the critical path is wasted effort.

要在关键路径上做优化,在非关键路径上瞎优化是在浪费时间。

When thinking about performance, don’t assume there’s a single best solution. It may happen that there are several subclasses of the problem (e.g., small messages vs. large messages), each having its own optimal algorithm.

在做性能考量时,不要假定存在单一最佳方案,很可能一个问题下存在多个子类(如小消息和大消息,ZeroMQ的方案是小消息直接编码在消息句柄中,大消息则采用指针引用,避免内存拷贝),每个子类都有自己的最优算法。

批处理
Lesson learned: To get optimal throughput combined with optimal response time in an asynchronous system, turn off all the batching algorithms on the low layers of the stack and batch on the topmost level. Batch only when new data are arriving faster than they can be processed.

在异步系统中,在底层最好关闭批处理算法而让上层进行批处理操作。并且要按需开启批处理,在处理能力足够的情况,可以不进行批处理而减少开销。

并发处理
Lesson learned: When striving for extreme performance and scalability, consider the actor model; it’s almost the only game in town in such cases. However, if you are not using a specialised system like Erlang or ØMQ itself, you’ll have to write and debug a lot of infrastructure by hand. Additionally, think, from the very beginning, about the procedure to shut down the system. It’s going to be the most complex part of the codebase and if you have no clear idea how to implement it, you should probably reconsider using the actor model in the first place.

在追求高性能和弹性的时候,要考虑使用actor模型,ZeroMQ这里采用了多个线程,线程间采用Event进行通信(印象中好像基于Libevent),从而使得线程可以在CPU核上进行水平扩展,取得极大的并发性能。

这里ZeroMQ的创建中还额外提醒要在设计之初就考虑系统的关闭处理,这通常是系统中最为复杂的地方;以我们的经验来看,我们很多业务进程都不能良好的shutdown,或者说都不支持shutdown…

无锁算法
Lesson learned: Lock-free algorithms are hard to invent, troublesome to implement and almost impossible to debug. If at all possible, use an existing proven algorithm rather than inventing your own. When extreme performance is required, don’t rely solely on lock-free algorithms. While they are fast, the performance can be significantly improved by doing smart batching on top of them.

尽可能采用已知的无锁算法,避免自己造轮子发明一个(前面在公司我在开发一个软转发时,就参考了一个网上搜索到的无锁ring queue算法,实践证明比我自己想一个要好用的多,性能也比较好),另外,在无锁算法上再加上一些智能的批处理机制,会取得更大的性能提升。

API设计
Lesson learned: While code reuse has been promoted from time immemorial and pattern reuse joined in later on, it’s important to think of reuse in an even more generic way. When designing a product, have a look at similar products. Check which have failed and which have succeeded; learn from the successful projects. Don’t succumb to Not Invented Here syndrome. Reuse the ideas, the APIs, the conceptual frameworks, whatever you find appropriate. By doing so you are allowing users to reuse their existing knowledge. At the same time you may be avoiding technical pitfalls you are not even aware of at the moment.

这个经验与上面的类似,本质上就是尽可能减少造轮子,ZeroMQ参考BSD socket的API设计是非常成功的,从而使得用户学习起来非常容易;这就如同在通信业务里面,采用CISCO风格的CLI,显然用户会更容易上手一些。

附:

[1] The Architecture of Open Source Application

[2] WIKI百科上的Actor模型

自已动手打造wifi路由器

由于家里那台华为的HG8245越来越不给力,虽然是自己的前东家出品,这产品也真不耐用,用后门上去可以发现经常性出现kernel分配skb不到导致上网时断时连,妈的内存只有一M空余了,当然,也许他们也知道这东西电信是不给开路由的,既然咱自己动手hack开了路由,就别怪这种后果。

于是最近花了一周晚上的时间,买了个PCDuino板,自己搭建了一个家用wifi路由器,由于我家基本不存在WLAN间互访的问题,少了个LANSWITCH影响不大,做个NAT Router,PCDuino跑的比较Happy,网速快的很开心;至于HG8245,俺将之改回了bridge模式,给俺透传个报文,目前跑的很欢。

废话少说,以下拉个清单,供有需要打造wifi router的朋友参考交流。

原材料汇总

  • PCDuino v1,v2集成wifi实则非常鸡肋,不必花冤枉钱
  • 802.11n USB WIFI无卡 (需支持AP模式),京东上购得EDUPEP-MS15002一款,只需39元,采用RTL8192CU芯片,支持2T2R实现300Mbps数据传递,对于我那只有12Mbps上的光纤,已是足足有余
  • HDMI to DVI转接线 (如显式器支持HDMI则直接买HDMI线即可)
  • 5V/2A DC电源 (本人拿IPAD充电器替代)
  • 旧笔记本上拆下来的320G硬盘,用来做下载机硬盘
  • 4G左右的SDCARD,虽然PCDuino自带4G NAND flash,但这玩意显得得保护得当,启动盘与系统盘还是复制到SDCARD上比较好

背景知识

以下是我总结下来的需要了解的一些背景知识,似乎有点多,但科班出身的咱们码农们,不少是熟之又熟啦。

  • 基础Linux操作系统知识
  • 基础网络知识(交换与路由)
  • 交叉编译
  • 内核编译、内核模块编译
  • iptables使用
  • routing/bridging
  • PPPoE
  • HostAP配置(实现wifi认证接入)

打造过程

  1. 复制系统到SDCARD双系统设计算是全志A10这款SoC的亮点,默认的Linaro系统接电启动可以通过Ctrl-Alt-F1进入文本模式,启动板子配置工具,此时可选择将NAND Flash上的系统拷贝到SDCARD上,A10在下次启动就可以根据SDCARD上是否存在启动文件而从SDCARD启动,从而避免对Nand Flash的寿命产生影响。此后除非把SDCARD上的内核弄坏,一般情况下就无需Nand Flash启动了。(二般情况下,我就犯了错,替换SDCARD启动分区上的内核文件时,没注意到大小不对,使得还需要用Nand Flash启动,手工挂载SDCARD启动分区,拷贝可用的内核予以修复,其实也并不难办,只是稍显麻烦)。
  2. 升级SDCARD上的系统玩过Debian系OS的话,对apt-get比较熟悉了,轻车熟悉升级一把即可,Linaro源里的包还算丰富,由于A10主频有1GHZ,并且自带1G内存,所以系统默认就带了编译工具,对于像我这里用到的RTL8192CU驱动编译的话,在PCDuino上直接就可以编译完成了,无需进行交叉编译。
  3. Wifi AP设置wifi AP配置的一切麻烦都来自于8192CU这看似性价比高的芯片,A10所用3.4.29+内核里的驱动虽能用,但用起来还是不够好,所以RealTek官方还是给出了自己的源码,虽然会增加麻烦,但所幸官方提供了包括修改过的HostAPD源码,编译下内核再编译个HostAPD,就可以搞定了。官方给出的驱动包里的东西已经比较清除了,Linksprite上的链接提供了板子上直接编译该驱动的方法,以下附上链接:PCDuino编译 8192CU驱动
  4. IPtables规则设置IPtables依赖的netfilter功能在PCDuino默认内核上是没有开启的,因此需要重新编译内核和内核模块,理论上可以直接在PCDuino上完成,但内核编译显得耗时过长,生命有限,能少折腾还是少折腾了。Linksprite上的链接提供了在X86_64 Linux上交叉编译内核的方法,从文章的链接中也可以找到如何替换SDCARD上内核的办法,以下附上链接:PCDuino交叉编译内核需要注意的是,因为8192CU驱动为我们自己编译,建议下完内核代码后,把这款驱动的编译修改到内核编译过程里,避免每次需要手工复制,并且如果是在板子上编译的内核模块,与新交叉编译的内核文件并不配套,导致不能顺利加载,影响wifi接入功能的顺利调试。因为家里有多台设备需要通过此wifi router上网,IP tables的作就就是在ppp虚拟口(在未完成PPPoE拨号时,这里可以用内置接口eth0替换,相当于在HG8245下面又做了一级NAT)和WIFI接口间进行NAT,目前只用到一条MASQUERADE规则即可::::console iptables -A POSTROUTING -t nat -o ppp0 -j MASQUERADE
  5. PPPoE拨号在之前的步骤中,我一直还是通过HG8245 DHCP接入上网的,当上述设置完成后,就可以恢复HG8245为桥接模式了,在PCDuino上通过内置的RJ45 100M端口,通过HG8245光猫PPPoE拨号出去,自此但可享受远离HG8245频繁掉线的困扰了。Debian/Ubuntu系统上PPPoe拨号相对比较简单,网上有大把流程,这里就不再赘述了。

陷阱(坑啊坑)

  • PPPoE与TCP MSS问题由于PPPoE这样Overlay在Ethernet(最近研究了太多的overlay技术了)上,显然是有额外的封装代价的(需要在正常的以太报文上再额外消耗8个字节),这样就会与TCP协商时的MSS产生冲突,TCP采取本地网卡的MTU来填写TCP报文的MSS,而大部分终端默认网卡MTU都是1500,这样就会有可能在PPPoE封包过程(设想终端发过来的报文刚好1500,再加8字节的封装就大于1500了)中产生大于运营商链路MTU的报文,从而使和报文被丢弃,直接症状就是视频或图片加较缓慢或不能加载,影响家庭用户心情。以下链接提供了较为详细的介绍,可供参考:MTU and PPPoE详解对此问题,Linux Iptables提供了TCP Clamping的功能,直白点就是动态干预调整TCP MSS字段的设置(另外一种方法基于path mtu discovery,但容易受现网环境影响,不必折腾),具体命令如下::::console iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1460

后记

PCDuino用着让人不爽的几个地方:

  • PCDuino V1有点大材小用,外置的一些如PWM接口、Arduino接口对WIFI router这个角色基本没用,全志A10实际上带了Audio DAC,然而却没有引入3.5mm插口,没有HDMI显示器的话,音频输出有点伤
  • PCDuino社区不如RaperryPI强大,Linaro这个For arm Soc的发行版也不怎么酷,A10的kernel源码还基于3.4.29+,比较旧
  • PCDuino V1所用A10的EMAC集成网卡驱动有点乱,也不支持DMA收发,有点无爱

基于Ubuntu的Linaro用着让人不爽的几个地方:

  • Upstart这套init系统用着有点不爽,看来被Systemd占了上风也是有道理的。

然而生命的意义就在于尝试和体验,折腾虽然累了点,但看着老婆上网相当开心的样子,我还是很满足的:)

Emacs批量重命名文件

对于Emacs党来说,最好什么事情都能在Emacs中用键盘完成,即酷又方便,今天在编码过程中决定对一批文件进行重命名,发现在Emacs里完成这件事是相当的直觉(intuitive,直觉很重要,我个人很喜欢这种方式,因为通过减少思考而能够形成潜意识,从而提高效率),总结下来一共如下四步:

1> 使用find-dired命令找到所有待修改的文件

/home/fortitude/tmp/:
find . \( -name "test*" \) -ls
1180244    0 -rw-r--r--   1 fortitude bigdog          0 1月 31  2013 test.py~
1224276    4 -rw-r--r--   1 fortitude bigdog         36 1月 31  2013 test.py
1180238    4 -rw-r--r--   1 fortitude bigdog        142 1月 31  2013 test.pyc

2> 使用wdired-change-to-wdired-mode命令切换dired模式到wdired模式,即可编辑的dired模式

注意modeline上将出现Editable Dired标识

3> 像替换buffer一样替换上面的test为test1,祭出Emacs的各种编辑功能:)

/home/fortitude/tmp/:
find . \( -name "test*" \) -ls
1180244    0 -rw-r--r--   1 fortitude bigdog          0 1月 31  2013 test1.py~
1224276    4 -rw-r--r--   1 fortitude bigdog         36 1月 31  2013 test1.py
1180238    4 -rw-r--r--   1 fortitude bigdog        142 1月 31  2013 test1.pyc

4> 使用wdired-finish-edit1(默认快捷键为C-c C-c)来保存配置,批量编辑完成

/home/fortitude/tmp:
总用量 100
drwxr-xr-x  3 fortitude bigdog 4096 5月   5 23:22 .
drwxr-xr-x 84 fortitude bigdog 4096 5月   5 22:47 ..
-rw-r--r--  1 fortitude bigdog   36 1月  31 2013 test1.py
-rw-r--r--  1 fortitude bigdog    0 1月  31 2013 test1.py~
-rw-r--r--  1 fortitude bigdog  142 1月  31 2013 test1.pyc

我认为这里最酷的东西就是wdired-change-to-wdired-mode这个命令了,使得编辑文件名如同编辑buffer一样,相当惊喜!

云计算网络中的Virtual Router

云计算中的Virtual Private Cloud是一个比较流行的概念,该概念最早源于Amazon AWS,用于将众多虚拟机组成一个虚拟网络,并通过一个虚拟网关将用户远程的办公网络接入到云中来。

国内IaaS服务商在不断提升虚拟机业务的同时,也有青云、Ucloud已经先后推出了VPC的支持,阿里云据说内部也已经在做,但估计大公司在推产品方面因为有多方力量的制约,至今还未见到商用。

对于企业用户而言,通过自己手工安装各种VPN软件,通过隧道技术连接到云中的虚拟网络,还需要手工配置路由这些操作,会存在管维上的一些困难,因此,有些大公司已经明显看到这种趋势,开始推出基于虚拟机的软件路由器,主流的产品如下:

Cisco CSR 1000V

运行IOS XE,在硬件交换机上,协议类软件比较接近于Linux应用程度,而IOS XE就是基于Linux而来,因此对于思科而言,推出这样的产品并不会有多大的困难。

Brocade Vyatta 5400 vRouter

收购Vyatta得来,与下面的Vyatta Core/VYOS同源

H3C VSR 1000V

H3C的路由虽然与华为V3同源,但似乎没有用DOPRA这样的OS抽象玩意,而是跑在Linux上,因此这个VSR的推出应该与CSR 1000V类似,不会太困难。

这些商用的路由均提供了Secure WAN gateway、Routing,H3C和Cisco甚至都提供了MPLS功能的支持,并支持主流的Hypervisor软件,如VMware ESXi/KVM等,Cisco的1000V甚至在AWS Marketplace上提供了实例镜像供用户直接部署。 对于普通用户而言,除了上述商用产品外,目前可以选择的几款开源类似产品如下:

Vyatta Core

VYOS

Vyatta Core社区版本fork而来

目前这两款软件均提供了普通X86版本以及虚拟化版本(后续本网站会考虑分享一些使用心得)。

从目前来看,国内VPC还处于比较初级的阶段,似乎也未见到比较典型的部署案例(以后本网站会持续关注并分享),但随着公有云的逐渐流行,这方面应该会有不错的发展,值得期待,估计华为也会比较快推出这样的东西了。

PS:

我个人后面会有计划在国内建立一个Vyatta的开源社区,提供一个供本地化的交流平台。

对于EMACS language environment/coding system/charset的理解

即然已经列出了这个题目,就需要将其完成;当然,如果习惯于查阅英文资料的话,可以直接去gnu网站上查阅EMACS的网站,下面这些内容,也是摘自于该网站。
  
1、language environment

语言环境用于控制EMACS读取文件时所选取的编码系统(即下面的coding system),适用于读取文件、收取邮件、以及其他你读入EMACS的文本,同时,它也可以用来控制你创建文件时所使用的默认编码系统。

2、coding system

由于语言的多样化,就出现了多种编码系统(如LINUX上的utf-8、chinese-gbk、chinese-gb等),EMACS在内部处理中,并不使用这些多种多样的编码系统,实际上,它内部采用一种自有的编码系统(emacs-internal),在输入或者输出时,分别进行外部编码系统--->内部编码系统、内部编码系统--->外部编码系统的转换。

3、charset

在emacs中,charset即character set的缩写,汉语即对应于我们的字符集,就是通过2字节或者多字节(如GB2312采用2字节、UTF-8采用1~6可变字节表示字符)的定义,来表示一个语言所包含的各种字符(如汉字等),在EMACS中,可以通过list-charset-chars来查看特定字符集的内容。

根据第2点的介绍,不论文件源编码是什么,emacs可以做到识别读入并转化为自身的编码系统,并按照用户设置的优先编码系统,寻找合适的编码进行输出显示,而编码系统又会影响字符集的选择,从而最终影响字体的选择。

对于上面列出的概念,可能并不易于理解,可以借助于下面这个示例进行分析:
  
1、在设置:prefer-coding-system为utf-8-auto、set-language-environment为UTF-8时,在EMACS中,针对于一个新打开的包含中文编码,将光标定位于一个中文字符,键入c-u c-x =命令后,将显示如下结果:

  character: 用 (29992, #o72450, #x7528)<br>
  preferred charset: unicode-bmp (Unicode Basic Multilingual Plane (U+0000..U+FFFF))<br>
  code point: 0x7528<br>
  syntax: w     which means: word<br>
  category: .:Base, C:2-byte han, c:Chinese, h:Korean, j:Japanese, |:line breakable<br>
  buffer code: #xE7 #x94 #xA8<br>
  file code: #xE7 #x94 #xA8 (encoded by coding system utf-8-unix)<br>
  display: by this font (glyph code)<br>
  xft:-unknown-文泉驿正黑-normal-normal-normal-*-14-*-*-*-*-0-iso10646-1 (#x2FDC)<br>
  
2、在设置:prefer-coding-system为chinese-gbk、set-language-environment为Chinese-GBK时,在EMACS中,针对于一个新打开的包含中文编码,将光标定位于一个中文字符,键入c-u c-x =命令后,将显示如下结果:
  
  character: 用 (29992, #o72450, #x7528)<br>
  preferred charset: chinese-gbk (GBK Chinese simplified.)<br>
  code point: 0xD3C3<br>
  syntax: w     which means: word<br>
  category: .:Base, C:2-byte han, c:Chinese, h:Korean, j:Japanese, |:line breakable<br>
  buffer code: #xE7 #x94 #xA8<br>
  file code: #xE7 #x94 #xA8 (encoded by coding system utf-8-unix)<br>
  display: by this font (glyph code)<br>
  xft:-WenQuanYi-WenQuanYi Bitmap Song-normal-normal-normal-*-13-*-*-*-*-*-iso10646-1 (#x46D8)<br>