撞残的飞机

昨晚做了一个梦,买了一架小型私人飞机,然后在屋外试飞,云里雾里,怡然自得。结果在降落的时候撞到了院里的墙壁,把飞机屁股撞烂了一块。而后又试飞了一次,结果在降落的时候又撞到了墙壁,这次屁股都给撞冒了烟,感觉到当时心里难受极了。

为什么要把这些没头脑的破梦记录下来

梦到底是什么呢?它其实是我们潜意识的表现。说到潜意识就有趣了,在晴天白日,它就像是潜入水下的怪物,我们很难察觉。而梦很妙,在梦里,这些怪物们会浮出水面,变换一种形式出现在我们面前。

对,变换一种形式,所以它要告诉我们的,并非显而易见的。所以,就有了周公解梦。

记得小时候,过年我们买了一个日历,一日一页,每页一个周公解梦。那时,我每做一个梦,都要去翻找对应的那一页,看看这个梦到底是什么意思。结果最后把日历翻得破烂不堪。这是我对梦的一个很早的记忆。

如何通过梦找到另一个更真实的自己

梦的意义,就是帮我们找到那个潜藏的、不易觉察的自己。它更真实,因为它是我们身体放松状态下的感受,受到的压力更小,所以更容易舒展自己。

它在帮我们找到更真实的自己。这听起来太棒了,我们自己就会引导自己找到更真实的自己。

但是实现起来却没那么容易,它需要练习,如何练习呢?

首先,我觉得《周公解梦》有时是靠谱的,但有时是不靠谱。“有时不靠谱”就意味着它不靠谱。虽然听起来有点让人沮丧,但还好,我们可以参考一下,毕竟还有我们自己把关一下。

然后,我认为心理学对解梦很重要,同时它又是我们了解自己的另一种途径。这觉得这就是科学的好处,让不够清晰明确的概念慢慢形成体系框架,使得后来人可以通过在前人的基础上进一步完善,科学得以继续往前发展。人类终于通过自己的方法,即科学,实现了跨越时间的,前辈与后辈的合作。

这个梦到底意味着什么呢

回到我这个梦,对我来说到底意味着什么呢?

我利用网络,查看了梦的解释,五花八门,什么大难临头,什么胜利凯旋,什么人际关系转好。这叫人怎么信,又怎么能安抚我那不平静的心。

我在梦里有一种事物被破坏的感觉,自己心爱的东西被自己毁坏了,有一种说不出的难受。

飞机对我来说,是一个“挑战”的意象。

于是,我回想起了这个阶段做的事情,最近在做一个编辑器插件,用来辅助之前开发的一个系统。开发用的语言是自己之前没怎么用过的,语法结构、常用库啥的都得学习。这么想来,我是很担心这个项目会失败的。但因平日忙工作,可能这个担心自己并没有很清楚的意识到,而在梦里,另一个自己告诉我,我其实挺担心这个项目会被我搞砸的,尤其是别人没做过的东西。其实是担心搞一个新的方向的失败,它个梦就是现实的心理状态的一个投射。

所以这个挑战的意象,我觉得更像是挑战自己的内心,而非现实的处境。

写到这里的时候,我感到了幸运。毕竟是在做自己之前未尝试过的东西。失败其实并不可怕,我们又做了一个方面的尝试。

Git之对象数据库

数据库,顾名思义,即存储数据的仓库。

我们的Git是一个关系型对象数据库,怎么理解呢,它主要管理对象及对象之间的关系。而对象间的关系,组成了一个图结构。

图结构

我们先来看一下Git里对象的关系是怎样的。如下图。

git-objects.png

这是我用git-show-objects工具生成的某个Git仓库的对象关系。

矩形框(包括折角矩形框)即我们要研究的对象,而椭圆形是我们的指针。下面我们来分别看一下Git里的对象和指针。

对象

Git中的对象主要分两大类:不可变对象可变对象

不可变意味着,如果对象的某个域要发生变化,我们会创建一个新的对象,而不是修改原来的对象。

这就有几个很有意思的事情:

  1. 频繁创建对象会占用大量磁盘,但它会带来更好的灵活性和可跟踪性
  2. 不可变的对象更安全,可以共享使用,这在一定程度上又节省了磁盘

对于可变对象,由于其域可变,故我们是决不可能用它来保存历史的,但是保存临时的或一次性的数据则非常理想。

不可变对象

Git的图结构主要是由不可变对象组成的,Blob、Tree和Commit是我们接触最多的三类不可变对象。

Blob

Blob对象用来保存我们项目文件的内容,对应上图中的折角矩形。

