Emacs/Vim 深度比较

December 25th, 2016 No comments

生命在于折腾,折腾完了 Atom Editor,开始跟着陈斌大婶和 purcell的配置折腾 Emacs,比较下。很多人都在比较键位,比较插件,这是十分肤浅的,我们比较点深入的东西:

代码结构

  • Emacs 源代码:eLisp 79%, C 21%
  • Vim 源代码:C 52%, VimScript 48%

从代码结构上来讲,Emacs的代码最多的是 elisp,C代码只是一个微内核,Vim 里C代码还是大头。当然不排除 24.X, 25.X以后 Emacs源代码里带了好几个重量级的包,而 Vim向来比较精简一些,官方没带啥大点的插件有关。去除自带插件后,Emacs的 elisp代码比例应该会下降很多,不过总体来说,Emacs有更多组件使用 elisp开发而成,也就是说可以被用户修改或者替换的地方比 Vim要多,当然速度也会相应慢一点(比如 Emacs新打开上万行的文件连续按住PageDown时cpu 100%占满),不过比较大 JB,Atom Editor来说,还是快不少。

系统接口

大框架基本类似:

  • Vim 可以操作: buffer, window, tabpage, 光标,marker, region 跳转表等等。
  • Emacs 可以操作:buffer, window, frame, 光标,marker,region 异步进程 等等。

Vim 有 local,Emacs有 mode,Vim有事件触发,Emacs有各种钩子,基本大框架类似的。

键位设置也都很灵活,会配置的话,可以把 Emacs键位全部弄成 Vim的,比如 Evil,或者Vim里面也可以配制成进去就自动进入插入模式,全部用 Emacs键位。

具体到比如 buffer 或者窗口里面,Emacs的窗口或者 buffer /window 属性更多一些,Vim也有一些 Emacs没有的基础设施,比如 jumplist, quickfix之类的,不过 Emacs也可以用插件实现,实现 jumplist没问题,比较独立,但每个插件实现一个类似的 quickfix的东西,实在是比较蛋疼。

脚本语言

VimScript 类似 Lua,但更啰嗦些,没啥好说的,要上手可能就是2-3天的样子,主要是实际编写中熟悉各种 Vim内部结构体系,以及api。

EmacsLisp 类似 clisp,稍微有些区别而已。clisp中的向量: (vector 1 2 3 4) 在 elisp中可以用中括号来表示:[1 2 3 4] 这样对减少小括号数量有一些帮助,代码读起来没那么拗口,可惜实际写 elisp的过程中,向量用的并不多,各种插件用的最多的还是 list,alist,plist 几种容器,小括号还是满天飞,如果不借助编辑器的匹配,缩进,还有彩虹括号(不同层次的括号用不同颜色的小插件)代码很难阅读,自己写好写点,看别人代码要对括号对半天(特别是他们把很多写一行的话)。

Lisp变量可视范围比较好用,局部变量可以 “遮盖住” 全局变量,比如你在 VimScript里面要临时跳转到一个新目录需要这么写:

let cd = haslocaldir()? "cd" : "lcd"
let saved = getcwd()
silent! exec cd "my new directory"
......
silent! exec cd saved

而 elisp里面,利用局部变量遮盖特性,简单的:

(let ((default-directory "my new directory"))
  .....
  )

在这个 let的作用范围内,buffer的全局变量 default-directory 被新定义的同名局部变量给遮盖住了,里面的代码(包括调用外面定义的函数)取这个名字的变量,都会取到你新定义的变量,作用范围结束就恢复了,这样比 vimscript方便不少,类似的用法还有 (with-selected-window new-window...) 把当前窗口设置成 new-window并做一些事情,当离开代码作用域以后,自动恢复成先前的窗口,如果VimScript来做这事情,又需要保存前窗口,然后跳转,最后又恢复。

VimScript在 vim6.0及以前的时代里比 EmacsLisp弱很多,连个 List,HashTable之类的容器都没有,怎么写复杂的程序嘛,Vim7.0以后补充了字典和列表还有三元式,简单面向对象等,终于可以写点复杂的东西了,而 Vim下很多传奇性的插件,其实也正是 7.0以后才出现的。Vim8.0以后又进一步补充了匿名函数和 partial function (类似简陋版的 curry 函数)等等特性,除了啰嗦外,写点复杂东西问题不大,比如前久有人用 VimScript写了一个 C编译器,可以在Vim里跑 C脚本。

