<?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>basic coder</title>
	<atom:link href="http://basiccoder.com/feed" rel="self" type="application/rss+xml" />
	<link>http://basiccoder.com</link>
	<description>I am just a coder...</description>
	<lastBuildDate>Fri, 19 Apr 2013 17:25:34 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>读书笔记-《细说清朝》</title>
		<link>http://basiccoder.com/note-qing.html</link>
		<comments>http://basiccoder.com/note-qing.html#comments</comments>
		<pubDate>Fri, 19 Apr 2013 17:22:50 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[读书笔记]]></category>
		<category><![CDATA[历史]]></category>
		<category><![CDATA[读书]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1113</guid>
		<description><![CDATA[读历史首先是要看写这段历史的人的口碑和人品，再去看读者对这本书的评价，历史被歪曲的太多，没有经过沉淀的历史书籍从不敢读，黎东方教授的细说系列长久以来被认为是历史读物中的上品，而清朝又是和我们衔接最紧密的一段历史，对于清朝发生的那些事情多多少少都有些了解，却从来没有完整客观地知道那个时代都经历过什么，也从来没有对那个时代的人有一个客观的评价，于是鼓起勇气读完了《细说清朝》。

读这本书感觉作者仿佛一位说书人，将那个年代的历史对你娓娓道来，清朝瞬间的崛起，短暂的辉煌，上百年的内忧外患都在本书中尽然展现，这本书更好地帮我们了解了那个时代的历史，更好地了解了那个时代的人。

中国的历史是帝王的历史，相比之下美国的历史则是平民的历史，这一点也不假，那么厚的一本《光荣与梦想》也不过讲了区区四十年的美国历史，刨除几个大的历史事件，绝大多数篇幅都在描述民众生活常态，以至于读起来会感觉枯燥很多，甚至一部《阿甘正传》都能概括美国大半部历史，相比之下四十年在中国的历史长河中不过是弹指一瞬，中国有太多的四十年，有太多的朝代更迭，帝王更替，而在这漫长的历史中最不值钱的也最不值得一提的便是平民百姓，思想被禁锢被奴化的中国人最容易被历史淡忘，于是历史上只留下了帝王将相。... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>读历史首先是要看写这段历史的人的口碑和人品，再去看读者对这本书的评价，历史被歪曲的太多，没有经过沉淀的历史书籍从不敢读，黎东方教授的细说系列长久以来被认为是历史读物中的上品，而清朝又是和我们衔接最紧密的一段历史，对于清朝发生的那些事情多多少少都有些了解，却从来没有完整客观地知道那个时代都经历过什么，也从来没有对那个时代的人有一个客观的评价，于是鼓起勇气读完了《细说清朝》。</p>
<p>读这本书感觉作者仿佛一位说书人，将那个年代的历史对你娓娓道来，清朝瞬间的崛起，短暂的辉煌，上百年的内忧外患都在本书中尽然展现，这本书更好地帮我们了解了那个时代的历史，更好地了解了那个时代的人。</p>
<p>中国的历史是帝王的历史，相比之下美国的历史则是平民的历史，这一点也不假，那么厚的一本《光荣与梦想》也不过讲了区区四十年的美国历史，刨除几个大的历史事件，绝大多数篇幅都在描述民众生活常态，以至于读起来会感觉枯燥很多，甚至一部《阿甘正传》都能概括美国大半部历史，相比之下四十年在中国的历史长河中不过是弹指一瞬，中国有太多的四十年，有太多的朝代更迭，帝王更替，而在这漫长的历史中最不值钱的也最不值得一提的便是平民百姓，思想被禁锢被奴化的中国人最容易被历史淡忘，于是历史上只留下了帝王将相。</p>
<p>从这本书中重新认识了李鸿章，历史课本中总是会给他戴上误国卖国的帽子，也正是他在那些不平等条约上签字，可现实的李鸿章虽比不过曾国潘清廉，但也不是个卖国的小人，正直与否我不敢说，但他确实是个好的外交家，虽然关于条约的周旋其实本无意义，弱国无外交的道理我们都懂，但李也没有轻易地做最后的让步，从书中我看到的不是李鸿章卑躬屈膝的奴才嘴脸，而是一个为了国家民族利益奔波的古稀老人，至于李的人品到底如何我的认知水平不敢枉下定论，俄国人说给了李巨额贿赂，黎东方在书中也对此提出质疑，一方面没有确凿的证据，另一方面一个七十多岁的老人，行将就木，而且处在那样一个乱世，要那么多钱有何用？我也并非在替李辩护，只是之前在认识能力有限的时候被灌输了太多关于他不客观的评价，想改变一下认识而已。</p>
<p>历史是人写的，总会有不经意的扭曲或者臆断，司马迁的史记也未必完全客观，三皇五帝的历史应该大多数靠传说吧，更不用说市井中的刺客列传，但这都可以理解，而对历史的刻意扭曲就只能持抨击的态度了，中国每一个专制的政权上台以前做的很重要的事情就是修订前朝历史，而更重要的事情是把对本朝不利的历史给抹掉或曲解，所以我不喜欢读抗战的历史，专制执政党出版的历史往往都不是真正的历史，现实中都有那么多隐藏的真相，更不用说那段曲折的历史了，我对于历史的兴趣不在于研究，只是仅仅想知道过去都发生过什么事，想对印象中的那些人有个客观公正的评价，仅此而已，经过篡改的历史说成思想的毒药应该不算过份。</p>
<p>清朝写的明史据说前半部还算客观，后半部明清交替的时候自然就倾向于美化清朝，有人说袁崇焕在清朝撰写的明史中被从误国改成了民族英雄，至于袁崇焕有没有误国我不敢说，但他却实用了不少昏招，印象最深刻的是他绝不该杀毛文龙，他在关外一系列举措加速了明朝的灭亡，虽然当时的明朝确实“气数已尽”，但如果当时将袁崇焕换成孙承宗，相信明朝不至于亡地那么快。</p>
<p>回想一下清朝似乎没有多少平静的年月，从努尔哈赤起兵反清到多尔衮带兵入关，这个过程一直都是战乱，终于到了康熙这一朝平定了三藩，收复了台湾，又平了西北，算得上安定，黎东方对康熙的评价很高，事实上也只是说康熙是个好皇帝而已，但翻遍本书也只是对康熙一人有这样的评价，而且对于一个的皇帝的评价说他好，那就说明他确实做过一些了不起的事情，至少不会是个昏君，而传说中的康乾盛世，也不过是乾隆占了一点祖宗的光而已，在乾隆那一朝就慢慢地开始有反清的势力抬头了，于是到了嘉庆各路反清势力大规模活动起来，白莲教天理教天地会，到了道光年间发生了著名的鸦片战争，再到了咸丰年间，内忧外患一起来，太平天国兴起，各咱联军一起涌了进来，想想当一个乱世的帝王真不如当一个治世的平民。</p>
<p>年幼的光绪皇帝，四岁本是在父母呵护之下无忧无虑玩耍的年纪，却被无情地推上了政治的风口浪尖，认识到中国残酷的现状后他励志图改，只可惜维新只持续了百日，日本的明治维新让日本由一个原本靠抢劫中国沿海的蛮夷国家，跻身于世界强国之列，徜若光绪皇帝的变法行动能顺利执行，那中国未来一百年的历史或许会明朗许多，但时局已定，清朝贯彻了两百年太监后宫不能干政，确实未曾出现过像王振魏忠贤那样的太监，可惜最终却亡国于后宫之手，也真是讽刺。</p>
<p>在清朝所有这些王公大臣中最感兴趣的不是曾国藩，不是李鸿章，更不是和坤，是第二次鸦片战争时期的广东巡抚叶名琛，记不清有没有在历史课本上读过此人，但这个人的作风确实让人不可思议，对他的评价也往往趋向于两个极端，有人把他称做“六不”总督，“不战、不和、不守，不死、不降、不走；相臣度量，疆臣抱负；古之所无，今之罕有”，是居于天朝的傲慢还是一种绝望中的坦然，这些都说不清楚了，总督衙门被炮轰他仍能处之坦然，虽不作为但也不贫生不卖国，最后被俘绝食而死，也有封疆大吏应有的气度，在当时的环境下出战其实也是徒增损伤，而他偏偏又不议和，这是出于民族气节还是有其它原因我想我们后世人很难说清楚了，对于我只是知道历史上曾经有过这样一个人，至少在现在我还做不出对他客观的评价。</p>
<p>读中国的历史已经不需要再去思考“这个朝代为何灭亡”这样的问题了，用公知最喜欢说的一句话就可以概括“体制有问题”，时下的体制都有问题，更不用说那些高度集权的封建王朝了，一个王朝的兴起可能由一个人主导，王朝的长治久安却绝不是一个人能主导的了的，倘若说康熙是个好皇帝，但却不能保证每个皇帝都是康熙，也不能保证在今天的政治背景下康熙是个好的皇帝，在明天的政治背景下康熙还是个好皇帝，历史有太多的偶然有太多的变数，权力没有制衡历史就很容易被引导到错误的方向，从而一发而不可收，而这过程中损失最大的是平民百姓，时至今日的中国打着宪政的幌子，骨子里还是前朝的模式，这样的社会不敢期望长治久安。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/note-qing.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>记一下那些伪随机数生成函数</title>
		<link>http://basiccoder.com/pseudo-random-func-note.html</link>
		<comments>http://basiccoder.com/pseudo-random-func-note.html#comments</comments>
		<pubDate>Wed, 27 Mar 2013 10:22:45 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[random]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1103</guid>
		<description><![CDATA[今天踩了一个伪随机数生成函数的坑，与其说是个坑到不如说自己功力不够深厚，对这些随机数生成的函数族欠缺了解，先来介绍下我的问题吧。

拿了100M的数据用LDA算法来跑，单线程的时候一次迭代需要大约15s的时间，而改用两个线程跑，线程之间对不同的数据块进行迭代，一次迭代完成的时间居然需要大约50s，而且线程数越多就会越慢，因为迭代的采样函数是一个纯计算的函数，多线程的情况下每个线程的数据量要比单线程成倍地缩小，迭代速度相比单线程应该快N倍才对，现在的结果是不符合常理的，因为每个线程都是独立的训练集，线程之前没有共享数据。

其实在我用几M的小数据测试的时候就发现这个问题，当时以为是数据量小迭代速度太快，以至于在迭代完成后的线程同步占用了一些时间才导致多线程版本会慢于单线程版本，但数据量大的时候这样就不可理解了。

后来开了两个线程发现，迭代计算的时候两个CPU大部分时间居然都没有跑在用户空间，相反大量的cpu时间都耗在了系统时间，仔细看了代码，这部分就只有一个random()函数不是我的计算函数，改成定值之后测试速度马上彪上去了，那就可以断写问题就出在这里。

random()函数是不可重入的函数，不保证它是线程安全的，但我看了glibc的代码发现， 其实它在glibc的实现里面是线程安全的：... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>今天踩了一个伪随机数生成函数的坑，与其说是个坑到不如说自己功力不够深厚，对这些随机数生成的函数族欠缺了解，先来介绍下我的问题吧。</p>
<p>拿了100M的数据用LDA算法来跑，单线程的时候一次迭代需要大约15s的时间，而改用两个线程跑，线程之间对不同的数据块进行迭代，一次迭代完成的时间居然需要大约50s，而且线程数越多就会越慢，因为迭代的采样函数是一个纯计算的函数，多线程的情况下每个线程的数据量要比单线程成倍地缩小，迭代速度相比单线程应该快N倍才对，现在的结果是不符合常理的，因为每个线程都是独立的训练集，线程之前没有共享数据。</p>
<p>其实在我用几M的小数据测试的时候就发现这个问题，当时以为是数据量小迭代速度太快，以至于在迭代完成后的线程同步占用了一些时间才导致多线程版本会慢于单线程版本，但数据量大的时候这样就不可理解了。</p>
<p>后来开了两个线程发现，迭代计算的时候两个CPU大部分时间居然都没有跑在用户空间，相反大量的cpu时间都耗在了系统时间，仔细看了代码，这部分就只有一个random()函数不是我的计算函数，改成定值之后测试速度马上彪上去了，那就可以断写问题就出在这里。</p>
<p>random()函数是不可重入的函数，不保证它是线程安全的，但我看了glibc的代码发现， 其实它在glibc的实现里面是线程安全的：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">long</span> <span style="color: #993333;">int</span>
__random <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #993333;">int32_t</span> retval<span style="color: #339933;">;</span>
&nbsp;
  __libc_lock_lock <span style="color: #009900;">&#40;</span>lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span> __random_r <span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>unsafe_state<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>retval<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  __libc_lock_unlock <span style="color: #009900;">&#40;</span>lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #b1b100;">return</span> retval<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
weak_alias <span style="color: #009900;">&#40;</span>__random<span style="color: #339933;">,</span> random<span style="color: #009900;">&#41;</span></pre></div></div>

<p>这样就很清楚了，glibc的random()实现实际是线程安全的，但不可重入，因为在一开始就拿了一把锁，也就是因为这把锁导致了我的程序性能下降如此厉害。</p>
<p>那rand()函数又是怎样的呢？rand()的manual手册里面的介绍也是说，rand()不是线程安全的函数，但其实glibc的实现里面它仍然是线程安全的，再看看glibc的代码：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* Return a random integer between 0 and RAND_MAX.  */</span>
<span style="color: #993333;">int</span>
rand <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">return</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span><span style="color: #009900;">&#41;</span> __random <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>这样就很清楚了，在glibc的实现里面，rand()和random()实际上是同一个函数，虽然返回值不一样，但其实random()返回的也只是个32位的值而已，二者没什么不同。只是POSIX标准说它不是线程安全的，但不同的实现有不同的做法，比如glibc就是例外。</p>
<p>要想使它随机生成函数线程安全，就要使用它们的可重入版本，看看rand_r()这个函数的实现(以前一直不理解这个_r后缀的意思，现在知道了它是可重入的意思。。。)：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span>
rand_r <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>seed<span style="color: #009900;">&#41;</span>                                             
<span style="color: #009900;">&#123;</span> 
  <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> next <span style="color: #339933;">=</span> <span style="color: #339933;">*</span>seed<span style="color: #339933;">;</span>                                           
  <span style="color: #993333;">int</span> result<span style="color: #339933;">;</span>
&nbsp;
  next <span style="color: #339933;">*=</span> <span style="color: #0000dd;">1103515245</span><span style="color: #339933;">;</span>
  next <span style="color: #339933;">+=</span> <span style="color: #0000dd;">12345</span><span style="color: #339933;">;</span>
  result <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span>next <span style="color: #339933;">/</span> <span style="color: #0000dd;">65536</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">%</span> <span style="color: #0000dd;">2048</span><span style="color: #339933;">;</span>
&nbsp;
  next <span style="color: #339933;">*=</span> <span style="color: #0000dd;">1103515245</span><span style="color: #339933;">;</span>
  next <span style="color: #339933;">+=</span> <span style="color: #0000dd;">12345</span><span style="color: #339933;">;</span>
  result <span style="color: #339933;">&lt;&lt;=</span> <span style="color: #0000dd;">10</span><span style="color: #339933;">;</span>
  result <span style="color: #339933;">^=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span>next <span style="color: #339933;">/</span> <span style="color: #0000dd;">65536</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">%</span> <span style="color: #0000dd;">1024</span><span style="color: #339933;">;</span>
&nbsp;
  next <span style="color: #339933;">*=</span> <span style="color: #0000dd;">1103515245</span><span style="color: #339933;">;</span>
  next <span style="color: #339933;">+=</span> <span style="color: #0000dd;">12345</span><span style="color: #339933;">;</span>
  result <span style="color: #339933;">&lt;&lt;=</span> <span style="color: #0000dd;">10</span><span style="color: #339933;">;</span>
  result <span style="color: #339933;">^=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span>next <span style="color: #339933;">/</span> <span style="color: #0000dd;">65536</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">%</span> <span style="color: #0000dd;">1024</span><span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #339933;">*</span>seed <span style="color: #339933;">=</span> next<span style="color: #339933;">;</span>
&nbsp;
  <span style="color: #b1b100;">return</span> result<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>只需要给每个线程保存各自独立的seed，那rand_r这个函数就可重入喽，但这个函数的算法显然比较弱，看它的手册里面是这样说的：</p>
<pre>
This is a very small amount of state, so this function will be a weak pseudo-random generator. Try drand48_r(3) instead.
</pre>
<p>看看drand48这一系列的函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">YNOPSIS
       <span style="color: #339933;">#include &lt;stdlib.h&gt;</span>
&nbsp;
       <span style="color: #993333;">double</span> drand48<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">double</span> erand48<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> xsubi<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">3</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">long</span> <span style="color: #993333;">int</span> lrand48<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">long</span> <span style="color: #993333;">int</span> nrand48<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> xsubi<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">3</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">long</span> <span style="color: #993333;">int</span> mrand48<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">long</span> <span style="color: #993333;">int</span> jrand48<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> xsubi<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">3</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">void</span> srand48<span style="color: #009900;">&#40;</span><span style="color: #993333;">long</span> <span style="color: #993333;">int</span> seedval<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> <span style="color: #339933;">*</span>seed48<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> seed16v<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">3</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">void</span> lcong48<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> param<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">7</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>应该来说，像erand48，nrand48这些应该都可以设计成线程安全的，我没有仔细去读这些函数的源码，因为实现都比较复杂，但有一点可以确定，他们在生成随机数的时候不仅仅只需要个种子，一方面它们需要一个类型为struct drand48_data的种子，另一方面也需要一个unsigned short xsubi[3]这样的状态数组，通过这两组变量来生成随机数，struct drand48_data我理解的是它是只读的，只要在多线程的过程中不修改它，那么就可以把erand48(),nrand48()这些函数作为线程安全的来用，但如果在运行过程中调用lcong48()改变了drand48_data的值，那恐怕就不能保证线程安全了。</p>
<p>drand48系统也有对应的可重入版本：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">       <span style="color: #339933;">#include &lt;stdlib.h&gt;</span>
&nbsp;
       <span style="color: #993333;">int</span> drand48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">double</span> <span style="color: #339933;">*</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">int</span> erand48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> xsubi<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">3</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
                     <span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">double</span> <span style="color: #339933;">*</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">int</span> lrand48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">long</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">int</span> nrand48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> <span style="color: #993333;">int</span> xsubi<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">3</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
                     <span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">long</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">int</span> mrand48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span><span style="color: #993333;">long</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">int</span> jrand48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> <span style="color: #993333;">int</span> xsubi<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">3</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
                     <span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #339933;">,</span> <span style="color: #993333;">long</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">int</span> srand48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">long</span> <span style="color: #993333;">int</span> seedval<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">int</span> seed48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> <span style="color: #993333;">int</span> seed16v<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">3</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
                    <span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
       <span style="color: #993333;">int</span> lcong48_r<span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">short</span> <span style="color: #993333;">int</span> param<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">7</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>
                     <span style="color: #993333;">struct</span> drand48_data <span style="color: #339933;">*</span>buffer<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>这样子问题就简单了，只要每个线程都维护自己的drand48_data变量和xsubi数组，那erand48_r(), nrand48_r就可以保证线程安全又可重入了。哦，再补充一点，这些函数不应该算是严格的可重入，应该叫隐式可重入？如果这些传递进来的指针都指向一块共享地址的话，那这些函数同样是不可重入的喽。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/pseudo-random-func-note.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>读书笔记-《观念的水位》</title>
		<link>http://basiccoder.com/note-gndsw.html</link>
		<comments>http://basiccoder.com/note-gndsw.html#comments</comments>
		<pubDate>Sun, 17 Mar 2013 14:49:41 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[读书笔记]]></category>
		<category><![CDATA[读书]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1097</guid>
		<description><![CDATA[网络上对于刘瑜的评论似乎趋向于两个极端，说她好的自然大有人在，说她不好的也不在少数，还有人调侃地把她和安妮宝贝和孤郭敬明相提并论，不同的人有不同的政治立场，刘瑜作为一名时评作家，她的观点自然有人赞同有人反对，这都无可厚非，不管是《民主的细节》还是刚读完的这本《观念的水位》，对我而言都是非常有营养的。

根据百度百科里面的介绍，刘瑜在《南方周末》写时评专栏，在《新周刊》写书评和影评专栏，也正像作者在序言中提到的，《观念的水位》这本书就是这些专栏文章的合集，所以前半部分大多是时政评论，后半部分就是书评和影评，我对书评和影评不太感冒，没有读过的书没看过的电影被别人一评价，这书这电影再看起来就没有原来的味道了，观点就会不自觉得被这些评论牵着鼻子走，所以对我而言前半部分的吸引力要远大于后半部分。这本书把一些离散的文章集合在一起，作者也没有刻意地去让他们之前有什么衔接，所以读起来思维可能需要有一点跳跃，比如前一篇在讲伯林墙，后一篇就已经在说曾国潘了。

书中有一篇文章名字和书名相同，以这篇文章的标题作为书名，我相信作者有她的意图，此书的出版意在提高读者的政治观念，文化观念，历史观念，因为只有民众观念的提高才能促成社会变革，这篇文章中有一段话我很赞同，在这里引用一下：

我心中理想的社会变革是一个“水张船高”的地程：政治制度的变革源于公众政治观念的变化，而政治观念的变化又根植于人们生活观念的变化。水涨了，般自然浮起来。我观察社会变革的动力，不那么关注船上有没有技艺高超的船夫，而更关注水位的变化... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>网络上对于刘瑜的评论似乎趋向于两个极端，说她好的自然大有人在，说她不好的也不在少数，还有人调侃地把她和安妮宝贝和孤郭敬明相提并论，不同的人有不同的政治立场，刘瑜作为一名时评作家，她的观点自然有人赞同有人反对，这都无可厚非，不管是《民主的细节》还是刚读完的这本《观念的水位》，对我而言都是非常有营养的。</p>
<p>根据百度百科里面的介绍，刘瑜在《南方周末》写时评专栏，在《新周刊》写书评和影评专栏，也正像作者在序言中提到的，《观念的水位》这本书就是这些专栏文章的合集，所以前半部分大多是时政评论，后半部分就是书评和影评，我对书评和影评不太感冒，没有读过的书没看过的电影被别人一评价，这书这电影再看起来就没有原来的味道了，观点就会不自觉得被这些评论牵着鼻子走，所以对我而言前半部分的吸引力要远大于后半部分。这本书把一些离散的文章集合在一起，作者也没有刻意地去让他们之前有什么衔接，所以读起来思维可能需要有一点跳跃，比如前一篇在讲伯林墙，后一篇就已经在说曾国潘了。</p>
<p>书中有一篇文章名字和书名相同，以这篇文章的标题作为书名，我相信作者有她的意图，此书的出版意在提高读者的政治观念，文化观念，历史观念，因为只有民众观念的提高才能促成社会变革，这篇文章中有一段话我很赞同，在这里引用一下：</p>
<p>我心中理想的社会变革是一个“水张船高”的地程：政治制度的变革源于公众政治观念的变化，而政治观念的变化又根植于人们生活观念的变化。水涨了，般自然浮起来。我观察社会变革的动力，不那么关注船上有没有技艺高超的船夫，而更关注水位的变化。</p>
<p>网络兴起确实促进了公众观念的提高，虽然我们的信息仍然受到层层封锁，但信息流速之快总让我们有意无意间捕捉到那些残酷的社会现实，当満大街的人都在谈论民主，都在渴望民主的时候，那民主的到来就真的不远了，倘若民众仍然蒙昧地相信”光荣伟大正确“这些空洞的字眼，那我们期待的民主恐怕永远不会到来，哲学家告诉我们没有什么东西是绝对的，辩证法简单而生硬，所谓的”光荣伟大正确“经不起任何一个角度的推敲，岂图依靠洗脑来维系的政权和封建的伦理道德有什么区别，言论都谈不上自由哪里来的什么民主呢，又哪里谈得上什么伟大光荣正确呢。</p>
<p>刘瑜《在民主的细节》一书中给我们讲述了美国的民主制度，我不敢肯定书中她的观点绝对正确，比如提到伊战我们脑海中就不自觉地浮现出”霸权主义“这样的词眼，甚至都没有去思考，不禁有点可怕，刘瑜站在了美国政府的一边，她认为伊战是民主国家的民主输出，她给出的是一个全新的观点，我虽不敢轻易赞同，但也绝没有理由否定，这些国际上的政治决策，我觉得也不是我读一两本跟政治有关的书就可以轻易理解的。不管观点正确与否，刘瑜的文字都很诚肯，有一种让人信服的力量，再配合上她给出的相关数据，就让人更加无言反对了。</p>
<p>扯开伊战不说，西方国家的民主才真的可以称地上是民主，而且把西方国家的政治制度拿来和我们国家做一个简单的对比，就会发现其实政治也是一种科学，读我国历代帝王的历史，经常会尝试去想如果我坐在帝王的位置上该如何去管理这样一个庞大的国家，才可以对人民不失公允，思前想后才发现这绝不是一个人能完成的事情，政治制度本身就是一种科学，靠自觉或者靠道德约束的制度都不靠谱，孔子说仁，孟子说向善，无疑都是在凸显人性中的善，那人性中的恶呢，人是社会的动物，如果你说人性本善，那怎么解释文革时期疯狂的红卫兵，又怎么解释二战时泯灭人性的纳粹，体制内不可能人人都是海瑞，没有三权分立的相互制约，空谈民主空谈自由都是徒劳。</p>
<p>回想起我们国家的历史，那真是不折不扣的血泪史，封建王朝的历史自然不必言说，那些在当家做主站起来的先辈，在大饥荒中饿死一批，在文革中整死一批，最后活下来的又是什么遭遇，我从来不敢去读关于文革的书，太过于压抑，你不敢想象那些残酷的事情就发生在我们这样一个人民民主专政的国家，对了，什么叫人民民主专政呢，专政这样的词眼又如何能跟民主捆绑在一起呢，还是说我们的民主其实又是中国特色社会主义的民主，还是说所谓的人民当家做主其实就是王朝更迭中帝王口中的”为民请愿“，不过是一个王朝取代另一个王朝冠冕堂皇的说词，换作现代就是一个政党取代另一政党的说词。</p>
<p>更多的时候即使我们这些从学校走出来的人也被历史课本蒙蔽了双眼，考研时反复背诵地群众路线，说白了就是打地主分田地，简单暴力而又直接，刘瑜从另一个角度给我们揭开了历史的面纱，难道国民党就不懂得团结群众了吗，这也是我一直迷惑的地方，蒋公眼里就真的只有”剿匪“？对于他们这些熟读历史的政治家来说，不会不懂得群众的力量这种事情，国民党也曾实行过土改，只不过作为当时国内唯一的合法政党，国民党实行的是温和的和平土改，政府使用土地债券从地主手中买地，再让农民通过分期付款的方式低价从政府手中购买土地，对于农民和地主都是一种双赢的方式，关于和平土改缘何失败，有各种各样的说法，但刘瑜给的结论最能让人接受，也最一针见血，打土豪分田地这种办法简单粗暴，农民本来就对地主没有好感，突然有人告诉你说又可以打地主又可以免费分田地，多么现实的问题，在那种历史背景下，在那种文化程度下，农民自然会选择这种原始暴力的方式。</p>
<p>我们生活的社会不像书本中描绘的那样美好，有太多的残酷的历史被从书本中抹掉，有太多残酷的现实被从网络中抽离，我们吃不上安全食品就去骂不良商家，买不起房就去骂地产商，试问一个有良性制度的政府会允许这些事情出现吗，民生这种字眼都已经显得空洞无力了。</p>
<p>时代不一样了，民众的观念也在提高，相信历史总有一天会播开云雾。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/note-gndsw.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>读书笔记-《万历十五年》</title>
		<link>http://basiccoder.com/note-wlswn.html</link>
		<comments>http://basiccoder.com/note-wlswn.html#comments</comments>
		<pubDate>Sat, 09 Mar 2013 15:06:36 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[读书笔记]]></category>
		<category><![CDATA[读书]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1091</guid>
		<description><![CDATA[多书，科幻玄幻政治历史，有些书是用来娱乐打发时间，有些书是为了开阔眼界，丰富内涵，每读完一本总会有些许的思考，但却一直没有把读完这本书带来的收获记录下来，今天决定开始对读过的>每本书写读书笔记，不能让这些书的内容只是作为过眼云烟，记录下来也算对自己有所沉淀。

黄仁宇的这本《万历十五年》是从杭州回北京的飞机上读完的，读这本书花了不少时间，这期间也主要因为工作太忙，工作之余还要抽出时间来补专业知识，所以就冲淡了读这些文学作品的时间。出差唯一的好处是可以>在旅途中读书，这个过程是很享受的。

《万历十五年》这本书写的是万年皇帝第十五年也就是公元1587年前前后后的历史，作者黄仁宇博士履历丰富，学识也很渊博，《万历十五年》在我看来没有博人眼球的华美文字，字里行间也是朴实无华，作者站在一个>后来人的角度上客观公正地去审视那个年代的历史，为我们重现了一个典型的中国封建王朝有政治和民生... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>过去的一年里读了好多书，科幻玄幻政治历史，有些书是用来娱乐打发时间，有些书是为了开阔眼界，丰富内涵，每读完一本总会有些许的思考，但却一直没有把读完这本书带来的收获记录下来，今天决定开始对读过的每本书写读书笔记，不能让这些书的内容只是作为过眼云烟，记录下来也算对自己有所沉淀。</p>
<p>黄仁宇的这本《万历十五年》是从杭州回北京的飞机上读完的，读这本书花了不少时间，这期间也主要因为工作太忙，工作之余还要抽出时间来补专业知识，所以就冲淡了读这些文学作品的时间。出差唯一的好处是可以在旅途中读书，这个过程是很享受的。</p>
<p>《万历十五年》这本书写的是万年皇帝第十五年也就是公元1587年前前后后的历史，作者黄仁宇博士履历丰富，学识也很渊博，《万历十五年》在我看来没有博人眼球的华美文字，字里行间也是朴实无华，作者站在一个后来人的角度上客观公正地去审视那个年代的历史，为我们重现了一个典型的中国封建王朝的政治和民生。</p>
<p>时处明朝的中国在世界上还是一个辉煌的国度，但其实是在走向没落的边缘，当时的政治体制就已经决定了明朝的没落是不可避免的。</p>
<p>不得不说，从读过关于明朝的书籍来看，明朝的政治制度相比其它的朝代还是有一定的优越性的，文官集团的斗争虽然是王朝落没的关键因素，它也同样带有一点民主的意思，低级别的文官敢于上书弹劾比自己官位高得多的官员，甚至弹劾自己的领导，更有甚者有人公开上书指点皇上的是非，很多文官也有着他们自己的诤诤铁骨，虽然这远远称不上民主，但说它有一点民主的意思我觉得也不过份。</p>
<p>作者在本书中多次明确地阐述，封建王朝最终落没的原因，在于统治者总是试图通过道德来约束人民，而没有建立完善的法律框架，于是四书五经三从四德等等这些就在那个年代发挥了巨大的作用，普通百姓想要入朝为官就只有科举这一条出路，科举的内容也无非是四书五经圣人之言，朱熹的批注也被指定成唯一的官方正确版，如果说现在的中国社会没有言论自由，那封建社会的学者连思想自由都没有，由于缺乏完善的法律框架，统治者就必须将这些圣人之言作为人民日常生活的准绳，以此来教化人民，而那时的中国是一个典型的农业社会，人民知识水平和智力水平低下，依靠这些很容易愚化百姓，但仅仅凭道憄上的约束是远远不够的，法治不健全就会导致各项政策难以推动，就更不用说官员钻各种空子了，这些问题即便在今天的中国，有了一定的法治基础都还远远没有解决，更不用说在那个落后的年代了。</p>
<p>《万历十五年》通过几个历史事件和几个历史人物刻画了那个年代的历史，作者挑选的那个时代的几个人物也各有他们的特点。</p>
<p>张居正是一个改革派，作为皇帝的老师同时也受到太后的青睐，手上有着至高无上的权力，由他来推行变革可以说是顺水推舟，张居正虽然生活上远不能算是个清官，作风上也受人非议，但通过这些历史作品可以看出他也确实心怀天下，结交党羽也是为了能在朝中取得更多的支持，以此来作为政治基础来推行他的改革，虽然张居正生活不够清廉，但在他的努力之下民生确实有所改善。需要明白，但凡变革就必然会触动很多人的利益，张居正虽然到死也没有卸任，但在他死后弹劾他的言论纷至沓来，最终在死后还得到清算，因为有太多人的利益受到了触动，弹劾地人如此之多以至于和张居正有过密切联系的人也大都没有什么好下场，一代名将戚继光之所以在死后三年仍不能见容于万历皇帝，也是因为他之前与张居正交往过于密切。</p>
<p>海瑞印象中应该出现过在历史课本上，各个地方给他们标题都是“清官”，为官清廉地有些极端，记得在《明朝那些事儿》里面作者写到有一次海瑞买了两斤肉，卖肉的屠夫激动万分，感叹居然在有生之年还能做成海县令的生意，海瑞清廉地有些洁癖，在那样的政治背景之下，如此正直的官是不容易于他人的，正直是必要的，但变通仍不可或缺，海瑞岂图靠一人之力构建和谐社会这样的打算很显然是不会成功的，还是绕回来，没有统一的制度，单靠一已之力是不可能对社会现状有颠覆性的改变的。</p>
<p>相比于海瑞，戚继光则有他的过人之处，他的军事才能无可挑剔，即使没有读过明史的人提到戚继光也会不自觉地在他名字前面加上个“抗倭英雄”，戚继光练兵有他自己的手段，用兵也有自己的方法，以至于在和倭寇的战争戚家军几乎未尝过败绩。看看明朝其它的一些悲剧人物就可以知道，单打独斗是成不了气侯的，海瑞一生正直如此，最终其实也是壮志未酬，而戚继光可以说成是识实务者，他懂得适应当前的形势来做出决断，又懂得结交朝中大臣，张居正就对他赏识有加，可以说有张居正在朝中的大力支持，戚继光才能在外更好地发挥他的军事才能。</p>
<p>书中还写到了内阁首辅申时行，哲学家李贽，但总起来讲没有哪个历史人物有能力靠一己之力改变历史的，历史的车轮滚滚向前，也只不过是汇聚了众多历史人物的力量，由底层的人民大众推动向前的，《万历十五年》是本好书，让我们更清晰地了解那个时代的历史。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/note-wlswn.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sheepdog块设备驱动死锁的问题</title>
		<link>http://basiccoder.com/sheepdog-block-device-driver-deadlock.html</link>
		<comments>http://basiccoder.com/sheepdog-block-device-driver-deadlock.html#comments</comments>
		<pubDate>Tue, 15 Jan 2013 13:46:30 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Sheepdog]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1075</guid>
		<description><![CDATA[Sheepdog的块设备驱动写好有一段时间了，陆续修改了几个版本之后，近期进行压测的时候遇到一个死锁的问题，头痛了一个多星期，今天请教了一下淘宝内核组的<a href="http://weibo.com/pagefault">@伯瑜</a>同学，在他的热情帮助下分析出来了死锁出现的原因，解决的办法暂未找到，或者说这问题无解，待我细细说来。

问题是这样的，在把Sheepdog中的某个VDI挂载成块设备之后(/dev/sheepa)，我在sheepa上安装了一个debian，然后启动QEMU，启动盘就设为/dev/sheepa:

<pre lang="bash">
qemu-system-x86_64 -m 512 -hda /dev/sheepa
</pre>

启动完成后开始狂做IO，也没用啥复杂的办法，就是dd
<pre lang="bash">
dd if=/dev/zero of=xxx bs=4096 count=1000000
</pre>

就这样在写入几个G的数据后就有可能会出现死锁，而且出现的机率很低，复现一次要花好长时间，有时候几十个G的磁盘都被Sheepdog写满了也复现不出来。死锁发生时，sheep其中一个进程会D住(一个sheep会启动两个进程，一个是主进程，一个是日志进程)，这时候QEMU也会hang住，甚至在有些极端的情况下ps -ef也会hang住。

我在sheep block driver里面加入debug信息定位到hang住的位置，是在read_reply()这个函数里面，这个函数是在等待sheep的请求响应，这个时候sheep已经hang住当然不会响应，于是QEMU的IO一直没有返回，也会一直hang在那里，可为什么sheep会hang住呢，这个问题是当时我没想明白的，后来经过伯瑜大神的指点才明白过来... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>Sheepdog的块设备驱动写好有一段时间了，陆续修改了几个版本之后，近期进行压测的时候遇到一个死锁的问题，头痛了一个多星期，今天请教了一下淘宝内核组的<a href="http://weibo.com/pagefault">@伯瑜</a>同学，在他的热情帮助下分析出来了死锁出现的原因，解决的办法暂未找到，或者说这问题无解，待我细细说来。</p>
<p>问题是这样的，在把Sheepdog中的某个VDI挂载成块设备之后(/dev/sheepa)，我在sheepa上安装了一个debian，然后启动QEMU，启动盘就设为/dev/sheepa:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">qemu-system-x86_64 <span style="color: #660033;">-m</span> <span style="color: #000000;">512</span> <span style="color: #660033;">-hda</span> <span style="color: #000000; font-weight: bold;">/</span>dev<span style="color: #000000; font-weight: bold;">/</span>sheepa</pre></div></div>

<p>启动完成后开始狂做IO，也没用啥复杂的办法，就是dd</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">dd</span> <span style="color: #007800;">if</span>=<span style="color: #000000; font-weight: bold;">/</span>dev<span style="color: #000000; font-weight: bold;">/</span>zero <span style="color: #007800;">of</span>=xxx <span style="color: #007800;">bs</span>=<span style="color: #000000;">4096</span> <span style="color: #007800;">count</span>=<span style="color: #000000;">1000000</span></pre></div></div>

<p>就这样在写入几个G的数据后就有可能会出现死锁，而且出现的机率很低，复现一次要花好长时间，有时候几十个G的磁盘都被Sheepdog写满了也复现不出来。死锁发生时，sheep其中一个进程会D住(一个sheep会启动两个进程，一个是主进程，一个是日志进程)，这时候QEMU也会hang住，甚至在有些极端的情况下ps -ef也会hang住。</p>
<p>我在sheep block driver里面加入debug信息定位到hang住的位置，是在read_reply()这个函数里面，这个函数是在等待sheep的请求响应，这个时候sheep已经hang住当然不会响应，于是QEMU的IO一直没有返回，也会一直hang在那里，可为什么sheep会hang住呢，这个问题是当时我没想明白的，后来经过伯瑜大神的指点才明白过来。</p>
<p>用sysrq-trigger查看各进程的状态，sysrq-trigger是个好东西，调试内核相关的代码还是不能少了这些工具的帮助，文档在这里<a href="http://kernel.org/doc/Documentation/sysrq.txt">http://kernel.org/doc/Documentation/sysrq.txt</a></p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #7a0874; font-weight: bold;">echo</span> t <span style="color: #000000; font-weight: bold;">&gt;</span> <span style="color: #000000; font-weight: bold;">/</span>proc<span style="color: #000000; font-weight: bold;">/</span>sysrq-trigger</pre></div></div>

<p>echo t的作用就是把当前所有进程和状态都打到/var/log/messages里面去，看一下D住的两个进程sheep和QEMU的状态：</p>
<blockquote style="line-height:18px;text-indent:0em;">
<p>[10388.111972] sheep           D ffff88011e213580     0  2522      1 0&#215;00000080<br />
[10388.111977]  ffff8800cc8736b8 0000000000000086 ffff88010476ae40 ffff8800cc873fd8<br />
[10388.111982]  ffff8800cc873fd8 ffff8800cc873fd8 ffffffff81a0d020 ffff88010476ae40<br />
[10388.111987]  ffff8800cc873688 ffff88010476ae40 ffff88011e213e48 0000000000000002<br />
[10388.111992] Call Trace:<br />
[10388.111997]  [<ffffffff8111dc40>] ? __lock_page+0&#215;70/0&#215;70<br />
[10388.112000]  [<ffffffff815ea8df>] schedule+0x3f/0&#215;60<br />
[10388.112004]  [<ffffffff815ea98f>] io_schedule+0x8f/0xd0<br />
[10388.112009]  [<ffffffff8111dc4e>] sleep_on_page+0xe/0&#215;20<br />
[10388.112015]  [<ffffffff815e9150>] __wait_on_bit+0&#215;60/0&#215;90<br />
[10388.112019]  [<ffffffff8111dd80>] wait_on_page_bit+0&#215;80/0&#215;90<br />
[10388.112024]  [<ffffffff81078dc0>] ? autoremove_wake_function+0&#215;50/0&#215;50<br />
[10388.112030]  [<ffffffff81130d4e>] shrink_page_list+0x20e/0&#215;960</p>
<p>[10388.113765] qemu-system-x86 D ffff88011e213580     0 11870   3433 0&#215;00000080<br />
[10388.113770]  ffff88001e005b98 0000000000000086 ffff880083b71720 ffff88001e005fd8<br />
[10388.113775]  ffff88001e005fd8 ffff88001e005fd8 ffffffff81a0d020 ffff880083b71720<br />
[10388.113780]  0000000e1e005ba8 ffff880083b71720 ffff88011e213e48 0000000000000002<br />
[10388.113785] Call Trace:<br />
[10388.113790]  [<ffffffff8111dc40>] ? __lock_page+0&#215;70/0&#215;70<br />
[10388.113794]  [<ffffffff815ea8df>] schedule+0x3f/0&#215;60<br />
[10388.113797]  [<ffffffff815ea98f>] io_schedule+0x8f/0xd0<br />
[10388.113802]  [<ffffffff8111dc4e>] sleep_on_page+0xe/0&#215;20</p>
</blockquote>
<p>贴了其中一部分相关信息，D住的sheep进程正在shrink_page_list，说明sheep在申请内存的时候空闲内存不够了，sheep需要回收一些内存，这时候kernel发现某一个内存页可以回收，于是尝试回收该内存页，在该页回收完成并可用之前sheep会hang住等待该内存页的回收。</p>
<p>而QEMU也是D在了某内存页的处理上，而死锁的产生是由于sheep在等待回收的那个内存页由QEMU持有，QEMU要释放这个页面就需要将page cache回写，而QEMU的backend存储用的是sheepdog，所以QEMU持有的那个内存页在回写到磁盘的时候会发请求给sheep块设备driver，driver在收到请求后把请求再扔给sheep，只有在sheep返回后这个请求才算处理成功，也就是这个要释放的内存页才算成功回写至磁盘，而这时的sheep进程恰恰在等待这个内存页，不会响应任务请求，因此就这样死锁了，sheep等待QEMU回写释放内存，QEMU回写需要等待sheep的请求响应，两人谁也不让谁就锁在那里了。</p>
<p>我的host内存相对较大，有4个G，所以这个问题不容易复现，需要大量地写入数据才有可能出现这种竞争场景，如果内存小一些问题出现的机率会更高一些。</p>
<p>这个问题不是编码上的bug，在这种场景下很难避免，解决的办法是sheep block device driver和sheep instance不要在同一台host上，我对这个module的期望也并不是像目前Sheepdog+QEMU一样使Sheep和QEMU运行在同一台host主机上，而是我们的任何一台的客户端机器都可以挂载远端集群上的大块存储空间虚拟成一个大硬盘。因此解决的办法就只能让driver和sheep分开。</p>
<p>这个问题困扰了我好久，分析自己的代码也分析了好长时间，但始终没能自己找出问题所在，还是缺少一种大局观，也缺少一些调试内核的经验，这次经过大神的指点也学会了不少东西，还是值得庆祝的。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/sheepdog-block-device-driver-deadlock.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Sheepdog块设备驱动</title>
		<link>http://basiccoder.com/sheepdog-block-device-driver.html</link>
		<comments>http://basiccoder.com/sheepdog-block-device-driver.html#comments</comments>
		<pubDate>Fri, 28 Dec 2012 08:40:49 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Sheepdog]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1069</guid>
		<description><![CDATA[最初刚开始玩<a href="https://github.com/collie/sheepdog" target="_blank">Sheepdog</a>的时候就想着要是能把它作为一个像硬盘一样的块设备，直接挂在终端机器上就好玩了，直到最近工作不是特别忙的时候才有时间想想这事情，花了好几个晚上终于把给Sheepdog写好了一个块设备驱动sheepdev，可以把集群中创建好的VDI直接注册到kernel中，然后使用用户态的相关工具fdisk/mkfs/mount等对这个块设备进行分区格式化挂载等，代码已经提交到upstream，至于还要修改几次，最终能不能merge到upstream中去都还不确定，先简单记录一下自己的工作经历。

块设备驱动作为一个kernel module，在module init的时候我注册了一个proc entry /proc/sheep，通过这个proc entry对这个块设备进行控制，比如：

echo "add 127.0.0.1:7070 a5d05d" > /proc/sheep 

可以将VDI Id为a5d05d的VDI作为一个块设备安装到本地，/dev目录下会出现一个/dev/sheepa这样的块设备文件，添加第二个后出现/dev/sheepb，以此类推。

也可以通过下面的方法来删除已经注册上来的块设备：

echo "del sheepa" > /proc/sheep

在安装块设备的时候根据用户提供的VDI Id和sheep地址，首先来向sheep创建一个长连接，并通过该连接向sheep获取该VDI的inode数据，该长连接创建后会一起保持用于driver与sheep的通信，只有获取inode的过程是同步的，后期的读写操作都是异步进行的... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>最初刚开始玩<a href="https://github.com/collie/sheepdog" target="_blank">Sheepdog</a>(Distributed Storage System for QEMU/KVM)的时候就想着要是能把它作为一个像硬盘一样的块设备，直接挂在终端机器上就好玩了，直到最近工作不是特别忙的时候才有时间想想这事情，花了好几个晚上终于把给Sheepdog写好了一个块设备驱动sheepdev，可以把集群中创建好的VDI直接注册到kernel中，然后使用用户态的相关工具fdisk/mkfs/mount等对这个块设备进行分区格式化挂载等，代码已经提交到upstream，至于还要修改几次，最终能不能merge到upstream中去都还不确定，先简单记录一下自己的工作经历。</p>
<p>块设备驱动作为一个kernel module，在module init的时候我注册了一个proc entry /proc/sheep，通过这个proc entry对这个块设备进行控制，比如：</p>
<p>echo &#8220;add 127.0.0.1:7070 a5d05d&#8221; > /proc/sheep </p>
<p>可以将VDI Id为a5d05d的VDI作为一个块设备安装到本地，/dev目录下会出现一个/dev/sheepa这样的块设备文件，添加第二个后出现/dev/sheepb，以此类推。</p>
<p>也可以通过下面的方法来删除已经注册上来的块设备：</p>
<p>echo &#8220;del sheepa&#8221; > /proc/sheep</p>
<p>在安装块设备的时候根据用户提供的VDI Id和sheep地址，首先来向sheep创建一个长连接，并通过该连接向sheep获取该VDI的inode数据，该长连接创建后会一起保持用于driver与sheep的通信，只有获取inode的过程是同步的，后期的读写操作都是异步进行的。</p>
<p>在获取到inode数据之后，inode中包含了这个VDI的size等信息，在创建disk之前这个size信息是唯一需要从sheep获取的，关于sector size这些信息，由于我们是一个虚拟的块设备，都延用了内核通用的块设备大小512字节，sectors数也是通过inode->size/kernel_sector_size计算出来的，创建disk的函数如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> sheep_add_disk<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> sheepdev <span style="color: #339933;">*</span>dev<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> ret<span style="color: #339933;">;</span>
	<span style="color: #993333;">struct</span> request_queue <span style="color: #339933;">*</span>queue<span style="color: #339933;">;</span>
&nbsp;
	dev<span style="color: #339933;">-&gt;</span>disk <span style="color: #339933;">=</span> alloc_disk<span style="color: #009900;">&#40;</span>SHEEP_BLKDEV_MINORS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		DBPRT<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;allocate gendisk failure<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		ret <span style="color: #339933;">=</span> <span style="color: #339933;">-</span>EBUSY<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> ret<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	queue <span style="color: #339933;">=</span> blk_init_queue<span style="color: #009900;">&#40;</span>sheep_request<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>dev<span style="color: #339933;">-&gt;</span>que_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* 4M boundary */</span>
	blk_queue_segment_boundary<span style="color: #009900;">&#40;</span>queue<span style="color: #339933;">,</span> <span style="color: #208080;">0x3fffff</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #339933;">-&gt;</span>major <span style="color: #339933;">=</span> sheepdev_major<span style="color: #339933;">;</span>
	dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #339933;">-&gt;</span>first_minor <span style="color: #339933;">=</span> dev<span style="color: #339933;">-&gt;</span>minor <span style="color: #339933;">*</span> SHEEP_BLKDEV_MINORS<span style="color: #339933;">;</span>
	dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #339933;">-&gt;</span>queue <span style="color: #339933;">=</span> queue<span style="color: #339933;">;</span>
	dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #339933;">-&gt;</span>fops <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>sheepdev_ops<span style="color: #339933;">;</span>
	dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #339933;">-&gt;</span>private_data <span style="color: #339933;">=</span> dev<span style="color: #339933;">;</span>
	snprintf<span style="color: #009900;">&#40;</span>dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #339933;">-&gt;</span>disk_name<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #339933;">-&gt;</span>disk_name<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
		 SHEEP_BLKDEV_NAME<span style="color: #ff0000;">&quot;%c&quot;</span><span style="color: #339933;">,</span> dev<span style="color: #339933;">-&gt;</span>minor <span style="color: #339933;">+</span> <span style="color: #ff0000;">'a'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	set_capacity<span style="color: #009900;">&#40;</span>dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #339933;">,</span> dev<span style="color: #339933;">-&gt;</span>sectors<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	add_disk<span style="color: #009900;">&#40;</span>dev<span style="color: #339933;">-&gt;</span>disk<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>调用set_capacity()之后kernel会调用check_partition()进行分区检查，这时候如果request处理流程还没有完成的话，kernel会hang在set_capacity()，所以在最初编码测试的阶段add_disk()之前我先把capacity设置为0。</p>
<p>在这里调用了blk_queue_segment_boundary()这个函数把请求的boundary设置了4M，sheepdog的文件存储是4M大小的固定块文件，超出4M处理起来就麻烦了。</p>
<p>在add_disk()函数完成之后kernel就开始发request过来了，这时候的request处理函数必须要打通的。</p>
<p>request中包含了经过io scheduler合并过的bio，我们只需要把这些bio连接起来当做一个大的读/写请求发出去便可以了，sheepdev用了一个单一个长连接来处理sheep的读写请求，但创建了两个内核线程sheep_req和sheep_fin。</p>
<p>sheep_request()这个callback函数会将block层发来的请求摘出来放到一个pending_request队列中，每放进一个请求后就会唤醒sheep_req线程，sheep_req这个kernel thread做的事情是从这个队列中把请求取出来，然后根据请求的内容来确定是读还是写，然后把读/写的数据连接合并，不论读写sheep_req都会把请求直接发送出去，而不会同步地等待返回的数据，然后sheepdev会把这个请求再放入一个finish_list队列中，这时候请求附加了一个sheepdog协议中的请求号，每一个请求客户端都会对其进行+1，一般的cs通信协议都会有这样一个请求ID号，用于协议的异步处理，我之前的hybrid聊天客户端也用了这种办法。</p>
<p>sheep_req在向finish_list中放入一个请求后便会去唤醒sheep_fin这个kernel thread，sheep_fin被唤醒后会去读与sheep的长连接中的数据，获取一个请求的response，这个response消息中包含了一个请求ID，sheepdev根据这个ID来从finish_list中找出对应的request，并根据response的结果调用下面的函数结束这个request:</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> sheep_end_request<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> request <span style="color: #339933;">*</span>req<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> ret<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">struct</span> request_queue <span style="color: #339933;">*</span>q <span style="color: #339933;">=</span> req<span style="color: #339933;">-&gt;</span>q<span style="color: #339933;">;</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">long</span> flags<span style="color: #339933;">;</span>
&nbsp;
	spin_lock_irqsave<span style="color: #009900;">&#40;</span>q<span style="color: #339933;">-&gt;</span>queue_lock<span style="color: #339933;">,</span> flags<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	__blk_end_request_all<span style="color: #009900;">&#40;</span>req<span style="color: #339933;">,</span> ret<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	spin_unlock_irqrestore<span style="color: #009900;">&#40;</span>q<span style="color: #339933;">-&gt;</span>queue_lock<span style="color: #339933;">,</span> flags<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Sheepdog我认为有一点设计不合理的地方就是在创建某个object对象之后，对应的inode数据不会被sheep自动更新，而是需要依靠客户端程序来更新，这样就给客户端的实现带来了一些繁琐，如果修改的话涉及到的代码范围比较广(collie/qemu)，我也不打算对它做什么修改了。</p>
<p>由于Sheepdog这个问题，对于SD_OP_CREATE_AND_WRITE_OBJ这样的请求而言，它返回后并不能代表这个block request的结束，必须等待更新inode的请求结束后才可以决定对应的block request是否结束。</p>
<p>关于这个驱动目前核心的就这些东西，没有什么特别复杂的东西，只是一些kernel driver设计的繁琐的东西，原理了解了就很简单了。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/sheepdog-block-device-driver.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>BLCR的进程Checkpoint</title>
		<link>http://basiccoder.com/blcr%e7%9a%84%e8%bf%9b%e7%a8%8bcheckpoint.html</link>
		<comments>http://basiccoder.com/blcr%e7%9a%84%e8%bf%9b%e7%a8%8bcheckpoint.html#comments</comments>
		<pubDate>Fri, 30 Nov 2012 09:05:30 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Kernel]]></category>
		<category><![CDATA[BLCR]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1055</guid>
		<description><![CDATA[<a href="http://basiccoder.com/blcr-introduction-checkpoin.html" target="_blank">前一篇日志</a>从Checkpoint的角度分析了BLCR的软件架构，没有写最核心的做dump操作的那部分代码，这部分代码完全是在内核态运行的，涉及到进程的各种状态，包括进程的PID/PGID，虚拟内存映射，打开的文件，寄存器的状态，credentials，timers，信号状态等等。

要提一下的是昨天淘宝内核组的@RoverYu同学给我看了一篇LWN上的文章，<a href="http://lwn.net/Articles/525675/" target="_blank">http://lwn.net/Articles/525675/</a>，这个东西叫<a href="http://criu.org/Main_Page" target="_blank">CRIU(CheckPoint/Restart in user space)</a>，是在用户态实现进程的Checkpoint/Restart，虽然是用户态的CR，但这种事情没有kernel的配合肯定是做不来的，我没有仔细去研究CRIU的实现机制，只是看了下简单的介绍，貌似是添加了一些相关的系统调用，否则这种事情单靠用户态程序肯定是办不到的，比方说PID做Checkpoint的时候有getpid()这样式syscall，可以得到进程的pid然后保存在文件里面就可以，但Restart的时候可没有setpid()这样的syscall可以用，当然CRIU和kernel进行通信也用到了/proc文件系统，相比之下BLCR和kernel通信完全只用了/proc文件系统，之后就把CR所有的工作都扔给内核去做了，在kernel中进行Checkpoint最核心的函数是cr_dump_self()，也就是进程通过ioctl向kernel发送CR_OP_HAND_CHKPT请求之后kernel对应的处理函数，接下来仔细讨论下这个函数。

对应于进程的每一个子进程或者线程都会执行这个cr_dump_self()，也就是每个task_struct对于一个cr_dump_self()，对于共享mm_struct的线程而言，这些函数只有其中一个dump mm即可，其它的只是保存一下进程的寄存器状态便可以了... ]]></description>
				<content:encoded><![CDATA[<p><a href="http://basiccoder.com/blcr-introduction-checkpoin.html" target="_blank">前一篇日志</a>从Checkpoint的角度分析了BLCR的软件架构，没有写最核心的做dump操作的那部分代码，这部分代码完全是在内核态运行的，涉及到进程的各种状态，包括进程的PID/PGID，虚拟内存映射，打开的文件，寄存器的状态，credentials，timers，信号状态等等。</p>
<p>要提一下的是昨天淘宝内核组的炳天大神给我看了一篇LWN上的文章，<a href="http://lwn.net/Articles/525675/" target="_blank">http://lwn.net/Articles/525675/</a>，这个东西叫<a href="http://criu.org/Main_Page" target="_blank">CRIU(CheckPoint/Restart in user space)</a>，是在用户态实现进程的Checkpoint/Restart，虽然是用户态的CR，但这种事情没有kernel的配合肯定是做不来的，我没有仔细去研究CRIU的实现机制，只是看了下简单的介绍，貌似是添加了一些相关的系统调用，否则这种事情单靠用户态程序肯定是办不到的，比方说PID做Checkpoint的时候有getpid()这样式syscall，可以得到进程的pid然后保存在文件里面就可以，但Restart的时候可没有setpid()这样的syscall可以用，当然CRIU和kernel进行通信也用到了/proc文件系统，相比之下BLCR和kernel通信完全只用了/proc文件系统，之后就把CR所有的工作都扔给内核去做了，在kernel中进行Checkpoint最核心的函数是cr_dump_self()，也就是进程通过ioctl向kernel发送CR_OP_HAND_CHKPT请求之后kernel对应的处理函数，接下来仔细讨论下这个函数。</p>
<p>对应于进程的每一个子进程或者线程都会执行这个cr_dump_self()，也就是每个task_struct对于一个cr_dump_self()，对于共享mm_struct的线程而言，这些函数只有其中一个dump mm即可，其它的只是保存一下进程的寄存器状态便可以了。</p>
<p>首先要获取将要dump到的文件的file struct</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">dest_filp <span style="color: #339933;">=</span> cr_loc_get<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>req<span style="color: #339933;">-&gt;</span>dest<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>shared<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>IS_ERR<span style="color: #009900;">&#40;</span>dest_filp<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #b1b100;">return</span> PTR_ERR<span style="color: #009900;">&#40;</span>dest_filp<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>关于这个shared标志，意思就是多个进程是否共享这一个目标文件，cr_checkpoint提供了一个-d参数用来指定dump出来的文件可以放在这个目录下面，并且对每个进程都单独创建一个文件，这种时候shared就不是共享的了，每个进程对应的目标文件都是相互独立的，也就无须进行同步了，同时我也发现这个-d参数虽然有，但却标志着unimplemented，也就是说我前面说的这几句话都是废话，这个功能BLCR虽然打算提供但尚未实现，默认只能将所有进程的信息都dump到同一个文件中，shared标志为1，这时候在完成初始化之后在dump进程数据的时候就需要加锁了。</p>
<p>接下来BLCR把除SIGKILL之外的所有信号都block了(包括SIGSTOP)，否则来个什么信号进程上下文又变了，那我们Checkpoint的数据就不可靠了。</p>
<p>初始化工作，给这个dump文件写一个file header，这个头写一个就够了，所有这些进程都去抢req->serial_mutex这个锁，谁抢到谁干这个事情：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">down<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>req<span style="color: #339933;">-&gt;</span>serial_mutex<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>test_and_set_bit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>req<span style="color: #339933;">-&gt;</span>done_header<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        result <span style="color: #339933;">=</span> cr_save_file_header<span style="color: #009900;">&#40;</span>req<span style="color: #339933;">,</span> dest_filp<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>result <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		req<span style="color: #339933;">-&gt;</span>result <span style="color: #339933;">=</span> result<span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
up<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>req<span style="color: #339933;">-&gt;</span>serial_mutex<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>至于这个文件的内容就没什么好说的了，几个标志。</p>
<p>接下来做的工作是把共享同一个mm_struct的进程同步，保证大家在dump前停在同一个位置，这个同步就是用到了proc_req里面的barrier变量来完成的，涉及到kernel函数就是wait_event_interruptible()</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">// Synchronize to ensure all tasks in the current process have stopped running.</span>
once <span style="color: #339933;">=</span> cr_signal_predump_barrier<span style="color: #009900;">&#40;</span>cr_task<span style="color: #339933;">,</span> <span style="color: #808080; font-style: italic;">/* block= */</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>once <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #b1b100;">goto</span> cleanup_unlocked<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>第一个wakeup的进程once返回1，这个返回值在暂停itimers的时候会用到，也是为了保证只有一个人把itimers暂停就可以了，谁先醒来谁就去暂停一下，其它人就不要动了。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">// One task pauses the itimers</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>once<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	cr_pause_itimers<span style="color: #009900;">&#40;</span>proc_req<span style="color: #339933;">-&gt;</span>itimers<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #666666; font-style: italic;">// vmadump_barrier ensures this is written before reading</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>接下来根据刚才提到的dump file的shared标志加锁，进入cr_do_dump()这个函数来dump进程的信息，几个共享mm_struct还是会去抢一个锁，谁先抢到谁就会成为这组进程的leader，只有leader进程才会去保存mm_struct相关的一些信息，其它的进程只是保存一下各自的寄存器信息就可以了。</p>
<p>接下来针对于这个proc_req(一个proc_req对应一个mm_struct)也可保存一个header，这个header主要内容是一共有几个线程在用这个mm_struct，线程的clone_flags是多少，也是几个task去抢一把锁，谁先抢到谁写这个东东。</p>
<p>接下来保存process linkage信息，进程的pgid,pgrp,tgid,sessionid等等这些信息。完了后进入cr_freeze_threads()这个函数，这个函数做很多dump工作，主要工作其实都是刚才选出来的那个leader来做的，其它不是leader的进程只是保存了下寄存器信息，leader做如下工作，下面就涉及到非常多的细节了，每一个都可以拿出来写一篇文章，有些太过于细节的东西我也没仔细看，就简单介绍一下：</p>
<p>1. 写一个头信息：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">    <span style="color: #993333;">static</span> <span style="color: #993333;">struct</span> vmadump_header header <span style="color: #339933;">=</span><span style="color: #009900;">&#123;</span>VMAD_MAGIC<span style="color: #339933;">,</span> VMAD_FMT_VERS<span style="color: #339933;">,</span> VMAD_ARCH<span style="color: #339933;">,</span>
					  <span style="color: #009900;">&#40;</span>LINUX_VERSION_CODE <span style="color: #339933;">&gt;&gt;</span> <span style="color: #0000dd;">16</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> <span style="color: #208080;">0xFF</span><span style="color: #339933;">,</span>
					  <span style="color: #009900;">&#40;</span>LINUX_VERSION_CODE <span style="color: #339933;">&gt;&gt;</span> <span style="color: #0000dd;">8</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> <span style="color: #208080;">0xFF</span><span style="color: #339933;">,</span>
					  LINUX_VERSION_CODE <span style="color: #339933;">&amp;</span> <span style="color: #208080;">0xFF</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>2. 保存PID</p>
<p>3. 保存Credentials， cr_save_creds()</p>
<p>4. 保存cpu状态信息， vmadump_store_cpu()</p>
<p>5. 保存signal信息，所有的进程都会把sigblock和sigpending信息dump出来，但只有leader进程会把sigaction给dump出来。</p>
<p>6. 保存current->clear_child_id, current->personality</p>
<p>7. 保存memory信息，这部分信息是只有leader进程才会去保存的，vmadump这块是比较复杂的，主要是遍历mm_struct的vma，BLCR可以控制哪里vma可以被dump出来，cr_checkpoint提供了如下的参数：</p>
<blockquote><p>
Options to save optional portions of memory:<br />
       &#8211;save-exe<br />
              save the executable file.</p>
<p>       &#8211;save-private<br />
              save private mapped files.  (executables and libraries are mapped this way)</p>
<p>       &#8211;save-shared<br />
              save shared mapped files.  (System V IPC is mapped this way).</p>
<p>       &#8211;save-all<br />
              save all of the above.</p>
<p>       &#8211;save-none<br />
              save none of the above (the default).
</p></blockquote>
<p>首先非0匿名页是肯定需要保存的，其它的像可执行文件，mmap进来的库文件，和mmap的共享文件都是可以根据用户指定的checkpoint选项来dump的。</p>
<p>从cr_freeze_threads()出来后继续在cr_do_vmadump()这个函数往下跑，接下来做如下几件事情：</p>
<p>1. 保存fs信息(cr_save_fs_struct())，主要保存umask,root path和current path.</p>
<p>2. 保存mmap() table (cr_save_mmaps_maps())</p>
<p>3. 保存itimers() (cr_save_itimers())</p>
<p>4. 保存mmaped() pages (cr_save_mmaps_data())</p>
<p>5. 保存打开的文件 (cr_save_all_files())</p>
<p>以上每一步都可以展开写很多东西，细节太多了，也鉴于我本人水平有限，有些东西也可能理解的不对就暂时不往细里写了，有时间我把vmadump这块仔细整理下。</p>
<p>跑到这里对于某一个proc_req的dump基本就完了，再退回到上层函数写一个tailer就OK了，然后把停掉的itimers打开，再把block的信号给恢复过来就完成了。</p>
<p>可以说整个这个过程需要对内核有非常深入的理解才能完成，我这种小白只是拿过来别人的代码学习一下，也确实通过这个东西对kernel多了很多理解，过段时间有空了把Restart过程写一下，相比于Checkpoint，Restart会有很多技巧 <img src='http://basiccoder.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/blcr%e7%9a%84%e8%bf%9b%e7%a8%8bcheckpoint.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>BLCR(Berkeley Lab Checkpoint/Restart)介绍及Checkpoint架构剖析</title>
		<link>http://basiccoder.com/blcr-introduction-checkpoin.html</link>
		<comments>http://basiccoder.com/blcr-introduction-checkpoin.html#comments</comments>
		<pubDate>Thu, 29 Nov 2012 05:23:17 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Kernel]]></category>
		<category><![CDATA[BLCR]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1043</guid>
		<description><![CDATA[<a href="https://ftg.lbl.gov/projects/CheckpointRestart/" target="_blank">BLCR(Berkeley Lab Checkpoint/Restart)</a>简单地讲是一个对进程做Checkpoint/Restart的套件，实现了用户态的libcr库和kernel module来完成相关的Checkpoint/Restart工作，最近在阅读BLCR的代码，也简单地hack过代码，写这篇文章来记录下我对于BLCR的理解，先暂时只写Checkpoint相关的BLCR架构流程。

<strong>1. BLCR的用法</strong>

对于一个进程如果需要对它进行Checkpoint，那么它首先需要具备以下两个条件之一：

- 进程通过cr_run启动，如cr_run ./test

- 进程在编译时链接了libcr库，如gcc -o test test.c -lcr

上述两程方法是等价的，即如果test在链接时未链入libcr，那用cr_run启动它也可以进行Checkpoint，同样，如果一个进程在编译时链接了libcr，那么启动时无需使用cr_run进程启动也可以进行Checkpoint.

假设刚启动的进程pid为3030，那么对该进程做Checkpoint可以简单地通过下面的命令来完成：
<pre lang="bash">
$ cr_checkpoint 3030
</pre>
BLCR在完成后默认会生成一个context.3030的文件，里面包含了进程运行的几乎所有信息，可以通过该文件Restart该进程，如：
<pre lang="bash">
$ cr_restart context.3030
</pre... ]]></description>
				<content:encoded><![CDATA[<p><a href="https://ftg.lbl.gov/projects/CheckpointRestart/" target="_blank">BLCR(Berkeley Lab Checkpoint/Restart)</a>简单地讲是一个对进程做Checkpoint/Restart的套件，实现了用户态的libcr库和kernel module来完成相关的Checkpoint/Restart工作，最近在阅读BLCR的代码，也简单地hack过代码，写这篇文章来记录下我对于BLCR的理解，先暂时只写Checkpoint相关的BLCR架构流程。</p>
<p><strong>1. BLCR的用法</strong></p>
<p>对于一个进程如果需要对它进行Checkpoint，那么它首先需要具备以下两个条件之一：</p>
<p>- 进程通过cr_run启动，如cr_run ./test</p>
<p>- 进程在编译时链接了libcr库，如gcc -o test test.c -lcr</p>
<p>上述两程方法是等价的，即如果test在链接时未链入libcr，那用cr_run启动它也可以进行Checkpoint，同样，如果一个进程在编译时链接了libcr，那么启动时无需使用cr_run进程启动也可以进行Checkpoint.</p>
<p>假设刚启动的进程pid为3030，那么对该进程做Checkpoint可以简单地通过下面的命令来完成：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ cr_checkpoint <span style="color: #000000;">3030</span></pre></div></div>

<p>BLCR在完成后默认会生成一个context.3030的文件，里面包含了进程运行的几乎所有信息，可以通过该文件Restart该进程，如：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ cr_restart context.3030</pre></div></div>

<p>BLCR支持四种范围的Checkpoint:<br />
<block><br />
-T 指定一个进程pid，它会Checkpoint整个进程树，这是BLCR的默认行为<br />
-p 指定一个进程pid，它会Checkpoint指定的这单个进程<br />
-g 指定一个group id，它会Checkpoint整个进程组的所有进程<br />
-s 指定一个session id，它会Checkpoint整个会话里面的所有进程<br />
</block><br />
<strong>2. BLCR的基本原理</strong></p>
<p>BLCR的用户态工具(cr_checkpoint/cr_restart)通过proc file跟kernel进行交互，cr_module在初始化的时候创建了proc entry /proc/checkpoint/ctrl，并定义了该entry的open/release/ioctl/poll方法，open和release主要做一些和CR相关的初始化工作和资源释放工作，用户态工具与kernel的交互主要通过ioctl来完成。</p>
<p>BLCR通过在kernel中向要被Checkpoint的进程发送一个信号来通知进程对自己做Checkpoint，BLCR使用了signum=64的实时信号，这个信号在用户进程中是不能被重写的，假如我们在某进程中对64号信号重新注册了sighandler，那么cr_checkpoint就会hang住，因为这个sighandler会陷入内核来完成当前进程的Checkpoint工作，然后cr_checkpoint会通过proc entry的poll接口来等待POLLIN事件，sighandler被重写之后Checkpoint根本不会被执行，POLLIN事件也自然就不会被唤醒了。</p>
<p>这就引入了另一个问题，如何为用户态进程的64号信号注册一个指定的sighandler，也就是上面提到的两种方法，一种是使用cr_run执行，一种是给程序链入libcr。</p>
<p>BLCR编译完成后会生成几个共享库文件，libcr.so,libcr_run.so和libcr_omit.so，先不讨论omit.so，libcr.so和libcr_run.so中都存在一个初始化函数，函数的声明如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> __attribute__<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>constructor<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> cri_init<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span></pre></div></div>

<p>这个函数声明为constructor，也就是这个函数会在其它函数开始执行前执行，BLCR在这个函数里面对进程的64号信号注册sighandler，因此链接了libcr的程序在开始执行的时候64号信号的sighandler就会被注册为指定的sighandler，而对于通常没用链接libcr的程序而言就使用了另一种方法，使用cr_run执行要被Checkpoint的程序，cr_run其实是一个简单的bash脚本，它做的主要工作就是设置一个环境变量LD_PRELOAD，简单地讲这个变量会让程序在运行前优先加载某个动态链接库，cr_run默认是把LD_PRELOAD设置为libcr_run.so，这个库中和libcr这个库一样使用了同一个cri_init()函数，只不过他们注册的sighandler略有不同。</p>
<p><strong>2.1 Checkpoint入口和出口</strong></p>
<p>cr_checkpoint中最主要的函数cr_request_checkpoint()，它会构造一个checkpoint request(cr_chkpt_reqs)，这个request中包含用户所指定的一些Checkpoint参数,然后通过ioctl陷入内核，并将这个request通过ioctl参数传递给内核，这时候的ioctl所使用的request code为CR_OP_CHKPT_REQ，这个request code在cr_module中对应的处理函数为cr_chkpt_req()，接下来的Checkpoint逻辑对于用户态的cr_checkpoint来讲就是异步进行的了，具体的逻辑后面再详细写，先写一下cr_checkpoint如何同步地等待Checkpoint的完成，前面说过用户态进程与kernel的交互都是通过 proc entry来完成的，在进程的始终都会记录打开的proc entry file的fd，在发送完CR请求后便会等待CR的完成，这里的等待就是通过对刚才那个fd进行select来实现的，而select/epoll/poll在内核中对应的都是sys_poll，在cr_module初始化的时候就给这个proc entry的ops注册了poll callback，贴一下这个callback的定义吧：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> ctrl_poll<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> file <span style="color: #339933;">*</span>filp<span style="color: #339933;">,</span> poll_table <span style="color: #339933;">*</span>wait<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> mask<span style="color: #339933;">;</span>
	cr_pdata_t <span style="color: #339933;">*</span>priv<span style="color: #339933;">;</span>
&nbsp;
	CR_KTRACE_FUNC_ENTRY<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	priv <span style="color: #339933;">=</span> filp<span style="color: #339933;">-&gt;</span>private_data<span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>priv <span style="color: #339933;">&amp;&amp;</span> priv<span style="color: #339933;">-&gt;</span>rstrt_req<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		mask <span style="color: #339933;">=</span> cr_rstrt_poll<span style="color: #009900;">&#40;</span>filp<span style="color: #339933;">,</span> wait<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>priv <span style="color: #339933;">&amp;&amp;</span> priv<span style="color: #339933;">-&gt;</span>chkpt_req<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		mask <span style="color: #339933;">=</span> cr_chkpt_poll<span style="color: #009900;">&#40;</span>filp<span style="color: #339933;">,</span> wait<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
		mask <span style="color: #339933;">=</span> POLLERR<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> mask<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span>
cr_chkpt_poll<span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> file <span style="color: #339933;">*</span>filp<span style="color: #339933;">,</span> poll_table <span style="color: #339933;">*</span>wait<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	cr_pdata_t		<span style="color: #339933;">*</span>priv<span style="color: #339933;">;</span>
	cr_chkpt_req_t		<span style="color: #339933;">*</span>req<span style="color: #339933;">;</span>
&nbsp;
	priv <span style="color: #339933;">=</span> filp<span style="color: #339933;">-&gt;</span>private_data<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>priv<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> POLLERR<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	req <span style="color: #339933;">=</span> priv<span style="color: #339933;">-&gt;</span>chkpt_req<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>req<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> POLLERR<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>req <span style="color: #339933;">==</span> CR_CHKPT_RESTARTED<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> POLLIN <span style="color: #339933;">|</span> POLLRDNORM<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	poll_wait<span style="color: #009900;">&#40;</span>filp<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>req<span style="color: #339933;">-&gt;</span>wait<span style="color: #339933;">,</span> wait<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> check_done<span style="color: #009900;">&#40;</span>req<span style="color: #009900;">&#41;</span> <span style="color: #339933;">?</span> <span style="color: #009900;">&#40;</span>POLLIN <span style="color: #339933;">|</span> POLLRDNORM<span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>由cr_checkpoint构造中的Request被塞到了proc file的privite_data中，poll其实就是在等待req->wait被wake up，这个queue什么时候被wake up也有两种方法，具体还是取决于要被Checkpoint的进程是用前面讨论的两种方法中的哪一种启动的，刚才提到两种方法给进程的64号信号注册的sighandler略有不同，BLCR定义了好多handler函数，我只写一下两种情况下的默认行为：<br />
<em><br />
1. 程序链接了libcr.so</em></p>
<p>在这种情况下的sighandler函数向内核发送指令还是通过proc entry的ioctl完成的，具体组合为:</p>
<p>request code = CP_OP_HAND_CHKPT，flags = 0</p>
<p>而发送完这个指令后程序陷入内核把进程的相关信息dump出来，这个过程是同步进行的，所以在没有完成之前ioctl会hang在那里等待过程的完成，因此ioctl返回后便可以知道dump过程已经完成，接下来在sighandler函数中再通过ioctl发送一个request code = CP_OP_HAND_DONE的指令，这个请求对应在kernel的handler最终会把req->wait这个queue给激活，从而导致在用户态的cr_checkpoint的select返回POLLIN事件。</p>
<p><em>2. 程序通过LD_PRELOAD动态链接了libcr_run.so</em></p>
<p>这个sighandler默认是用汇编来写的，具体说来就是一个ioctl syscall:</p>
<p>request code = CP_OP_HAND_CHKPT，flags = _CR_CHECKPOINBT_STUB</p>
<p>与第一种情况不同的是，这个syscall完成以后并没有再调用另一个syscall发送CP_OP_HAND_DONE事件，这时候对于Checkpoint过程完成事件的通知就依赖于这个_CR_CHECKPOINT_STUB flags，在执行dump操作的核心函数cr_dump_self的最后有这样一句话:</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>req<span style="color: #339933;">-&gt;</span>die <span style="color: #339933;">||</span> result <span style="color: #339933;">||</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> _CR_CHECKPOINT_STUB<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #666666; font-style: italic;">// this task will not call the HAND_DONE ioctl, so finish up now.</span>
	cr_chkpt_task_complete<span style="color: #009900;">&#40;</span>cr_task<span style="color: #339933;">,</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>先不管req->die和result这两个，只要flags设置了_CR_CHECKPOINT_STUB，这个函数便会执行cr_chkpt_task_complete，这个函数在确定了所有要进行Checkpoint的进程全都dump完成后会激活req->wait:</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>list_empty<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>req<span style="color: #339933;">-&gt;</span>tasks<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	wake_up<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>req<span style="color: #339933;">-&gt;</span>wait<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p><strong>2.2 Checkpoint的请求结构</strong></p>
<p>在cr_checkpoint的cr_request_checkpoint()这个函数通过ioctl向内核中发送了CR_OP_CHKPT_REQ请求，内核对于这个请求的处理函数中做了如下三件事情：</p>
<p>1. 将用户态传过来的checkpoint request对象塞到已打开的proc entry的private_data中。<br />
2. 调用build_req()对要进行Checkpoint的进程/进程组/会话中的每个进程/线程创建request对象，build_req又根据前面提到的Checkpoint Scope选择要构建Request的进程的范围，默认的Scope是进程树，因此build_req()又调用build_req_tree()这个函数，对一颗进程树进行广度优先遍历，对每一个task_struct创建一个cr_task对象，再对每一个mm_struct创建一个cr_chkpt_proc_req_t，搞个图就容易理解这个结构了:</p>
<p><a href="http://basiccoder.com/wp-content/uploads/2012/11/blcr_proc_req1.png"><img src="http://basiccoder.com/wp-content/uploads/2012/11/blcr_proc_req1.png" alt="" title="blcr_proc_req" width="608" height="403" class="aligncenter size-full wp-image-1052" /></a></p>
<p>对进程进行Checkpoint最主要是把进程的内存dump到文件中去，针对于每个mm来创建一个proc_req是非常合理的，这个proc_req里面主要包含一些barrier，来保证共用同一个mm的进程在dump之前可以进行同步。</p>
<p>3. 调用cr_trigger_phase1来触发各个进程进行Checkpoint，这里面涉及到phase为PHASE1,PHASE2的proc_req，这些是在cr_checkpoint这个进程被Checkpoint的时候才会涉及到的，可以说BLCR考虑到了很多种情况，如果cr_checkpoint这个进程在Checkpoint另一个进程的时候，自己又被别人Checkpoint了，那这种情况也是需要处理了，虽然可能现实意义不大，但BLCR也是花了大篇幅的代码去实现了这种情况，这部分代码我就不写了，不过也确实花了不少时间才弄明白它的意图，要说一点的是普通进程的proc_req的phase都是0.</p>
<p>cr_trigger_phase1做的事情归纳起来就是遍历刚刚创建好的req->tasks这个链表，然后向每个cr_task发送64号信号，具体的task在收到这个信号之后就调用之前注册好的用户态的sighandler，这个handler里面做的工作就是再调用ioctl向kernel发送一个CR_OP_HAND_CHKPT，这个请求会在kernel中触发cr_dump_self，这个函数是对某一个进程进行Checkpoint的核心函数。</p>
<p>关于cr_dump_self这个函数所做的操作接下来找时间再写吧</p>
]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/blcr-introduction-checkpoin.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Common Lisp使用iolib进行网络编程</title>
		<link>http://basiccoder.com/network-programing-in-common-lisp-using-iolib.html</link>
		<comments>http://basiccoder.com/network-programing-in-common-lisp-using-iolib.html#comments</comments>
		<pubDate>Wed, 04 Jan 2012 08:38:24 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Lisp]]></category>
		<category><![CDATA[Common Lisp]]></category>
		<category><![CDATA[epoll]]></category>
		<category><![CDATA[quicklisp]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1022</guid>
		<description><![CDATA[Common Lisp进行网络编程可用的库还是挺多的，比较常用的库有<a href="http://common-lisp.net/project/usocket/">usocket</a>和<a href="http://common-lisp.net/project/iolib/">iolib</a>，usocket我简单了解了一下没有真正拿来用，它的API比较简单，文档写得比较全面，相比之下，iolib要比usocket强大的多，但缺点是文档太少，官方的文档可用的内容非常少，但如果能阅读一下iolib的相关源码，就会发现其实iolib是一个很强大的网络编程库，其中包含了DNS解析，socket基本操作（bind,listen等等），IO多路复用以及通常用来做IPC的socketpair，而且iolib的multiplex用起来有种libevent的感觉，用iolib可以实现一般的应用层网络编程，至于是否支持raw socket，我还没仔细研究，不过感觉应该问题不大。

<strong>1.iolib的安装</strong>
使用<a href="http://www.cliki.net/ASDF-Install">asdf-install</a>可以在线安装iolib，但貌似asdf-install不会自动解决包的依赖问题，最近才发现原来asdf-install其实已经是一个废弃的项目，官方已经不推荐使用了，在cliki的asdf-install首页最开头就有一句醒目的提示语：


<blockquote>
ASDF-install is OBSOLETE. DO NOT USE ASDF-INSTALL, EVER. DO NOT ASK AROUND ABOUT HOW TO GET IT RUNNING. IT IS O-B-S-O-L-E-T-E. Not working. Not maintained. Please use quicklisp instead.</blockquote... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>Common Lisp进行网络编程可用的库还是挺多的，比较常用的库有<a href="http://common-lisp.net/project/usocket/">usocket</a>和<a href="http://common-lisp.net/project/iolib/">iolib</a>，usocket我简单了解了一下没有真正拿来用，它的API比较简单，文档写得比较全面，相比之下，iolib要比usocket强大的多，但缺点是文档太少，官方的文档可用的内容非常少，但如果能阅读一下iolib的相关源码，就会发现其实iolib是一个很强大的网络编程库，其中包含了DNS解析，socket基本操作（bind,listen等等），IO多路复用以及通常用来做IPC的socketpair，而且iolib的multiplex用起来有种libevent的感觉，用iolib可以实现一般的应用层网络编程，至于是否支持raw socket，我还没仔细研究，不过感觉应该问题不大。</p>
<p><strong>1.iolib的安装</strong><br />
使用<a href="http://www.cliki.net/ASDF-Install">asdf-install</a>可以在线安装iolib，但貌似asdf-install不会自动解决包的依赖问题，最近才发现原来asdf-install其实已经是一个废弃的项目，官方已经不推荐使用了，在cliki的asdf-install首页最开头就有一句醒目的提示语：</p>
<blockquote><p>
ASDF-install is OBSOLETE. DO NOT USE ASDF-INSTALL, EVER. DO NOT ASK AROUND ABOUT HOW TO GET IT RUNNING. IT IS O-B-S-O-L-E-T-E. Not working. Not maintained. Please use quicklisp instead.</p></blockquote>
<p>取而代之的是<a href="http://www.quicklisp.org/beta/index.html">quicklisp</a>，之前就有人跟我推荐过quicklisp我还没来得及尝试，这几天试了下确实非常方便，可以自动地下载程序包及其依赖的相关程序包，无需手工解决依赖问题，让我想到debian的apt-get，关于quicklisp的安装和使用都非常简单，在它首页上都有使用说明。而且quicklisp几乎每个月都会在<a href="http://blog.quicklisp.org/">官方blog</a>上放出过去的几个月程序包的下载排行（如：<a href="http://blog.quicklisp.org/search?updated-min=2011-01-01T00:00:00-05:00&#038;updated-max=2012-01-01T00:00:00-05:00&#038;max-results=29">Project download stats for November</a>），可以在选择程序包的时候有个参考。</p>
<p><strong>2.创建passive socket</strong></p>
<p>当创建一个用于充当server角色的程序时通常需要创建passive socket，用来监听客户端的连接，关于socket编程的基本步骤已经是大家所熟知的了，create socket,bind,listen,accept等等，iolib是使用cffi(<a href="http://common-lisp.net/project/cffi/">The Common Foreign Function Interface</a>)通过调用linux系统调用来实现的，因此和用C语言编程几乎是一个套路，方法如下：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setq</span> socket
      <span style="color: #66cc66;">&#40;</span>make-socket
       <span style="color: #66cc66;">:</span><span style="color: #555;">connect</span> <span style="color: #66cc66;">:</span><span style="color: #555;">passive</span>
       <span style="color: #66cc66;">:</span><span style="color: #555;">address-family</span> <span style="color: #66cc66;">:</span><span style="color: #555;">internet</span>
       <span style="color: #66cc66;">:</span><span style="color: #555;">type</span> <span style="color: #66cc66;">:</span><span style="color: #555;">stream</span>
       <span style="color: #66cc66;">:</span><span style="color: #555;">external-format</span> '<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">utf-</span><span style="color: #cc66cc;">8</span> <span style="color: #66cc66;">:</span><span style="color: #555;">eol-style</span> <span style="color: #66cc66;">:</span><span style="color: #555;">crlf</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>bind-address socket
              <span style="color: #66cc66;">&#40;</span>ensure-address <span style="color: #ff0000;">&quot;127.0.0.1&quot;</span><span style="color: #66cc66;">&#41;</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">port</span> <span style="color: #cc66cc;">1086</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">reuse-addr</span> t<span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>listen-on socket <span style="color: #66cc66;">:</span><span style="color: #555;">backlog</span> <span style="color: #cc66cc;">10</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setq</span> client <span style="color: #66cc66;">&#40;</span>accept-connection socket<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>multiple-value-bind <span style="color: #66cc66;">&#40;</span>who port<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#40;</span>remote-<span style="color: #b1b100;">name</span> socket<span style="color: #66cc66;">&#41;</span>
      <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;Client ~A:~D connected.~%&quot;</span> who port<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>close socket<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>几个操作一目了解，更细节的操作（比如如何创建UDP套接字）就去翻下源码好了，bind-address这个这个函数第二个参数用ensure-address把字符串转换成所需要的address类型，iolib也定义了一系列形如+ipv4-unspecified+的静态变量，类似于C语言里面的INADDR_ANY，最后一个参数reuse-addr相当于用setsockopt对套接字设置SO_REUSEADDR选项。</p>
<p>对于iolib的passive我一直有一个问题未能解决，当程序作为server在监听客户端连接时，在C语言中可以使用CTRL+C给程序发送SIGINT信号让程序终止，排除TIME_WAIT等这些情况，程序再次启动时仍可以bind到同一个指定端口（即使没有显示地调用close关闭套接字），但在slime中使用C-c C-c终止程序，并确保在slime-selecter中已经结束掉所有的用户线程之后，再次启动程序绑定同一个端口便会提示端口已被占用，除非结束掉lisp进程(sbcl/ccl等)，我也就直接选择重启emacs，这个问题一直未能解决，困扰我很长时间，所以我只能在程序运行中不断地插入close来在不该关闭的地方临时关闭套接字。</p>
<p><strong>3. 创建active socket</strong></p>
<p>通常使用active socket的程序是作为客户端的角色，下面是我写的一段简单的示例代码，用于发送一个http请求：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">let</span> <span style="color: #66cc66;">&#40;</span>socket ip http<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setf</span> socket <span style="color: #66cc66;">&#40;</span>make-socket
              <span style="color: #66cc66;">:</span><span style="color: #555;">connect</span> <span style="color: #66cc66;">:</span><span style="color: #555;">active</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">address-family</span> <span style="color: #66cc66;">:</span><span style="color: #555;">internet</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">type</span> <span style="color: #66cc66;">:</span><span style="color: #555;">stream</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">external-format</span> '<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">utf-</span><span style="color: #cc66cc;">8</span><span style="color: #66cc66;">&#41;</span>
              <span style="color: #66cc66;">:</span><span style="color: #555;">ipv6</span> <span style="color: #b1b100;">nil</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setf</span> ip <span style="color: #66cc66;">&#40;</span>lookup-hostname <span style="color: #ff0000;">&quot;basiccoder.com&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;IP of ~a is: ~a~%&quot;</span> host ip<span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span>connect socket ip <span style="color: #66cc66;">:</span><span style="color: #555;">port</span> <span style="color: #cc66cc;">80</span> <span style="color: #66cc66;">:</span><span style="color: #555;">wait</span> t<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;Connected to ~a via ~a:~a to ~a:~a~%&quot;</span>
          host <span style="color: #66cc66;">&#40;</span>local-host socket<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#40;</span>local-port socket<span style="color: #66cc66;">&#41;</span>
          <span style="color: #66cc66;">&#40;</span>remote-host socket<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#40;</span>remote-host socket<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setf</span> http <span style="color: #66cc66;">&#40;</span>make-http-request <span style="color: #ff0000;">&quot;GET&quot;</span> <span style="color: #ff0000;">&quot;/&quot;</span> host
               <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;Connection&quot;</span> <span style="color: #ff0000;">&quot;Closed&quot;</span><span style="color: #66cc66;">&#41;</span>
                <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;User-Agent&quot;</span> <span style="color: #ff0000;">&quot;Mozilla&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;send: ~A~%&quot;</span> http<span style="color: #66cc66;">&#41;</span>
&nbsp;
  <span style="color: #66cc66;">&#40;</span>format socket http<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span>finish-output socket<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>使用lookup-hostname来解决IP地址，通过connect来向远程服务器进行连接，make-http-request是我写的一个生成http请求头的一个宏，返回http请求字符串;创建的socket对象其实是一个流对象，因此可以使用format向流中写入数据，写入的数据会保存在缓冲区中，当调用(finish-output socket)函数时开始执行数据的发送操作。iolib也提供了send-to函数，不详细讨论了。</p>
<p><strong>4. 从流中读取数据</strong><br />
由于socket对象是一个流对象，因此可以用任何从流中读出数据的方法来从socket中接收数据，如read-line,read-byte等等，但read-line存在一个问题，当使用read-line读取数据时，当数据中存在非ASCII字符中便会抛出异常，它在读取的过程中会对数据进行ASCII解码，而read-byte则不会存在这个问题，因为它读出的是二进制的字节，它不关心编码方式，但read-byte我感觉很多情况下是不太适用的，因为它一次只能读取一个字节，一般情况下多次执行这样的操作效率不会太高，当然，iolib也提供了receive-from函数，间接地调用了系统调用recvfrom()，是一种带缓冲区的接收方式，也比较符合C语言的编程习惯。</p>
<p>receive-from的使用方法如下：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>multiple-value-bind <span style="color: #66cc66;">&#40;</span>buf-vector rbytes<span style="color: #66cc66;">&#41;</span>
          <span style="color: #66cc66;">&#40;</span>receive-from socket <span style="color: #66cc66;">:</span><span style="color: #555;">buffer</span> buf-vector
                               <span style="color: #66cc66;">:</span><span style="color: #555;">start</span> <span style="color: #cc66cc;">0</span> <span style="color: #66cc66;">:</span><span style="color: #555;">end</span> <span style="color: #cc66cc;">4096</span>
                               <span style="color: #66cc66;">:</span><span style="color: #555;">size</span> <span style="color: #cc66cc;">4096</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>receive-from返回的是values,主返回值是包含接收到的数据的vector，另一个返回值是读取到的字节数，函数调用的参数里面:buffer不是必须的，当:buffer未指定时，则需要指定:size参数，这时receive-from会自动创建一个指定大小的vector并将数据填充后返回。receive-from返回的vector中保存的是字节码值，并不是字符串，可以使用octets-to-string函数将其转换成string，具体可以参考下我的这篇日志：<a href="http://basiccoder.com/add-gbk-support-to-babel.html">Common Lisp为Babel添加GBK支持</a>。</p>
<p><strong>5. IO多路复用</strong></p>
<p>iolib提供了multiplex机制，原理也是对epoll/poll/kqueue进行了封装，我在linux下测试默认是用的epoll，使用方法和libevent非常相似，首先要创建一个全局的event base:</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">setf</span> *http-event-base*
        <span style="color: #66cc66;">&#40;</span>make-instance 'iomux<span style="color: #66cc66;">:</span><span style="color: #555;">event-base</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&lt;</span>/lisp<span style="color: #66cc66;">&gt;</span>
&nbsp;
将要进行利用的socket对象添加到该event base中，使用set-io-handler函数：
<span style="color: #66cc66;">&lt;</span>pre lang<span style="color: #66cc66;">=</span><span style="color: #ff0000;">&quot;lisp&quot;</span><span style="color: #66cc66;">&gt;</span>
<span style="color: #66cc66;">&#40;</span>set-io-handler *http-event-base*
                <span style="color: #66cc66;">&#40;</span>socket-os-fd socket<span style="color: #66cc66;">&#41;</span>
                <span style="color: #66cc66;">:</span><span style="color: #555;">read</span>
                <span style="color: #66cc66;">&#40;</span>make-http-event-loop conn client<span style="color: #66cc66;">&#41;</span>
                <span style="color: #66cc66;">:</span><span style="color: #555;">one-shot</span> t<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>第三个选项:read表示监听套接字是否有数据可读，同类的选项还有:write和:error，第四个参数是事件发生是要执行的回调函数，由于lisp中没有类似于C语言中的void*这种方式，不会像C语言一样给回调函数通过一个指针来传递相关的参数，但lisp的高阶函数使用传递额外参数更加方便了，上述代码中的make-http-event-loop函数的返回值是一个lambda函数，用来作为set-io-handler的回调函数，而 conn和clinet两个参数可以通过make-http-event-loop传递给lambda函数：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">defun</span> make-http-event-loop <span style="color: #66cc66;">&#40;</span>conn client<span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">lambda</span> <span style="color: #66cc66;">&#40;</span>fd event exception<span style="color: #66cc66;">&#41;</span>
     <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;event ~A on fd(~D) with connection
:~A client :~A&quot;</span> event fd conn client<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>最后，调用event-dispatch函数来进入事件循环：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>event-dispatch *http-event-base*<span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">when</span> *http-event-base*
  <span style="color: #66cc66;">&#40;</span>close *http-event-base*<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p><strong>6. iolib的其它参考文档</strong></p>
<div>
[1] <a href="http://common-lisp.net/project/iolib/manual/">http://common-lisp.net/project/iolib/manual/</a><br />
[2] <a href="http://pages.cs.wisc.edu/~psilord/blog/data/iolib-tutorial/tutorial.html">http://pages.cs.wisc.edu/~psilord/blog/data/iolib-tutorial/tutorial.html</a></div>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/network-programing-in-common-lisp-using-iolib.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Common Lisp为Babel添加GBK支持</title>
		<link>http://basiccoder.com/add-gbk-support-to-babel.html</link>
		<comments>http://basiccoder.com/add-gbk-support-to-babel.html#comments</comments>
		<pubDate>Thu, 22 Dec 2011 05:14:51 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Lisp]]></category>
		<category><![CDATA[babel]]></category>
		<category><![CDATA[Common Lisp]]></category>
		<category><![CDATA[iolib]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1018</guid>
		<description><![CDATA[前段时间在学Common Lisp，接触新语言我干的第一件事一般是通过HTTP抓取某个web页面，因为对网络编程比较感兴趣，而且平时写的程序也多是网络相关的，所以比较关心这方面的用法，于是用IOLib写了一个简单的小程序尝试着抓取了几个大门户网站的页面代码，关于IOLib的基本用法改天我也写篇日志记录一下，也算是和大家分享一下，毕竟能找到的中文资料比较少，而且文档也不是特别全，就像<a href="http://pages.cs.wisc.edu/~psilord/blog/27.html">这篇文章里面说的</a>：”Such is the nature of open source documentation. “，于是大多数的用法都得通过hack源代码来弄明白，言归正传，在写这个小程序的时候我遇到了一些问题，关于字符编码的问题，下面慢慢道来吧。

IOLib的receive-from方法是通过调用recvfrom来进行的，这种带缓存的接收方式很符合其它语言进行编程的套路，但它所接收到的buffer数据是需要存储在一个vector '(unsigned-byte 8)中的，虽然字符串在本质上也是向量，但对于字符串的很多操作不能直接应用于vector，而且vector中的元素都是每个字符的unicode编码，而不是确定的字符，于是便需要进行转换，最初我使用的办法：
<pre lang="lisp">
(map 'string #'code-char buffer)
</pre... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>前段时间在学Common Lisp，接触新语言我干的第一件事一般是通过HTTP抓取某个web页面，因为对网络编程比较感兴趣，而且平时写的程序也多是网络相关的，所以比较关心这方面的用法，于是用IOLib写了一个简单的小程序尝试着抓取了几个大门户网站的页面代码，关于IOLib的基本用法改天我也写篇日志记录一下，也算是和大家分享一下，毕竟能找到的中文资料比较少，而且文档也不是特别全，就像<a href="http://pages.cs.wisc.edu/~psilord/blog/27.html">这篇文章里面说的</a>：”Such is the nature of open source documentation. “，于是大多数的用法都得通过hack源代码来弄明白，言归正传，在写这个小程序的时候我遇到了一些问题，关于字符编码的问题，下面慢慢道来吧。</p>
<p>IOLib的receive-from方法是通过调用recvfrom来进行的，这种带缓存的接收方式很符合其它语言进行编程的套路，但它所接收到的buffer数据是需要存储在一个vector &#8216;(unsigned-byte 8)中的，虽然字符串在本质上也是向量，但对于字符串的很多操作不能直接应用于vector，而且vector中的元素都是每个字符的unicode编码，而不是确定的字符，于是便需要进行转换，最初我使用的办法：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>map 'string #'code-char buffer<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>这个方法简单粗暴，直接在对一个vector上的每一个元素应用code-char函数，然后将输出映射为string，首先需要了解code-char/char-code这对函数的用法，大多数的Common Lisp实现都使用Unicode字符编码，当然，Unicode向下兼容ASCII和ISO-8859-1，所以这一对函数的作用就是在字符和它的Unicode码之间相互转换，如：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>char-code #\中<span style="color: #66cc66;">&#41;</span>
<span style="color: #cc66cc;">20013</span>
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>code-char <span style="color: #cc66cc;">20013</span><span style="color: #66cc66;">&#41;</span>
#\U4E2D
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;~d&quot;</span> #x4E2D<span style="color: #66cc66;">&#41;</span>
<span style="color: #cc66cc;">20013</span>
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>format t <span style="color: #ff0000;">&quot;~a&quot;</span> #\U4E2D<span style="color: #66cc66;">&#41;</span>
中</pre></div></div>

<p>字符“中”的Unicode码是20013，用code-char将其转换成字符之后，REPL并不是将可视的字符直接显示出来，而是显示#\U4E2D，表示这是一个Unicode字符，后面的4E2D是该字符的16进程Unicode码，换成10进制也便是20013，将该这了符直接打印出的话便可以得到字符“中”了。</p>
<p>当抓取的页面编码不是Unicode（一般情况下都不是的），直接使用这种办法转换成字符串就会导致除ASCII字符以外的其它字符乱码。</p>
<p>Google搜索下在stackoverflow上发现这个问题的解决办法，<a href="http://common-lisp.net/project/babel/">babel</a>和<a href="http://weitz.de/flexi-streams/">flexi-streams</a>的octets-to-string函数可以实现由vector向string的转换，而vector必须是(make-array element-length :element-type &#8216;(unsigned-byte 8))类型，在使用该函数在抓取新浪/网易等门户网站的数据的时候会抛出异常：</p>
<p>代码：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>babel<span style="color: #66cc66;">:</span><span style="color: #555;">octets-to-string</span> buffer <span style="color: #66cc66;">:</span><span style="color: #555;">encoding</span> <span style="color: #66cc66;">:</span><span style="color: #555;">utf-</span><span style="color: #cc66cc;">8</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>异常：</p>
<pre>
Illegal :UTF-8 character starting at position 437.
 [Condition of type BABEL-ENCODINGS:INVALID-UTF8-CONTINUATION-BYTE]
</pre>
<p>起初我对这个问题百思不得其解，后来在水木还有StackOverflow上提出这个问题之后，我才发现原来我的问题是那么弱，也非常感谢大家的热心解答，确实如字面意思，有非法的UTF-8字符，也就是说字符串并非使用UTF-8编码的，而我习惯性地以为这些大网站理应都是UTF-8编码才对，谁知跟我想的正好想反，他们大多都不是UTF-8，有GBK甚至有GB2312，关于GBK和UTF-8我在推特上提起过这个问题，有推友说大网站仍在延用GB2312是为了节省流量，这个我可以接受，也有推友说使用GBK是因为它比UTF-8可能多几个字符，这个我也可以接受，但有的推友却跑过来说GB*是国标，用UTF-8什么的那是崇洋媚外，这个我表示无论如何也接受不了，互联网是没有国界的存在，在这样一个大的开放平台上你搞个国标有个毛用，当然我们的互联网也不开放是真的。</p>
<p>GB2312是GBK的子集，GBK向下兼容GB2312，使用GB2312就意味着能使用的字符数要远小于GBK，这两者相对于UTF-8对于中文的优点就是它们对于汉字的编码是两个字节的，而UTF-8是三个字节的，当然，我说的仅是指汉字，UTF-8是变长编码的，它也支持2字节和4字节甚至更多。</p>
<p>babel是不支持GBK的，sbcl的sb-ext:octets-to-string和Closure的ccl:octets-to-string都支持GBK，我个人比较较真，想给babel写个GBK的patch，以便可以实现平台无关的转码。</p>
<p>可能是我搜索技术不行，搜到的关于GBK的中文正式文档不多，只在维基百科上找到了关于<a href="http://en.wikipedia.org/wiki/GBK">GBK的介绍</a>，虽然没有涉及到具体的GBK到Unicode转码规则，但它提供的关于GBK的介绍也是很有帮助的，发现原来GBK与Unicode的转换并不像UTF-8与Unicode之间的转换那样有固定的规则，而是需要通过查表实现，于是乎我在网上下载到了一个包含所有GBK符号的码表，排列顺序也是按GBK的相关规则来的，解码的过程我参考了这篇文章：<a href="http://dourok.info/2010/06/%E4%BB%8Egbk%E5%88%B0unicode%E7%9A%84%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6%E6%98%A0%E5%B0%84/">从GBK到Unicode的中文字符映射</a>，编码的话就是反其道而行之，原理很简单，参考维基百科上的两个图就可以推算出来。</p>
<p>对于lisp而言，宏是这门语言一个很大的亮点，通过宏甚至可以定制语言的特性，babel写义了几个宏作为编解码的接口，最重要的有下面四个:</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">define-octet-counter
define-code-point-counter
define-encoder
define-decoder</pre></div></div>

<p>前两个宏首先对要进行编/解码的数据进行预处理，分别计算出编/解码后的数据所占用的长度，从而预先分配存储空间。后两个宏分别定义对应的编码和解码算法，这两个宏具体的使用方法都是参考了enc-unicode.lisp这个文件里面关于UTF-8的编解码代码。</p>
<p>添加了GBK支持的babel我推送到github上去了(<a href="https://github.com/levin108/babel/">https://github.com/levin108/babel/</a>)，需要的同学可以下载，有什么写得不对的或者不好的欢迎提出来，我是新手需要学习。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/add-gbk-support-to-babel.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>使用ASDF构建Common Lisp程序包</title>
		<link>http://basiccoder.com/constructing-common-lisp-package-by-asdf.html</link>
		<comments>http://basiccoder.com/constructing-common-lisp-package-by-asdf.html#comments</comments>
		<pubDate>Tue, 13 Dec 2011 03:46:43 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Lisp]]></category>
		<category><![CDATA[asdf]]></category>
		<category><![CDATA[Common Lisp]]></category>
		<category><![CDATA[Emacs]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=1010</guid>
		<description><![CDATA[在切入正题之前先写点不相关的，工作确定之后便开始忙论文的事，忙里偷闲总想搞点什么以做娱乐，不得不说，腾讯面试官说过的要精通两到三门不同的语言我印象很深刻，自己也想尝试一下新东西，VIM让我审美疲劳了，也想尝试一下Emacs，机缘巧合由田春老师翻译的<a href="http://www.amazon.cn/%E5%AE%9E%E7%94%A8Common-Lisp%E7%BC%96%E7%A8%8B-Peter-Seibel/dp/B005V9BBDK/ref=sr_1_1?ie=UTF8&#038;qid=1323743979&#038;sr=8-1">《实用Common Lisp编程》</a>刚上市不久，Emacs和Lisp也有不少渊源，再加上Lisp作为一门生命持久的元老级别的语言，至今仍然能倍受广大黑客的推崇，我相信它一定有学习的价值，而且Hadoop的MapReduce据说也是受Lisp的map和reduce函数的启发而来，相信对于Lisp的学习肯定不会是浪费时间，尽管将来工作中应用Lisp的机会可能很少，但深入学习的话肯定会对自己有一定的启发和帮助。
 
 于是几乎同一时间我开始尝试使用Emacs并在卓越上订购了中文版的《实用Common Lisp编程》，抽空阅读尝试。总起来说这本书是非常不错的，几乎是面面俱到，但有些我认为也很有用的宏如defstruct,deftype,check-type等书中没有给出相关介绍，另外关于cl的package书中有一章节专门讲了定义的规则，但对于package的管理及安装并没有提及，我个人觉得如果是practical编程的话提一下cl中重要的ASDF包管理工具还是很有必要的，既然书中没有提到就得自己通过其它的渠道去了解学习，这方面中文的资料相对较少，大多数的资料都是在外文网站上查到的，当然也包括到stackoverflow上的提问... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>在切入正题之前先写点不相关的，工作确定之后便开始忙论文的事，忙里偷闲总想搞点什么以做娱乐，不得不说，腾讯面试官说过的要精通两到三门不同的语言我印象很深刻，自己也想尝试一下新东西，VIM让我审美疲劳了，也想尝试一下Emacs，机缘巧合由田春老师翻译的<a href="http://www.amazon.cn/%E5%AE%9E%E7%94%A8Common-Lisp%E7%BC%96%E7%A8%8B-Peter-Seibel/dp/B005V9BBDK/ref=sr_1_1?ie=UTF8&#038;qid=1323743979&#038;sr=8-1">《实用Common Lisp编程》</a>刚上市不久，Emacs和Lisp也有不少渊源，再加上Lisp作为一门生命持久的元老级别的语言，至今仍然能倍受广大黑客的推崇，我相信它一定有学习的价值，而且Hadoop的MapReduce据说也是受Lisp的map和reduce函数的启发而来，相信对于Lisp的学习肯定不会是浪费时间，尽管将来工作中应用Lisp的机会可能很少，但深入学习的话肯定会对自己有一定的启发和帮助。</p>
<p> 于是几乎同一时间我开始尝试使用Emacs并在卓越上订购了中文版的《实用Common Lisp编程》，抽空阅读尝试。总起来说这本书是非常不错的，几乎是面面俱到，但有些我认为也很有用的宏如defstruct,deftype,check-type等书中没有给出相关介绍，另外关于cl的package书中有一章节专门讲了定义的规则，但对于package的管理及安装并没有提及，我个人觉得如果是practical编程的话提一下cl中重要的ASDF包管理工具还是很有必要的，既然书中没有提到就得自己通过其它的渠道去了解学习，这方面中文的资料相对较少，大多数的资料都是在外文网站上查到的，当然也包括到stackoverflow上的提问。</p>
<p>ASDF全称是Another System Definition Facility，asdf这个组合很有意思，正好和左手的键盘基本按键重合，当初还以为是作者很有个性地起了这个随意的名字呢。我个人的理解是ASDF是类似于automake或者cmake的工具，提供一种程序包的管理方法和工具，因此也没什么复杂的地方，只不过有一些基本的规则需要遵守，了解了就没什么了。</p>
<p>程序包中一般会有一个.asd文件，该文件定义了程序包中源码文件的组织方式及依赖关系，类似于cmake中的CMakelists.txt，当然该文的编码方式是使用lisp风格的。</p>
<p><strong>1. asdf工具的安装配置。</strong><br />
目前存在的common lisp实现有很多，在这篇文章中有介绍：<a href="http://common-lisp.net/~dlw/LispSurvey.html">Common Lisp Implementations: A Survey</a>，免费的common lisp实现中性能比较好使用也比较简单的应该属sbcl了，《practical common lisp》也推荐了sbcl，作为一个新手我第一选择当然也选了sbcl，在sbcl中已经集成了asdf工具，无需再手动安装，但安装方法也很简单，可以参考<a href="http://common-lisp.net/project/asdf/asdf.html#Loading-ASDF">ASDF Manual</a>，该手册对asdf有详细的介绍。</p>
<p><strong>2. asdf包的编译加载。</strong><br />
asdf是一个工具集，可以对包进行各种操作，其中包括编译，加载等。asdf对于包的编译和加载等操作都需要基于.asd文件，也就是说编译某个包asdf需要先找到该包对应的.asd文件，该文件一般是存放在源码根目录中的，asdf对于该文件的寻址有两种方式，new style和old style，下面分别简单介绍下这两种方式：</p>
<p>old style是为了兼容旧版本的程序的，目前已经不推荐新程序使用，其方法是将程序包的.asd文件所存在的路径添加到asdf的*central-registry*变量中，可使用如下语句完成该操作：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>push <span style="color: #ff0000;">&quot;/home/levin/lisp/spider/&quot;</span> asdf<span style="color: #66cc66;">:</span>*central-registry*<span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>上述操作便可将/home/levin/lisp/spider/（注意结尾的&#8217;/'）这个路径添加到*central-registry*变量中，这样asdf便可对该包进行寻址，但在REPL中执行的该操作只对当前的会话有效的，REPL重启后需要重新添加路径到该变量中，未免有些繁琐，可以将该语句添加至common lisp实现的启动文件中，如sbcl即为.sbclrc这个文件，这样在REPL启动之后该路径便会自动添加到*central-registry*变量中。</p>
<p>new style是ASDF2所提倡使用的方法，其方法是在~/.config/中创建common-lisp目录用于存放相关的配置文件，和该主题相关的配置文件需要创建一个名为source-registry.conf.d的目录，在该目录下可以创建文件名任意的文件，将下面的语句添加至该文件中即可，文件如：</p>
<p>~/.config/common-lisp/source-registry.conf.d/01-spider-source.conf</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">directory</span> <span style="color: #ff0000;">&quot;/home/lisp/lisp/spider/&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>如上表示在/home/lisp/lisp/spider/这个目录中查找.asd文件，也可以将:directory替换成:tree，使asdf在目录中递归寻找.asd文件：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">tree</span> <span style="color: #ff0000;">&quot;/home/lisp/lisp/spider/&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>完成对asdf包的寻址操作之后，便可以使用asdf对程序包进行编译加载，编译和加载包分别可使用：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">compile-system</span> <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span>
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">load-system</span> <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>也可以使用asdf的operate对包进行编译和加载：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">oos</span> 'asdf<span style="color: #66cc66;">:</span><span style="color: #555;">compile-op</span> <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span>
CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">oos</span> 'asdf<span style="color: #66cc66;">:</span><span style="color: #555;">load-op</span> <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>对于一个未经编译完成的包在编译完成会自动加载，同样，未编译完成的包在加载时也会自动先进行编译。</p>
<p><strong>3. asdf系统的构建：</strong></p>
<p>之前提到的.asd文件是asdf的很重要的文件，该文件最简单的形式如下：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>defpackage <span style="color: #66cc66;">:</span><span style="color: #555;">spider-system</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">use</span> <span style="color: #66cc66;">:</span><span style="color: #555;">cl</span> <span style="color: #66cc66;">:</span><span style="color: #555;">asdf</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>
<span style="color: #66cc66;">&#40;</span>in-package <span style="color: #66cc66;">:</span><span style="color: #555;">spider-system</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>defsystem spider
  <span style="color: #66cc66;">:</span><span style="color: #b1b100;">name</span> <span style="color: #ff0000;">&quot;spider&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">author</span> <span style="color: #ff0000;">&quot;levin li&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">version</span> <span style="color: #ff0000;">&quot;0.0.1&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">license</span> <span style="color: #ff0000;">&quot;MIT&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">description</span> <span style="color: #ff0000;">&quot;A spider program.&quot;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">depends-on</span> <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">iolib</span><span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">:</span><span style="color: #555;">components</span> <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">file</span> <span style="color: #ff0000;">&quot;package&quot;</span><span style="color: #66cc66;">&#41;</span>
               <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">file</span> <span style="color: #ff0000;">&quot;spider&quot;</span> <span style="color: #66cc66;">:</span><span style="color: #555;">depends-on</span> <span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;package&quot;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>最开头的包定义是为了防止system名与其它包冲突，定义的包中包含一些基本信息，:depends-on声明了该程序将要依赖的包的，:components定义了包中的组件，即源代码，上述的源码包中只有两个文件，package.lisp和spider.lisp，package.lisp是定义一个cl package，可以理解为其它语言中的namespace，package.lisp定义了一个package，之后的程序如spider.lisp可能都会包含在该包中，因此:depends-on(&#8220;package&#8221;)这句话就非常重要，它会在编译spider.lisp之前先加载已经编译的好的package.lisp，若未声明spider依赖package，则在一次编译完成后REPL重启并对spider.lisp进行修改，而package.lisp未加改动，则asdf只会编译修改过的spider.lisp，这时候系统会提示spider包未找到，因为asdf发现package.lisp的目标文件是最新的，默认不会对其进行编译和加载，这也是我刚开始遇到的问题之一，在认真读了ASDF Manual后才解决了这个问题，关于该文件的一些详细的定义可以参考ASDF Manual。</p>
<p>package.lisp的内容如下：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>in-package <span style="color: #66cc66;">:</span><span style="color: #555;">cl-user</span><span style="color: #66cc66;">&#41;</span>
&nbsp;
<span style="color: #66cc66;">&#40;</span>defpackage <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">use</span> <span style="color: #66cc66;">:</span><span style="color: #555;">cl</span> <span style="color: #66cc66;">:</span><span style="color: #555;">iolib</span><span style="color: #66cc66;">&#41;</span>
  <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">:</span><span style="color: #555;">export</span> <span style="color: #66cc66;">:</span><span style="color: #555;">send-request</span>
           <span style="color: #66cc66;">:</span><span style="color: #555;">test</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>其中定义了包的名称，包的依赖的其它包，及要导出的符号等，关于package的更多介绍还是参考《practical common lisp》等资料。而spider.lisp是代码文件，开头需要有一行:</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;"><span style="color: #66cc66;">&#40;</span>in-package <span style="color: #66cc66;">:</span><span style="color: #555;">spider</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>表明当前程序包在spider包中定义，继而可以在spider.lisp中编写其它的逻辑代码，定义函数，宏或变量，所有的这些定义都被包含在包spider中，其中只有在package.lisp中声明为:export的符号才可以被其它包所引用，如上定义，该包中只有send-request和test两个函数可以被其它外部包引用。</p>
<p><strong>4. asdf包的安装</strong></p>
<p>绝大多数的common lisp包都是使用asdf组织的，可以使用asdf-install工具安装软件包，asdf-install使用之前需要先加载：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf<span style="color: #66cc66;">:</span><span style="color: #555;">oos</span> 'asdf<span style="color: #66cc66;">:</span><span style="color: #555;">load-op</span> <span style="color: #66cc66;">:</span><span style="color: #555;">asdf-install</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>之后便可以使用asdf-install安装软件包，一般而言有三种方式：</p>
<p>1. 通过包的名字进行安装：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf-install<span style="color: #66cc66;">:</span><span style="color: #555;">install</span> <span style="color: #ff0000;">&quot;iolib&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>这时asdf-install会自动从cliki.net上下载可用的包并安装，<a href="http://www.cliki.net/asdf-install">http://www.cliki.net/asdf-install</a>这个页面列出了在线可用的软件包的列表。</p>
<p>2. 通过包的url进行安装：</p>

<div class="wp_syntax"><div class="code"><pre class="lisp" style="font-family:monospace;">CL-USER<span style="color: #66cc66;">&gt;</span> <span style="color: #66cc66;">&#40;</span>asdf-install<span style="color: #66cc66;">:</span><span style="color: #555;">install</span> <span style="color: #ff0000;">&quot;http://weitz.de/files/cl-ppcre.tar.gz&quot;</span><span style="color: #66cc66;">&#41;</span></pre></div></div>

<p>3. 通过包的本地路径进行安装：<br />
CL-USER> (asdf-install:install &#8220;/home/levin/lisp/iolib.tar.gz&#8221;)</p>
<p>更加详细的安装方法可以参考<a href="http://common-lisp.net/project/asdf-install/tutorial/">asdf-install turorial</a>.</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/constructing-common-lisp-package-by-asdf.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>毕业求职经历</title>
		<link>http://basiccoder.com/graduate-job-hunting.html</link>
		<comments>http://basiccoder.com/graduate-job-hunting.html#comments</comments>
		<pubDate>Wed, 02 Nov 2011 15:48:55 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[My Life]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=941</guid>
		<description><![CDATA[前天去淘宝把三方交了，找工作就到此为止了，找工作的经历虽然不算复杂漫长，但也觉得很累很辛苦。

正式开始投简历找工作应该是从九月份开始的，九月中旬注册了几家招聘网站，陆陆续续地开始往外投简历，目标也比较明确，虽然专业一直是通信，但却想去互联网公司做后端开发，服务器或者分布式系统相关的开发，所以通信类的公司没有怎么投简历，只投了华为的云计算方向和爱立信的软件研发方向。国企没有投，片面地觉得国企是最没有战斗力的公司，还是想到互联网行业的私企做些有挑战性的工作。

第一家面试的公司是IBM研究院，在上地的钻石大厦，离北邮超级远，先坐车到清华西门，然后转车到西北旺，下车后发现右手边是农田，左手边是树林，没有路人，没有出租车，于是完全迷路了，后来沿着树林走了一段时间后偶然发现树林对面有座大楼冒出个头来，于是穿过树林过去问了下那楼的保安，说那就是钻石大厦，OMG，让我好找。IBM CRL没有笔试，师兄推荐的部门，因为是第一次面试没什么经验，也没有准备英文自我介绍，还有些紧张，做了一个简单的slide，没有被问到复杂的算法问题，因为时间比较短，所以挑了一两个重点的项目讲了讲，结束后感觉表现真的很烂，也可能是师兄推荐的原因最后让我拿到了二面的机会，二面安排在十一前，仍然是讲项目，在师兄的指点下改了slide，而且也提前准备了英文自我介绍，这中间也参加过几次面试，也有了点经验，所以感觉还不错，结束后几天也拿到了互联网公司的offer，IBM CRL感觉不太适合我，那边偏向于研究一些，像我这种做工程的在那边确实不是太好发展，于是联系师兄把情况说清楚了，已经浪费了大家两次面试的时间了，如果有第三次面试机会我还是不去了，这样IBM的面试也就到此为止了... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>前天去淘宝把三方交了，找工作就到此为止了，找工作的经历虽然不算复杂漫长，但也觉得很累很辛苦。</p>
<p>正式开始投简历找工作应该是从九月份开始的，九月中旬注册了几家招聘网站，陆陆续续地开始往外投简历，目标也比较明确，虽然专业一直是通信，但却想去互联网公司做后端开发，服务器或者分布式系统相关的开发，所以通信类的公司没有怎么投简历，只投了华为的云计算方向和爱立信的软件研发方向。国企没有投，片面地觉得国企是最没有战斗力的公司，还是想到互联网行业的私企做些有挑战性的工作。</p>
<p>第一家面试的公司是IBM研究院，在上地的钻石大厦，离北邮超级远，先坐车到清华西门，然后转车到西北旺，下车后发现右手边是农田，左手边是树林，没有路人，没有出租车，于是完全迷路了，后来沿着树林走了一段时间后偶然发现树林对面有座大楼冒出个头来，于是穿过树林过去问了下那楼的保安，说那就是钻石大厦，OMG，让我好找。IBM CRL没有笔试，师兄推荐的部门，因为是第一次面试没什么经验，也没有准备英文自我介绍，还有些紧张，做了一个简单的slide，没有被问到复杂的算法问题，因为时间比较短，所以挑了一两个重点的项目讲了讲，结束后感觉表现真的很烂，也可能是师兄推荐的原因最后让我拿到了二面的机会，二面安排在十一前，仍然是讲项目，在师兄的指点下改了slide，而且也提前准备了英文自我介绍，这中间也参加过几次面试，也有了点经验，所以感觉还不错，结束后几天也拿到了互联网公司的offer，IBM CRL感觉不太适合我，那边偏向于研究一些，像我这种做工程的在那边确实不是太好发展，于是联系师兄把情况说清楚了，已经浪费了大家两次面试的时间了，如果有第三次面试机会我还是不去了，这样IBM的面试也就到此为止了。</p>
<p>第二家面试的单位应该是华为了，之前参加过一个华为的机试，出奇的简单，就只有一道题补全一个函数，功能是求数组里面小于平均数的数的个数，两分钟就写完了然后在机房里面刷推想等下同学，于是就被赶出去了。过了不久通知面试，在知春路某酒店，记得通知的是12点，十一点半赶过去，等了一会儿面试官让先去吃饭，吃完饭回来继续等，直到等到下午将近一点半实在有点不耐烦了，去找hr简单表达了一下不满，于是过了十分钟大约一点半左右被安排了面试。虽然面试的是云计算，但我相信给我面试的那个姐姐应该对云计算也没有什么深入的了解，而且对我的开源项目表现出不屑，只对简历上写的实验室的863项目感点兴趣，说是因为是国家项目，其实学校里面做的国家项目大家都懂的，当时自己心情也确实没那么有耐心了，毕竟已经在那里浪费了两个小时的时间了，一个多小时后结束，第二天居然收到了二面通知，没有去参加二面，华为就这样结束了。</p>
<p>考虑到华为的这种情况，通信公司我已经完全不想考虑了，爱立信的机试我也没有参加。</p>
<p>网易有道经过实验室师兄内推搞到了一个提前参加笔试的机会，清华科技园创业大厦，笔试是在晚上，题量不大但做得很烂，但很幸运地收到了面试通知，一面的时候和面试官比较投缘，问的问题我也大都了解，tcp/ip,socket,epoll等等的，都比较熟，算法问题也都答的差不多，很顺利进了二面，二面表现比较差，问了几个系统设计的题，微博的推送机制，搜索引擎的搜索提示，这些我之前还真没去了解过，想了想把就重点说了下cache，结合着kv系统简单设计了下，面试官也没说好坏，开始问的两个算法题记不清是什么了，但印象中是没有什么好的思路的，那一次面试感觉非常差，但因为没有收到拒信也纠结了好久，终于在十一过后的某一天收到了三面的通知，有点意外，三面的大哥没有问算法，问我的软件的架构，分布式缓存系统设计等等，感觉答的还不错，感觉至少要比二面好，等了好久，却收到了有道的拒信，有道成了第一家拒掉我的公司，找工作需要实力也需要机缘，有道的面试我应该是实力和机缘都不够吧。</p>
<p>淘宝的笔试在华为二面的那天晚上，没有参加华为下午的二面也是怕他们太拖拉耽误了淘宝的笔试，笔试现场很混乱，因为没有记名，霸面的同学把屋子挤得满满的，记不清当时是怎么协调的了，出着汗答到时间结束，最后一个算法大题居然没来得及想，随便写了个简单算法，后来面试的时候瞅了一眼笔试的试卷，那个题只得了3分，后面的附加题到是抽时间做了，线程进程，锁，网络编程都是比较熟悉的东西。在北理参加360笔试的时候收到了淘宝的面试通知，东三环嘉泰金融大厦，离北邮也确实有点远，淘宝当天面试的人很多，但还算比较准时的，一面问的基础都比较熟悉，每次一被问到STL我就怂了，这东西只会简单的用用，不过跟面试官表达过长时间使用C之后，他问的关于STL的都是怎么用的方面，这些我还都是搞得定的，后来让写了个递归的小程序，也很简单，结束后让我去休息去等一会儿，过了一会儿又把我叫到一间小会议室里面，我问这是要干嘛，说是面试啊，原来是连着面的，于是自我介绍讲项目，二面的面试官感觉很犀利，问C10K，prefork/worker-master在C10K问题上的差别，我之前理解的也不太对，面试官也都给我解释清楚了，后来问了很多，关于读的书，平时的爱好等等，还出了两个智力题，只想出来了一个，本来有不少问题要问的，关于淘宝的开源产品等等的问题，后来HR在外面示意时间应该是超了，我就没仔细问，于是又去休息区等着了。中午淘宝请吃了个午饭，下午一点多三面，见到了大牛行癫，被问到了Memcached和Redis的哈希表的处理方式的问题，异步的expand，但Memcached其实我表达错了，Memcached是异步来rehash的，而Redis因为是单线程，它的rehash是嵌在各种操作(lookup,insert)中进行的，都不是一下全部完成的，因为大数据量时rehash会相当耗时，当然，我理解不对的地方行癫也都给我解释清楚了，最后还把二面关于淘宝基础产品的问题都问了，之后就是回来等消息了，几天后收到offer，没有传说中的几十w年薪，选择淘宝是因为在淘宝能做想做的方向，<a href="http://twitter.com/colyli">@colyli</a>帮忙推荐到淘宝的虚拟化团队做分布式存储，这个方向我很感兴趣，所以拿到淘宝offer之后找工作就基本接近尾声了。</p>
<p>360的面试是在淘宝后的几天，北大博雅大酒店，打车杀了过去，360也是三面连着的，前两面是技术面，最后一面是hr面，一面的技术面问了很多基础知识，甚至还问了vim的操作，这些都比较熟悉，另外也问了C10K的问题，其它的都是在聊天了，面试官人很好，给我讲hadoop，分布式系统等等，二面也差不多的流程，感觉面试官主要是想看我能不能把心思都放到工作上，可能是因为简历上我在工作之外做的自己的项目比较多，当然，这个肯定不是问题，三面过后也同样拿到了360的offer，和淘宝几乎是同一时间，感觉非常对不起360，没有决定去却接受了人家的offer，这是非常损rp的，所以在把三方交给淘宝之后赶紧写邮件给360的hr拒掉了这个offer。</p>
<p>腾讯的笔试应该是在清华，比较晚了，因为当时拿到了淘宝的offer，其实已经不太想再折腾了，因为感觉选择越多最后纠结就越多，淘宝的工作我已经很满意了，但后来觉得腾讯是中国互联网老大，不去试试也不甘心，笔试还不错，题基本上都会，一面是在知春路的京仪大酒店，两点钟面试官非常准时，问的东西都很常细，线程池，锁，网络编程的细节，算法，都是些很细节的问题，那次面试我才知道有pthread_cond_timewait()这个函数，惭愧，二面安排在腾讯北京分公司，苏州街银科大厦，面试过程比较随意，面试人很好，感觉就是聊天，问了一些关于项目的问题，最后问了一个智力题，当时感冒还发着烧，脑子实在是转不动，于是就没想出来，于是就这样结束了，三面是hr面，hr叔叔人也很好，问了一下工作中遇到的问题，offer的期待等等，二十多分钟，最后送我走的时候还给我介绍了腾讯北京这边的情况，给我介绍了腾讯演播室，带我看了很多名星的签名，照片，走的时候还看到了超级正典腾讯18层的前台MM，突然觉得去腾讯工作也不错哈。hr说要11月1号到10号之间才会发offer，30号我就需要把三方交给淘宝，跟hr说过这个情况，说自己没有太多选择，希望能尽快得到腾讯的消息好做决定，但30号也实在太仓促只有两天的时间，于是最后只能选择了淘宝，今天是2号仍然没有收到腾讯的消息，有可能是挂了，即便是有幸拿到offer现在也去不了了。</p>
<p>参加过的笔试还有微软，EMC这两家笔试完都没消息了，微软笔试答得很烂，可以理解，EMC笔试感觉还不错的，结果也一样挂了，当时都没怎么重视笔试，以为笔试一般不会挂掉，但事实上我大多数都是挂在了笔试。</p>
<p>之前也参加过谷歌的笔试，安排在了清华的技科楼，当时顶着巨大的堵车压力勉强按时赶到清华，但诺大一个清华居然没有人知道技科楼怎么走，问了好久都没有找到路，最后好不容易找到个地图才发现了那位置，找到考场的时候已经开始考了将近20分钟了，最近我还很嚣张地提前交了试卷，其实感觉答得还算可以，不过肯定是过不了的，后来果然收到了谷歌的拒信，不过能有机会参加一次谷歌的笔试也很满足啦，嗯。</p>
<p>参加过笔试面试的差不多就这几家，没有广撒网式的投简历，之前也投过新浪搜狐亚马逊等互联网公司的简历，最后都放弃了笔试，当然，绝不是看不上这些公司，只是觉得有个差不多的工作就可以了，选择越多纠结越多，而且最后拿到别人的offer最后又不去给人家公司也会带来麻烦，影响人家招聘。</p>
<p>找工作的经历基本上就这些了，抓紧时间把毕业论文搞出来，也希望能有时间可以去淘宝实习几天，提前了解下要做的东西。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/graduate-job-hunting.html/feed</wfw:commentRss>
		<slash:comments>26</slash:comments>
		</item>
		<item>
		<title>Redis的事件循环与定时器模型</title>
		<link>http://basiccoder.com/redis%e7%9a%84%e4%ba%8b%e4%bb%b6%e5%be%aa%e7%8e%af%e4%b8%8e%e5%ae%9a%e6%97%b6%e5%99%a8%e6%a8%a1%e5%9e%8b.html</link>
		<comments>http://basiccoder.com/redis%e7%9a%84%e4%ba%8b%e4%bb%b6%e5%be%aa%e7%8e%af%e4%b8%8e%e5%ae%9a%e6%97%b6%e5%99%a8%e6%a8%a1%e5%9e%8b.html#comments</comments>
		<pubDate>Fri, 07 Oct 2011 11:33:25 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[NoSql]]></category>
		<category><![CDATA[epoll]]></category>
		<category><![CDATA[Redis]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=930</guid>
		<description><![CDATA[假期的最后一天，简单翻阅了下<a href="http://redis.io">Redis</a>的源码，读一款server软件的源码我一般是从进程/线程模型开始的，Redis让我有些诧异，它采用了单进程单线程的模型，一般的server软件都会采用多进程或者多线程再或者多线程多进程混合的模型来设计，从而充分利用多核处理器的并行计算能力来提高软件的性能，Redis这种模型我只能推断程序的可并行化程度不高，顺序计算反而能省去多线程同步和维护线程池/进程池的开销，我对于数据库server端的设计没有什么经验也没有太多的理解，如有谬误欢迎大家指正。

当然，这里要写的不是关于Redis的进程模型，而是Redis的事件模型和定时器模型。

Redis没有依赖<a href="http://en.wikipedia.org/wiki/Libevent">libevent</a>，而是自己通过IO多路复用的方式来实现了事件循环和定时器，不像<a href="http://en.wikipedia.org/wiki/Nginx">nginx</a>或者<a href="http://en.wikipedia.org/wiki/Apache">apache</a>有多种多路复用方式可供选择，Redis只采用了三种：epoll/kqueue/select，默认采用<a href="http://en.wikipedia.org/wiki/Epoll">epoll</a>，在linux环境下最优的方式当然是epoll，当在FreeBSD平台下epoll不存在时则使用<a href="http://en.wikipedia.org/wiki/Kqueue">kqueue</a>，当然若两种方式都未定义则使用性能最差的<a href="http://en.wikipedia.org/wiki/Select_%28Unix%29">select</a>，我只阅读了跟epoll相关的代码... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>假期的最后一天，简单翻阅了下<a href="http://redis.io">Redis</a>的源码，读一款server软件的源码我一般是从进程/线程模型开始的，Redis让我有些诧异，它采用了单进程单线程的模型，一般的server软件都会采用多进程或者多线程再或者多线程多进程混合的模型来设计，从而充分利用多核处理器的并行计算能力来提高软件的性能，Redis这种模型我只能推断程序的可并行化程度不高，顺序计算反而能省去多线程同步和维护线程池/进程池的开销，我对于数据库server端的设计没有什么经验也没有太多的理解，如有谬误欢迎大家指正。</p>
<p>当然，这里要写的不是关于Redis的进程模型，而是Redis的事件模型和定时器模型。</p>
<p>Redis没有依赖<a href="http://en.wikipedia.org/wiki/Libevent">libevent</a>，而是自己通过IO多路复用的方式来实现了事件循环和定时器，不像<a href="http://en.wikipedia.org/wiki/Nginx">nginx</a>或者<a href="http://en.wikipedia.org/wiki/Apache">apache</a>有多种多路复用方式可供选择，Redis只采用了三种：epoll/kqueue/select，默认采用<a href="http://en.wikipedia.org/wiki/Epoll">epoll</a>，在linux环境下最优的方式当然是epoll，当在FreeBSD平台下epoll不存在时则使用<a href="http://en.wikipedia.org/wiki/Kqueue">kqueue</a>，当然若两种方式都未定义则使用性能最差的<a href="http://en.wikipedia.org/wiki/Select_%28Unix%29">select</a>，我只阅读了跟epoll相关的代码。</p>
<p>在<strong>main()</strong>函数的最后调用了<strong>aeMain()</strong>这个函数进入Redis的事件循环，这个函数的很简单，循环调用<strong>aeProcessEvents()</strong>来对事件进行处理:</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> aeMain<span style="color: #009900;">&#40;</span>aeEventLoop <span style="color: #339933;">*</span>eventLoop<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    eventLoop<span style="color: #339933;">-&gt;</span>stop <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>eventLoop<span style="color: #339933;">-&gt;</span>stop<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">-&gt;</span>beforesleep <span style="color: #339933;">!=</span> NULL<span style="color: #009900;">&#41;</span>
            eventLoop<span style="color: #339933;">-&gt;</span>beforesleep<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        aeProcessEvents<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">,</span> AE_ALL_EVENTS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>在此之前Redis做了很多初始化的工作，这些工作大多是在<strong>initServer()</strong>这个函数中执行的，初始化一些相关的list,dict等，调用<strong>aeCreateEventLoop()</strong>初始化<a href="http://en.wikipedia.org/wiki/Event_loop">eventloop</a>，这个函数初始化eventloop相关的数据结构，并最终调用了<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_create.2.html">epoll_create()</a>函数，对epoll上下文进行初始化。紧接着Redis创建了用于listen的socket对象，并调用<strong>aeCreateFileEvent()</strong>把该socket描述符的读事件加入到事件池中去，另外，还调用了<strong>aeCreateTimeEvent()</strong>函数来初始化一下定时器，定期地执行<strong>serverCron()</strong>这个函数，接下来看一下<strong>aeCreateFileEvent()</strong>和<strong>aeCreateTimeEvent()</strong>这两个函数。</p>
<p><strong>aeCreateFileEvent()</strong>这个函数初始化<strong>aeFileEvent</strong>结构(该结构保存事件的一些状态，以及事件的文件描述符等)，并调用<strong>aeApiAddEvent()</strong>函数将描述符相关的事件添加到事件池中，对于epoll它的实现如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> aeApiAddEvent<span style="color: #009900;">&#40;</span>aeEventLoop <span style="color: #339933;">*</span>eventLoop<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> fd<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> mask<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    aeApiState <span style="color: #339933;">*</span>state <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>apidata<span style="color: #339933;">;</span>
    <span style="color: #993333;">struct</span> epoll_event ee<span style="color: #339933;">;</span>
    <span style="color: #808080; font-style: italic;">/* If the fd was already monitored for some event, we need a MOD
     * operation. Otherwise we need an ADD operation. */</span>
    <span style="color: #993333;">int</span> op <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>events<span style="color: #009900;">&#91;</span>fd<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">mask</span> <span style="color: #339933;">==</span> AE_NONE <span style="color: #339933;">?</span>
            EPOLL_CTL_ADD <span style="color: #339933;">:</span> EPOLL_CTL_MOD<span style="color: #339933;">;</span>
&nbsp;
    ee.<span style="color: #202020;">events</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    mask <span style="color: #339933;">|=</span> eventLoop<span style="color: #339933;">-&gt;</span>events<span style="color: #009900;">&#91;</span>fd<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">mask</span><span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* Merge old events */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&amp;</span> AE_READABLE<span style="color: #009900;">&#41;</span> ee.<span style="color: #202020;">events</span> <span style="color: #339933;">|=</span> EPOLLIN<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&amp;</span> AE_WRITABLE<span style="color: #009900;">&#41;</span> ee.<span style="color: #202020;">events</span> <span style="color: #339933;">|=</span> EPOLLOUT<span style="color: #339933;">;</span>
    ee.<span style="color: #202020;">data</span>.<span style="color: #202020;">u64</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* avoid valgrind warning */</span>
    ee.<span style="color: #202020;">data</span>.<span style="color: #202020;">fd</span> <span style="color: #339933;">=</span> fd<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>epoll_ctl<span style="color: #009900;">&#40;</span>state<span style="color: #339933;">-&gt;</span>epfd<span style="color: #339933;">,</span>op<span style="color: #339933;">,</span>fd<span style="color: #339933;">,&amp;</span>ee<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">return</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>非常简洁，这个函数只不过是把<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_ctl.2.html">epoll_ctl()</a>相关的操作做了一下封装，至此描述符已经加入到事件池中进行监听了，接着看<strong>aeCreateTimeEvent()</strong>这个函数。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">long</span> <span style="color: #993333;">long</span> aeCreateTimeEvent<span style="color: #009900;">&#40;</span>aeEventLoop <span style="color: #339933;">*</span>eventLoop<span style="color: #339933;">,</span> <span style="color: #993333;">long</span> <span style="color: #993333;">long</span> milliseconds<span style="color: #339933;">,</span>
        aeTimeProc <span style="color: #339933;">*</span>proc<span style="color: #339933;">,</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>clientData<span style="color: #339933;">,</span>
        aeEventFinalizerProc <span style="color: #339933;">*</span>finalizerProc<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">long</span> <span style="color: #993333;">long</span> id <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>timeEventNextId<span style="color: #339933;">++;</span>
    aeTimeEvent <span style="color: #339933;">*</span>te<span style="color: #339933;">;</span>
&nbsp;
    te <span style="color: #339933;">=</span> zmalloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>te<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>te <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">return</span> AE_ERR<span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>id <span style="color: #339933;">=</span> id<span style="color: #339933;">;</span>
    aeAddMillisecondsToNow<span style="color: #009900;">&#40;</span>milliseconds<span style="color: #339933;">,&amp;</span>te<span style="color: #339933;">-&gt;</span>when_sec<span style="color: #339933;">,&amp;</span>te<span style="color: #339933;">-&gt;</span>when_ms<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>timeProc <span style="color: #339933;">=</span> proc<span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>finalizerProc <span style="color: #339933;">=</span> finalizerProc<span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>clientData <span style="color: #339933;">=</span> clientData<span style="color: #339933;">;</span>
    te<span style="color: #339933;">-&gt;</span>next <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>timeEventHead<span style="color: #339933;">;</span>
    eventLoop<span style="color: #339933;">-&gt;</span>timeEventHead <span style="color: #339933;">=</span> te<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> id<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>同样是初始化数据结构，但没有调用<strong>aeApiAddEvent()</strong>这个函数，当然，定时器又不需要文件描述符，当然不需要添加相关事件，定时器的实现只是使用了<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_wait.2.html">epoll_wait()</a>的定时功能，<strong>aeAddMillisecondsToNow()</strong>这个函数顾名思义是把当前时间加上一个给定的毫秒数，然后算出一个when_sec和when_ms，eventloop对象的<strong>timeEventHead</strong>实际上是一个单向链表，它用于保存所有的定时器事件，当添加一个定时器事件时其实只是向该链表头中插入了一个元素，其会后由<strong>aeProcessEvents()</strong>这个函数遍历该链表取出超时的事件进行处理，接着我们看下这个事件处理里面最核心的函数。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> aeProcessEvents<span style="color: #009900;">&#40;</span>aeEventLoop <span style="color: #339933;">*</span>eventLoop<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> flags<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">int</span> processed <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> numevents<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Nothing to do? return ASAP */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_TIME_EVENTS<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_FILE_EVENTS<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Note that we want call select() even if there are no
     * file events to process as long as we want to process time
     * events, in order to sleep until the next time event is ready
     * to fire. */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">-&gt;</span>maxfd <span style="color: #339933;">!=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span> <span style="color: #339933;">||</span>
        <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_TIME_EVENTS<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_DONT_WAIT<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">int</span> j<span style="color: #339933;">;</span>
        aeTimeEvent <span style="color: #339933;">*</span>shortest <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
        <span style="color: #993333;">struct</span> timeval tv<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>tvp<span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_TIME_EVENTS <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_DONT_WAIT<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
            shortest <span style="color: #339933;">=</span> aeSearchNearestTimer<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>shortest<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #993333;">long</span> now_sec<span style="color: #339933;">,</span> now_ms<span style="color: #339933;">;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;">/* Calculate the time missing for the nearest
             * timer to fire. */</span>
            aeGetTime<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>now_sec<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>now_ms<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            tvp <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>tv<span style="color: #339933;">;</span>
            tvp<span style="color: #339933;">-&gt;</span>tv_sec <span style="color: #339933;">=</span> shortest<span style="color: #339933;">-&gt;</span>when_sec <span style="color: #339933;">-</span> now_sec<span style="color: #339933;">;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>shortest<span style="color: #339933;">-&gt;</span>when_ms <span style="color: #339933;">&lt;</span> now_ms<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                tvp<span style="color: #339933;">-&gt;</span>tv_usec <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>shortest<span style="color: #339933;">-&gt;</span>when_ms<span style="color: #339933;">+</span><span style="color: #0000dd;">1000</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">-</span> now_ms<span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span><span style="color: #0000dd;">1000</span><span style="color: #339933;">;</span>
                tvp<span style="color: #339933;">-&gt;</span>tv_sec <span style="color: #339933;">--;</span>
            <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
                tvp<span style="color: #339933;">-&gt;</span>tv_usec <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>shortest<span style="color: #339933;">-&gt;</span>when_ms <span style="color: #339933;">-</span> now_ms<span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span><span style="color: #0000dd;">1000</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>tvp<span style="color: #339933;">-&gt;</span>tv_sec <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> tvp<span style="color: #339933;">-&gt;</span>tv_sec <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>tvp<span style="color: #339933;">-&gt;</span>tv_usec <span style="color: #339933;">&lt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> tvp<span style="color: #339933;">-&gt;</span>tv_usec <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #808080; font-style: italic;">/* If we have to check for events but need to return
             * ASAP because of AE_DONT_WAIT we need to se the timeout
             * to zero */</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_DONT_WAIT<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                tv.<span style="color: #202020;">tv_sec</span> <span style="color: #339933;">=</span> tv.<span style="color: #202020;">tv_usec</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
                tvp <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>tv<span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #808080; font-style: italic;">/* Otherwise we can block */</span>
                tvp <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* wait forever */</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        numevents <span style="color: #339933;">=</span> aeApiPoll<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">,</span> tvp<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>j <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> j <span style="color: #339933;">&lt;</span> numevents<span style="color: #339933;">;</span> j<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            aeFileEvent <span style="color: #339933;">*</span>fe <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>eventLoop<span style="color: #339933;">-&gt;</span>events<span style="color: #009900;">&#91;</span>eventLoop<span style="color: #339933;">-&gt;</span>fired<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">fd</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
            <span style="color: #993333;">int</span> mask <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>fired<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">mask</span><span style="color: #339933;">;</span>
            <span style="color: #993333;">int</span> fd <span style="color: #339933;">=</span> eventLoop<span style="color: #339933;">-&gt;</span>fired<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">fd</span><span style="color: #339933;">;</span>
            <span style="color: #993333;">int</span> rfired <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
	    <span style="color: #808080; font-style: italic;">/* note the fe-&gt;mask &amp; mask &amp; ... code: maybe an already processed
             * event removed an element that fired and we still didn't
             * processed, so we check if the event is still valid. */</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fe<span style="color: #339933;">-&gt;</span>mask <span style="color: #339933;">&amp;</span> mask <span style="color: #339933;">&amp;</span> AE_READABLE<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                rfired <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
                fe<span style="color: #339933;">-&gt;</span>rfileProc<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">,</span>fd<span style="color: #339933;">,</span>fe<span style="color: #339933;">-&gt;</span>clientData<span style="color: #339933;">,</span>mask<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>fe<span style="color: #339933;">-&gt;</span>mask <span style="color: #339933;">&amp;</span> mask <span style="color: #339933;">&amp;</span> AE_WRITABLE<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>rfired <span style="color: #339933;">||</span> fe<span style="color: #339933;">-&gt;</span>wfileProc <span style="color: #339933;">!=</span> fe<span style="color: #339933;">-&gt;</span>rfileProc<span style="color: #009900;">&#41;</span>
                    fe<span style="color: #339933;">-&gt;</span>wfileProc<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #339933;">,</span>fd<span style="color: #339933;">,</span>fe<span style="color: #339933;">-&gt;</span>clientData<span style="color: #339933;">,</span>mask<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            processed<span style="color: #339933;">++;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #808080; font-style: italic;">/* Check time events */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>flags <span style="color: #339933;">&amp;</span> AE_TIME_EVENTS<span style="color: #009900;">&#41;</span>
        processed <span style="color: #339933;">+=</span> processTimeEvents<span style="color: #009900;">&#40;</span>eventLoop<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> processed<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* return the number of processed file/time events */</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>这其中的<strong>aeApiPoll()</strong>这个函数其实就是对<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_wait.2.html">epoll_wait()</a>操作的一个封装，<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_wait.2.html">epoll_wait()</a>的最后一个参数是一个毫秒级的超时时间，Redis充分利用了这个时间在对IO事件进行监听的同时来实现了定时。这个函数的前面一大部分代码是在计算这个超时时间的，它调用<strong>aeSearchNearestTimer()</strong>这个函数来获取最近要超时的一个定时器对象，如何获取的呢？就是遍历刚才的提到的那个<strong>timeEventHead</strong>链表，来找出时间值最小的一个，注意是遍历，因为链表中的定时器也是无序的，不过我相信作者有一天会把它换成红黑树或者其它的数据结构吧。如果找到一个将要超时的定时器，则将它与当前时间进行比较，如果当前时间大于定时器时间则表示定时器已超时，将超时时间设为0，若当前时间小于定时器时间，则将超时时间设为两者之差。如果定时器队列为空，或者说没有任何定时器事件，则可以根据<strong>AE_DONT_WAIT</strong>这个标志来决定<a href="http://www.kernel.org/doc/man-pages/online/pages/man2/epoll_wait.2.html">epoll_wait()</a>是non-blocking立即返回，还是一直阻塞在那里。</p>
<p><strong>aeApiPoll()</strong>函数返回时，有两种情况，一种是IO事件被触发，另一种是定时器超时，当IO事件被触发时，遍历所有活跃描述符并调用相关的回调函数对其进行处理。当没有IO事件被触发，而是超时时，则返回值numevents为0，函数会转向<strong>processTimeEvents()</strong>来遍历定时器列表，调用定时器回调函数处理定时器事件，当IO事件被触发而并没有定时器超时时，如果设置了<strong>AE_TIME_EVENTS</strong>标志则也会对定时器列表进行遍历，主循环便是如此，我认为这会多少对效率有一定的影响，当然可能现在的Redis定时器列表并不太大，所以效率问题也可以忽略。</p>
<p>以上是简单地对今天的工作做的总结，欢迎大家批评指教。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/redis%e7%9a%84%e4%ba%8b%e4%bb%b6%e5%be%aa%e7%8e%af%e4%b8%8e%e5%ae%9a%e6%97%b6%e5%99%a8%e6%a8%a1%e5%9e%8b.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Memcached内存管理机制浅析</title>
		<link>http://basiccoder.com/memcached-memory-mamagement.html</link>
		<comments>http://basiccoder.com/memcached-memory-mamagement.html#comments</comments>
		<pubDate>Thu, 08 Sep 2011 12:41:12 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[NoSql]]></category>
		<category><![CDATA[Memcached]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=923</guid>
		<description><![CDATA[<a href="http://memcached.org/">Memcached</a>的内存管理在网上也可以搜集到不少不错的文章，新浪的这篇《<a href="http://blog.developers.api.sina.com.cn/?p=124">Memcached深度分析</a>》讲得不错，读别人的文章还是不如自己直接去读源码分析源码来得直接，这里写一下我阅读Memcached源码时对于Memcached内存管理机制的理解。

Memcached的代码结构很简单，从<strong>main()</strong>函数入口进去之后便是几个模块的初始化函数，和内存管理相关的主要有两个函数，一个是<strong>assoc_init()</strong>，这个是用来初始化哈希表的，关于这个哈希表的作用留在外面讨论，另一个是<strong>slabs_init()</strong>，该函数用来初始化slab，下面先来讨论一下slab机制。

<div><strong>1. 内存slab的管理</strong></div>

<div><strong>1.1 slabs的初始化</strong></div>
Memcached把内存分为一个个的slab，每个slab又分成一个个的chunk，系统会定义一个slab_class数组，其中每个元素是都是一个对该slab的描述，包括这个slab里面的每个chunk的大小，这个slab里面包含多少个chunk等信息，先把slab分布情况打印出来看看，对Memcached的内存分配有个大体的认识，然后再去读代码可能会好一些。... ]]></description>
				<content:encoded><![CDATA[<p><a href="http://memcached.org/">Memcached</a>的内存管理在网上也可以搜集到不少不错的文章，新浪的这篇《<a href="http://blog.developers.api.sina.com.cn/?p=124">Memcached深度分析</a>》讲得不错，读别人的文章还是不如自己直接去读源码分析源码来得直接，这里写一下我阅读Memcached源码时对于Memcached内存管理机制的理解。</p>
<p>Memcached的代码结构很简单，从<strong>main()</strong>函数入口进去之后便是几个模块的初始化函数，和内存管理相关的主要有两个函数，一个是<strong>assoc_init()</strong>，这个是用来初始化哈希表的，关于这个哈希表的作用留在外面讨论，另一个是<strong>slabs_init()</strong>，该函数用来初始化slab，下面先来讨论一下slab机制。</p>
<div><strong>1. 内存slab的管理</strong></div>
<div><strong>1.1 slabs的初始化</strong></div>
<p>Memcached把内存分为一个个的slab，每个slab又分成一个个的chunk，系统会定义一个slab_class数组，其中每个元素是都是一个对该slab的描述，包括这个slab里面的每个chunk的大小，这个slab里面包含多少个chunk等信息，先把slab分布情况打印出来看看，对Memcached的内存分配有个大体的认识，然后再去读代码可能会好一些。</p>
<div style="line-height:14px">
$ memcached -vv<br />
slab class   1: chunk size        80 perslab   13107<br />
slab class   2: chunk size       104 perslab   10082<br />
slab class   3: chunk size       136 perslab    7710<br />
slab class   4: chunk size       176 perslab    5957<br />
slab class   5: chunk size       224 perslab    4681<br />
slab class   6: chunk size       280 perslab    3744<br />
slab class   7: chunk size       352 perslab    2978<br />
slab class   8: chunk size       440 perslab    2383<br />
slab class   9: chunk size       552 perslab    1899<br />
slab class  10: chunk size       696 perslab    1506<br />
slab class  11: chunk size       872 perslab    1202<br />
slab class  12: chunk size      1096 perslab     956<br />
slab class  13: chunk size      1376 perslab     762<br />
slab class  14: chunk size      1720 perslab     609<br />
slab class  15: chunk size      2152 perslab     487<br />
slab class  16: chunk size      2696 perslab     388<br />
slab class  17: chunk size      3376 perslab     310<br />
slab class  18: chunk size      4224 perslab     248<br />
slab class  19: chunk size      5280 perslab     198<br />
slab class  20: chunk size      6600 perslab     158<br />
slab class  21: chunk size      8256 perslab     127<br />
slab class  22: chunk size     10320 perslab     101<br />
slab class  23: chunk size     12904 perslab      81<br />
slab class  24: chunk size     16136 perslab      64<br />
slab class  25: chunk size     20176 perslab      51<br />
slab class  26: chunk size     25224 perslab      41<br />
slab class  27: chunk size     31536 perslab      33<br />
slab class  28: chunk size     39424 perslab      26<br />
slab class  29: chunk size     49280 perslab      21<br />
slab class  30: chunk size     61600 perslab      17<br />
slab class  31: chunk size     77000 perslab      13<br />
slab class  32: chunk size     96256 perslab      10<br />
slab class  33: chunk size    120320 perslab       8<br />
slab class  34: chunk size    150400 perslab       6<br />
slab class  35: chunk size    188000 perslab       5<br />
slab class  36: chunk size    235000 perslab       4<br />
slab class  37: chunk size    293752 perslab       3<br />
slab class  38: chunk size    367192 perslab       2<br />
slab class  39: chunk size    458992 perslab       2<br />
slab class  40: chunk size    573744 perslab       1<br />
slab class  41: chunk size    717184 perslab       1<br />
slab class  42: chunk size   1048576 perslab       1
</div>
<p>这是Memcached的默认配置，chunk size是按照<strong>CHUNK_ALIGN_BYTES</strong>对齐的，chunk size相比于前一个slab中的chunk size有一个上升因子factor，1.4.7里面factor的默认值是1.25，我们可以看到按默认配置slab总共分成了42类。<br />
先给出一个我用Dia画的Memcached的内存分配图，<a href="http://projects.gnome.org/dia/">Dia</a>不如Visio好用，凑合着画了一个，如果有理解不对的地方欢迎大家指出。<br />
<a href="http://basiccoder.com/wp-content/uploads/2011/09/memcached_memory_layout.png"><img src="http://basiccoder.com/wp-content/uploads/2011/09/memcached_memory_layout.png" alt="" title="memcached_memory_layout" width="516" height="417" class="aligncenter size-full wp-image-925" /></a></p>
<p>接下来看一下slabs_init()的代码，还是只保留关键代码，节省版面。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> slabs_init<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> size_t limit<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">double</span> factor<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> bool prealloc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">int</span> i <span style="color: #339933;">=</span> POWER_SMALLEST <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> size <span style="color: #339933;">=</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>item<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> settings.<span style="color: #202020;">chunk_size</span><span style="color: #339933;">;</span>
&nbsp;
    mem_limit <span style="color: #339933;">=</span> limit<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>prealloc<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* Allocate everything in a big chunk with malloc */</span>
        mem_base <span style="color: #339933;">=</span> malloc<span style="color: #009900;">&#40;</span>mem_limit<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>mem_base <span style="color: #339933;">!=</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            mem_current <span style="color: #339933;">=</span> mem_base<span style="color: #339933;">;</span>
            mem_avail <span style="color: #339933;">=</span> mem_limit<span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> 
    <span style="color: #009900;">&#125;</span>
    memset<span style="color: #009900;">&#40;</span>slabclass<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>slabclass<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">++</span>i <span style="color: #339933;">&lt;</span> POWER_LARGEST <span style="color: #339933;">&amp;&amp;</span> size <span style="color: #339933;">&lt;=</span> settings.<span style="color: #202020;">item_size_max</span> <span style="color: #339933;">/</span> factor<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* Make sure items are always n-byte aligned */</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>size <span style="color: #339933;">%</span> CHUNK_ALIGN_BYTES<span style="color: #009900;">&#41;</span>
            size <span style="color: #339933;">+=</span> CHUNK_ALIGN_BYTES <span style="color: #339933;">-</span> <span style="color: #009900;">&#40;</span>size <span style="color: #339933;">%</span> CHUNK_ALIGN_BYTES<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        slabclass<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">size</span> <span style="color: #339933;">=</span> size<span style="color: #339933;">;</span>
        slabclass<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">perslab</span> <span style="color: #339933;">=</span> settings.<span style="color: #202020;">item_size_max</span> <span style="color: #339933;">/</span> slabclass<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">size</span><span style="color: #339933;">;</span>
        size <span style="color: #339933;">*=</span> factor<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    power_largest <span style="color: #339933;">=</span> i<span style="color: #339933;">;</span>
    slabclass<span style="color: #009900;">&#91;</span>power_largest<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">size</span> <span style="color: #339933;">=</span> settings.<span style="color: #202020;">item_size_max</span><span style="color: #339933;">;</span>
    slabclass<span style="color: #009900;">&#91;</span>power_largest<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">perslab</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#ifndef DONT_PREALLOC_SLABS</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>pre_alloc <span style="color: #339933;">=</span> getenv<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;T_MEMD_SLABS_ALLOC&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>pre_alloc <span style="color: #339933;">==</span> NULL <span style="color: #339933;">||</span> atoi<span style="color: #009900;">&#40;</span>pre_alloc<span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000066;">printf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;prealloc memory.<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            slabs_preallocate<span style="color: #009900;">&#40;</span>power_largest<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #339933;">#endif</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>prealloc这个参数其实是跟后面的<strong>DONT_PREALLOC_SLABS</strong>这个宏是相关的，表示是否要在初始化的时候为slabs分配内存，如果需要预先为slabs分配内存，则先跟系统申请mem_limit字节的内存，之后的slab都是从这块内存上分配的，这块内存大小默认是64M，说起来当时犯了个很低级的错误，当时看到这里的时候发现mem_limit的默认值是1024 * 1024 * 64，于是断点在这里，发现<strong>malloc()</strong>没有返回NULL，当时想我一个2G的机器申请64G的内存到底是怎么分配成功的，纠结了好久才发现不是64G，是64M，所以读代码/写代码的时候还真得保持头脑清醒才行。。。</p>
<p>然后再说prealloc，如果没有定义<strong>DONT_PREALLOC_SLABS</strong>这个宏的话，初始化的时候会先申请64M的内存，接着调用preallocate这个函数，看下这个函数的注释，然后我要吐槽一下我的英语，前两天被它的那句注释搞晕了。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#ifndef DONT_PREALLOC_SLABS</span>
<span style="color: #808080; font-style: italic;">/* Preallocate as many slab pages as possible (called from slabs_init)
on start-up, so users don't get confused out-of-memory errors when
they do have free (in-slab) space, but no space to make new slabs.
if maxslabs is 18 (POWER_LARGEST - POWER_SMALLEST + 1), then all
slab types can be made. if max memory is less than 18 MB, only the
smaller ones will be made. */</span>
<span style="color: #993333;">static</span> <span style="color: #993333;">void</span> slabs_preallocate <span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> maxslabs<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#endif</span></pre></div></div>

<p>这句话的大体意思应该是：在启动的时候尽可能多地分配slabs页，所以用户不要因为内存足够却获得OOM而感到郁闷。。。</p>
<p>我当时的理解时：在启动的时候尽可能多地分配slabs页，这样用户就不会因为内存足够却被提示OOM而感到郁闷了。。。</p>
<p>我仔细地查阅源代码，发现如果prealloc，那么64M的内存用光之后并不会再去malloc新内存，跟我当时理解的这句注释的意思正好相反，仔细研究代码发现代码没有什么问题之后我反过来看了一眼这句注释，我觉得是我把这句英文理解错了吧。。。</p>
<p>OK，也就是说如果开启了prealloc功能的话，那么很有可能在有空闲内存的情况下分配内存失败，另外提前为slabs分配内存也有可能会造成内存的浪费，有可能所有的item都不会使用某个slab class，这样这个slab class里面分配的内存就浪费掉了，<strong>DONT_PREALLOC_SLABS</strong>在1.4.7里面是默认定义的，也就是说prealloc功能是默认关闭的，于是就不考虑先prealloc了。</p>
<p><strong>slabs_init()</strong>接下来的代码就很简单了，对每个slab的chunk size进行对齐然后设置该slab class的相关成员变量的值。</p>
<div><strong>1.2 slabclass_t的结构介绍</strong></div>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> size<span style="color: #339933;">;</span>      <span style="color: #808080; font-style: italic;">/* sizes of items */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> perslab<span style="color: #339933;">;</span>   <span style="color: #808080; font-style: italic;">/* how many items per slab */</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">**</span>slots<span style="color: #339933;">;</span>           <span style="color: #808080; font-style: italic;">/* list of item ptrs */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> sl_total<span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* size of previous array */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> sl_curr<span style="color: #339933;">;</span>   <span style="color: #808080; font-style: italic;">/* first free slot */</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>end_page_ptr<span style="color: #339933;">;</span>         <span style="color: #808080; font-style: italic;">/* pointer to next free item at end of page, or 0 */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> end_page_free<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* number of items remaining at end of last alloced page */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> slabs<span style="color: #339933;">;</span>     <span style="color: #808080; font-style: italic;">/* how many slabs were allocated for this class */</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">**</span>slab_list<span style="color: #339933;">;</span>       <span style="color: #808080; font-style: italic;">/* array of slab pointers */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> list_size<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* size of prev array */</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> killing<span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* index+1 of dying slab, or zero if none */</span>
    size_t requested<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* The number of requested bytes */</span>
<span style="color: #009900;">&#125;</span> slabclass_t<span style="color: #339933;">;</span>
<span style="color: #993333;">static</span> slabclass_t slabclass<span style="color: #009900;">&#91;</span>MAX_NUMBER_OF_SLAB_CLASSES<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span></pre></div></div>

<p>size和perslab这两个字段已经说过了，slots这里存放的是空闲的slab列表，当调用<strong>do_slabs_free()</strong>这个函数之后，要释放的chunk就被放到这个数组的尾部，sl_curr数组尾部开始的第一个空闲的chunk，sl_total表示数组的总大小，当sl_curr大小等于sl_total的时候数组会通过<strong>realloc()</strong>进行扩容，容易是旧容量的2倍。</p>
<p>end_page_ptr这个字段表示该slab里面的当前空闲的chunk地址，end_page_free，表示该slab中剩余的空闲chunk的数目，其它的几个字段按注释都很容易理解了。</p>
<div><strong>1.3 创建新的slab</strong></div>
<p>前面提到的<strong>slabs_preallocate()</strong>函数只不过是对每一个已初始化的slab_class调用<strong>do_slabs_newslab()</strong>函数为其分配一块slab内存空间，看下这个函数的代码。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> do_slabs_newslab<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> id<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    slabclass_t <span style="color: #339933;">*</span>p <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>slabclass<span style="color: #009900;">&#91;</span>id<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> len <span style="color: #339933;">=</span> p<span style="color: #339933;">-&gt;</span>size <span style="color: #339933;">*</span> p<span style="color: #339933;">-&gt;</span>perslab<span style="color: #339933;">;</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>ptr<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>mem_limit <span style="color: #339933;">&amp;&amp;</span> mem_malloced <span style="color: #339933;">+</span> len <span style="color: #339933;">&gt;</span> mem_limit <span style="color: #339933;">&amp;&amp;</span> p<span style="color: #339933;">-&gt;</span>slabs <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span>
        <span style="color: #009900;">&#40;</span>grow_slab_list<span style="color: #009900;">&#40;</span>id<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">||</span>
        <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>ptr <span style="color: #339933;">=</span> memory_allocate<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>size_t<span style="color: #009900;">&#41;</span>len<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    memset<span style="color: #009900;">&#40;</span>ptr<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span>size_t<span style="color: #009900;">&#41;</span>len<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    p<span style="color: #339933;">-&gt;</span>end_page_ptr <span style="color: #339933;">=</span> ptr<span style="color: #339933;">;</span>
    p<span style="color: #339933;">-&gt;</span>end_page_free <span style="color: #339933;">=</span> p<span style="color: #339933;">-&gt;</span>perslab<span style="color: #339933;">;</span>
&nbsp;
    p<span style="color: #339933;">-&gt;</span>slab_list<span style="color: #009900;">&#91;</span>p<span style="color: #339933;">-&gt;</span>slabs<span style="color: #339933;">++</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> ptr<span style="color: #339933;">;</span>
    mem_malloced <span style="color: #339933;">+=</span> len<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>slab_class的slab_list字段保存的是已分配的slabs列表，该列表实际上是个数组，当数组中没有空闲位置时则会调用<strong>grop_slab_list()</strong>对数组进行扩容，接下来便会调用<strong>memory_allocate()</strong>给slab分配内存，这个函数会检测是否已经开始了prealloc功能，如果开启了便会在预分配的内存块上申请一块内存，当这块预分配的内存用完时并不会对其进行扩容，于是便返回分配内存失败，这也就造成了系统明明有剩余内存，Memcached却提示SERVER_ERROR out of memory，当然，如果没有开启prealloc功能，这个函数便会直接调用<strong>malloc()</strong>分配内存，接下来对各个指针进行初始化。刚分配的空闲slab，它的end_page_str指针是指向slab内存首部的，end_page_free字段代表slab内存中包含中的chunk数。</p>
<div><strong>1.4 在slab上分配内存</strong></div>
<p>在某个slab_class上分配size大小的内存的函数是<strong>do_slabs_alloc()</strong>，这个函数有两个参数，要分配的内存字节数size，和该内存应该存在于哪个slab class上的slab class id. 这两个参数在是有相关性的，在调用该函数的时候class id一般是通过size来计算得出来的，先看一下这个函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>do_slabs_alloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> size_t size<span style="color: #339933;">,</span> <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> id<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    slabclass_t <span style="color: #339933;">*</span>p<span style="color: #339933;">;</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>ret <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>id <span style="color: #339933;">&lt;</span> POWER_SMALLEST <span style="color: #339933;">||</span> id <span style="color: #339933;">&gt;</span> power_largest<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span> NULL<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    p <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>slabclass<span style="color: #009900;">&#91;</span>id<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* fail unless we have space at the end of a recently allocated page,
       we have something on our freelist, or we could allocate a new page */</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span> <span style="color: #009900;">&#40;</span>p<span style="color: #339933;">-&gt;</span>end_page_ptr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">||</span> p<span style="color: #339933;">-&gt;</span>sl_curr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">||</span>
           do_slabs_newslab<span style="color: #009900;">&#40;</span>id<span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* We don't have more memory available */</span>
        ret <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>p<span style="color: #339933;">-&gt;</span>sl_curr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* return off our freelist */</span>
        ret <span style="color: #339933;">=</span> p<span style="color: #339933;">-&gt;</span>slots<span style="color: #009900;">&#91;</span><span style="color: #339933;">--</span>p<span style="color: #339933;">-&gt;</span>sl_curr<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #808080; font-style: italic;">/* if we recently allocated a whole page, return from that */</span>
        ret <span style="color: #339933;">=</span> p<span style="color: #339933;">-&gt;</span>end_page_ptr<span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">--</span>p<span style="color: #339933;">-&gt;</span>end_page_free <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            p<span style="color: #339933;">-&gt;</span>end_page_ptr <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>caddr_t<span style="color: #009900;">&#41;</span>p<span style="color: #339933;">-&gt;</span>end_page_ptr<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> p<span style="color: #339933;">-&gt;</span>size<span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
            p<span style="color: #339933;">-&gt;</span>end_page_ptr <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>ret<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        p<span style="color: #339933;">-&gt;</span>requested <span style="color: #339933;">+=</span> size<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> ret<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>1.如果end_page_ptr等于0，并且sl_curr等于0，则表示slab中已经没有空闲内存，并且回收的chunk free list里面也没有可用内存了，于是这时候需要调用<strong>do_slabs_newslab()</strong>创建新的slab。</p>
<p>2.如果sl_curr不等于0，则表示chunk free list中还有可用的内存，直接返回一个可用的chunk即可。</p>
<p>3.如果chunk free list里面没有可用内存，而slab中还有空闲内存，则直接从slab中申请一个chunk的内存，然后将end_page_ptr后移。</p>
<div><strong>2. 对象item的管理</strong></div>
<div><strong>2.1 item对象的分配</strong></div>
<p>存入系统的每个key-value对都会被转换成一个item，这个item中保存了相关的状态标志信息，当服务器收到一个set请求时便需要在内存中创建一个item，item的内存理所当然是在上面讨论过的slab分配器上分配的。item的存储使用了LRU的方法，把item链入一个链表中，其中全局变量<em>heads[LARGEST_ID]</em>和<em>tails[LARGEST_ID]</em>这两个数组保存各个slab class所对应的item链表的表头和表尾，item创建的函数<strong>do_item_alloc()</strong>太长，就不把代码贴出来了，描述一下它的过程。</p>
<p>在这里先提一下前面提到的哈希表，哈希表是用来把item通过key散列到哈希表上的，这样就可以通过key来快速地定位item，在<strong>do_item_unlink()</strong>这个函数中，首先要把该item从哈希表中删除，然后再从list中移除，最后检测该item的refcount，如果refcount是0，则调用<strong>item_free()</strong>释放内存，<strong>item_free()</strong>再调用底层的<strong>slab_free()</strong>去释放内存，<strong>slab_free()</strong>只是<strong>do_slab_free()</strong>的线程安全版本，它在内部先加锁随后调用<strong>do_slab_free()</strong>，再之后解锁。</p>
<p>OK，接着看item的分配过程，首先会从链表的尾开始往前找，如果某节点的item设置了过期时间并且该item已过期，则回收该item，调用<strong>do_item_unlink()</strong>把它从链表中取出来，刚才说过<strong>do_item_unlink()</strong>这个函数在refcount为0的时候会释放掉这个item，所以为了防止这个item内存被释放，先将它的refcount设置为1，若向前查找50次都没有找到符合要求的item，则循环断开。</p>
<p>如果没有找到可以回收的item，然后就调用<strong>slabs_alloc()</strong>分配内存，如果内存也分配失败，就尝试着从链表尾开始向前找出一些没有人用的item(refcount=0)，把它<strong>do_item_unlink()</strong>掉，这时候因为refcount=0，所以它相关的内存也会被释放还给slab分配器，这个尝试又从尾向前尝试50次，OK，slab分配器中可能又有可用内存了，再用<strong>slabs_alloc()</strong>分配内存，如果还失败。。。好吧，这次只能从链表中删除一些正在引用但过期时间小于current_time &#8211; CURRENT_REPAIR_TIME的节点，这个尝试又从尾向前尝试50次，OK，再做最后一次尝试再去<strong>slabs_alloc()</strong>分配内存，如果这次还是失败，那就彻底放弃了，内存分配失败。。。</p>
<div><strong>3. Memcached的哈希表</strong></div>
<div><strong>3.1 Memcached用到的哈希算法</strong></div>
<p>Memcached用到的哈希算法比较复杂，算法地址在<a href="http://burtleburtle.net/bob/hash/doobs.html">http://burtleburtle.net/bob/hash/doobs.html</a>，Memcached维护了两个哈希表，primary_hashtable和old_hashtable，primary_hashtable是当前正在使用的哈希表，当表没有进行扩张时从这张表中插入或者查找，old_hashtable用于哈希表扩张的时候使用，它指向旧的哈希表，当哈希表中的item数大于表的大小的3/2时，则哈希表进行扩张，此时插入和查找等操作都是在old_hashtable中进行的。</p>
<div><strong>3.2 数据项插入哈希表</strong></div>
<p>数据项插入哈希表时用了<strong>assoc_insert()</strong>这个函数，下面看下它的代码</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">int</span> assoc_insert<span style="color: #009900;">&#40;</span>item <span style="color: #339933;">*</span>it<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">uint32_t</span> hv<span style="color: #339933;">;</span>
    <span style="color: #993333;">unsigned</span> <span style="color: #993333;">int</span> oldbucket<span style="color: #339933;">;</span>
&nbsp;
    assert<span style="color: #009900;">&#40;</span>assoc_find<span style="color: #009900;">&#40;</span>ITEM_key<span style="color: #009900;">&#40;</span>it<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> it<span style="color: #339933;">-&gt;</span>nkey<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* shouldn't have duplicately named things defined */</span>
&nbsp;
    hv <span style="color: #339933;">=</span> hash<span style="color: #009900;">&#40;</span>ITEM_key<span style="color: #009900;">&#40;</span>it<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> it<span style="color: #339933;">-&gt;</span>nkey<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>expanding <span style="color: #339933;">&amp;&amp;</span>
        <span style="color: #009900;">&#40;</span>oldbucket <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>hv <span style="color: #339933;">&amp;</span> hashmask<span style="color: #009900;">&#40;</span>hashpower <span style="color: #339933;">-</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;=</span> expand_bucket<span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        it<span style="color: #339933;">-&gt;</span>h_next <span style="color: #339933;">=</span> old_hashtable<span style="color: #009900;">&#91;</span>oldbucket<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        old_hashtable<span style="color: #009900;">&#91;</span>oldbucket<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> it<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        it<span style="color: #339933;">-&gt;</span>h_next <span style="color: #339933;">=</span> primary_hashtable<span style="color: #009900;">&#91;</span>hv <span style="color: #339933;">&amp;</span> hashmask<span style="color: #009900;">&#40;</span>hashpower<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        primary_hashtable<span style="color: #009900;">&#91;</span>hv <span style="color: #339933;">&amp;</span> hashmask<span style="color: #009900;">&#40;</span>hashpower<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> it<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    hash_items<span style="color: #339933;">++;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span> expanding <span style="color: #339933;">&amp;&amp;</span> hash_items <span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#40;</span>hashsize<span style="color: #009900;">&#40;</span>hashpower<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> <span style="color: #0000dd;">3</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">/</span> <span style="color: #0000dd;">2</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        assoc_expand<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    MEMCACHED_ASSOC_INSERT<span style="color: #009900;">&#40;</span>ITEM_key<span style="color: #009900;">&#40;</span>it<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> it<span style="color: #339933;">-&gt;</span>nkey<span style="color: #339933;">,</span> hash_items<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>如果expanding是true，哈希表正在扩张，则把item插入到old_hashtable中，否则则插入到primary_hashtable中，然后检测item数是否大于hashsize * 3 / 2，如果是，则进行扩张，哈希表的查找删除等操作也大致类似，不拿出来说了。</p>
<div><strong>3.3 哈希表的扩张</strong></div>
<p>哈希表的扩张其实是异步进行的，Memcached在初始化时在<strong>main()</strong>函数中会调用<strong>start_assoc_maintenance_thread()</strong>函数来开启一个线程对哈希表进行定期维护，线程函数通过对条件变量的wait进行睡眠，当被激活时发现expanding为true，则对哈希表进行扩张，把旧表的元素复制到新表中，然后释放旧表的内存空间，搞定后再睡去。。。触发哈希表扩张事件的函数是<strong>assoc_expand()</strong></p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> assoc_expand<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    old_hashtable <span style="color: #339933;">=</span> primary_hashtable<span style="color: #339933;">;</span>
&nbsp;
    primary_hashtable <span style="color: #339933;">=</span> calloc<span style="color: #009900;">&#40;</span>hashsize<span style="color: #009900;">&#40;</span>hashpower <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span> <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>primary_hashtable<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>settings.<span style="color: #202020;">verbose</span> <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span>
            fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Hash table expansion starting<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        hashpower<span style="color: #339933;">++;</span>
        expanding <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">true</span><span style="color: #339933;">;</span>
        expand_bucket <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        pthread_cond_signal<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>maintenance_cond<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
        primary_hashtable <span style="color: #339933;">=</span> old_hashtable<span style="color: #339933;">;</span>
        <span style="color: #808080; font-style: italic;">/* Bad news, but we can keep running. */</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>这个函数让把old_hashtable指向primary_hashtable，之后给primary_hashtable重新分配内存空间，然后把expanding标志设为true，接着激活maintenace_cond信号，maintenace线程被唤醒开始异步地把old_hashtable中的元素拷贝到primary_hashtable中来。</p>
<p>OK，这是我对Memcached内存管理机制的一个简单的探索和了解，如有谬误的地方，欢迎大家批评指正。</p>
]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/memcached-memory-mamagement.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>VIM复制粘贴的那些事</title>
		<link>http://basiccoder.com/vim-copy-paste-related.html</link>
		<comments>http://basiccoder.com/vim-copy-paste-related.html#comments</comments>
		<pubDate>Wed, 07 Sep 2011 06:06:11 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[slackware]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=910</guid>
		<description><![CDATA[说起vim的复制粘贴一直是个比较困扰我的问题，之前一直用gvim，跟系统剪贴板之间的复制粘贴都没有问题，gvim毕竟还需要再开个窗口，麻烦，而且不如vim那样快捷，但vim里面最让我头痛的是复制粘贴问题，想把vim里面的内容复制到其它的地方貌似怎么也不行，粘贴进来的话免强可以，但格式可能会很乱，折腾来折腾去，今天先是发现了个往外复制比较蹩脚的办法:
<pre lang="vim">:set mouse=v</pre>
这样鼠标就可以变成文本选择指针的样子，可以选择选择字体，然后点右键选复制，或者CTRL+SHIFT+C，但如果<strong>mouse=a</strong>这种模式下的话选择之后，右键的复制是灰色的，当然这种情况复制如果有行号的话行号也会被复制进去，所以在复制前先把行号关了，复制完再打开，这办法貌似也能凑合，但着实太不专业，而且太麻烦，远不如用gvim的<strong>"+y</strong>这种来得方便，<strong>"+y</strong>这个很多推友表示是可以的，但在我这里不可以，刚经<a href="http://twitter.com/multiple1902">@multiple1902</a>指点，有可能是vim编译的问题。

查看了一下vim版本相关信息：
<pre lang="bash">vim --version</pre>
发现<strong>clipboard</strong>和<strong>xterm_clipboard</strong>这些选项都是不可使用的功能，这样看来果然是编译问题了，slackware讲求KISS，编译的时候没有加入该选项也可以理解，于是从vim官网svn上check下来源码(话说我为啥check下来的是7.2呢，难道7.3还没有stable...)，重新编译... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>说起vim的复制粘贴一直是个比较困扰我的问题，之前一直用gvim，跟系统剪贴板之间的复制粘贴都没有问题，gvim毕竟还需要再开个窗口，麻烦，而且不如vim那样快捷，但vim里面最让我头痛的是复制粘贴问题，想把vim里面的内容复制到其它的地方貌似怎么也不行，粘贴进来的话免强可以，但格式可能会很乱，折腾来折腾去，今天先是发现了个往外复制比较蹩脚的办法:</p>

<div class="wp_syntax"><div class="code"><pre class="vim" style="font-family:monospace;"><span style="color: #000000;">:</span><span style="color: #804040;">set</span> <span style="color: #668080;">mouse</span>=v</pre></div></div>

<p>这样鼠标就可以变成文本选择指针的样子，可以选择选择字体，然后点右键选复制，或者CTRL+SHIFT+C，但如果<strong>mouse=a</strong>这种模式下的话选择之后，右键的复制是灰色的，当然这种情况复制如果有行号的话行号也会被复制进去，所以在复制前先把行号关了，复制完再打开，这办法貌似也能凑合，但着实太不专业，而且太麻烦，远不如用gvim的<strong>&#8220;+y</strong>这种来得方便，<strong>&#8220;+y</strong>这个很多推友表示是可以的，但在我这里不可以，刚经<a href="http://twitter.com/multiple1902">@multiple1902</a>指点，有可能是vim编译的问题。</p>
<p>查看了一下vim版本相关信息：</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">vim</span> <span style="color: #660033;">--version</span></pre></div></div>

<p>发现<strong>clipboard</strong>和<strong>xterm_clipboard</strong>这些选项都是不可使用的功能，这样看来果然是编译问题了，slackware讲求KISS，编译的时候没有加入该选项也可以理解，于是从vim官网svn上check下来源码(话说我为啥check下来的是7.2呢，难道7.3还没有stable&#8230;)，重新编译。</p>
<p>编译的时候必须要选上的选项一个是<strong>&#8211;enable-multibyte</strong>，如果没加这个选项的话，中文应该就会乱码了。<br />
另外关于剪贴板相关的选项，具体我也不清楚是哪一个，看了下几个相关的选项觉得最有可能是的是<strong>&#8211;enable-xim</strong>，只加了这两个选项之后编译完测试发现vim已经可以和系统剪贴板之间共享数据了，<strong>&#8220;+y</strong>可以使用了，世界一下子就清爽了很多，于是重新加完整选项编译：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">.<span style="color: #339933;">/</span>configure <span style="color: #339933;">--</span>prefix<span style="color: #339933;">=/</span>usr \
            <span style="color: #339933;">--</span>sysconfdir<span style="color: #339933;">=/</span>etc \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>tclinterp \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>pythoninterp \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>perlinterp \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>rubyinterp \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>cscope \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>multibyte \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>xim \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>gtk2<span style="color: #339933;">-</span>check \
            <span style="color: #339933;">--</span>enable<span style="color: #339933;">-</span>fontset \
            <span style="color: #339933;">--</span>with<span style="color: #339933;">-</span>x</pre></div></div>

<p>OK，编译完成之后removepkg vim删掉之前slackware安装包中提供的vim，然后用makepkg打包安装，哎呀，说起包管理方式我还是最喜欢slackware的tgz包，虽然没有debian的apt那么华丽，但简洁就是美啊。</p>
<p>vim安装完之后已经可以用了，但存在一个问题，vim退出之后终端便会乱码，到网上搜了半天各种关于ubuntu下的解法，拿到slackware上来完全不适用，命令啦路径啦什么的根本找不到，最后搜到一个非常简单的解决办法，说是这个问题是因为gnome-terminal标题名称的不标准，我矁了一眼标题写着“终端”两个字，给出的解决办法是在前后各加一个空格，让系统让为它是英文，我直接把它改成了Terminal，然后再启动vim然后退出就没有问题了。</p>
<p>总算解决了我对于vim的怨念，一直以为是vim的问题，没想到竟然是slackware打包的问题。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/vim-copy-paste-related.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Memcached的线程模型及状态机</title>
		<link>http://basiccoder.com/thread-model-and-state-machine-of-memcached.html</link>
		<comments>http://basiccoder.com/thread-model-and-state-machine-of-memcached.html#comments</comments>
		<pubDate>Mon, 05 Sep 2011 05:45:10 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[NoSql]]></category>
		<category><![CDATA[libevent]]></category>
		<category><![CDATA[Memcached]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=906</guid>
		<description><![CDATA[<a href="http://memcached.org/">Memcached</a>是一种应用较广泛的分布式内存对象缓存系统，应用之余总想了解它的实现机理，这也就是开源的好处，以至于每接触一款优秀的开源软件都有去阅读它源代码的冲动，Memcached-1.4.7的代码量还是可以接受的，只有10K行左右，我比较关心的两个方面还是它的进程（线程）管理机制和内存管理机制，这里先简单写一下我对Memcached进程管理方面的理解。

Memcached使用<a href="http://libevent.org/">libevent</a>实现事件循环，libevent在Linux环境下默认采用epoll作为IO多路复用方法，这个不重要，接下来要讨论的是Memcached的进程管理模型。

Memcached采用了很典型的Master-Worker模型，采用的是多线程而不是多进程，而线程之间没有冗余的共享数据，这样便降低了多线程进行线程同步的开销，核心的共享数据是消息队列，主线程会把收到的事件请求放入队列，随后调度程序会选择一个空闲的Worker线程来从队列中取出事件请求进行处理。

在main()函数里面，Memcached为主线程调用event_init()创建了一个libevent base对象，随后调用thread_init()来初始化线程，我们来看下这个函数的实现... ]]></description>
				<content:encoded><![CDATA[<p><a href="http://memcached.org/">Memcached</a>是一种应用较广泛的分布式内存对象缓存系统，应用之余总想了解它的实现机理，这也就是开源的好处，以至于每接触一款优秀的开源软件都有去阅读它源代码的冲动，Memcached-1.4.7的代码量还是可以接受的，只有10K行左右，我比较关心的两个方面还是它的进程（线程）管理机制和内存管理机制，这里先简单写一下我对Memcached进程管理方面的理解。</p>
<p>Memcached使用<a href="http://libevent.org/">libevent</a>实现事件循环，libevent在Linux环境下默认采用epoll作为IO多路复用方法，这个不重要，接下来要讨论的是Memcached的进程管理模型。</p>
<p>Memcached采用了很典型的Master-Worker模型，采用的是多线程而不是多进程，而线程之间没有冗余的共享数据，这样便降低了多线程进行线程同步的开销，核心的共享数据是消息队列，主线程会把收到的事件请求放入队列，随后调度程序会选择一个空闲的Worker线程来从队列中取出事件请求进行处理。</p>
<p>在main()函数里面，Memcached为主线程调用event_init()创建了一个libevent base对象，随后调用thread_init()来初始化线程，我们来看下这个函数的实现。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> thread_init<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> nthreads<span style="color: #339933;">,</span> <span style="color: #993333;">struct</span> event_base <span style="color: #339933;">*</span>main_base<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">int</span>         i<span style="color: #339933;">;</span>
&nbsp;
    pthread_mutex_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>cache_lock<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    pthread_mutex_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>stats_lock<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    pthread_mutex_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_lock<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    pthread_cond_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_cond<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    pthread_mutex_init<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>cqi_freelist_lock<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    cqi_freelist <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
&nbsp;
    threads <span style="color: #339933;">=</span> calloc<span style="color: #009900;">&#40;</span>nthreads<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>LIBEVENT_THREAD<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span> threads<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        perror<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Can't allocate thread descriptors&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    dispatcher_thread.<span style="color: #202020;">base</span> <span style="color: #339933;">=</span> main_base<span style="color: #339933;">;</span>
    dispatcher_thread.<span style="color: #202020;">thread_id</span> <span style="color: #339933;">=</span> pthread_self<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> nthreads<span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #993333;">int</span> fds<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>pipe<span style="color: #009900;">&#40;</span>fds<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            perror<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Can't create notify pipe&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        threads<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">notify_receive_fd</span> <span style="color: #339933;">=</span> fds<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        threads<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span>.<span style="color: #202020;">notify_send_fd</span> <span style="color: #339933;">=</span> fds<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
        setup_thread<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>threads<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Create threads after we've done all the libevent setup. */</span>
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> nthreads<span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        create_worker<span style="color: #009900;">&#40;</span>worker_libevent<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>threads<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Wait for all the threads to set themselves up before returning. */</span>
    pthread_mutex_lock<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>init_count <span style="color: #339933;">&lt;</span> nthreads<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        pthread_cond_wait<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_cond<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>init_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    pthread_mutex_unlock<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>init_lock<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>代码很简单，初始化几个全局锁和一个条件变量，init_lock和init_cond这一对锁/条件变量的作用很明显，它们的作用是用来等待所有的worker线程启动完毕后，thread_init()才可以继续执行，见该函数最后的几句，非常通用的用法，等待init_count数达到预定的线程数后主线程方可继续执行，否则就一直在wait，每创建一个worker线程就会让init_count的值加1，创建worker线程的工作是在setup_thread()函数中进行的，这个后面讨论。</p>
<p>thread_init()在初始化完全局锁并为线程池分配分配空间之后便开始对线程池中的每个线程进行初始化，哦，在这之前把全局的调度线程设置成为主线程，OK，接下来就开始遍历线程池，每个线程都有一对notify_fd，它们通过管道连接，这个管道便是主线程对线程池中的工作线程进行调度的接口，看一下setup_thread()这个函数的实现（节省版面，只保留了关键代码）。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> setup_thread<span style="color: #009900;">&#40;</span>LIBEVENT_THREAD <span style="color: #339933;">*</span>me<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    me<span style="color: #339933;">-&gt;</span>base <span style="color: #339933;">=</span> event_init<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span> me<span style="color: #339933;">-&gt;</span>base<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Can't allocate event base<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;">/* Listen for notifications from other threads */</span>
    event_set<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>me<span style="color: #339933;">-&gt;</span>notify_event<span style="color: #339933;">,</span> me<span style="color: #339933;">-&gt;</span>notify_receive_fd<span style="color: #339933;">,</span>
              EV_READ <span style="color: #339933;">|</span> EV_PERSIST<span style="color: #339933;">,</span> thread_libevent_process<span style="color: #339933;">,</span> me<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    event_base_set<span style="color: #009900;">&#40;</span>me<span style="color: #339933;">-&gt;</span>base<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>me<span style="color: #339933;">-&gt;</span>notify_event<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>event_add<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>me<span style="color: #339933;">-&gt;</span>notify_event<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Can't monitor libevent notify pipe<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        exit<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    me<span style="color: #339933;">-&gt;</span>new_conn_queue <span style="color: #339933;">=</span> malloc<span style="color: #009900;">&#40;</span><span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">struct</span> conn_queue<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>me<span style="color: #339933;">-&gt;</span>new_conn_queue <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        perror<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Failed to allocate memory for connection queue&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        exit<span style="color: #009900;">&#40;</span>EXIT_FAILURE<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    cq_init<span style="color: #009900;">&#40;</span>me<span style="color: #339933;">-&gt;</span>new_conn_queue<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>首先调用event_init()为该线程初始化一个事件池，我们知道libevent的事件池（event base）不是线程安全的，所以每个线程需要有自己的事件池，所有的IO事件都由这个事件池来处理，在这个函数里面，首先将notify_receive_fd添加到事件监听循环中，设置回调函数thread_libevent_process()，当主线程向该worker线程通过pipe IPC发送消息时，便会触发该worker线程执行thread_libevent_process()函数。随后setup_thread()函数创建connection队列并将其初始化。</p>
<p>接着回过头看thread_init()函数，初始化线程池结束以后，所需要做的工作便是为线程池创建对应的线程，也就是调用了create_worker()函数，这个函数只不过把pthread_create()做了一个简单的封装，至少到目前为止里面没有什么特别的代码，线程的执行函数是worker_libevent()，该函数将全局线程数加一，随后便调用event_base_loop()，完全将事件循环交给了libevent，当然到目前为止libevent的事件池中只有用来进行IPC的管道文件描述符（说到这里，当时我还在奇怪呢，代码执行这里还并没有添加socket描述符到事件池里面，那event_loop岂不一执行就结束了，后来才反应过来里面有IPC管道文件描述符了），那么这些描述符是在什么时候添加的呢？刚才已经讨论过了，是在setup_thread()函数里面，它的事件回调函数是thread_libevent_process()，OK，接下来我们看看这个函数吧。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> thread_libevent_process<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> fd<span style="color: #339933;">,</span> <span style="color: #993333;">short</span> which<span style="color: #339933;">,</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>arg<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    LIBEVENT_THREAD <span style="color: #339933;">*</span>me <span style="color: #339933;">=</span> arg<span style="color: #339933;">;</span>
    CQ_ITEM <span style="color: #339933;">*</span>item<span style="color: #339933;">;</span>
    <span style="color: #993333;">char</span> buf<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>read<span style="color: #009900;">&#40;</span>fd<span style="color: #339933;">,</span> buf<span style="color: #339933;">,</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>settings.<span style="color: #202020;">verbose</span> <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
            fprintf<span style="color: #009900;">&#40;</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Can't read from libevent pipe<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    item <span style="color: #339933;">=</span> cq_pop<span style="color: #009900;">&#40;</span>me<span style="color: #339933;">-&gt;</span>new_conn_queue<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>NULL <span style="color: #339933;">!=</span> item<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        conn <span style="color: #339933;">*</span>c <span style="color: #339933;">=</span> conn_new<span style="color: #009900;">&#40;</span>item<span style="color: #339933;">-&gt;</span>sfd<span style="color: #339933;">,</span> item<span style="color: #339933;">-&gt;</span>init_state<span style="color: #339933;">,</span> item<span style="color: #339933;">-&gt;</span>event_flags<span style="color: #339933;">,</span>
                           item<span style="color: #339933;">-&gt;</span>read_buffer_size<span style="color: #339933;">,</span> item<span style="color: #339933;">-&gt;</span>transport<span style="color: #339933;">,</span> me<span style="color: #339933;">-&gt;</span>base<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>c <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            close<span style="color: #009900;">&#40;</span>item<span style="color: #339933;">-&gt;</span>sfd<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
            c<span style="color: #339933;">-&gt;</span>thread <span style="color: #339933;">=</span> me<span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        cqi_free<span style="color: #009900;">&#40;</span>item<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>函数的一开始便从fd中读取一个字节，不在乎这一个字节是什么，只是主线程发过来的一个单字节（实际上是个\0），用来触发fd的READ事件，主线程通过往worker线程的notify_write_fd中写入一个单字节来实现对worker线程的调度，因此这个函数里面首先要先把这一个字节读取出来，否则会影响以后的事件循环。紧接着从connection队列中弹出一个item，然后根据这个item的信息再创建一个connection，这里的conn还是不要理解为连接的好，理解成任务更好一些，conn对象中有读写缓冲区，状态（用来实现Memcached的有限状态机）等，这conn_new()这个函数里面会创建一个conn对象并对它进行初始化，但最重要的操作是设置对socketfd的事件监听函数，event_handler()，这个函数又调用drive_machine()，其中就实现了Memcached的有限状态机，通过对state的不同值来进行不同的操作，具体的状态如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">const</span> <span style="color: #993333;">char</span><span style="color: #339933;">*</span> <span style="color: #993333;">const</span> statenames<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span> <span style="color: #ff0000;">&quot;conn_listening&quot;</span><span style="color: #339933;">,</span>
						      <span style="color: #ff0000;">&quot;conn_new_cmd&quot;</span><span style="color: #339933;">,</span>
						        <span style="color: #ff0000;">&quot;conn_waiting&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_read&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_parse_cmd&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_write&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_nread&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_swallow&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_closing&quot;</span><span style="color: #339933;">,</span>
							<span style="color: #ff0000;">&quot;conn_mwrite&quot;</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>关于状态机是怎么运行的就不讨论了，代码篇幅过长，涉及到的细节太多，不展开说了，要说的一点是状态机在哪里进入的conn_listening状态，这是某个socketfd的起始状态，socket需要先监听然后accept，再之后才可以进行read,write等操作，listen必然会在程序初始化的过程中调用，我们可以看到在main()函数里面线程初始化结束以后，便开始对socket描述符进行初始化，获取系统存在几个可用的地址信息，然后创建socket描述符，再然后bind()，再之后调用listen()，这些操作都是在server_socket()函数中执行的，该函数初始化监听socket，并针对服务器做一些socket options的设置，最后最关键的一步是针对该socket调用了conn_new()，但事件池是main_base，也就是说accept()事件是由主线程接收到的，accept()是发生在主线程内，这样也就避免了多线程accept()的惊群，accept()函数的执行也是在状态机循环中执行的，drive_machine()函数中，accept()完成后，主线程便调用dispatch_conn_new()对该描述符进行调度，状态机的代码太长，不列出来了，看一看dispatch_conn_machine()的代码吧。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> dispatch_conn_new<span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span> sfd<span style="color: #339933;">,</span> <span style="color: #000000; font-weight: bold;">enum</span> conn_states init_state<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> event_flags<span style="color: #339933;">,</span>
                       <span style="color: #993333;">int</span> read_buffer_size<span style="color: #339933;">,</span> <span style="color: #000000; font-weight: bold;">enum</span> network_transport transport<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    CQ_ITEM <span style="color: #339933;">*</span>item <span style="color: #339933;">=</span> cqi_new<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> tid <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>last_thread <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">%</span> settings.<span style="color: #202020;">num_threads</span><span style="color: #339933;">;</span>
&nbsp;
    LIBEVENT_THREAD <span style="color: #339933;">*</span>thread <span style="color: #339933;">=</span> threads <span style="color: #339933;">+</span> tid<span style="color: #339933;">;</span>
&nbsp;
    last_thread <span style="color: #339933;">=</span> tid<span style="color: #339933;">;</span>
&nbsp;
    item<span style="color: #339933;">-&gt;</span>sfd <span style="color: #339933;">=</span> sfd<span style="color: #339933;">;</span>
    item<span style="color: #339933;">-&gt;</span>init_state <span style="color: #339933;">=</span> init_state<span style="color: #339933;">;</span>
    item<span style="color: #339933;">-&gt;</span>event_flags <span style="color: #339933;">=</span> event_flags<span style="color: #339933;">;</span>
    item<span style="color: #339933;">-&gt;</span>read_buffer_size <span style="color: #339933;">=</span> read_buffer_size<span style="color: #339933;">;</span>
    item<span style="color: #339933;">-&gt;</span>transport <span style="color: #339933;">=</span> transport<span style="color: #339933;">;</span>
&nbsp;
    cq_push<span style="color: #009900;">&#40;</span>thread<span style="color: #339933;">-&gt;</span>new_conn_queue<span style="color: #339933;">,</span> item<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    MEMCACHED_CONN_DISPATCH<span style="color: #009900;">&#40;</span>sfd<span style="color: #339933;">,</span> thread<span style="color: #339933;">-&gt;</span>thread_id<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>write<span style="color: #009900;">&#40;</span>thread<span style="color: #339933;">-&gt;</span>notify_send_fd<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        perror<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Writing to thread notify pipe&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>创建一个item并压入队列，然后通过一个简单的哈希从线程池中找出一个线程，写一个空字符来激活这个线程（前面讨论过了），接着被激活的线程就会从conn_queue中弹出一个item来进行处理。</p>
<p>OK，关于Memcached的线程模型要说的就这些吧，我自己读源码时对它的浅显的理解，欢迎大家批评讨论。</p>
]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/thread-model-and-state-machine-of-memcached.html/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Hybrid开发手记之聊天窗口的WebKit支持</title>
		<link>http://basiccoder.com/hybrid_dev_note_webkit_support_in_chat_window.html</link>
		<comments>http://basiccoder.com/hybrid_dev_note_webkit_support_in_chat_window.html#comments</comments>
		<pubDate>Fri, 05 Aug 2011 12:18:31 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[hybrid]]></category>
		<category><![CDATA[WebKit]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=896</guid>
		<description><![CDATA[近两天给Hybrid<a href="https://github.com/levin108/hybrid" target="_blank">(https://github.com/levin108/hybrid</a>)的聊天窗口加上了WebKit支持，之前没有实际用过WebKit，而且Web前台开发功力也不强，草草做了一个界面，但相比用GtkTextView来实现看上去还是要舒服好多，先上个图吧：
<a href="http://basiccoder.com/wp-content/uploads/2011/08/chat_with_robot_webkit.png" target="_blank"><img src="http://basiccoder.com/wp-content/uploads/2011/08/chat_with_robot_webkit-278x300.png" alt="" title="chat_with_robot_webkit" width="278" height="300" class="aligncenter size-medium wp-image-897" /></a>

本篇没有什么高深的东西，作为一个简单的开发文档。... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>近两天给Hybrid<a href="https://github.com/levin108/hybrid" target="_blank">(https://github.com/levin108/hybrid</a>)的聊天窗口加上了WebKit支持，之前没有实际用过WebKit，而且Web前台开发功力也不强，草草做了一个界面，但相比用GtkTextView来实现看上去还是要舒服好多，先上个图吧：<br />
<a href="http://basiccoder.com/wp-content/uploads/2011/08/chat_with_robot_webkit.png" target="_blank"><img src="http://basiccoder.com/wp-content/uploads/2011/08/chat_with_robot_webkit-278x300.png" alt="" title="chat_with_robot_webkit" width="278" height="300" class="aligncenter size-medium wp-image-897" /></a></p>
<p>本篇没有什么高深的东西，作为一个简单的开发文档。</p>
<p><strong>一，主题组件化的方法</strong></p>
<p>聊天窗口的显示区域已经组件化，并没有进行深层次的模块化，代码还是在一起编译的，只是逻辑上组件化了。</p>
<p>之前是固定的由GtkTextView实现，在加入GtkWebKit的时候同时也保留了GtkTextView的实现，这两者是可选的，不管是GtkWebKit还是GtkTextView都需要实现四个最基本的函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> GtkWidget<span style="color: #339933;">*</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>text_create<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">typedef</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>text_append<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>GtkWidget <span style="color: #339933;">*,</span> HybridAccount <span style="color: #339933;">*,</span>
							HybridBuddy <span style="color: #339933;">*,</span>	<span style="color: #993333;">const</span> gchar <span style="color: #339933;">*,</span> time_t<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">typedef</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>text_notify<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>GtkWidget <span style="color: #339933;">*,</span> <span style="color: #993333;">const</span> gchar <span style="color: #339933;">*,</span> gint<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #993333;">typedef</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>theme_set_ops_func<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>前三个函数是组件的操作函数，功能分别是创建聊天区域，向聊天区域中添加消息，向聊天区域中添加提示消息，最后一个是设置操作集合的钩子函数。</p>
<p>对于这两种不同的实现分别定义了两个文件，chat-textview.c和chat-webkit.c，这两个文件里面分别是两者各自的实现，而它们对外的接口只是一个GtkWidget，这得利于GOBJECT的这种类似多态的特性。</p>
<p>对于不同的实现会定义操作集变量：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> HybridChatTextOps webkit_ops <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
	hybrid_chat_webkit_create<span style="color: #339933;">,</span>
	hybrid_chat_webkit_append<span style="color: #339933;">,</span>
	hybrid_chat_webkit_notify
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>聊天窗口当前使用的聊天区域实现方式全由该操作集来确定，而使用哪个操作集可以由两种方式各自的theme_set_ops_func函数来设置。</p>
<p>我们可以把两种组件看成两个不同的主题，在聊天窗口文件中定义了该主题的列表：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> _HybridChatTheme <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">const</span> gchar <span style="color: #339933;">*</span>name<span style="color: #339933;">;</span>
	theme_set_ops_func func<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">static</span> HybridChatTheme theme_list<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
<span style="color: #339933;">#ifdef USE_WEBKIT</span>
	<span style="color: #009900;">&#123;</span>
		<span style="color: #ff0000;">&quot;webkit&quot;</span><span style="color: #339933;">,</span>
		hybrid_chat_set_webkit_ops
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> 
<span style="color: #339933;">#endif</span>
	<span style="color: #009900;">&#123;</span>
		<span style="color: #ff0000;">&quot;textview&quot;</span><span style="color: #339933;">,</span>
		hybrid_chat_set_textview_ops
	<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span>
		NULL<span style="color: #339933;">,</span> NULL
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>运行时程序会根据用户的当前配置情况来选择使用哪种主题。</p>
<p><strong>二，WebKit遇到的问题</strong></p>
<p>关于WebKit有几点小问题，第一次用难免会碰到些小问题，不过幸好还是解决掉了。</p>
<p><strong>1. undefined @1: ReferenceError: Can&#8217;t find variable</strong></p>
<p>WebKit外部来操作DOM模型主要是通过从外部调用webkit_web_view_execute_script()来实现的，当然我看最新的GtkWebKit API里面已经支持直接操作DOM了，但貌似手头的系统上安装的版本都还没有这个API函数，为了兼容性的考虑还是采用了传统的方法。在HTML模块中用Javascript定义了函数appendMessage(html)，通过这个函数向WebKit中定义聊天信息，但当收到消息自动弹出的时候会提示undefined @1: ReferenceError: Can&#8217;t find variable appendMessage()，这种情况的原因很简单，函数是定义了但找不到，原因只能是因为模板字符串还没有加载完成便调用了appendMessage()函数，因此会出现这样的错误，因此在对WebKit进行脚本操作之前首先要等它初始化完成，也就是等它load_finished之后，WebKit提供了load_finished事件，但这个事件目前已经Deprecated了，替代的方法是使用load_status属性，属性和事件的使用方法明显不一样，关于WebKit deprecate load_finished事件的原因我没去仔细想，load_status属性的方法只能是轮询，在收到消息时检测load_status，或未加载完成，则调用g_timeout_add()将操作延时后执行，执行中再检测load_status，若仍未完成则继续延时，直到加载完成为止，实际上在实现的时候我给g_timeout_add的第一个参数写了0，事实证明在进入加调函数时需要的那个简单的HTML模板就已经加载完成了，这时候再去调用webkit_web_view_execute_script()去添加一条新消息。</p>
<p><strong>2. Message: console message: undefined @1: SyntaxError: Parse error</strong></p>
<p>同样是在调用webkit_web_view_execute_script()遇到了上面的错误，通过对比和分析发现我传入的字符串参数中夹带了\n字符，把这个字符换成空格后便没有这个问题了，但换行在HTML中应该是<br/>，于是需要把\n替换成<br/>，C语言以及glibc都没有提供形如replace()这种方便的函数，于是我用GString，把字符一个一个的贴过去，遇到\n就贴一个<br/>在后面，效率是低点，但实现起来比较方便，代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> gchar<span style="color: #339933;">*</span>
escape_string<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> gchar <span style="color: #339933;">*</span>str<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	GString <span style="color: #339933;">*</span>res<span style="color: #339933;">;</span>
&nbsp;
	res <span style="color: #339933;">=</span> g_string_sized_new<span style="color: #009900;">&#40;</span>strlen<span style="color: #009900;">&#40;</span>str<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>str <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">*</span>str<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">switch</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>str<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">case</span> <span style="color: #0000dd;">13</span><span style="color: #339933;">:</span>
				res <span style="color: #339933;">=</span> g_string_append<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;&lt;br/&gt;&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">case</span> <span style="color: #ff0000;">'<span style="color: #000099; font-weight: bold;">\&quot;</span>'</span><span style="color: #339933;">:</span>
				res <span style="color: #339933;">=</span> g_string_append<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\\</span><span style="color: #000099; font-weight: bold;">\&quot;</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">case</span> <span style="color: #ff0000;">'<span style="color: #000099; font-weight: bold;">\t</span>'</span><span style="color: #339933;">:</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">default</span><span style="color: #339933;">:</span>
				res <span style="color: #339933;">=</span> g_string_append_c<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>str<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		str <span style="color: #339933;">++;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> g_string_free<span style="color: #009900;">&#40;</span>res<span style="color: #339933;">,</span> FALSE<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>以上大致完成了文章开头图片的样式，已经可以正常使用，但字体在我这slackware系统上还有些不太完美，也可能是我系统字体设置有问题，这个等后期再处理。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/hybrid_dev_note_webkit_support_in_chat_window.html/feed</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>新开源项目Hybrid开发手记</title>
		<link>http://basiccoder.com/new-opensource-project-hybrid-dev-note.html</link>
		<comments>http://basiccoder.com/new-opensource-project-hybrid-dev-note.html#comments</comments>
		<pubDate>Sat, 30 Jul 2011 07:21:30 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[GTK]]></category>
		<category><![CDATA[hybrid]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=891</guid>
		<description><![CDATA[博客有两个月没更新了吧，先说说这段时间都在做些什么吧，六月初的时候实验室项目验收完，以为可以轻松下来了，于是开了一个新的开源项目在做，这个稍后再说，没过多久就被拉去给新项目做设计文档去了，这活做起来比硬编码要麻烦得多，于是大多数时间都在为这个事情心烦，稍微有点闲下来的时间就去写点代码，接下来说下最近在做的这个开源项目。

之前的Openfetion虽然也受到了一些开源社区朋友的好评，但软件质量怎么想我心里比谁都明白，在twitter上我也公开承认过Openfetion的代码质量以及软件架构都非常差，说到代码质量，最初在开发Openfetion的时候没有想过会把它作为一个通用的软件拿出来给大家用，而是自己纯粹在写着玩，于是写得很随意，当然这也得怪我这种恶劣的编程习惯，专业的coder即便是写一个测试用的小代码也会严格按规范来，这是一种习惯，我承认之前做得不够好。再说到软件架构，对于IM软件我之前一样没有什么经验，在没有经过调研的情况就盲目开始编码，完全没有参考现有的开源软件架构，甚至没有个合适的事件循环，所以Openfetion目前的状态是勉强能用。

之前考虑过重构Openfetion，但其实重构的成本要远远高于重写，于是我决定<strong>终止Openfetion项目</strong>，不再为Openfetion增加/修改代码，也不鼓励其它人对它进行修改... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>博客有两个月没更新了吧，先说说这段时间都在做些什么吧，六月初的时候实验室项目验收完，以为可以轻松下来了，于是开了一个新的开源项目在做，这个稍后再说，没过多久就被拉去给实验室的新项目做设计文档去了，这活做起来比硬编码要麻烦得多，于是大多数时间都在为这个事情心烦，稍微有点闲下来的时间就去写点代码，接下来说下最近在做的这个开源项目。</p>
<p>之前的Openfetion虽然也受到了一些开源社区朋友的好评，但软件质量怎么样我心里比谁都明白，在twitter上我也公开承认过Openfetion的代码质量以及软件架构都非常差，说到代码质量，最初在开发Openfetion的时候没有想过会把它作为一个通用的软件拿出来给大家用，而是自己纯粹在写着玩，于是写得很随意，当然这也得怪我这种恶劣的编程习惯，专业的coder即便是写一个测试用的小代码也会严格按规范来，这是一种习惯，我承认之前做得不够好。再说到软件架构，对于IM软件我之前一样没有什么经验，在没有经过调研的情况就盲目开始编码，完全没有参考现有的开源软件架构，甚至没有个合适的事件循环，所以Openfetion目前的状态是勉强能用。</p>
<p>之前考虑过重构Openfetion，但其实重构的成本要远远高于重写，于是我决定<strong>终止Openfetion项目</strong>，不再为Openfetion增加/修改代码，也不鼓励其它人对它进行修改。</p>
<p>作为Openfetion的替代品，我发起了新的开源项目Hybrid（<a href="https://github.com/levin108/hybrid" target="_blank">https://github.com/levin108/hybrid</a>），这同样是一款IM软件，我把它定位为类似于Pidgin/Empathy的IM框架，但即没有使用libpurple也没有使用telepathy，这两个库使用起来都还是有很多限制，我喜欢自由自在的写代码，我不太想考虑太多关于Hybrid这个软件能走多远，能有多少人用，我需要一款这样的软件，我想让它支持飞信和Gtalk，这是我最需要的两种通信协议，这就够了。</p>
<p>Hybrid同样也使用了GTK作为UI库，纯C语言开发，用了OpenSSL处理加密逻辑，使用glib的GIOChannel来实现了异步IO事件循环，同时把libxml2的API做了封装，xml的处理代码看起来简洁多了（libxml2的API命名规则看起来实在太难受了，而且还需要经常对它的自定义数据类型进行转换）。软件的整体架构上参考了一个pidgin，之前的Openfetion用多线程来处理并发逻辑，线程之间的同步开销很大，现在经常还会出现的崩溃问题大多来源于线程同步没有做好。现在采用的方法是采用单线程，将处理逻辑都交给GUI线程来进行处理，当然所有的IO都必须是非阻塞IO，通过异步IO的方法来检测IO事件，当前所有的操作都必须是非阻塞的，包括SSL/TLS握手等，但目前还存在的一个问题是DNS解析过程仍然是阻塞的，我还没有去研究如何实现非阻塞DNS解析，这个会加入TODO List中。至于模块化也是采用了glib中的gmodule来实现的，没有直接使用dlopen的方法，这样应该会提高一点可移植性吧。</p>
<p>关于协议模块，飞信的协议模块在现有的pidgin插件的基础上重写了，使用了封装过的xml接口，同样也使用了一致的编码风格，但在之前的基础上写代码效率就高了很多，很快就完成了。另外关于Gtalk的协议模块也没有花太多时间，用了一个周末的时间就把基础框架弄好了，因为用的是XMPP，开放的协议实现起来非常简单，只需要读一遍协议然后照着开发就好了，一路写下来都很顺，没有遇到什么大的麻烦。</p>
<p>总之到目前为止，这款软件已经基本能满足我的需要了，但还是有很多需要完善的地方，如好友的搜索，webkit的聊天界面，以及自定义请求对话框等，这些等以后慢慢再更新，现在面临毕业找工作，时间相对也较少，有空的时候就更新一点。</p>
<p>还没有想好什么时候要发布一个正式版，有一点点追求完美，总想把它做得更好一点，有感兴趣的同学可以去把代码clone下来玩玩，有用过的同学欢迎反鐀意见。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/new-opensource-project-hybrid-dev-note.html/feed</wfw:commentRss>
		<slash:comments>34</slash:comments>
		</item>
		<item>
		<title>武汉Deepin Linux DAU会议见闻</title>
		<link>http://basiccoder.com/wuhan-deepin-linux-meeting-info.html</link>
		<comments>http://basiccoder.com/wuhan-deepin-linux-meeting-info.html#comments</comments>
		<pubDate>Wed, 01 Jun 2011 08:23:10 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=880</guid>
		<description><![CDATA[上次在ubuntu11.04 release party北京站遇到deepin linux负责人刘文欢，他也就是deepin本人，邀请我去参加上个周末在武汉举行的deepin linux DAU大会，当时没有马上答应他，因为怕实验室太忙时间安排不开，后来跟领导商量了下领导表示支持，于是就趁周末跑了过去，一方面是希望能见到开源社区的朋友，另一方面也是很想看看deepin这个出自国人之手的linux发行版会做到什么水准。

会议到场人数很多，我作为特邀嘉宾很惭愧地坐在了前排，会议第一部分是deepin主题演讲，deepin操作系统创始人hiweed介绍深度的发展以及现状，接着是深度软件中心的主要开发者王勇介绍软件中心的相关情况，我个人感觉软件中心还是一个很不错的软件，虽然看上去很容易让人联想到360的软件中心，但不能不说这确实是一个很符合用户使用习惯的软件，从软件的搜索和安装都比ubuntu软件中心要舒服一些，当然这也是我个人的感受，据deepin介绍，软件中心的开发者王勇只有22岁，而且并没有读过大学，当时我就对他特别敬佩，像我们这些读了这么多年书现在还是这样技术平平的人真应该感到惭愧。最后由deepin本人介绍开源社区的管理与运营，让大家对深度这个社区有了一个比较深入的认识。

会议的第二部分是主题嘉宾演讲，第一位是国内开源界的名人袁萌教授，讲永中Office的事情，袁萌可以说是相当有争议的人物了，之前在ubuntu 10.10和11.04的release party现场都见过袁老出现，而且对于这种开源活动袁老似乎每次都不会缺席，这种活动每次出现的为数不多的老年人身影也就只有袁老了，但不管是他在新浪博客上还是在其它地方，大家对他的评论都是褒贬不一，我之前对袁老不熟悉，后来在twitter上打听袁老的事情，有人表示袁老人不错，但口头老挂着永中和U盘的事情就让人有点烦了，确实，袁老讲话我听不太明白（口音原因），但言谈中总会提到永中和U盘的事情，我对此到没什么特别的看法，可能年纪大了人会比较偏执，遇到自己觉得好的东西就会一直推崇。再看他的新浪博客，有人支持也有人很负面地骂，确实让人感觉到袁老真是中国开源界最有争议的人物，其实我觉得像袁老这样一位古希老... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>上次在ubuntu11.04 release party北京站遇到deepin linux负责人刘文欢，他也就是deepin本人，邀请我去参加上个周末在武汉举行的deepin linux DAU大会，当时没有马上答应他，因为怕实验室太忙时间安排不开，后来跟领导商量了下领导表示支持，于是就趁周末跑了过去，一方面是希望能见到开源社区的朋友，另一方面也是很想看看deepin这个出自国人之手的linux发行版会做到什么水准。</p>
<p>会议到场人数很多，我作为特邀嘉宾很惭愧地坐在了前排，会议第一部分是deepin主题演讲，deepin操作系统创始人hiweed介绍深度的发展以及现状，接着是深度软件中心的主要开发者王勇介绍软件中心的相关情况，我个人感觉软件中心还是一个很不错的软件，虽然看上去很容易让人联想到360的软件中心，但不能不说这确实是一个很符合用户使用习惯的软件，从软件的搜索和安装都比ubuntu软件中心要舒服一些，当然这也是我个人的感受，据deepin介绍，软件中心的开发者王勇只有22岁，而且并没有读过大学，当时我就对他特别敬佩，像我们这些读了这么多年书现在还是这样技术平平的人真应该感到惭愧。最后由deepin本人介绍开源社区的管理与运营，让大家对深度这个社区有了一个比较深入的认识。</p>
<p>会议的第二部分是主题嘉宾演讲，第一位是国内开源界的名人袁萌教授，讲永中Office的事情，袁萌可以说是相当有争议的人物了，之前在ubuntu 10.10和11.04的release party现场都见过袁老出现，而且对于这种开源活动袁老似乎每次都不会缺席，这种活动每次出现的为数不多的老年人身影也就只有袁老了，但不管是他在新浪博客上还是在其它地方，大家对他的评论都是褒贬不一，我之前对袁老不熟悉，后来在twitter上打听袁老的事情，有人表示袁老人不错，但口头老挂着永中和U盘的事情就让人有点烦了，确实，袁老讲话我听不太明白（口音原因），但言谈中总会提到永中和U盘的事情，我对此到没什么特别的看法，可能年纪大了人会比较偏执，遇到自己觉得好的东西就会一直推崇。再看他的新浪博客，有人支持也有人很负面地骂，确实让人感觉到袁老真是中国开源界最有争议的人物，其实我觉得像袁老这样一位古希老人，不图名利，仍然能对中国的开源事业如此热心，已经实属不易了，即便大家对他的观点不认同，也不至于用那么偏激的语言去骂他，除非你让我去相信这样一位风烛残年的退休老教授还在用某种低劣的手段去谋取私利，与袁老见面听他言谈，感觉他人非常正直，绝不是什么小人，所以我觉得大家可以不喜欢他，但真的没有必要去骂他。</p>
<p>会议袁老讲完后我也给大家就我的一些经历做了个小报告，简单介绍了一个飞信的开发过程以及我个人的Linux使用经验等，接下来是中科大的张成同学讲在校园同推广Linux，张成是mirrors.ustc.edu.cn的现任维护者，中科大开源协会的副会长，感觉中科大的开源协会做得蛮不错的，大家都很热心地在做一些很有意义的事情，张成技术也非常不错，再一次让我表示汗颜。再之后便是在场的用户提问，具体都问了哪些问题我也记不清了，我也没有刻意地去做会议记录。</p>
<p>会议结束后第二天晚上我和deepin在他们公司单独聊了两个多小时，deepin确实是想在Linux发行版这一块做些事情出来的，在中国做这样的事情难免会遇到种种阻力，包括舆论上的政策上的等等，红旗这样一个烂到根的公司似乎让国人对国产Linux发行版很是绝望，雨林木枫反对的人也很多，我到不太了解原因是什么，我个人不太喜欢它是因为它之前山寨了Windows的UI来适应中国用户的使用习惯，其实说起来如果没有什么版权协议的问题的话也没什么不好。做操作系统是个很漫长的过程，不可能在一两年内就得到用户的普遍认可，即便是ubuntu也是经历了一个长期的过程，Canonical公司也出资做了那么久的公益，说实话，哪家做Linux的公司纯粹做公益我们都没法相信，包括Canonical也是想用ubuntu做出一个品牌再去拓展其它的业务，如ubuntu server. 当然，深度的经济实力远不如Canonical，但做发行版在最开始是很难有经济回报的，我们所能期望的就是深度能坚持下去，毕竟深度和红旗是两家完全不同性质的公司，应该不会像红旗那样腐化，而做这样一个发行版也一直是hiweed和deepin的梦想，其实我个人还是对他们挺有信心的，即便中国人做不出来自己自主研发的操作系统，能出一个被国际认同的Linux发行版也确实是一件好事。</p>
<p>我是站在一个中立的立场，我支持我认为好的东西，如果有一天深度变成了红旗那我也会跟着去骂的。当时deepin在北京给我演示深度系统的时候我真地觉得很不错，起码它降低了新手的技术门坎，通过虚拟化的手段让OFFICE和QQ都跑在了上面，可以说这两个软件对很多新用户来说都是很大的壁垒，这个问题解决了就解决了很多新手的烦恼，我个人觉得任何一家第三方的OFFICE都很难与MS OFFICE兼容，即便是永中它的公式编辑器也完全跟不上。他们没有集成这两款软件，也就不涉及到版权协议之类的问题了。</p>
<p>总之这一行还是受益匪浅的，希望几年之后deepin能成为让国人为之骄傲的发行版，当然也希望它千万不要成为第二个红旗。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/wuhan-deepin-linux-meeting-info.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Apache MPM Prefork设计方法浅析</title>
		<link>http://basiccoder.com/apache-mpm-prefork-design-intro.html</link>
		<comments>http://basiccoder.com/apache-mpm-prefork-design-intro.html#comments</comments>
		<pubDate>Mon, 16 May 2011 15:33:34 +0000</pubDate>
		<dc:creator>levin</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Apache]]></category>

		<guid isPermaLink="false">http://basiccoder.com/?p=876</guid>
		<description><![CDATA[
最近几天翻阅了apache的<a href="http://httpd.apache.org/docs/2.0/mpm.html" target="_blank">MPM(Multi-Processing Module)</a>机制相关的代码，虽然还有很多细节没有搞明白，但对apache的服务器模型有了一个大体的概念，对于不同的操作系统，apache提供了不同的默认MPM模型，下表是不同操作系统默认的MPM模型：
<table> 
 
<tr><td>BeOS</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/beos.html">beos</a></code></td></tr> 
<tr><td>Netware</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpm_netware.html">mpm_netware</a></code></td></tr> 
<tr><td>OS/2</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpmt_os2.html">mpmt_os2</a></code></td></tr> 
<tr><td>Unix</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/prefork.html">prefork</a></code></td></tr> 
<tr><td>Windows</td><td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpm_winnt.html">mpm_winnt</a></code></td></tr> 
</table> 

Unix平台则对应着prefork模型，prefork从名字上看意思是预先生成子进程，所以这种模型大致上是怎么工作的我们心里差不多有些认识了，prefork是一种很重要的服务器程序设计模型，对应的还有prethread，prefork一般应用在Unix平台上，因为在服务器启动时需要预告fork出一些空闲的子进程，由它们共同监听客户端的请求，这样来实现快速高并发的特性，这种机制之所以不适合Windows等平台，是因为在Windows等平台上进程的代价太高。

apache的进程管理中有一个叫做scoreboard(记分牌)的概念，主进程在进入MPM循环以前会先在进程池中创建一个scoreboard对象，该对象定义如下... ]]></description>
				<content:encoded><![CDATA[<p class='fp' style='text-indent:0em;'>最近几天翻阅了apache的<a href="http://httpd.apache.org/docs/2.0/mpm.html" target="_blank">MPM(Multi-Processing Module)</a>机制相关的代码，虽然还有很多细节没有搞明白，但对apache的服务器模型有了一个大体的概念，对于不同的操作系统，apache提供了不同的默认MPM模型，下表是不同操作系统默认的MPM模型：</p>
<table>
<tr>
<td>BeOS</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/beos.html">beos</a></code></td>
</tr>
<tr>
<td>Netware</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpm_netware.html">mpm_netware</a></code></td>
</tr>
<tr>
<td>OS/2</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpmt_os2.html">mpmt_os2</a></code></td>
</tr>
<tr>
<td>Unix</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/prefork.html">prefork</a></code></td>
</tr>
<tr>
<td>Windows</td>
<td><code class="module"><a href="http://httpd.apache.org/docs/2.0/mod/mpm_winnt.html">mpm_winnt</a></code></td>
</tr>
</table>
<p>Unix平台则对应着prefork模型，prefork从名字上看意思是预先生成子进程，所以这种模型大致上是怎么工作的我们心里差不多有些认识了，prefork是一种很重要的服务器程序设计模型，对应的还有prethread，prefork一般应用在Unix平台上，因为在服务器启动时需要预告fork出一些空闲的子进程，由它们共同监听客户端的请求，这样来实现快速高并发的特性，这种机制之所以不适合Windows等平台，是因为在Windows等平台上进程的代价太高。</p>
<p>apache的进程管理中有一个叫做scoreboard(记分牌)的概念，主进程在进入MPM循环以前会先在进程池中创建一个scoreboard对象，该对象定义如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>
    global_score <span style="color: #339933;">*</span>global<span style="color: #339933;">;</span>
    process_score <span style="color: #339933;">*</span>parent<span style="color: #339933;">;</span>
    worker_score <span style="color: #339933;">**</span>servers<span style="color: #339933;">;</span>
    lb_score     <span style="color: #339933;">*</span>balancers<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> scoreboard<span style="color: #339933;">;</span></pre></div></div>

<p>global_score保存主进程的状态，process_score则是一个数组插槽，每个插槽保存一个子进程的状态，worker_score则是一个二维数组插槽，用来保存每个子进程创建的线程状态，根据这个结构主进程可以对子进程以及相关的线程进行管理，apache按照最大化的原则来分配内存，比如会按配置中允许最多的进程数目来为parent分配内存空间。</p>
<p>MPM初始化还有一个很重要的方面是创建一个进程锁，fork()出来的子进程与父进程并不共享内存空间，多进程之于多线程的优势在于多进程可以省去多线程进行线程同步的开销，而这里创建的进程锁，主要作用是为了给accept()加锁，为了避免<a href="http://www.citi.umich.edu/projects/linux-scalability/reports/accept.html" target="_blank">thundering herd</a>问题。apache实现了五种类型的进程锁，使用flock()或fcntl()实现的文件锁，Posix信号量或System V信号量，以及使用pthread线程库实现的互斥锁。我理解的是文件锁的效率会低于其它类型的锁，因为文件锁要涉及到文件系统的IO操作。我只阅读了跟pthread相关的代码，和一般的多进程程序实现方式一致，因为子进程与父进程以及子进程之间不共享内存空间的，所以不可能像多线程程序一样将互斥锁定义为全局变量 ，因此使用共享内存机制，将互斥锁变量存放到共享内存里面，并设置共享属性。然后便可以使用该互斥锁对子进程中的accept()过程进行加锁。</p>
<p>初始化最后需要开始创建预定个数的子进程，调用startup_children()函数创建指定个数的子进程，该函数会检查scoreboard的空闲插槽，在空闲插槽上调用make_child()函数来在该插槽位置处创建一个子进程，该函数设置scoreboard中进程的状态，并fork()一个子进程，将子进程的pid写入到scoreboard对应的插槽处，子进程创建之后设置SIGHUP和SIGTERM信号，这两个信号对应的回调函数均为clean_child_exit()函数，该函数销毁内存池然后退出子进程。</p>
<p>make_child()函数执行成功后进行child_main()函数，即子进程的主循环。该函数看起来比较复杂，其实做的事情也很简单，首先是创建相关的内存池，对进程锁进行初始化（对于pthread进程锁对应是一个空函数，即无需进行初始化），将socket描述符加入到pollset中，这里的pollset也是apache抽象出来的概念，它的实现可以是kqueue/port/epoll/poll/select，具体采用哪种方式也是配置可选的。这里是我不太明白的地方，经常看到评论说nginx效率高于apache，当问起nginx效率高于apache的主要原因时，得到的答案很多都是nginx采用kqueue和epoll实现了高并发，其实感觉这个理由并不充分，我们可以看到apache同样也实现了kqueue和epoll的多路复用，如果这因为这个的话那apache没有理由会比nginx效率低多少的，另外也看到有说apache的进程管理机制占用内存过高，而且时常需要进行进程切换从而占用了CPU时间，这个说法可以接受，现在非常想去读下nginx的源码，想看看它到底是采用了什么样的机制带来了它如此之多的好评，下一步就可始阅读下nginx的源码，对比着apache，探索一些高并发服务器设计的最优方法。</p>
<p>子进程进入主循环之后会调用accept()方法，这个方法是需要进行加锁的，之后创建一个新的连接对象，并调用HOOK函数对连接进行处理，HOOK机制是apache模块化很重要的一种机制，在主程序中调用HOOK函数，具体的实现由具体的模块来定义。</p>
<p>父进程在创建完子进程之后也进行主循环，监控活动子进程的数目，并通过一定的调度使用子进程数目维护一个平衡，父进程使用waitpid()函数来检测子进程的退出情况，如果有进程退出，则创建一个新的进程来替代已结束的进程从而维持总数的一个平衡。</p>
<p>当然apache还有平稳启动机制，关于平衡启动的代码我暂时略过了，没有细读，以后有时间再回过头来仔细研究。</p>
</p>]]></content:encoded>
			<wfw:commentRss>http://basiccoder.com/apache-mpm-prefork-design-intro.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
