美微书签收藏的网页

美微书签和网页 http://miaoo.in/ 的作者无关,不对其内容负责。美微书签快照谨为网络故障时之索引,不代表被收藏的网页即时页面。
Miaoo 随笔杂记

Miaoo 随笔杂记

人的一切痛苦,本质上都是对自己的无能的愤怒

λ-演算与编码

历史

λ-演算(λ-calculus)最早由阿隆索.邱奇(Alonzo Church)提出,被看作是一套用于研究函数定义、函数应用和递归的形式系统,用来证明可确定性问题。说到邱奇,他正是大名鼎鼎的阿兰.图灵(Alan Turning)的博士生导师。而邱奇的λ-演算与图灵的图灵机被证明具有等价的计算能力。 John McCarthy 在1960年以λ-演算为基础开发了 Lisp 语言,进而使得λ-演算成为函数式编程乃至计算机程序语言的理论基础。

本文不会给出诸如 β-归约 或是 α-变换 等一些概念的形式化定义,而是会将其思想融入到具体编码(encoding)或者运算的过程中。

基本元素

λ-演算主要由以下几个元素组成:

  • 变量, 例如, v, foo;
  • 匿名函数, 例如, λx, 表示一个参数为 x 的匿名函数.
  • 函数的应用(application), 例如 λx.x+1, 表示一个参数为 x 的匿名函数,其函数体为 x+1.

如果我们写成 BNF 的形式,一条λ-演算表达式可以写作:

<exp>  := <var>
       |  (<exp> <exp>)
       |  (λ<var>.<exp>)

此外,在λ-演算中,一切皆是函数,无论是数字还是普通变量其实都是一个个的函数,正所谓函数即变量,变量即函数,因此我们很自然的能够将函数作为参数进行传递(如同我们在计算机程序语言中传递变量一样)。接下来我们会看到,通过以上几条简单的语法元素,是如何构成λ-演算强大的表达能力的。

数字编码

一种常见的自然数的递归定义如下:

  • 某一个自然数的后继

所以,我们只要能形式化的表述『零』以及『后继』这两个概念,就能对所有自然数进行定义。对『零』和『后继』的编码方式有很多,这里我们介绍一种称为邱奇数(church numberal)的编码方式。

在邱奇数编码的定义中,将数看做是一个函数 ƒ 对变量 x 的作用,例如数字 0 表示函数 ƒ 对 x 进行了 0 次作用,数字 n 表示函数 ƒ 对变量 x 进行了 n 次作用。由此,写成λ-演算表达式的形式:

0 := λƒ.λx.x
1 := λƒ.λx.(f x)
2 := λƒ.λx.f (f x)
3 := λƒ.λx.f (f (f x))
...

以 0 为例讲解一下λ-演算的作用过程,由于λ-演算约定一个匿名函数只能有一个参数,而由上文邱奇数对数的约定可以看出,要表达一个自然数,我们需要两个参数:函数 ƒ 以及变量 x 。由上面的式子可以看出,我们在外层定义了一个匿名函数其参数为 ƒ (即 λƒ),与此同时该匿名函数的返回值又是另一个匿名函数(λx),这个内层的匿名函数的参数为 x,返回值为 x 本身。由此我们实现了接收 ƒ 和 x 两个参数的目的;而这种过程有一个专门的名字进行描述,称为 柯里化(currying)。

还记得我们在文章开头所说:『函数即变量,变量即函数』么,这里我们把函数 ƒ 作为匿名函数参数进行传递,在函数式语言中,这称为高阶函数(high order function)。

总结起来,任意自然数 n 是一个有两个参数的匿名函数(参数分别为 ƒ 和 x)。为了便于更直观的理解上述函数,我们可以用可读性更高的程序语言来表示相同的过程:

def zero(f,x):
    return x

def one(f,x):
    return f(x)

def two(f,x):
    return f(f(x))

...

我们再来看看如何对『后继』进行编码。在自然数中,任意自然数 n 的后继即为 n+1。回到邱奇数的定义,一个数代表函数 ƒ 对变量 x 的作用,对任意数字 n ,表示函数 ƒ 对变量 x 进行了 n 次作用,那么,自然数 n+1 表达了什么呢?一方面我们很容易想到 n+1 是 ƒ 对 x 的 n+1 次作用,另一方面,n+1 也表示对数字 n 再进行一次 ƒ 的作用(apply),换句话说,就是将函数 ƒ 作用于 n 。

有了上面的的描述,我们很容易将『后继』表示如下:

successor := λn.λf.λx.f (n f x)