Lisp里面的宏类似C++模版加强版,C++模版只有一层,定义好模版,然后生成代码。Lisp的宏可以先用程序生成模版,再由模版生成代码,类似模版的模版。不过实际使用中,为了不让自己的代码飞的太远,难以维护,大部分插件开发者在 elisp里面最多就是 require一下 common lisp的兼容包,用一些 common lisp里面诸如 loop, for, incf, case 之类的通用宏,自己定义也限于定义一些小工具,没有人丧心病狂的搞一些影响程序结构的东西,定义成另外一门语言。

大部分插件开发者实际上还是把它当作充满括号的 lua 来用,除去一些便利性外,麻烦也还是挺多的。

异步进程

早期的 Vim没有异步进程操作,导致很多插件没有 Emacs那么顺畅,比如生成 tag或者编译,Vim只有傻等着,Emacs可以同时做其他事情,Vim8.0以后异步接口和后台时钟等也比较完善了,假以时日各大常见插件能逐步发挥 8.0特性的时候,相信也能带来一致的体验,包括纯vimscript实现的 shell,以及 gdb集成,发邮件等,问题不大。

Read more…

Categories: 随笔 Tags:

Aix 折腾手记

December 8th, 2016 No comments

早年开发工作主要在 FreeBSD进行,2006年后来切换到 Linux下,期间穿插使用了一下 Solaris,所以我的网络库一直都是只支持这三个系统。为了让网络库支持更多平台,网上购置了一台 IBM AIX 小型机,因为其他大部分非 Linux系统,今天基本都可以在虚拟机里面安装了,而 AIX系统,你真的没法虚拟。

弄了几天以后,发现真他妈的麻烦,强大是强大,但是真的太琐碎了,相比之下,Linux/FreeBSD之流基本是傻瓜了。不看说明直接操作 AIX的话,可能连开机都麻烦,或者关机没关对,下次直接启动不了。

文字终端就没什么好拍的了,先上一张图形桌面的靓照吧:

是的你没看错,这就是 AIX 7,2012年的操作系统,就是那么的霸道,四处透着古典 Unix的味道。这样的机器今天还跑在各大银行的机房里,AIX系统管理员也拿着比 Linux系统管理员多几倍的工资,虽然工作岗位比较稀少。

下面来感受一下你想正常开关机,安装软件的话要怎么弄:

Read more…

Categories: 随笔 Tags:

Linux 网桥设置

December 8th, 2016 No comments

在公司机房的物理机上架设 KVM虚拟化的时候,经常需要配置网桥,先要安装网桥工具:

apt-get install bridge-utils   
apt-get install uml-utilities

编辑 /etc/network/interfaces,参考下面配置加入网桥配置信息:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet manual

auto br0
iface br0 inet static
    address 192.168.10.6
    netmask 255.255.255.0
    network 192.168.10.0
    broadcast 192.168.10.255
    dns-search dell1
    bridge_ports eth0
    bridge_stp off
    bridge_fd 0
    bridge_maxwait 0

auto eth1
iface eth1 inet manual

auto br1
iface br1 inet static
    address 14.152.50.6
    netmask 255.255.255.192
    network 14.152.50.0
    broadcast 14.152.50.63
    gateway 14.152.50.1
    bridge_ports eth1
    bridge_stp off
    bridge_fd 0
    bridge_maxwait 0
    # dns-* options are implemented by the resolvconf package, if installed
    dns-nameservers 114.114.114.114
    dns-search dell1

auto eth2
iface eth2 inet manual

auto br2
iface br2 inet static
    address 112.93.114.2
    netmask 255.255.255.224
    network 112.93.114.0
    broadcast 112.93.114.255
    gateway 112.93.114.1
    bridge_ports eth2
    bridge_stp off
    bridge_maxwait 0
    dns-nameservers 114.114.114.114
    dns-search dell1

上面配置针对双线接入多 IP的情况,这样 br0-br2就可以给虚拟机 guest使用了,br0是内网,br1-br2是电信或者联通出口。

Categories: 随笔 Tags:

Linux 硬件时区折腾备忘

December 8th, 2016 No comments

