<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>旧日重来</title>
    <link>http://live.aulddays.com/</link>
    <description>生活·技术·点滴</description>
    <language>zh_CN</language>
    <copyright>Copyright (C) 2007-2019 by 旧日重来</copyright>
    <generator>Feed Writer from live.aulddays.com</generator>
    <pubDate>Sat, 03 Aug 2019 00:03:35 +0800</pubDate>
    <item>
      <title>Firefox 68+ 怎样关闭多进程</title>
      <description>
        <![CDATA[<p>太长不看版：<a href="http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm#solution">查看解决方案</a></p>

<p>从 Firefox 68.0 开始，Mozilla <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1548941" target="_blank">强制开启了多进程功能</a>，同时之前 Aulddays 介绍的 <a href="http://live.aulddays.com/tech/19/firefox-tab-suspend.htm#multiprocess">about:config 方法</a> 也随之失效了。虽然 Mozilla 宣称多进程模式（或者所谓的 e10s / electrolysis）有很多好处，例如 性能和稳定性（主要在插件、视频播放相关功能上），但多进程显而易见的占用了更多的系统资源，并且系统在多进程切换时的开销更大，并不一定能被所谓的&ldquo;好处&rdquo;抵消。</p>

<p>另一方面，相对较少的资源占用也是 <a href="http://live.aulddays.com/">Aulddays</a> 坚持 Firefox 的几乎唯一原因（强大的插件曾经是另一个原因，但已经被 Mozilla 脑子抽筋自废掉了）。打开几十个 tab 基本是家常便饭，在 Chrome 下经常好几个 G 的内存就被吃掉了，并且关闭了 tab 也释放不了。Firefox 强制多进程，资源占用也跟着蹭蹭上去，似乎也没有理由阻止迁移到 Chrome 的脚步了。</p>

<p style="text-align: center;"><a href="http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm"><img alt="Firefox 多进程" src="http://liveres.aulddays.com/firefox-multi-process.png" style="width: 383px; height: 200px;" /></a></p>

<p>根据<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1548941#c0">官方的描述</a>，在 Firefox 68.0 版本之后，<strong>除非是以下这几种情况</strong>，否则强制多进程模式：</p>

<ol>
	<li>未定义 MOZ_BUILD_APP_IS_BROWSER（例如手机版的 Firefox）</li>
	<li>在内部自动化测试任务中</li>
	<li>第三方编译版本</li>
</ol>

<p>第 1 条不使用 PC 使用的场景；第 3 条自己编译的话成本太高，使用别人发布的版本（还得有人愿意做）安全性上也不放心。所以只剩下第 2 条或许可以实现。</p>

<p>查找了相关的信息之后，终于找到了通过模拟第 2 条以实现<a href="http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm#solution" id="solution" name="solution">禁用 Firefox 多进程模式的方法</a>：</p>

<pre>
定义 MOZ_FORCE_DISABLE_E10S 环境变量，并将值设为 1</pre>

<p>对 Linux 大多数用户来说，设置 <u>环境变量</u> 可能是一项常规操作（例如 profile/bashrc）。但对于 Windows 用户来说，可能就没那么熟悉了。这里针对 Windows 系统详细说明一下：</p>

<h3>1. 打开&ldquo;系统属性&rdquo;对话框</h3>

<h4>情况1：Windows 7</h4>

<div style="background: rgb(238, 238, 238) none repeat scroll 0% 0%; border: 1px solid rgb(204, 204, 204); padding: 5px 10px; margin-left: 40px;">控制面板 - 系统和安全 - 系统 - 点击窗口左边的&ldquo;高级系统设置&rdquo;</div>

<h4>情况2：Windows 10</h4>

<p>Windows 10 开始，MS 故意把控制面板隐藏的越来越深，最新更新的 Windows 10 中很难找到。当然方法还是有的，这里举几个选项</p>

<div style="background: rgb(238, 238, 238) none repeat scroll 0% 0%; border: 1px solid rgb(204, 204, 204); padding: 5px 10px; margin-left: 40px;">
<ol>
	<li>点击 开始，键盘输入&ldquo;控制面板&rdquo;，之后同 Windows 7</li>
	<li>点击 开始，在程序列表中找到：Windows 系统 - 控制面板，之后同 Windows 7</li>
	<li>打开一个文件夹浏览的窗口 -&gt; 右键左边的&ldquo;此电脑&rdquo; -&gt; 点击窗口左边的&ldquo;高级系统设置&rdquo;</li>
</ol>
</div>

<p style="text-align: center;">&nbsp;<a href="http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm"><img alt="系统属性 对话框" src="http://liveres.aulddays.com/firefox-system-env1.png" style="width: 420px; height: 442px;" /></a></p>

<h3>2. 打开&ldquo;环境变量&rdquo;对话框</h3>

<p>在&ldquo;系统属性&rdquo;中，点击顶部的&ldquo;高级&rdquo;，再点击下面的&ldquo;环境变量&rdquo;按钮</p>

<h3>3. 设置环境变量</h3>

<p>点击&ldquo;新建&rdquo;，再填入 变量名：<code>MOZ_FORCE_DISABLE_E10S</code>；变量值：<code>1</code></p>

<p style="text-align: center;"><a href="http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm"><img alt="环境变量 对话框" src="http://liveres.aulddays.com/firefox-system-env-new.png" style="width: 394px; height: 406px;" /></a></p>

<h3>4. 重启 Firefox，大功告成</h3>
<p>查看：<a href="http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm</guid>
      <pubDate>Thu, 01 Aug 2019 23:09:16 +0800</pubDate>
    </item>
    <item>
      <title>解决 Firefox 切换后台 tab 空白并自动刷新</title>
      <description>
        <![CDATA[<p>太长不看版：<a href="http://live.aulddays.com/tech/19/firefox-tab-suspend.htm#solution">查看解决方案</a></p>

<p>Firefox 更新到 67.0 版之后，经常发生一个奇怪的现象：打开多个 tab 后，进行切换时，有时会发现原先打开的 tab 变成了空白，切换到前台之后需要再重新刷新。</p>

<p>这个问题经常会造成麻烦，一方面要花费时间等待网页重新加载，还浪费流量；另一方面如果网页包含动态内容，则很可能加载不全或失败；更麻烦的是，如果是在输入一段文字，而网页后台又没有自动保存，则很有可能之前的写的文字都消失不见了，<a href="http://live.aulddays.com/">aulddays</a> 可就被坑惨过。</p>

<p>怎么解决这个问题呢？</p>

<p style="text-align: center;"><a href="http://live.aulddays.com/tech/19/firefox-tab-suspend.htm"><img alt="firefox tab 切换刷新" height="307" src="http://liveres.aulddays.com/Firefox-suspend-tab1.png" width="458" /></a></p>

<p>查看 <a href="https://www.mozilla.org/en-US/firefox/67.0/releasenotes/" target="_blank">Firefox 67.0 的更新说明</a> 可以发现，这个版本引入了一个叫做&ldquo;Suspending unused tabs&rdquo;的功能，翻译成中文大概叫做&ldquo;终止空闲的 tab&rdquo;。进一步查看这个功能的详细说明，可以发现其大概的逻辑如下：</p>

<ol>
	<li>功能的背景是：用户经常打开很多 tab，但计算机资源往往有限，希望实现在这种情况下不要影响操作系统的正常运行</li>
	<li>当 Firefox 检测到系统空闲内存低于 400M 时会触发</li>
	<li>触发时会&ldquo;终止&rdquo;一段时间没有使用/查看的 tab</li>
	<li>没有正在播放视频/音频的网页会被优先中止，如果内存占用仍然很高，则视频/音频也会被终止</li>
	<li>据说前期有不少用户反馈 Firefox 内存占用较高，于是开发了这个功能</li>
</ol>

<p>看到这里，<a href="http://live.aulddays.com/">aulddays</a> 不由的也要吐槽一下，我的电脑空闲内存明明还很多，妥妥的稳定高于 2G 以上，为什么还老是被终止呢？是不是程序员小哥开发时走了神。</p>

<p>不过最后，我们还是找到了解决方法：<a href="http://live.aulddays.com/tech/19/firefox-tab-suspend.htm#solution" id="solution" name="solution">禁用 suspending unused tabs</a>：</p>

<ol>
	<li>在 Firefox 地址栏输入：<code>about:config</code></li>
	<li>如果出现了警示的信息，点击&ldquo;我保证小心 / I accept the risk&rdquo;</li>
	<li>在出现的搜索框中输入&ldquo;lowmemory&rdquo;</li>
	<li>双击下面的 <code>browser.tabs.unloadOnLowMemory</code> 选项，使最右边的 value 设置为 <code>false</code></li>
	<li>重启 Firefox</li>
</ol>

<p style="text-align: center;"><a href="http://live.aulddays.com/tech/19/firefox-tab-suspend.htm"><img alt="禁用firefox的终止tab" height="247" src="http://liveres.aulddays.com/disable-firefox-suspend-tab1.png" width="476px" /></a></p>

<p>补充，新版的 Firefox 默认启用了<a id="multiprocess" name="multiprocess">多进程模式</a>，资源占用明显增加，要禁用这个功能，把 <code>browser.tabs.remote.autostart</code> 设置为 <code>false</code>（如果不存在，则新建一个）</p>

<p>-------------------分割线------------------</p>

<p>再更新：Firefox 68.0 开始，忽略一切 about:config 关于多进程的设置，强制开启。点击这里查看：<a href="http://live.aulddays.com/tech/19/firefox-disable-multiprocess.htm">新版 Firefox 禁用多进程的方法</a></p>
<p>查看：<a href="http://live.aulddays.com/tech/19/firefox-tab-suspend.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/19/firefox-tab-suspend.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/19/firefox-tab-suspend.htm</guid>
      <pubDate>Sat, 27 Jul 2019 22:35:57 +0800</pubDate>
    </item>
    <item>
      <title>解决 Windows 文件共享“找不到用户名”</title>
      <description>
        <![CDATA[<p>一个共享文件夹，使用一直正常，有一天突然提示报错：</p>

<blockquote>
<p>\\<a href="http://live.aulddays.com/">live.aulddays.com</a> 无法访问。您可能没有权限使用网络资源。请与这台服务器的管理员联系以查明您是否有访问权限。</p>

<p>找不到用户名。</p>
</blockquote>

<p style="text-align: center;"><a href="http://live.aulddays.com/tech/17/share-username-could-not-be-found.htm"><img alt="找不到用户名" src="http://liveres.aulddays.com/share-username-could-not-be-found.png" style="width: 576px; height: 179px;" /></a></p>

<p>报错信息让人摸不着头脑。追查了一下，似乎通常出现在域环境中。由于域用户的信息发生了变化或密码过期，造成本地记录的登录失效所致。</p>

<p>不过很令人费解的是 Windows 分明已经检查出是用户信息有误，但却仅仅弹个框报个错完事，而不给用户检查修改的机会。另一方面，相关的设置在系统里埋藏的很深，一般用户又很难找到。实在是一个非常失败的产品设计。</p>

<p>要解决这个问题，如果是 Windows 7 系统，首先需要进入<a href="http://live.aulddays.com/tech/17/share-username-could-not-be-found.htm">凭据管理器</a>，有两种方法：<br />
1. 打开 控制面板 -&gt; 用户账户 -&gt; 凭据管理器<br />
2. 打开&ldquo;开始&rdquo;菜单，键盘输入 &quot;cred&quot;，选择&ldquo;管理 Windows 凭据&rdquo;，如图</p>

<p style="text-align: center;"><a href="http://live.aulddays.com/tech/17/share-username-could-not-be-found.htm"><img alt="凭据管理器" src="http://liveres.aulddays.com/share-username-could-not-be-found-start.png" style="width: 411px; height: 352px;" /></a></p>

<p>在凭据管理器中，可以找到相应的用户信息，进行编辑即可。如果编辑之后仍有问题，可以完全删除，这样再次尝试打开共享文件夹时就会弹出填写登录信息的对话框了。</p>

<p style="text-align: center;"><a href="http://live.aulddays.com/tech/17/share-username-could-not-be-found.htm"><img alt="凭据管理器" src="http://liveres.aulddays.com/share-username-could-not-be-found-cred.png" style="width: 687px; height: 623px;" /></a></p>
<p>查看：<a href="http://live.aulddays.com/tech/17/share-username-could-not-be-found.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/17/share-username-could-not-be-found.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/17/share-username-could-not-be-found.htm</guid>
      <pubDate>Mon, 27 Nov 2017 19:49:31 +0800</pubDate>
    </item>
    <item>
      <title>概率加权的随机抽样 (Weighted Random Sampling) – A-Res 蓄水池算法</title>
      <description>
        <![CDATA[<p>最近，<a href="http://live.aulddays.com">Aulddays</a> 遇到一个随机抽样任务。有一个对象集合，由于整个集合非常大，希望考虑每个对象的热门程度抽样出一部分对象来进行分析。把这个任务抽象出来，其实就对应了一个带概率加权的随机抽样 (<a href="http://live.aulddays.com/tech/17/weighted-random-sampling-reservoir-algorithm.htm">Weighted Random Sampling</a>) 问题。对应到不同的应用场景，可以对应解决搜索query抽样、商品抽样、网页抽样等任务。对于不加权的普通随机抽样，其实并不难解决，在样本集合非常大的情况下，还可以使用经典的蓄水池算法 (<a href="https://en.wikipedia.org/wiki/Reservoir_sampling" target="_blank">Reservoir Sampling</a>) 来高效实现的抽样。但对于带概率加权的情况，就不太容易了，特别的，在 Aulddays 的任务中，结果集合中每个对象只有1次，对应了无放回的情况，则更困难一些。在内存足够的情况下，似乎还能想到接近于 O(<em>m</em>log<em>n</em>) 的思路，但大数据上就没什么好的思路了。Survey 了一番之后，发现这个问题其实已经被研究了很久，但直到 2006 年才得到较好的解决：<a href="http://www.sciencedirect.com/science/article/pii/S002001900500298X" target="_blank">Pavlos S. Efraimidis and Paul G. Spirakis, 2006, Weighted random sampling with a reservoir</a>。这里介绍一下他们提出的 <a href="http://live.aulddays.com/tech/17/weighted-random-sampling-reservoir-algorithm.htm">A-Res 算法</a>。</p>

<h2>问题定义</h2>

<h3>(简单)随机抽样</h3>

<p>给定 <em>n</em> 个样本（样本集合），从中随机抽出 <em>m</em> 个样本（抽样集合），样本集合中每个样本出现在抽样集合的概率相等（=<em>m</em>/<em>n</em>）。</p>

<h3>加权随机抽样</h3>

<p>样本集合中每个样本附加一个权重 <em>w<sub>i</sub></em> &gt;0，每个样本被抽中的概率由 <em>w<sub>i</sub></em> 确定。<em>w<sub>i</sub></em> 有两种指定方法：</p>

<ol>
	<li>概率值，即所有样本的权重 <em>w<sub>i</sub></em> 加和为 1</li>
	<li>相对权重，<em>w<sub>i</sub></em> 只表明样本之间被抽中概率的相对关系，但事先并不知道每个样本具体的概率是多少。在大数据的场景下，这个是更常见的情况</li>
</ol>

<p>显然，概率值是相对权重的一个特例，解决了相对权重的情况，拿概率值作为输入也是直接可运行的。</p>

<h3>有放回/无放回 (with/without replacement)</h3>

<p>顾名思义，无放回就是被抽中之后就不会被再次加入候选，也就是一个样本在最终的抽样结果中最多只出现1次。有放回则可能在结果中出现多次。更一般的，还可以定义 <em>k</em>-1-放回，即一个样本最多被抽中 <em>k</em> 次（被放回 <em>k</em>-1 次）。不难看出，无放回对应 <em>k</em>=1，而有放回对应 <em>k</em>=<em>m</em> (<em>m</em>是抽样集合的大小)<br />
这里首先重点研究无放回的情况。对于<a href="http://live.aulddays.com/tech/17/weighted-random-sampling-reservoir-algorithm.htm#replacement">有放回的情况</a>，可以在无放回算法的基础上做简单扩展来解决。</p>

<h2>A-Res 算法</h2>

<p>A-Res 算法也利用了蓄水池 (reservoir) 的思想来解决大数据的问题，也就是说，事先不用知道样本集合的大小 <em>n</em> 和样本权重的情况（i.e. 权重之和），只需要依次遍历一次样本集合即可以得到结果，空间复杂度是 O(<em>m</em>)，正比于结果集合的大小。<br />
出人意料的，A-Res 算法相当简洁，只需要计算和记录一个参数即可。具体算法如下：</p>

<pre class="SrcCode">
<u><strong>Algorithm</strong></u><span class="sy0">:</span> A<span class="sy0">-</span>Res
<u><strong>Input</strong></u><span class="sy0">:</span> 样本序列 V，长度未知，第 i 个样本 v<sub>i</sub> 的权重为 w<sub>i</sub>
<u><strong>Output</strong></u><span class="sy0">:</span> 长度为 m 的结果集合 R
&nbsp;
<span class="kw1">foreach</span> v<sub>i</sub> in V <span class="br0">(</span>i <span class="sy0">=</span> <span class="nu0">1</span><span class="sy0">,</span> <span class="nu0">2</span><span class="sy0">,</span> <span class="sy0">...</span><span class="br0">)</span><span class="sy0">:</span>
   k<sub>i</sub> <span class="sy0">=</span> <span class="kw3">rand</span><span class="br0">(</span><span class="nu0">0</span><span class="sy0">,</span> <span class="nu0">1</span><span class="br0">)</span> ^ <span class="br0">(</span><span class="nu0">1</span> <span class="sy0">/</span> w<sub>i</sub><span class="br0">)</span>
   <span class="kw1">if</span> i <span class="sy0">&lt;=</span> m<span class="sy0">:</span>
      <span class="br0">(</span>v<sub>i</sub><span class="sy0">,</span> k<sub>i</sub><span class="br0">)</span> 加入 R
   <span class="kw1">else</span><span class="sy0">:</span>
      <span class="br0">(</span>v<sub>t</sub><span class="sy0">,</span> k<sub>t</sub><span class="br0">)</span> <span class="sy0">=</span> <span class="kw3">min</span> <sub>k</sub> &isin; R <span class="co1">// <a href="http://live.aulddays.com/">Aulddays</a>: 选出 R 中 k 最小的那个样本</span>
      <span class="kw1">if</span> k<sub>i</sub> <span class="sy0">&gt;</span> k<sub>t</sub><span class="sy0">:</span>
         <span class="br0">(</span>v<sub>i</sub><span class="sy0">,</span> k<sub>i</sub><span class="br0">)</span> 替换 <span class="br0">(</span>v<sub>t</sub><span class="sy0">,</span> k<sub>t</sub><span class="br0">)</span></pre>

<blockquote><span style="color:#ff0000;"><strong>提示</strong></span>：当 <em>w<sub>i</sub></em> 值为一般权重而非概率值时，可能会是一个很大的数值，从而使得 <em>k<sub>i</sub></em> 的指数操作可能会丢失精度。这种情况下可以对 <em>k<sub>i</sub></em> 取 log() 而变成 <code><em>k<sub>i</sub></em> = log(rand(0, 1)) / <em>w<sub>i</sub></em></code>，因为后续在各个 <em>k<sub>i</sub></em> 之间只涉及比较相对大小而不是绝对值，所以可以保证精度的同时不影响结果。</blockquote>

<p>循环中的每一轮需要查找/更新蓄水池中 <code>k<sub>i</sub></code> 特征值的最小值，显然这里用一个<a href="http://en.wikipedia.org/wiki/Heap_(data_structure)">最小堆</a>来维护是一个较优化的选择。于是整个算法的时间复杂度就是 <code>O(m*log(n))</code></p>

<p>显然，整个算法的核心在于 <code><em>k<sub>i</sub></em> = (rand(0, 1)) ^ (1 / <em>w<sub>i</sub></em>)</code> 这个特征值，这里做一个简单<a href="http://live.aulddays.com/tech/17/weighted-random-sampling-reservoir-algorithm.htm#proof" id="proof" name="proof">证明</a>：</p>

<ul>
	<li>令 <code>U<sub>1</sub></code>, <code>U<sub>2</sub></code> 为两个相互独立的随机变量且均服从 [0, 1] 区间上的均匀分布</li>
	<li>令 <code>k<sub>1</sub> = (U<sub>1</sub>)<sup>1/w<sub>1</sub></sup></code>, <code>k<sub>2</sub> = (U<sub>2</sub>)<sup>1/w<sub>2</sub></sup></code>, 其中 <code>w<sub>1</sub>, w<sub>2</sub> &gt; 0</code> （在算法中 <code>w<sub>1</sub></code>, <code>w<sub>2</sub></code> 就是对应目标样本的权重）</li>
	<li>可以推出概率关系：<code>P(k<sub>1</sub>&le;k<sub>2</sub>) = w<sub>2</sub>/(w<sub>1</sub>+w<sub>2</sub>)</code></li>
	<li>因此比较 <code>k<sub>i</sub></code> 特征值确实实现了按 <code>w<sub>i</sub></code> 的概率加权随机抽样</li>
</ul>

<h2><a id="replacement" name="replacement">扩展（分布式、有放回）</a></h2>

<p>因为所有数据只需过一轮，A-Res 可以比较容易的扩展到并行或分布式的情况。</p>

<p>对于<span style="color:#c0392b;">有放回</span>的情况，只需要这样进行修改：创建 <em>m</em> 个大小为 1 的蓄水池，对于每个样本 <em>v<sub>i</sub></em>，分别在每个蓄水池上独立的运行 A-Res 算法。</p>
<p>查看：<a href="http://live.aulddays.com/tech/17/weighted-random-sampling-reservoir-algorithm.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/17/weighted-random-sampling-reservoir-algorithm.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/17/weighted-random-sampling-reservoir-algorithm.htm</guid>
      <pubDate>Mon, 20 Nov 2017 18:51:10 +0800</pubDate>
    </item>
    <item>
      <title>phpMyAdmin 启用 mcrypt #1045 无法登录 MySQL 服务器 Cannot log in to the MySQL server</title>
      <description>
        <![CDATA[<p>安装好了 phpMyAdmin，运行正常，但登录之后提示建议启用 mcrypt。按照提示一路 <code>yum install php-mcrypt</code>，启用后却发现无法登录了，提示 <a href="http://live.aulddays.com/tech/17/phpmyadmin-mcrypt-1045.htm" target="_blank">#1045 Cannot log in to the MySQL server</a> （<a href="http://live.aulddays.com/tech/17/phpmyadmin-mcrypt-1045.htm" target="_blank">#1045 无法登录 MySQL 服务器</a>）。简单排查未发现明显问题，只好通过 debug 源码解决，记录在这里</p>

<p>出现这个错误一般有以下几类原因：</p>

<ol>
	<li>MySQL 服务未启动或异常</li>
	<li>php-mysql/php-mysqli/php-mysqlnd 库和配置文件不匹配</li>
	<li>连接配置有误，如 ip 地址/Linux socket地址不匹配。特别的，如果是支持 IPv6的机器（新版系统一般都支持），则监听地址配了 127.0.0.1 而 phpMyAdmin 配的是 localhost 则可能无法连接</li>
</ol>

<p>由于安装 php-mcrypt 之前是可以登录的，所以肯定不是上面几个常见原因。而 phpMyAdmin 又没有打印有用的出错日志，一时没有头绪。只好从源代码入手开始 debug 了。</p>

<p>phpMyAdmin 的代码很复杂，为快速入手，首先在所有源代码中搜索出错信息的字符串，很快就在 <code>./libraries/plugins/auth/AuthenticationCookie.class.php</code> 里发现了：</p>

<pre class="language-php SrcCode">
<span class="kw2">public</span> <span class="kw2">function</span> authFails<span class="br0">(</span><span class="br0">)</span><span class="sy0">:</span>
   <span class="co1">// Other stuff</span>
   <span class="re0">$conn_error</span> <span class="sy0">=</span> <span class="st_h">&#39;#&#39;</span> <span class="sy0">.</span> <span class="re0">$GLOBALS</span><span class="br0">[</span><span class="st_h">&#39;errno&#39;</span><span class="br0">]</span> <span class="sy0">.</span> <span class="st_h">&#39; &#39;</span>
      <span class="sy0">.</span> __<span class="br0">(</span><span class="st_h">&#39;Cannot log in to the MySQL server&#39;</span><span class="br0">)</span><span class="sy0">;</span></pre>

<p><code>authFails()</code> 函数本身是一个出错时打印信息的回调，而非真正出错的地方，所以并没有提供什么有用的信息。不过从这里入手可以进一步定位，php 内部提供了获取函数 callstack (调用栈) 的功能，所有只要在 <code>authFails()</code> 内部查看 callstack，就可以进一步找到出错的地点了。简单起见，这里直接调用 <code>debug_print_backtrace()</code> 来实现：</p>

<pre class="language-php SrcCode">
<span class="kw2">public</span> <span class="kw2">function</span> authFails<span class="br0">(</span><span class="br0">)</span><span class="sy0">:</span>
   <span class="co1">// Other stuff</span>
   debug_print_backtrace<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>    <span class="co1">// 打印 callstack，added by <a href="http://live.aulddays.com/" target="_blank">Aulddays</a></span>
   <span class="re0">$conn_error</span> <span class="sy0">=</span> <span class="st_h">&#39;#&#39;</span> <span class="sy0">.</span> <span class="re0">$GLOBALS</span><span class="br0">[</span><span class="st_h">&#39;errno&#39;</span><span class="br0">]</span> <span class="sy0">.</span> <span class="st_h">&#39; &#39;</span>
      <span class="sy0">.</span> __<span class="br0">(</span><span class="st_h">&#39;Cannot log in to the MySQL server&#39;</span><span class="br0">)</span><span class="sy0">;</span></pre>

<blockquote><strong><span style="color:#e74c3c;">注意</span></strong>，不要在生产环境中直接调用 <code>debug_print_backtrace()</code>，它把信息直接打印到网页上，可能会暴露内部信息。生产环境的话可以考虑用 <code>debug_backtrace()</code> 获取再通过 <code>error_log()</code> 之类打印到仅内部可见的地点</blockquote>

<p>callstack 如下：</p>

<pre class="language-delphi SrcCode">
<span class="re1">#0</span> AuthenticationCookie<span class="sy3">-</span>&gt;authFails<span class="br0">(</span><span class="br0">)</span> called <span class="kw1">at</span>
          <span class="br0">[</span><span class="sy3">/</span><a href="http://live.aulddays.com/">live.aulddays.com</a><span class="sy3">/</span>pma<span class="sy3">/</span>libraries<span class="sy3">/</span>dbi<span class="sy3">/</span>mysql<span class="sy1">.</span><span class="me1">dbi</span><span class="sy1">.</span><span class="me1">lib</span><span class="sy1">.</span><span class="me1">php</span><span class="sy1">:</span><span class="nu0">179</span><span class="br0">]</span>
<span class="re1">#1</span> PMA_DBI_connect<span class="br0">(</span><span class="sy1">,</span> testp<span class="sy1">,</span> <span class="br0">)</span> called <span class="kw1">at</span>
          <span class="br0">[</span><span class="sy3">/</span><a href="http://live.aulddays.com/">live.aulddays.com</a><span class="sy3">/</span>pma<span class="sy3">/</span>libraries<span class="sy3">/</span>common<span class="sy1">.</span><span class="me1">inc</span><span class="sy1">.</span><span class="me1">php</span><span class="sy1">:</span><span class="nu0">978</span><span class="br0">]</span>
<span class="re1">#2</span> require_once<span class="br0">(</span><span class="sy3">/</span>live.aulddays.com<span class="sy3">/</span>pma<span class="sy3">/</span>libraries<span class="sy3">/</span>common<span class="sy1">.</span><span class="me1">inc</span><span class="sy1">.</span><span class="me1">php</span><span class="br0">)</span> called <span class="kw1">at</span>
          <span class="br0">[</span><span class="sy3">/</span><a href="http://live.aulddays.com/">live.aulddays.com</a><span class="sy3">/</span>pma<span class="sy3">/</span>index<span class="sy1">.</span><span class="me1">php</span><span class="sy1">:</span><span class="nu0">11</span><span class="br0">]</span></pre>

<p>可以看见，实际的出错地点是 <code>PMA_DBI_connect()</code> 函数。再仔细观察，此函数的原型是：</p>

<pre class="language-php SrcCode">
<span class="kw2">function</span> PMA_DBI_connect<span class="br0">(</span><span class="re0">$user</span><span class="sy0">,</span> <span class="re0">$password</span><span class="sy0">,</span> <span class="sy0">...</span><span class="br0">)</span></pre>

<p>但 callstack 中实际参数只有 <code>$password</code>，而 <code>$user</code> 是空串，说明用户名在某个地方丢失了。接着查找，发现用户名是在下面这个函数中从浏览器 cookie 解析的：</p>

<pre class="language-php SrcCode">
<span class="kw2">public</span> <span class="kw2">function</span> cookieDecrypt<span class="br0">(</span><span class="re0">$encdata</span><span class="sy0">,</span> <span class="re0">$secret</span><span class="br0">)</span></pre>

<p>通过 firefox 的 developer tool 查看，站点 cookie 中 <code>pmaUser-1</code> 确实为空，而 <code>pmaAuth-1</code> 是有内容的：</p>

<p style="text-align: center;"><a href="http://live.aulddays.com/tech/17/phpmyadmin-mcrypt-1045.htm" target="_blank"><img alt="phpmyadmin mcrypt 1045 cookie" src="http://liveres.aulddays.com/phpmyadmin-mcrypt-1045-cookie.png" style="width: 438px; height: 56px;" /></a></p>

<p>说明在创建 cookie 时用户名就已经丢失。跟着上面的 <code>Decrypt()</code> 函数顺藤摸瓜，不难找到对应的 <code>Encrypt()</code> 函数：</p>

<pre class="language-php SrcCode">
<span class="kw2">public</span> <span class="kw2">function</span> cookieEncrypt<span class="br0">(</span><span class="re0">$data</span><span class="sy0">,</span> <span class="re0">$secret</span><span class="br0">)</span></pre>

<p>该函数功能很简单，代码就不贴了，具体就是看一下是否安装了 mcrypt，如果是就用 <code>mcrypt_encrypt()</code> 来加密，否则调用 phpMyAdmin 自带的加密函数来实现。查看文档，<code>mcrypt_encrypt()</code> 函数成功返回加密结果，失败返回 <code>false</code>。而 phpMyAdmin 在这里并未检查返回值，而 php 内部把 <code>false</code> 转为了空串造成问题。下面就继续看一下为什么出错。这里把 <code>cookieEncrypt()</code> 的相关内容单独提出来测试一下，创建 <code>test.php</code>，内容如下：</p>

<pre class="language-php SrcCode">
<span class="kw3">srand</span><span class="br0">(</span><span class="br0">(</span>double<span class="br0">)</span> <span class="kw3">microtime</span><span class="br0">(</span><span class="br0">)</span> <span class="sy0">*</span> <span class="nu0">1000000</span><span class="br0">)</span><span class="sy0">;</span>
<span class="re0">$td</span> <span class="sy0">=</span> <span class="kw3">mcrypt_module_open</span><span class="br0">(</span>MCRYPT_BLOWFISH<span class="sy0">,</span> <span class="st_h">&#39;&#39;</span><span class="sy0">,</span> MCRYPT_MODE_CBC<span class="sy0">,</span> <span class="st_h">&#39;&#39;</span><span class="br0">)</span><span class="sy0">;</span>
<span class="kw1">if</span> <span class="br0">(</span><span class="re0">$td</span> <span class="sy0">===</span> <span class="kw4">false</span><span class="br0">)</span> <span class="br0">{</span>
   PMA_fatalError<span class="br0">(</span>__<span class="br0">(</span><span class="st_h">&#39;Failed to use Blowfish from mcrypt!&#39;</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span>
<span class="br0">}</span>
<span class="re0">$iv</span> <span class="sy0">=</span> <span class="kw3">mcrypt_create_iv</span><span class="br0">(</span><span class="kw3">mcrypt_enc_get_iv_size</span><span class="br0">(</span><span class="re0">$td</span><span class="br0">)</span><span class="sy0">,</span> MCRYPT_RAND<span class="br0">)</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$result</span> <span class="sy0">=</span> <span class="kw3">mcrypt_encrypt</span><span class="br0">(</span>MCRYPT_BLOWFISH<span class="sy0">,</span>
   <span class="st0">&quot;asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdsadfasdf&quot;</span><span class="sy0">,</span>
   <span class="st0">&quot;test&quot;</span><span class="sy0">,</span> MCRYPT_MODE_CBC<span class="sy0">,</span> <span class="re0">$iv</span><span class="br0">)</span><span class="sy0">;</span></pre>

<p>运行发现报错如下：</p>

<pre>
PHP Warning:&nbsp; mcrypt_encrypt():
       Size of key is too large for this algorithm in /live.aulddays.com/test.php on line 8
PHP Catchable fatal error:
     &nbsp; mcrypt_encrypt(): Mcrypt initialisation failed in /live.aulddays.com/test.php on line 8</pre>

<p>加密的 key 太长导致加密失败。而这里的 key 就是 <code>config.ini.php</code> 中配置的 <code>$cfg[&#39;blowfish_secret&#39;]</code>。原来 <a href="http://live.aulddays.com/" target="_blank">Aulddays</a> 把这个 key 写的太长，造成 mcrypt 运行出错，改短之后就正常了。经过进一步测试，<strong><span style="color:#d35400;">mcrypt 可接受的最大长度是 56 字节</span></strong>。</p>

<p>有趣的是，phpMyAdmin 对这个字符串有最短长度的检查，而未作最大长度检查。另一方面，phpMyAdmin 内部实现的加密代码在长度超长时仍能正常运行。</p>
<p>查看：<a href="http://live.aulddays.com/tech/17/phpmyadmin-mcrypt-1045.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/17/phpmyadmin-mcrypt-1045.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/17/phpmyadmin-mcrypt-1045.htm</guid>
      <pubDate>Tue, 31 Oct 2017 14:29:08 +0800</pubDate>
    </item>
    <item>
      <title>GReader Archive - Google Reader 阅读历史存档下载工具</title>
      <description>
        <![CDATA[<p>Google Reader 将在<a href="http://www.cnbeta.com/articles/229697.htm" target="_blank"> 7月1日关闭</a>。虽然谷歌官方提供了<a href="http://www.google.com/takeout/#custom:reader" target="_blank">Take out</a>用于导出用户数据，但导出的范围仅限订阅列表、关注列表、加星或分享过的条目之类，而阅读过的大部分内容则很难找回来了。<a href="http://live.aulddays.com/tech/13/google-reader-archive-download.htm" target="_blank">GReader Archive</a> 这个工具可以让大家从 Google Reader 下载到订阅数据源的<strong>所有历史数据</strong>。</p>
<p>GReader Archive 基于网上流传的 <a href="http://undoc.in/googlereader.html" target="_blank">Google Reader 非官方 API</a>，使用 Python 开发、跨平台，按 GPL 协议开源。值的注意的是，经 <a href="http://live.aulddays.com/" target="_blank">Aulddays</a> 实验，谷歌的服务器对数据下载的 antispider 做的相当严格，所以程序中相应的对抓取速度做了限制，每下载一个数据文件（默认2000个条目一个数据文件）会等待 30 秒，因而总的抓取过程会比较长。例如像 Aulddays 这样重度（100+ subcription，已读 30w+ 条目）使用者，差不多花了 48 小时的时间。另外，随着 Google Reader 关闭的邻近，响应的下载接口可能随时也会关闭，所以需要下载数据的还要抓紧时间~~
<p class="Image"><a href="http://live.aulddays.com/tech/13/google-reader-archive-download.htm" target="_blank">
<img alt="GReader 重度使用者" src="http://liveres.aulddays.com/google-reader-archive-download-GReaderHist.png" height="212" width="396" /></a></p>
<h1>下载和使用</h1>
<p>点击<a href="https://github.com/Aulddays/GReader-Archive" target="_blank">这个链接</a>进入 GReader Archive github 页面；也可以点击<a href="https://github.com/Aulddays/GReader-Archive/archive/master.zip" target="_blank">这里</a>直接下载最新版的压缩包。</p>
<p>把下载的代码放在某个目录下，运行 run.py，提示输入 Google Reader 的账户信息，验证通过后即可开始下载。Linux 下一般已内建了对 Python 的支持。Windows 下可能需要另外安装 <a href="http://www.python.org/download/" target="_blank">Python 组件</a>后运行。Aulddays 在 Windows + Python 2.7 和 Linux + Python 2.6 下测试运行正常。Python 3.x 在语言上和 2.x 不太兼容，无法运行。</p>
<p>GReader Archive <strong>不直接支持</strong>启用了两步验证的账户。可以考虑<a href="http://live.aulddays.com/tech/13/google-reader-archive-download.htm#advanced">高级话题</a>里的方法来绕过（感谢 @myqing）。</p>
<h1>文件组织和硬盘空间</h1>
<p>GReader Archive 下载的所有数据都会保存在 <code>data</code> 文件夹下。每个 RSS 订阅源会创建一个单独的文件夹，该订阅源的数据会按 2000 条一个文件的方式切分放在文件夹下。如果您的 Google Reader 账户中对订阅做了文件夹的组织，那么 <code>data</code> 文件夹中也会建立相应的子文件夹</p>
<p>由于每个订阅都会下载 Google Reader 中的全部历史数据（这个<a href="http://live.aulddays.com/tech/13/google-reader-archive-download.htm#advanced">后面还会详说</a>），最终占用的硬盘空间可能比较大。 Aulddays 的 100+ 个订阅源共产生了 2GB+ 的数据。所以下载前最好留出一些硬盘空间</p>
<h1><a name="resuming">断点续传</a></h1>
<p>由于总下载时间会比较长，GReader Archive 支持断点续传。在下载过程中可以<strong>随时关闭程序</strong>。当下次启动时，如果输入的账户信息与之前相同的话，程序会给出下面的选项：</p>
<pre>
Unfinished download found. Continue (press c) or Start over again (press s)?:
</pre>
<p>此时选择 'c' 就可以继续上次未完成的下载了。</p>
<h1>Antispider</h1>
<p>谷歌的 Antispider 做的比较严，尽管程序里设置了抓取一个等待 30 秒，运行一段时间之后还是有可能被谷歌 Antispider 了，此时程序 log 里会打印如下信息：</p>
<pre>
2013-05-22 18:04:45,667 [INFO] Client IP antispidered. wait 1 hour and continue...
</pre>
遇到这种情况，可以有以下选择：
<ol>
<li>如果你的外网 IP 可以换（例如 ADSL，断线重连下有很大概率 IP 就换了），那么换新 IP 之后基本可以原地满血复活。如果不想等 1 个小时，那么可以配合上面的<a href="http://live.aulddays.com/tech/13/google-reader-archive-download.htm#resuming">断点续传</a>进行~~</li>
<li>如果 IP 是固定的，那么据经验看 4-5 个小时之后会解除 antispider，此后程序会自动继续</li>
<li>如果很久也没解除，建议关闭程序，等 24 小时后再<a href="http://live.aulddays.com/tech/13/google-reader-archive-download.htm#resuming">断点续传</a></li>
<li>如果频繁被 antispider，可以考虑把等待时间改成一些，在 run.py 开始有一行：<code>waittime = 30</code></li>
</ol>
<h1>下载速度</h1>
<p>某些朋友的网络访问谷歌可能不太顺畅，进而造成下载缓慢。GReader Archive 支持自定义谷歌服务器 IP，大家可以针对自己的情况将自定义 IP 写在 <code>conf/customip.list</code> 文件中，每行一个地址。有一个示例放在了 <code>conf/customip.list.sample</code>，里面的 IP 可能（仅是可能）改进某些地区的下载速度，将它重命名为 <code>customip.list</code> 即可生效。</p>
<p>作者建议慎用自定义 IP 的功能，因为使用了非官方的地址可能会造成下载失败甚至账户信息泄露。建议使用时配合下面的<a href="http://live.aulddays.com/tech/13/google-reader-archive-download.htm#advanced">高级话题</a>。</p>
<h1><a name="advanced">高级话题</a></h1>
<p>值得注意的是，对于一个 RSS 数据源，通过 GReader Archive 下载到的并不只是本账户阅读过的数据而是<strong>该 RSS 数据源的所有历史数据</strong>（更准确的说，是有任何一个用户在 Google Reader 上订阅了该数据源以来的所有数据）。也就是说，用任何一个账户登录都可以下载到 RSS 源的完整历史。基于这个特性，可以做一些有意思的事情，举例如下：</p>
<ol>
<li>由于 Google Reader 使用了非官方的 API，为了保证我们的正常账户不受影响，又或者您的账户启用了两步验证无法用本工具直接下载，可以考虑找个马甲账户来运行。马甲账户中也无需一个一个手动添加订阅，用 <a href="http://www.google.com/takeout/#custom:reader" target="_blank">Takeout</a> 导出再导入即可。</li>
<li>假设某个订阅源需要单独下载（例如之前下载失败或有更新想重新下载），也可以使用马甲用户来搞定。</li>
<li>有些以前的网站现在已经关闭了，但其 RSS 内容很可能在 Google Reader 上还有存档，只要知道以前的 RSS 链接就可以下载到了。</li>
</ol>
<p><p>查看：<a href="http://live.aulddays.com/tech/13/google-reader-archive-download.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/13/google-reader-archive-download.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/13/google-reader-archive-download.htm</guid>
      <pubDate>Wed, 05 Jun 2013 10:16:04 +0800</pubDate>
    </item>
    <item>
      <title>Linux Shell 进制错误 - value too great for base (数值太大不可为算数进制的基)</title>
      <description>
        <![CDATA[<p>Linux Shell 编程进行数学运算时，如果有字符 &#39;0&#39; 打头的数 Bash 会当做八进制解释，而这经常会引起问题。比如我们用&quot;08&quot;参加运算，本意是当做10进制的8，实际却会运行报错：value too great for base (数值太大不可为算数进制的基)。</p>

<p>通常情况下，直接写的数值不会以0打头，但如果是以前面命令的格式化输出为参数时，则往往不能控制。更危险的是，如果参与运算的以&#39;0&#39;打头数值达到了两位数或更多，且数值的每一位均在0-7之间，则 Shell 不会报错，但会输出错误的运算结果。下面是两个会出错的例子：</p>

<pre>
time=`date +%S`
time2=$((time%15))</pre>

<p>当当前时间的秒正好是8或9的时候便会出现类似</p>

<pre>
<span class="SrcCode slc"><a href="http://live.aulddays.com/" target="_blank">aulddays</a>:~$</span> <span class="SrcCode kwa">time=`date +%S`</span> 
<span class="SrcCode slc"><a href="http://live.aulddays.com/" target="_blank">aulddays</a>:~$</span> <span class="SrcCode kwa">time2=$((time%15))</span>
<span class="SrcCode str">bash: 08: value too great for base (error token is &quot;08&quot;)</span><span class="SrcCode line">\or</span>
<span class="SrcCode str">bash: 09: value too great for base (error token is &quot;09&quot;)</span>
<span class="SrcCode str">-bash: 08: 数值太大不可为算数进制的基 （错误符号是 &quot;08&quot;）</span><span class="SrcCode line">\or</span>
<span class="SrcCode str">-bash: 09: 数值太大不可为算数进制的基 （错误符号是 &quot;09&quot;）</span>
</pre>

<p>的报错。又如，使用 seq 时，为了格式工整，往往会在个位数前补&#39;0&#39;对齐：</p>

<pre>
<span class="SrcCode kwa">for </span>hour <span class="SrcCode kwb">in</span> `<span class="SrcCode str">seq -f %02g 0 23</span>`
<span class="SrcCode kwa">do</span>
	hour2=$(($hour+10))
<span class="SrcCode kwa">done</span></pre>

<p>之类，当循环到8时也会出错。</p>

<p>究其原因，就是shell将&ldquo;08&rdquo;当成了8进制数，而8进制数中合法的最大的数字是&#39;7&#39;，因而出错了。要解决这个问题，<span class="code-attribute">必须让Shell强制将&quot;08&quot;当成10进制来解释</span>，具体做法，已第一个例子来讲需要改写成：</p>

<pre>
time=`date +%S`
time2=$((10#time%15))</pre>

<p>这样便可让 Bash 将&quot;08&quot;解释为十进制数。前面已经说过，当以&#39;0&#39;打头的数值位数有多位时且每一位都在0-7之间时，Bash 会以八进制解释且不会报错。如果发现脚本执行结果中的数值出现了诡异的问题时，一定要检查一下是否是<a href="http://live.aulddays.com/tech/11/linux-shell-too-great-base-error.htm">整数进制发生了错误</a>。</p>
<p>查看：<a href="http://live.aulddays.com/tech/11/linux-shell-too-great-base-error.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/11/linux-shell-too-great-base-error.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/11/linux-shell-too-great-base-error.htm</guid>
      <pubDate>Fri, 24 Jun 2011 15:28:54 +0800</pubDate>
    </item>
    <item>
      <title>SecureCRT 连接 Linux 主机中文复制粘帖后出现乱码</title>
      <description>
        <![CDATA[<p>
使用 SecureCRT 登录远程系统后，中文可以正常显示，但复制中文再粘帖后却出现乱码。百思不得其解。</p>
	<p>
	经过测试后发现，原来是 <a href="http://live.aulddays.com/"  target="_blank">Aulddays</a> 
	自定义终端显示字体后产生的问题。如果使用默认字体，或者 Fixedsys 之类的字体，则不会产生乱码，换用特定字体后就会发生。可是 Fixedsys 
	等字体在显示中文时会默认展示宋体，在终端中显示起来实在非常丑陋。那么，怎样在 SecureCRT 
	中使用雅黑之类的中文字体，而又避免粘帖中文时产生乱码呢？经过反复研究，<a href="http://live.aulddays.com/" target="_blank">Aulddays</a> 
	终于找到了办法。具体需要经过下面两步的设置：</p>
	<h2>
	1. <a href="http://live.aulddays.com/tech/11/securecrt-character-encoding.htm#SecureCRTCharacterEncoding" name="SecureCRTCharacterEncoding">配置终端字符编码</a></h2>
	<p>
	首先需要确定当前 Linux 终端使用的是什么编码。在 Shell 中输入如下命令：</p>
	<pre>env | grep LANG</pre>
	<p>
	如果输出的 LANG 带有 utf-8 之类的编码标志，则说明终端编码是 utf-8。否则，终端编码是 GB：</p>
	<pre>LANG=zh_CN.UTF-8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="SrcCode slc">==&gt; utf-8 编码</span>

LANG=zh_CN              <span class="SrcCode slc">==&gt; GB 编码</span>

LANG=en_US              <span class="SrcCode slc">==&gt; GB 编码</span></pre>
	<p>
	下面打开 SecureCRT 的 Options -&gt; Session Options 对话框，在 Terminal -&gt; Appearance 中找到 
	Character Encoding 下拉框，选择对应的 UTF-8 / GB18030(或GB2312) 编码：</p>
	<p class="Image">
	<a href="http://live.aulddays.com/tech/11/securecrt-character-encoding.htm" target="_blank">
	<img alt="SecureCRT 字符编码" height="511" src="http://liveres.aulddays.com/securecrt-character-encoding-SecureCRTChEncoding.png" width="553" /></a></p>
	<h2>
	2. 
	<a href="http://live.aulddays.com/tech/11/securecrt-character-encoding.htm#SecureCRTFontEncoding" name="SecureCRTFontEncoding">选择正确的字体编码</a></h2>
	<p>
	在设置了字符编码后，还有关键的一步，就是要设置所配字体的编码。仍然是在刚才的 Session Options 对话框中，点击 Font 
	按钮。在弹出的字体对话框中，选择想要的字体，最后在字体对话框的最下面一定要选择“中文 GB2312”。<span class="code-tag">注意，无论在上一步终端编码使用的是 utf-8 
	还是 GB，在这里都一定要选择 GB2312</span>。</p>
	<p class="Image">
	<a href="http://live.aulddays.com/tech/11/securecrt-character-encoding.htm" target="_blank">
	<img alt="SecureCRT 字体编码" height="357" src="http://liveres.aulddays.com/securecrt-character-encoding-SecureCRTFontEncoding.png" width="447" /></a></p>
	<p>
	以上两步配置完成后， SecureCRT 即可以完美的显式中文并实现中文正确的复制粘帖操作。</p>
<p>查看：<a href="http://live.aulddays.com/tech/11/securecrt-character-encoding.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/11/securecrt-character-encoding.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/11/securecrt-character-encoding.htm</guid>
      <pubDate>Sat, 08 Jan 2011 12:40:56 +0800</pubDate>
    </item>
    <item>
      <title>Firefox 开始兼容 Chrome 的扩展</title>
      <description>
        <![CDATA[<p>
最近，<a href="http://www.google.com/chrome" target="_blank">Google Chrome</a> 
的发展势头相当迅猛，千呼万唤始出来的扩展功能也大受欢迎，各种新的扩展层出不穷。另一方面，非 IE 浏览器的老大  
<a href="http://www.mozilla.com/firefox/" target="_blank">Mozilla Firefox</a> 
显然受到了很大的压力，Firefox 一向以插件和扩展的强大而著称。事实上，从扩展功能开发者的角度看，Firefox 的扩展模型要更加底层和强大的，因而 
Firefox 插件和扩展可以实现很多 Chrome 上的扩展无法做到的事情。</p>
	<p>
	比如，最近 Mozilla 的网站上出现了一款新的插件（add-on），可以让你在 Firefox 中直接安装和使用 Chrome 
	的扩展！这款插件的名字叫 <a href="https://addons.mozilla.org/en-US/firefox/addon/98440">
	Google Chrome extensions manager</a>，目前还处在试验阶段。虽然目前尚只有很少部分 Chrome 
	扩展被完整支持，但从试用者的反馈来看，它的功能确实令人赞叹。</p>
	<p>
	目前此插件已经支持的 Chrome 功能有：</p>
	<ul>
		<li>manifest file (close to be complete)</li>
		<li>background pages</li>
		<li>options pages</li>
		<li>browser actions</li>
		<li>content scripts</li>
		<li>match patterns</li>
		<li>所有 Firefox 支持的 html5 API (如 localStorage...)</li>
		<li>以下 Chrome API：<ul>
			<li>browserAction</li>
			<li>extension</li>
			<li>tabs</li>
			<li>windows</li>
		</ul>
		</li>
	</ul>
	<p class="Image">
	<a href="https://addons.mozilla.org/en-US/firefox/addon/98440">
	<img alt="Extension Manager" src="http://liveres.aulddays.com/firefox-chrome-extension-firefoxchrome2.png" height="497" width="700"  /></a></p>
	<p class="Image">
	<a href="http://live.aulddays.com/tech/10/firefox-chrome-extension.htm">
	<img alt="Chrome extension for Firefox" src="http://liveres.aulddays.com/firefox-chrome-extension-firefoxchrome1.png" height="165" width="580" /></a></p><p>查看：<a href="http://live.aulddays.com/tech/10/firefox-chrome-extension.htm">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/10/firefox-chrome-extension.htm</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/10/firefox-chrome-extension.htm</guid>
      <pubDate>Thu, 25 Mar 2010 10:40:31 +0800</pubDate>
    </item>
    <item>
      <title>打印浮点数输出 1.#IND 1.#INF nan inf 等解决</title>
      <description>
        <![CDATA[<p>
进行浮点数编程时，如果没有注意，常常会出现输出类似 <code>1.#IND</code>, <code>1.#INF</code> 或者 <code>nan</code>, <code>inf</code> 之类奇怪的输出。这通常隐含了浮点数操作的异常。</p>
	<h2>
	<a href="http://live.aulddays.com/tech/10/double-float-ind-nan-inf/#meaning" name="meaning">特殊浮点数的含义</a></h2>
	<p>
	<code>1.#INF</code> / <code>inf</code>：这个值表示“无穷大 (infinity 的缩写)”，即超出了计算机可以表示的浮点数的最大范围（或者说超过了 
	<code>double</code> 类型的最大值）。例如，当用 0 除一个整数时便会得到一个<code>1.#INF</code> / <code>inf</code>值；相应的，如果用 
	0 除一个负整数也会得到
	<code>-1.#INF</code> / <code>-inf</code> 值。</p>
	<p>
	<code>-1.#IND</code> / <code>nan</code>：这个的情况更复杂，一般来说，它们来自于任何未定义结果（非法）的浮点数运算。&quot;IND&quot;是 
	indeterminate 的缩写，而&quot;nan&quot;是 not a number 的缩写。产生这个值的常见例子有：对负数开平方，对负数取对数，<code>0.0/0.0</code>，<code>0.0*∞</code>, <code>∞/∞</code> 等。</p>
	<p>
	简而言之，如果遇到 <code>1.#INF</code> / <code>inf</code>，就检查是否发生了运算结果溢出除零，而遇到 <code>1.#IND</code> / <code>nan</code>，就检查是否发生了非法的运算。</p>
	<h2>
	<a href="http://live.aulddays.com/tech/10/double-float-ind-nan-inf/#judge" name="judge">特殊浮点数的判断 (可移植版本)</a></h2>
	<p>
	很多 C 库都提供了一组函数用来判断一个浮点数是否是无穷大或 NaN。<code>int _isnan(double x)</code> 函数用来判断一个浮点数是否是 
	NaN，而 <code>int _finite(double x)</code> 用以判断一个浮点数是否是无穷大。</p>
	<p>
	你可能已经注意到了，上面两个函数都是以下划线开头的，因此在<em>可移植性</em>上可能是存在问题的，那么如何实现一个<em>可移植（portable）</em>的判断版本呢？首先，对于 
	Nan，可以用下面的代码实现：</p>
	<pre class="SrcCode"><span class="SrcCode kwb">bool</span> <span class="SrcCode kwd">IsNumber</span><span class="SrcCode sym">(</span><span class="SrcCode kwb">double</span> x<span class="SrcCode sym">)</span>
<span class="SrcCode sym">{</span>
    <span class="SrcCode slc">// 这里的比较操作看上去总是会得到 true</span>
    <span class="SrcCode slc">// 但有趣的是对于 IEEE 754 浮点数 NaN 来说总会得到 false!</span>
    <span class="SrcCode kwa">return</span> <span class="SrcCode sym">(</span>x <span class="SrcCode sym">==</span> x<span class="SrcCode sym">);</span>
<span class="SrcCode sym">}</span>
</pre><p>
	而下面的代码可以判断一个浮点数是否是<strong>有限的</strong>（finite, 即既不是 NaN 又不是 infinite）：</p>
	<pre class="SrcCode"><span class="SrcCode kwb">bool</span> <span class="SrcCode kwd">IsFiniteNumber</span><span class="SrcCode sym">(</span><span class="SrcCode kwb">double</span> x<span class="SrcCode sym">)</span>
<span class="SrcCode sym">{</span>
    <span class="SrcCode kwa">return</span> <span class="SrcCode sym">(</span>x <span class="SrcCode sym">&lt;=</span> DBL_MAX <span class="SrcCode sym">&amp;&amp;</span> x <span class="SrcCode sym">&gt;= -</span>DBL_MAX<span class="SrcCode sym">);</span>
<span class="SrcCode sym">}</span>
</pre>
<p>
	其中，<code>DBL_MAX</code> 是 <code>&lt;float.h&gt;</code> 中预定义的常量。</p>
	<p>
	把上面两个函数结合起来，还可以实现一个浮点数是否是 Inf 的判断。</p><p>查看：<a href="http://live.aulddays.com/tech/10/double-float-ind-nan-inf/">原文地址</a>; 来源：<a href="http://live.aulddays.com/">live.aulddays.com</a>.</p>]]>
      </description>
      <link>http://live.aulddays.com/tech/10/double-float-ind-nan-inf/</link>
      <author>旧日重来 http://live.aulddays.com/</author>
      <guid isPermaLink="true">http://live.aulddays.com/tech/10/double-float-ind-nan-inf/</guid>
      <pubDate>Thu, 11 Mar 2010 11:24:34 +0800</pubDate>
    </item>
  </channel>
</rss>