可见,『后继』是一个有三个参数的匿名函数(参数分别为n, f, x),它的返回值为 ƒ 对数 n 的一次作用,即为 n 的后继(想一想,在『后继』的表达式中,是如何表达数字 n 的)。

由此,我们完成了对整个自然数的定义,在这之上,我们就能进一步定义更多的操作,比如对于加法,我们输入任意两个自然数 m 和 n,希望得到 m+n:

add := λn.λm.m successor n

布尔值编码

对于布尔值的变量,只有在条件判断语句中(如 if),才能体现出其值(true or false)的意义,所以我们先来定义 if 表达式。if 表达式通常有如下的形式:

if condition:
   true-exp
else:
   false-exp

可以看出,if 表达式有三个输入参数:condition, true-exp, false-exp, 通过对条件变量 condition 进行判断,分别选择 true-exp 或是 false-exp 作为返回值。

根据上面的定义,我们可以由此得出 if 表达式对应的形式化定义:

if := λv.λt.λf.v t f

用自然语言来解释,就是 if 作为一个函数,输入三个参数:v, t, f, 其返回值为函数 v 作用于 t f 之上的结果(即t 和 f 作为 v 的两个参数输入到 v 中)。

很容易我们就能将 t 对应于之前提到的 true-exp,f 对应于 false-exp,v 则对应 condition 。显然,v 应该是一个布尔值函数,即 true 函数 或 false 函数,我们将 t 和 f 作为 v 的两个参数输入,如果 v 为 true 函数,其将第一个参数(即 t)作为返回值,若 v 为 false 函数,则输出第二个参数(即 f)。由此我们得到 true 和 false 的定义:

true := λx.λy.x
false := λx.λy.y

以上就是λ-演算中对于 true 和 false 的定义。此外,细心的读者可能会发现 false 的定义和 0 的定义实质上是相同的,所以在程序语言中 0 和 false 都代表『假』值是一种合理的设计。

此外,我们还可以进一步定义一些常见的逻辑函数:

and := λx.λy.x y false
or  := λx.λy.x true y
not := λx.x false true

请读者自行思考、推导、验证上述表达式的正确性。

序对编码

熟悉 Lisp 以及其方言(如 scheme )的读者对序对(pair)应该不陌生,简单的说,我们称 M 和 N 的有序集合为一个序对,记为<M,N>。对于序对,我们有三个操作:创建序对(mkpair),提取第一个元素(fst),提取第二个元素(snd)。例如:

fst(mkpair M N) = M
snd(mkpair M N) = N

序对类型的本质,是由 mkpair 构造,并且能根据选择 fst 或是 snd 返回序对的首或尾元素。说到根据某个条件选择第一个元素或是第二个元素,这不正是我们上文所介绍的布尔值编码所对应的过程吗?如果把构造序对(mkpair)看做是构造一个 if 表达式,而选择第一个还是第二个元素作为 condition 用于判断,若 condition 为 fst(对应布尔值的 true),返回第一个元素,若为 snd (对应 false),则返回第二个元素,我们就能将对序对的编码完全对应到对布尔值的编码之上,

mkpair := λx.λy.λs.s x y
fst := λp.p true
snd := λp.p false

荐书:政治的历史与边界

牛津通识读本是牛津大学出版社邀请各个领域著名的学者,用大概100页左右的篇幅来介绍自己所研究领域的内容,以达到科普和启蒙的目的,内容涉及到了政治学、心理学、设计、金融等等。而本文所推荐的这本书是牛津通识读本中关于政治学的分册。作者通过简明的言语描绘了政治学的起源、现状以及与我们每一个人的联系。

作者一来就旗帜鲜明的指出:『政治中没有专制者的位置』,也就是说:政治(这里粗略可以指自由与民主政治)与专制是两个相互对立的概念。

在作者的笔下的专制,是秩序靠武力来建立,以恐怖来维系,朝令夕改反复无常的一种体制,在其中,有关秩序的最高准则取决于君主个人的好恶,所以一方面民众期待明君,企图依靠君主的智慧来主持正义,如历史上大卫、所罗门这样的伟大君王,另一方面根人们将深蒂固的社会习俗作为普遍认可的正义观念,进而成为专制社会中自然法则的一部分。

作为古希腊文化的继承者,反对专制一度是欧洲政治传统的重要组成部分。古希腊人犹如武士,他们一方面欣赏波斯、远东文明绚丽的文化,另一方面却又鄙夷东方臣民与君主间诸如『匍匐下跪』这样如同奴隶与主人似的关系,特别是在基督教中,『匍匐下跪』是用来描述人与神之间的距离的一种意象。而两千多年后,欧洲人完全继承了古希腊人对专制习惯性的反感。所以在欧洲,追求专制权力的人通常必须把自己伪装起来。但是,欧洲人有时也会被某种以诱人的理想主义面目出现的专制体制所蒙蔽-希特勒就使用了这种手法。这提醒我们,即便是在对专制有着如此抵抗力的欧洲,专制主义在时间和空间上离我们并不遥远。

