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

  <title><![CDATA[Middle Earth]]></title>
  <link href="http://blog.zhangsen.org/atom.xml" rel="self"/>
  <link href="http://blog.zhangsen.org/"/>
  <updated>2012-07-21T18:01:29+08:00</updated>
  <id>http://blog.zhangsen.org/</id>
  <author>
    <name><![CDATA[jesse]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[goagent 安装]]></title>
    <link href="http://blog.zhangsen.org/2012/07/goagent-setup.html"/>
    <updated>2012-07-21T17:41:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/07/goagent-setup</id>
    <content type="html"><![CDATA[<p>我总是后知后觉，现在才开始折腾 goagent。之前买过 VPN，最近一直用 ssh tunnel。试了 goagnet 才发现真心好用啊。</p>

<p>但是目前 GAE 貌似大面积被封了，所以 goagent 一开始安装都有问题，根本没法传到服务器上。</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ python uploader.zip
</span><span class='line'>APPID:zhngsn-p4
</span><span class='line'>Application: zhngsn-p4 
</span><span class='line'>Host: appengine.google.com
</span><span class='line'>INFO - - [Jul 21 17:45:56] Server: appengine.google.com
</span><span class='line'>Rolling back the update.
</span><span class='line'>Traceback (most recent call last):
</span><span class='line'>  File "/usr/lib64/python2.7/runpy.py", line 162, in _run_module_as_main
</span><span class='line'>    "__main__", fname, loader, pkg_name)
</span><span class='line'>  File "/usr/lib64/python2.7/runpy.py", line 72, in _run_code
</span><span class='line'>    exec code in run_globals
</span><span class='line'>  File "uploader.zip/__main__.py", line 10, in &lt;module&gt;
</span><span class='line'>    main()
</span><span class='line'>[...]
</span><span class='line'>  File "uploader.zip/google/appengine/tools/appengine_rpc.py", line 365, in Send
</span><span class='line'>  File "/usr/lib64/python2.7/urllib2.py", line 400, in open
</span><span class='line'>    response = self._open(req, data)
</span><span class='line'>  File "/usr/lib64/python2.7/urllib2.py", line 418, in _open
</span><span class='line'>    '_open', req)
</span><span class='line'>  File "/usr/lib64/python2.7/urllib2.py", line 378, in _call_chain
</span><span class='line'>    result = func(*args)
</span><span class='line'>  File "/usr/lib64/python2.7/urllib2.py", line 1215, in https_open
</span><span class='line'>    return self.do_open(httplib.HTTPSConnection, req)
</span><span class='line'>  File "uploader.zip/fancy_urllib/__init__.py", line 367, in do_open
</span><span class='line'>urllib2.URLError: &lt;urlopen error [Errno 32] Broken pipe&gt;</span></code></pre></td></tr></table></div></figure>


<p>墙很高啊。</p>

<p>尝试用 tsocks，通过现有的 ssh tunnel 上传，一样的 URLError。有人提到可以设置
https_proxy 环境变量，但是手上又没有 http 代理 (socks5 代理能转成 http 代理么)
。</p>

<p>本来要放弃了，但是转念一想，刚才我还上传了 GAE 上的一个小网站呢。于是用 google
官方的 GAE SDK 试了下，还真成了。</p>

<p>goagent 代码其实很简单，需要上传到 GAE 的 app 就在 server/python 目录下。</p>

<p>所以，到 server/python 目录下，编辑 <code>app.yaml</code>，把申请的 app 名字填到第一行:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>application: your-app-name</span></code></pre></td></tr></table></div></figure>


<p>然后，把官方的 SDK 下载下来后，</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>/path/to/google_appengine/appcfg.py update .</span></code></pre></td></tr></table></div></figure>


<p>就 OK 了。没被重置，也不需要每次都输密码。如果申请了多个应用做负载均衡，就依次修改 app.yaml，运行 <code>appcfg.py update</code>，把每一个都部署上。</p>

<p>go 版本在 server/go, 应该是一样的。</p>