前段时间折腾家中 Nas的虚拟化服务,有时候虚拟机系统时间总是快8个小时。Guest这边设好了,到了 物理机就会慢8个小时。网上说只要修改/etc/default/rcS中的 UTC=no就行了,但还是没反映,没办法,一步步找问题。发现在/etc/rcS.d/S05hwclock.sh有这样一段话:

# 2012-02-16 Roger Leigh rleigh@debian.org
# - Use the UTC/LOCAL setting in /etc/adjtime rather than
# the UTC setting in /etc/default/rcS. Additionally
# source /etc/default/hwclock to permit configuration.

 
也就是说时间是按照/etc/adjtime设置的,而不是/etc/default/rcS,晕倒。查了下adjtime文件,原来这个才是现在调整时间的设置文件,那个rcS已经被忽略了,也就是网上的那些方法只适合以前的系统,看来走了不少弯路啊。

将 /etc/adjtime 第三行由 UTC 改为 LOCAL 即可。

Categories: 随笔 Tags:

Linux 线上系统调优备忘

December 8th, 2016 No comments

大公司呆久了,都会对 SA的依赖十分强烈,很多事情 SA都帮我们搞定了。如今控制成本,没有招聘 SA,又没有购买 VPS,从买物理机开始到 IDC部署,服务器调优,虚拟机管理,全部都是自己来,才发现,安装一台 Linux机器自己玩很简单,但是要达到线上服务器的标准,还有若干调优工作需要做,有 SA的日志是多幸福的事情啊。

物理机设备驱动

Dell服务器默认安装系统后会报找不到驱动:

W: Possible missing firmware /lib/firmware/tigon/tg3_tso5.bin

因为 Debian/Ubuntu 的包都是开源的,默认开源驱动性能不行,于是需要添加 non-free源:

deb http://ftp.de.debian.org/debian main contrib non-free
deb-src http://ftp.de.debian.org/debian main contrib non-free

然后:

apt-get update
apt-get install firmware-linux-free firmware-linux-nonfree

解决 Dell驱动报错问题。

配置限制

查看 /etc/security/limits.conf 没有就新建,加入 core 和 nofile 的相关配置,比如:

* soft nofile 65536
* hard nofile 65536
* soft core unlimited
* hard core unlimited
# End of file

之类的,不用到 ~/.bashrc 里面调用 ulimit。

网卡多队列

打开内核网卡多队列支持,避免网卡中断都集中在 cpu0 上处理,多队列打开后,可以让网卡中断均摊到各个 cpu上,对提高并发十分有用:

wget http://skywind3000.github.io/install/set_irq_affinity.sh

放到 /etc/config 目录下面(没有就新建),然后在 /etc/rc.local 里面加一行,每次重启就跑 set_irq_affinity.sh 且必须用 bash跑,参数传入需要开启多队列的网卡,以下是 /etc/rc.local 的内容:

/bin/bash 
/etc/set_irq_affinity.sh eth0 eth1 eth2

exit 0

下面是 setirqaffinity.sh 的代码:

Read more…

Categories: 随笔 Tags:

Linux 下配置 Iptables 端口转发

December 8th, 2016 No comments

经常需要对服务端的端口进行转发映射,让链接A服务器(202.181.55.93:8080)端口的所有tcp数据,forward到B服务器(106.185.43.22:80)端口,比如国内联通出国访问比较快,而电信比较坑爹,如果你家是电信网络,你却想畅快的访问境外的 vps,那么可以在你联通网络的境内服务器上进行这样一个端口映射,你链接境内服务器(201.181.55.93:8080)端口相当于链接境外服务器的(106.185.43.22:80)端口。

Linux内核的 Iptables很强大,它维护一组内核变量,使用 /sbin/iptables命令进行直接规则设置,且通过配置新规则,可以直接支持端口转发,简单在 A服务器上使用下面这个 shell脚本即可:

#! /bin/sh

# create forward rule by source interface
# http://serverfault.com/questions/532569/how-to-do-port-forwarding-redirecting-on-debian
PortForward1() {
    local IN_IF=$1
    local IN_PORT=$2
    local OUT_IP=$3
    local OUT_PORT=$4
    local IPTBL="/sbin/iptables"
    echo "1" > /proc/sys/net/ipv4/ip_forward
    $IPTBL -A PREROUTING -t nat -i $IN_IF -p tcp --dport $IN_PORT -j DNAT --to-destination ${OUT_IP}:${OUT_PORT}
    $IPTBL -A FORWARD -p tcp -d $OUT_IP --dport $OUT_PORT -j ACCEPT
    $IPTBL -A POSTROUTING -t nat -j MASQUERADE
}

