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

  <title><![CDATA[Random Tech Thoughts]]></title>
  <link href="http://chenyufei.info/atom.xml" rel="self"/>
  <link href="http://chenyufei.info/"/>
  <updated>2012-05-04T19:52:37+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[我的第一个 OS X 程序 PinYinNick]]></title>
    <link href="http://chenyufei.info/blog/2012-05-04/osx-app-pinyinnick/"/>
    <updated>2012-05-04T18:58:00+08:00</updated>
    <id>http://chenyufei.info/blog/2012-05-04/osx-app-pinyinnick</id>
    <content type="html"><![CDATA[<p>去年买了
<a href="http://itunes.apple.com/us/app/dialvetica-contacts/id404074258?mt=8">Dialvetica</a>
却发现这个应用不支持用拼音搜索。十一回家的时候写了个汉字转拼音的库，跟作者联系希望能加入拼音的支持。未果，失望之余也无他法。后来了解到 MacRuby 可以直接调用 Cocoa
Framework，写了个 Ruby 脚本给所有联系人添加了拼音缩写，在 Dialvetica 里打开昵称搜索后用的也挺欢的。一个没想到的好处是其他程序比如 Address Book 里要找联系人也可以用昵称，iPhone 上凡是搜索联系人的地方也都能用拼音缩写。</p>

<p>我那个 Ruby 脚本只能在装了 MacRuby 的机器上用。这次五一回去的时候学了下 Cocoa 编程，写了个 native 的程序来做这事。</p>

<p>代码在 Github 上，项目为
<a href="https://github.com/cyfdecyf/PinYinNick">PinYinNick</a>。编译好的文件也已上传，可<a href="https://github.com/downloads/cyfdecyf/PinYinNick/PinYinNick-v1.0.zip">点此下载</a>。欢迎使用。</p>

<p>我几乎从来不写 GUI 程序，写这个程序的过程中得到了不少以往没有的体验。自从不碰
Java 以来一直都是用 Vim 写代码，这次觉得有个好用的 IDE 还是能提高不少效率的。
Interface Builder 真心好用，跟 Cocoa 的 key value coding/observing 结合使可以少写很多代码（当然这个程序的界面还是相当简单的）。静态程序分析对代码的检查的确可以避免一些错误，我刚开始用 ARC，有些内存管理的问题 Xcode 直接提示我解决掉了。最大的缺点是 Xcode 4.3 挺容易崩溃的。</p>

<p>虽然我没什么 GUI 编程的经验，不过 Cocoa 用起来还算顺手，感觉很方便、功能很强大。（比本科时用 Java Swing 方便太多了。）或许以后会再尝试写些程序来满足自己的需求。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[QEMU 源代码笔记]]></title>
    <link href="http://chenyufei.info/blog/2012-04-27/qemu-source-code-notes/"/>
    <updated>2012-04-27T22:36:00+08:00</updated>
    <id>http://chenyufei.info/blog/2012-04-27/qemu-source-code-notes</id>
    <content type="html"><![CDATA[<p>自己的代码过段时间不看就会忘，看别人的代码忘的更快。折腾 QEMU 很久了，经常会要回头看看过的代码，然后发现忘记各种细节。几次之后终于开始做笔记。</p>

<p>今天又多记了一笔，干脆稍微整理一下发布出来，有兴趣的<a href="http://chenyufei.info/notes/qemu-src.html">点此访问</a>。</p>

<p>由于是想到才记，所以没有统揽全局的那种 overview，非常零碎。有几个 QEMU 的模块比较复杂，用 <a href="www.lucidchart.com">LucidChart</a> 画了几张图，看起来可能会方便一些。</p>

<p>跟 tips 一样，会不定期更新。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[让 Safari 的 Google Search 使用 HTTPS]]></title>
    <link href="http://chenyufei.info/blog/2012-04-20/safariomnibar-force-safari-use-https-for-google-search/"/>
    <updated>2012-04-20T19:33:00+08:00</updated>
    <id>http://chenyufei.info/blog/2012-04-20/safariomnibar-force-safari-use-https-for-google-search</id>
    <content type="html"><![CDATA[<p>Google Plus 我很少用，但是 +1 我觉得还是一个不错的功能。以前看到不错的页面会存书签，后来发现这些书签平时不会用到，想搜东西的时候一来不会去搜书签，二来书签不包含内容也搜不到。而 +1 过的页面以后再次 Google 的时候会更容易找到，可以很方便的用来
mark 自己觉得有价值的网页。（Twitter 的 search 太差。Evernote 的 webclip 不错，不过比起单击一下就能 mark 还是麻烦了一点。所以只有非常有价值的内容我才会用 webclip。）</p>

<p>不过在使用 Safari 时，即使登录了 Google，搜索页面也没有 +1 按钮。原因是
Safari 的 Google Search 默认用了 http，而 +1 按钮只有使用 https 时才会显示。我通过修改 <a href="https://github.com/rs/SafariOmnibar">SafariOminibar</a> 的 search provider
文件使 Google Search 默认使用 https。</p>

<p>SafariOminibar 是一个使 Safari 地址栏和 Chrome 地址栏行为类似的一个 plugin，个人使用下来很稳定，推荐使用。注意它不是 extension，而是通过
<a href="http://en.wikipedia.org/wiki/SIMBL">SIMBL</a> 利用 InputManager system 注入代码这种 hack的方式实现的一个插件。下载安装之后修改
<code>/Library/Application Support/SIMBL/Plugins/SafariOmnibar.bundle/Contents/Resources/SearchProviders.plist</code>
这个文件，把 Google 的 url 全部改成 https 开头即可达到目的。</p>

<p>由于 Safari 5.2 将实现 Ominibar 的功能，因此 SafariOminibar 已经停止开发。等
Safari 5.2 发布之后可能得寻找其他方法。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Safari 无法使用本地 PAC 文件的原因和解决办法]]></title>
    <link href="http://chenyufei.info/blog/2012-04-19/osx-lion-pac-file-and-sandbox/"/>
    <updated>2012-04-19T21:31:00+08:00</updated>
    <id>http://chenyufei.info/blog/2012-04-19/osx-lion-pac-file-and-sandbox</id>
    <content type="html"><![CDATA[<p>最近网络环境非常糟糕，无法访问 Google 的情况越来越多。实验室的公用代理经常无法使用，不得不在本地写几个 PAC 文件备用。使用过程中发现用本地的 PAC 文件设定代理
Chrome, Firefox, Twitter For Mac 都能正常使用，唯独 Safari 不行。但是一旦本地开一个 web server 来获取这个文件就一切正常。</p>

<p>Google 一番得到<a href="https://discussions.apple.com/message/16106081#16106081">结果</a>，原因是 OS X Lion 的 sandbox 机制限制了 Safari 能够访问的文件，而我指定的 PAC 文件 Safari 没有权限读取。</p>

<p>很多程序的 sandbox configuration 文件位于 <code>/usr/share/sandbox</code>，这些文件限定了程序能够访问什么文件。例如这个目录下的 <code>sshd.sb</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>
<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>
</pre></td><td class='code'><pre><code class='scheme'><span class='line'><span class="c1">;; Copyright (c) 2008 Apple Inc.  All Rights reserved.</span>
</span><span class='line'><span class="c1">;;</span>
</span><span class='line'><span class="c1">;; sshd - profile for privilege separated children</span>
</span><span class='line'><span class="c1">;;</span>
</span><span class='line'><span class="c1">;; WARNING: The sandbox rules in this file currently constitute </span>
</span><span class='line'><span class="c1">;; Apple System Private Interface and are subject to change at any time and</span>
</span><span class='line'><span class="c1">;; without notice.</span>
</span><span class='line'><span class="c1">;;</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">version</span> <span class="mi">1</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">deny</span> <span class="nv">default</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">allow</span> <span class="nv">file-chroot</span><span class="p">)</span>
</span><span class='line'><span class="p">(</span><span class="nf">allow</span> <span class="nv">file-read-metadata</span> <span class="p">(</span><span class="nf">literal</span> <span class="s">&quot;/var&quot;</span><span class="p">))</span>
</span><span class='line'>
</span><span class='line'><span class="p">(</span><span class="nf">allow</span> <span class="nv">sysctl-read</span><span class="p">)</span>
</span><span class='line'><span class="p">(</span><span class="nf">allow</span> <span class="nv">mach-per-user-lookup</span><span class="p">)</span>
</span><span class='line'><span class="p">(</span><span class="nf">allow</span> <span class="nv">mach-lookup</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">global-name</span> <span class="s">&quot;com.apple.system.notification_center&quot;</span><span class="p">)</span>
</span><span class='line'>  <span class="p">(</span><span class="nf">global-name</span> <span class="s">&quot;com.apple.system.logger&quot;</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<p>有没有很惊讶？本来我只会发个推说下 PAC 文件会因为 sandbox 而无法使用，但是看到<strong>它的 configuration 使用 Scheme 来定义</strong>的时候决定写博客了。</p>

<p>Safari 的 sandbox configuration 位于
<code>/System/Library/PrivateFrameworks/WebKit2.framework/WebProcess.app/Contents/Resources/com.apple.WebProcess.sb</code></p>

<p>知道 PAC 文件不能使用的原因后解决起来就方便了。修改配置文件当然可以，但是遇到
Safari 升级可能要重新修改。<strong>另一个简单点的办法是把 PAC 文件放到 Safari 可以访问的目录，例如 <code>~/Library/Internet\ Plug-Ins</code> 目录</strong>。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[PAC 文件及其调试]]></title>
    <link href="http://chenyufei.info/blog/2012-03-18/pac-and-debug/"/>
    <updated>2012-03-18T14:22:00+08:00</updated>
    <id>http://chenyufei.info/blog/2012-03-18/pac-and-debug</id>
    <content type="html"><![CDATA[<p><strong>Update on 2012-04-29.</strong> 重构 PAC 文件，用数组保存所有需要用代理访问的域名，遍历数组来创建对象以加快查找。</p>

<p><a href="http://en.wikipedia.org/wiki/Proxy_auto-config">Proxy auto-config (PAC)</a> 文件可指定使用代理服务器的规则。比如在教育网内不能访问国外网站，我们可以通过 PAC 文件指定仅当访问某些国外网站时使用代理服务器，其他时候直接访问。</p>

<p><strong>如果熟悉 PAC 文件，只对调试方法有兴趣，请跳到 <a href="#debug">调试 PAC 文件</a> 。</strong></p>

<p>PAC 文件用 JavaScript 编写，必须包含 <code>FindProxyForURL(url, host)</code> 函数。在访问某个网址时，浏览器会调用 <code>FindProxyForURL</code> 根据其返回值来决定该如何访问。该函数的说明如下：</p>

<ul>
<li>参数：

<ul>
<li><code>url</code> 为访问的网址</li>
<li><code>host</code> 为从 <code>url</code> 中推出的主机名，例如 url
&#8220;http://www.reddit.com/r/programming/&#8221; 对应的 host 为 &#8220;www.reddit.com&#8221;</li>
</ul>
</li>
<li>返回值为字符串，告诉浏览器如何访问。下面是一些可用的返回值：

<ul>
<li><code>"DIRECT"</code> 直接访问，不使用代理</li>
<li><code>"PROXY host:port</code> 使用 HTTP 代理</li>
<li><code>"SOCKS host:port"</code> 使用 SOCKS 代理，因为不支持 DNS 解析，不推荐</li>
<li><code>"SOCKS5 host:port"</code> 使用 SOCKS5 代理

<ul>
<li><strong>注意，Safari 虽然支持 SOCKS5 代理，但是不支持在 PAC 文件中返回的
<code>SOCKS5</code>，只认 <code>SOCKS</code></strong></li>
<li>一个 workaround 是返回多个代理服务器配置，把<code>SOCKS5</code> 放在最前面，接一个
<code>SOCKS</code>，这样支持 <code>SOCKS5</code> 返回值的浏览器可以正常使用，而 Safari 会忽略第一个 <code>SOCKS5</code>。例如 <code>SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT</code></li>
<li>另一个办法是把 SOCKS 代理用<a href="http://www.pps.jussieu.fr/~jch/software/polipo/">polipo</a>
转成 http 代理</li>
</ul>
</li>
<li>可以返回多个代理服务器，用分号分隔，浏览器会按顺序尝试。例如 <code>"PROXY
host:port; SOCKS5 host2:port2; DIRECT"</code></li>
</ul>
</li>
</ul>


<p>下面是一个 PAC 文件的例子（这个文件的目的应该很清楚），完整版本见
<a href="https://github.com/cyfdecyf/cyf-util-conf/blob/master/conf/gfw.pac">github</a>。</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='javascript'><span class='line'><span class="kd">var</span> <span class="nx">direct</span> <span class="o">=</span> <span class="s1">&#39;DIRECT&#39;</span><span class="p">;</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">http_proxy</span> <span class="o">=</span> <span class="s1">&#39;PROXY host:port; DIRECT&#39;</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">blocked_list</span> <span class="o">=</span> <span class="p">[</span>
</span><span class='line'>  <span class="s2">&quot;akamai.net&quot;</span><span class="p">,</span>
</span><span class='line'>  <span class="s2">&quot;akamaihd.net&quot;</span>
</span><span class='line'><span class="p">];</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">blocked</span> <span class="o">=</span> <span class="p">{};</span>
</span><span class='line'><span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">blocked_list</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="nx">blocked</span><span class="p">[</span><span class="nx">blocked_list</span><span class="p">[</span><span class="nx">i</span><span class="p">]]</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">function</span> <span class="nx">host2domain</span><span class="p">(</span><span class="nx">host</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="kd">var</span> <span class="nx">dotpos</span> <span class="o">=</span> <span class="nx">host</span><span class="p">.</span><span class="nx">lastIndexOf</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">);</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="nx">dotpos</span> <span class="o">===</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">host</span><span class="p">;</span>
</span><span class='line'>  <span class="c1">// Find the second last dot</span>
</span><span class='line'>  <span class="nx">dotpos</span> <span class="o">=</span> <span class="nx">host</span><span class="p">.</span><span class="nx">lastIndexOf</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">,</span> <span class="nx">dotpos</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="nx">dotpos</span> <span class="o">===</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">host</span><span class="p">;</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">host</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="nx">dotpos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="kd">function</span> <span class="nx">FindProxyForURL</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">host</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="nx">blocked</span><span class="p">[</span><span class="nx">host2domain</span><span class="p">(</span><span class="nx">host</span><span class="p">)]</span> <span class="o">?</span> <span class="nx">http_proxy</span> <span class="o">:</span> <span class="nx">direct</span><span class="p">;</span>
</span><span class='line'><span class="p">};</span>
</span></code></pre></td></tr></table></div></figure>




<!-- more -->


<p>我定义了 <code>host2domain</code> 函数，抽取 host 中的 domain name。然后通过检查 blocked
object 是否有相应的 property 来决定是否使用代理。（为了写 PAC 文件我现学了一把
JavaScript，网上看到的例子都是一连串的 if-else，太低效而且不方便维护。写个数组遍历一把也好啊。）</p>

<p>PAC 文件可以使用一些预先定义好的函数，几个我觉得有用的函数如下：</p>

<ul>
<li><code>isPlainHostName(host)</code> 判断是否是简单域名，例如 localhost 就是一个简单域名</li>
<li><code>dnsDomainIs(host, domain)</code> 判断给定的 host 是否属于某个域名</li>
<li><code>dnsResolve(host)</code> 做 DNS 解析，返回 host 的 ip，注意：DNS 解析可能会 block
住浏览器</li>
<li><code>isInNet(ip, subnet, netmask)</code> 判断 ip 是否属于某个子网</li>
<li><code>myIpAddress()</code> 返回本机的 ip (貌似不太可靠，见 wikipedia 的说明)</li>
<li><code>shExpMatch(str, pattern)</code> 判断两个字符串是否匹配，<code>pattern</code> 中可以包含 shell
使用的通配符</li>
</ul>


<p>还有一些日期相关的函数，详细的列表可以看
<a href="http://www.findproxyforurl.com/pac_functions_explained.html">findproxyforurl.com</a>。</p>

<h1>使用 PAC 文件指定代理</h1>

<p>OS X 和 Windows 都支持 PAC，在代理配置里选择自动配置代理，填入 PAC 文件的 URL 即可。（本地文件用 <code>file://</code>路径。）要注意的是在 HTTP 服务器上部署 PAC 文件时，需把文件的 MIME 类型设置成<code>application/x-ns-proxy-autoconfig</code>。</p>

<p>如果可以控制 DNS 或者 DHCP 服务器还可以使用自动发现代理，如何部署可以参考
Wikipedia <a href="http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol">Web Proxy Autodiscovery Protocol (WPAD)</a>。</p>

<h1>调试 PAC 文件 <a id="debug"></a></h1>

<p>PAC 文件执行的环境跟网页里 JavaScript 执行的环境不同，不过用 Firefox 来调试还是挺方便的。</p>

<p>首先自然是让 Firefox 自己配置代理而不使用系统配置，修改过 PAC 文件后别忘了 reload
来更新。（Safari, Chrome 只能使用系统代理配置，不知怎样才能刷新系统的 PAC 缓存。）</p>

<p><img src="https://img.skitch.com/20120318-n6hyp25ab3k3jegkrdxtwm3yfh.jpg" alt="firefox proxy conf" /></p>

<p>PAC 文件里关键点上加 <code>alert</code>，实际执行的时候并不会弹窗，而会在 Error Console 里打印出信息。有了这个调试就方便多了。</p>

<p><img src="https://img.skitch.com/20120318-xwnk64y1wuypga2jhd9j98882k.jpg" alt="firefox error console" /></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[发布了收集的一些 Tips]]></title>
    <link href="http://chenyufei.info/blog/2012-03-05/tips-published/"/>
    <updated>2012-03-05T22:54:00+08:00</updated>
    <id>http://chenyufei.info/blog/2012-03-05/tips-published</id>
    <content type="html"><![CDATA[<p>把自己收集的关于 OS X, Linux 还有一些软件使用的 tips 用 Octopress 发布了出来。都是在用 Mac 以后才开始记录的，Spotlight 让我不至于因为不能迅速打开文件而懒得记录。</p>

<p><a href="http://chenyufei.info/tips/">Tips 列表</a>，直接浏览的话肯定挺无聊的，有些笔记内容非常少。这些笔记会不断的更新，也方便自己在没带笔记本的时候查看。</p>

<p>Octopress 默认的 CSS 对 list 的处理真难看，不过实在不擅长改网页相关的东西，等出更漂亮的主题以后再换吧。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SaaS class environment set up on OS X]]></title>
    <link href="http://chenyufei.info/blog/2012-02-27/saas-env-setup-on-osx/"/>
    <updated>2012-02-27T13:28:00+08:00</updated>
    <id>http://chenyufei.info/blog/2012-02-27/saas-env-setup-on-osx</id>
    <content type="html"><![CDATA[<p><strong>Update</strong></p>

<p>ruby-debug-base19 version 0.11.25 does not work with ruby 1.9.3-p0 on OS X.
I found a workaround on <a href="http://blog.wyeworks.com/2011/11/1/ruby-1-9-3-and-ruby-debug">WyeWorks Blog</a>.
Here&#8217;s <a href="https://gist.github.com/2003776">my modified gist</a> to install the
working version with RVM managed ruby-1.9.3-p0</p>

<hr />


<p>I&#8217;m taking the <a href="https://www.coursera.org/saas/class">SaaS class</a>. The class
project can be done using the pre-created VirtualBox image or on Amazon EC2
virtual machine, but I want to do it directly on my Mac because its more
convenient for me. Luckily, there is
<a href="http://beta.saasbook.info/bookware-vm-instructions">instructions</a> on how to
configure a bare VM.</p>

<p>Looking into the <a href="http://pastebin.com/download.php?i=WTXF7g7F">configure script</a>
used to configure the ubuntu VM shows that there is nothing special in the
software used. It just needs some ruby gems, PostgreSQL, and Sphinx. (The script
will also install and configure Vim, Emacs, but that&#8217;s not relevant to me.)
Configuring the environment on OS X should be easy, if you are not using Xcode
4.3.</p>

<!-- more -->


<p>If you are using Xcode 4.3, first create link for <code>gcc-4.2</code>, otherwise the
native extension for ruby gems can&#8217;t be built:</p>

<pre><code>cd /usr/bin
sudo ln -s llvm-gcc-4.2 gcc-4.2
sudo ln -s llvm-g++-4.2 g++-4.2
</code></pre>

<p>So here&#8217;s the ruby gems (I recommend to use RVM to create a new gemset):</p>

<pre><code>gem install rails -v 3.1.0
gem install rspec-rails -v 2.6.1
gem install cucumber -v 1.0.6
gem install nokogiri -v 1.5.0
gem install capybara -v 1.1.1
gem install rcov -v 0.9.10
gem install haml -v 3.1.3
gem install sqlite3 -v 1.3.4
gem install uglifier -v 1.0.3
gem install heroku -v 2.8.0
gem install execjs
gem install therubyracer
gem install flog
gem install flay
gem install reek
gem install rails_best_practices
gem install bundler
gem install haml
gem install simplecov
gem install factory_girl
gem install ruby-tmdb
gem install taps
gem install thinking-sphinx
gem install ruby-debug19
</code></pre>

<p>Next install PostgreSQL and Sphinx. I&#8217;m using Homebrew:</p>

<pre><code>brew install postgresql
brew install sphinx
</code></pre>

<p>The script given in the instructions uses PostgreSQL 8.4, but I guess there will
not be some big problems using the 9.1 version. If you want to install a
previous version of PostgreSQL, look the <a href="http://stackoverflow.com/questions/3987683/homebrew-install-specific-version-of-formula">answer on Stack Overflow</a>
on how to install a specific version of formula in Homebrew.</p>

<p>Sphinx installation is very likely to fail if you are using Xcode 4.3. Look <a href="https://github.com/mxcl/homebrew/issues/10016">this
Homebrew issue</a> for workaround. I
followed <a href="https://github.com/claymation">claymation</a>&#8217;s configure setting and manually built sphinx:</p>

<pre><code>cd ~/Library/Caches/Homebrew/
tar xf sphinx-2.0.3.tar.gz
cd sphinx-2.0.3
tar xf ../libstemmer_c.tgz
CXX=g++ CXXFLAGS="-Os -w -pipe -Qunused-arguments -D_FILE_OFFSET_BITS=64 \
  -DNDEBUG" CFLAGS="-Os -w -pipe -Qunused-arguments -D_FILE_OFFSET_BITS=64 \
  -DNDEBUG" ./configure --prefix=/usr/local/Cellar/sphinx/2.0.3 \
  --disable-dependency-tracking --localstatedir=/usr/local/var \
  --with-libstemmer --with-pgsql
make install
brew link sphinx
</code></pre>

<p>I&#8217;m not sure if this setup will work smoothly for the class. I&#8217;ll update this
post if I encounter any problem.</p>
]]></content>
  </entry>
  
  <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><strong>Update on 2012-04-24</strong>: 看到肖之的 <a href="http://xoyo.name/2012/04/auto-spacing-for-octopress/">给中英文间加个空格</a>
提到了 Jekyll 的 plugin 机制，因此改成用 plugin 机制避免更新 Jekyll 时需要重新
patch 的麻烦。</p>

<hr />


<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>
</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="n">han</span> <span class="o">=</span> <span class="s1">&#39;\p{Han}|[，。？；：‘’“”、！……（）]&#39;</span>
</span><span class='line'>  <span class="no">ChineseRegex</span> <span class="o">=</span> <span class="sr">/(</span><span class="si">#{</span><span class="n">han</span><span class="si">}</span><span class="sr">) *\n *(</span><span class="si">#{</span><span class="n">han</span><span class="si">}</span><span class="sr">)/m</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">join_chinese</span>
</span><span class='line'>    <span class="nb">gsub</span><span class="p">(</span><span class="no">ChineseRegex</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>利用 Jekyll 的插件机制可以在它调用 markdown 转换工具前修改需要转换的文本。把下面的代码放在一个文件中，放在 <code>plugins</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>
<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>
</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="nb">require</span> <span class="s1">&#39;./plugins/post_filters&#39;</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="n">han</span> <span class="o">=</span> <span class="s1">&#39;\p{Han}|[，。？；：‘’“”、！……（）]&#39;</span>
</span><span class='line'>  <span class="vc">@@chinese_regex</span> <span class="o">=</span> <span class="sr">/(</span><span class="si">#{</span><span class="n">han</span><span class="si">}</span><span class="sr">) *\n *(</span><span class="si">#{</span><span class="n">han</span><span class="si">}</span><span class="sr">)/m</span>
</span><span class='line'>  <span class="k">def</span> <span class="nf">join_chinese!</span>
</span><span class='line'>    <span class="nb">gsub!</span><span class="p">(</span><span class="vc">@@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><span class='line'>
</span><span class='line'><span class="c1"># Use Jekylly&#39;s plugin system to modify the content before invoking rdicount</span>
</span><span class='line'><span class="k">module</span> <span class="nn">Jekyll</span>
</span><span class='line'>  <span class="k">class</span> <span class="nc">JoinChineseFilter</span> <span class="o">&lt;</span> <span class="no">PostFilter</span>
</span><span class='line'>    <span class="k">def</span> <span class="nf">pre_render</span><span class="p">(</span><span class="n">post</span><span class="p">)</span>
</span><span class='line'>      <span class="n">post</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">join_chinese!</span>
</span><span class='line'>    <span class="k">end</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>需要注意的是分类文章的 rss 需要特殊处理一下，我自己的博客分类做得不好，所以分类
rss 应该没什么价值，所以就懒得处理了。需要处理的可以参考肖之的文章。</p>

<p>下面是原来直接修改 Jekyll 的方法，不再推荐。</p>

<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>
  
</feed>