专制体制长期统治的结果,便是在一个国度里专制强权的反复无常会引发一种回应,富有思想的臣民转向神秘主义或者其他消极隐退的信仰。他们在超越感官的精神世界里找到了生活的真谛,而社会和政治生活则被贬低为一种幻象。这种趋势的后果,即使有极为短暂的繁荣,但大多数时期是科学技术发展的长期滞后,例如中国的明清时代。

『在私人生活中我们自由而宽容;但在公共事务中我们严守法规』,在谈论到如何区别政治与专制,一个很重要的线索就是是否保持私人生活与公众领域之间的界限。私人领域指的是家庭生活以及个人良知的领域-一个人良知即个人凭自己的意愿选择信仰和兴趣。在民主自由政治体制中,公共权力受到国会、反对党、媒体等等的制约与制衡,从而保证权力不会随意地侵犯到个人的私人生活;而在公共事务中,政府会以严格的法律来维持社会的秩序。由此衍生来看,诸如『个人的一切都属于政治』这样伪装成普世真理的口号都是值得警惕,在潜意识中,这样的用词会模糊我们对公共和个体的判断标准。

作者在开篇的结尾部分中说,『许多学术和艺术作品都是政治的镜子,只有把那些意象整合起来,形成一个完整的认识体系,我们才能把握它们的真谛』,这或许从一个方面道出了政治学之于你我的意义与价值。

浅谈 Unix I/O 模型

在实际应用中,数据操作通常分为输入和输出,那么以输入为例,在操作系统中,一个数据的输入通常分为以下两个过程:

a. 等待数据准备好.
b. 将准备好的数据从内核拷贝到用户空间.

下面我们将会分别讨论 I/O 模型中的两个大类,即 同步 I/O 与 异步 I/O。

1. 同步 I/O

同步与异步 I/O 的最大不同,就是在在进行数据复制时(即过程 b ),所有的同步 I/O 模型均会发生阻塞。进一步来看,又可以根据在等待数据准备好时( 过程 a )是否发生阻塞,将同步 I/O 分为:

  • 阻塞 I/O ( blocking I/O ). 其在过程 a 与 过程 b 中均会阻塞。
  • 非阻塞 I/O ( non-blocking I/O ).  过程 a 不阻塞, 过程 b 阻塞。在过程 a 阶段若无数据准备好,则内核立即返回 EWOULDBLOCK 错误 (通过设置 errno),用户进程立即返回因而不会发生阻塞。此时,用户进程可以不断的通过轮询( polling )的方式查询数据是否准备好。一旦数据准备就绪, 进程便会进入阻塞模式(即阻塞于过程 b ),进行数据的拷贝直至完成。
  • I/O 复用( I/O multiplexing, event-driven ).  I/O 复用有时又被称为 事件驱动 I/O, 它的最大优势在于,我们可以将感兴趣的多个I/O事件(更精确的说,应该是 I/O 所对应的文件描述符)注册到 select/poll/epoll/kqueue 之中某一个系统调用上(很多时候,这些系统调用又被称为多路复用器。假设此时我们选择了 select() )。此后,调用进程会阻塞在 select() 系统调用之上(而不是阻塞在真正的 I/O 系统调用(如 read(), write() 等)上)。select() 会负责监视所有已注册的 I/O 事件,一旦有任意一个事件的数据准备好,那么 select() 会立即返回,此时我们的用户进程便能够进行数据的复制操作(过程 b )。总而言之,I/O 复用的优点就在于可以同时等待多个I/O事件;而缺点是会进行两次系统调用(一次 select(), 一次 read() )。

通过上面的讨论可以清楚的看到,同步 I/O 总会有阻塞的过程,这就是“同步”最本质的特征。

2.异步 I/O

如前文所说,异步 I/O 的最大特点在于在过程 a 和过程 b 中, 用户进程均不阻塞。 用户进程告知内核启动某一 I/O 操作, 并让内核全权代为执行(包括等待数据及拷贝数据至用户空间),此后用户进程可以立即执行其它的任何操作。等到所有 I/O 过程执行完成后, 内核会通知用户程。由此可见,在整个过程中,用户进程均不阻塞。