有一点比较关键,Blob对象只保存文件内容。这就意味着,如果我们有两个不同的文件,而其内容竟然一致,好了,我们只会生成一个Blob对象。

Tree

Tree对象用来保存我们项目目录的内容,对应上图中的蓝文字矩形。

跟Blob对象一样,如果我们有hello1/dir和hello2/dir两个目录,而两个目录的内容竟然递归相同,那我们的两个dir只需要一个Tree对象就可以了。

Tree对象内保存着一个列表,它的每一项,都是一个文件名+Blob对象目录名+另一个Tree对象(子目录)。

Commit

Commit对象用来保存我们的提交动作,对应上图中的橘红色矩形。

Commit对象里保存的信息比Blob和Tree都要多,因为它已经是我们经常访问到的对象了。

Commit对象里有我们想知道的作者提交时间 信息,它还保存着此次提交的父提交。当然,父提交有可能是两个,三个或百八十个,只要你喜欢。

可变对象

Git中的可变对象最典型的就是Index对象,它是一个全局对象,或称其为单例对象。

Index对象维护着当前Git对应的文件及状态,是我们操作Git的入口。它维护一个列表,每一项包含了文件的mode对应的Blob文件对应路径和一些Git自己使用的标记。

指针

上面提到保存着父提交保存着对应的Blob,那这些是以何种类型保存到对象的域的呢?

答案是哈希值

我们知道,在计算机中,一个对象是有一个内存地址对应的,而Git的对象,用的是对象的哈希值。即,Git计算某个对象的哈希值,使用此值作为文件的路径,并将对象的内容序列化后存储到该路径。

实际上,Git将其要维护的对象统一存放在项目根目录的.git/objects/位置,并以对象哈希值的前两个字符作目录名,剩余字符作文件名,生成对象目录。

我们之前说,Index对象是我们操作Git的入口。指针,即是我们访问对象的入口。

Git中的指针直接或间接指向Commit对象,分为常量指针和变量指针。它们对应上图中的椭圆形标记。

变量指针

随着我们对Git的操作,变量指针的指向有可能发生变化。

Head、Remote

变量指针就像我们的书签,标记着我们当前看到了哪一页。

最典型的变量指针是Head(Remote),也即是我们常说的分支。如上图中的heads/master,它对应我们本地的master分支。

HEAD是一个特殊的指针,跟Index相似,它有且只有一个,顺着它指的方向,我们总可以找到一个Commit。

这里要注意,HEAD正常情况下是指向Head的,但上图中它直接指向了某个Commit,这就是Git里面的游离态(detached)。

常量指针

常量指针是一旦创建,就很少发生变化的指针。注意,是很少,并非没有。

Tag

Tag是常量指针的典型,它主要充当我们的标记笔,考试画重点必备。

例如,我们可以用它来标记一个稳定的发行版本对应的Commit,以供伙伴们检出或下载。

常用操作

git init

初始化仓库,主要是初始化.git/目录,创建Index对象、HEAD指针,以准备接下来的工作。

init.png

git add

创建Blob对象,并将对应文件添加到Index对象。

add.png

git commit

递归创建Tree对象,创建Commit对象,并保存根目录对应的Tree对象。更新HEAD指向的指针的指向。

commit.png

问题

此时,一个简单的提交就完成了。当然,这连一个开始都够不上。

如果这三个命令你都理解了,不仿实操一下以下几个问题:

  1. fast-forware是如何操作的?
  2. 上面提到过detached,它有什么问题,应该如何避免?
  3. 根据Git的实现原理,如何设计系统可以更好的利用磁盘空间?

其实第三个问题恰是我们设计简洁系统的一个原则。

参考

根本停不下来?可以看一下maryrosecook写的《git from the inside out》,如果看英文费劲也可看一下我的粗陋翻译《彻底理解git》。

自己创建一个测试仓库,使用git-show-objects工具,边做边观察一下发生了什么。

为什么我不使用JetBrains的屌炸天编辑器

首先解释一下,JetBrains并没有出过一款叫做“屌炸天”的编辑器。

作为一个提到编辑器圣战内心就无比激动的码农,我使用过JetBrains的大部分产品,从开始的PhpStorm,到PyCharm、Intellij IDEA、CLION、android studio,这些产品我都有摸过。

JetBrains生产的编辑器无疑是非常贴心的,代码补全、智能提醒、强大的重构功能,等等一大坨超级特性曾经让我爱不释手。

说到曾经,为什么是曾经爱不释手呢?

有一段时间,我的主要工作集中在脚本开发上。这让我第一次意识到了这种大型IDE的笨重。