<p>部署完后 (我搞了四个做负载均衡) 运行 <code>local/proxy.py</code>。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[fedora 系统修复]]></title>
    <link href="http://blog.zhangsen.org/2012/07/fedora-update-disaster.html"/>
    <updated>2012-07-18T17:44:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/07/fedora-update-disaster</id>
    <content type="html"><![CDATA[<p>一台机器上安的 fedora 17，是用 beta 发布安装的。文档中提到 beta 发布会自动升级到正式版本，不需要改什么配置，所以就一直没在意。但是过了一段时间后，就开始出问题了。</p>

<p>主要的问题是 <code>yum update</code> 的时候报一大堆的 404 错误。类似这样的，</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>http://mirrors.sohu.com/fedora/releases/17/Everything/x86_64/os/drpms/LibRaw-0.14.3-3.fc17_0.14.3-4.fc17.x86_64.drpm:
</span><span class='line'>[Errno 14] HTTP Error 404 - Not Found :
</span><span class='line'>http://mirrors.sohu.com/fedora/releases/17/Everything/x86_64/os/drpms/LibRaw-0.14.3-3.fc17_0.14.3-4.fc17.x86_64.drpm</span></code></pre></td></tr></table></div></figure>


<p>不只 LibRaw 这个包，还有很多，NetworkManager, alsa-plugins-pulseaudio 等等等等。</p>

<p>这个 404 错误显然是 yum 试图用 drpm 来升级，而它尝试的地址都不对，在源里是不存在的。但是当单独升级每个包的时候，好像不会报错。</p>

<p>尝试了 <code>yum clean all</code>, 没什么作用。最后偶然发现很多包的 repo 信息是
<code>@koji-override-0/$releasever</code>，而正常情况下应该是 fedora, fedora-updates 这样的。</p>

<p>于是首先用 <code>yum list extras</code> 找出所有这样的不正常的包.</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>yum list extras [glob_exp1] [...]
</span><span class='line'>      List  the  packages installed on the system that are not available in any
</span><span class='line'>      yum repository listed in the config file.</span></code></pre></td></tr></table></div></figure>


<p>把上边得到的列表用 sed/cut 处理一下，用一个 for 循环，一个一个地单独升级，就能修复到正确的 repo 了。</p>

<p>然后呢&#8230; 发现不好使，有的包还是会尝试用 drpm。</p>

<p>最后的办法是先暂时把 drpm 支持关掉。升级完再打开。</p>

<p>而支持 drpm 的特性叫做 <code>presto</code>, 在 <code>/etc/yum/pluginconf.d/presto.conf</code> 里配置。(吭爹的名字，八杆子打不着)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Python dict 的实现]]></title>
    <link href="http://blog.zhangsen.org/2012/07/python-dict-implementation.html"/>
    <updated>2012-07-09T17:01:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/07/python-dict-implementation</id>
    <content type="html"><![CDATA[<ul>
<li><p>Python (cpython) 的 dict 是用哈希表实现的。</p></li>
<li><p>碰撞的处理方法是 open addressing，因为 chaining 的 overhead 太高 (哈希表里记录的只是哈希值和相关的 object 的地址，不记录实际 object，所以如果用链表来做
 chaining 的话，光 next 指针就占去了很多空间)。(不知道我理解得对不对)。</p></li>
</ul>


<blockquote><p>Open addressing is preferred over chaining since the link overhead for<br/>chaining would be substantial (100% with typical malloc overhead).</p></blockquote>


<ul>
<li>一般的哈希表在选择哈希函数时，都希望产生的哈希值尽量随机，但是 python 的哈希函数不是这样的。dict 和其它一些地方使用的哈希函数其实就是 python 的内置函数
<code>hash</code> ，它的返回值非常的<em>不随机</em>。比如对于最重要的两种 key，整数和字符串，整数的哈希值就是它本身，而字符串的哈希值计算也很简单。且看</li>
</ul>


<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>&gt;&gt;&gt; map(hash, (0, 1, 2, 3))
</span><span class='line'>[0, 1, 2, 3]
</span><span class='line'>&gt;&gt;&gt; map(hash, ("namea", "nameb", "namec", "named"))
</span><span class='line'>[-1658398457, -1658398460, -1658398459, -1658398462]</span></code></pre></td></tr></table></div></figure>


<p>这样的好处是如果 key 是连续的整数或者字符串，几乎不会有碰撞。而且哈希值基本都是连续的，效率也比较高。</p>

<ul>
<li><p>有时候，好处也是坏处。这样做的坏处是，表里会有数据连续地存在一块，而不是相互散开 (这正是哈希表，也称散列表的意义所在啊)，也就是，一旦有碰撞发生，用线性探测的方法来寻找可用位置是绝对不可行的。python 用的探测方法是一种 quadratic
probing，保证不会受到连成块的数据的影响。</p></li>
<li><p>既然是使用 open addressing，也就是说整个哈希表存储在一段连续的内存中，或者说是一个数组 (dict 是用 C 实现的)。这意味着，当数组快满时 (比如超过 2/3)，需要把数组扩大 (resize)。</p></li>
<li><p>python 保证在修改元素的时候不会发生 resize，也就是保证了可以一边遍历 dict，一边修改元素。但是其它的操作就不一定了，所以会有这样的错误:</p></li>
</ul>


<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>&gt;&gt;&gt; a = {1:2, 3:4}
</span><span class='line'>&gt;&gt;&gt; for k in a:
</span><span class='line'>...  del a[k]
</span><span class='line'>...
</span><span class='line'>Traceback (most recent call last):
</span><span class='line'>  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
</span><span class='line'>RuntimeError: dictionary changed size during iteration
</span><span class='line'>
</span><span class='line'>&gt;&gt;&gt; a = {1:2, 3:4}
</span><span class='line'>&gt;&gt;&gt; for k in a:
</span><span class='line'>...  a[2*k]=1
</span><span class='line'>...
</span><span class='line'>Traceback (most recent call last):
</span><span class='line'>  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
</span><span class='line'>RuntimeError: dictionary changed size during iteration</span></code></pre></td></tr></table></div></figure>


<p>参考</p>

<ul>
<li><p>http://www.laurentluce.com/posts/python-dictionary-implementation/</p></li>
<li><p>http://hg.python.org/cpython/file/tip/Objects/dictobject.c</p></li>
<li><p>http://en.wikipedia.org/wiki/Hash_table#Collision_resolution</p></li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[python 有这么慢吗]]></title>
    <link href="http://blog.zhangsen.org/2012/06/is-python-so-slower-than-c.html"/>
    <updated>2012-06-29T16:01:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/is-python-so-slower-than-c</id>
    <content type="html"><![CDATA[<p>逻辑几乎相同的两段代码，python 比 C 慢 50 倍？</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>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="cm">/* Find the sum of all numbers which are equal to the sum of the factorial of</span>
</span><span class='line'><span class="cm">   their digits. */</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#include &lt;stdio.h&gt;</span>
</span><span class='line'>
</span><span class='line'><span class="kt">int</span> <span class="nf">solve</span><span class="p">()</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">f</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">limit</span><span class="p">,</span> <span class="n">sum</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">f</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span><span class='line'>    <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
</span><span class='line'>        <span class="n">f</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">f</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="n">i</span><span class="p">;</span>
</span><span class='line'>    <span class="n">limit</span> <span class="o">=</span> <span class="mi">7</span><span class="o">*</span><span class="n">f</span><span class="p">[</span><span class="mi">9</span><span class="p">];</span>
</span><span class='line'>    <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'>    <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">3</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">limit</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'>        <span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
</span><span class='line'>        <span class="k">while</span> <span class="p">(</span><span class="n">j</span><span class="o">&gt;</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>            <span class="n">s</span> <span class="o">+=</span> <span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="o">%</span><span class="mi">10</span><span class="p">];</span>
</span><span class='line'>            <span class="n">j</span> <span class="o">/=</span> <span class="mi">10</span><span class="p">;</span>
</span><span class='line'>        <span class="p">}</span>
</span><span class='line'>        <span class="k">if</span> <span class="p">(</span><span class="n">i</span><span class="o">==</span><span class="n">s</span><span class="p">)</span>
</span><span class='line'>            <span class="n">sum</span> <span class="o">+=</span> <span class="n">i</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">sum</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="n">printf</span><span class="p">(</span><span class="s">&quot;%d</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">solve</span><span class="p">());</span>
</span><span class='line'>    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>




<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='python'><span class='line'><span class="c"># Find the sum of all numbers which are equal to the sum of the factorial of</span>
</span><span class='line'><span class="c"># their digits.</span>
</span><span class='line'>
</span><span class='line'><span class="kn">import</span> <span class="nn">math</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">solve</span><span class="p">():</span>
</span><span class='line'>    <span class="n">f</span> <span class="o">=</span> <span class="p">[</span><span class="n">math</span><span class="o">.</span><span class="n">factorial</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)]</span>
</span><span class='line'>    <span class="n">limit</span> <span class="o">=</span> <span class="mi">7</span><span class="o">*</span><span class="n">f</span><span class="p">[</span><span class="mi">9</span><span class="p">]</span>
</span><span class='line'>    <span class="n">ret</span> <span class="o">=</span> <span class="p">[]</span>
</span><span class='line'>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">limit</span><span class="o">+</span><span class="mi">1</span><span class="p">):</span>
</span><span class='line'>        <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span>
</span><span class='line'>        <span class="n">j</span> <span class="o">=</span> <span class="n">i</span>
</span><span class='line'>        <span class="k">while</span> <span class="n">j</span><span class="o">&gt;</span><span class="mi">0</span><span class="p">:</span>
</span><span class='line'>            <span class="n">s</span> <span class="o">+=</span> <span class="n">f</span><span class="p">[</span><span class="n">j</span><span class="o">%</span><span class="mi">10</span><span class="p">]</span>
</span><span class='line'>            <span class="n">j</span> <span class="o">/=</span> <span class="mi">10</span>
</span><span class='line'>        <span class="k">if</span> <span class="n">i</span><span class="o">==</span><span class="n">s</span><span class="p">:</span>
</span><span class='line'>            <span class="n">ret</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">ret</span>
</span><span class='line'>
</span><span class='line'><span class="k">print</span> <span class="nb">sum</span><span class="p">(</span><span class="n">solve</span><span class="p">())</span>
</span></code></pre></td></tr></table></div></figure>


<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>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="err">$</span> <span class="n">time</span> <span class="n">python</span> <span class="mi">34</span><span class="o">-</span><span class="nb">sum</span><span class="o">-</span><span class="n">of</span><span class="o">-</span><span class="n">digit</span><span class="o">-</span><span class="n">factorial</span><span class="o">.</span><span class="n">py</span>
</span><span class='line'><span class="mi">40730</span>
</span><span class='line'>
</span><span class='line'><span class="n">real</span>    <span class="mi">0</span><span class="n">m5</span><span class="o">.</span><span class="mo">053</span><span class="n">s</span>
</span><span class='line'><span class="n">user</span>    <span class="mi">0</span><span class="n">m4</span><span class="o">.</span><span class="mi">850</span><span class="n">s</span>
</span><span class='line'><span class="n">sys</span>     <span class="mi">0</span><span class="n">m0</span><span class="o">.</span><span class="mi">145</span><span class="n">s</span>
</span><span class='line'>
</span><span class='line'><span class="err">$</span> <span class="n">time</span> <span class="o">./</span><span class="n">a</span><span class="o">.</span><span class="n">out</span>
</span><span class='line'><span class="mi">40730</span>
</span><span class='line'>
</span><span class='line'><span class="n">real</span>    <span class="mi">0</span><span class="n">m0</span><span class="o">.</span><span class="mi">115</span><span class="n">s</span>
</span><span class='line'><span class="n">user</span>    <span class="mi">0</span><span class="n">m0</span><span class="o">.</span><span class="mi">111</span><span class="n">s</span>
</span><span class='line'><span class="n">sys</span>     <span class="mi">0</span><span class="n">m0</span><span class="o">.</span><span class="mo">002</span><span class="n">s</span>
</span></code></pre></td></tr></table></div></figure>


<p>这是 Project Euler 的第 34 题。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[今日的农村]]></title>
    <link href="http://blog.zhangsen.org/2012/06/todays-countryside.html"/>
    <updated>2012-06-28T21:27:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/todays-countryside</id>
    <content type="html"><![CDATA[<p>端午回家，又到村庄的农田旁边走了走。原本的良田，现在是这样了。</p>

<p>农田被圈成了工地，道路早已废弃许久。走在曾经被植被覆盖的乡间，竟然也能被吹一脸土。</p>

<p><img src="https://lh3.googleusercontent.com/-jgIeuQjZ0rk/T-xaAI7ehMI/AAAAAAAAAV8/NKWdqRCNav0/s720/IMG_20120623_173646.png"></p>

<p>围墙里是曾经的农田。</p>

<p><img src="https://lh4.googleusercontent.com/-TMO473kfz70/T-xaLfGWJUI/AAAAAAAAAWU/NDc08C_KU7g/s912/IMG_20120623_173857.png"></p>

<p>路对面的耕地还没有被征去，但是农民早不敢投入任何成本，随便种下的葡萄苗，只等着被征的时候多换点拆迁款。</p>

<p><img src="https://lh3.googleusercontent.com/-dT5qZBrBZxU/T-xaHg1A-6I/AAAAAAAAAWM/n4KiFgs7eaU/s912/IMG_20120623_173751.png"></p>

<p>收拾得这么干净，也是一种无奈吧。</p>

<p><img src="https://lh4.googleusercontent.com/-RNlvFnmvoNU/T-xaDmbd75I/AAAAAAAAAWE/p1TiDoXk9WM/s912/IMG_20120623_173755.png"></p>

<p>真正让人震惊的，是后边不远处的烟囱。正在建的小区，是打算把附近的村庄拆掉，然后把村民都迁过来的。但是化工厂就在旁边，这是怎样的居心？</p>

<p><img src="https://lh4.googleusercontent.com/-eLkE5lS3r7s/T-xf7jhBbcI/AAAAAAAAAWo/nvYyVo83I6U/s912/IMG_20120623_174513.png"></p>

<p>现在如火如荼的拆迁，显然就是地方政府的圈钱运动。而中央政府睁只眼闭只眼，道理也很简单，在中国是哪些人还有自己的土地？只有农民还在名义上拥有属于自己的土地。只要动用“拆”这个字，就能把所有人赶到了 70 年产权的商品房里，腾出来的大片土地就又是一笔财源。只要忠厚的中国人都不说话，政府又何乐而不为？</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[动态分配的二维数组]]></title>
    <link href="http://blog.zhangsen.org/2012/06/2d-array.html"/>
    <updated>2012-06-28T20:58:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/2d-array</id>
    <content type="html"><![CDATA[<p>在 C 语言里，其实是可以动态分配二维数组的。只调用一次 <code>malloc</code>，就可以得到一个二维数组，并且支持 <code>p[i][j]</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>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="kt">int</span> <span class="o">**</span><span class="nf">alloc_2d</span><span class="p">(</span><span class="kt">int</span> <span class="n">rows</span><span class="p">,</span> <span class="kt">int</span> <span class="n">cols</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'>    <span class="kt">int</span> <span class="o">**</span><span class="n">p</span><span class="p">,</span> <span class="o">*</span><span class="n">data</span><span class="p">;</span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
</span><span class='line'>    <span class="n">p</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">rows</span><span class="o">*</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span> <span class="o">+</span> <span class="n">rows</span><span class="o">*</span><span class="n">cols</span><span class="o">*</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">int</span><span class="p">));</span>
</span><span class='line'>    <span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="o">*</span><span class="p">)(</span><span class="n">p</span><span class="o">+</span><span class="n">rows</span><span class="p">);</span>
</span><span class='line'>    <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">rows</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
</span><span class='line'>        <span class="n">p</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span> <span class="o">+</span> <span class="n">i</span><span class="o">*</span><span class="n">cols</span><span class="p">;</span>
</span><span class='line'>    <span class="k">return</span> <span class="n">p</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>释放的时候调用 <code>free</code> 即可。</p>

<p>再看我之前那些用一维数组模拟二维数组的拙劣代码，真是不堪回首。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[计算区间内的素数]]></title>
    <link href="http://blog.zhangsen.org/2012/06/primes-in-a-range.html"/>
    <updated>2012-06-27T20:47:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/primes-in-a-range</id>
    <content type="html"><![CDATA[<p>生成素数的一般方法是用“筛”，古老的 Sieve of Eratosthenes，来生成 [1,n] 之间的所有素数。这里的问题稍有变化，要求计算某个区间 [m,n] 内的素数，其中 m 和 n 都有可能很大，但是 n-m 不大。</p>

<p>这其实是 SPOJ 上的第二道题 (<a href="http://www.spoj.pl/problems/PRIME1/">PRIME1</a>)。</p>

<p>由于 m 和 n 很大，所以不能用试除法 (trial division)，而且 Sieve of Eratosthenes
也不可行。</p>

<p>这种情况可以用分段的筛 (segmented sieve)。也就是先用 sieve 计算出 [1,sqrt(n)]
上的素数。然后用这个素数表，在 [m,n] 上再次应用 sieve，得到所求的素数。两次的筛都不是很大，可以放在内存中，而且速度也可以接受。如果 n-m 还是很大，还可以再次分成几段来做 sieve，这是<a href="http://programmingpraxis.com/2010/02/05/segmented-sieve-of-eratosthenes/">这里</a>的做法。
(我怎么觉得后面这种情况才是 segmented sieve 呢?)</p>

<p>一个类似的用这种分段策略的题目是，假设一个文件里有 4 billion 个整数，数字都是普通的 int，也就是有 32 位。要求找到一个不在这个文件里的整数，限制可用内存为 10MB
。</p>

<p>用一般的 bitmap 肯定是不行的。可以采用分段策略，通过两次遍历完成。把 2<sup>32</sup> 这个区间分成若干段，第一次遍历求每一个区间中数字出现的个数。然后找出一个必然存在缺失数字的区间，对这个小的区间创建 bitmap，进行第二次遍历，找到一个缺失的数字。空间复杂度满足要求，因为假设有 M 个小区间，每个区间有 N 个数，那么 <code>M*N=2^32</code>，第一次遍历需要 <code>M*4B</code>，第二次遍历需要 <code>N*4B</code> (假设 bitmap 都用 int 数组)。所以 M
和 N 都取为 2<sup>16</sup> = 65536 即可。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[dup3, pipe2, epoll_create1]]></title>
    <link href="http://blog.zhangsen.org/2012/06/dup3-pipe2-epoll-create1.html"/>
    <updated>2012-06-27T09:51:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/dup3-pipe2-epoll-create1</id>
    <content type="html"><![CDATA[<p>dup, pipe, epoll_create 这些函数都有一些变种，<code>dup3</code>, <code>pipe2</code>, <code>epoll_create1</code>
。为什么会这样？原因是这些接口设计时考虑不足，导致无法进行扩展，当新需求出现的时候，只能再添加新的接口。</p>

<p>这些函数有什么相同之处呢，它们都是用来创建新的文件描述符的 (file descriptor)。这个需求就是跟文件描述符相关。</p>

<p>在通过 fork/exec 创建一个新进程的时候，默认的行为是子进程继承父进程的所有已打开的文件。为了安全性，一般会把文件设成 close-on-exec，也就是说执行 exec 的时候，自动把文件关闭。否则的话，父进程的文件就会泄漏给子进程。</p>

<p>比如浏览器里，假设一个 tab 是银行付款页面，另一个 tab 是“某个”页面，这个页面需要用一个 plugin，而浏览器通过 fork/exec 来运行这个 plugin。如果文件描述符没有设置为 close-on-exec，那么银行页面的文件就有可能泄漏给这个 plugin。然后&#8230;</p>

<p>所以一般都会通过 <code>fcntl</code> 来设置一个 flag, <code>FD_CLOEXEC</code>，保证在 fork/exec 的时候自动关闭文件。但是由于 <code>open</code> 和 <code>fcntl</code> 之间总是存在一定的时间差，所以在多线程的程序里会有潜在的危险。一个办法是用锁，保证 open 和 fcntl 之间不会进行 fork，但是这并不可行 (且看引用原文的描述)。</p>

<p>最好的办法就是给 open 添加一个 flag，使得文件在打开时就已经设为 close-on-exec。</p>

<p>但是&#8230; 能打开文件 (也就是创建文件描述符) 的函数不只有 open。由于 UNIX 众所周知的设计特点，socket/accept/pipe/dup/epoll 都能创建文件描述符。而最大的问题是，不是所有的函数都像 open 那样有一个 <code>flags</code> 参数。比如 <code>int pipe(int pipefd[2])</code>。管道固然是个绝佳的概念，但是谁也没想到，后人竟然想对管道设置 flag。</p>

<p>但是为了保证原子性得设置 close-on-exec，必须要有 flag 参数，所以对这些函数只能创建新的接口，<code>dup3</code>, <code>pipe2</code> 等等就是这样来的。这个解决方案不算完美，但也没有更好的办法了 (比如 linus 提出过一个“间接系统调用”的方案)。</p>

<p>而另一些函数，比如 <code>int socket(int domain, int type, int protocol)</code>，虽然没有
flag，但有一个 <code>type</code> 参数。由于 type 的取值范围很有限，而且 type 和 flag 还算有点相似，所以开发者决定复用这个参数，把 <code>SOCK_CLOEXEC</code> 看作新的 type。这样函数接口就不需要修改了。</p>

<p>这样，所有能创建文件描述符的接口就都(相当于)有 flag 参数了，不仅可以用于设置
close-on-exec，而且也能方便将来的其它扩展。比如 <code>XXX_NONBLOCK</code> (现在已有)，和提了好久的 non-sequential file descriptor 的支持。</p>

<p>引用: <a href="http://udrepper.livejournal.com/20407.html">udrepper.livejournal.com</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[firefox 被某智篡改的主页]]></title>
    <link href="http://blog.zhangsen.org/2012/06/chinese-firefox.html"/>
    <updated>2012-06-25T21:34:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/chinese-firefox</id>
    <content type="html"><![CDATA[<p>端午回家，升级了下家里电脑的 firefox，发现主页竟然给改成 firefox.com.cn 上的一个地址了，又是某智做的好事。而且是默认项，“恢复默认设置”也不管用。解决办法是设成 <code>about:home</code>，就能恢复 firefox (真正)默认的主页了。</p>

<p>某智现在做的，还算是在忍受范围内，没有之前那一堆“火狐魔镜”之类的东西了。不过依然让人不爽，比如默认搜索引擎设成 baidu ，这符合 mozilla 的官方政策吗？再次怀疑
mozilla 搞这些的必要性。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[newline]]></title>
    <link href="http://blog.zhangsen.org/2012/06/newline.html"/>
    <updated>2012-06-21T14:42:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/newline</id>
    <content type="html"><![CDATA[<p>翻译是一件看起来简单做起来难的事。在维基百科上查到了这么一段话，想翻译成中文，吭吃吭吃好不容易写完，感觉却还是佶屈聱牙。</p>

<p>&#8220;There is also some confusion whether newlines terminate or separate lines. If a
newline is considered a separator, there will be no newline after the last line
of a file. The general convention on most systems is to add a newline even
after the last line, i.e. to treat newline as a line terminator. Some programs
have problems processing the last line of a file if it is not newline
terminated. Conversely, programs that expect newline to be used as a separator
will interpret a final newline as starting a new (empty) line.&#8221;  (<a href="http://en.wikipedia.org/wiki/Newline">en.wikipedia.org</a>)</p>

<p>“换行字符可以看作是行的结束符，也可以看作行之间的分隔符，这两种处理方式之间存在一些歧义。如果换行字符被当作分隔符，那么文件的最后一行就不需要再有换行字符。但是多数系统的做法是在最后一行的后面也加上一个换行字符，也就是把换行字符看作是行的结束符。这样的程序在处理末行没有换行字符的文件时，可能会存在问题。相反地，有的程序把换行符看作分隔符，就会把最末尾的换行字符看作是新行的开始，也就是多出了一个空行。”  (<a href="http://zh.wikipedia.org/wiki/%E6%8F%9B%E8%A1%8C">zh.wikipedia.org</a>)</p>

<p>P.S. 写了个简单的 tail 程序，却总是多出一行来。结果发现，用 getline 等函数其实就相当于把换行符当作了分隔符，而不是结束符 (C++ 的 getline 的定义就是 <code>getline
( istream&amp; is, string&amp; str, char delim )</code>, 指明了 &#8220;delim&#8221;)。所以最后的这个换行应该特殊处理。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[asdf]]></title>
    <link href="http://blog.zhangsen.org/2012/06/asdf.html"/>
    <updated>2012-06-20T23:04:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/asdf</id>
    <content type="html"><![CDATA[<p>MS 出平板了？今天看到的这段话还真准，(apple) rarely makes a random jump into a
disconnected market just to chase a shiny new trinket (I&#8217;m looking at you,
Microsoft, and your Bing, MSN, and Zune debacles).</p>

<p>http://www.quora.com/Apple-Inc-2/Is-Apple-really-going-to-try-and-kill-Google</p>

<p>拭目以待吧。</p>

<p>P.S. 我本来要发 twitter 的，结果这字数限制实在让人崩溃，一句话都说不完。还是自家地盘爽。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[GNU Screen: altscreen on]]></title>
    <link href="http://blog.zhangsen.org/2012/06/altscreen-on.html"/>
    <updated>2012-06-19T13:06:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/altscreen-on</id>
    <content type="html"><![CDATA[<p>折腾起来，真是一发而不可收。</p>

<p>刚发现了 screen 的这个选项，使能之后，退出程序或者按 ctrl-z 之后不会留下“残余”。</p>

<figure class='code'><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>
</pre></td><td class='code'><pre><code class=''><span class='line'>altscreen on|off
</span><span class='line'>
</span><span class='line'>If  set to on, "alternate screen" support is enabled in virtual terminals,
</span><span class='line'>just like in xterm.  Initial setting is `off'.</span></code></pre></td></tr></table></div></figure>


<p>(git 需要设置下 pager，<a href="http://linuxtips.manki.in/2012/02/making-git-commands-clear-screen-when.html">linuxtips.manki.in</a>):</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>git config --global core.pager 'less -+X -+F'</span></code></pre></td></tr></table></div></figure>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Colorful Term]]></title>
    <link href="http://blog.zhangsen.org/2012/06/colorful-term.html"/>
    <updated>2012-06-19T10:35:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/colorful-term</id>
    <content type="html"><![CDATA[<p>虽然之前折腾过256 色的 terminal，但是总觉得效果好像不大对。下边是正确的折腾步骤。</p>

<ul>
<li>首先环境变量 TERM 应该设成 xterm-256color (保证你有这个文件
/usr/share/terminfo/x/xterm-256color)。</li>
</ul>


<p>在 <code>~/.bashrc</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>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nb">export </span><span class="nv">TERM</span><span class="o">=</span>xterm-256color
</span></code></pre></td></tr></table></div></figure>


<p>但是要注意的是，这一行必须写在导入系统设置之前，也就是这样</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='bash'><span class='line'><span class="c"># User specific aliases and functions</span>
</span><span class='line'>
</span><span class='line'><span class="nb">export </span><span class="nv">TERM</span><span class="o">=</span>xterm-256color
</span><span class='line'>
</span><span class='line'><span class="c"># Source global definitions</span>
</span><span class='line'><span class="k">if</span> <span class="o">[</span> -f /etc/bashrc <span class="o">]</span>; <span class="k">then</span>
</span><span class='line'>        . /etc/bashrc
</span><span class='line'><span class="k">fi</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="o">[</span> -f /etc/profile <span class="o">]</span>;<span class="k">then</span>
</span><span class='line'><span class="k">        </span><span class="nb">source</span> /etc/profile
</span><span class='line'><span class="k">fi</span>
</span></code></pre></td></tr></table></div></figure>


<p>否则，就是这副很普通的样子，</p>

<p><img src="http://blog.zhangsen.org/images/p/ls-plain.png"></p>

<p>正确的话是这个俨然闪闪放光的样子，</p>

<p><img src="http://blog.zhangsen.org/images/p/ls-good.png"></p>

<p>所以默认的 .bashrc 把 &#8220;User specific aliases and functions&#8221; 放在前边，还是大有深意的。</p>

<p>(当然，影响的只是 <code>ls</code>，vim 等应该不受影响。)</p>

<ul>
<li><p>screen 里不需要特殊的配置。有人提到需要换成 screen-256color (在 .screenrc 里写 <code>term screen-256color</code>)，但是我在 fedora 17 上没遇到什么问题。</p></li>
<li><p>然后是 solarized 的主题，就不细说了，把 gnome-terminal 和 vim 设好就 ok。</p></li>
</ul>


<p>P.S. vim 的 <code>set t_Co=256</code>，貌似是没有任何作用，跟我用了 solarized 的
colorscheme 有关？不管怎么设，vim 和 gvim 都是一个样子的。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Perfect]]></title>
    <link href="http://blog.zhangsen.org/2012/06/perfect.html"/>
    <updated>2012-06-15T16:19:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/perfect</id>
    <content type="html"><![CDATA[<p>今天才突然意识到，&#8221;perfect&#8221; 有“完全”的意思。比如“完全平方数”是 &#8220;perfect square&#8221;
，“现在完成式”是 &#8220;present perfect&#8221;。</p>

<p>P.S. 数学小贴士：完全平方数都有奇数个因子，其它整数都有偶数个因子。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Migrate to Octopress - follow up]]></title>
    <link href="http://blog.zhangsen.org/2012/06/migrate-to-octopress-follow-up.html"/>
    <updated>2012-06-14T21:45:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/migrate-to-octopress-follow-up</id>
    <content type="html"><![CDATA[<p>部署完发现问题还是挺多的。主要就是几个链接没有了，包括 feed, archive 等等。</p>

<p>搜索了一番，<a href="http://approache.com/blog/migrating-from-blogger-to-octopress/">这里</a> 提到了对 blogger 的 redirect。代码在这 (<a href="https://github.com/dnagir/approache-redirects/blob/master/app.rb">github.com</a>)。</p>

<p>其实就是几条 URL 匹配规则。因为 octopress 虽然基本是静态的，但是其实是有一个小的 web 框架的 (sinatra)，所以还是比较灵活。</p>

<p>我把我用的 heroku 部署相关的东西放这了 (<a href="https://github.com/zhangsen/octopress-heroku-deploy">github.com</a>)。octopress 需要的 patch 在这 (<a href="https://github.com/zhangsen/octopress/commit/2215aaad70da4b4e4017c43afa8b6a08ab1905b9">github.com</a>)。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Migrate to Octopress]]></title>
    <link href="http://blog.zhangsen.org/2012/06/migrate-to-octopress.html"/>
    <updated>2012-06-14T17:42:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/06/migrate-to-octopress</id>
    <content type="html"><![CDATA[<p>心血来潮，把 blog 换到了 octopress。用了几年的 blogger，以前看到
octopress/jekyll 的时候，从来都觉得没什么，blogger 这种 It just works™ 的就很好。结果昨天重装了个系统，头脑一热，顺带把 blog 也换了。人啊，奇怪的生物。</p>

<p>我这人特别不喜欢遇到 404 的链接，所以到了我迁移网站的时候，就理所当然要努力保证兼容性了。感谢 octopress，其实就是改个配置的事儿。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'># file : _config.yml
</span><span class='line'>permalink: /:year/:month/:title.html</span></code></pre></td></tr></table></div></figure>


<p>然后用<a href="https://gist.github.com/2928871">这个小工具</a>把 blogger 上原来的文章和留言都抓过来。这段代码还有不少版本，我也是根据前人的稍微修改了下，具体使用的过程网上也有不少文章。这里还要感谢 blogger，把打包下载做得这么容易，让用户很容易就跑了。什么是大国心态?</p>

<p>然后由于我之前(好像)用的是 feedburner，所以这块就省事了，订阅我博客的人应该不需要什么改动。</p>

<p>然后写篇声明，写完后把 DNS 一改，就 ok 了，无缝迁移，哈哈。目前发现只有各个月的
archive 页面丢掉了，其它链接应该没有 404 的。</p>

<p>还有一点是，之前就开始用 disqus，所以留言也都还保留着。这个也很好，感谢 disqus。</p>

<p>网站放在 heroku 上，因为我不是很愿意把 blog 内容搞个 public repo，放到 github
上。但是 octopress 文档里的部署到 heroku 的方法不很 (很不!) 爽，因为要把生成的乱七八糟的内容都加到 repo 里。所以找了<a href="http://joshuawood.net/how-to-deploy-jekyll-slash-octopress-to-heroku/">一个办法</a>，给 octopress 打了个补丁:</p>

<p>还搜到了一个办法是利用 heroku 的 custom buildpack，嗯，太复杂。</p>

<p>octopress 是挺漂亮的，但是在 google 的过程中，发现大家都长一个样&#8230;</p>

<p>用 markdown 写的第一篇 blog，撒花。</p>

<p>P.S.</p>

<p>feed 还是出问题了，如果之前订阅的是
http://blog.zhangsen.org/feeds/posts/default, Google Reader 其实记录的仍然是这个地址，而不是重定向的 feedburner，TODO, FIXME。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[rbtree in linux kernel]]></title>
    <link href="http://blog.zhangsen.org/2012/05/rbtree-in-linux-kernel.html"/>
    <updated>2012-05-27T16:48:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/05/rbtree-in-linux-kernel</id>
    <content type="html"><![CDATA[<div class='post'>
正在(重新)看算法导论。这是看 http://lwn.net/Articles/184495/ 的一个小结。<br /><br />红黑树作为近似平衡的二叉搜索树，由于高效的操作，尤其是插入和删除能很快的 rebalancing (不会  go off for an indeterminate period of time rebalancing itself)，所以在 kernel 里有很多应用。<br /><br />kernel 里的红黑树在 lib/rbtree.c 里实现。为了效率，rbtree 不是按照黑盒子的方式实现的，用户必须实现自己的插入和搜索函数。因为如果用函数指针来指定比较操作，由此带来的大量的间接的函数调用势必会影响效率。<br /><br />rbtree 的另一个特点是，跟 kernel 里的 list 类似，struct rb_node 是作为成员嵌套进用户的数据结构的，而不是像通常的课本上那样，定义一个节点的结构，然后把 satellite data 放进这个结构。rbtree 都是在这个 rb_node 上进行操作，如果涉及到了实际数据，比如插入和搜索，就需要用户自己实现。<br /><br />从一个 struct rb_node 取得包含它的 struct 可以通过  container_of() 或者 rb_entry() 来实现。<br /><br />rb_node 里包含左右子节点的指针，父节点的指针，还有节点的颜色。特殊之处在于用了一个 unsigned long 来同时记录颜色和父节点的指针。貌似是依赖于指针的最后两位一定是 0。所以可以把最后一位用来标记颜色。这样就省了一个 int。<br /><br />搜索就是普通 BST 的搜索，遍历一下即可，没有特殊的地方。<br /><br />而插入的时候，需要调用 rbtree 的几个函数。用户需要自己实现找到要插入的位置，然后调用 rb_link_node 来插入新节点，最后调用 rb_insert_color 来 fixup (recoloring)。<br /><br />rbtree 还提供了几个函数来实现遍历，rb_first, rb_last, rb_next, rb_prev。<br /><br />rbtree 还提供了对 augmented rbtree 的支持。Documentation/rbtree.txt 举了一个 interval tree 的例子。用户需要在插入和删除节点的时候需要提供合适的 callback 来处理 augmentation 信息。<br /><br />P.S. 最近都是在 google docs 里写东西，然后遇到适合发布的就贴到 blog 里来。这样子的感觉好多了，不会在 blog 里留一堆废掉的草稿。当然，发布的只是很小一部分，比如已经写了将近 40 页的算法导论的笔记 (而且看了还不到一半)，估计不会发了 ;)</div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[git internals]]></title>
    <link href="http://blog.zhangsen.org/2012/04/git-internals.html"/>
    <updated>2012-04-19T21:14:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/04/git-internals</id>
    <content type="html"><![CDATA[<div class='post'>
这是在 beihang open source club 讲 git 的讲稿。正好借着这个机会把之前看的东西整理整理。其实好几个月之前就跟 grissiom 大概聊过这些内容了，但现在才找到机会(和动力)写下来。<br /><br />这有带图片和(一点)格式的版本: <a href="https://docs.google.com/document/pub?id=1FFssCYnbMgKk9rEEctDWz23Ne8WV4Uy5rOIehX0Rdkw">https://docs.google.com/document/pub?id=1FFssCYnbMgKk9rEEctDWz23Ne8WV4Uy5rOIehX0Rdkw</a><br /><br />0.<br /><br />git 本质上是一个 content-addressable filesystem，然后在其上做了个 VCS 的界面。<br /><br />git 一开始的设计都是围绕着怎么操作这个文件系统来的，所以原来的用户界面不是“那么的”友好。现在的界面已经好多了，但是明白底下这个文件系统的机制，能帮助你更好的发掘 git 的潜力。<br /><br />prerequisite: 读者应该对 git 的 基本使用方法，尤其是 staging area 有所了解。可以看此文 http://progit.org/2011/07/11/reset.html 的 The Three Trees of Git 一节。<br /><br />1. plumbing 和 porcelain<br /><br />git 一开始并不是想提供一个 VCS，而是一个 VCS toolkit。所以 git 有一些很底层的命令，由这些命令再组成更上层的界面。(UNIX 哲学)<br /><br />底层的这些命令称为 plumbing commands，上层的命令称为 porcelain commands。<br /><br />例子：<br /><br />$ echo &#8216;test content&#8217; | git hash-object -w &#8211;stdin<br />$ git cat-file -p d670460b4b4a<br /><br />2. .git 目录结构<br /><br />$ git init<br />Initialized empty Git repository in /home/jesse/src/gittalk/.git/<br /><br />$ ls -a<br />.&nbsp; ..&nbsp; .git<br /><br />$ ls -F .git<br />config&nbsp; description&nbsp; HEAD&nbsp; hooks/&nbsp; info/&nbsp; objects/&nbsp; refs/<br /><br />$ touch a<br />$ git add a<br />$ ls .git -F<br />config&nbsp; description&nbsp; HEAD&nbsp; hooks/&nbsp; index&nbsp; info/&nbsp; objects/&nbsp; refs/<br /><br />git 运行所需要的所有文件都在 .git 里。<br /><br />config: 配置文件<br />description: GitWeb 用。<br />info: .git/info/exclude<br />hooks: 脚本<br /><br />HEAD, index, objects, refs: git 的核心。<br /><br />objects: 所有的数据<br />refs: 指向 commit object 的指针<br />HEAD: 当前 branch<br />index: staging area 信息<br /><br />3. content-addressable filesystem<br /><br />content-addressable filesystem，什么意思？就是一个简单的 key-value 系统。<br /><br />git hash-object 把一个 object 作为 value 存入文件系统，然后返回这个 object 的 key。key 是一个长度为 40 的 sha1 值。<br /><br />$ find .git/objects/ -type f<br />$<br />$ echo &#8216;test content&#8217; | git hash-object -w &#8211;stdin<br />d670460b4b4aece5915caf5c68d12f560a9fe3e4<br />$<br />$ find .git/objects/ -type f<br />.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4<br /><br />存入一个 value 后，可以通过 key 把它读取出来：<br /><br />$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4<br />test content<br /><br />这里的 object 是一个字符串。<br /><br />hash-object 也可以读取文件内容作为 value 存入：<br /><br />$ echo &#8216;version 1&#8217; &gt; test.txt<br />$ git hash-object -w test.txt<br />83baae61804e65cc73a7201a7252750c76066a30<br /><br />试试对文件做点修改：<br /><br />$ echo &#8216;version 2&#8217; &gt; test.txt<br />$ git hash-object -w test.txt<br />1f7a7a472abf3dd9643fd615f6da379c4acb3e3a<br /><br />现在文件系统或者数据库里有三个 object 了：<br /><br />$ find .git/objects -type f<br />.git/objects/83/baae61804e65cc73a7201a7252750c76066a30<br />.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4<br />.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a<br /><br />也就是说，git 的操作是以文件为单位的，一个文件作出修改后，都以新的 object 存入。<br /><br />object 存入时，用 zip 算法进行压缩，然后用 sha1 算法计算 key。<br /><br />4. objects<br /><br />注意到，上边的三个 object 存的都是文件内容，没有文件名或者创建时间这些信息，也没有用我们平时用 git commit 时填入的 commit message 等等。<br /><br />git 有若干种 object，上边用 hash-object 创建的是 blob 类型的 object。<br /><br />$ git cat-file -t 1f7a7a472a<br />blob<br /><br />cat-file -t 打印某个 object 的类型信息。<br /><br />还有两种比较重要的 object。tree object 和 commit object。tree 对应目录树的概念，把 blob 组织起来。而 commit object 与 tree 对应，并且 commit 之间有 parent-child 的关系，这样就能形成 VCS 的历史记录了。<br /><br />5. tree<br /><br />tree 类似于 UNIX 系统上的目录。一个 tree object 包含一组 tree entry。每个 tree entry 是一个 blob (其实是一个 blob 的 sha1 值)，或者是一个 subtree (的 sha1 值), 以及它们的名字、类型、权限。<br /><br />$ git cat-file -p master^{tree}<br />100644 blob a906cb2a4a904a152e80877d4088654daad0c859&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; README<br />100644 blob 8f94139338f9404f26296befa88755fc2598c289&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Rakefile<br />040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lib<br /><br />master^{tree} 是 master 分支上最后一个 commit 所指向的 tree。这个 tree object 包含三个 entry (也就是三个 sha1 值)，其中两个是 blob，第三个是另一个 tree object。这三个 sha1 值还有对应的 mode 和 filename。<br /><br /><br /><br />git 创建 tree 的方式是使用当前 staging area 的内容创建一个 tree object。所以需要先创建 staging-area 或者 index。<br /><br />创建 index 的命令是 git update-index<br /><br />$ git update-index &#8211;add &#8211;cacheinfo 100644 \<br />&nbsp;&nbsp;&nbsp; 83baae61804e65cc73a7201a7252750c76066a30 test.txt<br /><br />把 83baae 这个 blob object (包含“version 1&#8221;的那个)，按照指定的 mode 和名字，加入 index。<br /><br />100644 表示一个 normal file。文件还可以设为可执行，或者是符号链接。但是 git 支持的权限很有限，是 UNIX 上文件模型的子集。<br /><br />然后用 git write-tree 创建 tree object。<br /><br />$ git write-tree<br />d8329fc1cc938780ffdd9f94e0d364e0ea74f579<br /><br />$ find .git/objects/ -type f<br />.git/objects/83/baae61804e65cc73a7201a7252750c76066a30<br />.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4<br />.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579<br />.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a<br /><br />$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579<br />100644 blob 83baae61804e65cc73a7201a7252750c76066a30&nbsp;&nbsp;&nbsp; test.txt<br /><br />$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579<br />tree<br /><br />这样就创建了一个新的 tree object，它包含一个文件 test.txt。现在一共有三个 blob 和一个 tree 了。object 文件是包含类型信息的，git 能够知道 d8329fc 是个 tree object。<br /><br />再创建一个 tree object。把当前目录下的 test.txt (包含&#8221;version 2&#8221;) 加入，并且加入一个新文件 new.txt。<br /><br />$ git update-index test.txt<br />$ echo &#8216;new file&#8217; &gt; new.txt<br />$ git update-index &#8211;add new.txt<br /><br />$ git write-tree<br />0155eb4229851634a0f03eb265b69f5a2d56f341<br /><br />$ git cat-file -p 0155eb422<br />100644 blob fa49b077972391ad58037050f2a75f74e3671e92&nbsp;&nbsp;&nbsp; new.txt<br />100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a&nbsp;&nbsp;&nbsp; test.txt<br /><br />这个 tree object 包含两个文件。<br /><br />虽然 tree object 是有结构的，但仍然是很基本的数据。我们可以把之前创建的第一个 tree 读入staging area (现在的 staging area 是第二个 tree 里的东西)，并且放进一个子目录。<br /><br />$ git read-tree &#8211;prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579<br /><br />$ git write-tree<br />3c4e9cd789d88d8d89c1073707c3585e41b0e614<br /><br />$ git cat-file -p 3c4e<br />040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bak<br />100644 blob fa49b077972391ad58037050f2a75f74e3671e92&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new.txt<br />100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test.txt<br /><br />新建的第三个 tree 相当于包含前两个 tree 的内容。(注意，这些操作都是在 index 和后端存储之间进行的，没有动到 working dir。)<br /><br /><br /><br />6. commit object<br /><br />文件的基本信息在 tree 里保存，那 tree 的基本信息呢？比如创建 tree 的时间、人物、原因。所以 git 又提供了 commit object 保存这些信息。<br /><br />$ echo &#8216;first commit&#8217; | git commit-tree d8329f<br />9fa147d9147f7dbbfa641ebe2992a336bc1a4c93<br /><br />git commit-tree 能创建一个新的 commit object。<br /><br />$ git cat-file -p 9fa147d9<br />tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579<br />author Jesse Zhang &lt;xxx@xxx&gt; 1334569863 +0800<br />committer Jesse Zhang &lt;xxx@xxx&gt; 1334569863 +0800<br /><br />first commit<br /><br />现在能看到 git log 的样子了。<br /><br />再创建两个 commit。<br /><br />$ echo &#8216;second commit&#8217; | git commit-tree 0155eb -p 9fa147d91<br />b951b414a680c16e48c095a080b435874a5da79e<br /><br />$ echo &#8216;third commit&#8217;&nbsp; | git commit-tree 3c4e9c -p b951b414<br />29c0bd0cd9084ad30e0eaca38632500d700d6644<br /><br />这两个 commit 都用 -p 指定了 parent commit。第一个 commit 没有指定 parent，所以是一个 initial commit，或者 root commit，或者 orphan commit。<br /><br />现在已经能用 git log 了。<br /><br />$ git log &#8211;stat 29c0bd0<br />commit 29c0bd0cd9084ad30e0eaca38632500d700d6644<br />Author: Jesse Zhang &lt;xxx&gt;<br />Date:&nbsp;&nbsp; Mon Apr 16 17:54:25 2012 +0800<br /><br />&nbsp;&nbsp;&nbsp; third commit<br /><br />bak/test.txt |&nbsp;&nbsp;&nbsp; 1 +<br />1 files changed, 1 insertions(+), 0 deletions(-)<br /><br />commit b951b414a680c16e48c095a080b435874a5da79e<br />Author: Jesse Zhang &lt;xxx&gt;<br />Date:&nbsp;&nbsp; Mon Apr 16 17:54:12 2012 +0800<br /><br />&nbsp;&nbsp;&nbsp; second commit<br /><br />new.txt&nbsp; |&nbsp;&nbsp;&nbsp; 1 +<br />test.txt |&nbsp;&nbsp;&nbsp; 2 +-<br />2 files changed, 2 insertions(+), 1 deletions(-)<br /><br />commit 9fa147d9147f7dbbfa641ebe2992a336bc1a4c93<br />Author: Jesse Zhang &lt;xxx&gt;<br />Date:&nbsp;&nbsp; Mon Apr 16 17:51:03 2012 +0800<br /><br />&nbsp;&nbsp;&nbsp; first commit<br /><br />test.txt |&nbsp;&nbsp;&nbsp; 1 +<br />1 files changed, 1 insertions(+), 0 deletions(-)<br /><br /><br />7. git references<br /><br />使用上边提到的 objects，已经可以建立起完整的历史了，但是所有的 key 都是 sha1 值，不便使用。利用 references，或者 refs，可以把 sha1 值与一个名字联系起来。<br /><br />一个 ref 是一个简单的文本文件，其内容是一个 sha1 值，表示这个 ref 指向的 object。<br /><br />$ find .git/refs<br />.git/refs<br />.git/refs/heads<br />.git/refs/tags<br /><br />$ find .git/refs -type f<br />$<br /><br />新建一个 “head reference” (ref 还分好几种，这种指向某个 commit 的叫 head ref，放在 .git/refs/heads/ 目录下)，让它指向最新的 commit：<br /><br />echo &#8220;29c0bd0cd9084ad30e0eaca38632500d700d6644&#8221; &gt; .git/refs/heads/master<br /><br />这样就用这个 ref 指代其 sha1 值了：<br /><br />$ git log &#8211;pretty=oneline master<br />29c0bd0cd9084ad30e0eaca38632500d700d6644 third commit<br />b951b414a680c16e48c095a080b435874a5da79e second commit<br />9fa147d9147f7dbbfa641ebe2992a336bc1a4c93 first commit<br /><br />git 不推荐手动修改 ref 文件。可以使用 update-ref 命令：<br /><br />$ git update-ref refs/heads/master 29c0bd0cd9084ad30e0eaca38632500d700d6644<br /><br />这其实就是 git 里的 branch，一个很简单的指向某个 commit 的指针，或者说 ref。<br /><br />用 update-ref 创建一个新 branch：<br /><br />$ git update-ref refs/heads/test b951b414a<br /><br />$ git log &#8211;pretty=oneline test<br />b951b414a680c16e48c095a080b435874a5da79e second commit<br />9fa147d9147f7dbbfa641ebe2992a336bc1a4c93 first commit<br /><br />repo 现在的结构：<br /><br />并且可以使用 git branch 了，已经有了两个分支：<br /><br />$ git branch<br />* master<br />&nbsp;test<br /><br />在创建新分支的时候，git 仅仅是创建了一个简单的 ref，把新分支名与指定的 commit 联系起来。所以创建分支是个非常 cheap 的。<br /><br />那么 merge 分支呢？<br /><br />1). git 拿到要进行合并的分支，找到它们所代表的 sha1 值，<br />2). 读取这两个 sha1 值所表示的 commit object，<br />3). 读取这两个 commit 对应的 tree object。<br />4). 创建新 tree。把这两个 tree 合并到一块，产生一个新的 tree (以及可能的新的 blob)<br />5). 创建 merge commit。把这个 tree 提交为新的 commit，就是最后的 merge commit。merge commit&nbsp; 有多个 parent。<br /><br />8. HEAD<br /><br />git 怎么知道当前的最新 commit 呢？或者说怎么知道当前所在的分支？<br /><br />HEAD 文件用来记录当前的分支。HEAD 也是一个 ref，但是它称作一个 symbolic ref，因为 HEAD 文件里不是包含 sha1 值，而是指向另外一个 ref (refs 目录里的一个文件)。<br /><br />$ cat .git/HEAD ref: refs/heads/master<br /><br />执行 git checkout &lt;branch&gt; 的时候，git 就会更新 HEAD 文件，切换到新分支。<br /><br />执行 git commit 的时候，新创建的 commit object 的 parent 就是 HEAD 所指向的那个 commit。<br /><br />可以使用 git symbolic-ref 来读取或修改 HEAD 这种 symbolic ref：<br /><br />$ git symbolic-ref HEAD<br />refs/heads/master<br /><br />$ git symbolic-ref HEAD refs/heads/test<br />$ cat .git/HEAD<br />ref: refs/heads/test<br /><br />9. 不讲<br /><br />tags, remotes<br /><br />10. 资料<br /><br />本文所有内容来自 Pro Git 第 9 章，Git Internals, http://progit.org/book/ch9-0.html。<br /><br />其他资料：<br /><br />git 手册的 Hack git 那一章，http://schacon.github.com/git/user-manual.html#hacking-git 。尤其是其中提到的 git 的第一个 commit:<br /><br />$ git checkout e83c5163</div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[gnome3]]></title>
    <link href="http://blog.zhangsen.org/2012/04/gnome3.html"/>
    <updated>2012-04-17T20:53:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/04/gnome3</id>
    <content type="html"><![CDATA[<div class='post'>
gnome3 大多数时候还是比较顺手的。不顺手的地方基本也能找到相应的插件。<br /><br />1. alt-tab 把窗口都组合到一块去，这个比较恶。幸好有一些插件，比如 <a href="https://extensions.gnome.org/extension/38/windows-alt-tab/">https://extensions.gnome.org/extension/38/windows-alt-tab/</a> 。能恢复到原来的样子。win7 在任务栏上把窗口也组合到一块，也很烦人，但是起码 alt-tab 是正常的。(而且人家有配置选项好吧？）<br /><br />2. 关机按钮。这个就不多说了，相应的插件目前在 https://extensions.gnome.org 上排第一位。(<a href="https://extensions.gnome.org/extension/5/alternative-status-menu/">https://extensions.gnome.org/extension/5/alternative-status-menu/</a>)<br /><br />3. 现在的网络管理简直就是个鸡肋，想配 VPN 都没地方找。不过幸好，还可以从命令行运行原来的：<br /><br /><div style="font-family: &quot;Courier New&quot;,Courier,monospace;">$ nm-connection-editor</div><br />然后就可以照常导入 vpnc 的配置文件了，等等。<br /><br />最后再强调一下，gnome3 大多数时候还是比较顺手的 :) 确实能让人感觉更专注，支持插件也是个很棒的设计。</div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[kindle and wifi]]></title>
    <link href="http://blog.zhangsen.org/2012/04/kindle-and-wifi.html"/>
    <updated>2012-04-16T20:17:00+08:00</updated>
    <id>http://blog.zhangsen.org/2012/04/kindle-and-wifi</id>
    <content type="html"><![CDATA[<div class='post'>
The Kindle was designed in the USA and so uses only the US Wi-Fi channels one to 11<br /><br /><a href="http://www.pcpro.co.uk/realworld/370147/why-your-kindle-wont-connect-to-wi-fi">http://www.pcpro.co.uk/realworld/370147/why-your-kindle-wont-connect-to-wi-fi</a><br /><br />怪不得我的 kindle 死活连不上 wifi。只好把自动扫描信道关了，选个 11 以内的。</div>
]]></content>
  </entry>
  
</feed>