# create forward rule by source ip
# http://blog.csdn.net/zzhongcy/article/details/42738285
ForwardPort2() {
    local IN_IP=$1
    local IN_PORT=$2
    local OUT_IP=$3
    local OUT_PORT=$4
    local IPTBL="/sbin/iptables"
    echo "1" > /proc/sys/net/ipv4/ip_forward
    $IPTBL -t nat -A PREROUTING --dst $IN_IP -p tcp --dport $IN_PORT -j DNAT --to-destination ${OUT_IP}:${OUT_PORT}
    $IPTBL -t nat -A POSTROUTING --dst $OUT_IP -p tcp --dport $OUT_PORT -j SNAT --to-source $IN_IP
}

PortForward1 eth3 8080 106.185.43.22 80



# put this file on /etc/network/if-up.d

上面两个函数,一个是针对网卡,一个是针对本地IP设置转发,记得把这个脚本放到 /etc/network/if-up.d 下面并设置可执行权限(Debian/Ubuntu),每次开机重启时保证可以运行,即可。

Categories: 随笔 Tags:

Vim 里如何映射 CTRL-h 为 left ?

November 28th, 2016 No comments

很多人习惯在配置文件中映射 CTRL+HJKL 为光标移动,却碰到了一些问题:

inoremap <c-h> <left>
inoremap <c-j> <down>
inoremap <c-k> <up>
inoremap <c-l> <right>

映射后无效或者映射以后按 <BS>键不能删除,这是什么原因呢?

很简单,因为你的终端软件(Xshell, SecureCRT)有些老,默认配置是按下<BS>键以后
发送的是:^H (ASCII 码 0x08),而 ^H在你的 Vim 里被你 inoremap 成 了,所以你按了<BS>会被认为按下了左键。