后来,我开发一个基于django的系统,最初使用的是vim + 自已找的一大批插件。系统开发完后,我想起了被遗忘在角落的PyCharm。这时,我打算重新拾起这个屌炸天的编辑器。

此时,我已经是vim的重度用户了。为了延续vim那妙不可言的触感,我在PyCharm里安装了IdeaVim。不过用了没多久,就感觉哪里不对劲起来。

好了,BB了这么多,以下才是文章内容。

为什么我不使用JetBrains的屌炸天编辑器?

真的很棒

JetBrains的编辑器真的很棒,界面非常漂亮,而且非常智能,就像你编码时有一个专家在你身边一样。

如果是初学者,你值得拥有。

启动慢

启动真的非常非常慢。

emacs对此有理:别看我启动慢,但我一天只启动一次,启动一次在里面宅一天。

但是你JetBrains不行啊,尤其是对于运维开发的同学,很多时候是跟脚本打交道,而且要跨机器,JetBrains做不到。

我不习惯把一个笨重的软件放那里放一天,除非8个小时中我要在里面呆6个小时。

编辑体验不够流畅

有的人可以做到,主动关闭自己的听觉,这时他们就真的听不到了。

如果你做不到,你要想想,敲个字符它就给你跳个小灯泡,还主动问你要不要来杯咖啡是个什么意思。

我以前一直觉得vim的界面可真够简陋的,给我显示个文件夹图标吧,或者让我在编辑器里起个bash玩?

搞飞机!编辑器是用来编辑的,NERDTree还帮我们实现了一些文件浏览的功能,还不知足!

我以前有一种想法,一定要让编辑器最节省按键。所以搞了一套非常神奇的一字码快捷键的代码片断来自动补全。

但是当我思考编辑器想要给我什么的时候,我还是被自己吓了一跳。

编辑器帮我把我头脑中的想法通过指尖表达出来,并纠正校对。从这个角度讲,如果为了省个按键,把if定义成了i,那么在之后的时间里,我们的思维势必会被这个i扭成一团。流畅地表达想法很重要,最好是一气哈成。

JetBrains的界面真的是太棒了,但是感觉自己游在里面,注意力都被目的之外的东西分散了。

然而还是得看习惯

如果你是一个已脱离智能提示这种高级趣味的码农,如果你已有足够的经验来判断是非,还是建议你多用用朴实无华的编辑器(绝不包括notepad)。

当然,还是一个习惯问题,我的老大就在用JetBrains的产品,我有很多优秀的同事也在用。

这不是选举,你也不必投票。

所以JetBrains产的编辑器真的非常棒

闲暇时,我还是会打开PyCharm,欣赏一下这惊艳的UI,然后关掉它,想想生活真是很美好!

16年逝,17年始

屎屎屎,都是屎。。。

一年又转一年,感觉白驹过隙的,是否又废了这365天,过成了一个老爷爷老太太的模样?

关键字:读书

16年的关键字该是读书,虽然量不大,但还是开了个好头。也多亏自己这浮躁的嫉妒心被《微信读书》抓个正着。

认知:钱到底是什么

16年,自己的认知发生了很大的变化,特别是对钱的认识上。

之前晚上转火车都不舍得花钱住店。

16年去过几次海底捞,终于明白海底捞对我意味着什么了。

钱是等价交换物,是一把衡量价值的尺子。

对个人来说,比钱更珍贵的东西,我现在找到了这么几样:时间、精力、好心情。

16年第一次在得到上,狠下心订阅了400块的专栏。其实,没过几个星期,我就知道,花出去的钱已经变换成更重要的知识回赠回来了。

这几样比钱更重要的东西,成了我在kindle花几百块买电子书的理由:随着互联网的开放,我们当然可以找到大部分资源,但是找资源的过程中,我们有可能会失去比金钱更重要的东西,时间,精力。

关于“老好人”

年底最大的收获是,下定了决心,不再做一个虚假的“老好人”。

转头一看,却也已经带着这个面具欢快的生活了二三十年。“老好人”并非一条不归路,但走地越久,越像是一个干瘪的僵师,倒更像是老人家说的,离坟墓越来越近。

不要为难自己,做更真实的自己。

Linux命令之iostat

iostat来自sysstat工具包,可以查看CPU和系统磁盘IO的统计信息。本文主要介绍iostat的主要命令参数和输出的部分解释。意在帮助不熟悉的用户入境,高级用户可以直接查看man page。

用法

iostat [ options ] [ <interval> [ <count> ] ]
  • opions 选项
  • interval 每隔多少秒统计一次
  • count 连续统计多少次

