<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>神仙的仙居</title>
	<atom:link href="http://xiezhenye.com/feed" rel="self" type="application/rss+xml" />
	<link>http://xiezhenye.com</link>
	<description>这里是神仙，也就是谢振业的blog。走过路过，记得要回贴 ^_^</description>
	<lastBuildDate>Mon, 30 Apr 2012 11:05:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>找回失落的磁盘空间</title>
		<link>http://xiezhenye.com/2012/04/%e6%89%be%e5%9b%9e%e5%a4%b1%e8%90%bd%e7%9a%84%e7%a3%81%e7%9b%98%e7%a9%ba%e9%97%b4.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e6%2589%25be%25e5%259b%259e%25e5%25a4%25b1%25e8%2590%25bd%25e7%259a%2584%25e7%25a3%2581%25e7%259b%2598%25e7%25a9%25ba%25e9%2597%25b4</link>
		<comments>http://xiezhenye.com/2012/04/%e6%89%be%e5%9b%9e%e5%a4%b1%e8%90%bd%e7%9a%84%e7%a3%81%e7%9b%98%e7%a9%ba%e9%97%b4.html#comments</comments>
		<pubDate>Mon, 16 Apr 2012 09:22:37 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[df]]></category>
		<category><![CDATA[fs]]></category>
		<category><![CDATA[tune2fs]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3715</guid>
		<description><![CDATA[今天遇到一个机器出错，日志显示是磁盘满。于是 df 一下，果然： # df Filesystem 1K-blocks Used Available Use% Mounted on ... /dev/sda2 4914492 4666144 0 100% /data Available 是 0，但是，等等，为啥 Used 要小于 1K-blocks 呢？剩下的空间到哪里去了呢？ 搜索了一下，原来对 ext2/ext3 文件系统，会保留一定比例空间给 root 用户，普通用户无法使用。所以就会有这样的结果。这个比例默认是 5% 。这样一来，就会有不少空间没法使用。如果想要从这里榨一点空间处理，可以用 tune2fs 。如： tune2fs -m2 /dev/sda2 之后就会看到： # df Filesystem 1K-blocks Used Available Use% Mounted on ... /dev/sda2 4914492 4666068 148564 97% /data [...]]]></description>
			<content:encoded><![CDATA[<p>今天遇到一个机器出错，日志显示是磁盘满。于是 df 一下，果然：</p>
<pre>
# df
Filesystem           1K-blocks      Used Available Use% Mounted on
...
/dev/sda2              4914492   4666144         0 100% /data
</pre>
<p>Available 是 0，但是，等等，为啥 Used 要小于 1K-blocks 呢？剩下的空间到哪里去了呢？ 搜索了一下，原来对 ext2/ext3 文件系统，会保留一定比例空间给 root 用户，普通用户无法使用。所以就会有这样的结果。这个比例默认是 5% 。这样一来，就会有不少空间没法使用。如果想要从这里榨一点空间处理，可以用 tune2fs 。如：</p>
<pre>
tune2fs -m2 /dev/sda2
</pre>
<p>之后就会看到：</p>
<pre>
# df
Filesystem           1K-blocks      Used Available Use% Mounted on
...
/dev/sda2              4914492   4666068    148564  97% /data
</pre>
<p>这就榨出了 100 多 M 出来。如果分区大的话，这个空间还是很可观的。</p>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2012/04/%e6%89%be%e5%9b%9e%e5%a4%b1%e8%90%bd%e7%9a%84%e7%a3%81%e7%9b%98%e7%a9%ba%e9%97%b4.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>给 mysql 系统表加上 trigger</title>
		<link>http://xiezhenye.com/2012/04/%e7%bb%99-mysql-%e7%b3%bb%e7%bb%9f%e8%a1%a8%e5%8a%a0%e4%b8%8a-trigger.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e7%25bb%2599-mysql-%25e7%25b3%25bb%25e7%25bb%259f%25e8%25a1%25a8%25e5%258a%25a0%25e4%25b8%258a-trigger</link>
		<comments>http://xiezhenye.com/2012/04/%e7%bb%99-mysql-%e7%b3%bb%e7%bb%9f%e8%a1%a8%e5%8a%a0%e4%b8%8a-trigger.html#comments</comments>
		<pubDate>Thu, 05 Apr 2012 02:58:34 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[trigger]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3708</guid>
		<description><![CDATA[默认情况下，mysql 是不能给系统表，例如 mysql.user 加上触发器的。会提示 ERROR 1465 (HY000): Triggers can not be created on system tables 但是还是可以有办法绕过这个限制。 在其他 db 里另外建一个结构名字一样的表，例如 create table test.user like mysql.user 然后在那个表上建好触发器。这样会在数据库目录中生成 “表名.TRG” 文件。把这个文件拷贝到 mysql 库的目录中，确认访问权限没问题后，重启一下 mysql ，触发器就可以生效了。]]></description>
			<content:encoded><![CDATA[<p>默认情况下，mysql 是不能给系统表，例如 mysql.user 加上触发器的。会提示</p>
<pre>ERROR 1465 (HY000): Triggers can not be created on system tables</pre>
<p>但是还是可以有办法绕过这个限制。</p>
<p>在其他 db 里另外建一个结构名字一样的表，例如</p>
<pre>create table test.user like mysql.user</pre>
<p>然后在那个表上建好触发器。这样会在数据库目录中生成 “表名.TRG” 文件。把这个文件拷贝到 mysql 库的目录中，确认访问权限没问题后，重启一下 mysql ，触发器就可以生效了。</p>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2012/04/%e7%bb%99-mysql-%e7%b3%bb%e7%bb%9f%e8%a1%a8%e5%8a%a0%e4%b8%8a-trigger.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>bash 技巧笔记</title>
		<link>http://xiezhenye.com/2012/02/bash-%e6%8a%80%e5%b7%a7%e7%ac%94%e8%ae%b0.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=bash-%25e6%258a%2580%25e5%25b7%25a7%25e7%25ac%2594%25e8%25ae%25b0</link>
		<comments>http://xiezhenye.com/2012/02/bash-%e6%8a%80%e5%b7%a7%e7%ac%94%e8%ae%b0.html#comments</comments>
		<pubDate>Thu, 01 Mar 2012 01:48:31 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[bash]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3700</guid>
		<description><![CDATA[alias 可以对一个或一组命令起一个别名。和 bash 函数不同的是 alias 没有自己的独立进程。在有些情况下，和函数是不能相互替代的。 比如 alias shift2="shift 2" 之后就可以在脚本或函数中使用，踢掉两个参数。这样的东西用函数是不管用的。但是，alias 如果写在脚本里，运行时候会说找不到命令。因为对 bash 脚本，默认没有展开 alias 。需要先设置一下才行： shopt -s expand_aliases bash 中定义的变量可以用 export 导出，这样在子子孙孙的进程中都可以访问到。实际上，函数也是可以导出的。用 export -f 即可。这样在子进程的 bash 中也可以使用这些函数了。 出于调试目的或者要批量导出，可能需要列出所有函数。可以用 declare -F ，不过这个结果里，每行前面都有 &#8220;declare -f&#8221; 。用 compgen -A function [prefix] 结果就很干净。另外，这个函数还可以指定函数的前缀。 如果要列出指定前缀的变量，则可以用 echo ${!prefix*}]]></description>
			<content:encoded><![CDATA[<p>alias 可以对一个或一组命令起一个别名。和 bash 函数不同的是 alias 没有自己的独立进程。在有些情况下，和函数是不能相互替代的。<br />
比如</p>
<pre>
alias shift2="shift 2"
</pre>
<p>之后就可以在脚本或函数中使用，踢掉两个参数。这样的东西用函数是不管用的。但是，alias 如果写在脚本里，运行时候会说找不到命令。因为对 bash 脚本，默认没有展开 alias 。需要先设置一下才行：</p>
<pre>
shopt -s expand_aliases
</pre>
<p>bash 中定义的变量可以用 export 导出，这样在子子孙孙的进程中都可以访问到。实际上，函数也是可以导出的。用 export -f 即可。这样在子进程的 bash 中也可以使用这些函数了。</p>
<p>出于调试目的或者要批量导出，可能需要列出所有函数。可以用 </p>
<pre>
declare -F
</pre>
<p>，不过这个结果里，每行前面都有 &#8220;declare -f&#8221; 。用 </p>
<pre>
compgen -A function [prefix]
</pre>
<p>结果就很干净。另外，这个函数还可以指定函数的前缀。</p>
<p>如果要列出指定前缀的变量，则可以用</p>
<pre>
echo ${!prefix*}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2012/02/bash-%e6%8a%80%e5%b7%a7%e7%ac%94%e8%ae%b0.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>读取 mysql binlog 开始和结束时间</title>
		<link>http://xiezhenye.com/2012/01/%e8%af%bb%e5%8f%96-mysql-binlog-%e5%bc%80%e5%a7%8b%e5%92%8c%e7%bb%93%e6%9d%9f%e6%97%b6%e9%97%b4.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e8%25af%25bb%25e5%258f%2596-mysql-binlog-%25e5%25bc%2580%25e5%25a7%258b%25e5%2592%258c%25e7%25bb%2593%25e6%259d%259f%25e6%2597%25b6%25e9%2597%25b4</link>
		<comments>http://xiezhenye.com/2012/01/%e8%af%bb%e5%8f%96-mysql-binlog-%e5%bc%80%e5%a7%8b%e5%92%8c%e7%bb%93%e6%9d%9f%e6%97%b6%e9%97%b4.html#comments</comments>
		<pubDate>Sat, 21 Jan 2012 05:22:42 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[binlog]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3695</guid>
		<description><![CDATA[mysql binlog 记录了所有可能涉及更新的操作，可以用来作为增量备份的一种选择。为了管理 binlog ，需要读取每个 binlog 文件的准确的开始和结束时间。用 mysqlbinlog 工具可以解析 binlog 文件，所以也可以通过分析输出结果来获取。但是 mysqlbinlog 只能顺序读取记录，如果只是分析开始时间还好，要分析结束时间，就必须等它把整个 binlog 处理完。在 binlog 文件体积大的时候，代价就大了些。好在 mysql 对 binlog 文件的格式是公开的，所以我们可以直接通过解析文件自己实现。 binlog 文件的格式在 http://forge.mysql.com/wiki/MySQL_Internals_Binary_Log 可以找到。每个 binlog 文件都有相同的开头：0xfe 0&#215;62 0&#215;69 0x6e 。也就是 0xfe 后面加上 bin 。之后，就是一个个事件数据。binlog 的事件类型有很多种，但每个 binlog 文件的第一个事件一定是格式描述事件（format description event），描述了 binlog 文件格式版本信息；最后一个时间一定是轮转事件（rotate event），记录了下一个 binlog 的文件名和事件开始偏移位置。每个事件都有一个一致的事件头，其中就有事件的时间戳、事件类型等。读取第一个事件和最后一个事件的信息就可以获取 binlog 文件的准确开始和结束时间了。 读取第一个事件 format description event 要容易一些，seek 跳过文件头，读取事件头就行了。读取最后一个事件的时间要稍麻烦些。因为事件的长度是不固定的。对于轮转事件来说，除了事件头以外，后面还有一个 64位整数的开始位置偏移量以及下一个 binlog [...]]]></description>
			<content:encoded><![CDATA[<p>mysql binlog 记录了所有可能涉及更新的操作，可以用来作为增量备份的一种选择。为了管理 binlog ，需要读取每个 binlog 文件的准确的开始和结束时间。用 mysqlbinlog 工具可以解析 binlog 文件，所以也可以通过分析输出结果来获取。但是 mysqlbinlog 只能顺序读取记录，如果只是分析开始时间还好，要分析结束时间，就必须等它把整个 binlog 处理完。在 binlog 文件体积大的时候，代价就大了些。好在 mysql 对 binlog 文件的格式是公开的，所以我们可以直接通过解析文件自己实现。</p>
<p>binlog 文件的格式在 http://forge.mysql.com/wiki/MySQL_Internals_Binary_Log 可以找到。每个 binlog 文件都有相同的开头：0xfe 0&#215;62 0&#215;69 0x6e 。也就是 0xfe 后面加上 bin 。之后，就是一个个事件数据。binlog 的事件类型有很多种，但每个 binlog 文件的第一个事件一定是格式描述事件（format description event），描述了 binlog 文件格式版本信息；最后一个时间一定是轮转事件（rotate event），记录了下一个 binlog 的文件名和事件开始偏移位置。每个事件都有一个一致的事件头，其中就有事件的时间戳、事件类型等。读取第一个事件和最后一个事件的信息就可以获取 binlog 文件的准确开始和结束时间了。</p>
<p>读取第一个事件 format description event 要容易一些，seek 跳过文件头，读取事件头就行了。读取最后一个事件的时间要稍麻烦些。因为事件的长度是不固定的。对于轮转事件来说，除了事件头以外，后面还有一个 64位整数的开始位置偏移量以及下一个 binlog 的文件名。长度不确定的部分就是最后的文件名部分。好在那个偏移量是一个固定的值：4（也就是跳过文件头），所以可以从后往前读取，用它来作为标记，检查是否读完了文件名。然后就可以跳过文件名和偏移量，读取最后一个事件的事件头了。</p>
<p>php 代码如下：</p>
<pre>
&lt;?php
/**
 * read binlog info
 *
 * A mysql binlog file is begin with a head "\xfebin" and then log evnets. The
 * first event is a format description event, the last event is a rotate event.
 *
 * For more infomation about mysql binlog format, see http://forge.mysql.com/wiki/MySQL_Internals_Binary_Log
 */
class BinlogInfo {
    const EVENT_HEAD_SIZE = 19;
    const FORMAT_DESCRIPTION_EVENT_DATA_SIZE = 59;
    const BINLOG_HEAD = "\xfebin";
    const FORMAT_DESCRIPTION_EVENT = 15;
    const ROTATE_EVENT = 4;

    private $eventHeadPackStr = '';
    private $formatDescriptionEventDataPackStr = '';

    function __construct() {
        $this-&gt;eventHeadPackStr = $this-&gt;eventHeadPackStr();
        $this-&gt;formatDescriptionEventDataPackStr = $this-&gt;formatDescriptionEventDataPackStr();
    }

    protected function eventHeadPackStr() {
        $event_header_struct = array(
            'timestamp' =&gt; 'l',
            'type_code' =&gt; 'c',
            'server_id' =&gt; 'l',
            'event_length' =&gt; 'l',
            'next_position' =&gt; 'l',
            'flags' =&gt; 's',
        );
        return $this-&gt;toPackStr($event_header_struct);
    }

    protected function formatDescriptionEventDataPackStr() {
        $format_description_event_data_struct = array(
            'binlog_version' =&gt; 's',
            'server_version' =&gt; 'a50',
            'create_timestamp' =&gt; 'l',
            'head_length' =&gt; 'c'
        );
        return $this-&gt;toPackStr($format_description_event_data_struct);
    }

    protected function toPackStr($arr) {
        $ret = '';
        foreach ($arr as $k=&gt;$v) {
            $ret.= '/'.$v.$k;
        }
        $ret = substr($ret, 1);
        return $ret;
    }

    /**
     * @param resource $file
     *
     * Mysql binlog file begin with a 4 bytes head: "\xfebin".
     */
    protected function isBinlog($file) {
        rewind($file);
        $head = fread($file, strlen(self::BINLOG_HEAD));
        return $head == self::BINLOG_HEAD;
    }

    /**
     * @param resource $file
     *
     * Format description event is the first event of a binlog file
     */
    protected function readFormatDescriptionEvent($file) {
        fseek($file, strlen(self::BINLOG_HEAD), SEEK_SET);
        $head_str = fread($file, self::EVENT_HEAD_SIZE);
        $head = unpack($this-&gt;eventHeadPackStr, $head_str);
        if ($head['type_code'] != self::FORMAT_DESCRIPTION_EVENT) {
            return null;
        }
        $data_str= fread($file, self::FORMAT_DESCRIPTION_EVENT_DATA_SIZE);
        $data = unpack($this-&gt;formatDescriptionEventDataPackStr, $data_str);

        return array('head'=&gt;$head, 'data'=&gt;$data);
    }

    /**
     * @param resource $file
     *
     * Rotate event is the last event of a binglog.
     * After event header, there is a 64bit int indicate the first event
     * position of next binlog file and next binlog file name without \0 at end.
     * The position is always be 4 (hex: 0400000000000000).
     *
     */
    protected function readRotateEvent($file)
    {
        /**
         * Rotate event size is 19(head size) + 8(pos) + len(filename).
         * 100 bytes can contain a filename which length less than 73 bytes and
         * it is short than the length of format description event so filesize -
         * bufsize will never be negative.
         */
        $bufsize = 100;
        $size_pos = 8;
        fseek($file, -$bufsize, SEEK_END);
        $buf = fread($file, $bufsize);
        $min_begin = strlen(self::BINLOG_HEAD) + self::EVENT_HEAD_SIZE + $size_pos;
        $ok = false;
        for ($i = $bufsize - 1; $i &gt; $min_begin; $i--) {
            if ($buf[$i] == "\0") {
                $ok = true;
                break;
            }
        }
        if (!$ok) {
            return null;
        }
        $next_filename = substr($buf, $i + 1);

        $head_str = substr($buf, $i + 1 - $size_pos - self::EVENT_HEAD_SIZE, self::EVENT_HEAD_SIZE);
        $head = unpack($this-&gt;eventHeadPackStr, $head_str);
        if ($head['type_code'] != self::ROTATE_EVENT) {
            return null;
        }
        return array('head'=&gt;$head, 'nextFile'=&gt;$next_filename);
    }

    /**
     * @param string $path path to binlog file
     */
    function read($path) {
        $file = fopen($path, 'r');
        if (!$file) {
            return null;
        }
        if (!$this-&gt;isBinlog($file)) {
            fclose($file);
            return null;
        }

        $fde = $this-&gt;readFormatDescriptionEvent($file);
        $re = $this-&gt;readRotateEvent($file);
        fclose($file);
        return array(
            'beginAt' =&gt; $fde['head']['timestamp'],
            'endAt' =&gt; $re['head']['timestamp'],
            'nextFile' =&gt; $re['nextFile'],
            'serverVersion' =&gt; $fde['data']['server_version'],
        );
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2012/01/%e8%af%bb%e5%8f%96-mysql-binlog-%e5%bc%80%e5%a7%8b%e5%92%8c%e7%bb%93%e6%9d%9f%e6%97%b6%e9%97%b4.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>三亚</title>
		<link>http://xiezhenye.com/2012/01/%e4%b8%89%e4%ba%9a.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e4%25b8%2589%25e4%25ba%259a</link>
		<comments>http://xiezhenye.com/2012/01/%e4%b8%89%e4%ba%9a.html#comments</comments>
		<pubDate>Fri, 06 Jan 2012 16:04:42 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[旅行]]></category>
		<category><![CDATA[三亚]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3656</guid>
		<description><![CDATA[元旦去了三亚。之前想着念着很久，这回终于下定决心出行，实现了在冬天到三亚去的愿望。 一下飞机，立马从冬天变成了夏天。在上海穿羽绒服，到了三亚就得是短袖了。 三亚湾和大东海的沙滩还是很软的。虽然海水一般，但在沙滩上坐着走着看海玩沙看日落，在沙滩上走，让海水漫过脚，一切都还是很惬意的，在大东海，还碰到一场沙滩婚礼。在三亚还有好多俄国的大叔大妈帅哥美女…… 第二天我们上蜈支洲岛。船接近码头的时候，就看到了真正色彩明亮的海水。从沙滩开始，海水从透明到蓝绿色再到深蓝色。 沙滩上面还有潮水冲上来的碎珊瑚。虽然走起来会有些硌脚，但却让沙滩变得洁白。在沙滩上捡珊瑚、贝壳，也是件很有意思的事情。 第二天坐环岛观光车，看到了岛的另一面。与之前的洁白的沙滩美丽的海水完全不同。这边礁石嶙峋，惊涛拍岸。 而在我们要离开的时候，云层散开，阳光直射海面。海水的颜色更加艳丽。看着真不想离开了~ 最后回到上海的时候，一下子感觉，好冷啊…………………………]]></description>
			<content:encoded><![CDATA[<p>元旦去了三亚。之前想着念着很久，这回终于下定决心出行，实现了在冬天到三亚去的愿望。</p>
<p>一下飞机，立马从冬天变成了夏天。在上海穿羽绒服，到了三亚就得是短袖了。</p>
<p>三亚湾和大东海的沙滩还是很软的。虽然海水一般，但在沙滩上坐着走着看海玩沙看日落，在沙滩上走，让海水漫过脚，一切都还是很惬意的，在大东海，还碰到一场沙滩婚礼。在三亚还有好多俄国的大叔大妈帅哥美女……</p>
<p><a href="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP1858.jpg"><img class="alignnone size-full wp-image-3670" title="IMGP1858" src="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP1858.jpg" alt="" width="600" height="399" /></a></p>
<p><a href="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP2006.jpg"><img title="IMGP2006" src="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP2006.jpg" alt="" width="600" height="399" /></a></p>
<p>第二天我们上蜈支洲岛。船接近码头的时候，就看到了真正色彩明亮的海水。从沙滩开始，海水从透明到蓝绿色再到深蓝色。</p>
<p><a href="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP1883.jpg"><img class="alignnone size-full wp-image-3662" title="IMGP1883" src="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP1883.jpg" alt="" width="600" height="399" /></a></p>
<p>沙滩上面还有潮水冲上来的碎珊瑚。虽然走起来会有些硌脚，但却让沙滩变得洁白。在沙滩上捡珊瑚、贝壳，也是件很有意思的事情。</p>
<p><a href="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP1983.jpg"><img class="alignnone size-full wp-image-3666" title="IMGP1983" src="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP1983.jpg" alt="" width="600" height="400" /></a></p>
<p>第二天坐环岛观光车，看到了岛的另一面。与之前的洁白的沙滩美丽的海水完全不同。这边礁石嶙峋，惊涛拍岸。</p>
<p><a href="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP19991.jpg"><img class="alignnone size-full wp-image-3668" title="IMGP1999" src="http://xiezhenye.com/wp-content/uploads/2012/01/IMGP19991.jpg" alt="" width="600" height="399" /></a></p>
<p>而在我们要离开的时候，云层散开，阳光直射海面。海水的颜色更加艳丽。看着真不想离开了~</p>
<p>最后回到上海的时候，一下子感觉，好冷啊…………………………</p>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2012/01/%e4%b8%89%e4%ba%9a.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>bash 中有效建立锁</title>
		<link>http://xiezhenye.com/2011/12/bash-%e4%b8%ad%e6%9c%89%e6%95%88%e5%bb%ba%e7%ab%8b%e9%94%81.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=bash-%25e4%25b8%25ad%25e6%259c%2589%25e6%2595%2588%25e5%25bb%25ba%25e7%25ab%258b%25e9%2594%2581</link>
		<comments>http://xiezhenye.com/2011/12/bash-%e4%b8%ad%e6%9c%89%e6%95%88%e5%bb%ba%e7%ab%8b%e9%94%81.html#comments</comments>
		<pubDate>Wed, 21 Dec 2011 09:02:02 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[锁]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3650</guid>
		<description><![CDATA[有时候需要防止一段代码在被同时执行，就需要使用锁来防止代码重入。常常见到这样的代码： if [ -f /var/lock/mylock ]; then touch /var/lock/mylock ... rm -f /var/lock/mylock fi 但实际上，这样是有问题的。如果两个进程在 test ( [ ) 和 touch 之间，另一个进程同时执行，就会出现竞争问题，最后就可能出现同时运行的情况。要避免这种情况出现，就得改一下加锁的方式。可以用 mkdir 代替 touch，这样在锁目录以及存在的时候，会直接出错； if mkdir /var/lock/mylock 2>/dev/null; then ... rm -rf /var/lock/mylock fi 或者先用 set -C ，让 > 重定向在文件已经存在时出错，然后用 echo &#8230; > 来生成锁文件 if ( set -C; echo $$> /var/lock/mylock 2>/dev/null); then ... [...]]]></description>
			<content:encoded><![CDATA[<p>有时候需要防止一段代码在被同时执行，就需要使用锁来防止代码重入。常常见到这样的代码：</p>
<pre>
if [ -f /var/lock/mylock ]; then
  touch /var/lock/mylock
  ...
  rm -f /var/lock/mylock
fi
</pre>
<p>但实际上，这样是有问题的。如果两个进程在 test ( [ ) 和 touch 之间，另一个进程同时执行，就会出现竞争问题，最后就可能出现同时运行的情况。要避免这种情况出现，就得改一下加锁的方式。可以用 mkdir 代替 touch，这样在锁目录以及存在的时候，会直接出错；</p>
<pre>
if mkdir /var/lock/mylock 2>/dev/null; then
  ...
  rm -rf /var/lock/mylock
fi
</pre>
<p>或者先用 set -C ，让 > 重定向在文件已经存在时出错，然后用 echo &#8230; > 来生成锁文件</p>
<pre>
if ( set -C; echo $$> /var/lock/mylock 2>/dev/null); then
  ...
  rm -f /var/lock/mylock
fi
</pre>
<p>这两种方法可以保证加锁和检测锁是一个原子操作，避免竞争问题。</p>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2011/12/bash-%e4%b8%ad%e6%9c%89%e6%95%88%e5%bb%ba%e7%ab%8b%e9%94%81.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>坑爹的 phpunit</title>
		<link>http://xiezhenye.com/2011/12/%e5%9d%91%e7%88%b9%e7%9a%84-phpunit.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e5%259d%2591%25e7%2588%25b9%25e7%259a%2584-phpunit</link>
		<comments>http://xiezhenye.com/2011/12/%e5%9d%91%e7%88%b9%e7%9a%84-phpunit.html#comments</comments>
		<pubDate>Thu, 15 Dec 2011 10:16:50 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[phpunit php]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3643</guid>
		<description><![CDATA[最新的 phpunit 是 3.6 。之前用的是 3.5 。在一个新机器上安装，按官网的方法，装了新版本后，运行，出错！ Call to undefined method PHP_CodeCoverage_Filter::getInstance() 新版本的 CodeCoverage 改变了调用方法。从 phpunit 3.4 升级到 3.5 的时候，CodeCoverage 的使用也发生过改变。这回又来了…… 这次不想改测试代码，于是打算用旧的 PHP_CodeCoverage 。于是 sudo pear install -f http://pear.phpunit.de/get/PHP_CodeCoverage-1.0.5.tgz 结果，还是出错 Class &#8216;File_Iterator&#8217; not found 看看 pear 的目录里，明明有 File/Iterator.php 啊，再看看那个 Iterator/Factory.php ，里面没有 require。。。 试着加一条 require_once 'File/Iterator.php'; 这回 ok 了。不过改代码这事也挺恶心。于是试着把 File_Iterator 也用个旧版本 sudo pear install -f http://pear.phpunit.de/get/File_Iterator-1.2.6.tgz [...]]]></description>
			<content:encoded><![CDATA[<p>最新的 phpunit 是 3.6 。之前用的是 3.5 。在一个新机器上安装，按官网的方法，装了新版本后，运行，出错！</p>
<p>Call to undefined method PHP_CodeCoverage_Filter::getInstance()</p>
<p>新版本的 CodeCoverage 改变了调用方法。从 phpunit 3.4 升级到 3.5 的时候，CodeCoverage 的使用也发生过改变。这回又来了…… 这次不想改测试代码，于是打算用旧的 PHP_CodeCoverage 。于是</p>
<pre>sudo pear install -f http://pear.phpunit.de/get/PHP_CodeCoverage-1.0.5.tgz</pre>
<p>结果，还是出错</p>
<p>Class &#8216;File_Iterator&#8217; not found</p>
<p>看看 pear 的目录里，明明有 File/Iterator.php 啊，再看看那个 Iterator/Factory.php ，里面没有 require。。。 试着加一条</p>
<pre>require_once 'File/Iterator.php';</pre>
<p>这回 ok 了。不过改代码这事也挺恶心。于是试着把 File_Iterator 也用个旧版本</p>
<pre>sudo pear install -f http://pear.phpunit.de/get/File_Iterator-1.2.6.tgz</pre>
<p>然后就又 ok 了。</p>
<p>这货为啥总是改接口呢？还不保持向后兼容呢…… </p>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2011/12/%e5%9d%91%e7%88%b9%e7%9a%84-phpunit.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>让 php 用 nginx 打包 zip</title>
		<link>http://xiezhenye.com/2011/11/%e8%ae%a9-php-%e7%94%a8-nginx-%e6%89%93%e5%8c%85-zip.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e8%25ae%25a9-php-%25e7%2594%25a8-nginx-%25e6%2589%2593%25e5%258c%2585-zip</link>
		<comments>http://xiezhenye.com/2011/11/%e8%ae%a9-php-%e7%94%a8-nginx-%e6%89%93%e5%8c%85-zip.html#comments</comments>
		<pubDate>Thu, 01 Dec 2011 07:09:07 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[php zip nginx]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3621</guid>
		<description><![CDATA[php 本身有 zip 模块，可以生产 zip 文件。但是这个 zip 模块只能使用本地文件来打包。如果需要打包输出的文件来自网络，就得先保存临时文件。在文件数量多或者文件大的时候就很杯具。另外，由 php 来输出大的打包文件会占用 php 进程大量时间，影响并发能力。 nginx 有一个第三方模块，mod_zip 。同样可以输出 zip 包。和 X-Accel-Redirect 有点类似，只需要 php 输出相应文件的路径等信息，然后给一个特殊的响应头即可。 nginx zip 模块使用的响应头是 X-Archive-Files: zip 。加上这个响应头，nginx zip 模块就会处理响应正文，完成打包输出。 比如： printf("%s %d %s %s\n", $crc32, $size, $url, $path ); 逐条输出要打包的文件。 $crc32 是 16 进制的文件 crc32 值。也可以不提供，用 &#8220;-&#8221; 代替。不过这样就没法用 Range 分块下载，断点续传了。 $size 是文件大小的十进制整数。 $url 是要打包的源地址。如果要打包一个本地文件，可以先在 nginx [...]]]></description>
			<content:encoded><![CDATA[<p>php 本身有 zip 模块，可以生产 zip 文件。但是这个 zip 模块只能使用本地文件来打包。如果需要打包输出的文件来自网络，就得先保存临时文件。在文件数量多或者文件大的时候就很杯具。另外，由 php 来输出大的打包文件会占用 php 进程大量时间，影响并发能力。</p>
<p>nginx 有一个第三方模块，<a href="http://wiki.nginx.org/NgxZip" target="_blank">mod_zip</a> 。同样可以输出 zip 包。和 X-Accel-Redirect 有点类似，只需要 php 输出相应文件的路径等信息，然后给一个特殊的响应头即可。</p>
<p>nginx zip 模块使用的响应头是 X-Archive-Files: zip 。加上这个响应头，nginx zip 模块就会处理响应正文，完成打包输出。</p>
<p>比如：</p>
<pre>
printf("%s %d %s %s\n", $crc32, $size, $url, $path );
</pre>
<p>逐条输出要打包的文件。</p>
<p>$crc32 是 16 进制的文件 crc32 值。也可以不提供，用 &#8220;-&#8221; 代替。不过这样就没法用 Range 分块下载，断点续传了。<br />
$size 是文件大小的十进制整数。<br />
$url 是要打包的源地址。如果要打包一个本地文件，可以先在 nginx 中做一个 internal path。<br />
$path 是 zip 包中的路径。</p>
<p>不过这样没法创建空目录。一方面，zip 格式开始就没有定义空目录，后来的标准和软件都是通过加一个 / 结尾的 0 大小文件来实现的。这时，就需要先在 nginx 中做一个 internal 的 0 大小文件，比如位于 /_0 。然后输出</p>
<pre>
printf("%s %d %s %s\n", '00000000', 0, '/_0', $path.'/');
</pre>
<p>如果要支持中文路径，可以使用 X-Archive-Charset: utf8 这样的响应头，内容为你输出的编码。nginx zip 模块会按标准转换成 utf8 的标准格式。不过各个软件对这个 zip 的标准支持不一，比如 windows 的 zip 目录就不支持，只能以 gbk 编码直接输出。其他软件对编码支持效果也各不相同。测试过的 winrar，7zip，windows zip 目录中，winrar 倒是都可以很好支持。7zip 可能会把部分中文空目录变成 0 大小文件。所以，这点还需要自己斟酌处理。</p>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2011/11/%e8%ae%a9-php-%e7%94%a8-nginx-%e6%89%93%e5%8c%85-zip.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>go 语言 Makefile 指定依赖包位置</title>
		<link>http://xiezhenye.com/2011/11/go-%e8%af%ad%e8%a8%80-makefile-%e6%8c%87%e5%ae%9a%e4%be%9d%e8%b5%96%e5%8c%85%e4%bd%8d%e7%bd%ae.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=go-%25e8%25af%25ad%25e8%25a8%2580-makefile-%25e6%258c%2587%25e5%25ae%259a%25e4%25be%259d%25e8%25b5%2596%25e5%258c%2585%25e4%25bd%258d%25e7%25bd%25ae</link>
		<comments>http://xiezhenye.com/2011/11/go-%e8%af%ad%e8%a8%80-makefile-%e6%8c%87%e5%ae%9a%e4%be%9d%e8%b5%96%e5%8c%85%e4%bd%8d%e7%bd%ae.html#comments</comments>
		<pubDate>Mon, 28 Nov 2011 03:06:54 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[go make]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3604</guid>
		<description><![CDATA[编译 go 程序可以使用自带的一些 Makefile 脚本来简化编写 Makefile 。官方的文档过于简略，没提到需要指定依赖包位置的方法。翻过那几个脚本代码后，发现原来有 LDIMPORTS 和 GCIMPORTS 可以指定。 比如： include $(GOROOT)/src/Make.inc LDIMPORTS=-L ./pkg/_obj GCIMPORTS=-I ./pkg/_obj TARG=tool GOFILES=\ tool.go\ include $(GOROOT)/src/Make.cmd GCIMPORTS 指定编译阶段的参数，对 Make.cmd，Make.pkg 都有效。LDIMPORTS 指定链接阶段的参数，这个对 Make.pkg 就没用了。 另外，还可以用类似 CLEANFILES+= pkg/_obj ，在 make clean 的时候来清理更多的东西。 以及 all: pkg/_obj tool pkg/_obj: cd pkg; make 这样的方法在依赖包未编译时，自动编译依赖包。]]></description>
			<content:encoded><![CDATA[<p>编译 go 程序可以使用自带的一些 Makefile 脚本来简化编写 Makefile 。官方的文档过于简略，没提到需要指定依赖包位置的方法。翻过那几个脚本代码后，发现原来有 LDIMPORTS 和 GCIMPORTS 可以指定。</p>
<p>比如：</p>
<pre>
include $(GOROOT)/src/Make.inc

LDIMPORTS=-L ./pkg/_obj
GCIMPORTS=-I ./pkg/_obj

TARG=tool
GOFILES=\
	tool.go\

include $(GOROOT)/src/Make.cmd
</pre>
<p>GCIMPORTS 指定编译阶段的参数，对 Make.cmd，Make.pkg 都有效。LDIMPORTS 指定链接阶段的参数，这个对 Make.pkg 就没用了。</p>
<p>另外，还可以用类似 CLEANFILES+= pkg/_obj ，在 make clean 的时候来清理更多的东西。</p>
<p>以及</p>
<pre>
all: pkg/_obj tool

pkg/_obj:
	cd pkg; make
</pre>
<p>这样的方法在依赖包未编译时，自动编译依赖包。</p>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2011/11/go-%e8%af%ad%e8%a8%80-makefile-%e6%8c%87%e5%ae%9a%e4%be%9d%e8%b5%96%e5%8c%85%e4%bd%8d%e7%bd%ae.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>让 linux 交互式命令行程序支持方向键等功能</title>
		<link>http://xiezhenye.com/2011/11/%e8%ae%a9-linux-%e4%ba%a4%e4%ba%92%e5%bc%8f%e5%91%bd%e4%bb%a4%e8%a1%8c%e7%a8%8b%e5%ba%8f%e6%94%af%e6%8c%81%e6%96%b9%e5%90%91%e9%94%ae%e7%ad%89%e5%8a%9f%e8%83%bd.html?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=%25e8%25ae%25a9-linux-%25e4%25ba%25a4%25e4%25ba%2592%25e5%25bc%258f%25e5%2591%25bd%25e4%25bb%25a4%25e8%25a1%258c%25e7%25a8%258b%25e5%25ba%258f%25e6%2594%25af%25e6%258c%2581%25e6%2596%25b9%25e5%2590%2591%25e9%2594%25ae%25e7%25ad%2589%25e5%258a%259f%25e8%2583%25bd</link>
		<comments>http://xiezhenye.com/2011/11/%e8%ae%a9-linux-%e4%ba%a4%e4%ba%92%e5%bc%8f%e5%91%bd%e4%bb%a4%e8%a1%8c%e7%a8%8b%e5%ba%8f%e6%94%af%e6%8c%81%e6%96%b9%e5%90%91%e9%94%ae%e7%ad%89%e5%8a%9f%e8%83%bd.html#comments</comments>
		<pubDate>Mon, 14 Nov 2011 02:41:37 +0000</pubDate>
		<dc:creator>神仙</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[linux rlwrap]]></category>

		<guid isPermaLink="false">http://xiezhenye.com/?p=3509</guid>
		<description><![CDATA[自己写交互式命令行程序，通常都是从标准输入输出读写。运行的时候，只能敲完整一个命令，然后回车。想要按方向键移动光标，按 del 往后删除都只会出现个 ^[[A 这样的东西。要实现那些功能，得处理终端命令，这个活并不轻松。虽然有 readline 这样的库，但还是会带来很多麻烦。 好在 unix 的哲学就是一个程序干一件事情，并把它做到最好。于是就有一个 rlwrap 的程序。用这个程序启动你的工具，你的程序就立马有了那些功能。还可以按上下键翻阅历史命令，ctrl+r 来搜索历史输入等。 &#160;]]></description>
			<content:encoded><![CDATA[<p>自己写交互式命令行程序，通常都是从标准输入输出读写。运行的时候，只能敲完整一个命令，然后回车。想要按方向键移动光标，按 del 往后删除都只会出现个 ^[[A 这样的东西。要实现那些功能，得处理终端命令，这个活并不轻松。虽然有 readline 这样的库，但还是会带来很多麻烦。</p>
<p>好在 unix 的哲学就是一个程序干一件事情，并把它做到最好。于是就有一个 rlwrap 的程序。用这个程序启动你的工具，你的程序就立马有了那些功能。还可以按上下键翻阅历史命令，ctrl+r 来搜索历史输入等。</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://xiezhenye.com/2011/11/%e8%ae%a9-linux-%e4%ba%a4%e4%ba%92%e5%bc%8f%e5%91%bd%e4%bb%a4%e8%a1%8c%e7%a8%8b%e5%ba%8f%e6%94%af%e6%8c%81%e6%96%b9%e5%90%91%e9%94%ae%e7%ad%89%e5%8a%9f%e8%83%bd.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