以上谈到了 Unix 系统当中 I/O 操作的具体执行方式。在此基础之上, 我们介绍两种常见的 I/O 事件处理模型。事件处理模型的意义在于我们从更宏观的角度来看待实际应用中如何来处理 I/O 事件。我们还是以一个读操作( read() )为例, 根据 I/O 操作是基于同步或异步,分别介绍如下两种模型:

  • Reactor 模式( event loop ) . 基于同步 I/O
  1. 将要读的文件描述符注册到多路复用器中(如 select(), poll(), epoll() )。
  2. 调用进程阻塞在多路复用器上,其等待已注册的任意一个 I/O 事件发生。
  3.  一旦事件发生(即文件描述符变为可读, 多路复用器返回), 将调用用户提供的事件处理函数( event handler)进行实际的 I/O 复制操作。
  4. 读取完成后,事件处理函数可以对数据进行进一步的处理。
  • Proactor 模式. 基于异步 I/O
  1. 用户进程启动一个异步读文件操作, 同时将该操作注册到多路复用器上。多路复用器并不关心文件是否可读,而只关心该异步操作是否完成。
  2. 整个异步读文件操作由内核完成,用户进程不需要关心。多路复用器阻塞以等待某个完成通知的到达。
  3. 当内核完成了整个读文件操作 – 即数据已经准备好,并已由内核复制到了用户事先提供的缓冲区后 – 通知多路复用器读操作已完成。
  4. 多路复用器再调用相应的事件处理函数( event handler )处理位于用户缓冲区的数据。

由此我们可以看到 Reactor 与 Proactor 最大的区别在于数据准备好后,是由谁来将这些数据复制到用户空间中的缓存区之中。Reactor 由于调用的是同步 I/O , 所以当多路复用器由于数据准备好而返回之后,将会由用户的事件处理函数自行将数据复制到用户缓冲区中, 而 Proactor 由于调用的是异步 I/O ,  因此等待及复制数据均由内核完成,用户进程只需要等待内核的完成通知,此后由事件处理函数对已在缓冲区中的数据进行进一步的处理。

由于 Proactor 模型需要操作系统提供异步 I/O 的支持,要求较高,故基于上面所描述的思想, 我们可以用 Reactor 模型来模拟 Proactor (只需要用户额外提供用户缓冲区来存放读取出的数据)。简单来说,就是由多路复用器来代替用户进程来完成实际的 I/O 复制操作。

在 Reactor 模型的第 3 步中,当某个文件描述符可读使得多路复用器返回之后,多路复用器执行一个非阻塞读操作, 将数据从内核读至用户提供的缓冲区中,此操作完成后通知(调用)对应的事件处理函数,告知其 I/O 操作已完成。 这样一来,我们就可以在各种系统中(无论支持异步 I/O 与否)均对外提供统一的 Proactor 模型接口,而对用户隐藏其后的具体实现细节。

 

参考资料

1. < Unix network programming >, Chapter 6.

2. Comparing Two High-Performance I/O Design Pattern

Don’t panic!

生日的时候特别适合抛去生活的琐碎,谈点理想,反思内心。

关于金钱

“You should regard money as fuel for what you really want to do, not as a goal in and of itself. Money is like gas in the car—you need to pay attention or you’ll end up on the side of the road—but a well-lived life is not a tour of gas stations.” – O’Reilly

虽然发生了种种,但是还是很感谢我的父母,为我提供了一个相对无忧的环境,让我能不必为生计所困而做些自己喜欢和热爱的事。以至自己能够站在一个相对清醒的角度来思考金钱的意义。我很赞同上面引用的那段话,金钱之于人生犹如燃料之于汽车,人生的“汽车”不能没有“燃料”的助力,但人生的终点绝不能只是“加油站”。相对幸运的是,IT产业算是一个能够获取足够多“燃料”的行业,所以我期望的是通过我不断的努力获得乐趣与满足感,同时能获得财务的自由,以至能够做些超越金钱层面而更有意义的事儿。

说到“有意义”,最近一直在看苹果联合创始人沃兹尼克(Steve Wozniak)的自传<iWoz>–沃兹尼克在谈到当年设计并制造个人计算机的意义时说:计算机作为一种人性的需求–是一种实现社会平等的工具( computers as a benefit to humanity — a tool that would lead to social justice. )。我很认同这种将所做之事与人性需求之间建立某种联系的理念,一方面以满足人性的需求是实现价值最大化的最好途径,另一方面当面对诸如各种商业化挑战、道德困境的时候,坚守人性为上就显得尤为可贵。我认为这是作为一个工程师所能具有的最高价值理想。

