<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Chen Yufei's Blog]]></title>
  <link href="http://chenyufei.info/atom.xml" rel="self"/>
  <link href="http://chenyufei.info/"/>
  <updated>2012-01-31T11:49:05+08:00</updated>
  <id>http://chenyufei.info/</id>
  <author>
    <name><![CDATA[Chen Yufei]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[各种 Spinlock 实现的性能测试]]></title>
    <link href="http://chenyufei.info/blog/2012-01-28/spinlock-implementation-comparision/"/>
    <updated>2012-01-28T13:57:00+08:00</updated>
    <id>http://chenyufei.info/blog/2012-01-28/spinlock-implementation-comparision</id>
    <content type="html"><![CDATA[<p>之前自己写的程序遇到了伸缩性问题，怀疑是自己实现的锁在使用的核数增加时会产生严重的竞争，所以对这篇 <a href="http://locklessinc.com/articles/locks/">Spinlocks and Read-Write Locks</a> 里介绍的各种 spinlock 测试了下性能。(当然，根本的办法是避免在锁上竞争，比较 spinlock 实现的性能更多是出于好奇。)</p>

<p>各种 spinlock 的实现和测试程序都在 <a href="https://github.com/cyfdecyf/cyf-util-conf/tree/master/c/spinlock">github 上</a>。（除了cmpxchg 以外都是 copy 那篇文章里的。）为了使用方便，spinlock 所需要的原子指令等都在各自的头文件里。</p>

<p><strong>首先要说明的是，下面的测试结果以及结论依赖于具体的 CPU。选择 spinlock 的实现的时候还是要自己测试一下</strong>。</p>

<!-- more -->


<p>测试程序非常简单，多个线程做 counter++ 操作，保证总的 lock/unlock 操作数相等，以此来测试 lock/unlock 操作的开销，counter 只是用来验证 spinlock 实现的正确性。</p>

<p>在给出测试结果之前，先对各个 spinlock 的实现做简单的说明，[Spinlocks and Read-Write Lock][] 中有详细的说明。</p>

<ul>
<li><p>最简单的两个实现分别是用 <code>cmpxchg</code> 和 <code>xchg</code> 指令，其中 <code>cmpxchg</code> 需要加
<code>lock</code> prefix 保证原子性，而 <code>xchg</code> 指令执行的时候会自动加 <code>lock</code> (简化的描述，实际情况参考 Intel 的文档。)</p></li>
<li><p>Ticket spinlock 是 Linux 内核里使用的一种 spinlock，保证先到先得的顺序拿锁，保证公平性</p></li>
<li><p>MCS spinlock 设计的目的是防止竞争严重时产生大量的处理器总线开销而降低性能。</p>

<p>普通的 spinlock lock/unlock 使用的是同一个内存地址，多个 core 同时访问时需要由
cache coherence protocol 来保证每个 core 从各自的 cache 里看到的都是正确的数据。当竞争严重时会产生大量的 cache coherence message。</p>

<p>MCS spinlock 设计的关键是让每个等待拿锁的 core 在自己的等待变量上 spin，这个变量只会出现在自己 cache 中，从而减少了 cache coherence message。</p></li>
<li><p>K42 spinlock 解决了 MCS spinlock 的接口需要多传递一个等待变量的问题。简单来说它把 lock 函数桟上的内容作为等待变量，其实现与 MCS spinlock 非常相似。</p></li>
</ul>


<p>我在一台有 4 颗 Intel Xeon CPU, 10 cores/CPU（一共 40 核）运行 Debian 6 的服务器上运行了测试程序，测试方法如下：</p>

<ul>
<li>每种 spinlock 实现都测试使用 1, 2, 4, 8, 16, 32 个线程的执行时间，执行三遍取平均数</li>
<li>测试分成两组：

<ul>
<li>由 OS 控制线程在哪个核上执行</li>
<li>绑定线程到特定的 core 上，目标是使用尽可能少的 CPU。这么做是考虑到单个CPU 内部的 cache coherence message 开销比跨 CPU 的开销要小很多。</li>
</ul>
</li>
<li>只统计某一个线程的执行所有 lock/unlock 操作的时间

<ul>
<li>为了让所有的线程基本上同时开始执行，用了一个 ad hoc 的同步，这可能对统计整个程序执行时间造成影响</li>
<li>尽可能忽略程序启动和退出的时间的影响</li>
<li>统计单个线程的时间可能不太精确，但对于比较伸缩性来说应该足够</li>
</ul>
</li>
</ul>


<p>下面是运行时间的测试结果，标记 bind core 的为绑定核的测试结果。ticket spinlock
由于运行时间比较长，单独在一张图中给出。另外单独分析一个程序的数据比较容易看清问题，所以先分析 ticket spinlock 的运行数据。</p>

<p><img src="https://img.skitch.com/20120128-x463w65jk1p7itktenaqj25huw.jpg" alt="ticket spinlock" /></p>

<p><img src="https://img.skitch.com/20120128-rycmj2w1iegtxewaiep876tdrk.jpg" alt="ticket spinlock bind core experiment" /></p>

<ul>
<li><p>先看第一张图中绑核的结果（绿色），注意到绑定在单个 CPU 8 核到两个 CPU 16 核运行时间的明显上升，而从 2~8 个核时间变化不大。猜想 <strong>ticket spinlock 的伸缩性取决于竞争的 CPU 的数目</strong>。主要原因是跨 CPU 的cache coherence 开销远大于 CPU 内部的开销。从 16 核到 32 核的运行时间上升也体现了这一点。</p></li>
<li><p>为了验证上面的结论，进行了另外的测试：使用 10 个核，但采用不同的绑定方式。<code>9+1</code>
代表把 9 个线程绑定在一个 CPU 的 9 个核上，而第十个线程绑定在另一个 CPU 上；
<code>8+2</code>, <code>7+3</code> 以此类推。</p>

<p>从结果来看，的确是随着使用的 CPU 数量增加，运行时间增加。</p></li>
<li><p>再来看不绑核的情况。2 个线程的时间跟 <code>9+1</code> 的接近，而 4 线程及以上的时间几乎相同，都接近于 <code>7+3</code> 的时间。</p>

<p>结合 <code>htop</code> 来看执行测试时的 CPU 使用情况，<strong>默认情况下 Linux 会把线程放到不同的 CPU 上执行</strong>。这种方式可以避免线程/进程间的 cache 污染，同时也可以利用更多的
CPU cache，但是对这种 lock contention 严重的情况来说，这种策略不是最优的。</p></li>
</ul>


<p>分析完 ticket spinlock，再来看其他 spinlock 实现的运行时间情况：</p>

<p><img src="https://img.skitch.com/20120128-rhb7kk3mxd8ew36s1m49y5m1tp.jpg" alt="spinlock performance" /></p>

<p><img src="https://img.skitch.com/20120128-qrk9djnp2e6q52nc2xbi1xt6xf.jpg" alt="spinlock perforamnce bind core" /></p>

<p>从上面的两张图里可以看到：</p>

<ul>
<li><code>xchg</code> 实现的 spinlock 无论是否绑核，性能都很好，且没有严重的伸缩性问题</li>
<li><code>cmpxchg</code> 无论是在单个还是多个 CPU 之间竞争，随着核数的增多，性能会变得很差</li>
<li>MCS 和 K42 的伸缩性类似 ticket spinlock，也取决于竞争的 CPU 的数目。而且由于自身的复杂性，在使用的核数少的时候其性能相比 <code>cmpxchg</code> 也并没有优势。</li>
</ul>


<p>总的来说：</p>

<ul>
<li>实现 spinlock 还是用 <code>xchg</code> 最为高效且不用担心伸缩性的问题</li>
<li>尽量避免 <code>cmpxchg</code></li>
<li>如果不要求公平性，避免 ticket spinlock</li>
<li>MCS, K42 本身过于复杂，性能相比其他锁要差。这可能是它们没有被广泛使用的原因吧。也可能以前的 CPU cache coherence 实现的比较差，在核数多的情况下它们的性能更好</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[解决 Markdown 转 HTML 中文换行变空格的问题]]></title>
    <link href="http://chenyufei.info/blog/2011-12-23/fix-chinese-newline-becomes-space-in-browser-problem/"/>
    <updated>2011-12-23T20:20:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-12-23/fix-chinese-newline-becomes-space-in-browser-problem</id>
    <content type="html"><![CDATA[<p>在 Vim 里编辑文件时我设置行宽不超过 80 个字符，因此编辑 Markdown 文件时一段话里会有多个换行。我用 Octopress 默认配置的 rdiscount 来生成 HTML 文件，这些换行都会保留在最后生成的 HTML 文件中。</p>

<p>浏览器对换行的处理是转成空格，这对英文来说很自然，但对中文就很讨厌了。我在 Stack
Overflow 上提了这个问题，<a href="http://stackoverflow.com/a/8551033/306935">从这个详细的回答来看</a>，这是一个历史遗留问题，短期内不会解决。</p>

<p>所以还是自己动手吧。解决办法是在生成 HTML 前把所有连续的中文行转成一行。(修改
Markdown 的实现也可以，但 rdiscount 用了 C 写的 Markdown 实现 discount，hack 起来比较麻烦。) 在 Ruby 1.9 中，连接中文行可以很方便的用正则表达式实现，注意开头的
<code>#encoding: UTF-8</code> 是必须的。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1">#encoding: UTF-8</span>
</span><span class='line'>
</span><span class='line'><span class="k">class</span> <span class="nc">String</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">join_chinese</span>
</span><span class='line'>    <span class="k">unless</span> <span class="vi">@chinese_regex</span>
</span><span class='line'>      <span class="n">han</span> <span class="o">=</span> <span class="s1">&#39;\p{Han}|[，。？；：‘’“”、！……（）]&#39;</span>
</span><span class='line'>      <span class="vi">@chinese_regex</span> <span class="o">=</span> <span class="no">Regexp</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&quot;(</span><span class="si">#{</span><span class="n">han</span><span class="si">}</span><span class="s2">) *</span><span class="se">\n</span><span class="s2"> *(</span><span class="si">#{</span><span class="n">han</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">,</span> <span class="no">Regexp</span><span class="o">::</span><span class="no">MULTILINE</span><span class="p">)</span>
</span><span class='line'>    <span class="k">end</span>
</span><span class='line'>    <span class="nb">gsub</span><span class="p">(</span><span class="vi">@chinese_regex</span><span class="p">,</span> <span class="s1">&#39;\1\2&#39;</span><span class="p">)</span>
</span><span class='line'>  <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>


<p>可以用来转换文件的代码在 <a href="https://gist.github.com/1492799">gist 上</a>，但我不希望把 Markdown 源文件变成有着巨长行的文本，所以我修改了 jekyll，在调用 rdiscount 前对内容调用 <code>join_chinese</code> 方法。需要修改 <code>lib/jekyll/converters/markdown.rb</code>，patch 如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
</pre></td><td class='code'><pre><code class='diff'><span class='line'><span class="gd">--- bak-markdown.rb 2011-12-23 20:14:24.000000000 +0800</span>
</span><span class='line'><span class="gi">+++ markdown.rb  2011-12-23 20:13:19.000000000 +0800</span>
</span><span class='line'><span class="gu">@@ -1,3 +1,15 @@</span>
</span><span class='line'><span class="gi">+#encoding: UTF-8</span>
</span><span class='line'><span class="gi">+</span>
</span><span class='line'><span class="gi">+class String</span>
</span><span class='line'><span class="gi">+  def join_chinese</span>
</span><span class='line'><span class="gi">+    unless @chinese_regex</span>
</span><span class='line'><span class="gi">+      han = &#39;\p{Han}|[，。？；：‘’“”、！……（）]&#39;</span>
</span><span class='line'><span class="gi">+      @chinese_regex = Regexp.new(&quot;(#{han})\n(#{han})&quot;, Regexp::MULTILINE)</span>
</span><span class='line'><span class="gi">+    end</span>
</span><span class='line'><span class="gi">+    gsub(@chinese_regex, &#39;\1\2&#39;)</span>
</span><span class='line'><span class="gi">+  end</span>
</span><span class='line'><span class="gi">+end</span>
</span><span class='line'><span class="gi">+</span>
</span><span class='line'> module Jekyll
</span><span class='line'>
</span><span class='line'>   class MarkdownConverter &lt; Converter
</span><span class='line'><span class="gu">@@ -115,7 +127,7 @@</span>
</span><span class='line'>             }).to_html
</span><span class='line'>           end
</span><span class='line'>         when &#39;rdiscount&#39;
</span><span class='line'><span class="gd">-          RDiscount.new(content, *@rdiscount_extensions).to_html</span>
</span><span class='line'><span class="gi">+          RDiscount.new(content.join_chinese, *@rdiscount_extensions).to_html</span>
</span><span class='line'>         when &#39;maruku&#39;
</span><span class='line'>           Maruku.new(content).to_html
</span><span class='line'>       end
</span></code></pre></td></tr></table></div></figure>


<p>用 Markdown 来写这种类型的 blog 真是舒服多了。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[AI Class 结束了]]></title>
    <link href="http://chenyufei.info/blog/2011-12-23/ai-class-finished/"/>
    <updated>2011-12-23T18:02:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-12-23/ai-class-finished</id>
    <content type="html"><![CDATA[<p>AI Class 结束了，昨天收到了 Statement of Accomplishment。这学期的很多周末都花在了做作业上，最后看到这个还是挺开心的。</p>

<p><img src="http://chenyufei.info/uploads/ai-statement.jpg" alt="AI Class Statement of Accomplishment" /></p>

<p>总体来说课程不难，但涉及的面很广。上课的一个很大的感受是，讲课好的老师可以帮你节省很多看书的时间，而且有些方法的思想只有在视频里才可以很好的展示。我买了
Artificial Intelligence A Modern Approach (AIMA) 这本书，在课程开始前按照课程涉及内容看了一些，速度比较慢。课程开始之后比较忙，一般只遇到不太明白的地方才看，明显感觉到比没上过课时看起来轻松很多。可惜的是本科和硕士阶段我很少遇到这样的老师，上课和自己看书的效果差不多。</p>

<p>课程涉及内容的一个不完整的列表如下，帮助我自己记录从这门课里学到哪些东西。有同学想看 AIMA 这本大部头又不知道如何选重点可以参考。</p>

<!--more-->


<ul>
<li>Introduction

<ul>
<li>Agent model</li>
<li>Different types of environments</li>
</ul>
</li>
<li>Search algorithm

<ul>
<li>BFS, DFS, A*-search</li>
</ul>
</li>
<li>Probabilistic Inference

<ul>
<li>Bayes network</li>
<li>Conditional independence</li>
<li>Conditional dependence (这个我完全记不起来以前学概率统计时有没有涉及。A B 两个独立事件，如果他们同时会影响事件
C，那么在给定事件 C 的情况下，A B 不独立。)</li>
<li>D-separation (从 Bayes 网络的结构直接推断几个事件是否条件独立。)</li>
</ul>
</li>
<li>Machine Learning

<ul>
<li>Supervised/Unsupervised learning</li>
<li>SPAM detection (介绍的就是跟 Paul Graham 用的那个方法。)</li>
<li>Lapalace smoothing</li>
<li>k Nearest Neighbors</li>
<li>k-Means clustering</li>
<li>EM algorithm (这个没弄明白)</li>
</ul>
</li>
<li>Logic and Planning

<ul>
<li>First order Logic</li>
<li>Planning in the belief state</li>
<li>Situation Calculus</li>
<li>Markov Decision Process: 在 Stochastic and Fully Observable 的环境中使用
(A* 之类的算法仅适用于 Deterministic 的环境中。)</li>
<li>Reinforcement Learning: 在不知道 Reward 和 Transition model 的情况下使用

<ul>
<li>Q-learning</li>
</ul>
</li>
<li>Scheduling</li>
</ul>
</li>
<li>Hidden Markov Model

<ul>
<li>上这门课之后终于明白这个 HMM 是干啥的了</li>
</ul>
</li>
<li>Particle filter

<ul>
<li>很简单很漂亮的算法，算法的效率还很高，可以处理非常复杂的概率分布</li>
<li>因为期中考试的原因，课程里没有给 quiz，讲的也不太清楚，后来看了其他学校的材料弄明白的</li>
<li>跟 HMM 一样，可以用来解决 localization problem (给定地图，如何判断自己究竟在哪里)</li>
</ul>
</li>
<li>Games

<ul>
<li>Alpha-beta pruning</li>
<li>Dominant Strategy</li>
<li>Pareto Optimal</li>
<li>Equilibrium (纳什均衡)</li>
</ul>
</li>
<li>Computer Vision

<ul>
<li>Perspective projection (小孔成像之类的东西，用镜头可以提高进光量)</li>
<li>Invariance, Greyscale Images</li>
<li>Extracting features

<ul>
<li>用 vertical/horizontal fiter 来找图像中物体的边缘</li>
<li>Prewitt Mask, Gaussian Kernel, Harris Corner detector</li>
</ul>
</li>
<li>从二维图像重建三维信息

<ul>
<li>用两个相机，根据图像的差异计算距离</li>
<li>Alignment problem: 计算 disparity 需要将不同图像中的相同物体对应起来</li>
</ul>
</li>
</ul>
</li>
<li>Robotics

<ul>
<li>描述对象：Kinematic 只考虑位置，Dynamic 还考虑速度</li>
<li>用 Dynamic Programming 做 path lanning</li>
</ul>
</li>
<li>Natural Language Processing

<ul>
<li>Language models (统计模型，考虑语法的模型)</li>
<li>统计模型

<ul>
<li>letter based model，建立 n-gram model 后可以检测单词属于那种语言</li>
<li>用 gzip 做语言分类的方法很巧妙</li>
<li>Segmentation: 分词，一个方法是选取一个分段，使得其中词连续出现的概率最大</li>
<li>Spelling correction</li>
</ul>
</li>
<li>考略语法的模型

<ul>
<li>Probabilistic Context Free Grammar (PCFG)</li>
<li>Lexicalized Probabilistic Context Free Grammar (LPCFG)</li>
<li>Machine Translation</li>
</ul>
</li>
</ul>
</li>
</ul>


<p>课程开始介绍的可以认为是 AI 里基础的一些方法，最后介绍的 Games, Computer Vision,
Robotics, NLP 都是很大的领域，这门课里只是非常简单的涉及到。</p>

<p>明年 2 月开始还有很多精彩的课程，我目前注册了三门，到时候看时间充裕程度选择最感兴趣的好好上。</p>

<ul>
<li><a href="http://www.security-class.org/">Computer Security</a>: 出来混，安全问题一定要当心</li>
<li><a href="http://www.saas-class.org/">Software Engineering for Software as a Service</a>: Rails 和 David Patterson!
不太理解为什么 Patterson 教这门课，相当好奇和期待</li>
<li><a href="http://www.hci-class.org/">Human-Computer Interaction</a>: 介绍里说很多例子里会用 web design 做例子，这是我对这门课感兴趣的原因</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Prevent applications like vim and less clearing screen on exit]]></title>
    <link href="http://chenyufei.info/blog/2011-12-15/prevent-vim-less-clear-screen-on-exit/"/>
    <updated>2011-12-15T16:11:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-12-15/prevent-vim-less-clear-screen-on-exit</id>
    <content type="html"><![CDATA[<h1>TL;DR</h1>

<p>The content clearing behavior is caused by the <code>smcup/rmcup</code> capability. Here&#8217;s the
methods to preserve screen content depends on what terminal you use:</p>

<ul>
<li><p>For <code>tmux</code>, the <code>alternate-screen</code> option defaults to <code>on</code>. Add the following
in <code>.tmux.conf</code> to disable it:</p>

<pre><code>set-window-option -g alternate-screen off
</code></pre></li>
<li><p><code>screen</code> does not enable the annoying terminal capability by default. If you
encounter this, add the following to <code>.screenrc</code>:</p>

<pre><code>alterscreen off
</code></pre></li>
<li><p>If you don&#8217;t use <code>screen</code> or <code>tmux</code>, I suggest you invest some time in these
tools and I assure you they worth it. If you can&#8217;t use these tools, then follow
the directions in <a href="http://blogs.oracle.com/samf/entry/smcup_rmcup_hate">smcup/rmcup:
hate</a>. This removes the
declaration of the annoying capabilities in the <code>terminfo</code> database, so the
application will not use them.</p></li>
</ul>


<p>Update: If you find <code>ssh</code> &#8220;clears&#8221; screen after logout, check if <code>.bash_logout</code>
on the server contains <code>clear</code> command.</p>

<!--more-->


<h1>Why?</h1>

<p>When working on terminal, I prefer applications such as <code>less</code> and <code>vim</code>
preserve their content on exit. This is quite handy to me. For example I often
look up some commands&#8217; man page  for some options and then started typing on the
terminal. With the man page preserved on the screen, I can still look it up when
typing. (Note that <code>man</code> send its output to <code>$PAGER</code>, which is <code>less</code> for me.)</p>

<p>I&#8217;ve noticed that the content preserving behavior is different on different
machines, and it also varies if you use <code>screen</code> or <code>tmux</code>. After arguing with
one member in my lab which behavior is better, I finally decided to figure out
what caused the difference.</p>

<h1>Behind the scene</h1>

<p>So let&#8217;s first figure out how is the clear screen behavior implemented. (More
actually, restore to what the screen looks like before calling the command.)
<code>tmux</code>&#8217;s man page explains what the <code>alternate-screen</code> option does:</p>

<blockquote><p>This option configures whether programs running inside tmux may use the
<strong>terminal alternate screen feature</strong>, which allows the <strong>smcup</strong> and <strong>rmcup</strong>
terminfo(5) capabilities.</p></blockquote>

<p>If we set this option on, <code>less</code> and <code>vim</code> will first store the content on the
screen and restore the content after exit. (You can disable this for <code>less</code> with
the <code>-X</code> option.)</p>

<p>The man page of <code>terminfo</code> is not very clear on what <code>smcup/rmcup</code> does.
Here&#8217;s an example of using <code>smcup/rmcup</code> to save and restore screen,
copied and modified from
<a href="http://wiki.bash-hackers.org/scripting/terminalcodes">bash-hackers</a>. I&#8217;m using
<code>git log</code> as an example here (you can also try <code>more</code> the file itself, but
the effect is not that obvious):</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="c">#!/bin/sh</span>
</span><span class='line'><span class="c"># save screen</span>
</span><span class='line'>tput smcup
</span><span class='line'>
</span><span class='line'><span class="c"># This will always preserve content regardless of terminal and terminfo.</span>
</span><span class='line'><span class="c"># But as we surround it with smcup and rmcup, we will see the screen being</span>
</span><span class='line'><span class="c"># cleared when exits.</span>
</span><span class='line'>git log
</span><span class='line'>
</span><span class='line'><span class="c"># restore</span>
</span><span class='line'>tput rmcup
</span></code></pre></td></tr></table></div></figure>


<h1>How the solution works</h1>

<p>For application to save and restore screen, 2 conditions must be met:</p>

<ul>
<li>The terminal supports <code>smcup/rmcup</code> capability (such as iTerm2 on OS X)</li>
<li>The <code>terminfo</code> set by <code>$TERM</code> declares these capability
(<code>xterm</code> declares, <code>vt100</code> does not. But the above example still works even if
 <code>$TERM</code> is set to <code>vt100</code>. I guess <code>tput</code> still sends out the correct control
 code even though the <code>vt100 terminfo</code> set by <code>$TERM</code> does not declare it. But
 applications should first check the capability of terminal. and only send
 control-code if that capability exists.)</li>
</ul>


<p><strong>Breaking either of the conditions can preserve the screen content.</strong> That&#8217;s
how the solution in the TL;DR section works.</p>

<h1>References</h1>

<ul>
<li><a href="http://forums.freebsd.org/showthread.php?t=13515">how to prevent clear screen after less or more commands under
tmux</a></li>
<li><a href="http://superuser.com/questions/137714/using-screen-commands-like-less-and-man-dont-clear-the-screen-afterwards">Using screen, commands like less and man don&#8217;t clear the screen
afterwards</a></li>
<li><a href="http://superuser.com/questions/106637/less-command-clearing-screen-upon-exit-how-to-switch-it-off">&#8216;less&#8217; command clearing screen upon exit - how to switch it
off?</a></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[博客转用 Octopress]]></title>
    <link href="http://chenyufei.info/blog/2011-12-13/migrate-to-octopress/"/>
    <updated>2011-12-13T18:34:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-12-13/migrate-to-octopress</id>
    <content type="html"><![CDATA[<p>很早就想用 Vim + Markdown 写博客，甚至有想过自己写一个，不过我在 HTML 上的三脚猫功夫让我打消了这个念头。最近听 Zellux 说 <a href="http://octopress.org/">Octopress</a> 可以实现这一点并且亲身实践，所以决定也转用 Octopress 了。</p>

<p>Octopress 的好处和如何从 wordpress 迁移数据可以参考 <a href="http://blog.yxwang.me/2011/11/migrated-to-octopress/">Zellux 的文章</a>，我用了他的脚本，按照我的需求做了一点修改，<a href="https://gist.github.com/1467485">代码在此</a>。
Octopress 的文档还不错，这里记录下我迁移数据和 deploy 时遇到的一些问题。</p>

<!--more-->


<ul>
<li><p>如果打算用 <code>rsync</code> 来发布的话路径一定要配置正确。由于发布时 <code>rsync</code> 名来带了
<code>--delete</code> 参数，所以如果服务器上的目录下有其他内容会被全部删除。<code>Rakefile</code> 里的 <code>document_root</code>指定 <code>rsync</code> 时服务器上的路径，它跟 <code>_config.yml</code> 里的
<code>root</code> 没有什么联系。（我自己悲剧了……）</p></li>
<li><p>维持原先 wordpress permalink 的形式。从 wordpress 导出评论到 disqus 之后是使用
permalink 来找对应的评论的，如果改动的话评论就无法在原来的文章下出现了。</p></li>
<li><p>带中文的 URL permalink 的带来的麻烦。</p>

<ul>
<li><p>migrate 文件把每篇文章转为一个 markdown 文件，且以 wordpress 导出的文件里
<code>post\_name</code> 作文件名。<code>post_name</code> 是 URL encode 过的，因此中文会变成 <code>%xx</code> 的形式，但在 generate 整个 site 的时候会对文件名再次进行 URL encode，所以原来的
<code>%xx</code> 会变成 <code>%25xx</code>，这会导致 disqus 无法找到评论。</p>

<p> 解决办法是在 migrate 的时候对 <code>post_name</code> URL decode 一下，这样中文文章的名字也会好看很多。</p></li>
<li><p>disqus 无法找到中文文章的评论。</p>

<p>jekyll 在生成的页面中设置 <code>disqus_url</code> 变量来获取对应文章的评论，这个变量使用了 <code>page.url</code>，其中 URL encode 过的字符是大写英文字符，但在 wordpress 导出到 disqus 里的 permalink 是小写的，大写小不匹配导致找不到对应的评论。（URL
中除了 path部分大小写可能有意义，所以 disqus 不能忽略大小写。）</p>

<p>解决办法是创建一个 Liquid filter 来把 <code>page.url</code> 里的 &#8216;%XX&#8217; 转成 &#8216;%xx&#8217;，我所做的修改也放在 <a href="https://gist.github.com/1471046">gist 上</a>。</p></li>
</ul>
</li>
<li><p>图片和其他附件的处理。把服务器上 <code>wp-content/uploads</code> 拷贝到
<code>octopress/source/uploads</code>，用 sed 批量改下文章里的 url，改成相对路径。</p></li>
</ul>


<h2>后记</h2>

<p>整个过程最麻烦的地方就在于中文 URL permalink 的处理上了，其他都还好。</p>

<p>希望换成 Octopress 之后可以更勤快的更新，一些用 markdown 做的笔记也许也可以整理一下然后发布出来。</p>

<p>回头看看自己刚开始写的日志觉得当时真是二啊……如果当时有 Twitter 的话很多文章都没必要发了。不过就这么留着吧，有些事情不是这个博客记录着都差点忘了，回过头来也能看到自己成长的过程。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[13 寸 Macbook Pro 拆光驱更换 SSD]]></title>
    <link href="http://chenyufei.info/blog/2011-09-06/13-%E5%AF%B8-macbook-pro-%E6%8B%86%E5%85%89%E9%A9%B1%E6%9B%B4%E6%8D%A2-ssd/"/>
    <updated>2011-09-06T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-09-06/13-寸-macbook-pro-拆光驱更换-ssd</id>
    <content type="html"><![CDATA[<p><strong>Update: 尝试转用 octopuses 的时候不小心删除了 wordpress 的目录，备份是很早之前的，这篇文章的图片全丢了，现在的图是重新画的，有些细节记不清了……
</strong></p>

<p>最近买了 Intel G3 320 120G 的 SSD 换到了 Macbook Pro 上。SSD 如果看性能的话 OCZ 的上一代产品 vertex 2 比这一款还好（从评测来看 sandforce 的 controller 性能很突出），价格还便宜一点。不过看到有人说 vertex 2 在 MBP 上有遇到<a href="http://www.chrisk.de/blog/2011/04/intel-320-series-vs-ocz-vertex-2-vs-apple/">无法 sleep 之类的问题</a>，为图省心还是买了 Intel 的。</p>

<p>网上拆光驱换 SSD 的教程很多，不过很多都是写 15 寸的 MBP。Apple4us 的 <a href="http://apple4.us/2011/03/kill-cdrom-embrace-ssd.html">干掉光驱、拥抱 SSD</a> 虽然是拍的 13 寸 MBP 的照片，但他不是写详细过程的。13 寸 MBP 因为体积小，内部更紧凑，拆光驱比 15 寸麻烦，要拆卸的东西更多。这篇文章就上些图，说明下那里要拆吧。光驱位硬盘架，USB 光驱盒淘宝上搜下有很多。（淘宝上的都能用，不过建议资金充裕的还是买质量好点的。我买到的光驱盒很山寨，而即使是看起来做工还行的硬盘支架跟 Apple4us 里的 MCE OptiBay 的照片比起来细节上也差一点。）</p>

<!--more-->


<p>出于数据安全考虑，我把 SSD 装在了光驱位以保证 HDD 还能使用 SMS (Sudden Movement Sensor)。MBP 的螺丝非常容易滑牙，动手之前一定要找到合适的螺丝刀。即使有合适的螺丝刀拧的时候也要小心，我在淘宝上买的螺丝刀还是不小心拧滑牙了一颗螺丝，好在还是拧下来了。</p>

<p>下面上图，说明都在图上。（Skitch Plus 做这种图片标注之类的操作真是利器啊。）</p>

<p><a href="http://chenyufei.info/uploads/2011/09/ssd-0.jpg"><img src="http://chenyufei.info/uploads/2011/09/ssd-0.jpg" alt="" title="ssd-0" width="550" height="410" class="aligncenter size-full wp-image-511"></a></p>

<p><a href="http://chenyufei.info/uploads/2011/09/ssd-1.jpg"><img src="http://chenyufei.info/uploads/2011/09/ssd-1.jpg" alt="" title="ssd-1" width="550" height="410" class="aligncenter size-full wp-image-520"></a></p>

<p><a href="http://chenyufei.info/uploads/2011/09/ssd-2.jpg"><img src="http://chenyufei.info/uploads/2011/09/ssd-2.jpg" alt="" title="ssd-2" width="550" height="410" class="aligncenter size-full wp-image-519"></a></p>

<p><a href="http://chenyufei.info/uploads/2011/09/ssd-3.jpg"><img src="http://chenyufei.info/uploads/2011/09/ssd-3.jpg" alt="" title="ssd-3" width="550" height="410" class="aligncenter size-full wp-image-517"></a></p>

<p>换上 SSD 以后的优化从 <a href="http://blog.jjgod.org/2010/04/17/macosx-ssd-tweaks/">jjgod 的文章</a>出发，找到了<a href="http://blog.philippklaus.de/2011/04/ssd-optimizations-on-mac-os-x/">这一篇</a>，现在把 ~/Library/Caches 放在 ramdisk 上（我有 8G 内存，用 1G 做了 ramdisk），hibernate 是很早之前就禁用了。打开 Lion 上的 Trim support 是参考了<a href="http://digitaldj.net/2011/07/21/trim-enabler-for-lion/">TRIM Enabler for Lion</a>，注意不要用 Trim enabler 那个软件。</p>

<p>最后说下 Intel 320 系列 SSD 的保修。保修政策分两种，包装盒上型号最后两位是 B5 或者 K5 的保修 5 年；其他型号的如果通过  Intel® Solid-State Drive Toolbox 查出来损耗值 E9 降到 1（意味着写操作到最大次数限制了），那即使不到 5 年也不能保修了。具体可以看 Intel 支持论坛上的<a href="http://communities.intel.com/thread/23408">这个讨论</a>，还有 <a href="http://www.intel.com/p/zh_CN/support/category/ssdc/ssd320-120gb25/doc_guide">Intel 官方的保修文档</a>，注意看“5年有限保修有媒体损耗输出指示器”。（这名字翻译的很蛋疼。。。）</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Reeder: Fast, Polished, Simply Beautiful (vs. MobileRSS)]]></title>
    <link href="http://chenyufei.info/blog/2011-07-23/reeder-fast-polished-simply-beautiful-vs-mobilerss/"/>
    <updated>2011-07-23T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-07-23/reeder-fast-polished-simply-beautiful-vs-mobilerss</id>
    <content type="html"><![CDATA[<p><strong>Update: 尝试转用 octopuses 的时候不小心删除了 wordpress 的目录，备份是很早之前的，这篇文章的图片全丢了，之前的截图也没了，所以不打算恢复图片了……另外，这篇文章写的是 MobileRSS 3.4，最新的版本是否存在那些问题未知。Reeder 用的很舒服，没有兴趣再尝试 MobileRSS 了。</strong></p>

<p>我个人非常喜欢 Reeder，所以决定写这篇文章对 Reeder 和 MobileRSS 做一点比较。虽然 Reeder 的功能比 MobileRSS 少，但在阅读这个核心功能上做得非常精致，贴心，用着很舒服。但 Reeder 比较有个性，你更可能喜欢哪个还是要自己决定。（Zellux 在评论里提到 MobileRSS 抄袭 Reeder 的界面设计的事，当时有些应用直接封禁了 MobileRSS，MobileRSS 后来改了。有兴趣的读者可以自行Google。）</p>

<!--more-->


<p>我比较过许多 Google Reader app 的免费版本，如 Byline, iReadG, feedly, MobileRSS，最后购买了 MobileRSS。Reeder 因为没有免费的 iPhone 版可试用而没有尝试。<b>MobileRSS 最大的问题是总是有一些恼人的 bug</b>，例如：</p>

<ul>
<li>同步未读文章的 bug，经常出现读过的文章再次同步之后又变成未读，而不同 feed 的未读文章数之和也会与所有未读数不一致。向开发者提过这个问题，不过最近 3.4 的更新依然没有解决。</li>
    <li>使用过 Reeder 之后才发现 MobileRSS 对内容的显示也有问题，MobileRSS 不能显示表格，没有正确处理颜色。</li>
</ul>


<p>因为无法忍受 MobileRSS 的那些恼人的问题而一直心动想换用 Reeder，<a href="http://www.macstories.net/iphone/reeder-2-review/">Reeder 2: Call It a Comeback</a> 这篇文章对 Reeder 的评论让我决定购买 Reeder。</p>

<p><b>用过 Reeder 后知道它才是我想要的。</b>下面分几个方面来比较 Reeder 和 MobileRSS。</p>

<h1>速度</h1>

<p>Reeder 的同步速度和使用时的反应速度都非常快，上面提到的那篇文章对此有如下描述：</p>

<blockquote>
It’s fast.

Again: it’s fast.

Seriously fast.
</blockquote>


<p>这个只有用过才有体会，无法用图来描述。这一点在用户体验上影响其实很大，决定了你从点击 app 到能开始看文章要多久。Reeder 在打开 app 和同步的速度上都胜过 MobileRSS 不少。</p>

<h1>功能</h1>

<p>比较功能前想引用 Instapaper 作者博客里的一句话：</p>

<blockquote>
Making a product better often requires removing features.
</blockquote>


<p>从支持的 Google Reader 操作上来说，MobileRSS 胜过 Reeder。除了基本的阅读功能外，MobileRSS 有一些额外功能：</p>

<ul>
<li>管理订阅</li>
    <li>全屏阅读</li>
    <li>支持 Google 的推荐文章和订阅</li>
    <li>对不输出全文的 rss 得到全文</li>
    <li>支持新浪微博</li>
</ul>


<p>我从来没有在 iPhone 上来管理过订阅。对 Google Reader 的应用，我更看重阅读这一个核心功能。</p>

<p>全屏阅读的确有价值，但问题是手经常会不小心碰到屏幕，开启全屏阅读反而恼人。（这个问题网易新闻的全屏阅读也有。）</p>

<p>MobileRSS 获取 rss 全文的功能不错，不过有时候会有点问题，而且会减慢同步速度。<strong>所以即使是用 MobileRSS 也建议直接用做好的全文 rss feed。</strong>某些非全文的 rss 可以在 Reeder 上用 Readability 来获取全文，另外网上有很多做好的 rss 全文输出 feed，比如<a href="http://rss.imlim.com/">这里</a>。没有的话也可以用 Yahoo! Pipes 自己定制一个，不过会麻烦一点，可以参考<a href="http://www.junstyle.com.cn/archives/520">这篇文章</a>。</p>

<p>Reeder 没有中文翻译，英文不好的人用起来会比较吃力。</p>

<h1>用户界面</h1>

<p>用户界面是个见仁见智的问题，我喜欢简洁的界面，不要分散我的注意力，less is more。Reeder 正对我胃口。</p>

<p><a href="http://chenyufei.info/uploads/2011/07/main.png"><img src="http://chenyufei.info/uploads/2011/07/main.png" alt="Main window for Reeder and MobileRSS" title="Main window for Reeder and MobileRSS" width="550" class="aligncenter size-full wp-image-492"></a></p>

<p>首先比较主界面。截图左面的是 Reeder，很简洁，只有已加星标，未读和所有三项。Reeder 把配置放在系统配置里，这样使得程序界面上需要的元素更少了。</p>

<p>注意看打开 Reeder 时的电池区域，和下面的截图比较下。Reeder 用这里来提示当前执行的同步操作，点击之后可以看同步任务的细节。这个设计不占地方，在阅读时进行同步也不会产生干扰。MobileRSS 的同步提示就在主界面选择全部和未读的地方，一旦开始同步就无法使用这两个按钮。</p>

<p><a href="http://chenyufei.info/uploads/2011/07/article.png"><img src="http://chenyufei.info/uploads/2011/07/article.png" alt="article window" title="article" width="550" class="size-full wp-image-472"></a></p>

<p>查看文章的界面。很喜欢 Reeder 的淡黄色调，不刺眼，标题的处理个人觉得更胜一筹；MobileRSS 虽然可以调成黑色背景，但这个设置下文字难以看清。</p>

<p><a href="http://chenyufei.info/uploads/2011/07/readbility.png"><img src="http://chenyufei.info/uploads/2011/07/readbility.png" alt="readability" title="readability" width="550" class="size-full wp-image-475"></a></p>

<p>Reeder 用 Readability 获取全文后的效果。</p>

<p><a href="http://chenyufei.info/uploads/2011/07/table.png"><img src="http://chenyufei.info/uploads/2011/07/table.png" alt="Displaying table" title="table" width="550" class="size-full wp-image-477"></a></p>

<p>最后看内容还原的效果，MobileRSS 不能显示表格！MobileRSS 对文字颜色处理的问题就懒得贴图了，毕竟还不会太影响阅读，表格显示是致命问题。</p>

<h1>结束语</h1>

<p>应该说 Reeder 是一个非常有个性的程序，可能有人会很讨厌这样的程序。或许在我使用 Reeder for Mac beta 喜欢上它的风格时我就应该意识到在 iPhone 上我也应该使用 Reeder，它的作者 Silvio Rizzi 在界面设计上简洁的风格非常对我胃口，看看 <a href="http://reederapp.com/">Reeder 的主页</a>也能感觉到他的风格。</p>

<p>另外托方校长的福，作为重度 Google 依赖者，最近已经到了没有 <strong>Very Public Network</strong> 就无法正常收发邮件，愉快阅读订阅文章的地步。方校长您是最需要感谢 Very Public Network 的人，不然相信很多人以最最粗鲁的方式问候您本人和祖先的次数将大大增加。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Building Tunnelblick with openvpn-ipv6 on Snow Leopard]]></title>
    <link href="http://chenyufei.info/blog/2011-07-19/building-tunnelblick-with-openvpn-ipv6-on-snow-leopard/"/>
    <updated>2011-07-19T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-07-19/building-tunnelblick-with-openvpn-ipv6-on-snow-leopard</id>
    <content type="html"><![CDATA[<p><strong>Update: I will no longer compile Tunnelblick directly with
<a href="https://github.com/jjo/openvpn-ipv6">openvpn-ipv6</a>.
Instead I will just compile openvpn-ipv6 and replace the openvpn executable
shipped by Tunnelblick (located under <code>Tunnelblick.app/Contents/Resources/openvpn/openvpn-&lt;version&gt;</code>).
This would be much easier to catch up with the new releases of Tunnelblick.</strong></p>

<p><strong>You can download my compiled 64bit openvpn binary for OS X on
<a href="https://github.com/cyfdecyf/openvpn-ipv6-osx-binary/downloads">this github downloads page</a>.
I only use it on Lion, not sure if it works on Snow Leopard.</strong></p>

<p>As my university&#8217;s network is in CERNET, we can only access foreign web site directly with ipv6. Because the stock Tunnelblick does not support ipv6, I&#8217;ve been using <a href="http://code.google.com/p/tunnelblick-ipv6/">tunnelblick-ipv6</a> nearly everyday recently. The problem with Tunnelblick-ipv6 on google code is that it&#8217;s uploaded in Dec 2009 and can not run on 64bit OS X kernel, so I decided to compile an ipv6 enabled Tunnelblick by myself.</p>

<p>Tunnelblick is actually a GUI front end to openvpn. To enable ipv6 support we need an ipv6 enabled openvpn. Thanks to <a href="https://github.com/jjo">jjo</a>, he has provided an <a href="https://github.com/jjo/openvpn-ipv6">ipv6 enabled openvpn</a> on github. The Tunnelblick project only includes official openvpn releases due to security reasons and that&#8217;s why they do not include this version of openvpn. (Explained in this Google code <a href="http://code.google.com/p/tunnelblick/issues/detail?id=150&can=1&q=ipv6">issue</a>.)</p>

<p>It&#8217;s not quite difficult to build Tunnelblick actually, but some problems arise during the process. I&#8217;ll briefly describe the process below. <b>The <a href="https://github.com/cyfdecyf/openvpn-ipv6-osx-binary">final modified code</a> is on github so you don&#8217;t need to do it again.</b> I&#8217;ve also uploaded compiled binary for you if you trust me :) Here&#8217;s the link <a href="https://github.com/downloads/cyfdecyf/openvpn-ipv6-osx-binary/Tunnelblick-3.2beta25build2647.dmg">Tunnelblick-3.2beta25build2647.dmg</a></p>

<!--more-->


<p>First get Tunnelblick&#8217;s source code, I&#8217;m using git to access subversion repository</p>

<pre>
git svn clone -s -r 1500:HEAD http://tunnelblick.googlecode.com/svn/ tunnelblick
</pre>


<p>Get openvpn-ipv6 from jjo&#8217;s repository</p>

<pre>
git clone https://github.com/jjo/openvpn-ipv6.git
</pre>


<p>Use openvpn-ipv6 to replace the openvpn in tunnelblick&#8217;s third party directory.</p>

<p>Then I <strong>removed the ppc build target for all the third party library and set the SDK to 10.6</strong> since I&#8217;m not interested with other version of OS X and do not have 10.4 SDK installed. (I did this using Xcode and manually modified third part tools&#8217; configuration file.)</p>

<p><strong>Some problems arise when building openvpn-ipv6</strong>. It <strong>can not be configured with openssl-1.0.0d</strong> provided by Tunnelblick. I installed openvpn-0.9.8r with homebrew and modified the third part tools Makefile to use that instead. Another problem with openvpn-ipv6 is that <strong><code>IPV6_RECVPKTINFO</code> is missing on OSX</strong>, after applying the <a href="http://permalink.gmane.org/gmane.network.openvpn.devel/4775">one patch</a> I can build Tunnelblick successfully use the following command</p>

<pre>
xcodebuild -configuration Release
</pre>


<p>Finally I cleaned up the modified code and put it on github. openvpn-ipv6 is added as a git submodule.</p>

<p>It&#8217;s also possible to replace the official Tunnelblick&#8217;s openvpn program with
the one compiled from openvpn-ipv6 to make it support ipv6. So I&#8217;ve also
uploaded the compiled binary to <a href="https://github.com/cyfdecyf/openvpn-ipv6-osx-binary/downloads">this github downloads page</a>.
Use it to replace the one in <code>Tunnelblick.app/Contents/Resources</code>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在 C 语言中包装函数 — 用 libtcc 动态生成代码]]></title>
    <link href="http://chenyufei.info/blog/2011-03-06/wrap-c-function-using-libtcc/"/>
    <updated>2011-03-06T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-03-06/wrap-c-function-using-libtcc</id>
    <content type="html"><![CDATA[<p>在<a href="http://chenyufei.info/blog/2011-02-28/wrap-c-function-closure-gcc-nested-function/">上一篇文章</a>的最后我提到尝试拷贝二进制代码然后修改函数跳转地址来包装函数，但是失败了。这种做法其实是在动态的创建出新的代码，每个 wrap 函数其实对应了一个新的函数。（用 nested function 来实现 closure 时，函数只有一个，只不过环境不同。）</p>

<p>如果可以在运行时编译代码，就可以通过动态生成代码的方式来包装函数了。</p>

<p>当我发现可以用 libtcc 动态生成代码来包装 C 函数的时候忍不住再次感慨，<a href="http://bellard.org/">Fabrice Bellard</a> (QEMU，FFMPEG 的创建者) 真是神一样的程序员！运行时编译代码听起来很唬人，但 libtcc 作为<a href="http://bellard.org/tcc/">Tiny C Compiler (TCC)</a> 的一个部分真的是非常简单易用！</p>

<p>TCC 是 Bellard 从 2001 年的 C 混淆代码比赛上写的一个 C 编译器 OTCC 发展而来的。（OTCC 只能编译 C 的一个子集。）TCC 本身非常小，x86 上的可执行文件才 100k，但是完整支持 C99。由于 TCC 的编译速度非常快，你可以在 C 文件的第一行加上 &#8220;#!/usr/local/bin/tcc -run&#8221;，然后把 C 程序当成脚本来执行。</p>

<p>本文的重点是用 libtcc 来动态生成代码，从而在 C 语言中包装函数。</p>

<!--more-->


<p>如果要执行下面的例子代码，需要先下载 TCC 并编译安装。TCC 源码包里的 tests/libtcc_test.c 是一个使用 libtcc 动态编译代码的例子，非常简单。下面给出用 libtcc 来包装函数的例子，其实在 <code>create_wrap_function</code> 中我只是稍微修改了下 libtcc_test.c 里面的代码，保留了原先代码的注释。</p>

<script src="https://gist.github.com/845986.js?file=libtcc-wrap.c"></script>


<p>使用命令 <code>gcc libtcc-wrap.c -o libtcc-wrap -ldl -ltcc</code> 编译后即可执行。</p>

<p>这个例子里面，我们把包装函数的代码保持在字符串里，用 libtcc 在运行时编译这段源代码。为了在动态编译的源代码中使用其他的函数，我们需要用 <code>tcc_add_symbol</code> 添加这些函数。被包装函数在源代码中的符号是 <code>origin</code>，我们用实际的被包装函数的指针作为这个符号的 location。</p>

<p><code>create_wrap_function</code> 中调用 libtcc 来编译代码的过程看起来还是很简单的。虽然这种方式对于每个被包装的函数都要生成新的代码，但它能工作在 libtcc 支持的所有平台上。</p>

<p>有了动态编译源代码的能力，我们是不是可以在 C 中创建出类似解释型语言中 REPL 的东西？不过其实已经有现成的 C 解释器产品了，有兴趣的同学可以尝试下 <a href="http://www.softintegration.com/">Ch</a>。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[在 C 语言中包装函数 -- Closure 和 GCC nested function]]></title>
    <link href="http://chenyufei.info/blog/2011-02-28/wrap-c-function-closure-gcc-nested-function/"/>
    <updated>2011-02-28T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-02-28/wrap-c-function-closure-gcc-nested-function</id>
    <content type="html"><![CDATA[<p>NOTE：gist 上的代码是不是没法输出到 RSS？如果看不到代码麻烦直接在浏览器打开吧。</p>

<p>最近在项目中遇到一个问题，我希望对程序中的一些函数添加日志记录功能（只需记录类似参数这样与函数内部逻辑无关信息），这些函数都存放在一个函数指针数组中，所有对这些函数的调用都通过从这个数组中取出对应的项来完成。</p>

<p>为完成这个任务有如下几种方案：</p>

<ol>
<li>修改每一个需要添加日志记录的函数，这需要修改很多的函数</li>
    <li>找到所有调用函数的代码，记录日志。这个方案可行性很差，由于函数指针可能被赋值给其他变量，找出所有的调用很困难，非常容易遗漏</li>
    <li>在将函数存入函数指针数组时，对原先的函数进行包装（wrap function），添加日志记录的功能。之后通过函数指针数组的调用其实都是调用这个包装过的函数。由于函数指针数组赋值在项目代码中只有一处，所以这个方法需要进行的改动最小。</li>
</ol>


<p>第三个方案有点 <a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming">AOP</a> 的影子，可以做的很漂亮，但是很难在 C 语言中实现。如果 C 提供 closure，下列代码即可对函数进行包装达到目的：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="c1">// 被包装函数的原型</span>
</span><span class='line'><span class="k">typedef</span> <span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">func_t</span><span class="p">)(</span><span class="kt">int</span> <span class="n">arg</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// 创建包装过函数</span>
</span><span class='line'><span class="n">func_t</span> <span class="nf">create_wrap_function</span><span class="p">(</span><span class="n">func_t</span> <span class="n">f</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">wrapped</span><span class="p">(</span><span class="kt">int</span> <span class="n">arg</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">val</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">arg</span><span class="p">);</span> <span class="c1">// 调用原函数</span>
</span><span class='line'>        <span class="c1">// 记录日志</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">val</span><span class="p">;</span> <span class="c1">// 返回原函数的返回值</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">wrapped</span><span class="p">;</span> <span class="c1">// 返回包装过的函数</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>




<!--more-->


<h1>GCC nested function</h1>

<p>其实上述代码在 GCC 中能够编译通过，这是由于 GCC 支持 <a href="http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html">nested function 扩展</a>。Jserv‘s blog 中有<a href="http://blog.linux.org.tw/~jserv/archives/2010/07/gcc_nested_func.html">对此进行介绍的文章</a>，不过他是对 32bit linux 系统进行分析。（推荐 Jserv 的博客，有很多不错的技术文章。）可能由于术语使用差异的原因，我觉得他对跳板代码的解释有点难以理解。我参考他的文章和例子代码，根据我自己的探索和理解过程在此对 64bit 的情况进行分析。</p>

<p>首先给出完整的代码：</p>

<script src="https://gist.github.com/845986.js?file=wrap-function.c"></script>


<p>主要要考虑的问题有两个：</p>

<ol>
<li>
<code>create_wrap_function</code> 如何将内嵌的函数的地址返回出去</li>
    <li>包装函数 <code>wrapped</code> 如何访问其作用域外的变量，在例子中是 <code>f</code>，这个变量是“父函数” <code>create_wrap_function</code> 中的变量</li>
</ol>


<p>用 gcc -g 编译以后再用 gdb 来执行，过程如下：</p>

<script src="https://gist.github.com/845986.js?file=gdb-cmd.txt"></script>


<p>在执行 &#8220;n 2&#8221; 之后，函数指针 <code>bar</code> 的值是一个栈上的地址，这说明可能内嵌函数的代码是存放在栈上的。于是反汇编 <code>bar</code>，从得到的三条指令来看，这个栈上的内容不是一个函数。注意到最后一条指令跳转到另一段代码执行，我们反汇编跳转的目标，gdb 给出的信息说明这段代码是内嵌的 wrapped 函数。由此可以看出，<strong>对内嵌函数，gcc 将其编译成一个普通的函数；当我们获取内嵌函数的地址时，我们得到的是一段<a href="http://gcc.gnu.org/onlinedocs/gccint/Trampolines.html">跳板代码</a>，执行这段代码时会跳到内嵌函数执行</strong>。通过在 gdb 中执行 &#8220;x/30i create_wrap_function&#8221; 可以看到，<code>wrapped</code> 函数紧跟在 <code>create_wrap_function</code> 之后。跳板代码其他指令的作用等我们回答完第二个问题是也就清楚了。</p>

<p>为回答第二个问题，我们看 <code>wrapped</code> 函数是如何去调用原函数的。该函数中的第一个 call 指令完成对 <code>wrapped</code> 的调用，从反汇编的代码来看，这是一个 call to absolute address，而这个绝对地址从 <code>rax</code> 寄存器中得到。因此追溯 <code>rax</code> 在 <code>wrapped</code> 的操作情况即可知道原函数地址的来源。可以看到 <code>mov    %r10,%rax</code> 是 <code>rax</code> 的来源，而 <code>r10</code> 寄存器应该是在执行 wrapped 函数之前就已经设置好的。回顾 <code>bar</code> 的反汇编代码，可以看到第二条指令就是设置 <code>r10</code> 寄存器的，而且是将一个栈地址存入 <code>r10</code>。通过 gdb 跟踪，可以发现这个栈地址是 <code>create_wrap_function</code> 运行时栈上的一个地址，而且保存了它的第一个参数。（修改例子代码，让内嵌函数访问多个 scope 外的变量，从反汇编的代码推测 <strong><code>r10</code> 寄存器中存放的应该是内嵌函数所有用到的 scope 外变量中在栈最顶部变量的地址</strong>。）</p>

<p>第二个问题的答案到此也就清楚了，<strong>gcc 将内嵌函数需要访问的 scope 之外的变量的地址存放在 <code>r10</code> 寄存器中，然后再调用内嵌函数</strong>。跳板代码在执行跳转指令之前就会完成这个设置。</p>

<p>如果我们在 <code>create_wrap_function</code> 中直接调用 <code>wrapped</code> 函数，也可以在反汇编代码中看到调用 <code>wrapped</code> 之前有设置 <code>r10</code> 的指令，但是这里是不会有跳板代码。<strong>跳板代码在取函数地址的时候才会生成在栈上</strong>，如果没有跳板代码，通过函数指针进行函数调用是就会因为 <code>r10</code> 没有正确设置而出错。</p>

<h1>利用跳板代码来实现 closure</h1>

<p>简单来说，<strong>closure = function + data</strong>，这些 data 是函数正常运行所需要的。GCC nested function 不是闭包的原因在于内嵌函数需要访问的 scope 之外的变量存放于栈上，一旦栈被修改，内嵌函数就不能正常运行。</p>

<p>Jserv 的博客中使用的技巧是自己分配一块内存来保存函数需要的数据（用 <a href="http://mitpress.mit.edu/sicp/full-text/book/book.html">SICP</a> 里的模型来看类似函数求值的环境），利用 GCC 生成的跳板代码来为函数设置好环境以便能够访问到这些数据。跳板代码关键的部分是两个，一是跳转地址，二是内嵌函数 scope 外变量的地址。下面给出按照 Jserv 的博客中的思路，在 64bit 系统上实现类似 closure 功能的代码。（在 Linux 下测试通过，在 OS X 下有点问题，以后或许会 fix 吧。）</p>

<script src="https://gist.github.com/845986.js?file=closure-wrap-function.c"></script>


<p><code>create_closure</code> 将 nested function 需要用到的 scope 外的变量拷贝到 heap 上分配的内存，并且将跳板代码也拷贝到 heap 上。这样得到的函数指针（其实是跳板代码）就可以在任何地方使用而不用担心栈被破坏。</p>

<p>为获取跳板代码的二进制编码，在 gdb 中可以用 <code>dump memory <file> <start addr> <end addr></code> 命令将跳板代码保存到文件，然后用 <code>objdump -m i386:x86-64 -b binary <file></code> 反汇编得到。</p>

<p>在实现这段代码时遇到过一个 bug，<code>create_closure</code> 一定要在定义 nested function 的函数中调用，否则跳板代码所在的栈可能被修改而导致 <code>create_closure</code> 中不能得到正确的 target。</p>

<h1>一种失败的尝试</h1>

<p>我还尝试过另外一种包装函数的方法。先创建一个单独的普通函数，在其中对原函数的调用通过一个 magic number 来完成。创建包装函数时，通过 <code>memcpy</code> 将上述函数的二进制代码拷贝到 heap 上，用 <code>memmem</code> (gcc 扩展函数) 找到这个 magic number 然后修改成被包装函数的地址。但是不幸的是 64bit 系统上的变量寻址都是通过 rip relative 的方式进行的（包括函数调用，用函数指针虽然可以使得编译器用绝对地址调用，但取得函数指针要访问的变量还是没法绕过 rip relative 的寻址方式），所以这种方式只能完成最最简单的包装函数，而且代码有点丑陋。</p>

<p>关于 64bit 系统上的寻址模式，可以参考 <a href="http://www.nynaeve.net/?p=192">Most data references in x64 are RIP-relative</a></p>

<h1>结论</h1>

<p>这种 closure 的实现方式是<strong>体系结构，OS，编译器相关的</strong>，非常不通用。不到必要的时候还是不要随便使用。</p>

<p>包装函数也可通过用 <a href="http://bellard.org/tcc/">Tiny C Compiler (TCC)</a> 动态编译代码的方式来实现，这在下一篇文章中介绍。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[多线程程序的 IO]]></title>
    <link href="http://chenyufei.info/blog/2011-02-25/multi-thread-application-io/"/>
    <updated>2011-02-25T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-02-25/multi-thread-application-io</id>
    <content type="html"><![CDATA[<p>最近碰到一个跟多线程程序 IO 有关的 bug。看来即使 stdio 函数是线程安全的，在处理多线程 IO 的时候还是得当心。</p>

<p>程序有两个线程，一个线程用 fprintf 输出大量日志，另一个线程在特定条件下会调用 exit 退出。调试半天以后发现在问题出在 exit 时，日志文件会出错。（比如少掉一个换行，出现重复的 log entry 或者丢掉一些 log 等等。）</p>

<p>原以为 exit 时，所有输出都会被 flush 出去。但回头想想，如果 fprintf 正在执行，而整个进程退出了，那缓存着的日志自然不能保证完全写出。（不知道为啥一开始调试的时候没想到。。。）</p>

<p>如果执行 exit 之前由该线程调用 fflush 还是会出同样的问题，因为另外一个线程可能在 fflush 执行完，而 exit 执行前输出日志。</p>

<p>最后的解决办法是在遇到特定退出条件时设置标志变量，输出日志的线程看到这个标志设置后调用 fflush 并且不再输出日志。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Macbook Pro 上 SD 卡只读的问题]]></title>
    <link href="http://chenyufei.info/blog/2011-01-10/macbook-pro-sd-card-read-only/"/>
    <updated>2011-01-10T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-01-10/macbook-pro-sd-card-read-only</id>
    <content type="html"><![CDATA[<p>以前工作地好好的，今天突然发现 SD 卡在我的 mbp (OS 10.6.5) 的内置读卡器上只读，同学的机器上 (OS X 10.6.4) 却是可写的。</p>

<p>用 fat32 的优盘排除了文件系统的问题，借了别人的外置读卡器来，结果还是只读。。。</p>

<p>Google 下，结果很给力。<a href="%E2%80%9Chttp://discussions.info.apple.com/thread.jspa?threadID=2166984&start=0&tstart=0%E2%80%9D">这个帖子</a>有个看起来不太很靠谱但真的能行的解决办法，<b>把 SD 卡的 lock 开关扳到 lock 和 unlock 中间的位置</b>，然后一切都正常了。。。</p>

<p>从在外置读卡器症状相同来看，应该是软件而不是内置读卡器的问题。正在龟速下载升级包，更新到 10.6.6 之后再看看是不是就正常了。</p>

<p>Update on 2011.1.11:</p>

<p>升级到 10.6.6，问题没有解决…… 10.6.4 的时候还是正常的，不过现在不能确定是软件还是硬件的问题，等同学的机器升级以后再测试下。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Use Dnsmasq to setup a local DNS server]]></title>
    <link href="http://chenyufei.info/blog/2011-01-03/use-dnsmasq-to-setup-a-local-dns-server/"/>
    <updated>2011-01-03T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2011-01-03/use-dnsmasq-to-setup-a-local-dns-server</id>
    <content type="html"><![CDATA[<p>In a <a href="http://chenyufei.info/blog/2010-12-19/powerdns-setup-on-debian-lenny-using-bind-or-sqlite3-backend/">previous article</a>, I introduced how to use PowerDNS to setup a local DNS server for my lab. But it turns out that <a href="http://www.thekelleys.org.uk/dnsmasq/doc.html">Dnsmasq</a> is a better choice for the task. It&#8217;s much much easier to setup since it&#8217;s designed for this, the web page of Dnsmasq states that:</p>

<blockquote>
It is designed to provide DNS and, optionally, DHCP, to a small network &#8230; It can serve the names of local machines which are not in the global DNS &#8230; Dnsmasq is targeted at home networks using NAT &#8230; but would be a good choice for any smallish network (up to 1000 clients is known to work) where low resource use and ease of configuration are important.
</blockquote>




<!--more-->


<h1>Installation and configuration</h1>

<p>On debian, use <code>aptitude install dnsmasq</code> to install Dnsmasq. The configuration file is located at <code>/etc/dnsmasq.conf</code>, which contains very useful comments and you even don&#8217;t need to refer to the man page. In my lab, I just use the DNS service provided by Dnsmasq, so I&#8217;m going to talk only about the DNS service.</p>

<p>By default, Dnsmasq <strong>read <code>/etc/resolv.conf</code> to find the upstream DNS server</strong>, and <strong>read <code>/etc/hosts</code> for local host name</strong>, after <code>dnsmasq</code> server is up, you already have a working local DNS server! It&#8217;s just that simple! To add name for a machine, just add that in <code>/etc/hosts</code> and then send <code>SIGHUP</code> to dnsmasq.</p>

<p>There&#8217;s only a little configuration options I want explain more about.</p>

<ul>
<li>
<code>expand-host</code>: by default, simple host names in <code>/etc/hosts</code> are treated as top level domains. If this option is set, simple names in the hosts file will also be expanded with the value of <code>domain</code> option.

E.g. If <code>domain=lab</code>, then you can use either &#8220;serv&#8221;, &#8220;serv.&#8221; to lookup the &#8220;serv&#8221; machine. If &#8220;expand-host&#8221; is set, you can also use &#8220;serv.lab&#8221;.</li>

<li>
<code>local</code>: tells dnsmasq to answer queries from hosts file or DHCP, not from the upstream DNS server. E.g. <code>local=/lab/</code>
</li>

<li>
<code>address</code>: we can use this to force a name to an IP address. This can override upstream DNS server&#8217;s DNS record. We can also do this by adding names in the hosts file.
</li>

</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Kindle 3 Wi-Fi connection problem]]></title>
    <link href="http://chenyufei.info/blog/2010-12-27/kindle-3-wi-fi-connection-problem/"/>
    <updated>2010-12-27T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2010-12-27/kindle-3-wi-fi-connection-problem</id>
    <content type="html"><![CDATA[<p>Seems that many people have problems with Kindle wi-fi connection on some routers. As <a href="http://www.amazon.com/tag/kindle%20customer%20service%20q%20and%20a/forum/ref=cm_cd_et_md_pl?_encoding=UTF8&cdForum=Fx1GLDPZMNR1X53&cdMsgNo=22&cdPage=1&cdSort=oldest&cdThread=Tx1M0TR4JLKU5K2&cdMsgID=Mx1GLWDOFQBQRHT#Mx1GLWDOFQBQRHT">this replay</a> in a thread on amazon&#8217;s customer discussion forum suggests, <strong>kindle 3 verifies internet connection before considering the Wi-Fi connection successful</strong>.</p>

<!--more-->


<p>I verified this in 2 ways:</p>

<ul>
<li>
On a wireless router which kindle can connect, unplug the ethernet cable so it can&#8217;t connect to the internet. Then try to connect on kindle again, kindle will report that it can&#8217;t connect to the &#8220;Internet&#8221;.</li>
<li>
On some wireless network which needs login on web page, kindle browser will visit and display a &#8220;Kindle Reachability Probe Page&#8221; after login. This is another sign of the internet connection verification. (Kindle will not drop the wi-fi connection in case the page can&#8217;t be visited.)</li>
</ul>


<p>I think this verification is NOT necessary and will cause problems in some situation. If I have a local wireless network without internet connection, and I want to start a web server on a computer and transfer some files to kindle without using the USB cable, the internet connection verification make this impossible.</p>

<p>I am not sure whether this relates to the netgear routers&#8217; problems reported by others in the same thread. I bought my kindle through a friend and do not have purchase record in amazon, so I can&#8217;t post reply on amazon&#8217;s forum. That&#8217;s why I post it here and maybe someone will find this.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[PowerDNS setup on Debian Lenny using bind or sqlite3 backend]]></title>
    <link href="http://chenyufei.info/blog/2010-12-19/powerdns-setup-on-debian-lenny-using-bind-or-sqlite3-backend/"/>
    <updated>2010-12-19T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2010-12-19/powerdns-setup-on-debian-lenny-using-bind-or-sqlite3-backend</id>
    <content type="html"><![CDATA[<p>Update on May 4th, 2011: Sebastian Andrzej Siewior writes a <a href="http://download.breakpoint.cc/isuz/isuz-v1.tar.bz2">C program</a> that can make incremental SQL updates for sqlite based on a bind zone file. This would be useful if you are managing a large domain using sqlite.</p>

<p>As the number of computers in my lab grows, it&#8217;s better to use a local DNS server. I&#8217;ve done this with bind when I&#8217;m an undergraduate student, but I forget almost all about bind&#8217;s configuration. <a href="http://www.powerdns.com">PowerDNS</a> seems to be promising: good performance, flexible, easy to use. It has a built-in web server monitor, web based zone management tool <a href="http://www.poweradmin.org">Poweradmin</a>. So I decide to use it this time.</p>

<p><strong>Update: When troubleshooting the problem in the final note, I find that <a href="http://www.thekelleys.org.uk/dnsmasq/doc.html">dnsmasq</a> is more suitable for my purpose. It reads <code>/etc/resolv.conf</code> and <code>/etc/hosts</code> for it&#8217;s DNS configuration. I will write more about this in another article.
</strong></p>

<!--more-->




<h3>Installation</h3>


<p>I am setting up the DNS server on a Debian Lenny system. PowerDNS uses backend to store zone (domain) information. A backend can be bind configuration, relational database, LDAP server etc. I&#8217;m going to show how to use the bind and sqlite3 backend.  First install pownerdns and the sqlite3 backend:</p>

<pre>
sudo aptitude install pdns-server pdns-backend-sqlite3
</pre>


<p><b><code>pdns-server</code></b> combines the authoritative server (which can only lookup host in its own database) and the recursor (which send recursive DNS request to other DNS servers). (If you don&#8217;t need local DNS database and just want to use a DNS cache, <b><code>pdns-recursor</code></b> can be used.)</p>

<h3>Testing DNS server</h3>


<p>Before going on, I want mention that we can use <code>nslookup</code> to make DNS lookup to a specific server manually. (In case you don&#8217;t have this command, install <code>bind9-host</code> package on Debian.)</p>

<pre>
$ nslookup <host> [DNS server]
# For example:
$ nslookup example.com localhost
</pre>


<p>Without specifying DNS server, nslookup will use one in <code>/etc/resolv.conf</code>.</p>

<h3>Basic setup</h3>


<p>The pdns configuration on Debian Lenny lives in <code>/etc/powerdns/</code>.</p>

<ul>
<li>
<code>pdns.conf</code> contains the main configuration for pdns server. It can include other configurations.</li>
<li>
<code>pdns.d/pdns.local</code> this contains local changes we can make, such as backend configurations.
</li>
</ul>


<h3>Enabling recursion</h3>


<p>Recursion allows the server to send recursive DNS request to other DNS server. To enable this, set the <code>recursor</code> in <code>pdns.conf</code> to the DNS server we want to send recursive request. Then make sure that <code>allow-recursion</code> are set correctly. Simply commenting out <code>allow-recursion</code> allow recursion from anywhere.</p>

<p>Reload the configuration and use <code>nslookup google.com localhost</code> to see if recursion works.</p>

<h3>Troubleshooting</h3>


<p>The log will be very helpful if something goes wrong. You can change <code>loglevel</code> to 9 to log all the message. The default setting put log in <code>/var/log/daemon.log</code>. Take a look there to see if you server starts correctly.</p>

<h3>Using the bind backend to configure domain</h3>


<p>If you do not know anything about zone file, please refer to other resources first. You don&#8217;t need to be a guru on zone file, but understanding what is SOA, what is A/MX record is necessary. I recommend the the <a href="http://en.wikipedia.org/wiki/Zone_file">zone file</a> wikipedia article and redhat&#8217;s<a href="http://www.centos.org/docs/4/html/rhel-rg-en-4/s1-bind-zone.html">reference manul</a>.</p>

<p>Here&#8217;s a configuration example using the setup for our lab. I put them under <code>/etc/powerdns/bind</code>. The first file <code>named.conf</code>is a simple bind configuration which only includes another zone file.</p>

<pre>
zone "ppi" {
        type master;
        file "/etc/powerdns/bind/ppi.zone";
        allow-update { none; };
};
</pre>


<p>The second file <code>ppi.zone</code> is a zone file which contains the host and ip mapping for our lab.</p>

<pre>
$ORIGIN ppi.     ; start of this zone file in the name space, not necessary if included from named.conf
$TTL 1h          ; default expiration time of all resource records without their own TTL value
@               IN      SOA  localhost someone.mail.com. ( ; @ is replaced with . in email address
                        1       ; serial number of this zone file
                        1d      ; slave refresh (1 day)
                        2h      ; slave retry time in case of a problem (2 hours)
                        4w      ; slave expiration time (4 weeks)
                        1h      ; minimum caching time in case of failed lookups (1 hour)
                )
                IN      NS      ns                      ; ns.ppi is a nameserver for ppi
                IN      MX      1 mail
                IN      A       10.x.x.x
ns              IN      A       10.x.x.x
mail            IN      A       10.x.x.x

serv1           IN      A       10.x.x.x
</pre>


<p>Now let&#8217;s configure powerdns to use the bind configuration we&#8217;ve just created. Edit <code>/etc/powerdns/pdns.d/pdns.local</code> to contain the following content:</p>

<pre>
launch=bind
bind-config=/etc/powerdns/bind/named.conf
</pre>


<p>Reload the configuration by issuing the command <code>/etc/init.d/pdns reload</code>. Now you can use <code>nslookup</code> to get the IP address of &#8220;serv1.ppi&#8221;. If the lookup failed, take a look at the log.</p>

<p>Remember that every time you update the zone file, you need to tell pdns to reload the configuration. This is not the case if we are using a relational database backend, which I&#8217;ll cover next.</p>

<h3>Using the sqlite3 backend to configure domain</h3>


<p>I found the information about sqlite3 backend in PowerDNS&#8217;s documentation. I will put all the files under <code>/etc/powerdns/sqlite</code> directory.</p>

<p>The 1st step is toset up powerdns to use the sqlite3 backend. Follow the instruction <a href="http://doc.powerdns.com/gsqlite.html">here</a> to setup a sqlite3 database with the specified schema. <strong>Note that your database should be readable by the <code>pdns</code> user, otherwise powerdns will fail to start.</strong> Edit <code>pdns.local</code> to like this:</p>

<pre>
launch=gsqlite3
gsqlite3-database=/etc/powerdns/sqlite3/db.sqlite3
</pre>


<p>Now we can restart the pdns server.</p>

<p>Next create a domain, I found the instruction in the <a href="http://doc.powerdns.com/configuring-db-connection.html#CONFIGURING-MYSQL">MySQL configuration example</a>. (It took me some time to find it.) Use the following command to create the domain:</p>

<pre>
$ sqlite3 db.sqlite3 'insert into domains (id, name, type) values (0, 'ppi', 'NATIVE');'
</pre>


<p>Then we can use <code>zone2sql</code> tool provided by PowerDNS to convert the zone file into SQL statement to insert into the records table. I&#8217;ve created a small shell script to do that:</p>

<pre>
#!/bin/bash

usage() {
    echo "Usage: `basename $0` <path to named><sqlite3 db><domain>"
}

if [[ $# != 2 ]]; then
    usage
    exit 1
fi

bindconf=$1
db=$2

if [[ -r $db ]]; then
    # create a backup first
    cp $db $db~
else
    echo "Must use an existing sqlite3 database with schema setup."
fi

sqlite3 $db 'delete from records'
zone2sql --named-conf=$bindconf 2>/dev/null | sqlite3 $db
</domain></sqlite3></path></pre>


<p>You can use this script to load previous zone file into the sqlite3 database. After that, you pdns server will be working again as using the bind configuration file.</p>

<h3>Conclusion</h3>


<p>Though both backend works, I prefer the sqlite3 backend since modification to zone configuration doesn&#8217;t require server to reload configuration. But I don&#8217;t like operate with SQL statements directly. So my choice is to change the zone file, then use the script to update the database. For a small environment like our lab, this works well.</p>

<p>Final note: I found that on windows, <strong>nslookup works but ping doesn&#8217;t</strong> when looking up the name like &#8220;serv1.ppi&#8221;. A little gooling take me to <a href="http://stackoverflow.com/questions/330395/dns-problem-nslookup-works-ping-doesnt">stackoverflow</a> and <a href="http://www.thekelleys.org.uk/dnsmasq/docs/FAQ">dnsmasq FAQ</a>  (search for &#8220;looking up local names&#8221; in that FAQ). Adding a dot at the end the the name like &#8220;serv1.ppi.&#8221; works, refer to dnsmasq&#8217;s FAQ for how to configure your system to force that.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[glibc stdio functions are thread-safe]]></title>
    <link href="http://chenyufei.info/blog/2010-11-17/glibc-stdio-functions-are-thread-safe/"/>
    <updated>2010-11-17T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2010-11-17/glibc-stdio-functions-are-thread-safe</id>
    <content type="html"><![CDATA[<p>根据 man flockfile，stdio 的函数是线程安全的。FILE 里其实包含有 lock，stdio 函数会执行加锁操作。</p>

<p>需要的时候用户可以使用不加锁的 stdio 函数(例如 fwrite_unlocked，参考 man unlocked_stdio)，自己调用 flockfile 等函数来执行加锁解锁操作。</p>

<p>以前我还自己写测试来判断 glibc 的 stdio 函数是否线程安全，今天才看到 man page 里的说明。</p>

<p>PS:
今天还碰到个 uint16_t 和 int 做比较出错的 bug。。。我用宏定义了一个常数 -1，C 可以通过 -1UL 来指定 unsigned long int 1，但是没有办法指定为 uint16_t，最后只好换成用 const uint16_t 定义常量就搞定了。</p>

<p>const 的限制在于不能用它定义的其他常数的算数运算来定义其他的 const，这点限制不太方便。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Git http/ssh/git protocol through HTTP proxy]]></title>
    <link href="http://chenyufei.info/blog/2010-10-12/git-httpsshgit-protocol-through-http-proxy/"/>
    <updated>2010-10-12T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2010-10-12/git-httpsshgit-protocol-through-http-proxy</id>
    <content type="html"><![CDATA[<p>My university network only gives us access to <a href="http://en.wikipedia.org/wiki/CERNET">CERNET</a>. This causes trouble when I need to access resources outside CERNET. Luckily, many applications support HTTP proxy, and git works with proxy very well.</p>

<p>Using http protocol to access a git repository is the most simple way. Set an environment variable is enough. <code>"export http_proxy=proxyhost:proxyport</code>&#8221;. If your proxy requires authentication, &#8221;<code>export http_proxy=http://username:passwd@proxyhost:proxyport</code>&#8221; should work. (I didn&#8217;t tested the later one. <code>man libcurl-tutorial</code> if you want to see the magic behind this syntax.)</p>

<p>For ssh/git protocol, we need to create an <a href="http://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_Tunneling">http tunnel</a> using either <a href="http://www.agroman.net/corkscrew/">corkscrew</a>/socat/connect-proxy (I used corkscrew). <strong>Note that the proxy server needs to allow connect to the destination port.</strong> (If your proxy server only allows connect to 80/443 port, this will not work.)</p>

<p>To use git protocol, you can find instructions <a href="https://gforge.ti.com/gf/project/omapandroid/wiki/?pagename=Git+Firewall">here</a>.</p>

<p>To use ssh protocol, ssh magic is needed. Take a look <a href="http://techblog.iamzellux.com/2010/05/git-through-https-proxy/">here</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[推荐 flos http proxy]]></title>
    <link href="http://chenyufei.info/blog/2010-09-16/%E6%8E%A8%E8%8D%90-flos-http-proxy/"/>
    <updated>2010-09-16T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2010-09-16/推荐-flos-http-proxy</id>
    <content type="html"><![CDATA[<p>发现 ccproxy 不支持 ipv6 的二级代理，搜了下发现了这个 flos http proxy。</p>

<p>很简单小巧的程序，配置起来比 ccproxy 也简单很多，win7 也能用。</p>

<p>用户认证方面比较弱，只能配置一个用户名密码，不过对我来说够用了。</p>

<p>没找到官方网站，google 下找个能下载的就行，我用的版本是 1.0a7。</p>

<p>给个<a href="http://www.skycn.com/soft/49392.html">下载的链接</a>。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Release of COREMU]]></title>
    <link href="http://chenyufei.info/blog/2010-07-21/release-of-coremu/"/>
    <updated>2010-07-21T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2010-07-21/release-of-coremu</id>
    <content type="html"><![CDATA[<p>到 <a href="http://ppi.fudan.edu.cn">PPI</a> 半年了，COREMU 终于在 <a href="http://sf.net/p/coremu">sourceforge</a> 上发布了。COREMU 的主要工作是王肇国（我本科同学，现在也在 PPI）完成的，其他开发人员可以在 PPI 的 COREMU 项目主页上找到。</p>

<p>简单说 COREMU 把 Qemu 并行化了。原先的 Qemu 只能使用一个物理 CPU，COREMU 则是使用多个物理 CPU 来模拟虚拟 CPU。</p>

<p>目前 COREMU 只能在 x86_64 Linux 系统上运行，支持模拟 x86_64 和 ARM Cortex A9 MPCore。在我们的测试用的 4 核系统上能模拟出 255 核，并且成功运行 Linux。</p>

<p>在这里宣传一下。半年来一直与 bug 做斗争，现在终于发布了，希望 COREMU 能够发挥些作用。有什么问题的话欢迎发邮件联系 :) cyfdecyf at gmail dot com</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[RDC 远程 64bit win 7 没声音的解决方法]]></title>
    <link href="http://chenyufei.info/blog/2010-07-07/rdc-64bit-win7-no-soun/"/>
    <updated>2010-07-07T00:00:00+08:00</updated>
    <id>http://chenyufei.info/blog/2010-07-07/rdc-64bit-win7-no-soun</id>
    <content type="html"><![CDATA[<p>找的有点辛苦。。。装个 <a href="http://support.microsoft.com/kb/973062">hotfix</a> 就可以了，注意不同版本的系统要下不同的 hotfix。</p>

<p>话说微软的 hotfix 要用起来还真麻烦，邮件申请，还要用密码解压。。。好在很快能搞定</p>

<p>另外装了这个 hotfix 以后 CoRD 还是没声音，只好用 RDC 了。</p>
]]></content>
  </entry>
  
</feed>

