<?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>StephenChan&#039;s Tech Space</title>
	<atom:link href="http://blog.endlesscode.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.endlesscode.com</link>
	<description>潜心修炼</description>
	<lastBuildDate>Tue, 10 Aug 2010 17:19:30 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>读过《观止》</title>
		<link>http://blog.endlesscode.com/2010/08/11/reading-showstopper/</link>
		<comments>http://blog.endlesscode.com/2010/08/11/reading-showstopper/#comments</comments>
		<pubDate>Tue, 10 Aug 2010 17:19:30 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[Reading]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=1047</guid>
		<description><![CDATA[今晚总算把《观止》看完了，看得比较快，基本上是当叙事小说一样看。这个书名看起来像文艺书，但实际上是讲述Windows NT这个操作系统的创造史。为什么Windows NT这么出色，因为在当时它的诞生改变了人们对操作系统的看法，它引入了现在我们看起来很理所当然但当时还没有的技术：抢占式的多任务处理、可安装在通用的硬件平台、支持32位内存寻址、支持大容量文件并具有容错功能的NTFS（NT File System）等等。当然这些系统功能并没有在书上描述，这本书从头到尾都是在讲述整个NT团队是如此在艰苦的进度压力和技术压力之下花了将近5年时间创造出NT。
这个NT团队的领导人就是Dave Cutler，性格独断脾气火爆，技术牛B，现在来说就是一个传奇的程序员，当他领导NT团队的时候已经是个快50岁的人了。有个小插曲或者会更令人记住他是谁，就是当他招聘小秘的时候会问一个问题“How do u think about the word 'fuck'?"，只有那个回答了"Its my favorite word”的MM才拿到了offer。
从1988年10月开始到正式发布的1993年7月23日，历时接近5年时间，团队从开始的20多人到最后的200多人，在这5年时间里面NT团队面对的是各种功能需求的增加、遥遥无期的进度、不断冒出的bug，还有其他非技术问题，如家庭和爱人，可以说是可歌可泣，NT团队的每个成员为Windows NT奉献太多了。
虽然看得比较快，但是一些体会还是想记录一下，以后有时间再翻出来看或者也会有不同的体会。
从本来只有一个内核开发的团队，到后面慢慢增加了图形开发小组和网络开发小组，整个团队大部分人都是物理和数学出身，毕竟从理科出身的容易投入到计算机行业，加上当时计算机行业还远没现在这么热门。由于当时NT的目标定得比较高，因此很多迎合市场的需求都被添加到功能列表中，并要求兼容以前的DOS和Windows程序，不停地添加功能需求以及保证兼容性基本上就直接导致了进度问题，由于领导的Dave Cutler是个基本上为了NT放弃了家庭的人，因此给NT的团队带来的直接影响就是在进度压力之下的疯狂加班，纵然如此，但是在微软的给予的高股票期权和Cutler的压迫之下，团队的成员也是能积极高效地工作，其实这里，我还觉得有个原因是在这种环境之下驱动大家去工作的，就是大家都希望能够创造出一个出色的产品，虽然工作辛苦，但是这种渴望创造的欲望也是驱动团队成员努力工作的一个原因。
程序员、测试员和构建员是组成NT团队的主要3个角色，在那个年代，测试员和构建员的地位并不高，但是慢慢程序不断地被挑出bug并不断地被完善，越显得测试员和构建员的重要性。相比于目前自己所做的项目，哪怕是相差20年，目前我们项目的流程还是比不上那时的NT团队。每个程序员的commit都是基于不会导致构建失败的前提下，当然构建的时机可能是即时也可能是固定某个时候，但是每个程序员的commit都是使构建能正常进行的，整个流程都是井然有序地进行。而且，更让我欣赏NT团队的是，在项目的后期，Dave Cutler甚至是长期定居在构建实验室，来专门看每个程序员提交的各种代码，来保证程序代码的质量，那么多不知道他怎么看得过来。对于程序员犯的错误，Dave Cutler可是不留情面地批评，或者正是这种领导的压迫之下反而提高了团队成员的代码严谨，或者可以说是领导威严的作用。
团队规模变大，可能就增加了成员之间对技术问题的冲突，但是正如书上说，这种冲突有时候会激发对程序代码的改进，其实倒觉得这种冲突是来源于对自己代码逻辑的自信，或者说是个性鲜明，和提倡中庸之道的国内还是有点区别。
到了项目后期，真的可以算得上夺命狂奔，因为有了deadline，所以对bug的分类控制、暂停新功能需求的加入、不停的压力测试、客户的bug反馈和beta版的多次发布等等，各个成员都日以继夜地修正各种showstopper和一等级的bug。
最后Windows NT发布了，微软就站在了操作系统的顶峰了。而从始自终，Gates都没有对NT团队有什么干预，只是定期了解开发进度和进行产品的测试，给了NT团队很大的自由度。
《观止》这本书虽然只是描述了整个NT的创造史，其实更像一本项目管理的书。
]]></description>
			<content:encoded><![CDATA[<p>今晚总算把《观止》看完了，看得比较快，基本上是当叙事小说一样看。这个书名看起来像文艺书，但实际上是讲述Windows NT这个操作系统的创造史。为什么Windows NT这么出色，因为在当时它的诞生改变了人们对操作系统的看法，它引入了现在我们看起来很理所当然但当时还没有的技术：抢占式的多任务处理、可安装在通用的硬件平台、支持32位内存寻址、支持大容量文件并具有容错功能的NTFS（NT File System）等等。当然这些系统功能并没有在书上描述，这本书从头到尾都是在讲述整个NT团队是如此在艰苦的进度压力和技术压力之下花了将近5年时间创造出NT。</p>
<p>这个NT团队的领导人就是Dave Cutler，性格独断脾气火爆，技术牛B，现在来说就是一个传奇的程序员，当他领导NT团队的时候已经是个快50岁的人了。有个小插曲或者会更令人记住他是谁，就是当他招聘小秘的时候会问一个问题“How do u think about the word 'fuck'?"，只有那个回答了"Its my favorite word”的MM才拿到了offer。</p>
<p>从1988年10月开始到正式发布的1993年7月23日，历时接近5年时间，团队从开始的20多人到最后的200多人，在这5年时间里面NT团队面对的是各种功能需求的增加、遥遥无期的进度、不断冒出的bug，还有其他非技术问题，如家庭和爱人，可以说是可歌可泣，NT团队的每个成员为Windows NT奉献太多了。<span id="more-1047"></span></p>
<p>虽然看得比较快，但是一些体会还是想记录一下，以后有时间再翻出来看或者也会有不同的体会。</p>
<p>从本来只有一个内核开发的团队，到后面慢慢增加了图形开发小组和网络开发小组，整个团队大部分人都是物理和数学出身，毕竟从理科出身的容易投入到计算机行业，加上当时计算机行业还远没现在这么热门。由于当时NT的目标定得比较高，因此很多迎合市场的需求都被添加到功能列表中，并要求兼容以前的DOS和Windows程序，不停地添加功能需求以及保证兼容性基本上就直接导致了进度问题，由于领导的Dave Cutler是个基本上为了NT放弃了家庭的人，因此给NT的团队带来的直接影响就是在进度压力之下的疯狂加班，纵然如此，但是在微软的给予的高股票期权和Cutler的压迫之下，团队的成员也是能积极高效地工作，其实这里，我还觉得有个原因是在这种环境之下驱动大家去工作的，就是大家都希望能够创造出一个出色的产品，虽然工作辛苦，但是这种渴望创造的欲望也是驱动团队成员努力工作的一个原因。</p>
<p>程序员、测试员和构建员是组成NT团队的主要3个角色，在那个年代，测试员和构建员的地位并不高，但是慢慢程序不断地被挑出bug并不断地被完善，越显得测试员和构建员的重要性。相比于目前自己所做的项目，哪怕是相差20年，目前我们项目的流程还是比不上那时的NT团队。每个程序员的commit都是基于不会导致构建失败的前提下，当然构建的时机可能是即时也可能是固定某个时候，但是每个程序员的commit都是使构建能正常进行的，整个流程都是井然有序地进行。而且，更让我欣赏NT团队的是，在项目的后期，Dave Cutler甚至是长期定居在构建实验室，来专门看每个程序员提交的各种代码，来保证程序代码的质量，那么多不知道他怎么看得过来。对于程序员犯的错误，Dave Cutler可是不留情面地批评，或者正是这种领导的压迫之下反而提高了团队成员的代码严谨，或者可以说是领导威严的作用。</p>
<p>团队规模变大，可能就增加了成员之间对技术问题的冲突，但是正如书上说，这种冲突有时候会激发对程序代码的改进，其实倒觉得这种冲突是来源于对自己代码逻辑的自信，或者说是个性鲜明，和提倡中庸之道的国内还是有点区别。</p>
<p>到了项目后期，真的可以算得上夺命狂奔，因为有了deadline，所以对bug的分类控制、暂停新功能需求的加入、不停的压力测试、客户的bug反馈和beta版的多次发布等等，各个成员都日以继夜地修正各种showstopper和一等级的bug。</p>
<p>最后Windows NT发布了，微软就站在了操作系统的顶峰了。而从始自终，Gates都没有对NT团队有什么干预，只是定期了解开发进度和进行产品的测试，给了NT团队很大的自由度。</p>
<p>《观止》这本书虽然只是描述了整个NT的创造史，其实更像一本项目管理的书。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/08/11/reading-showstopper/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>香港游记</title>
		<link>http://blog.endlesscode.com/2010/08/10/journey-to-hk/</link>
		<comments>http://blog.endlesscode.com/2010/08/10/journey-to-hk/#comments</comments>
		<pubDate>Mon, 09 Aug 2010 16:15:47 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[It's My Life]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=1033</guid>
		<description><![CDATA[上周5去香港玩了3天，本来是想昨晚就写一下的，但是10点多回到来和同学吃了个宵夜就晚了。
周5早上7点半就醒了，然后去匆匆地坐地铁去火车站，坐传说中的河蟹号到罗湖，其实这还是挺快的，河蟹号一个小时就从广州到罗湖了，不过第一次过境，还是花了点时间，到香港之后，再从地铁和公交到西环，这里也花了1.5个小时了，不过香港的公交比广州的舒服多了，没有广州挤得那么厉害，而且，小公巴如果满人了司机是不会让人再上来的。
Day 1：
第一天到阿清那已经1点多了，两个人就去附近吃了个性价比还算可以的餐厅吃了个扒，然后接下来就去港大了。港大的正门没有像一些大学那些有气势，相对来说还是比较小的，而且港大也是建在半山腰的，从西环那里上去都是坐电梯上去的。参观了一下港大，中山像、莲花池、月明池，不过有个雕塑还是纪念敏感词事件的，叫国殇之柱。就是下面这个，照得不太好，要侧着脖子看。
接着下午就去了黄大仙，我和阿清都求了个事业签，那个解签的说我的是中下签，最近2个月事业不顺，要跳槽就趁早，我听得都蒙了。然后她说阿清下下签，阿清一听，皱了皱眉头，“哼？”的一声，那个解签的又说阿清会有贵人相助，不用太担心，这个有点搞笑，后来还叫我们去求个护符，看来这个中下/下下-&#62; 护符是个固定的模式。
之后我们就去了一趟铜锣湾，商业街那是非常地热闹，不过我们也只是去买了几件衣服基本没有买什么了，毕竟逛商业街的确不应该是2个男的应该做的事情。
晚上就去了金紫荆广场，那里游客也不少，其实金紫荆广场也没有什么大的看点，就只有一个金色的紫荆花雕像。接下来就坐游轮去星光大道那边，星光大道那里都是一些明星的手印，一些较为出名的就比较多人围观，比如刘德华、成龙等等，我也照了几个，下面这张是刘德华的，嘻嘻。基本上去了星光大道就回去了，本来还想去兰桂坊的，但逛了大半天太累了。
Day 2：
因为第一天玩得太累了，因此第二天睡到很晚才起来，所以上午也就没有去什么地方玩，由于同学下午要去上课，我也背着个书包又做了一回学生。上的这门课是Network Security，professor是全程用英语讲课，不过港式英语口音太怪了。讲到Security Model的时候提到一个牛B的模型“Chinese Wall”，“Prevent information flow that will result in conflict of interest”，这个GFW都已经算是一个安全模型了。
晚上本来还想去一趟兰桂坊的，但是由于下大雨，两个人只能回去看电影玩星际了。这一天基本上没有怎么出去外面玩。
Day 3：
今天也是睡到很晚才起来，所以行程都是从下午开始。因为晚上要回广州，所以下午能计划的比较少，就去坐了一下山顶缆车，不过由于是周日，人那是相当地多。去到了山顶，俯视全香港，那景色是相当地让人看起来舒服：
晚上去大快活把晚餐消灭了，不过刚好赶上下午茶时间，所以价格还是相当地便宜，两个人70HK$已经吃得非常赞了，米线、蜜汁鸡比、西多士还有红豆冰，消费没有原来想象中那么高。

结束
总的来说，玩得还算不错，因为是借宿在同学家，因此也省了一笔，而且这3天阿清都是带我去一些性价比相当高的地方吃饭，光是吃扒都吃了2天，囧，港大饭堂的、华人餐厅、中环的餐厅，吃扒吃到牙都累了，不过凡是能点西多士的地方我都会点这个。衣服以我们这边的消费水平来讲是比较贵，但以香港那边的消费水平来看其实也是一般般。没有去海洋公园和迪士尼，两个男的去这两个景点还真是没有意思。
]]></description>
			<content:encoded><![CDATA[<p>上周5去香港玩了3天，本来是想昨晚就写一下的，但是10点多回到来和同学吃了个宵夜就晚了。</p>
<p>周5早上7点半就醒了，然后去匆匆地坐地铁去火车站，坐传说中的河蟹号到罗湖，其实这还是挺快的，河蟹号一个小时就从广州到罗湖了，不过第一次过境，还是花了点时间，到香港之后，再从地铁和公交到西环，这里也花了1.5个小时了，不过香港的公交比广州的舒服多了，没有广州挤得那么厉害，而且，小公巴如果满人了司机是不会让人再上来的。</p>
<h2>Day 1：</h2>
<p>第一天到阿清那已经1点多了，两个人就去附近吃了个性价比还算可以的餐厅吃了个扒，然后接下来就去港大了。港大的正门没有像一些大学那些有气势，相对来说还是比较小的，而且港大也是建在半山腰的，从西环那里上去都是坐电梯上去的。参观了一下港大，中山像、莲花池、月明池，不过有个雕塑还是纪念敏感词事件的，叫国殇之柱。就是下面这个，照得不太好，要侧着脖子看。<img class="aligncenter size-medium wp-image-1034" title="IMG_2631" src="http://blog.endlesscode.com/wp-content/uploads/2010/08/IMG_2631-300x200.jpg" alt="IMG_2631" width="300" height="200" /></p>
<p><span id="more-1033"></span>接着下午就去了黄大仙，我和阿清都求了个事业签，那个解签的说我的是中下签，最近2个月事业不顺，要跳槽就趁早，我听得都蒙了。然后她说阿清下下签，阿清一听，皱了皱眉头，“哼？”的一声，那个解签的又说阿清会有贵人相助，不用太担心，这个有点搞笑，后来还叫我们去求个护符，看来这个中下/下下-&gt; 护符是个固定的模式。</p>
<p>之后我们就去了一趟铜锣湾，商业街那是非常地热闹，不过我们也只是去买了几件衣服基本没有买什么了，毕竟逛商业街的确不应该是2个男的应该做的事情。</p>
<p>晚上就去了金紫荆广场，那里游客也不少，其实金紫荆广场也没有什么大的看点，就只有一个金色的紫荆花雕像。接下来就坐游轮去星光大道那边，星光大道那里都是一些明星的手印，一些较为出名的就比较多人围观，比如刘德华、成龙等等，我也照了几个，下面这张是刘德华的，嘻嘻。<img class="aligncenter size-medium wp-image-1038" title="IMG_2667" src="http://blog.endlesscode.com/wp-content/uploads/2010/08/IMG_2667-300x200.jpg" alt="IMG_2667" width="300" height="200" />基本上去了星光大道就回去了，本来还想去兰桂坊的，但逛了大半天太累了。</p>
<h2>Day 2：</h2>
<p>因为第一天玩得太累了，因此第二天睡到很晚才起来，所以上午也就没有去什么地方玩，由于同学下午要去上课，我也背着个书包又做了一回学生。上的这门课是Network Security，professor是全程用英语讲课，不过港式英语口音太怪了。讲到Security Model的时候提到一个牛B的模型“Chinese Wall”，“Prevent information flow that will result in conflict of interest”，这个GFW都已经算是一个安全模型了。</p>
<p>晚上本来还想去一趟兰桂坊的，但是由于下大雨，两个人只能回去看电影玩星际了。这一天基本上没有怎么出去外面玩。</p>
<h2>Day 3：</h2>
<p>今天也是睡到很晚才起来，所以行程都是从下午开始。因为晚上要回广州，所以下午能计划的比较少，就去坐了一下山顶缆车，不过由于是周日，人那是相当地多。<img class="aligncenter size-medium wp-image-1039" title="IMG_2675" src="http://blog.endlesscode.com/wp-content/uploads/2010/08/IMG_2675-300x200.jpg" alt="IMG_2675" width="300" height="200" />去到了山顶，俯视全香港，那景色是相当地让人看起来舒服：</p>
<p><img class="aligncenter size-large wp-image-1040" title="IMG_2680" src="http://blog.endlesscode.com/wp-content/uploads/2010/08/IMG_2680-1024x682.jpg" alt="IMG_2680" width="1024" height="682" />晚上去大快活把晚餐消灭了，不过刚好赶上下午茶时间，所以价格还是相当地便宜，两个人70HK$已经吃得非常赞了，米线、蜜汁鸡比、西多士还有红豆冰，消费没有原来想象中那么高。</p>
<p><img class="aligncenter size-medium wp-image-1042" title="IMG_2683" src="http://blog.endlesscode.com/wp-content/uploads/2010/08/IMG_2683-300x200.jpg" alt="IMG_2683" width="300" height="200" /></p>
<h2>结束</h2>
<p>总的来说，玩得还算不错，因为是借宿在同学家，因此也省了一笔，而且这3天阿清都是带我去一些性价比相当高的地方吃饭，光是吃扒都吃了2天，囧，港大饭堂的、华人餐厅、中环的餐厅，吃扒吃到牙都累了，不过凡是能点西多士的地方我都会点这个。衣服以我们这边的消费水平来讲是比较贵，但以香港那边的消费水平来看其实也是一般般。没有去海洋公园和迪士尼，两个男的去这两个景点还真是没有意思。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/08/10/journey-to-hk/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>写在毕业一年后</title>
		<link>http://blog.endlesscode.com/2010/07/01/one-year-after-graduation/</link>
		<comments>http://blog.endlesscode.com/2010/07/01/one-year-after-graduation/#comments</comments>
		<pubDate>Wed, 30 Jun 2010 16:14:00 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[It's My Life]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=1009</guid>
		<description><![CDATA[不经不觉毕业也一年了，走得太匆忙，并没有和大家留下什么伤感的回忆。
从入职到现在，除了觉得时间过得很快之外，就是觉得自己没有把握好时间，或者毕业后各方面的原因学习动力和激情也比不上在学校的时候。
回头看看这一年……
09年的下半年，大部分时间是投入在研究fopen这个Facebook开源SNS，由于这是搭建在Linux平台并用php和c++写的，Linux和php这两方面对我来说都不太熟悉，折腾了好长的一段时间。fopen并没有原本所期待那样能够真正地投入到正式的产品使用，更多的，它只是Facebook的一个超级简化版，而且文档也相当地不完整，整个fopen依赖的库文件也很多，我当时也没能弄明白这些库文件是做什么的。在改写JavaScript的部分，虽然代码逻辑是比较清晰，但是像它那样的逻辑重写1000的JavaScript代码都要10多秒，不知道它在正式投入使用的时候是怎么提升解析速度的，又或者是正式产品中是使用了C++重写或者利用其他的一些缓存技术。在OpenAPI部分，那时候还弄不明白那个timestamp和callback的参数作用，后来才明白timestamp是用于防止replay attack，而callback是为了使用jsonp的，只怪当时知识面太窄了。虽然最后也用python实现了类似的JavaScript Parser和OpenAPI以及JavaScript client lib，但实际上需要完善的空间实在太多，只是时间无法安排过来。、
09年11月份开始的时候封闭开发了好长一段时间，封闭开发最大的体会就是环境很安静，但人能够很集中精神地开发，效率的确高很多。其实这段时间最大的收获是在美林的时候认识了老邓，他是云风带领的那个工作室的成员，他把我带进了函数式编程的世界，跟我讲了很多除了c++、java、.net之外的其他语言情况，比如lisp、squeak、haskell、erlang等等，还跟我讲了计算机世界里面一些有意思的事情和人物，并介绍了好几本书给我。作为刚毕业的新人，老员工知识和经验的分享正是新人所需要的。
从新年放假回来后，前2个月是主要是完成一些比较琐碎的东西，开发一些平台上的小功能和修复一些bug。后2个月主要是开发一些营销活动，进度非常赶，这些活动项目时效比较短，我只是加班把这些活动项目做出来，但是我没有时间把这些项目做得好，我自己是个追求完美的人，作品写得这么挫却又无可奈何心里面是何等的不爽。我觉得公司的营销同事制定营销推广计划的时候应该和开发人员先协商进度安排以及项目上线的各方面可行性，而不是把计划定好了，然后有什么进度压力就直接推给开发人员。
Python写了一年多，但是我觉得在同一届入职的同事里面，掌握得最差的应该是我了。在Python花的时间的确是太少了，我仍然需要阅读更多关于Python方面的资料和写更多的代码，pythonic是我的目标。JavaScript方面在实际上项目倒是提高了不少，对闭包和作用域链的理解，一些IE的hack，页面脚本的加载顺序等等，Firebug的脚本调试作用也相当的强大和方便。总的来说，各方面都有一定的提高，不过前端的css的样式设置我依然觉得很痛苦。虽然平时都是在Linux环境下开发，但是我对Linux的理解还是相当地肤浅，Shell编程和Linux C的提高是我的下一个目标，Linux下的很多命令，如iptables、tr、sort、awk等等只是略懂一二，我觉得太多的Linux常识我需要去补充。
从自己每个月写的blog数量基本上可以反映出自己在当月的工作繁忙程度以及自身的状态。6月份的状态算是上半年中最好的了，放下了一些纠结让自己更能专注。我很怀念在学校那种为了完成一个作业或者项目连续好多个晚上搞得通宵，不分昼夜专注地去做，工作之后很难有这种机会了。前几天和大学的同事吃饭，也是舍友，他说他想花5年的时间给自己充电再去微软，他当初在百度和微软实习过，只是有点可惜。前段时间回到学校，也得知有位同学正准备出国。感慨大家都很有目标，也同时为了自己的目标而在努力地奋斗。一生何求，我也得要为自己定个目标。
胡扯了一下就写了2个小时，时间是只能靠自己把握的，这是个和青春竞争的人生阶段。
]]></description>
			<content:encoded><![CDATA[<p>不经不觉毕业也一年了，走得太匆忙，并没有和大家留下什么伤感的回忆。</p>
<p>从入职到现在，除了觉得时间过得很快之外，就是觉得自己没有把握好时间，或者毕业后各方面的原因学习动力和激情也比不上在学校的时候。</p>
<p>回头看看这一年……</p>
<p>09年的下半年，大部分时间是投入在研究fopen这个Facebook开源SNS，由于这是搭建在Linux平台并用php和c++写的，Linux和php这两方面对我来说都不太熟悉，折腾了好长的一段时间。fopen并没有原本所期待那样能够真正地投入到正式的产品使用，更多的，它只是Facebook的一个超级简化版，而且文档也相当地不完整，整个fopen依赖的库文件也很多，我当时也没能弄明白这些库文件是做什么的。在改写JavaScript的部分，虽然代码逻辑是比较清晰，但是像它那样的逻辑重写1000的JavaScript代码都要10多秒，不知道它在正式投入使用的时候是怎么提升解析速度的，又或者是正式产品中是使用了C++重写或者利用其他的一些缓存技术。在OpenAPI部分，那时候还弄不明白那个timestamp和callback的参数作用，后来才明白timestamp是用于防止replay attack，而callback是为了使用jsonp的，只怪当时知识面太窄了。虽然最后也用python实现了类似的JavaScript Parser和OpenAPI以及JavaScript client lib，但实际上需要完善的空间实在太多，只是时间无法安排过来。、</p>
<p>09年11月份开始的时候封闭开发了好长一段时间，封闭开发最大的体会就是环境很安静，但人能够很集中精神地开发，效率的确高很多。其实这段时间最大的收获是在美林的时候认识了老邓，他是云风带领的那个工作室的成员，他把我带进了函数式编程的世界，跟我讲了很多除了c++、java、.net之外的其他语言情况，比如lisp、squeak、haskell、erlang等等，还跟我讲了计算机世界里面一些有意思的事情和人物，并介绍了好几本书给我。作为刚毕业的新人，老员工知识和经验的分享正是新人所需要的。<span id="more-1009"></span></p>
<p>从新年放假回来后，前2个月是主要是完成一些比较琐碎的东西，开发一些平台上的小功能和修复一些bug。后2个月主要是开发一些营销活动，进度非常赶，这些活动项目时效比较短，我只是加班把这些活动项目做出来，但是我没有时间把这些项目做得好，我自己是个追求完美的人，作品写得这么挫却又无可奈何心里面是何等的不爽。我觉得公司的营销同事制定营销推广计划的时候应该和开发人员先协商进度安排以及项目上线的各方面可行性，而不是把计划定好了，然后有什么进度压力就直接推给开发人员。</p>
<p>Python写了一年多，但是我觉得在同一届入职的同事里面，掌握得最差的应该是我了。在Python花的时间的确是太少了，我仍然需要阅读更多关于Python方面的资料和写更多的代码，pythonic是我的目标。JavaScript方面在实际上项目倒是提高了不少，对闭包和作用域链的理解，一些IE的hack，页面脚本的加载顺序等等，Firebug的脚本调试作用也相当的强大和方便。总的来说，各方面都有一定的提高，不过前端的css的样式设置我依然觉得很痛苦。虽然平时都是在Linux环境下开发，但是我对Linux的理解还是相当地肤浅，Shell编程和Linux C的提高是我的下一个目标，Linux下的很多命令，如iptables、tr、sort、awk等等只是略懂一二，我觉得太多的Linux常识我需要去补充。</p>
<p>从自己每个月写的blog数量基本上可以反映出自己在当月的工作繁忙程度以及自身的状态。6月份的状态算是上半年中最好的了，放下了一些纠结让自己更能专注。我很怀念在学校那种为了完成一个作业或者项目连续好多个晚上搞得通宵，不分昼夜专注地去做，工作之后很难有这种机会了。前几天和大学的同事吃饭，也是舍友，他说他想花5年的时间给自己充电再去微软，他当初在百度和微软实习过，只是有点可惜。前段时间回到学校，也得知有位同学正准备出国。感慨大家都很有目标，也同时为了自己的目标而在努力地奋斗。一生何求，我也得要为自己定个目标。</p>
<p>胡扯了一下就写了2个小时，时间是只能靠自己把握的，这是个和青春竞争的人生阶段。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/07/01/one-year-after-graduation/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>zz Dojo Javascript 编程规范</title>
		<link>http://blog.endlesscode.com/2010/06/25/zz-coding-convention-of-javascript/</link>
		<comments>http://blog.endlesscode.com/2010/06/25/zz-coding-convention-of-javascript/#comments</comments>
		<pubDate>Fri, 25 Jun 2010 05:46:58 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=977</guid>
		<description><![CDATA[虽然这说是Dojo的编程规范，但实际上大部分都是普遍应用的规范，所以就当是JavaScript的编程规范转载过来了
前言
相当不错的 Javascript 编程风格规范，建议大家采用此规范编写 Javascript。原文链接： http://dojotoolkit.org/developer/StyleGuide 。
翻译（Translated by）：i.feelinglucky{at}gmail.com from http://www.gracecode.com ，转载请注明出处、作者和翻译者，谢谢配合。
本文地址： http://code.google.com/p/grace/wiki/DojoStyle 。
序
Any violation to this guide is allowed if it enhances readability.
所有的代码都要变成可供他人容易阅读的。
快读参考
核心 API 请使用下面的风格：



结构
规则
注释


模块
小写
不要使用多重语义（Never multiple words）


类
骆驼



公有方法
混合
其他的外部调用也可以使用 lower_case()，这样的风格


公有变量
混合



常量
骆驼 或 大写




下面的虽然不是必要的，但建议使用：



结构
规则


私有方法
混合，例子：_mixedCase


私有变量
混合，例子：_mixedCase


方法（method）参数
混合，例子：_mixedCase, mixedCase


本地（local）变量
混合，例子：_mixedCase, mixedCase




命名规范

变量名称 必须为 小写字母。
类的命名使用骆驼命名规则，例如：
Account, EventHandler

常量 必须 在对象（类）或者枚举变量的前部声明。枚举变量的命名必须要有实际的意义，并且其成员 必须 使用骆驼命名规则或使用大写：
var NodeTypes = {
    Element : 1,
    DOCUMENT: 2
}
简写单词 不能使用 大写名称作为变量名：
getInnerHtml(), getXml(), XmlDocument
方法的命令 必须 为动词或者是动词短语：
obj.getSomeValue()
公有类的命名 必须 使用混合名称（mixedCase）命名。
CSS 变量的命名 必须 使用其对应的相同的公共类变量。
私有类的变量属性成员 必须 使用混合名称（mixedCase）命名，并前面下下划线（_）。例如：
var MyClass [...]]]></description>
			<content:encoded><![CDATA[<div style="width:80%;border:1px dotted #000000;padding:5px;text-align:center;margin:5px auto;">虽然这说是Dojo的编程规范，但实际上大部分都是普遍应用的规范，所以就当是JavaScript的编程规范转载过来了</div>
<h2>前言</h2>
<p style="max-width: 65em;">相当不错的 Javascript 编程风格规范，建议大家采用此规范编写 Javascript。原文链接： <a rel="nofollow" href="http://dojotoolkit.org/developer/StyleGuide">http://dojotoolkit.org/developer/StyleGuide</a> 。</p>
<p style="max-width: 65em;">翻译（Translated by）：i.feelinglucky{at}gmail.com from <a style="color: #0000cc;" rel="nofollow" href="http://www.gracecode.com">http://www.gracecode.com</a> ，转载请注明出处、作者和翻译者，谢谢配合。</p>
<p style="max-width: 65em;">本文地址： <a rel="nofollow" href="http://code.google.com/p/grace/wiki/DojoStyle">http://code.google.com/p/grace/wiki/DojoStyle</a> 。</p>
<h2>序</h2>
<p style="max-width: 65em;">Any violation to this guide is allowed if it enhances readability.</p>
<p style="max-width: 65em;">所有的代码都要变成可供他人容易阅读的。</p>
<h2>快读参考</h2>
<p style="max-width: 65em;">核心 API 请使用下面的风格：</p>
<table style="border-collapse: separate;" border="0">
<tbody>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;"><strong>结构</strong></td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;"><strong>规则</strong></td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;"><strong>注释</strong></td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">模块</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">小写</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">不要使用多重语义（Never multiple words）</td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">类</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">骆驼</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;"></td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">公有方法</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">混合</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">其他的外部调用也可以使用 lower_case()，这样的风格</td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">公有变量</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">混合</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;"></td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">常量</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">骆驼 或 大写</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;"></td>
</tr>
</tbody>
</table>
<p style="max-width: 65em;">下面的虽然不是必要的，但建议使用：</p>
<table style="border-collapse: separate;" border="0">
<tbody>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;"><strong>结构</strong></td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;"><strong>规则</strong></td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">私有方法</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">混合，例子：<tt style="font-size: 13px;">_mixedCase</tt></td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">私有变量</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">混合，例子：<tt style="font-size: 13px;">_mixedCase</tt></td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">方法（method）参数</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">混合，例子：<tt style="font-size: 13px;">_mixedCase, mixedCase</tt></td>
</tr>
<tr>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">本地（local）变量</td>
<td style="font-size: 13px; padding: 5px; border: 1px solid #aaaaaa;">混合，例子：<tt style="font-size: 13px;">_mixedCase, mixedCase</tt></td>
</tr>
</tbody>
</table>
<p><span id="more-977"></span></p>
<h2>命名规范</h2>
<ol style="max-width: 65em; padding-left: 40px;">
<li>变量名称 <em>必须为</em> 小写字母。</li>
<li>类的命名使用骆驼命名规则，例如：
<pre class="brush:javascript">Account, EventHandler</pre>
</li>
<li>常量 <em>必须</em> 在对象（类）或者枚举变量的前部声明。枚举变量的命名必须要有实际的意义，并且其成员 <em>必须</em> 使用骆驼命名规则或使用大写：</li>
<pre class="brush:javascript">var NodeTypes = {
    Element : 1,
    DOCUMENT: 2
}</pre>
<li>简写单词 <em>不能使用</em> 大写名称作为变量名：</li>
<pre class="brush:javascript">getInnerHtml(), getXml(), XmlDocument</pre>
<li>方法的命令 <em>必须</em> 为动词或者是动词短语：</li>
<pre class="brush:javascript">obj.getSomeValue()</pre>
<li>公有类的命名 <em>必须</em> 使用混合名称（mixedCase）命名。</li>
<li>CSS 变量的命名 <em>必须</em> 使用其对应的相同的公共类变量。</li>
<li>私有类的变量属性成员 <em>必须</em> 使用混合名称（mixedCase）命名，并前面下下划线（<tt style="font-size: 13px;">_</tt>）。例如：</li>
<pre class="brush:javascript">var MyClass = function(){
   var _buffer;
   this.doSomething = function(){
   };
}</pre>
<li>变量如果设置为私有，则前面 <em>必须</em> 添加下划线。</li>
<pre class="brush:javascript">this._somePrivateVariable = statement;</pre>
<li>通用的变量 <em>必须</em> 使用与其名字一致的类型名称：</li>
<pre class="brush:javascript">setTopic(topic) // 变量 topic 为 Topic 类型的变量</pre>
<li>所有的变量名 <em>必须</em> 使用英文名称。</li>
<li>变量如有较广的作用域（large scope），必须使用全局变量；此时可以设计成一个类的成员。相对的如作用域较小或为私有变量则使用简洁的单词命名。</li>
<li>如果变量有其隐含的返回值，则避免使用其相似的方法：</li>
<pre class="brush:javascript">getHandler(); // 避免使用 getEventHandler()</pre>
<li>公有变量必须清楚的表达其自身的属性，避免字义含糊不清，例如：</li>
<pre class="brush:javascript">MouseEventHandler，而非 MseEvtHdlr。</pre>
<p>请再次注意这条规定，这样做得的好处是非常明显的。它能明确的表达表达式所定义的含义。例如：</p>
<pre class="brush:javascript">dojo.events.mouse.Handler // 而非 dojo.events.mouse.MouseEventHandler</pre>
<li>类/构造函数 <em>可以使用</em> 扩展其基类的名称命名，这样可以正确、迅速的找到其基类的名称：</li>
<pre class="brush:javascript">EventHandler
UIEventHandler
MouseEventHandler</pre>
<p>基类可以在明确描述其属性的前提下，缩减其命名：</p>
<pre class="brush:javascript">MouseEventHandler as opposed to MouseUIEventHandler.</pre>
</ol>
<h2>特殊命名规范</h2>
<ol style="max-width: 65em; padding-left: 40px;">
<li>术语 "get/set" 不要和一个字段相连，除非它被定义为私有变量。</li>
<li>前面加 "is" 的变量名 <em>应该</em> 为布尔值，同理可以为 "has", "can" 或者 "should"。</li>
<li>术语 "compute" 作为变量名应为已经计算完成的变量。</li>
<li>术语 "find" 作为变量名应为已经查找完成的变量。</li>
<li>术语 "initialize" 或者 "init" 作为变量名应为已经实例化（初始化）完成的类或者其他类型的变量。</li>
<li>UI （用户界面）控制变量应在名称后加控制类型，例如： leftComboBox, TopScrollPane。</li>
<li>复数必须有其公共的名称约定（原文：Plural form MUST be used to name collections）。</li>
<li>带有 "num" 或者 "count" 开头的变量名约定为数字（对象）。</li>
<li>重复变量建议使用 "i", "j", "k" （依次类推）等名称的变量。</li>
<li>补充用语必须使用补充词，例如： get/set, add/remove, create/destroy, start/stop, insert/delete, begin/end, etc.</li>
<li>能缩写的名称尽量使用缩写。</li>
<li>避免产生歧义的布尔变量名称，例如：</li>
<pre class="brush:javascript">isNotError, isNotFound 为非法</pre>
<li>错误类建议在变量名称后加上 "Exception" 或者 "Error"。</li>
<li>方法如果返回一个类，则应该在名称上说明返回什么；如果是一个过程，则应该说明做了什么。</li>
</ol>
<h2>文件</h2>
<ol style="max-width: 65em; padding-left: 40px;">
<li>缩进请使用 4 个空白符的制表位。</li>
<li>如果您的编辑器支持 文件标签_（file tags），请加添如下的一行使我们的代码更容易阅读：
<pre class="brush:javascript">// vim:ts=4:noet:tw=0:
//译注：老外用 VIM 编辑器比较多，此条可以选择遵循。</pre>
</li>
<li>代码折叠必须看起来是完成并且是合乎逻辑的：
<pre class="brush:javascript">var someExpression = Expression1
    + Expression2
    + Expression3;

var o = someObject.get(
    Expression1,
    Expression2,
    Expression3
);

//注：表达式的缩进与变量声明应为一致的。
//注：函数的参数应采用明确的缩进，缩进规则与其他块保持一致。</pre>
</li>
</ol>
<h2>变量</h2>
<ol style="max-width: 65em; padding-left: 40px;">
<li>变量必须在声明初始化以后才能使用，即便是 NULL 类型。</li>
<li>变量不能产生歧义。</li>
<li>相关的变量集应该放在同一代码块中，非相关的变量集不应该放在同一代码块中。</li>
<li>变量应该尽量保持最小的生存周期。</li>
<li>循环/重复变量的规范：
<ul>
<li>只有循环控制块的话，则必须使用 FOR 循环。</li>
</ul>
<ul>
<li>循环变量应该在循环开始前就被初始化；如使用 FOR 循环，则使用 FOR 语句初始化循环变量。</li>
</ul>
<ul>
<li>"do ... while" 语句是被允许的。</li>
</ul>
<ul>
<li>"break" 和 "continue" 语句仍然允许使用（但请注意）。</li>
</ul>
</li>
<li>条件表达式
<ul>
<li>应该尽量避免复杂的条件表达式，如有必要可以使用临时布尔变量。</li>
</ul>
<ul>
<li>The nominal case SHOULD be put in the "if" part and the exception in the "else" part of an "if" statement.</li>
</ul>
<ul>
<li>应避免在条件表达式中加入块。</li>
</ul>
</li>
<li>杂项
<ul>
<li>尽量避免幻数（Magic numbers），他们应该使用常量来代替。</li>
</ul>
<ul>
<li>浮点变量必须指明小数点后一位（即使是 0）。</li>
</ul>
<ul>
<li>浮点变量必须指明实部，即使它们为零（使用 0. 开头）。</li>
</ul>
</li>
</ol>
<h2>布局</h2>
<h3>块</h3>
<ol style="max-width: 65em; padding-left: 40px;">
<li>普通代码段 <em>应该</em> 看起来如下：</li>
<pre class="brush:javascript">while (!isDone){
    doSomething();
    isDone = moreToDo();
}</pre>
<li>IF 语句 <em>应该</em> 看起来像这样：</li>
<pre class="brush:javascript">if (someCondition){
    statements;
} else if (someOtherCondition){
    statements;
} else {
    statements;
}</pre>
<li>FOR 语句 <em>应该</em> 看起来像这样：</li>
<pre class="brush:javascript">for (initialization; condition; update){
    statements;
}</pre>
<li>WHILE 语句 <em>应该</em> 看起来像这样：</li>
<pre class="brush:javascript">while (!isDone) {
    doSomething();
    isDone = moreToDo();
}</pre>
<li>DO ... WHILE 语句 <em>应该</em> 看起来像这样：</li>
<pre class="brush:javascript">do {
    statements;
} while (condition);</pre>
<li>SWITCH 语句 <em>应该</em> 看起来像这样：</li>
<pre class="brush:javascript">switch (condition) {
case ABC:
    statements;
    //  fallthrough
case DEF:
    statements;
    break;
default:
    statements;
    break;
}</pre>
<li>TRY ... CATCH 语句 <em>应该</em> 看起来像这样：</li>
<pre class="brush:javascript">try {
    statements;
} catch(ex) {
    statements;
} finally {
    statements;
}</pre>
<li>单行的 IF - ELSE，WHILE 或者 FOR 语句也 <em>必须</em> 加入括号，不过他们可以这样写：</li>
<pre class="brush:javascript">if (condition){ statement; }
while (condition){ statement; }
for (intialization; condition; update){ statement; }</pre>
</ol>
<h3>空白</h3>
<ol style="max-width: 65em; padding-left: 40px;">
<li>操作符 <em>建议</em> 使用空格隔开（包括三元操作符）。</li>
<li>下面的关键字 <em>避免使用</em> 空白隔开：
<ul style="max-width: 65em; padding-left: 40px;">
<li>break</li>
<li>catch</li>
<li>continue</li>
<li>do</li>
<li>else</li>
<li>finally</li>
<li>for</li>
<li>function （如果为匿名函数，例如：var foo = function(){}; ）</li>
<li>if</li>
<li>return</li>
<li>switch</li>
<li>this</li>
<li>try</li>
<li>void</li>
<li>while</li>
<li>with</li>
</ul>
</li>
<li>下面的关键字必须使用空白隔开：
<ul style="max-width: 65em; padding-left: 40px;">
<li>case</li>
<li>default</li>
<li>delete</li>
<li>function （如果为申明，例如：function foo(){}; ）</li>
<li>in</li>
<li>instanceof</li>
<li>new</li>
<li>throw</li>
<li>typeof</li>
<li>var</li>
</ul>
</li>
<li><strong>逗号（,）</strong> <em>建议</em> 使用空白隔开。</li>
<li><strong>冒号（:）</strong> <em>建议</em> 使用空白隔开。</li>
<li><strong>点（.）</strong> 在后部 <em>建议</em> 使用空白隔开。</li>
<li><strong>点（.）</strong> <em>避免</em> 在前部使用空白。</li>
<li><strong>函数调用和方法</strong> <em>避免</em> 使用空白，例如：<tt style="font-size: 13px;"> doSomething(someParameter); // 而非 doSomething (someParameter)</tt></li>
<li><strong>逻辑块</strong> 之间使用空行。</li>
<li><strong>声明</strong> <em>建议</em> 对齐使其更容易阅读。</li>
</ol>
<h3>注释</h3>
<ol style="max-width: 65em; padding-left: 40px;">
<li>生涩的代码就 <em>没有必要</em> 添加注释了，首先您需要 <strong>重写</strong> 它们。</li>
<li>所有的注释请使用英文。</li>
<li>从已解决的方案到未开发的功能，注释 <em>必须</em> 与代码相关。</li>
<li>大量的变量申明后 <em>必须</em> 跟随一段注释。</li>
<li>注释需要说明的是代码段的用处，尤其是接下来的代码段。</li>
<li>注释 <em>没有必要</em> 每行都添加。</li>
</ol>
<h2>文档</h2>
<p style="max-width: 65em;">下面提供了一些基本的函数或者对象的描述方法：</p>
<ul style="max-width: 65em; padding-left: 40px;">
<li><strong>总结（summary）</strong>: 简短的表述此函数或者对象实现的目的</li>
<li><strong>描述（description）</strong>: 对于此函数或者类的简短的描述</li>
<li><strong>返回（return）</strong>: 描述此函数返回什么（并不包括返回类型）</li>
</ul>
<h3>基本函数信息</h3>
<pre class="brush:javascript">function(){
    // summary: Soon we will have enough treasure to rule all of New Jersey.
    // description: Or we could just get a new roomate.
    //          Look, you go find him.  He don't yell at you.
    //          All I ever try to do is make him smile and sing around
    //          him and dance around him and he just lays into me.
    //          He told me to get in the freezer 'cause there was a carnival in there.
    // returns:  Look, a Bananarama tape!
}</pre>
<h3>对象函数信息</h3>
<p style="max-width: 65em;">没有返回值描述</p>
<pre class="brush:javascript">{
    // summary: Dingle, engage the rainbow machine!
    // description:
    //          Tell you what, I wish I was--oh my g--that beam,
    //          coming up like that, the speed, you might wanna adjust that.
    //          It really did a number on my back, there. I mean, and I don't
    //          wanna say whiplash, just yet, cause that's a little too far,
    //          but, you're insured, right?
}</pre>
<h3>函数的声明</h3>
<p style="max-width: 65em;">在有的情况下，对于函数的调用和声明是隐义（invisible）的。在这种情况下，我们没有办法在函数中加入说明等（供程序调用）。如果您遭遇了这种情况，您可以使用一个类来封装函数。</p>
<p style="max-width: 65em;">注：此此方法只能在函数没有初始化的参数情况下。如过不是，则它们会被忽略。</p>
<pre class="brush:javascript">dojo.declare(
    "foo",
    null,
    {
        // summary: Phew, this sure is relaxing, Frylock.
        // description:
        //              Thousands of years ago, before the dawn of
        //              man as we knew him, there was Sir Santa of Claus: an
        //              ape-like creature making crude and pointless toys out
        //              of dino-bones, hurling them at chimp-like creatures with
        //              crinkled hands regardless of how they behaved the
        //              previous year.
        // returns: Unless Carl pays tribute to the Elfin Elders in space.
    }
);</pre>
<h3>参数</h3>
<ol style="max-width: 65em; padding-left: 40px;">
<li>简单类型</li>
<p>简单的类型的参数可以直接在函数参数定义中注释说明。</p>
<pre class="brush:javascript">function(/*String*/ foo, /*int*/ bar)...</pre>
<li>可变类型参数<br />
下面是几个修饰符供参考：</p>
<ul style="max-width: 65em; padding-left: 40px;">
<li><strong>?</strong> 可选参数</li>
<li><strong>...</strong> 说面参数范围不确定</li>
<li><strong><a></a></strong> 数组</li>
</ul>
<pre class="brush:javascript">function(/*String?*/ foo, /*int...*/ bar, /*String[]*/ baz)...</pre>
</li>
<li>全局参数描述</li>
<p>如果你想增加一个描述，你可以将它们移至初始化块。<br />
基本信息格式为： *关键字* 描述字段（*key* Descriptive sentence）<br />
参数和变量的格式为： *关键字* ~*类型*~ 描述字段（ *key* ~*type*~ Descriptive sentence）<br />
注： *关键字* 和 ~*类型*~ 可以使用任何字母和数字表述。</p>
<pre class="brush:javascript">function (foo, bar) {
    // foo: String
    //      used for being the first parameter
    // bar: int
    //      used for being the second parameter
}</pre>
</ol>
<h3>变量</h3>
<p style="max-width: 65em;">由于实例变量、原型变量和外部变量的声明是一致的，所以有很多的方法声明、修改变量。具体的如何定义和定位应在变量最先出现的位置指明变量的名称、类型、作用域等信息。</p>
<pre class="brush:javascript">function foo() {
    // myString: String
    // times: int
    //        How many times to print myString
    // separator: String
    //        What to print out in between myString*
    this.myString = "placeholder text";
    this.times = 5;
}

foo.prototype.setString = function (myString) {
    this.myString = myString;
}

foo.prototype.toString = function() {
    for(int i = 0; i &lt; this.times; i++) {
        dojo.debug(this.myString);
        dojo.debug(foo.separator);
        }
}
foo.separator = "=====";</pre>
<h4>对象中的变量注释</h4>
<p style="max-width: 65em;">应使用和对象值和方法一致的标注方式，比如在他们声明的时候：</p>
<pre class="brush:javascript">{
    // key: String
    //      A simple value
    key: "value",
    // key2: String
    //      Another simple value
}</pre>
<h3>返回值</h3>
<p style="max-width: 65em;">因为函数可以同时返回多个不同（类型）的值，所以应每个返回值之后加入返回类型的注释。注释在行内注释即可，如果所有的返回值为同一类型，则指明返回的类型；如为多个不同的返回值，则标注返回类型为"mixed"。</p>
<pre class="brush:javascript">function() {
    if (arguments.length) {
        return "You passed argument(s)"; // String
    } else {
        return false; // Boolean
    }
}</pre>
<h3>伪代码（有待讨论）</h3>
<p style="max-width: 65em;">有时候您需要在函数或者类中添加对于此函数和类的功能性流程描述。如果您打算这样做，您可以使用 <tt style="font-size: 13px;">/*======== </tt>（= 字符最好出现 5 次或者更多），这样做的好处就是可以不用将这些东西加入代码（译注：原作者的意思可能为代码管理系统）。</p>
<p style="max-width: 65em;">这样看起来在 <tt style="font-size: 13px;">/*===== </tt>和 <tt style="font-size: 13px;">=====*/ </tt>会有非常长的一段注释，等待功能调整完毕以后就可以考虑是否删除。</p>
<pre class="brush:javascript">/*=====
module.pseudo.kwArgs = {
        // url: String
    //          The location of the file
    url: "",
    // mimeType: String
    //          text/html, text/xml, etc
    mimeType: ""
}
=====*/

function(/*module.pseudo.kwArgs*/ kwArgs){
    dojo.debug(kwArgs.url);
        dojo.debug(kwArgs.mimeType);
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/06/25/zz-coding-convention-of-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>zz HTML代码编写规范和建议</title>
		<link>http://blog.endlesscode.com/2010/06/25/zz-coding-convention-of-html/</link>
		<comments>http://blog.endlesscode.com/2010/06/25/zz-coding-convention-of-html/#comments</comments>
		<pubDate>Fri, 25 Jun 2010 03:54:06 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=967</guid>
		<description><![CDATA[转载来源：http://www.cnblogs.com/nicolaszhao/archive/2010/04/15/1712796.html
使用HTML5的DOCTYPE声明
&#60;!DOCTYPE html&#62; ，目前IE6，IE7还不认识，所以会以标准模式渲染页面。但是在其他浏览器下，在图文混排时图片下方会出现间隔空隙。 解决办法：
    img {
        vertical-align: bottom;
    }
页面显示字符集
使用HTML5的简写方式： &#60;meta charset="utf-8" /&#62;
遵循xhtml 1.0规则
这里只是为了编写HTML代码时，统一规范而已，在HTML5中已经不需要这样严格了，但是我们还是要规范下比较好。

所有标签必须结束；
所有标签必须小写；
标签属性都必须用引号引起来（单引号或双引号）；
标签属性必须有值：
    &#60;select&#62;
        &#60;option selected="selected"&#62;&#60;/option&#62;
    &#60;/select&#62;
    &#60;input type="checkbox" checked="checked" /&#62;

所有特殊符号必须转义。


合理使用标签

标签合理嵌套：a、span、strong、em、p、h1~h6等元素不能包含：div、ul、ol、dl、p；
严禁多div症、多span症、多table症，正确使用标签表示DOM结构，在文档没有css的条件下，任然具有结构和可读性：

h1~h6：文章标题、内容区块标题
p：文本段落
strong/em：强调文本
dl：包括标题和内容简介的区块
ul：无序列表
ol：有序列表
img：图像，必须加上alt属性来表示图像代替文本，背景和按钮不要使用该标签，请使用css处理。
table：数据网格，规则的分栏布局，必须显性定宽和定高
表单结构

使用fieldset做字段分类；
使用legend表示分类标题；
使用label表示字段文本，添加必要的for属性。




严禁使用已在xhtml 1.0中移除的用于表示样式的标签：s、i、b、font

规范命名

id： 连接符命名法“hello-world”
class: 连接符命名法“hello-world”
name： 骆驼式命名法“helloWorld”


表单元素的id以如下前缀命名


label
lbl


text
txt


password
txt


textarea
txt


file
txt


radio
rad


checkbox
chk


submit
btn


reset
btn


button
btn


hidden
hid




结构布局的元素id命名


主容器
main


页头
header


页脚
footer


内容区域
content


主导航
main-nav


二级导航
sub-nav


LOGO
logo



结构内部子元素id命名：父元素id的头字母 + 第几个子元素编号（从1开始） [...]]]></description>
			<content:encoded><![CDATA[<div style="width: 70%; margin: 5px auto; border: 1px dotted #000000; text-align: center; padding: 5px; font-size: 12px;">转载来源：<a href="http://www.cnblogs.com/nicolaszhao/archive/2010/04/15/1712796.html"><span style="font-weight: normal;">http://www.cnblogs.com/nicolaszhao/archive/2010/04/15/1712796.html</span></a></div>
<h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; font-size: 14px; font-weight: 700; color: #83cde1; padding: 0px;">使用HTML5的DOCTYPE声明</h3>
<p style="margin-top: 5px; margin-right: auto; margin-bottom: 15px; margin-left: auto; text-indent: 0px; padding: 0px;"><code style="color: #f2984c; padding: 0px; margin: 0px;">&lt;!DOCTYPE html&gt; </code>，目前IE6，IE7还不认识，所以会以标准模式渲染页面。但是在其他浏览器下，在图文混排时图片下方会出现间隔空隙。 解决办法：</p>
<pre style="margin-top: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; white-space: pre-wrap; border-top-width: 1px; border-right-width: 0px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f7f7f7; background-position: initial initial; background-repeat: initial initial; padding: 10px; border: initial dashed #cccccc;">    img {
        vertical-align: bottom;
    }</pre>
<h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; font-size: 14px; font-weight: 700; color: #83cde1; padding: 0px;">页面显示字符集</h3>
<p style="margin-top: 5px; margin-right: auto; margin-bottom: 15px; margin-left: auto; text-indent: 0px; padding: 0px;">使用HTML5的简写方式： <code style="color: #f2984c; padding: 0px; margin: 0px;">&lt;meta charset="utf-8" /&gt;</code></p>
<h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; font-size: 14px; font-weight: 700; color: #83cde1; padding: 0px;">遵循xhtml 1.0规则</h3>
<p style="margin-top: 5px; margin-right: auto; margin-bottom: 15px; margin-left: auto; text-indent: 0px; padding: 0px;">这里只是为了编写HTML代码时，统一规范而已，在HTML5中已经不需要这样严格了，但是我们还是要规范下比较好。</p>
<ul style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; list-style-type: decimal; list-style-position: inside; list-style-image: initial; padding: 0px;">
<li style="padding: 0px; margin: 0px;">所有标签必须结束；</li>
<li style="padding: 0px; margin: 0px;">所有标签必须小写；</li>
<li style="padding: 0px; margin: 0px;">标签属性都必须用引号引起来（单引号或双引号）；</li>
<li style="padding: 0px; margin: 0px;">标签属性必须有值：
<pre style="margin-top: 15px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; white-space: pre-wrap; border-top-width: 1px; border-right-width: 0px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f7f7f7; background-position: initial initial; background-repeat: initial initial; padding: 10px; border: initial dashed #cccccc;">    &lt;select&gt;
        &lt;option selected="selected"&gt;&lt;/option&gt;
    &lt;/select&gt;
    &lt;input type="checkbox" checked="checked" /&gt;</pre>
</li>
<li style="padding: 0px; margin: 0px;">所有特殊符号必须转义。</li>
</ul>
<p><span id="more-967"></span></p>
<h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; font-size: 14px; font-weight: 700; color: #83cde1; padding: 0px;">合理使用标签</h3>
<ul style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; list-style-type: decimal; list-style-position: inside; list-style-image: initial; padding: 0px;">
<li style="padding: 0px; margin: 0px;"><strong>标签合理嵌套：</strong>a、span、strong、em、p、h1~h6等元素不能包含：div、ul、ol、dl、p；</li>
<li style="padding: 0px; margin: 0px;">严禁多div症、多span症、多table症，正确使用标签表示DOM结构，在文档没有css的条件下，任然具有结构和可读性：
<ul style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 25px; list-style-type: upper-alpha; list-style-position: inside; list-style-image: initial; padding: 0px;">
<li style="padding: 0px; margin: 0px;">h1~h6：文章标题、内容区块标题</li>
<li style="padding: 0px; margin: 0px;">p：文本段落</li>
<li style="padding: 0px; margin: 0px;">strong/em：强调文本</li>
<li style="padding: 0px; margin: 0px;">dl：包括标题和内容简介的区块</li>
<li style="padding: 0px; margin: 0px;">ul：无序列表</li>
<li style="padding: 0px; margin: 0px;">ol：有序列表</li>
<li style="padding: 0px; margin: 0px;">img：图像，必须加上alt属性来表示图像代替文本，背景和按钮不要使用该标签，请使用css处理。</li>
<li style="padding: 0px; margin: 0px;">table：数据网格，规则的分栏布局，必须显性定宽和定高</li>
<li style="padding: 0px; margin: 0px;">表单结构
<ul style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 25px; list-style-type: lower-alpha; list-style-position: inside; list-style-image: initial; padding: 0px;">
<li style="padding: 0px; margin: 0px;">使用fieldset做字段分类；</li>
<li style="padding: 0px; margin: 0px;">使用legend表示分类标题；</li>
<li style="padding: 0px; margin: 0px;">使用label表示字段文本，添加必要的for属性。</li>
</ul>
</li>
</ul>
</li>
<li style="padding: 0px; margin: 0px;">严禁使用已在xhtml 1.0中移除的用于表示样式的标签：s、i、b、font</li>
</ul>
<h3 style="margin-top: 0px; margin-right: 0px; margin-bottom: 5px; margin-left: 0px; font-size: 14px; font-weight: 700; color: #83cde1; padding: 0px;">规范命名</h3>
<ul style="margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; list-style-type: decimal; list-style-position: inside; list-style-image: initial; padding: 0px;">
<li style="padding: 0px; margin: 0px;">id： 连接符命名法“hello-world”</li>
<li style="padding: 0px; margin: 0px;">class: 连接符命名法“hello-world”</li>
<li style="padding: 0px; margin: 0px;">name： 骆驼式命名法“helloWorld”</li>
</ul>
<table style="width: 40%; margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; border-top-width: 1px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 1px; border-collapse: collapse; padding: 0px; border: initial solid #cccccc;" border="0">
<caption style="padding-top: 0px; padding-right: 0px; padding-bottom: 5px; padding-left: 0px; text-align: left; font-weight: 700; margin: 0px;">表单元素的id以如下前缀命名</caption>
<tbody style="padding: 0px; margin: 0px;">
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;" width="50%">label</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">lbl</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">text</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">txt</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">password</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">txt</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">textarea</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">txt</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">file</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">txt</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">radio</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">rad</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">checkbox</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">chk</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">submit</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">btn</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">reset</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">btn</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">button</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">btn</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">hidden</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">hid</td>
</tr>
</tbody>
</table>
<table style="width: 40%; margin-top: 0px; margin-right: 0px; margin-bottom: 15px; margin-left: 0px; border-top-width: 1px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 1px; border-collapse: collapse; padding: 0px; border: initial solid #cccccc;" border="0">
<caption style="padding-top: 0px; padding-right: 0px; padding-bottom: 5px; padding-left: 0px; text-align: left; font-weight: 700; margin: 0px;">结构布局的元素id命名</caption>
<tbody style="padding: 0px; margin: 0px;">
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;" width="50%">主容器</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">main</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">页头</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">header</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">页脚</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">footer</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">内容区域</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">content</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">主导航</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">main-nav</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">二级导航</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #f9f9f9; background-position: initial initial; background-repeat: initial initial; margin: 0px; border: initial solid #cccccc;">sub-nav</td>
</tr>
<tr style="padding: 0px; margin: 0px;">
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">LOGO</td>
<td style="padding-top: 3px; padding-right: 5px; padding-bottom: 3px; padding-left: 5px; border-top-width: 0px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 0px; margin: 0px; border: initial solid #cccccc;">logo</td>
</tr>
</tbody>
</table>
<p style="margin-top: 5px; margin-right: auto; margin-bottom: 15px; margin-left: auto; text-indent: 0px; padding: 0px;"><strong>结构内部子元素id命名：</strong>父元素id的头字母 + 第几个子元素编号（从1开始） + （可选）当前元素在父容器中的显示位置（上/右/下/左）“t/r/b/l” + （可选）当前元素在父容器中为第几个子元素（从1开始），比如，内容区域id=”content”的子元素：c-1，c-2，c-3，c-4-t，c-4-r-1，c-4-b-2，c-4-l-3...</p>
<p style="margin-top: 5px; margin-right: auto; margin-bottom: 15px; margin-left: auto; text-indent: 0px; padding: 0px;"><strong>class命名：</strong>按功能命名，用连接符分割单词，单词要有语义，可以自解释，不要使用缩写，除非一看就能理解。</p>
<p style="margin-top: 5px; margin-right: auto; margin-bottom: 15px; margin-left: auto; text-indent: 0px; padding: 0px;"><strong>name命名：</strong>表单元素name名称为去掉该元素id的前缀，然后用id后面的单词，去掉分割符，使用骆骆式命名链接各单词，比如id=”txt-id-card”，那么name=”idCard”。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/06/25/zz-coding-convention-of-html/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How MapReduce Works</title>
		<link>http://blog.endlesscode.com/2010/06/24/how-mapreduce-works/</link>
		<comments>http://blog.endlesscode.com/2010/06/24/how-mapreduce-works/#comments</comments>
		<pubDate>Thu, 24 Jun 2010 15:38:08 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[Hadoop]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=934</guid>
		<description><![CDATA[一、从Map到Reduce
MapReduce其实是分治算法的一种实现，其处理过程亦和用管道命令来处理十分相似，一些简单的文本字符的处理甚至也可以使用Unix的管道命令来替代，从处理流程的角度来看大概如下：
cat input &#124; grep &#124;      sort      &#124;  uniq -c &#124; cat &#62; output
# Input -&#62; Map -&#62; Shuffle &#38; Sort -&#62; Reduce -&#62; Output
简单的流程图如下：
对于Shuffle，简单地说就是将Map的输出通过一定的算法划分到合适的Reducer中进行处理。Sort当然就是对中间的结果进行按key排序，因为Reducer的输入是严格要求按key排序的。
Input-&#62;Map-&#62;Shuffle&#38;Sort-&#62;Reduce-&#62;Output只是从宏观的角度对MapReduce的简单描述，实际在MapReduce的框架中，即从编程的角度来看，其处理流程是Input-&#62;Map-&#62;Sort-&#62;Combine-&#62;Partition-&#62;Reduce-&#62;Output。用之前的对温度进行统计的例子来讲述这些过程。
Input Phase
输入的数据需要以一定的格式传递给Mapper的，格式有多种，如TextInputFormat、DBInputFormat、SequenceFileInput等等，可以使用JobConf.setInputFormat来设置，这个过程还应该包括对输入的数据进行任务粒度划分(split)然后再传递给Mapper。在温度的例子中，由于处理的都是文本数据，输入的格式使用默认的TextInputFormat即可。
Map Phase
对输入的key、value对进行处理，输出的是key、value的集合，即map (k1, v1) -&#62; list(k2, v2)，使用JobConf.setMapperClass设置自己的Mapper。在例子中，将（行号、温度的文本数据）作为key/value输入，经过处理后，从温度的文件数据中提取出日期中的年份和该日的温度数据，形成新的key/value对，最后以list(年,  温度)的结果输出，如[(1950, 10), (1960, 40), (1960, 5)]。
Sort Phase
对Mapper输出的数据进行排序，可以通过JobConf.setOutputKeyComparatorClass来设置自己的排序规则。在例子中，经过排序之后，输出的list集合是按年份进行排序的list(年, 温度)，如[(1950, 10), (1950, 5), (1960, 40)]。
Combine [...]]]></description>
			<content:encoded><![CDATA[<h2>一、从Map到Reduce</h2>
<p>MapReduce其实是分治算法的一种实现，其处理过程亦和用管道命令来处理十分相似，一些简单的文本字符的处理甚至也可以使用Unix的管道命令来替代，从处理流程的角度来看大概如下：</p>
<pre class="brush:bash">cat input | grep |      sort      |  uniq -c | cat &gt; output
# Input -&gt; Map -&gt; Shuffle &amp; Sort -&gt; Reduce -&gt; Output</pre>
<p>简单的流程图如下：</p>
<p><img class="aligncenter size-full wp-image-941" title="procedure" src="http://blog.endlesscode.com/wp-content/uploads/2010/06/procedure.jpg" alt="procedure" width="592" height="438" />对于Shuffle，简单地说就是将Map的输出通过一定的算法划分到合适的Reducer中进行处理。Sort当然就是对中间的结果进行按key排序，因为Reducer的输入是严格要求按key排序的。<span id="more-934"></span></p>
<p>Input-&gt;Map-&gt;Shuffle&amp;Sort-&gt;Reduce-&gt;Output只是从宏观的角度对MapReduce的简单描述，实际在MapReduce的框架中，即从编程的角度来看，其处理流程是Input-&gt;Map-&gt;Sort-&gt;Combine-&gt;Partition-&gt;Reduce-&gt;Output。用之前的对温度进行统计的例子来讲述这些过程。</p>
<h3>Input Phase</h3>
<p>输入的数据需要以一定的格式传递给Mapper的，格式有多种，如TextInputFormat、DBInputFormat、SequenceFileInput等等，可以使用JobConf.setInputFormat来设置，这个过程还应该包括对输入的数据进行任务粒度划分(split)然后再传递给Mapper。在温度的例子中，由于处理的都是文本数据，输入的格式使用默认的TextInputFormat即可。</p>
<h3>Map Phase</h3>
<p>对输入的key、value对进行处理，输出的是key、value的集合，即map (k1, v1) -&gt; list(k2, v2)，使用JobConf.setMapperClass设置自己的Mapper。在例子中，将（行号、温度的文本数据）作为key/value输入，经过处理后，从温度的文件数据中提取出日期中的年份和该日的温度数据，形成新的key/value对，最后以list(年,  温度)的结果输出，如[(1950, 10), (1960, 40), (1960, 5)]。</p>
<h3>Sort Phase</h3>
<p>对Mapper输出的数据进行排序，可以通过JobConf.setOutputKeyComparatorClass来设置自己的排序规则。在例子中，经过排序之后，输出的list集合是按年份进行排序的list(年, 温度)，如[(1950, 10), (1950, 5), (1960, 40)]。</p>
<h3>Combine Phase</h3>
<p>这个阶段是将中间结果中有相同的key的&lt;key, value&gt;对合并成一对，Combine的过程与Reduce很相似，使用的甚至是Reduce的接口。通过Combine能够减少&lt;key, value&gt;的集合数量，从而减少网络流量。Combine只是一个可选的优化过程，并且无论Combine执行多少次(&gt;=0)，都会使Reducer产生相同的输出，使用JobConf.setCombinerClass来设置自定义的Combine Class。在例子中，假如map1产生出的结果为[(1950, 0), (1950, 20), (1950, 10)]，在map2产生出的结果为[(1950, 15), (1950, 25)]，这两组数据作为Reducer的输入并经过Reducer处理后的年最高温度结果为(1950, 25)，然而当在Mapper之后加了Combine（Combine先过滤出最高温度），则map1的输出是[(1950, 20)]和map2的输出是[(1950, 25)]，虽然其他的三组数据被抛弃了，但是对于Reducer的输出而言，处理后的年最高温度依然是(1950, 25)。</p>
<h3>Partition Phase</h3>
<p>把Mapper任务输出的中间结果按key的范围划分成R份（R是预先定义的Reduce任务的个数），默认的划分算法是"(key.hashCode() &amp; Integer.MAX_VALUE) % numPartitions"，这样保证了某一范围的key一定是由某个Reducer来处理，简化了Reducer的处理流程，使用JobConf.setPartitionClass来设置自定义的Partition Class。在例子中，默认就自然是对年份进行取模了。</p>
<h3>Reduce Phase</h3>
<p>Reducer获取Mapper输出的中间结果，作为输入对某一key范围区间进行处理，使用JobConf.setReducerClass来设置。在例子中，与Combine Phase中的处理是一样的，把各个Mapper传递过来的数据计算年最高温度。</p>
<h3>Output Phase</h3>
<p>Reducer的输出格式和Mapper的输入格式是相对应的，当然Reducer的输出还可以作为另一个Mapper的输入继续进行处理。</p>
<h2>二、Details of Job Run</h2>
<p>上面只是从task运行中描述了Map和Reduce的过程，实际上当从运行"hadoop jar"开始还涉及到很多其他的细节。从整个Job运行的流程来看，如下图所示：</p>
<p><img class="aligncenter size-full wp-image-943" title="hadoop_run" src="http://blog.endlesscode.com/wp-content/uploads/2010/06/hadoop_run.jpg" alt="hadoop_run" width="671" height="551" /></p>
<p>从上图可以看到，MapReduce运行过程中涉及有4个独立的实体：</p>
<ul>
<li>Client，用于提交MapReduce job。</li>
<li>JobTracker，负责协调job的运行。</li>
<li>TaskTrackers，运行 job分解后的多个tasks，task主要是负责运行Mapper和Reducer。</li>
<li>Distributed filesystem，用于存储上述实体运行时共享的job文件（如中间结果文件）。</li>
</ul>
<h3>Job Submission</h3>
<p>当调用了JobClient.runJob()之后，Job便开始被提交了，在Job提交这个步骤中，经历了以下的过程：</p>
<ol>
<li>Client向JobTacker申请一个新的job ID（step 2），job ID形如job_200904110811_0002的格式，是由JobTracker运行当前的job的时间和一个由JobTracker维护的自增计数（从1开始）组成的。</li>
<li>检查job的output specification，比如输出目录是否已经存在（存在则抛异常）、是否有权限写等等。</li>
<li>Computes the input splits for the job，这些input splits就是作为Mapper的输入。</li>
<li>Copies the resources needed to run the job, including the job JAR file, the configuration file and the computed input splits, to the jobtracker's filesystem in a direcotry named after the job ID（step 3）。</li>
<li>Tells the jobtracker that the job is ready for execution（step 4）。</li>
</ol>
<h3>Job Initialization</h3>
<p>当JobTracker收到Job提交的请求后，将job保存在一个内部队列，并让Job Scheduler处理并初始化。初始化涉及到创建一个封装了其tasks的job对象，并保持对task的状态和进度的根据（step 5）。当创建要运行的一系列task对象后，Job Scheduler首先开始从文件系统中获取由JobClient计算的input splits（step 6），然后再为每个split创建map task。</p>
<h3>Task Assignment</h3>
<p>TaskTrackers会使用一个简单的loop为定期向JobTracker发送heartbeat调用，发送的间隔时间大约5秒，一般取决于集群服务器的规模和繁忙程度以及网络拥挤程度。这个heartbeat一方面是告知JobTracker当前TaskTracker处于live状态，同时是用于JobTracker和TaskTracker进行通信，TaskTracker会根据heartbeat的返回值来执行一定的操作（step 7）。</p>
<p>To choose a reduce task the JobTracker simply takes the next in its list of yet-to-be-run reduce tasks, since there are no data locality considerations. For a map task, however, it takes account of the TaskTracker's network location and picks a task whose input splits is as close as possible to the tasktracker. In the optimal case, the task is data-local, that is , running on the same node that the split resides on. Alternatively, the task may be rack-local: on the same rack, but not the same node, as the split.</p>
<h3>Task Execution</h3>
<p>当TaskTrack被分配到一个task之后，接下来就是运行这个task。首先，它会需要的job JAR文件从shared filesystem拷贝到local filesystem，然后创建一个working direcotry并un-jars拷贝的JAR文件到该directory，最后就创建一个TaskRunner对象运行task。</p>
<p>TaskRunner在运行的时候是启动了一个新的JVM来run each task（step 10），这样是为了防止在用户自定义的Mapper出现异常令JVM挂了，从而连累到TaskTracker。TaskRunner子进程会使用umbilical接口和TaskTracker通信并每隔几秒向TaskTracker汇报进度。</p>
<p>对于使用Streaming和Pipes方式来创建的Mapper，也是作为TaskTracker的子进程来运行的。Streaming是使用标准输入输出来通信，而Pipes是使用socket来进行通信，如下图：</p>
<p><img class="aligncenter size-full wp-image-944" title="streamming_pipes" src="http://blog.endlesscode.com/wp-content/uploads/2010/06/streamming_pipes.jpg" alt="streamming_pipes" width="627" height="606" /></p>
<h3>Progress and Status Updates</h3>
<p>进度和状态是通过heartbeat来更新和维护的。来对于Map Task，进度就是已处理数据和所有输入数据的比例。对于Reduce Task，情况就有点复杂，包括3部分，拷贝中间结果文件、排序、Reduce调用，每部分占1/3。</p>
<h3>Job Completion</h3>
<p>当Job完成后，JobTracker会收一个Job Complete的通知，并将当前的Job状态更新为Successful，同时JobClient也会轮循获知提交的Job已经完成，将信息显示给用户。最后，JobTracker会清理和回收该Job的相关资源，并通知TaskTracker进行相同的操作（比如删除中间结果文件）。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/06/24/how-mapreduce-works/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HDFS简介</title>
		<link>http://blog.endlesscode.com/2010/06/16/hdfs-short-intro/</link>
		<comments>http://blog.endlesscode.com/2010/06/16/hdfs-short-intro/#comments</comments>
		<pubDate>Wed, 16 Jun 2010 07:21:02 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[Hadoop]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=915</guid>
		<description><![CDATA[一、HDFS
HDFS全称是Hadoop Distributed System。HDFS是为以流的方式存取大文件而设计的。适用于几百MB，GB以及TB，并写一次读多次的场合。而对于低延时数据访问、大量小文件、同时写和任意的文件修改，则并不是十分适合。
目前HDFS支持的使用接口除了Java的还有，Thrift、C、FUSE、WebDAV、HTTP等。HDFS是以block-sized chunk组织其文件内容的，默认的block大小为64MB，对于不足64MB的文件，其会占用一个block，但实际上不用占用实际硬盘上的64MB，这可以说是HDFS是在文件系统之上架设的一个中间层。之所以将默认的block大小设置为64MB这么大，是因为block-sized对于文件定位很有帮助，同时大文件更使传输的时间远大于文件寻找的时间，这样可以最大化地减少文件定位的时间在整个文件获取总时间中的比例 。
构成HDFS主要是Namenode（master）和一系列的Datanode（workers）。Namenode是管理HDFS的目录树和相关的文件元数据，这些信息是以"namespace image"和"edit log"两个文件形式存放在本地磁盘，但是这些文件是在HDFS每次重启的时候重新构造出来的。Datanode则是存取文件实际内容的节点，Datanodes会定时地将block的列表汇报给Namenode。
由于Namenode是元数据存放的节点，如果Namenode挂了那么HDFS就没法正常运行，因此一般使用将元数据持久存储在本地或远程的机器上，或者使用secondary namenode来定期同步Namenode的元数据信息，secondary namenode有点类似于MySQL的Master/Salves中的Slave，"edit log"就类似"bin log"。如果Namenode出现了故障，一般会将原Namenode中持久化的元数据拷贝到secondary namenode中，使secondary namenode作为新的Namenode运行起来。
二、读写流程
文件读取

文件读取的过程如下：

使用HDFS提供的客户端开发库，向远程的Namenode发起RPC请求；
Namenode会视情况返回文件的部分或者全部block列表，对于每个block，Namenode都会返回有该block拷贝的datanode地址；
客户端开发库会选取离客户端最接近的datanode来读取block；
读取完当前block的数据后，关闭与当前的datanode连接，并为读取下一个block寻找最佳的datanode；
当读完列表的block后，且文件读取还没有结束，客户端开发库会继续向Namenode获取下一批的block列表。
读取完一个block都会进行checksum验证，如果读取datanode时出现错误，客户端会通知Namenode，然后再从下一个拥有该block拷贝的datanode继续读。


写入文件

写入文件的过程比读取较为复杂：

使用HDFS提供的客户端开发库，向远程的Namenode发起RPC请求；
Namenode会检查要创建的文件是否已经存在，创建者是否有权限进行操作，成功则会为文件创建一个记录，否则会让客户端抛出异常；
当客户端开始写入文件的时候，开发库会将文件切分成多个packets，并在内部以"data queue"的形式管理这些packets，并向Namenode申请新的blocks，获取用来存储replicas的合适的datanodes列表，列表的大小根据在Namenode中对replication的设置而定。
开始以pipeline（管道）的形式将packet写入所有的replicas中。开发库把packet以流的方式写入第一个datanode，该datanode把该packet存储之后，再将其传递给在此pipeline中的下一个datanode，直到最后一个datanode，这种写数据的方式呈流水线的形式。
最后一个datanode成功存储之后会返回一个ack packet，在pipeline里传递至客户端，在客户端的开发库内部维护着"ack queue"，成功收到datanode返回的ack packet后会从"ack queue"移除相应的packet。
如果传输过程中，有某个datanode出现了故障，那么当前的pipeline会被关闭，出现故障的datanode会从当前的pipeline中移除，剩余的block会继续剩下的datanode中继续以pipeline的形式传输，同时Namenode会分配一个新的datanode，保持replicas设定的数量。

]]></description>
			<content:encoded><![CDATA[<h2>一、HDFS</h2>
<p>HDFS全称是Hadoop Distributed System。HDFS是为以流的方式存取大文件而设计的。适用于几百MB，GB以及TB，并写一次读多次的场合。而对于低延时数据访问、大量小文件、同时写和任意的文件修改，则并不是十分适合。</p>
<p>目前HDFS支持的使用接口除了Java的还有，Thrift、C、FUSE、WebDAV、HTTP等。HDFS是以block-sized chunk组织其文件内容的，默认的block大小为64MB，对于不足64MB的文件，其会占用一个block，但实际上不用占用实际硬盘上的64MB，这可以说是HDFS是在文件系统之上架设的一个中间层。之所以将默认的block大小设置为64MB这么大，是因为block-sized对于文件定位很有帮助，同时大文件更使传输的时间远大于文件寻找的时间，这样可以最大化地减少文件定位的时间在整个文件获取总时间中的比例 。</p>
<p>构成HDFS主要是Namenode（master）和一系列的Datanode（workers）。Namenode是管理HDFS的目录树和相关的文件元数据，这些信息是以"namespace image"和"edit log"两个文件形式存放在本地磁盘，但是这些文件是在HDFS每次重启的时候重新构造出来的。Datanode则是存取文件实际内容的节点，Datanodes会定时地将block的列表汇报给Namenode。</p>
<p>由于Namenode是元数据存放的节点，如果Namenode挂了那么HDFS就没法正常运行，因此一般使用将元数据持久存储在本地或远程的机器上，或者使用secondary namenode来定期同步Namenode的元数据信息，secondary namenode有点类似于MySQL的Master/Salves中的Slave，"edit log"就类似"bin log"。如果Namenode出现了故障，一般会将原Namenode中持久化的元数据拷贝到secondary namenode中，使secondary namenode作为新的Namenode运行起来。<span id="more-915"></span></p>
<h2>二、读写流程</h2>
<h3>文件读取</h3>
<p style="text-align: center;"><img class="aligncenter size-full wp-image-919" title="reading data from hdfs" src="http://blog.endlesscode.com/wp-content/uploads/2010/06/reading-data-from-hdfs.png" alt="reading data from hdfs" width="552" height="308" /></p>
<p>文件读取的过程如下：</p>
<ol>
<li>使用HDFS提供的客户端开发库，向远程的Namenode发起RPC请求；</li>
<li>Namenode会视情况返回文件的部分或者全部block列表，对于每个block，Namenode都会返回有该block拷贝的datanode地址；</li>
<li>客户端开发库会选取离客户端最接近的datanode来读取block；</li>
<li>读取完当前block的数据后，关闭与当前的datanode连接，并为读取下一个block寻找最佳的datanode；</li>
<li>当读完列表的block后，且文件读取还没有结束，客户端开发库会继续向Namenode获取下一批的block列表。</li>
<li>读取完一个block都会进行checksum验证，如果读取datanode时出现错误，客户端会通知Namenode，然后再从下一个拥有该block拷贝的datanode继续读。</li>
</ol>
<p style="text-align: center;">
<h3>写入文件</h3>
<p style="text-align: center;"><img class="aligncenter size-full wp-image-922" title="writing data to hdfs" src="http://blog.endlesscode.com/wp-content/uploads/2010/06/writing-data-to-hdfs.png" alt="writing data to hdfs" width="524" height="321" /></p>
<p>写入文件的过程比读取较为复杂：</p>
<ol>
<li>使用HDFS提供的客户端开发库，向远程的Namenode发起RPC请求；</li>
<li>Namenode会检查要创建的文件是否已经存在，创建者是否有权限进行操作，成功则会为文件创建一个记录，否则会让客户端抛出异常；</li>
<li>当客户端开始写入文件的时候，开发库会将文件切分成多个packets，并在内部以"data queue"的形式管理这些packets，并向Namenode申请新的blocks，获取用来存储replicas的合适的datanodes列表，列表的大小根据在Namenode中对replication的设置而定。</li>
<li>开始以pipeline（管道）的形式将packet写入所有的replicas中。开发库把packet以流的方式写入第一个datanode，该datanode把该packet存储之后，再将其传递给在此pipeline中的下一个datanode，直到最后一个datanode，这种写数据的方式呈流水线的形式。</li>
<li>最后一个datanode成功存储之后会返回一个ack packet，在pipeline里传递至客户端，在客户端的开发库内部维护着"ack queue"，成功收到datanode返回的ack packet后会从"ack queue"移除相应的packet。</li>
<li>如果传输过程中，有某个datanode出现了故障，那么当前的pipeline会被关闭，出现故障的datanode会从当前的pipeline中移除，剩余的block会继续剩下的datanode中继续以pipeline的形式传输，同时Namenode会分配一个新的datanode，保持replicas设定的数量。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/06/16/hdfs-short-intro/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>简单的Streaming和Pipes示例</title>
		<link>http://blog.endlesscode.com/2010/06/16/simple-demo-of-streaming-and-pipes/</link>
		<comments>http://blog.endlesscode.com/2010/06/16/simple-demo-of-streaming-and-pipes/#comments</comments>
		<pubDate>Wed, 16 Jun 2010 07:20:27 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[Hadoop]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=901</guid>
		<description><![CDATA[一、Hadoop Streaming
Streaming是Hadoop提供的一个可以使用其他编程语言来进行MapReduce来的API，因为Hadoop是基于Java（由于作者比较擅长Java，Lucene和Nutch都是出于Hadoop的作者）。Hadoop Streaming并不复杂，其只是使用了Unix的标准输入输出作为Hadoop和其他编程语言的开发接口，因此在其他的编程语言所写的程序中，只需要将标准输入作为程序的输入，将标准输出作为程序的输出就可以了。
在标准的输入输出中，key和value是以tab作为分隔符，并且在reduce的标准输入中，hadoop框架保证了输入的数据是经过了按key排序的。
下面的示例是用Python重写了上一个示例：
# max_temperature_map.py
#!/usr/bin/env python
import sys
for line in sys.stdin:
 val = line.strip().split()
 # 分隔年份和温度值，输出到标准输出
 print "%s\t%s"%(val[0], val[1])

# max_temperature_reduce.py
#!/usr/bin/env python
import sys
(last_key, max_val) = (None, 0)
for line in sys.stdin:
 (key, temp) = line.strip().split('\t')
 if last_key and last_key != key:
 print "%s\t%s" % (last_key, max_val)
 (last_key, max_val) = (key, int(temp))
 else:
 (last_key, max_val) = (key, max(max_val, int(temp)))

if last_key:
 [...]]]></description>
			<content:encoded><![CDATA[<h3>一、Hadoop Streaming</h3>
<p>Streaming是Hadoop提供的一个可以使用其他编程语言来进行MapReduce来的API，因为Hadoop是基于Java（由于作者比较擅长Java，Lucene和Nutch都是出于Hadoop的作者）。Hadoop Streaming并不复杂，其只是使用了Unix的标准输入输出作为Hadoop和其他编程语言的开发接口，因此在其他的编程语言所写的程序中，只需要将标准输入作为程序的输入，将标准输出作为程序的输出就可以了。</p>
<p>在标准的输入输出中，key和value是以tab作为分隔符，并且在reduce的标准输入中，hadoop框架保证了输入的数据是经过了按key排序的。</p>
<p>下面的示例是用Python重写了上一个示例：</p>
<pre class="brush:python"># max_temperature_map.py
#!/usr/bin/env python
import sys
for line in sys.stdin:
 val = line.strip().split()
 # 分隔年份和温度值，输出到标准输出
 print "%s\t%s"%(val[0], val[1])

# max_temperature_reduce.py
#!/usr/bin/env python
import sys
(last_key, max_val) = (None, 0)
for line in sys.stdin:
 (key, temp) = line.strip().split('\t')
 if last_key and last_key != key:
 print "%s\t%s" % (last_key, max_val)
 (last_key, max_val) = (key, int(temp))
 else:
 (last_key, max_val) = (key, max(max_val, int(temp)))

if last_key:
 print "%s\t%s" % (last_key, max_val)</pre>
<p><span id="more-901"></span>"if last_key and last_key != key"这一行命令主要因为当已经完成了某(key, value[])对的处理后直接输出，然后再重置(last_key, max_val)进行新的(key, value[])处理。因为reduce的输入是经过了排序的，因此"if last_key and last_key != key"是处理某key对应的value列表。</p>
<p>执行命令：</p>
<pre class="brush:bash">hadoop jar /home/stephenchan/hadoop-0.20.2/contrib/streaming/hadoop-0.20.2-streaming.jar \
-input sample.txt \
-output pyoutput \
-mapper max_temperature_map.py \
-reducer max_temperature_reduce.py
# 不用MapReduce其实也相当于下面的命令：
# cat sample.txt | python max_temperature_map.py | sort | python max_temperature_reduce.py &gt; pyoutput.txt</pre>
<h3>二、Hadoop Pipes</h3>
<p>Hadoop Pipes是Hadoop MapReduce的C++接口。与使用标准输入输出的Hadoop Streaming不同（当然Streaming也可以用于C++），Hadoop Pipes在tasktacker和map/reduce进行通信时使用的socket作为管道，不是标准输入输出，而不是JNI。</p>
<p>Hadoop Pipes不能运行在standalone模式下，所以要先配置成pseudo-distributed模式，因为Hadoop Pipes依赖于Hadoop的分布式缓存技术，而分布式缓存只会在HDFS运行的时候才会支持。</p>
<p>与Java的接口不一样，Hadoop Pipes的key和value都是基于STL的string，因此在处理时开发人员需要手动地进行数据类型的转换。</p>
<p>C++示例代码：</p>
<pre class="brush:c++">/* max_temperature.cpp */
#include &lt;algorithm&gt;
#include &lt;limits&gt;
#include &lt;string&gt;

#include "hadoop/Pipes.hh"
#include "hadoop/TemplateFactory.hh"
#include "hadoop/StringUtils.hh"

class MaxTemperatureMapper : public HadoopPipes::Mapper {
 public:
 MaxTemperatureMapper(HadoopPipes::TaskContext&amp; context) {
 }

 void map(HadoopPipes::MapContext&amp; context) {
 std::string line = context.getInputValue();
 std::string year = line.substr(0, 4);
 std::string temp = line.substr(5, 3);
 context.emit(year, temp);
 }
};

class MaxTemperatureReducer : public HadoopPipes::Reducer {
 public:
 MaxTemperatureReducer(HadoopPipes::TaskContext&amp; context) {
 }

 void reduce(HadoopPipes::ReduceContext&amp; context) {
 int maxValue = 0;
 while (context.nextValue()) {
 maxValue = std::max(maxValue, HadoopUtils::toInt(context.getInputValue()));
 }
 context.emit(context.getInputKey(), HadoopUtils::toString(maxValue));
 }
};

int main(int argc, char *argv[]) {
 return HadoopPipes::runTask(HadoopPipes::TemplateFactory&lt;MaxTemperatureMapper, MaxTemperatureReducer&gt;());
}</pre>
<p>makefile代码：</p>
<pre class="brush:plain">PLATFORM=Linux-i386-32
CC = g++
CPPFLAGS = -m32 -I$(HADOOP_INSTALL)/c++/$(PLATFORM)/include

max_temperature: max_temperature.cpp
 $(CC) $(CPPFLAGS) $&lt; -Wall -L$(HADOOP_INSTALL)/c++/$(PLATFORM)/lib -lhadooppipes \
 -lhadooputils -lpthread -g -O2 -o $@</pre>
<p>在使用make命令生成了max_temperature可执行文件之后，就要使用"hadoop fs"将可执行文件和示例数据文件拷贝到伪dfs中去。</p>
<pre class="brush:bash">hadoop fs -put max_temperature bin/max_temperature
hadoop fs -put sample.txt temp_sample.txt
hadoop pipes \
-D hadoop.pipes.java.recordreader=true \
-D hadoop.pipes.java.recordwriter=true \
-input temp_sample.txt \
-output cppoutput \
-program bin/max_temperature
# 运行的结果也是保存在伪dfs中，使用"hadoop fs"来查看
hadoop fs -cat cppoutput/part*</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/06/16/simple-demo-of-streaming-and-pipes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>简单的MapReduce示例(Java)</title>
		<link>http://blog.endlesscode.com/2010/06/16/simple-demo-of-mapreduce-in-java/</link>
		<comments>http://blog.endlesscode.com/2010/06/16/simple-demo-of-mapreduce-in-java/#comments</comments>
		<pubDate>Wed, 16 Jun 2010 07:19:58 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[Hadoop]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=891</guid>
		<description><![CDATA[Hadoop的三种模式以及相应的安装细节Google上一大把，其实都基本上是一些参数的设置。
下面自己在.bashrc文件中的设置：
# setting in .bashrc
export HADOOP_INSTALL=/home/stephenchan/hadoop-0.20.2
export CLASSPATH=.:$HADOOP_INSTALL/hadoop-0.20.2-core.jar:$HADOOP_INSTALL/lib:$CLASSPATH
export PATH=$PATH:$HADOOP_INSTALL/bin
样例的代码是来自＜Hadoop : The Definitive Guide&#62;一书，由于没有拿到天气的数据，所以自己用代码生成了一个类似的样例数据，只有年份和温度，用MapReduce来取每年的最高温度，但我省去了日期中的天，保留了年。代码在standalone模式下运行即可，官方的参考文档 ： http://hadoop.apache.org/common/docs/r0.20.2/cn/。目前Hadoop只在Linux下完全支持商用运行，在Unix和Windows只支持用来开发。
生成示例数据的Python代码（生成后再手动给三个年份设置最大的temp值（如999）来观察结果的正常与否）：
#!/usr/bin/env python

import string
import random
import hashlib
random_chars = string.digits
year_list = [1990, 2000, 2010]
for idx in range(1, 1000):
 year = year_list[idx%3]
 temp  = random.sample(random_chars, 3)
 s = "%s %s" % (year, "".join(temp))
 print s
MaxTemperature.java的源代码：
/* MaxTemperature.java */
import java.io.IOException;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class MaxTemperature {

 [...]]]></description>
			<content:encoded><![CDATA[<p>Hadoop的三种模式以及相应的安装细节Google上一大把，其实都基本上是一些参数的设置。</p>
<p>下面自己在.bashrc文件中的设置：</p>
<pre class="brush:bash"># setting in .bashrc
export HADOOP_INSTALL=/home/stephenchan/hadoop-0.20.2
export CLASSPATH=.:$HADOOP_INSTALL/hadoop-0.20.2-core.jar:$HADOOP_INSTALL/lib:$CLASSPATH
export PATH=$PATH:$HADOOP_INSTALL/bin</pre>
<p>样例的代码是来自＜Hadoop : The Definitive Guide&gt;一书，由于没有拿到天气的数据，所以自己用代码生成了一个类似的样例数据，只有年份和温度，用MapReduce来取每年的最高温度，但我省去了日期中的天，保留了年。代码在standalone模式下运行即可，官方的参考文档 ： <a href="http://hadoop.apache.org/common/docs/r0.20.2/cn/">http://hadoop.apache.org/common/docs/r0.20.2/cn/</a>。目前Hadoop只在Linux下完全支持商用运行，在Unix和Windows只支持用来开发。</p>
<p>生成示例数据的Python代码（生成后再手动给三个年份设置最大的temp值（如999）来观察结果的正常与否）：</p>
<pre class="brush:python">#!/usr/bin/env python

import string
import random
import hashlib
random_chars = string.digits
year_list = [1990, 2000, 2010]
for idx in range(1, 1000):
 year = year_list[idx%3]
 temp  = random.sample(random_chars, 3)
 s = "%s %s" % (year, "".join(temp))
 print s</pre>
<p><span id="more-891"></span>MaxTemperature.java的源代码：</p>
<pre class="brush:java">/* MaxTemperature.java */
import java.io.IOException;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class MaxTemperature {

 static class MaxTemperatureMapper extends Mapper&lt;LongWritable, Text, Text, IntWritable&gt; {

 public void map(LongWritable key, Text value, Context context)
 throws IOException, InterruptedException {

 String line = value.toString();
 // 从每行的数据中分解出年和温度
 String year = line.substring(0, 4);
 int airTemperature = Integer.parseInt(line.substring(5, 8));
 context.write(new Text(year), new IntWritable(airTemperature));
 }
 } 

 static class MaxTemperatureReducer extends Reducer&lt;Text, IntWritable, Text, IntWritable&gt; {

 public void reduce(Text key, Iterable&lt;IntWritable&gt; values, Context context)
 throws IOException, InterruptedException {

 int maxValue = Integer.MIN_VALUE;
 // key为年份, values为该年每天的温度，求出最大的温度值
 for (IntWritable value : values) {
 maxValue = Math.max(maxValue, value.get());
 }
 context.write(key, new IntWritable(maxValue));
 }
 }

 public static void main(String[] args) throws Exception {
 if(args.length !=2) {
 System.err.println("Usage: MaxTemperature &lt;input path&gt; &lt;output path&gt;");
 System.exit(-1);
 }

 Job job = new Job();
 job.setJarByClass(MaxTemperature.class);

 FileInputFormat.addInputPath(job, new Path(args[0]));
 FileOutputFormat.setOutputPath(job, new Path(args[1]));

 job.setMapperClass(MaxTemperatureMapper.class);
 job.setReducerClass(MaxTemperatureReducer.class);

 job.setOutputKeyClass(Text.class);
 job.setOutputValueClass(IntWritable.class);

 System.exit(job.waitForCompletion(true)?0:1);
 }
}</pre>
<p>执行的命令如下，虽然在Hadoop的命令文档中有提供"hadoop CLASSNAME"这样的命令，但我折腾了很久都一直提示"NoClassDefFoundError"，因此只能打包成jar然后再用"hadoop jar"命令运行。</p>
<pre class="brush:bash">mkdir MaxTemperature
javac -d MaxTemperature MaxTemperature.java
jar cvf MaxTemperature.jar -C MaxTemperature/ .
hadoop jar MaxTemperature.jar MaxTemperature sample.txt output</pre>
<p>结果的输出会在output的目录下。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/06/16/simple-demo-of-mapreduce-in-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>动态链接</title>
		<link>http://blog.endlesscode.com/2010/06/07/dynamic-linkage/</link>
		<comments>http://blog.endlesscode.com/2010/06/07/dynamic-linkage/#comments</comments>
		<pubDate>Mon, 07 Jun 2010 11:44:54 +0000</pubDate>
		<dc:creator>Stephen</dc:creator>
				<category><![CDATA[Operating System]]></category>

		<guid isPermaLink="false">http://blog.endlesscode.com/?p=812</guid>
		<description><![CDATA[动态链接的基本思想是把程序按照模块拆分成各个相对独立的部分，在程序运行时才将它们链接在一起形成一个完整的程序，而不是像静态链接那样把所有的程序模块都链接成一个单独的可执行文件。动态链接涉及运行时的链接及多个文件的装载，必需要有操作系统的支持，因为动态链接的情况下，进程的虚拟地址空间的分布会比静态链接情况下更为复杂，还有一些存储管理、内存共享、进程线程等机制在动态链接下也会有一些微妙的变化。在Linux系统中，ELF动态链接文件被称为“动态共享对象（DSO，Dynamic Shared Objects），简称共享对象，它们一般都是以“.so”为扩展名的一些文件，而在Windows系统中，动态链接文件被称为动态链接库（Dynamical Linking Library），即平时见到的以“.dll”为扩展名的文件。
动态链接库的特点与优势
把函数库推迟到程序运行时加载的好处有几个：

可以实现进程之间的资源共享。就是说，某个程序的在运行中要调用某个动态链接库函数的时候，操作系统首先会查看所有正在运行的程序，看在内存里是否已有此库函数的拷贝。如果有，则让其共享那一拷贝（共享代码段，数据码各自独立一份）；只有没有才链接载入。这种模式虽然会带来一些”动态链接“额外的开销，但却大大地节省了系统的内存资源，通过一定的优化，与静态链接相比，性能损失大约在5％以下。
程序升级变得简单。用户只需要升级动态链接库，而无需像静态链接那样重新编译其他原有的代码就可以完成整个程序的升级。
可以使链接载入由程序员在程序代码中控制，如dlopen、dlsym、dlclose等等。

装载重定位
静态链接是通过在链接过程对所有目标文件进行重定位的。而对于动态链接库而言，在链接时，可执行文件对所有绝对地址的引用不作重定位，而把这一步推迟到装载时再完成。一旦模块装载地址确定，即目标地址确定，那么系统对程序中所有的绝对地址引用进行重定位。负责完成这部分工作的是动态链接器（Dynamic Loader，其实也是一个共享对象，ld.so）。
动态链接模块被装载遇到至虚拟空间之后，指令部分是在多个进程之间共享的，由于装载时重定位的方法需要修改指令，所以没有办法做到同一份指令被多个进程共享，因为指令被重定位后对于每个进程来讲是不同的。当然，动态链接库中的可修改数据部分对于不同的进程来说有多个副本，所以它们可以采用装载时重定位的方法来解决。
因此，地址无关代码（PIC，Position-independent Code）就在这种需求的前提产生了。这种方案就是把指令中那些需要被修改的部分分离出来，跟数据部分放在一起，这样指令部分就可以保持不变，而数据部分可以在每个进程中拥有一个副本。
可以按是否跨模块和引用方式分为4类地址引用类型：
/* a.elf */
static int a;
extern int b;
extern void ext();

void bar()
{
    a = 1;  //类型2 模块内数据访问
    b = 2;  //类型3 模块间数据访问
}

void foo()
{
    bar();  //类型1 模块内调用或跳转
    ext();  //类型4 模块间调用或跳转
}
类型一 模块内部调用或跳转
对于现代的操作系统来讲，模块内部的跳转、函数调用都可以是相对地址调用，或者是基于寄存器相对调用，所以对于这种指令是不需要重定位的，这点从静态链接时的模块内部重定位可以看出。
类型二 模块内部数据访问
一个模块前面一般是若干个页的代码，后面紧跟着若干个页的代码，这些页之间的相对位置是固定的，也就是说，任何一条指令与它需要访问的模块内部数据之间的相对位置是固定的，那么只需要对于当前指令的地址加上固定的偏移量就可以访问模块内部数据了。现代的体系结构中，数据的相对寻址往往没有相对于当前指令地址（PC）的寻址方式，所以ELF用了一个很巧妙的方法来得到当前的PC值，然后再加上一个偏移量就可以达到访问相应变量的目的了。
其实就是在代码中调用了一个函数（__i686.get_pc_thunk.cx），在这个函数中把esp（堆栈）中存放的返回地址存到某个寄存器中，然后再原来的代码中从寄存器（此处为ecx）中取出来。反汇编出来的结果如下：
000004a7 &#60;__i686 .get_pc_thunk.cx&#62;:
 4a7:	8b 1c [...]]]></description>
			<content:encoded><![CDATA[<p>动态链接的基本思想是把程序按照模块拆分成各个相对独立的部分，在程序运行时才将它们链接在一起形成一个完整的程序，而不是像静态链接那样把所有的程序模块都链接成一个单独的可执行文件。动态链接涉及运行时的链接及多个文件的装载，必需要有操作系统的支持，因为动态链接的情况下，进程的虚拟地址空间的分布会比静态链接情况下更为复杂，还有一些存储管理、内存共享、进程线程等机制在动态链接下也会有一些微妙的变化。在Linux系统中，ELF动态链接文件被称为“动态共享对象（DSO，Dynamic Shared Objects），简称共享对象，它们一般都是以“.so”为扩展名的一些文件，而在Windows系统中，动态链接文件被称为动态链接库（Dynamical Linking Library），即平时见到的以“.dll”为扩展名的文件。</p>
<h4>动态链接库的特点与优势</h4>
<p>把函数库推迟到程序运行时加载的好处有几个：</p>
<ul>
<li>可以实现进程之间的资源共享。就是说，某个程序的在运行中要调用某个动态链接库函数的时候，操作系统首先会查看所有正在运行的程序，看在内存里是否已有此库函数的拷贝。如果有，则让其共享那一拷贝（共享代码段，数据码各自独立一份）；只有没有才链接载入。这种模式虽然会带来一些”动态链接“额外的开销，但却大大地节省了系统的内存资源，通过一定的优化，与静态链接相比，性能损失大约在5％以下。</li>
<li>程序升级变得简单。用户只需要升级动态链接库，而无需像静态链接那样重新编译其他原有的代码就可以完成整个程序的升级。</li>
<li>可以使链接载入由程序员在程序代码中控制，如dlopen、dlsym、dlclose等等。<span id="more-812"></span></li>
</ul>
<h4>装载重定位</h4>
<p>静态链接是通过在链接过程对所有目标文件进行重定位的。而对于动态链接库而言，在链接时，可执行文件对所有绝对地址的引用不作重定位，而把这一步推迟到装载时再完成。一旦模块装载地址确定，即目标地址确定，那么系统对程序中所有的绝对地址引用进行重定位。负责完成这部分工作的是动态链接器（Dynamic Loader，其实也是一个共享对象，ld.so）。</p>
<p>动态链接模块被装载遇到至虚拟空间之后，指令部分是在多个进程之间共享的，由于装载时重定位的方法需要修改指令，所以没有办法做到同一份指令被多个进程共享，因为指令被重定位后对于每个进程来讲是不同的。当然，动态链接库中的可修改数据部分对于不同的进程来说有多个副本，所以它们可以采用装载时重定位的方法来解决。</p>
<p>因此，地址无关代码（PIC，Position-independent Code）就在这种需求的前提产生了。这种方案就是把指令中那些需要被修改的部分分离出来，跟数据部分放在一起，这样指令部分就可以保持不变，而数据部分可以在每个进程中拥有一个副本。</p>
<p>可以按是否跨模块和引用方式分为4类地址引用类型：</p>
<pre class="brush:c++">/* a.elf */
static int a;
extern int b;
extern void ext();

void bar()
{
    a = 1;  //类型2 模块内数据访问
    b = 2;  //类型3 模块间数据访问
}

void foo()
{
    bar();  //类型1 模块内调用或跳转
    ext();  //类型4 模块间调用或跳转
}</pre>
<p><strong>类型一 模块内部调用或跳转</strong></p>
<p>对于现代的操作系统来讲，模块内部的跳转、函数调用都可以是相对地址调用，或者是基于寄存器相对调用，所以对于这种指令是不需要重定位的，这点从静态链接时的模块内部重定位可以看出。</p>
<p><strong>类型二 模块内部数据访问</strong></p>
<p>一个模块前面一般是若干个页的代码，后面紧跟着若干个页的代码，这些页之间的相对位置是固定的，也就是说，任何一条指令与它需要访问的模块内部数据之间的相对位置是固定的，那么只需要对于<strong>当前指令的地址</strong>加上<strong>固定的偏移量</strong>就可以访问模块内部数据了。现代的体系结构中，数据的相对寻址往往没有相对于当前指令地址（PC）的寻址方式，所以ELF用了一个很巧妙的方法来得到当前的PC值，然后再加上一个偏移量就可以达到访问相应变量的目的了。</p>
<p>其实就是在代码中调用了一个函数（__i686.get_pc_thunk.cx），在这个函数中把esp（堆栈）中存放的返回地址存到某个寄存器中，然后再原来的代码中从寄存器（此处为ecx）中取出来。反汇编出来的结果如下：</p>
<pre class="brush:plain">000004a7 &lt;__i686 .get_pc_thunk.cx&gt;:
 4a7:	8b 1c 24          mov    (%esp),%ecx
 4aa:	c3                   	ret
 4ab:	90                   	nop
<!--__i686--></pre>
<p><strong>类型三 模块间数据访问</strong></p>
<p>模块间的数据访问比模块内部稍微麻烦，因为模块间的数据访问目标地址要等到装载时才决定的。比如上面的变量b，它被定义在其他模块中，并且该地址在装载时才能确定，因此，这时就需要用到了在"a.elf"文件的数据段里面建立一个指向这些变量的指针数组（注意是在"a.elf"的数据段），也被称为全局偏移表（GOT，Global Offset Table）。</p>
<p>全局偏移表是将地址无关代码（PIC）通过计算转换成相应的绝对地址。当代码需要引用该全局变量时，可以通过GOT中相对应的项间接引用来获取到绝对地址。而在重定位项的类型是R_386_GLOB_DAT，其实就是对全局偏移表的引用。</p>
<p>在指令要访问变量b时，程序会先找到当前目标文件的GOT，然后根据GOT中变量所对应的项找到变量的绝对地址。每个变量都对应一个4个字节的地址，链接器在装载模块的时候会查找每个变量的绝对地址，然后填充GOT中的各个项，以确保每个指针所指向的地址正确。由于GOT本身是放在数据段的，所以它可以在模块装载时被修改，并且每个进程都可以有独立的副本，相互不受影响。</p>
<p>将上面的代码编译成so文件，可以看到got的属性是一个数据段：</p>
<pre class="brush:plain">pic.so:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
 18 .got          00000010  00001fe4  00001fe4  00000fe4  2**2
                      CONTENTS, ALLOC, LOAD, DATA</pre>
<p>在动态链接器将控制权交给进程映像中任何的代码之前，会处理所有的全局偏移表重定位项，确保执行过程中绝对地址信息可用。这个过程可以大概描述如下：</p>
<ol>
<li>动态链接器开始处理重定位项，处理到全局变量b。</li>
<li>从重定位表项中的r_info低8位判断重定位项的类型是R_386_GLOB_DAT，从r_info的高24位获取到重定位入口的符号表符号表的位置。</li>
<li>从重定位表项中的r_offset中获取到了全局变量b在"a.elf"全局偏移表GOT的位置。</li>
<li>根据从r_info的高24位获取到的下标在全局符号表中获取到了全局变量b的地址，经过重定位计算后将b的绝对地址更新到步骤3中得知的GOT位置。</li>
<li>当在"a.elf"中的代码指令要访问b时，程序在编译的时候已经在访问b的位置像类型一那样安插一段代码，也像类型一那样通过当前的PC值加上一偏移量计算出b在GOT中的位置，然后再从该位置获取到b的绝对地址。</li>
</ol>
<p><strong>类型四 模块间调用或跳转</strong></p>
<p>这种完全可以使用类型三的方法可以解决，但是使用这种方法会使性能存在一些问题。</p>
<p>至于共享模块的全局变量冲突问题，ELF共享库在编译时，默认都把定义在模块内部的全局变量当作定义在其他模块的全局变量，也就是说当共享模块被装载时，如果某个全局变量在可执行文件中拥有副本，那么动态链接器就会把GOT中的相应地址指向该副本，这样该变量在运行时实际上最终就只有一个实例。</p>
<p>而对于取地址这种重定位入口：</p>
<pre class="brush:c">static int a;
static int* p = &amp;a;</pre>
<p>实际上这样在数据段中绝对地址的引用，相应地对于p的重定位项类型为"R_386_RELATIVE"。</p>
<h4>延迟绑定</h4>
<p>动态链接的确但静态链接灵活很多，但是同时也比静态链接慢，因为动态链接下的模块间变量访问和函数调用都要进行间接处理，而且在程序启动的时候要寻找并装载所需要的共享对象，并进行符号查找和地址重定位等工作势必减慢程序的启动速度。因此，ELF采用了一种叫延迟绑定（Lazy Binding）的做法，基本的思想就是当函数第一次被用到时才进行绑定（包括符号查找、重定位等），如果没有用到则不进行绑定。这样的做法可以大大加快程序的启动速度，特别有利于一些有大量函数引用和大量模块的程序。</p>
<p>ELF文件将GOT拆分成两个表叫做".got"和".got.plt"。其中".got"用来保存全局变量引用的地址，".got.plt"用来保存函数引用的地址，也就是说，所有对于外部函数的引用全部被分离出来放到了".got.plt"。</p>
<p>当我们调用某个外部模块的函数时，如果按照通常的做法应该是通过GOT中相应的项进行间接跳转。PLT为了实现延迟绑定，在这个过程中间又增加了一层间接。调用函数并不直接通过".got.plt"跳转，而是通过一个叫作".plt"的结构来进行跳转。每个外部函数在".plt"中都有一个相应的项，比如bar()函数在".plt"中的项的地址可以称为"bar@plt"。".plt"的基本结构大概如下（以bar@plt为例）：</p>
<pre class="brush:asm">.PLT0:
push *(.got.plt+4)
jump *(.got.plt+8)

.PLT1:
jump *(bar@got.plt)
push offset_of_rel
jump .PLT0</pre>
<p>延迟绑定的符号解析过程大概如下（以上面示例中的bar()为例）：</p>
<ol>
<li>代码中调用bar()的地方在编译时已经被替换成一条调用bar@plt的转移指令，即是调用的".plt"中的bar所在的项,即".PLT1"。</li>
<li>当首次解析的时候，虽然在".got.plt"中有bar函数的项，但是在没有解析之前，该项存储的内容为0，而非bar函数的绝对地址，因此指令"jump *(bar@got.plt)"实际上没有任何的动作，只是直接跳转到下一条push指令（因为jump的偏移为0）。</li>
<li>指令"push offset_of_rel"是将bar()函数的重定位项的索引压栈，而该重定位项的类型为R_386_JMP_SLOT，offset_of_rel是重定位表".rel.plt"中的下标。</li>
<li>接着指令"jump .PLT0"跳转到.PLT0，将".got.plt"的第二表项压栈，然后再跳转到".got.plt"第三个表项指定的地址。这里要简述一下".got.plt"的结构，".got.plt"和".got"几乎是一样的，除了".got.plt"前三项的值有特殊意义：(1)第一项保存".dynamic"段的地址，这个段描述了本模块动态链接相关的信息（可暂忽略）；(2)保存本模块的ID（用于在动态链接时查找本模块的信息）；(3)保存的是_dl_runtime_resolve()函数的地址，用于启动动态链接器，加载模块，解析符号并重定位。。</li>
<li>此时，可以看到在堆栈上，已经存有bar()函数在重定位项的索引和当前要重定位的模块ID，那么_dl_runtime_resolve()函数就可以启动动态链接器，当动态链接器得到控制后，它恢复堆栈，获取在步骤3中入栈的重定位项索引和步骤4中入栈的本模块信息，通过这2个信息，动态链接器符号解析和计算绝对地址，将bar()的“真实”地址存储于bar()在全局偏移表".got.plt"的表项（原来的值为0）。</li>
<li>动态链接器更新了".got.plt"中的信息后，将执行的控制权传递给bar()函数。那么当再次调用bar()函数时，PC依然会跳转到".PLT1"，但是此时"bar@got.plt"的值已经不再是0了，因为会正确地跳转到bar()函数的绝对地址。</li>
</ol>
<h4>动态链接相关的数据结构</h4>
<p>在动态链接的情况下，可执行文件的装载与静态链接情况基本一样。首先操作系统会读取可执行文件的头部，检查文件合法性，然后从头部中的"Program Header"中读取每个"Segment"的虚拟地址、文件地址和属性，并将它们映射到进程虚拟空间的相应位置，这些步骤跟静态链接情况下基本一样。但不同的是，静态链接在完成以上工作后会将控制权直接转交给可执行文件的入口地址(e_entry)，然后程序开始执行。而动态链接情况下，操作系统需要加载可执行文件依赖的所有共享对象，此时还有很多外部符号的引用还处于无效地址的状态，因此操作系统会启动一个动态链接器，可加载可执行文件所依赖的共享对象，最后才将控制权转交给可执行文件。</p>
<p><strong>".interp"段</strong></p>
<p>这个段的内容是指定可执行文件所需要的动态链接器的路径，即是说动态链接器的位置即不是系统配置指定的，也不是由环境参数决定的，而是由ELF可执行文件决定的。</p>
<pre class="brush:plain">Contents of section .interp:
 8048134 2f6c6962 2f6c642d 6c696e75 782e736f  8048144 2e3200 /lib/ld-linux.so.2</pre>
<p>其实这个只是一个软链接。动态链接器在Linux下是Glibc的一部分，也就是属于系统库级别的，它的版本号往往跟系统中的Glibc库版本号一样的。</p>
<p><strong>".dynamic"段</strong></p>
<p>动态链接ELF中最重要的结构应该是".dynamic"段，这个段里面保存了动态链接器所需要的基本信息，比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址等，所以，".dynamic"段可以看成是动态链接下ELF文件的”文件头“。</p>
<pre class="brush:c">typedef struct {
    Elf32_Sword d_tag;
    union {
        Elf32_Word d_val;
        Elf32_Addr d_ptr;
    } d_un;
 } Elf32_Dyn;</pre>
<p>".dynamic"段的结构基本上是由一个类型值加上一个附加的数值或指针。而附加的union值可能是索引、下标和虚拟地址等等。而类型为DT_NEED类型则为动态链接的模块所依赖的模块路径。</p>
<p><strong>".dynsym"段</strong></p>
<p>这个段称为动态符号表（Dynamic Symbol Table），用来表示动态链接这些模块之间的符号导入导出关系。与".symtab"不同的是，".dynsym"只保存了与动态链接相关的符号，对于那些模块内部的符号，比如模块私有变量则不保存。很多时候动态链接的模块同时拥有".dynsym"和"symtab"两个表，".symtab"中往往保存了所有符号，包括".dynsym"中的符号。</p>
<p><strong>动态链接的重定位表</strong></p>
<p>在静态链接中，目标文件里面包含有专门用于表示重定位信息的重定位表，比如".rel.text"表示是代码段的重定位表，".rel.data"是数据段的重定位表。在相应在动态链接文件中，也有类似的重定位表分别叫做".rel.dyn"和".rel.plt"，它们分别相当于".rel.data"和".rel.plt"。</p>
<p>".rel.dyn"是对数据引用进行修正，它所修正的位置位于".got"以及数据段；而".rel.plt"是对函数引用的修正，它所修正的位置位于".got.plt"。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.endlesscode.com/2010/06/07/dynamic-linkage/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