此外,沃兹尼克也承认,自己讨厌社交讨厌一切非技术的琐事,所以没有乔布斯,也不会有后来的苹果公司。好在沃兹尼克是幸运的,他能遇到乔布斯这个商业奇才,使得自己杰出的技术才华能够触及世界的每个角落,开启个人电脑的时代,也使得自己能够金钱无忧的继续做一个快乐的工程师。但是,不是每个技术天才都能有如此杰出的商业搭档,能够有机会见证自己的才华改变世界。所以,认识、接受、善于利用商业的规则,使得价值与影响力最大化是不可或缺的。找到属于你的“乔布斯”,成为你自己的“乔布斯”,这是我所学到的。

关于专注

反思我之前几年的学习历程,感觉所做事不少,但是收获不够多。原因之一就是不够专注。具体来说,面对计算机领域的种种,我都是乐于尝试而疏于精通,我写过桌面应用,写过网站,  做过些许浅显研究(人脸建模,自然语言处理)。获得的乐趣不少,但是要让我有所作为缺还欠火候。

所以,当领略了沿途各种风景,我应该选择一条最心仪的道路走下去。专注才能带来力量。

在研究领域,我目前主要关注于低分辨率条件下人脸识别以及相关领域的研究。就研究意义上来说,人脸识别应用的广泛就不必多言,而在真实世界的应用中,各种由不可控环境所造成的待识别人脸分辨率较低是个非常现实和棘手的问题。所以我感觉这方面的研究是有趣而有价值的。当然我不确定在这个过程中我能产生怎样的成果,毕竟做研究就是这样,充满了对未知的探索以及其所带来的不确定性,全力以赴,力求有所创造。

在开发领域,我会将主要的精力集中在与网络、存储相关的开发。之所以关注这个领域,一是这个领域是目前开源界成果较多、较为活跃的领域,有不少活跃的开源项目能够供我学习(RedisNginx…,我目前正在读Redis的代码),通过学习、参与顶尖的开源项目,能够与世界上最优秀的开发者对话、交流、学习,这是作为学习者最大的幸事;其次,在互联网的时代,网络以及存储领域是支撑互联网各种应用的根基,并且存在着大量新应用与前所未有挑战,深度参与其中,能够在未来很长一段时间内跟随时代发展的趋势。

关于焦虑

之所以把不再焦虑(Don’t panic)作为标题,是因为这是我这一年多来所一直面对与抗争的“敌人”。当面临年龄的增长,选择的多样,患得患失的心态就自然而然的出现了。后来我意识到,在漫长的人生道路之中,目前的困境与忧虑是微不足道的,人生本来就具有多样性,只要有改变与选择的勇气,人有足够多的时间与机会去尝试各种可能性(各种案例就不再表)。所以,首先,面对各种选择,我想应该忠于兴趣、忠于内心,选择那些能越走越宽广的道路。其次,我告诫,自己现在的决定就是最好的决定,当根据目前所有能获得信息与经验做好了选择,那么就坚定的走下去,总会有收获,总会有所作为。

 

最后,感谢各位的祝福。

生日快乐,Miaoever!

 

2012.11.18

@home

getElementsByTagName() 与 querySelectorAll() 的比较

1.定义

getElementsByTagName() 返回一个动态( live )的 NodeList 对象,所有对文档( document )对象的操作都会实时的反应到这个动态 NodeList 上。

querySelectorAll()返会一个静态( static )的 NodeList 。对文档( document )对象的操作不会反馈到该 NodeList 上。querySelectorAll() 的作用如同返回对文档( document )某一时刻状态的快照。

2.性能差异

javascript test 1

由上图可以看到,创建动态( live ) NodeList 对象的速度比创建静态对性的速度快了90%。因为创建动态对象只需要在缓存中注册一个引用,而无需再做其它任何的操作。而静态对象,必须查询整个文档( document )对象以用于创建。因此后者需要更多的开销。

alert(document.getElementsByTagName('div')===document.getElementsByTagName('div')); //true
alert(document.querySelectorAll('div')===document.querySelectorAll('div'));//false

true意味着他们拿到的同是cache引用。false表明每次返回都是不一样的Object。

当需要访问NodeList对象中节点属性时,结果却恰恰相反:动态对象的性能比静态对象差很多。因为动态对象必须查询整个文档对象的变化。测试结果如下:

javascript test 2
若综合考虑两个因素(创建速度与访问速度)进行测试,结果如下:
javascript test 3

可见,getElementsByTagName() 的综合速度仍略优于 querySelectorAll() 。

3.结论

在创建NodeList对象时,getElementsByTagName() 的速度远远超过 querySelectorAll() . 而在创建完成之后,需要访问具体节点及其属性的时候,后者的性能则要优于前者。因此我们需要根据具体的场景选取合适的方法,如果只是需要按tag取得所有的元素,那么应该选择 getElementsByTagName() 。如果我们需要获取结果的快照,并且进一步需要更多复杂的CSS查询,那么就应该选择 querySelectorAll() 。

参考

1.http://www.nczonline.net/blog/2010/09/28/why-is-getelementsbytagname-faster-that-queryselectorall/
2.http://jsperf.com/getelementsbytagname-a-0-vs-queryselector-a
3.http://jsperf.com/nodelist-vs-array-iteration

Nodejs 搭建 Blog

前段时间无意中接触到Nodejs,感觉非常有趣。Node.js是什么呢?按照官方的介绍:

Node.js is a platform built on Chrome’s JavaScript
runtime for easily building fast,

scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

Nodejs的两大特点是事件驱动(Event-driven)以及非阻塞式I/O(non-blocking I/O),从而能够实现高并发。 如果熟悉Windows编程的话,应该对事件驱动并不不陌生,因为一个典型的Windows程序就是由各种事件以及消息循环所构成。而非阻塞I/O在*nix下则对应于select() or epoll()。对于我这样的web programming 新手,在web开发上使用这些概念却显得很诱人,再加上由于Nodejs底层由Google V8引擎驱动,上层使用javascript进行开发,使得后端(back-end)与前端(front-end)代码得到了统一,初次写下来,非常流畅,没有任何语言切换的烦恼。

于是,大概用了3、4天时间,参照了若干文档(见附录)写了个Blog程序,web server当然使用了Nodejs,同时使用了express作为framework,数据库使用mongoDB(使用mongoose进行连接),此外Nodejs默认模板引擎为jade,由于不太习惯jade的书写规则,所以换成了ejs。由于只是个简单的Blog程序,代码上没有太多可以分享的内容,我把代码放在了Github上,有兴趣的同学可以参考。

下面简要记录下相关环境的配置方法(以Ubuntu为例,其它可参加官方文档

1.Nodejs安装

sudo apt-get install python-software-properties
sudo apt-add-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs npm

2.MongoDB安装

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10

编辑 /etc/apt/sources.list,添加下面一行作为新源:

deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen

刷新源:

apt-get update

安装:

apt-get install mongodb-10gen

3.Node.js相关组件安装

npm install ejs express mongoose

以上环境搭建好后,就可以运行Node.js所写的程序了。

代码地址https://github.com/miaoever/NodeBlog

Demohttp://node.miaoo.in

一些我这几天来看过并且感觉不错的文档:

1.Node.js beginner

2.Node.js doc / express doc

3.Understanding node.js

3.Stackoverflow上关于Nginx+Node.js部署的问答

用PHP获取优酷、土豆视频缩略图

最近在用php做个project,需要获取优酷、土豆视频的缩略图。由于这两个网站均没有提供官方的获取视频缩略图的方法,粗略的想了一下,大概有两种变通的提取方式:一种是用ffmpeg直接读取上述网站的某个视频,然后截取其中的某帧作为该视频的缩略图;另外一种则是想办法读取网站自身提供的视频缩略图。为了能够快速的实现功能,我暂时选择了后一种方法。

1.探索

以优酷为例,随便点开一个视频播放页面,比如这里,网页上并没有直接提供该视频的缩略图链接,但是我无意中点击了页面上“转发至XX微博”后,发现跳转后的页面中竟然包含了先前视频的缩略图,于是回过头去看视频播放页源代码,在“转发至XX微博”字样的附近有一个跳转链接,其中包含了如下字样:

pic=http://g1.ykimg.com/xxxxxxxxxxxxxxxxxxx

这个链接就是我们所需要获取的缩略图url。

2.获取

<?php

	//使用curl获取尾页内容
	$curl = curl_init();
	$url = "视频链接地址";
	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
	$data = curl_exec($curl);

	//使用正则表达式提取缩略图链接
	preg_match( '/id="s_sina2".*?pic=(.*?)".?target=/', $data , $result );
	echo '<a target="_blank" href="'.$url.'"> <img src="'. $result[1].'" /></a>';
	curl_close($curl);

?>

3.其它
土豆网的在页面中某个js脚本代码里包含有缩略图链接,所以可以使用正则表达式直接提取,代码如下:

<?php

	$curl = curl_init();
	&url="视频链接地址";
	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
	$data = curl_exec($curl);
	preg_match( "/thumbnail = pic = '(.*?)'/", $data , $result );
	echo '<a target="_blank" href="'.$url.'"> <img src="'. $result[1].'" /></a>';

?>

4.备注
上述方法虽然简便,但是一旦网站更改网页结构,那么上述的代码也必须进行相应的修改,所以适应性较差。完整的代码请参看这里(Github),顺便提供一个缩略图提取的测试页面,请点击这里

Linux 笔记拾遗

整理了一部分Google note上的笔记在此。

  • 用Mutt发送邮件

使用Mut和msmpt能够快速的在命令行下发送邮件及附件,比如我目前使用的脚本格式如下:

mail “someting about linux” ”homemiaoonote.txt”

这样就能将名为“note.txt”的文件作为附件并以“someting about linux”为主题发送到我事先指定好的邮箱。平时我使用这个命令将一些重要的文件备份到我的邮箱,此外,我还用这个脚本将我的想要看的书籍发送到amazon提供的Kindle邮箱,以便自动推送到我的Kindle上。

1.安装mutt 及 msmtp

sudo apt-get install mutt msmtp

2.配置msmtp

打开配置文件:

vim .msmtprc

配置如下:

account default
host smtp.gmail.com
from 你的邮箱地址(eg:miaoo@gmail.com)
auth on
port 587
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
user 你的邮箱用户名(eg:miaoo@gmail.com)
password 你的邮箱密码
logfile ~/.msmtp.log

3.配置mutt

打开配置文件:

vim .muttrc

配置如下:

set sendmail=”/usr/bin/msmtp”
set use_from=yes
set realname=”Leo.Miao”
set from=你的邮箱地址(eg:miaoo@gmail.com)
set envelope_from=yes

4.发送脚本

新建一个脚本文件(vim mail.sh),并输入:

echo “这里输入默认的邮件内容”  | mutt -s “$1″ “你的邮箱地址” -a “$2″

其中-a 表示附件的路径,-s 表示邮件主题。保存退出,并添加执行权限,你可以将其复制到/usr/bin下,这样你在任何路径下都能使用该脚本了。

  • CentOS下安装GCC及G++

我的VPS使用的是CentOS 5.5 ,由于需要编译各种软件,所以需要安装GCC及G++。

安装GCC 和 G++

yum -y install gcc gcc-c++

这时可能提示 :

Missing Dependency: kernel-headers is needed by package glibc-headers-2.5-58.el5_6.4.i386 (updates)

说明需要升级内核。

编辑:

vi  /etc/yum.conf

去除Exclude 中的kernel* 字段并保存。再次执行:

yum -y install gcc gcc-c++

程序会自动升级内核至所需的版本。升级完成后,再最后恢复 kernel* 字段。

  • CentOS下升级Python

当你正常下载并且编译安装好新版本的Python后,会发现yum命令无法使用了。这是因为yum依赖于原有的Python 2.4版本的原因。编辑:

vim /usr/bin/yum

讲第一行修改为:

!/usr/bin/python2.4

保存退出。

  • Vim 的复制粘贴

复制:buffer内按v然后按y,系统剪贴板则按v(或V)然后按”+y

v:基于字符, 按方向键选择,然后按 “+y
V:基于行, 按 “+y

粘贴:按p 或P

p: 插在当前字符后面
P: 插在当前字符前面

更详细的介绍,可以参看这里

  • Nginx平滑重启

在更改Nginx配置后,平滑重启能够迅速的使得新配置得以生效,输入:

/usr/local/nginx/sbin/nginx -s reload

写在密码泄露后

近日网络上接连泄露出CSDN、多玩网、人人网包含大量注册用户名、邮箱及密码的数据库文件。我分别在以上三个泄露的数据库中查找我的注册信息,在前两个库中均能找到我的注册名及密码,其中,CSDN为密码明文!多玩网为密码MD5值。好在,我注册这两个网站时均使用的是最低安全级别的密码,加之我很少使用这两个网站的服务,所以,即使密码泄露乃至帐号被盗也不会造成太大影响。然而,对于这次密码泄露事件,我有以下的若干的思考:

1.为什么密码会泄露?

理论上,只要网站不是使用HTTPS协议进行POST提交密码等敏感登录信息,那么就有很大的被截获的风险。例如人人网主页提交登录时,既没有使用HTTPS进行提交,而且在数据包中直接使用明文提交用户名和密码,这样的后果是使用sniffer类软件可以直接截获这些敏感信息。

其次,如果安全措施得当,即使网站数据库被黑客拿到,那么面对大量复杂加密后的密码,直接计算获得大量密码的成本投入回报率是相当低的(除非黑客有特定的目标)。而从上述被泄露的数据库来看,基本是以明文存放密码数据,这样对于用户的损失是相当大的。

所以我认为,造成密码大量泄露主要的原因,首先是程序员缺乏基本的安全常识或者说对安全问题不够重视,在编码的时候没有考虑在各个薄弱环节尽可能的保护用户的敏感信息免于泄露。其次,国内许多互联网服务商表面上标榜用户为上帝,实际上基本不重视用户基本信息的安全,没有相应的安全机制对服务各个可能泄露用户信息的环节进行监测,且数据库文件轻易的被黑客盗取。当然,用户安全意识缺失也是很重要的原因。

2.为什么密码明文存放?

有人说是因为审查的需要,所以国内各大网站被迫存储用户的明文密码,以便监管部门随时提取用户的信息。我对这样的观点相当的不赞同,在国内流动或者存储的数据,对于监管部门以及内容存储商来说,本身就是明文可随时查看的,包括即时的QQ聊天记录、私人电子邮件等等,已经是公开的秘密了,更不必说论坛、微博客这种本身属于开放性的内容平台。所以,审查监管部门完全没有必要要求国内网站存储提供用户的密码信息。

所以,想来想去,问题还是出在网站所有者本身。

3.用户应该怎么办?

首先,不同的网络服务一定不要使用相同的密码,否则黑客一旦获得某个服务的密码,那么你所有的帐号就都有被盗的风险。比如此事件中,有大量的QQ邮箱和网站密码泄露出,如果邮箱和网站使用的同一密码的话,那么后果可想而知。

其次,对于国内的网络服务,尽量假设其为不可靠的,所以不要在其中存储重要的信息或内容。

此外,不要轻易的暴露自己的邮箱地址,且在注册非重要服务时,尽量使用不重要的邮箱进行注册。由于目前越来越多的网站使用邮箱地址作为用户识别的标志,所以,对邮箱地址的保护也是很有必要的。在各种中文社区,很容易就能看到大量的用户随意张贴自己的邮箱地址,这是非常不安全的。

再者对密码本身的问题,下面摘录的是某在线解密网站解密范围的介绍:

收录内容 说明 数量
1-6位大小写字母+数字+特殊字符 收录100% 大于 1400亿
7位小写字母+数字 收录100% 大于  783亿
8位小写字母 收录100% 大于 2082亿
8位小写字母+数字 已收录50%,正在添加 大于14000亿
8-11位数字 收录100% 大于  1000亿
1-15位其它数据 部分收录 大于28000亿
1-20位 900G独家超大字典 大于    910亿

可见,短密码以及纯数字或者纯字母密码,即使网站经过了简单的加密存放,在黑客面前基本相当于是明文的。因此,提高密码的复杂度,也有相当的必要。

基于上面的问题,我推荐使用类似LastPass这样的密码管理软件,这样只需要记住一个主管理密码,就能很容易的生成、管理其它各种不同的的密码。

另外,定期更换密码也能一定程度上降低安全风险。比如此次各大网站密码数据库外泄,据说这批数据在黑客圈中流传已久,只是近期才被圈外人所知。所以,不要等到密码泄露事件发生后再想着更改密码,而应该未雨绸缪。

当然,以上这些都是针对非特定的网络攻击、泄密的情况,如果真有黑客针对你的网络帐号进行盗取,那只有自求多福了。

Kindle 4 自定义屏保

荣耀属于我的朋友Nick,他贡献了本文的全部内容。我仅做了些整理。

原文地址:http://nickdotcat.info/?p=64

1) 自定义屏保

如果你是使用如<Kindle 4 更换中文字体>一文中所述的用USB线通过USBnet连接SSH登录的话,输入以下三条命令。

挂载驱动器:

mount /dev/mmcblk0p1 /mnt/base-mmc

将原Amazon屏保文件夹改名:

mv /mnt/base-mmc/opt/amazon/screen_saver/600x800 /mnt/base-mmc/opt/amazon/screen_saver/600x800_bak

添加到自定义屏保文件夹的链接:

ln -s /mnt/us/screensaver /mnt/base-mmc/opt/amazon/screen_saver/600x800

如果你是按<Kindle 4 通过Wifi进行SSH登录>提到的Wifi 方式登录SSH,输入下面的两条条命令。

将原Amazon屏保文件夹改名:

mv /opt/amazon/screen_saver/600x800 /opt/amazon/screen_saver/600x800_bak

添加到自定义屏保文件夹的链接:

ln -s /mnt/us/screensaver /opt/amazon/screen_saver/600x800

仅仅执行上面的代码,屏保时只能找到第一张屏保图片且每次进入屏保后并不会自动更换屏保图片。还需要修改此处:

mv /var/local/adunits /var/local/adunits_bak

重启后在普通模式下将Kindle连接至PC,在Kindle根目录新建一个名为screensaver的文件夹,将你喜欢的屏保放入这个文件夹中。重启Kindle屏保就会生效了。

另外,博主正在使用这个kindle皮套,手感不错,性价比非常高,推荐使用。