注意:如果没有提供intervalcount选项,则系统只统计一次。统计结果是上次统计(如果没有统计过,则是系统启动时间)到本次统计的数据。

输出

执行iostat的输出如下:

Linux 3.16.0-4-586 (oogway)     06/02/2016     _i686_    (1 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           2.19    0.00    0.39    0.18    0.00   97.24

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda               1.74        24.40         3.32     222286      30252

第一行:系统信息

系统信息的输出和uname -a的输出类似。

  • Linux 内核名称
  • 3.16.0-4-586 内核发行版本
  • oogway 主机名
  • 06/02/2016 当天日期
  • _i686_ 机器硬件名
  • 1 CPU CPU个数

第二行:CPU利用率

CPU利用率的信息和top里的CPUT信息类似。

  • %user 没有修改nice的任务的用户态时间占比
  • %nice 修改过nice的任务的用户态时间占比
  • %system 系统态时间占比
  • %iowait 等待IO的时间占比
  • %steal 虚拟cpu等待supervisor的时间占比
  • %idle 空闲时间占比

第四行:设备的读写信息

  • tps 每秒读写请求次数
  • kB_read/s 读取速度
  • kB_wrtn/s 写入速度
  • kB_read 读取量
  • kB_wrtn 写入量

常用参数

统计次数及频率

如果我们希望每一秒统计一次数据,连续统计10次,可以iostat 1 10

查看扩展统计信息

可以使用-x参数查看扩展的统计信息。

iostat -x输出如下:

Linux 3.16.0-4-586 (oogway)     06/02/2016     _i686_    (1 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           2.65    0.00    0.39    0.18    0.00   96.79

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.56     0.37    1.21    0.30    20.00     3.66    31.28     0.01    3.79    2.85    7.63   1.74   0.26

统计信息多了的这几项分别是:

  • rrqm/s 每秒读请求合并数
  • wrqm/s 每秒写请求合并数
  • r/s 每秒完成的读请求数
  • w/s 每秒完成的写请求数
  • rkB/s 读取速率,同kB_read/s
  • wkB/s 写入速率,同kB_write/s
  • avgrq-sz 请求的平均扇区数
  • avgqu-sz 请求的平均队列长度
  • await IO请求的平均耗时
  • r_await 读请求的平均耗时(ms)
  • w_await 写请求的平均耗时(ms)
  • svctm IO请求的平均服务耗时(该指标在未来将被移除)
  • %util 单位时间内IO请求占比。当达到100%时,说明系统IO已饱和(RAID和SSD除外)。公式(r/s+w/s) * (svctm/1000)。如果做了RAID或使用了SSD,这个值就不可靠了。

Linux命令之top

top命令可以动态实时地显示系统的运行状态,它的输出包括系统信息、进程信息和线程信息等。这篇文章主要讲解top输出的各项信息,以帮助用户更好的理解和使用top。

top的输出信息

top命令在我本地的输出如下:

top - 10:06:56 up  2:38,  1 user,  load average: 0.00, 0.03, 0.05
Tasks:  74 total,   1 running,  73 sleeping,   0 stopped,   0 zombie
%Cpu(s):  5.0 us,  0.7 sy,  0.0 ni, 94.0 id,  0.3 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:    772276 total,   281820 used,   490456 free,    30140 buffers
KiB Swap:   976892 total,        0 used,   976892 free.   126516 cached Mem

 PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
1359 root      20   0   69700  30232  13524 S  4.6  3.9   4:17.45 emacs
1780 root      20   0    5080   2796   2448 R  1.0  0.4   0:15.30 top
 456 redis     20   0   29660   3084   2472 S  0.7  0.4   0:34.57 redis-server
   1 root      20   0    5576   4208   2964 S  0.0  0.5   0:02.46 systemd

第一行:系统运行信息

该输出和uptime的输出类似。

  • 10:06:56 系统启动时间
  • 2:38 系统运行时间
  • 1 user 当前系统用户数,这里只有pysnow530一个用户
  • load average: 0.00, 0.03, 0.05 三个数据分别是1分钟、5分钟、15分钟内的负载平均值

注意:这里的负载是指某段时间内待运行任务队列长度的平均值,也就是当前等待运行的任务数。如果当前任务队列数为8,说明有8个任务已处于就绪状态,就等着CPU的时间片了。该值越大,说明系统负载越高。假设系统有一个单核CPU,此值持续大于0.7则说明系统存在问题;为1时即为满负载工作,系统已不能正常处理多余的任务;若CPU大于1,好吧,它已经忙不过来了。此时可查看系统进程,有可能是某进程出问题了。以上是一个单核CPU的情况,如果系统有两个双核CPU,则满负载时值为2x2=4,其它情况可类推。

第二行:各状态任务个数

  • total 全部任务
  • running 运行中任务
  • sleeping 睡眠中任务
  • stopped 已停止任务
  • zombie 僵死任务

第三行:cpu执行各状态时间占比

  • us 没有修改nice的进程用户态耗时占比
  • sy 执行内核态耗时占比
  • ni 修改过nice的进程用户态占比
  • id 空闲时间占比
  • wa 等待IO操作耗时占比,我们的CPU已将任务队列的任务运行完了,就等着IO了
  • hi 提供硬件中断耗时占比
  • si 提供软件中断耗时占比
  • st hypervisor从当前虚拟机偷走的时间占比

注意:如果wa过高(超过30%),一方面可能是我们的CPU太强悍了,另一方面也有可能是磁盘IO存在瓶颈。此时可通过iostat -x确认具体情况。

第四行和第五行:内存和交换区的使用信息

该输出和free命令的输出类似。

  • total 总容量大小
  • used 已使用容量大小
  • free 剩余容量大小
  • buffers 缓冲区
  • cached Mem 缓存内存,当读写文件时,系统为了提高读写性能,会将文件缓存到缓存内存。

剩余行:每个进程的信息

  • PID 进程id
  • USER 所属用户
  • PR 优先级
  • NI nice值
  • VIRT 虚拟内存
  • RES 资源内存
  • SHR 共享内存
  • S 进程状态,D表示不可中断的睡眠,R表示正在运行,S表示正在睡眠,T表示被跟踪或已停止,Z表示僵死进程,跟各状态任务个数对应
  • %CPU CPU使用率
  • %MEM 内存使用率
  • TIME+ 运行时间
  • COMMAND 进程对应命令

Python中的装饰器

本文主要讲解python中decorator的历史,然后说明decorator在python中的实现,以帮助初识decorator的pythoner能够灵活运用decorator。

装饰器模式

装饰器模式来自GoF的23种设计模式。

在设计模式中,装饰器模式的意图描述如下:

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

即将附加的职责动态添加到一个对象。在扩展功能时,它比子类继承的方式要来得灵活。它是面向切面编程的一种手段。

python中的装饰器

提出

在python中,decorator最早是在PEP 318 – Decorators for Functions and Methods被提出的。

具体实现

decorator在python中形如@decorator,如:

@log
def foo():
    ...

我们将通过观察字节码,看一看添加decorator在字节码层带来了哪些变化。

首先来看函数定义产生的字节码:

def foo():
    pass

0 LOAD_CONST               0 (<code object foo at 0xb72508d8, file "<stdin>", line 1>)
3 MAKE_FUNCTION            0
6 STORE_NAME               0 (foo)

foo函数的定义对应三条字节码。

LOAD_CONST指令将pass对应的代码对象压入堆栈。

MAKE_FUNCTION将代码对象弹出堆栈,并创建一个PyFunctionObject对象,然后将函数压入堆栈。

STORE_NAME将函数弹出栈,并将它绑定到foo变量。

此时,foo变量即是一个函数。

然后我们看一下加了装饰器的函数定义对应的字节码:

@log
def foo():
    pass

 0 LOAD_NAME                0 (log) <---------- 1
 3 LOAD_CONST               0 (<code object foo at 0xb720e8d8, file "<stdin>", line 1>)
 6 MAKE_FUNCTION            0
 9 CALL_FUNCTION            1       < --------- 2
12 STORE_NAME               1 (foo)

foo函数添加一个装饰器时,对应的字节码多个两条。上面使用1、2标出。

LOAD_NAME命令是新增命令,将log函数(装饰器函数)压入堆栈。

LOAD_CONST命令将foo下的代码对象压入栈项。

MAKE_FUNCTION命令将代码对象弹出栈,并创建一个PyFunctionObject对象,并压入堆栈。

CALL_FUNCTION命令是新增命令,调用最开始压入堆栈的装饰器函数,然后将函数的返回值压入堆栈。

STORE_NAME将栈项装饰器函数的返回值绑定到foo变量。

也就是说,装饰器是一个函数,它将被装饰的函数作为输入,并将输出绑定到被装饰的函数名上。而装饰器的使用本质上就是一个语法糖。

煮个粟子

知道了它的内部实现,我们就可以动手了。

例如,实现一个打印函数耗时的装饰器:

import time

def log(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        waste_time = end - start
        print 'waste time:', waste_time
    return wrapper

@log
def hello():
    print 'hello, world'

hello()
hello, world
waste time: 4.29153442383e-05