早在 VT100终端时代,^H(ASCII码 0x08)表示<BS> 而 ^? (ASCII码 0x7f)表示<DEL>。过去 0x7f是留给 DELETE键使用的。而到了 VT220时代,DELETE已经变为 ^[[3~ (ASCII 码 0x1b, 0x5b, 0x33, 0x7e 共4个字节),而 ^? 的 0x7f 换给了我们的<BS>,有些老点版本的终端软件,默认 <BS>还是使用 VT100的 ^H,比如 Xshell 4 Build 0142以前的版本,默认<BS>是发送^H。SecureCRT直到6.x版本还在默认发送 VT100的 ^H。

你需要做的就是改一下终端默认配置而已,大部分不那么陈旧的终端软件,如今都是默认VT220的标准,比如 Xshell4 Build 0142及以后的 Xshell5,Putty,Mac下面的 Terminal.app, iTerm2,Ubuntu下面的 gnome-terminal,他们都是把 <BS> 发送成 ^?

你可以在你服务端下面查看下默认的键位设置:

$ stty -a

现在所有 Linux服务器的 erase (bs)基本都是 ^? 了(如果链接到非 Linux老操作系统 erase不是这个的话,需要改一下,可以在系统层改,也可以vim里面 set t_kb=…),Vim里面也是认可 ^?的,可老旧的终端软件却默认发送 ^H,不过好在他们都支持修改:

Read more…

Categories: 随笔 Tags:

Vim 中正确使用 Alt映射

November 19th, 2016 No comments

最简单的做法是:首先将终端软件的 “使用 Alt键作为 Meta键” 的功能打开,其次将 Alt的模式改为 ESC+字母,意思是如果你在终端下按下 ALT+X,那么终端软件将会发送 <ESC>x 两个字节过去,字节码为:0x27, 0x78。如果你使用过 NeoVim 或者 Emacs的话,这一步应该早就做过了。

XShell4 终端设置:

SecureCRT:终端设置

其他终端软件里:

  • Putty/MinTTY 默认ALT+X 就是发送 <ESC>x过去
  • Mac下面的 iTerm2/Terminal.app 需要跟 XShell / SecureCRT一样设置一下
  • Ubuntu 下面的 GnomeTerminal 默认也是发送 <ESC>x过去的
  • 任意平台下面的 xterm 可以配置 ~/.Xdefaults 来设置这个行为。

这样的话,不管是 NeoVim 还是 Emacs都识别了,Vim 的话,你可以简单这样:

noremap <ESC>x :echo "ALT-X pressed"<cr>

注意 ESC后面是小写 x,如果你是大写 X就变成 ALT+SHIFT+X了。于是你在 Vim 中,ALT+X就能看到后面输出的那句话了。看到这里你也许要问:这和我快速按下 ESC再马上按下 x键有什么区别?答案是没有区别,在终端里面这两个操作是一模一样的键盘码传送过去。

就像你不设置 ttimeoutttimeoutlen,然后快速在 VIM 里面按下 <ESC>OP,Vim 会以为你按下了 一样。因为 F1 的终端下字符串序列就是 <ESC>OP ,而你在 Insert 模式下面马上 <ESC> 退出并按下大写 O ,向上插入一行,Vim 将会等待一秒钟(默认 timeout ),确认后面没有一个 P,才会进一步确认不是F1,而是向上插行。

所以更好的做法是直接按照 <M-x> 进行映射,并且告诉 vim,<M-x>的键盘序列码是多少,然后再加上 ttimeoutlen超时:

noremap <M-x> :echo "ALT-X pressed"<cr>
exec "set <M-x>=\ex"
set ttimeout ttimeoutlen=100

这样做的好处是告诉 Vim, ESC+x是一个完整的按键码,并且需要在 100ms以内进行判断,即,如果收到 ESC,并且100ms以后没有后续的x,则是认为是一个单独的ESC键,退出 INSERT模式,否则认为是按下了 ALT+X,这和 Vim处理方向键,处理 F1, F2等功能键的原则是相同的,具体见 :h set-termcap:

                            *:set-termcap* *E522*
For {option} the form "t_xx" may be used to set a terminal option.  This will
override the value from the termcap.  You can then use it in a mapping.  If
the "xx" part contains special characters, use the <t_xx> form: >
    :set <t_#4>=^[Ot
This can also be used to translate a special code for a normal key.  For
example, if Alt-b produces <Esc>b, use this: >
    :set <M-b>=^[b
(the ^[ is a real <Esc> here, use CTRL-V <Esc> to enter it)
The advantage over a mapping is that it works in all situations.

You can define any key codes, e.g.: >
    :set t_xy=^[foo;
There is no warning for using a name that isn't recognized.  You can map these
codes as you like: >
    :map <t_xy> something

这是最完善的 ALT键解决方案了,网上有个流传很广的方式是 map <ESC>x <M-x> 然后你后面再映射 <M-x> 时就能被触发到,这是错误的方法,不能使用更短的 ttimeoutlen来识别键盘码,而会使用普通组合键的 timeoutlen来判断,后者一般设置为默认 1000毫秒,所以这样把 26个字母映射后,你 ESC退出 INSERT模式后,一秒内按了任何一个字母就会被当成 ALT+X来处理了,经常误操作。

如此,我们可以在 .vimrc中 for循环将 <M-0><M-9><M-a><M-z>等全部 set一遍,vim中即可正常使用。

早年的终端,处理ALT组合键时,是将单个字符的最高位设置成 1,这也是 vim的默认处理方式,如今 rxvt终端也支持这种模式(见上图 SecureCRT设置面板)。这种键盘码不是 ESC+x的模式,可以直接识别,不需要计算超时,缺点是支持终端较少,对终端编码格式有依赖。

如今基本上 <ESC>+的模式基本成为大部分终端的默认方式,主流操作了,详细可以看::h map-alt-keys 以及 :h set-termcap 两个文档有具体说明,关于超时部分可以见(:h esckeys

当然,如果你真能在100ms内连续按下 ESC和 X的话,那是另外一回事情了,你可以调短 ttimeoutlen到50ms解决,但是不建议该值低于 25ms,否则在低速网络情况下,你按功能键会被vim错误识别成几个单独的按键序列。

对于牛逼的打字员,也许 ttimeoutlen 设置的再短也没有用,当他瞬间快速 ESC退出插入模式并同时按下u时,他可能发现自己居然还呆插入模式中,因为被识别称 ALT+u了,而我们之前把 开头的A-Z全部都设置了一遍,这样放错的概率会大很多。

好了,下面是终极解决方法,重新定义终端软件的按键序列码:

Read more…

Categories: 随笔 Tags: