<?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>Computer, Electron and Technology &#187; 平台</title>
	<atom:link href="http://www.donevii.com/post/tag/%e5%b9%b3%e5%8f%b0/feed" rel="self" type="application/rss+xml" />
	<link>http://www.donevii.com</link>
	<description>关注技术、移动互联网以及一切 GEEK &#38; NERD 的事情</description>
	<lastBuildDate>Wed, 21 Dec 2011 10:49:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>pplive for ubuntu linux</title>
		<link>http://www.donevii.com/post/916.html</link>
		<comments>http://www.donevii.com/post/916.html#comments</comments>
		<pubDate>Thu, 06 Aug 2009 11:22:06 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[平台]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/916.html</guid>
		<description><![CDATA[看到这条新闻有点唏嘘，多年以前做视频网站时和领导们聊过，为什么不去研究跨平台客户端，本地框架P2P Client + flash 在当时已经可以实现了…... ]]></description>
			<content:encoded><![CDATA[<p>看到这条新闻有点唏嘘，多年以前做视频网站时和领导们聊过，为什么不去研究跨平台客户端，本地框架P2P Client + <a href="http://www.donevii.com/post/tag/flash" class="st_tag internal_tag" rel="tag" title="Posts tagged with flash">flash</a> 在当时已经可以实现了……</p>
<div class="zemanta-pixie"><img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=6cd3c1ab-7ebe-8f6e-b6a3-2db855c3abb4" /></div>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/916.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Savage 2</title>
		<link>http://www.donevii.com/post/688.html</link>
		<comments>http://www.donevii.com/post/688.html#comments</comments>
		<pubDate>Tue, 16 Dec 2008 05:32:16 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[life]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[平台]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/688.html</guid>
		<description><![CDATA[这个游戏太棒了，RTS + FPS ，并且还是跨 win , linux , mac 三个平台，并且还免费。爽～～～～ RTS 模式 FPS 模... ]]></description>
			<content:encoded><![CDATA[<p>这个游戏太棒了，RTS + FPS ，并且还是跨 win , <a href="http://www.donevii.com/post/tag/linux" class="st_tag internal_tag" rel="tag" title="Posts tagged with linux">linux</a> , mac 三个平台，并且还免费。爽～～～～</p>
<p><a href="http://www.flickr.com/photos/31744939@N06/3100858787"><img src="http://farm4.static.flickr.com/3077/3100858787_2127dec25d.jpg" /></a></p>
<p>RTS 模式</p>
<p><a href="http://www.flickr.com/photos/31744939@N06/3100857533"><img src="http://farm4.static.flickr.com/3140/3100857533_17df91cee0.jpg" /></a></p>
<p>FPS 模式</p>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/688.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>各有所长 四大智能手机操作平台大比拼</title>
		<link>http://www.donevii.com/post/595.html</link>
		<comments>http://www.donevii.com/post/595.html#comments</comments>
		<pubDate>Fri, 24 Oct 2008 12:50:27 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[mobile]]></category>
		<category><![CDATA[andriod]]></category>
		<category><![CDATA[Mac OS]]></category>
		<category><![CDATA[Symbian]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[手机]]></category>

		<guid isPermaLink="false">http://www.donevii.com/?p=595</guid>
		<description><![CDATA[转载自http://android.hk.cn 本文将用老谋深算的Symbian平台、历史悠久的Windows Mobile平台、玲珑剔透的Mac OS X平台、前景光明的Android平台四个章节对目前市面上主流手机操作系统的优势和特色做一个... ]]></description>
			<content:encoded><![CDATA[<p>转载自http://android.hk.cn</p>
<p>本文将用<strong>老谋深算的Symbian平台</strong>、<strong>历史悠久的Windows Mobile平台</strong>、<strong>玲珑剔透的Mac OS X平台</strong>、<strong>前景光明的Android平台</strong>四个章节对目前市面上主流手机操作系统的优势和特色做一个详细的介绍。</p>
<p>       <strong>老谋深算的Symbian平台</strong></p>
<p>       随着科技和信息技术的发展，人们对手机功能的要求也越来越高。现在，很多人对手机已不仅仅满足于打电话和发短信等基本功能，而是要求它能够播放音乐、上网聊天、安排日程甚至进行GPS导航。因此，很多人已经逐步抛弃传统固化软件的手机，改投智能手机的怀抱。目前，在智能手机市场上，销量最大的平台有三种，分别是Symbian、Windows Mobile和Mac OS X，其中前两种都有一种以上的手机品牌在使用，而Mac OS X仅为苹果iPhone和iPhone 3G独占使用。现在，基于Google Android平台的新一代智能手机T-Mobile G1马上就要上市，它几乎无可非议的会受到人们的追捧。这样一来，市场上便形成了四大智能手机操作平台鼎立的局面。</p>
<p>       <strong><a href="http://www.donevii.com/post/tag/symbian" class="st_tag internal_tag" rel="tag" title="Posts tagged with Symbian">Symbian</a>（塞班）</strong></p>
<p>       首先要提到的当然是Symbian（塞班）系统。使用在智能手机上的Symbian系统分为S60和UIQ两种用户界面，其中前者现已发展到v3版本。得益于世界手机老大诺基亚的大力支持，安装了Symbian S60系统的手机已经广为人们使用。而UIQ的主推者是索尼爱立信，虽然UIQ的界面更加华丽，但由于索尼爱立信自身和手机市场的原因，Symbian UIQ一直没有能够得到很好的发展。</p>
<p>       由于Symbian由多家传统手机厂商联合研发，因此无论在运行速度还是易用性上跟其它的系统相比都有很大的优势，而且对移动通信协议，如GSM、GPRS、WCDMA和蓝牙等的支持要优于其它系统。由于发布时间较早，其第三方软件经过几年的发展数量已经非常之多，可以实现人们的大多数需求。</p>
<p>       但遗憾的是，由于系统的先天限制，Symbian系统的多媒体性能并不突出。刷固件较为困难、内存紧张等问题是一直是人们所诟病的几大缺憾。S60 v3发布后诺基亚开始推行的签名策略也浇灭了很多人开发第三方软件的热情。而且，Symbian系统实现触摸操作相当困难，好在诺基亚拥有强大的研发实力。相信继5800 XpressMusic之后，诺基亚还会继续推出触摸屏机型。不过尽管如此，Symbian平台凭着强大的电话功能和较强的易用性，仍然博得了很多人的青睐。</p>
<p>       <strong>历史悠久的Windows Mobile平台</strong></p>
<p>       <strong>Windows Mobile</strong></p>
<p>       作为微软下了血本研发的一个操作平台，Windows Mobile系统自然丝毫不能被轻视。Windows Mobile的前身是Windows CE，一个微软专为PDA和掌上电脑开发的嵌入式系统。后来随着智能手机的出现，Windows CE演变成了Windows Pocket PC。2003年，微软又开发了Pocket Phone Edition和Windows Powered Smart Phone操作系统，而这两者的融合，才是严格意义上的Windows Mobile。</p>
<p>       目前，Windows Mobile最高版本为6.1。它最大的优势在于其PDA（个人数字助理）功能非常强大，并可通过ActiveSync或Windows Mobile Center和计算机中Outlook等程序保持同步，商务人士可使用该系统随时处理文件、邮件或进行日程安排。由于这个系统的历史非常悠久，和桌面版Windows系统也有异曲同工之处，其第三方软件的数量已经多到了一个令人难以想象的地步。通过各种第三方软件，安装了Windows Mobile系统的手机可以发挥几乎无限的用途。对于软件开发者来说，由于Windows Mobile系统的软件开发较为简单，再加上新版系统中增加了对.Net Framework的支持，使软件的开发相当便利。而对于最终用户来说，Windows Mobile系统的操作方式跟桌面版Windows非常类似，都有开始菜单等人们所熟悉的组件，熟悉计算机的人可以很快上手使用。</p>
<p>       但Windows Mobile系统的缺陷也是显而易见的。按照通常的说法，它有着三大缺陷。其一是系统运行速度太慢。或许是微软往小小的系统中塞了很多东西，Windows Mobile也延续了Windows系统庞大臃肿的毛病，几乎可以算得上是反应最慢的智能手机操作系统。其二是支持的颜色数仅为65000色。关于这个问题很多年前人们就开始抱怨，到现在还是如此。不知道微软为什么不听听群众们的呼声。其三是系统操作过于复杂。它的很多设置都隐藏在层层菜单和选项下，有时甚至还需动用注册表修改器，仍然继承了桌面版Windows的传统。虽然这可能正好满足了少数人的“变态”控制欲。</p>
<p>       但是总的来说，Windows Mobile以其强大的功能、便捷的信息管理以及第三方软件众多等优势，一直以来都为世人所喜爱。当然，更重要的是它还有微软这样一个重量级的后盾。可以说，只要微软不倒，Windows Mobile也将一直生存下去。</p>
<p>       <strong>玲珑剔透的Mac OS X平台</strong></p>
<p>       <strong><a href="http://www.donevii.com/post/tag/mac-os" class="st_tag internal_tag" rel="tag" title="Posts tagged with Mac OS">Mac OS</a> X</strong></p>
<p>       苹果的iPhone手机自从公布的那天起便一直受到人们的关注，现在的iPhone 3G也是如此。iPhone使用的操作系统为精简过的Mac OS X，最高版本为2.1，其很多特性与完整版的Mac OS X相类似。该系统使用的内核基于BSD <a href="http://www.donevii.com/post/tag/unix" class="st_tag internal_tag" rel="tag" title="Posts tagged with unix">Unix</a>，具有抢占式多任务处理（pre-emptive multitasking）的特性。其内存管理功能非常优秀，具有强烈的Unix风格，允许同时运行很多软件，并从实质上消除了一个程序崩溃导致其它程序崩溃的可能性。</p>
<p>       iPhone的Mac OS X系统更多的优势来自于与iTunes服务的紧密紧密。iTunes原先仅是一个媒体播放软件，后来当iPod和iPhone发布后，iTunes逐渐演变为苹果在Mac和Windows系统上的一个无穷无尽的媒体源。人们可以通过iTunes登录到苹果的在线商店，购买各种音乐、视频或程序。这也成为苹果的一个新的盈利增长点。</p>
<p>       虽然iPhone和iPhone 3G的Mac OS X系统已被大大精简，但在苹果计算机的Mac OS X系统中广为人们熟知的Dock和Aqua等关键元素仍然存在。更重要的是，人们可以在安装特定程序后进入Unix命令行进行各种操作，并对系统底层进行控制。此时iPhone已不再是一台手机，它更像是台超便携的苹果电脑，能够处理很多普通人所意想不到的事情。</p>
<p>       不久以前，苹果为iPhone和iPod Touch开放了App Store应用程序商店。手机用户可通过信用卡在商店中购买自己喜欢的程序，而开发者也能通过App Store发布自己编写的程序，其收益与苹果七三分成。苹果的这个创新构思马上得到了人们的响应，短短几个月的时间App Store中的程序便达到了惊人的数量。</p>
<p>       <strong>前景光明的Android平台</strong></p>
<p>       <strong>Android</strong></p>
<p>       文章最后要提到的当然是本专题的主角——Google Android平台。Android 是一个真正意义上的开放性移动设备平台。它包括操作系统、用户界面和应用程序等移动电话工作所需的全部组件，而且不存在任何以往阻碍移动产业创新的专有权障碍。</p>
<p>       说到Android，便不能不提OHA（Open Handset Alliance）开放手机联盟。去年，Google与包括中国移动、英特尔、摩托罗拉、高通在内的33家业内巨头成立了该组织，这个联盟将共同推动Google手机平台Android的发展。Android作为Google企业战略的重要组成部分，将进一步推进“随时随地为每个人提供信息”这一企业目标的实现。</p>
<p>       跟iPhone相比，Android平台同样也拥有自己的在线应用程序商店，名为Android Market。这个在线商店仅需进行简单的注册后便可以自由上传程序。虽然这可能导致一些安全问题，但总的来说这种开放式的平台非常有利于第三方软件的发展。而Google在对这些软件进行幕后审查的同时，也采取了多项措施，最大限度的保证了软件开发者的利益。</p>
<p>       Android与前面三种系统相比最大的特点在于其开放性。这里所指的开放性包括两个方面，其一是Android以开源Linux系统为基础，对于开源爱好者而言，他们会觉得Android平台更能满足自己的使用需求。其二是Android对第三方软件的开放程度。Google不会对Android系统的第三方应用程序像苹果那样严格把关，而仅是在用户自行发布之后进行审查。这样一来必将极大的促进该系统第三方软件的发展。</p>
<p>       而就基于Android的第一款手机T-Mobile G1来说，其最不能令人忍受的缺陷恐怕就是没有3.5mm耳机插孔了。另外，由于T-Mobile背后使下的“阴招”，用户在用完1GB流量后带宽便会自动下降为50Kbps，这将大大影响人们的购买欲望。而且，Android系统目前还没有桌面同步软件，也不支持Exchange同步。虽然Google的很多在线服务都可以满足人们的需要，但总有上不了网的时候。况且，很多信息保存在计算机中要安全得多。</p>
<p>       四款主流或将要成为主流的智能手机操作平台到这里就介绍完了。俗话说的好，尺有所短，寸有所长，每个平台都有其各自的优点和不足。大家在选择智能手机的时候，需要关注的重点是各种平台的侧重点，这样在今后的使用中才会得心应手。至于该如何选择，相信大家心中都已经有数了吧。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/595.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>研究显示：网页游戏游戏及客服质量满意度较低</title>
		<link>http://www.donevii.com/post/519.html</link>
		<comments>http://www.donevii.com/post/519.html#comments</comments>
		<pubDate>Sat, 27 Sep 2008 03:11:51 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[life]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[类]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/519.html</guid>
		<description><![CDATA[易观国际联合17173.com共同发起的网页游戏在线调研，根据在线调研有效回收样本分析并撰写的《中国网页游戏市场用户调研报告2008》中指出，玩家在对网页游戏各项指标的评价中：对“游戏活... ]]></description>
			<content:encoded><![CDATA[<p>易观国际联合17173.com共同发起的网页游戏在线调研，根据在线调研有效回收样本分析并撰写的《中国网页游戏市场用户调研报告2008》中指出，玩家在对网页游戏各项指标的评价中：对“游戏活动”和“客服质量”表示“非常不满意”的分别达到了10.9%和11.7%。</p>
<p><img height="396" alt="" src="http://www.analysys.com.cn/UserFiles/watered/45109.jpg" width="573" /></p>
<p>易观国际认为主要原因是，在“赚快钱”的驱动下，很多运营商仓促的上线一些网页游戏。但是由于自身资源匮乏，缺乏资金、渠道，且缺乏运营经验，在运营过程中对玩家需求的反馈及玩家利益的保障做的很不够，大大影响了玩家的游戏体验。</p>
<p>在泛娱乐时代，玩家可以选择的娱乐项目已经非常多，网页游戏如果始终无法满足用户的体验，用户将很有可能流失到其他游戏或者其他的娱乐项目，例如传统的MMORPG或者休闲网络游戏等，这将非常不利于行业的发展。易观建议网页游戏运营商应从内部运营、客服等多个方面入手提升自身能力。</p>
<p><strong>研究定义</strong></p>
<p>网络游戏：网络游戏是一种依托于网络，可同时多人参与的电子游戏。它通过人与人之间的互动、对抗，达到交流、娱乐和休闲的目的。本报告中所指的网络游戏，特指以互联网为游戏平台，以PC为终端的网络游戏。</p>
<p>网页游戏：<a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">Web</a> game，又称无端网游，是基于网络浏览器的多人在线互动游戏，用户无需下载客户端，只要打开网页就可以玩网页游戏。目前国内的网页游戏以战争策略类的为主，例如《纵横天下》、《部落战争》等。</p>
<p>MMORPG：Massive Multiplayer Online Role Playing Games，大型多人角色扮演游戏。用户在游戏创建并扮演一个虚拟角色，在一个模拟现实的虚拟世界游戏背景中与其他角色进行互动。MMORPG一般具有三大特点：（1）多人（往往几千上万人）同时参与一个游戏；（2）游戏是可持续发展的；（3）游戏具有显著的社会性和社区型。</p>
<p>休闲游戏：休闲游戏一般都有卡通风格，内容较为轻松，游戏连续性不强，且每局游戏的时间较短，对用户来说更多是一种操作技巧的较量。</p>
<p>虚拟物品：指游戏中的虚拟道具、装备等。玩家购买虚拟物品一般是为了提升游戏中的技能或通过使用虚拟物品提升游戏的趣味性。</p>
<p><strong>研究方法</strong>：</p>
<p>本次调查数据通过网上联机问卷，问卷放置在17173.com网站上，由用户主动参与填写的方式获取。本次调查共回收有效问卷6074份，对于回收的有效数据通过统计分析软件SPSS进行数据分析处理，并在此基础上撰写研究报告。</p>
<p>欲了解中国网页游戏市场用户更多相关内容，请参阅易观国际《中国网页游戏市场用户调研报告2008》或联系易观国际客户服务部。</p>
<p><b>研究说明：</b></p>
<p>易观国际提供的产业分析，主要是在产业宏观数据、最终用户季度调研数据、厂商历史数据以及厂商季度业务监测信息等基础上，运用易观的产业分析模型，并结合市场研究、行业研究以及厂商研究方法得出的，主要反映了市场现状、趋势、拐点和规律，以及厂商的发展现状。</p>
<p>易观国际相信通过上述产业研究方法得出的数据在行业公认可接受误差范围内，可以准确反映行业走势与变化规律。</p>
<p>通过专业研究方法得到的研究结果，旨在供决策参考。厂商的实际数据请查询厂商发布的财务报告。</p>
<p>&#160;</p>
<p>相关搜索</p>
<p><a href="http://www.baidu.com/s?wd=%BF%CD%B7%FE%C2%FA%D2%E2%B6%C8%B5%F7%B2%E9&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=0&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">客服满意度调查</a>    <br /><a href="http://www.baidu.com/s?wd=qq%D3%CE%CF%B7%BF%CD%B7%FE&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=1&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">qq游戏客服</a>    <br /><a href="http://www.baidu.com/s?wd=%CC%DA%D1%B6%D3%CE%CF%B7%BF%CD%B7%FE%D6%D0%D0%C4&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=2&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">腾讯游戏客服中心</a>    <br /><a href="http://www.baidu.com/s?wd=%CC%DA%D1%B6%D3%CE%CF%B7%BF%CD%B7%FE&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=3&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">腾讯游戏客服</a>    <br /><a href="http://www.baidu.com/s?wd=%D3%CE%CF%B7%BF%CD%B7%FE&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=4&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">游戏客服</a></p>
<p><a href="http://www.baidu.com/s?wd=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=5&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">网页游戏游戏</a>    <br /><a href="http://www.baidu.com/s?wd=qq%D3%CE%CF%B7%BF%CD%B7%FE%B5%E7%BB%B0&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=6&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">qq游戏客服电话</a>    <br /><a href="http://www.baidu.com/s?wd=qq%D3%CE%CF%B7%BF%CD%B7%FE%D6%D0%D0%C4&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=7&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">qq游戏客服中心</a>    <br /><a href="http://www.baidu.com/s?wd=dnf%D3%CE%CF%B7%BF%CD%B7%FE&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=8&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">dnf游戏客服</a>    <br /><a href="http://www.baidu.com/s?wd=%CC%DA%D1%B6%D3%CE%CF%B7%BF%CD%B7%FE%B5%E7%BB%B0&amp;lm=0&amp;si=&amp;rn=10&amp;ie=gb2312&amp;ct=0&amp;cl=3&amp;f=1&amp;rsp=9&amp;oq=%CD%F8%D2%B3%D3%CE%CF%B7%D3%CE%CF%B7%BC%B0%BF%CD%B7%FE%D6%CA%C1%BF%C2%FA%D2%E2%B6%C8%BD%CF%B5%CD">腾讯游戏客服电话</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/519.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>google背后的分布式架构</title>
		<link>http://www.donevii.com/post/504.html</link>
		<comments>http://www.donevii.com/post/504.html#comments</comments>
		<pubDate>Tue, 23 Sep 2008 08:48:29 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[life]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[unix]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[测试]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓存]]></category>
		<category><![CDATA[芯片]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/504.html</guid>
		<description><![CDATA[　　Google是与众不同的。它的独特不仅仅表现于革新的思维和充满创意的应用 (比如那个大堂里的地球模型)，更在于其有别常规的IT策略…… 　　加利福尼亚州山景城(Mountain View)Google公司(Google... ]]></description>
			<content:encoded><![CDATA[<p>　　Google是与众不同的。它的独特不仅仅表现于革新的思维和充满创意的应用  (比如那个大堂里的地球模型)，更在于其有别常规的IT策略……</p>
<p>　　加利福尼亚州山景城(Mountain  View)Google公司(Google，下称Google)总部有一个43号大楼，该建筑的中央大屏幕上显示着一个与Google地球(Google  Earth)相仿的世界地图，一个转动的地球上不停地闪动着五颜六色的光点，恍如罗马宫廷的千万烛灯，每一次闪动标志着地球的这个角落一名Google用  户发起了一次新的搜索。</p>
<p>　　这同时意味着Google又一次满足了人们对未知信息的好奇与渴望。</p>
<p>　　Google是与众不同的。它的独特不仅仅表现于革新的思维和充满创意的应用  (比如那个大堂里的地球模型)，更在于其有别常规的IT策略。从人们的常理来看，简单的硬件商品和免费软件是无法构建出一个帝国的，但是Google做到  了。在性能调整后，Google把它们变成一个无可比拟的分布式计算平台，该平台能够支持大规模的搜索和不断涌现的新兴应用。我们原本认为这些应用都是个  人消费级别的，但是Google改变了这一切。现在商业世界也在使用它们，这就令这家搜索公司显得那么与众不同。</p>
<p>　　GoogleWeb  服务背后的IT架构对无数使用搜索引擎的用户来说也许并不是非常重要，但它是Google几百位致力于把全球信息组织起来，实现“随处可达，随时可用”目  标的工程师们的最核心工作。这就需要一个在覆盖范围和野心上都与Google的商业愿景完全相符的IT蓝图作为支撑。</p>
<p>　　Google  的经理们一直对公司的IT策略话题保持沉默，他们厌恶谈及特定的厂商或者产品，当被问到他们的服务器和数据中心时，他们总是闭口不谈。但与几位  Google的IT领导一起呆了一天后，我们最终得以揭示该公司的IT是如何运作的，那可不仅仅是一个运行在无数服务器集群上的、表面看来非常简单的搜索  引擎。在其简单的外表下，蕴涵着许多内部研发软件、定制硬件、人工智能，以及对性能的执着追求和打破常规的人力管理模式。</p>
<p>　　IT理念方面，Google对同行有一条建议：尽量避免那些人人都在使用的系统和软件，以自己的方式做事会更有独特的竞争优势。</p>
<p>　　“企业文化决定了你的做事方式。”道格拉斯&#8221;美林(Douglas  Merrill)，这位Google工程副总裁和事实上的首席信息官(CIO)  指出，“到了我们这样的发展阶段，企业观念和文化非常与众不同，这也反过来鞭策我们必须要采用与众不同的方式来运行那些他人看来很常规的系统。”<br />
　　Google  最大的IT优势在于它能建造出既富于性价比(并非廉价)又能承受极高负载的高性能系统。因此IT顾问史蒂芬&#8221;阿诺德(Stephen  Arnold)指出，Google与竞争对手，如亚马逊网站(Amazon)、电子港湾公司(eBay)、微软公司(Microsoft，下称微软)和雅 虎公司  (Yahoo，下称雅虎)等公司相比，具有更大的成本优势。Google程序员的效率比其他Web公司同行们高出50%～100%，原因是Google已  经开发出了一整套专用于支持大规模并行系统编程的定制软件库。据他估算，其他竞争公司可能要花上四倍的时间才能获得同等的效果。</p>
<p>　　打造服务器</p>
<p>　　Google  究竟是怎样做到这点的呢?其中一个手段，美林认为，“是因为我们自己动手打造硬件。”Google并不制造计算机系统，但它根据自己的参数定制硬件，然后  像MTV的节目“靓车打造”(Pimp My Ride)那样自己安装和调整硬件系统。开源程序经理克里斯&#8221;迪博纳(Chris  DiBona)评论道：“我们很善于购买商业服务器，并且改造他们为我们所用，最后把性能压榨和发挥到极致，以致有时候他们热得像要融化了似的。”</p>
<p>　　这种亲手打造的方式，来源于Google从车库诞生时与生俱来的节俭风格，更与Google那超大型的系统规模息息相关，良好的习惯一直延续至  今。据说 Google在65个数据中心拥有20万～45万台服务器—这个数目会有偏差(取决于你如何定义服务器和由谁来做这项统计)。但是，不变的是持续上升的趋势。</p>
<p>　　Google不会去讨论这些资产，因为它认为保密也是一种竞争优势。事实上，Google之所以喜欢开源软件也是因为它的私密性。“如果我们购  买了软件许可或代码许可，人们只要对号入座，就可以猜出Google的IT基础架构。”迪博纳分析说，  “使用开源软件，就使我们多了一条把握自己命运的途径。”</p>
<p>　　Google喜欢规模化的服务器运行方式。当有成百上千台机器时，定制服务器的优势也会成倍增加，效果也会更趋明显。Google正在俄勒冈州  哥伦比亚河边的达勒斯市建造一个占地30亩的数据中心，在那儿它可以获得运算和降温需要的低价水力电力能源(参见边栏《Google数据中心自有一套》)。</p>
<p>　　Google以“单元”(Cell)的形式组织这些运行  Linux操作系统的服务器，迪博纳把这种形式比喻成互联网服务的“磁盘驱动器”(但别和一直谣传的Google存储服务Gdrive混淆了，“并没有  Gdrive这回事。”一位Google女发言人明确表示。)，公司的软件程序都驻扎在这些并不昂贵的电脑机箱里，由程序员决定它们的冗余工作量。这种由  很多单元组成的文件系统代替了商业存储设备;迪博纳表示Google这些单元设备更易于建造和维护，他还暗示他们能处理更大规模的数据。</p>
<p>　　Google  不会漏过对任何技术细节的关注。多年来，公司的工程师就在研究微处理器的内部工作机制，随着Google规模的持续壮大，必然会用到特别定制和调节过的芯  片。知名工程师路易斯&#8221;巴罗索(Luiz  Barroso)去年在一篇发表在工业杂志上的论文中证实，近年来Google的主要负荷都由单核设计的系统承担着。但许多服务器端的应用，如  Google搜索索引服务，所需的并行计算在单核芯片的指令级别上执行得并不好。</p>
<p>　　曾在数据设备公司(Digital  Equipment)和康柏公司(Compaq)当过芯片设计师的巴罗索认为，随着AMD公司、英特尔公司(Intel)、太阳计算机系统公司(Sun)开始制造多核芯片，必将会出现越来越多芯片级别的并行计算。</p>
<p>　　Google  也曾考虑过自己制造计算机芯片，但从业界潮流来看，这个冒险的举动似乎不是很必要。“微处理器的设计非常复杂而且成本昂贵，”运营高级副总裁乌尔斯&#8221;霍尔 茨勒(Urs  Holzle)表示。Google宁愿与芯片制造商合作，让他们去理解自己的应用并设计适合的芯片。这是一种客户建议式的设计，其关注点在于总体吞吐量、  效能，以及耗电比，而不是看单线程的峰值性能。霍尔茨勒表示，“这也是最近多核CPU的设计潮流与未来方向。”<br />
裁缝般地定制软件</p>
<p>　　为了能尽量压榨硬件性能，Google开发了相当数量的定制软件。创新产品主要包括用于简化处理和创建大规模数据集的编程模型  MapReduce;用于存储和管理大规模数据的系统BigTable;分析分布式运算环境中大规模数据集的解释编程语言Sawzall;用于数据密集型  应用的分布式文件系统的 “Google文件系统”(Google File  System);还有为处理分布式系统队列分组和任务调度的“Google工作队列”(Google  Workqueue)。</p>
<p>　　正是从Sawzall这些工具里体现出Google对计算效率的执著关注。并不是每家公司都能从底层去解决效率问题，但是对Google来说，  为常规关系型数据库无法容纳的大规模数据集专门设计一种编程语言是完全合理的。即使其他编程工具可以解决问题，Google的工程师们仍然会为了追求效率  而另外开发一套定制方案。Google工程师认为，Sawzall能与C++中的MapReduce相媲美，而且它更容易编写一些。</p>
<p>　　Google  对效率的关注使它不可能对标准Linux内核感到满意;Google会根据自己的需要运行修改过的内核版本。通过调整Linux的底层性能，Google  工程师们在提高了整体系统可靠性的基础上，还一并解决了数据损坏和数据瓶颈等一系列棘手问题。对内核的修改也使Google的计算机集群系统因为通信效率  的提高而运行得更快。</p>
<p>　　当然，Google偶尔也会出现系统故障，情况一旦发生，无数的用户就会受到影响了。三年前一次持续30分钟的系统故障使20%的搜索流量受到影响。</p>
<p>　　Google  开发了自己的网站服务器却没有使用开源的Apache服务器，尽管它在网站服务器的市场占有率超过60%。迪博纳认为，Google的网站服务器可以运行  在更多数量的主机上，对Google站点上内容庞大又彼此互相依赖的应用程序来说，这种服务器的负载均衡能力远比Apache的能力更高。同时，在用标准  公共网关接口(CGI)访问数据库动态网页方面，Google服务器的编程难度要比  Apache更高，但是最终运行速度却更快。“如果我们能够压榨出10%～20%的性能，我们就可以节省出更多系统资源、电量和人力了。”迪博纳在总结中指出。</p>
<p>　　Google还设计了自己的客户关系管理(CRM)系统用于支持自己基于竞价和点击的互联网广告收费业务。但对是否需要设计自己的工具，Google的态度也不是一成不变的。比如在财会软件上，它就使用了甲骨文公司(Oracle)的Financials软件。</p>
<p>　　美林拿着一只叉子举例说明现成的产品也可以带来价值。但在有些场合现成的软件产品就不一定适用了。“我们的文化在各个层面对我们的运作都有深远影响，”他表示，“所以我们不想让购买所得的工具改变我们的工作方式和文化层面。”<br />
Google&#8217;s  BigTable 原理 （翻译）</p>
<p>    题记：google 的成功除了一个个出色的创意外，还因为有 Jeff Dean  这样的软件架构天才。<br />
                                                  &#8212;&#8212; 编者</p>
<p>官方的  Google Reader blog 中有对BigTable 的解释。这是Google 内部开发的一个用来处理大数据量的系统。这种系统适合处理半结构化的数据比如  RSS 数据源。 以下发言  是 Andrew Hitchcock  在 2005 年10月18号 基于： Google 的工程师 Jeff Dean  在华盛顿大学的一次谈话 (Creative Commons License).</p>
<p>首先，BigTable 从 2004  年初就开始研发了，到现在为止已经用了将近8个月。（2005年2月）目前大概有100个左右的服务使用BigTable，比如： Print,Search  History,Maps和 Orkut。根据Google的一贯做法，内部开发的BigTable是为跑在廉价的PC机上设计的。BigTable  让Google在提供新服务时的运行成本降低，最大限度地利用了计算能力。</p>
<p>BigTable 是建立在 GFS ，Scheduler ，Lock Service 和  MapReduce 之上的。</p>
<p>每个Table都是一个多维的稀疏图 sparse map。Table 由行和列组成，并且每个存储单元 cell  都有一个时间戳。在不同的时间对同一个存储单元cell有多份拷贝，这样就可以记录数据的变动情况。在他的例子中，行是URLs  ，列可以定义一个名字，比如：contents。Contents  字段就可以存储文件的数据。或者列名是：”language”，可以存储一个“EN”的语言代码字符串。</p>
<p>为了管理巨大的Table，把Table根据行分割，这些分割后的数据统称为：Tablets。每  个Tablets大概有 100-200 MB，每个机器存储100个左右的  Tablets。底层的架构是：GFS。由于GFS是一种分布式的文件系统，采用Tablets的机制后，可以获得很好的负载均衡。比如：可以把经常响应  的表移动到其他空闲机器上，然后快速重建。</p>
<p>Tablets在系统中的存储方式是不可修改的 immutable  的SSTables，一台机器一个日志文件。当系统的内存满后，系统会压缩一些Tablets。由于Jeff在论述这点的时候说的很快，所以我没有时间把听到的都记录下来，因此下面是一个大概的说明：</p>
<p>压缩分为：主要和次要的两部分。次要的压缩仅仅包括几个Tablets，而主要的压缩时关于整个系统的压缩。主压缩有回收硬盘空间的功能。Tablets的位置实际上是存储在几个特殊的BigTable的存储单元cell中。看起来这是一个三层的系统。<br />
客户端有一个指向METAO的Tablets的指针。如果METAO的Tablets被频繁使用，那个这台机器就会放弃其他的tablets专门支持  METAO这个Tablets。METAO tablets  保持着所有的META1的tablets的记录。这些tablets中包含着查找tablets的实际位置。（老实说翻译到这里，我也不太明白。）在这个系统中不存在大的瓶颈，因为被频繁调用的数据已经被提前获得并进行了缓存。</p>
<p>     现在我们返回到对列的说明：列是类似下面的形式：  family:optional_qualifier。在他的例子中，行：www.search-analysis.com   也许有列：”contents:其中包含html页面的代码。 “ anchor:cnn.com/news” 中包含着  相对应的url，”anchor:www.search-analysis.com/” 包含着链接的文字部分。列中包含着类型信息。<br />
     (翻译到这里我要插一句，以前我看过一个关于万能数据库的文章，当时很激动，就联系了作者，现在回想起来，或许google的 bigtable  才是更好的方案，切不说分布式的特性，就是这种建华的表结构就很有用处。)</p>
<p>     注意这里说的是列信息，而不是列类型。列的信息是如下信息，一般是：属性/规则。 比如：保存n份数据的拷贝或者保存数据n天长等等。当 tablets  重新建立的时候，就运用上面的规则，剔出不符合条件的记录。由于设计上的原因，列本身的创建是很容易的，但是跟列相关的功能确实非常复杂的，比如上文提到 的  类型和规则信息等。为了优化读取速度，列的功能被分割然后以组的方式存储在所建索引的机器上。这些被分割后的组作用于 列 ,然后被分割成不同的  SSTables。这种方式可以提高系统的性能，因为小的，频繁读取的列可以被单独存储，和那些大的不经常访问的列隔离开来。</p>
<p>在一台机器上的所有的  tablets  共享一个log，在一个包含1亿的tablets的集群中，这将会导致非常多的文件被打开和写操作。新的log块经常被创建，一般是64M大小，这个GFS的块大小相等。当一个机器down掉后，控制机器就会重新发布他的log块到其他机器上继续进行处理。这台机器重建tablets然后询问控制机器处理结构的存储位置，然后直接对重建后的数据进行处理。这个系统中有很多冗余数据，因此在系统中大量使用了压缩技术。</p>
<p>     Dean 对压缩的部分说的很快，我没有完全记下来，所以我还是说个大概吧：压缩前先寻找相似的 \行，列，和时间数据。</p>
<p>    他们使用不同版本的：  BMDiff 和 Zippy <a href="http://www.donevii.com/post/tag/%e6%8a%80%e6%9c%af" class="st_tag internal_tag" rel="tag" title="Posts tagged with 技术">技术</a>。</p>
<p>   BMDiff 提供给他们非常快的写速度： 100MB/s – 1000MB/s 。Zippy 是和 LZW  类似的。Zippy 并不像 LZW 或者 gzip 那样压缩比高，但是他处理速度非常快。</p>
<p>    Dean 还给了一个关于压缩 <a href="http://www.donevii.com/post/tag/web" class="st_tag internal_tag" rel="tag" title="Posts tagged with web">web</a>  蜘蛛数据的例子。这个例子的蜘蛛 包含 2.1B 的页面，行按照以下的方式命名：“com.cnn.www/index.<a href="http://www.donevii.com/post/tag/html" class="st_tag internal_tag" rel="tag" title="Posts tagged with html">html</a>:http”.在未压缩前的web  page 页面大小是：45.1 TB ，压缩后的大小是：4.2 TB ， 只是原来的 9.2%。Links 数据压缩到原来的 13.9% ,  链接文本数据压缩到原来的 12.7%。<br />
<span id="more-504"></span><br />
Google 还有很多没有添加但是已经考虑的功能。<br />
    1.   数据操作表达式，这样可以把脚本发送到客户端来提供修改数据的功能。<br />
    2. 多行数据的事物支持。<br />
    3.   提高大数据存储单元的效率。<br />
    4. BigTable 作为服务运行。<br />
    好像：每个服务比如： maps 和 search history  历史搜索记录都有他们自己的集群运行 BigTable。<br />
    他们还考虑运行一个全局的 BigTable  系统，但这需要比较公平的分割资源和计算时间。</p>
<p>大表(Bigtable):结构化数据的分布存储系统</p>
<p>http://labs.google.com/papers/bigtable-osdi06.pdf</p>
<p>｛中是译者评论,程序除外｝<br />
{本文的翻译可能有不准确的地方,详细资料请参考原文.}</p>
<p>摘要<br />
bigtable是设计来分布存储大规模结构化数据的，从设计上它可以扩展到上２^50字节，分布存储在几千个普通服务器上．google的很多项目使用  bt来存储数据，包括网页查询，google  earth和google金融．这些应用程序对bt的要求各不相同：数据大小（从URL到网页到卫星图象）不同，反应速度不同（从后端的大批处理到实时数  据服务）．对于不同的要求，bt都成功的提供了灵活高效的服务．在本文中，我们将描述bt的数据模型．这个数据模型让用户动态的控制数据的分布和结构．我  们还将描述BT的设计和实现．<br />
１．介绍<br />
在过去两年半里，我们设计，实现并部署了BT．BT是用来分布存储和管理结构化数据的．BT的设计使它能够管理2^50  bytes(petabytes)数据，并可以部署到上千台机器上．BT完成了以下目标：应用广泛，可扩展，高性能和高可用性（high availability）.  包括google analytics, google finance, orkut, personalized search, writely和google  earth在内的60多个项目都使用BT.这些应用对BT的要求各不相同，有的需要高吞吐量的批处理，有的需要快速反应给用户数据．它们使用的BT集群也各不相同，有的只有几台机器，有的有上千台，能够存储2^40字节(terabytes)数据．<br />
BT在很多地方和数据库很类似：它使用了很多数据库的实现策略．并行数据库[14]和内存数据库[13]有可扩展性和高性能，但是BT的界面不同．BT不支持完全的关系数据模型；而是为客户提供了简单的数据模型，让客户来动态控制数据的分布和格式{就是只存储字串，格式由客户来解释}，并允许客户推断底层存储数据的局部性｛以提高访问速度｝．数据下标是行和列的名字，数据本身可以是任何字串．BT的数据是字串，没有解释｛类型等｝．客户会在把各种结构或者半结构化的数据串行化｛比如说日期串｝到数据中．通过仔细选择数据表示，客户可以控制数据的局部化．最后，可以使用BT模式来控制数据是放在内存里还是在硬盘上．｛就是说用模式，你可以把数据放在离应用最近的地方．毕竟程序在一个时间只用到一块数据．在体系结构里，就是：locality,  locality,  locality｝<br />
第二节描述数据模型细节．第三节关于客户API概述．第四节简介BT依赖的google框架．第五节描述BT的实现关键部分．第6节叙述提高BT性  能的一些调整．第7节提供BT性能的数据．在第8节，我们提供BT的几个使用例子，第9节是经验教训．在第10节，我们列出相关研究．最后是我们的结论．<br />
２．数据模型<br />
BT是一个稀疏的，长期存储的｛存在硬盘上｝，多维度的，排序的映射表．这张表的索引是行关键字，列关键字和时间戳．每个值是一个不解释的字符数组．｛数据都是字符串，没类型，客户要解释就自力更生吧｝．<br />
(row:string,  column:string,time:int64)-&gt;string  {能编程序的都能读懂，不翻译了}<br />
我们仔细查看过好些类似bigtable的系统之后定下了这个数据模型。举一个具体例子（它促使我们做出某些设计决定），  比如我们想要存储大量网页及相关信息，以用于很多不同的项目；我们姑且叫它Webtable。在Webtable里，我们将用URL作为行关键字，用网页  的某些属性作为列名，把网页内容存在contents:列中并用获取该网页的时间戳作为标识，如图一所示。<br />
图一：一个存储Web网页的范例列表片断。行名是一个反向URL｛即com.cnn.www｝。contents列族｛原文用  family，译为族，详见列族｝ 存放网页内容，anchor列族存放引用该网页的锚链接文本。CNN的主页被Sports  Illustrater｛即所谓SI，CNN的王牌体育节目｝和MY-look的主页引用，因此该行包含了名叫“anchor:cnnsi.com”和  “anchhor:my.look.ca”的列。每个锚链接只有一个版本｛由时间戳标识，如t9，t8｝；而contents列则有三个版本，分别由时间  戳t3，t5，和t6标识。<br />
行<br />
表中的行关键字可以是任意字符串（目前支持最多64KB，多数情况下10－100字节足够了）。在一个行关键字下的每一个读写操作都是原子操作（不管读写这一行里多少个不同列），这是一个设计决定，这样在对同一行进行并发操作时，用户对于系统行为更容易理解和掌控。<br />
Bigtable通过行关键字的字典序来维护数据。一张表可以动态划分成多个连续行。连续行在这里叫做“子表”｛tablet｝，是数据分布和负载  均衡的单位。这样一来，读较少的连续行就比较有效率，通常只需要较少机器之间的通信即可。用户可以利用这个属性来选择行关键字，从而达到较好数据访问地域  性｛locality｝。举例来说，在Webtable里，通过反转URL中主机名的方式，可以把同一个域名下的网页组织成连续行。具体来说，可以把  maps.google.com/index.html中的数据存放在关键字com.google.maps/index.html下。按照相同或属性相  近的域名来存放网页可以让基于主机和基于域名的分析更加有效。<br />
列族<br />
一组列关键字组成了“列族”，这是访问控制的基本单位。同一列族下存放的所有数据通常都是同一类型（同一列族下的数据可压缩在一起）。列族必须先创  建，然后在能在其中的列关键字下存放数据；列族创建后，族中任何一个列关键字均可使用。我们希望，一张表中的不同列族不能太多（最多几百个），并且列族在  运作中绝少改变。作为对比，一张表可以有无限列。<br />
列关键字用如下语法命名：列族：限定词。  列族名必须是看得懂｛printable｝的字串，而限定词可以是任意字符串。比如，Webtable可以有个列族叫language，存放撰写网页的语  言。我们在language列族中只用一个列关键字，用来存放每个网页的语言标识符。该表的另一个有用的列族是anchor；给列族的每一个列关键字代表  一个锚链接，如图一所示。而这里的限定词则是引用该网页的站点名；表中一个表项存放的是链接文本。<br />
访问控制，磁盘使用统计，内存使用统计，均可在列族这个层面进行。在Webtable举例中，我们可以用这些控制来管理不同应用：有的应用添加新的基本数据，有的读取基本数据并创建引申的列族，有的则只能浏览数据（甚至可能因为隐私权原因不能浏览所有数据）。<br />
时间戳<br />
Bigtable表中每一个表项都可以包含同一数据的多个版本，由时间戳来索引。Bigtable的时间戳是64位整型。可以由Bigtable来  赋值，表示准确到毫秒的“实时”；或者由用户应用程序来赋值。需要避免冲突的应用程序必须自己产生具有唯一性的时间戳。不同版本的表项内容按时间戳倒序排  列，即最新的排在前面。<br />
为了简化对于不同数据版本的数据的管理，我们对每一个列族支持两个设定，以便于Bigtable对表项的版本自动进行垃圾清除。用户可以指明只保留表项的最后n个版本，或者只保留足够新的版本（比如，只保留最近7天的内容）。<br />
在Webtable举例中，我们在contents:列中存放确切爬行一个网页的时间戳。如上所述的垃圾清除机制可以让我们只保留每个网页的最近三个版本。<br />
3.API<br />
BT的API提供了建立和删除表和列族的函数．还提供了函数来修改集群，表和列族的元数据，比如说访问权限．<br />
//  Open the table<br />
Table *T = OpenOrDie(”/bigtable/web/webtable”);<br />
// Write a  new anchor and delete an old anchor<br />
RowMutation r1(T,  “com.cnn.www”);<br />
r1.Set(”anchor:www.<a href="http://www.donevii.com/post/tag/c" class="st_tag internal_tag" rel="tag" title="Posts tagged with c">c</a>-span.org”,  “CNN”);<br />
r1.Delete(”anchor:www.abc.com”);<br />
Operation op;<br />
Apply(&amp;op,  &amp;r1);<br />
图 2:  写入Bigtable.<br />
在BT中，客户应用可以写或者删除值，从每个行中找值，或者遍历一个表中的数据子集．图2的c++代码是使用RowMutation抽象表示来进行一系列的更新（为保证代码精简，没有包括无关的细节）．调用Apply函数，就对Ｗebtable进行了一个原子修改：它为http://www.cnn.com/增加了一个锚点，并删除了另外一个锚点．<br />
Scanner  scanner(T);<br />
ScanStream *stream;<br />
stream =  scanner.FetchColumnFamily(”anchor”);<br />
stream-&gt;SetReturnAllVersions();<br />
scanner.Lookup(”com.cnn.www”);<br />
for  (; !stream-&gt;Done(); stream-&gt;Next()) {<br />
printf(”%s %s %lld  %s\n”,<br />
scanner.RowName(),<br />
stream-&gt;ColumnName(),<br />
stream-&gt;MicroTimestamp(),<br />
stream-&gt;Value());<br />
}<br />
图3:  从Bigtable读数据.<br />
图3的C++代码是使用Scanner抽象来遍历一个行内的所有锚点．客户可以遍历多个列族．有很多方法可以限制一次扫描中产生的行，列和时间戳．  例如，我们可以限制上面的扫描，让它只找到那些匹配正则表达式*.cnn.com的锚点，或者那些时间戳在当前时间前10天的锚点．<br />
BT还支持其他一些更复杂的处理数据的功能．首先，BT支持单行处理．这个功能可以用来对存储在一个行关键字下的数据进行原子的读-修改-写操作．  BT目前不支持跨行关键字的处理，但是它有一个界面，可以用来让客户进行批量的跨行关键字处理操作．其次，BT允许把每个表项用做整数记数器．最后，BT  支持在服务器的地址空间内执行客户端提供的脚本程序．脚本程序的语言是google开发的Sawzall[28]数据处理语言．目前，我们基于的  Sawzall的API还不允许客户脚本程序向BT内写数据，但是它允许多种形式的数据变换，基于任何表达式的过滤和通过多种操作符的摘要．<br />
BT可以和MapReduce[12]一起使用．MapReduce是google开发的大规模并行计算框架．我们为编写了一套外层程序，使BT可以作为MapReduce处理的数据源头和输出结果．<br />
4.建立BT的基本单元<br />
BT是建立在其他数个google框架单元上的．BT使用google分布式文件系统(GFS)[17]来存储日志和数据文件{yeah,  right, what else can it use,  FAT32?}．一个BT集群通常在一个共享的机器池中工作，池中的机器还运行其他的分布式应用{虽然机器便宜的跟白菜似的，可是一样要运行多个程序，命苦的象小白菜}，BT和其他程序共享机器｛BT的瓶颈是ＩＯ/内存，可以和CPU要求高的程序并存｝．BT依赖集群管理系统来安排工作，在共享的机器上管理资源，处理失效机器并监视机器状态｛典型的server  farm结构，BT是上面的应用之一｝．<br />
BT内部存储数据的格式是google  SSTable格式．一个SSTable提供一个从关键字到值的映射，关键字和值都可以是任意字符串．映射是排序的，存储的｛不会因为掉电而丢失｝，不可改写的．可以进行以下操作：查询和一个关键字相关的值；或者根据给出的关键字范围遍历所有的关键字和值．在内部，每个SSTable包含一列数据块（通常每个块的大小是64KB,但是大小是可以配置的｛索引大小是16  bits，应该是比较好的一个数｝）．块索引（存储在SSTable的最后）用来定位数据块；当打开SSTable的时候，索引被读入内存｛性能｝．每次查找都可以用一个硬盘搜索完成｛根据索引算出数据在哪个道上，一个块应该不会跨两个道，没必要省那么点空间｝：首先在内存中的索引里进行二分查找找到数据块的位置，然后再从硬盘读去数据块．最佳情况是：整个SSTable可以被放在内存里，这样一来就不必访问硬盘了．｛想的美，前面是谁口口声声说要跟别人共享机器来着？你把内存占满了别人上哪睡去？｝<br />
BT还依赖一个高度可用的，存储的分布式数据锁服务Chubby[8]｛看你怎么把这个high  performance给说圆喽｝．一个Chubby服务由5个活的备份｛机器｝构成，其中一个被这些备份选成主备份，并且处理请求．这个服务只有在大多数备份都活着并且互相通信的时候才是活的｛绕口令？去看原文吧，是在有出错的前提下的冗余算法｝．当有机器失效的时候，Chubby使用Paxos算法[9,23]来保证备份的一致性｛这个问题还是比较复杂的，建议去看引文了解一下问题本身｝．Chubby提供了一个名字空间，里面包括了目录和小文件｛万变不离其宗｝．每个目录或者文件可以当成一个锁来用，读写文件操作都是原子化的．Chubby客户端的程序库提供了对Chubby文件的一致性缓存｛究竟是提高性能还是降低性能？如果访问是分布的，就是提高性能｝．每个Chubby客户维护一个和Chubby服务的会话．如果一个客户不能在一定时间内更新它的会话，这个会话就过期失效了｛还是针对大server  farm里机器失效的频率设计的｝．当一个会话失效时，其拥有的锁和打开的文件句柄都失效｛根本设计原则：失效时回到安全状态｝．Chubby客户可以在文件和目录上登记回调函数，以获得改变或者会话过期的通知．｛翻到这里，有没有人闻到java的味道了？｝<br />
BT使用Chubby来做以下几个任务：保证任何时间最多只有一个活跃的主备份；来存储BT数据的启动位置（参考5.1节）；发现小表  （tablet）服务器，并完成tablet服务器消亡的善后（5.2节）；存储BT数据的模式信息（每张表的列信息）；以及存储访问权限列表．如果有相当长的时间Chubby不能访问，BT就也不能访问了｛任何系统都有其弱点｝．最近我们在使用11个Chubby服务实例的14个BT集群中度量了这个效果，由于Chubby不能访问而导致BT中部分数据不能访问的平均百分比是0.0047%,这里Chubby不能访问的原因是Chubby本身失效或者网络问题．单个集群里，受影响最大的百分比是0.0326%｛基于文件系统的Chubby还是很稳定的｝.<br />
GFS是一个可扩展的分布式文件系统，用于大型的、分布式的、对大量数据进行访问的应用。它运行于廉价的普通硬件上，但可以提供容错功能。它可以给大量的用户提供总体性能较高的服务。<br />
出处：http://labs.google.com/papers/gfs.html<br />
1、设计概览<br />
（1）设计想定<br />
GFS与过去的分布式文件系统有很多相同的目标，但GFS的设计受到了当前及预期的应用方面的工作量及技术环境的驱动，这反映了它与早期的文件系统明显不同的设想。这就需要对传统的选择进行重新检验并进行完全不同的设计观点的探索。<br />
GFS与以往的文件系统的不同的观点如下：<br />
1、部件错误不再被当作异常，而是将其作为常见的情况加以处理。因为文件系统由成百上千个用于存储的机器构成，而这  些机器是由廉价的普通部件组成并被大量的客户机访问。部件的数量和质量使得一些机器随时都有可能无法工作并且有一部分还可能无法恢复。所以实时地监控、错  误检测、容错、自动恢复对系统来说必不可少。<br />
2、按照传统的标准，文件都非常大。长度达几个GB的文件是很平常的。每个文件通常包含很多应用对象。当经常要处理  快速增长的、包含数以万计的对象、长度达TB的数据集时，我们很难管理成千上万的KB规模的文件块，即使底层文件系统提供支持。因此，设计中操作的参数、  块的大小必须要重新考虑。对大型的文件的管理一定要能做到高效，对小型的文件也必须支持，但不必优化。<br />
3、大部分文件的更新是通过添加  新数据完成的，而不是改变已存在的数据。在一个文件中随机的操作在实践中几乎不存在。一旦写完，文件就只可读，很多数据都有这些特性。一些数据可能组成一  个大仓库以供数据分析程序扫描。有些是运行中的程序连续产生的数据流。有些是档案性质的数据，有些是在某个机器上产生、在另外一个机器上处理的中间数据。  由于这些对大型文件的访问方式，添加操作成为性能优化和原子性保证的焦点。而在客户机中缓存数据块则失去了吸引力。<br />
4、工作量主要由两种读操作构成：对大量数据的流方式的读操作和对少量数据的随机方式的读操作。在前一种读操作中，  可能要读几百KB，通常达  1MB和更多。来自同一个客户的连续操作通常会读文件的一个连续的区域。随机的读操作通常在一个随机的偏移处读几个KB。性能敏感的应用程序通常将对少量  数据的读操作进行分类并进行批处理以使得读操作稳定地向前推进，而不要让它来来回回的读。<br />
5、工作量还包含许多对大量数据进行的、连续的、向文件添加数据的写操作。所写的数据的规模和读相似。一旦写完，文件很少改动。在随机位置对少量数据的写操作也支持，但不必非常高效。<br />
6、系统必须高效地实现定义完好的大量客户同时向同一个文件的添加操作的语义。<br />
（2）系统接口<br />
GFS提供了一个相似地文件系统界面，虽然它没有向POSIX那样实现标准的API。文件在目录中按层次组织起来并由路径名标识。<br />
（3）体系结构：<br />
一个GFS集群由一个master和大量的chunkserver构成，并被许多客户（Client）访问。如图1  所示。Master和  chunkserver通常是运行用户层服务进程的Linux机器。只要资源和可靠性允许，chunkserver和client可以运行在同一个机器  上。<br />
文件被分成固定大小的块。每个块由一个不变的、全局唯一的64位的chunk－handle标识，chunk－ handle是在块创建时由  master分配的。ChunkServer将块当作Linux文件存储在本地磁盘并可以读和写由chunk－handle和位区间指定的数据。出于可靠  性考虑，每一个块被复制到多个chunkserver上。默认情况下，保存3个副本，但这可以由用户指定。<br />
Master维护文件系统所以的元数据（metadata），包括名字空间、访问控制信息、从文件到块的映射以及块  的当前位置。它也控制系统范围的活动，如块租约（lease）管理，孤儿块的垃圾收集，chunkserver间的块迁移。Master定期通过  HeartBeat消息与每一个  chunkserver通信，给chunkserver传递指令并收集它的状态。<br />
与每个应用相联的GFS客户代码实现了文件系统的API并与master和chunkserver通信以代表应用程序读和写数据。客户与master的交换只限于对元数据（metadata）的操作，所有数据方面的通信都直接和chunkserver联系。<br />
客户和chunkserver都不缓存文件数据。因为用户缓存的益处微乎其微，这是由于数据太多或工作集太大而无法  <a href="http://www.donevii.com/post/tag/%e7%bc%93%e5%ad%98" class="st_tag internal_tag" rel="tag" title="Posts tagged with 缓存">缓存</a>。不缓存数据简化了客户程序和整个系统，因为不必考虑缓存的一致性问题。但用户缓存元数据（metadata）。Chunkserver也不必缓存文  件，因为块时作为本地文件存储的。<br />
（4）单master。<br />
只有一个master也极大的简化了设计并使得master可以根据全局情况作出先进的块放置和复制决定。但是我们  必须要将master对读和写的参与减至最少，这样它才不会成为系统的瓶颈。Client从来不会从master读和写文件数据。Client只是询问  master它应该和哪个  chunkserver联系。Client在一段限定的时间内将这些信息缓存，在后续的操作中Client直接和chunkserver交互。<br />
以图1解释一下一个简单的读操作的交互。<br />
1、client使用固定的块大小将应用程序指定的文件名和字节偏移转换成文件的一个块索引（chunk  index）。<br />
2、给master发送一个包含文件名和块索引的请求。<br />
3、master回应对应的chunk  handle和副本的位置（多个副本）。<br />
4、client以文件名和块索引为键缓存这些信息。（handle和副本的位置）。<br />
5、Client  向其中一个副本发送一个请求，很可能是最近的一个副本。请求指定了chunk handle（chunkserver以chunk  handle标识chunk）和块内的一个字节区间。<br />
6、除非缓存的信息不再有效（<a href="http://www.donevii.com/post/tag/cache" class="st_tag internal_tag" rel="tag" title="Posts tagged with cache">cache</a> for a limited  time）或文件被重新打开，否则以后对同一个块的读操作不再需要client和master间的交互。<br />
通常Client可以在一个请求中询问多个chunk的地址，而master也可以很快回应这些请求。<br />
（5）块规模：<br />
块规模是设计中的一个关键参数。我们选择的是64MB，这比一般的文件系统的块规模要大的多。每个块的副本作为一个普通的Linux文件存储，在需要的时候可以扩展。<br />
块规模较大的好处有：<br />
1、减少client和master之间的交互。因为读写同一个块只是要在开始时向master请求块位置信息。对于读写大型文件这种减少尤为重要。即使对于访问少量数据的随机读操作也可以很方便的为一个规模达几个TB的工作集缓缓存块位置信息。<br />
2、Client在一个给定的块上很可能执行多个操作，和一个chunkserver保持较长时间的TCP连接可以减少网络负载。<br />
3、这减少了master上保存的元数据（metadata）的规模，从而使得可以将metadata放在内存中。这又会带来一些别的好处。<br />
不利的一面：<br />
一个小文件可能只包含一个块，如果很多Client访问改文件的话，存储这些块的chunkserver将成为访问的热点。但在实际应用中，应用程序通常顺序地读包含多个块的文件，所以这不是一个主要问题。<br />
（6）元数据（metadata）：<br />
master存储了三中类型的metadata：文件的名字空间和块的名字空间，从文件到块的映射，块的副本的位  置。所有的metadata都放在内存中。前两种类型的metadata通过向操作日志登记修改而保持不变，操作日志存储在master的本地磁盘并在几  个远程机器上留有副本。使用日志使得我们可以很简单地、可靠地更新master的状态，即使在master崩溃的情况下也不会有不一致的问题。相反，  mater在每次启动以及当有  chuankserver加入的时候询问每个chunkserver的所拥有的块的情况。<br />
A、内存数据结构：<br />
因为metadata存储在内存中，所以master的操作很快。进一步，master可以轻易而且高效地定期在后台扫描它的整个状态。这种定期地扫描被用于实现块垃圾收集、chunkserver出现故障时的副本复制、为平衡负载和磁盘空间而进行的块迁移。<br />
这种方法的一个潜在的问题就是块的数量也即整个系统的容量是否受限与master的内存。实际上，这并不是一个严重  的问题。Master为每个  64MB的块维护的metadata不足64个字节。除了最后一块，文件所有的块都是满的。类似的，每个文件的名字空间数据也不足64个字节，因为文件名  是以一种事先确定的压缩方式存储的.如果要支持更大的文件系统，那么增加一些内存的方法对于我们将元数据（metadata）保存在内存种所获得的简单  性、可靠性、高性能和灵活性来说，这只是一个很小的代价。<br />
B、块位置：<br />
master并不为chunkserver所拥有的块的副本的保存一个不变的记录。它在启动时通过简单的查询来获得这些信息。Master可以保持这些信息的更新，因为它控制所有块的放置并通过HeartBeat消息来监控chunkserver的状态。<br />
这样做的好处：因为chunkserver可能加入或离开集群、改变路径名、崩溃、重启等，一个集群重有成百个server，这些事件经常发生，这种方法就排除了master与chunkserver之间的同步问题。<br />
另一个原因是：只有chunkserver才能确定它自己到底有哪些块，由于错误，chunkserver中的一些块可能会很自然的消失，这样在master中就没有必要为此保存一个不变的记录。<br />
C、操作日志：<br />
操作日志包含了对metadata所作的修改的历史记录。它作为逻辑时间线定义了并发操作的执行顺序。文件、块以及它们的版本号都由它们被创建时的逻辑时间而唯一地、永久地被标识。<br />
操作日志是如此的重要，我们必须要将它可靠地保存起来，并且只有在metadata的改变固定下来之后才将变化呈现给用户。所以我们将操作日志复制到数个远程的机器上，并且只有在将相应的日志记录写到本地和远程的磁盘上之后才回答用户的请求。<br />
Master可以用操作日志来恢复它的文件系统的状态。为了将启动时间减至最小，日志就必须要比较小。每当日志的长度增长到超过一定的规模后，master就要检查它的状态，它可以从本地磁盘装入最近的检查点来恢复状态。<br />
创建一个检查点比较费时，master的内部状态是以一种在创建一个检查点时并不耽误即将到来的修改操作的方式来组  织的。Master切换到一个新的日子文件并在一个单独的线程中创建检查点。这个新的检查点记录了切换前所有的修改。在一个有数十万文件的集群中用一分钟  左右就能完成。创建完后，将它写入本地和远程的磁盘。<br />
（7）数据完整性<br />
名字空间的修改必须是原子性的，它们只能有master处理：名字空间锁保证了操作的原子性和正确性，而master的操作日志在全局范围内定义了这些操作的顺序。<br />
文  件区间的状态在修改之后依赖于修改的类型，不论操作成功还是失败，也不论是不是并发操作。如果不论从哪个副本上读，所有的客户都看到同样的数据，那么文件  的这个区域就是一致的。如果文件的区域是一致的并且用户可以看到修改操作所写的数据，那么它就是已定义的。如果修改是在没有并发写操作的影响下完成的，那  么受影响的区域是已定义的，所有的client都能看到写的内容。成功的并发写操作是未定义但却是一致的。失败的修改将使区间处于不一致的状态。<br />
Write操作在应用程序指定的偏移处写入数据，而record  append操作使得数据（记录）即使在有并发修改操作的情况下也至少原子性的被加到GFS指定的偏移处，偏移地址被返回给用户。<br />
在一系列成功的修改操作后，最后的修改操作保证文件区域是已定义的。GFS通过对所有的副本执行同样顺序的修改操作并且使用块版本号检测过时的副本（由于chunkserver退出而导致丢失修改）来做到这一点。<br />
因为用户缓存了会位置信息，所以在更新缓存之前有可能从一个过时的副本中读取数据。但这有缓存的截止时间和文件的重新打开而受到限制。<br />
在修改操作成功后，部件故障仍可以是数据受到破坏。GFS通过master和chunkserver间定期的handshake，借助校验和来检测对数据的破坏。一旦检测到，就从一个有效的副本尽快重新存储。只有在GFS检测前，所有的副本都失效，这个块才会丢失。<br />
2、系统交互<br />
（1）租约（lease）和修改顺序<br />
（2）数据流<br />
我们的目标是充分利用每个机器的网络带宽，避免网络瓶颈和延迟<br />
为了有效的利用网络，我们将数据流和控制流分离。数据是以流水线的方式在选定的chunkerserver链上线性的传递的。每个机器的整个对外带宽都被用作传递数据。为避免瓶颈，每个机器在收到数据后，将它收到数据尽快传递给离它最近的机器。<br />
（3）原子性的record  Append：<br />
GFS提供了一个原子性的添加操作：record  append。在传统的写操作中，client指定被写数据的偏移位置，向同一个区间的并发的写操作是不连续的：区间有可能包含来自多个client的数  据碎片。在record append中，  client只是指定数据。GFS在其选定的偏移出将数据至少原子性的加入文件一次，并将偏移返回给client。<br />
在分布式的应用中，不同机器上的许多client可能会同时向一个文件执行添加操作，添加操作被频繁使用。如果用传  统的write操作，可能需要额外的、复杂的、开销较大的同步，例如通过分布式锁管理。在我们的工作量中，这些文件通常以多个生产者单个消费者队列的方式  或包含从多个不同 client的综合结果。<br />
Record  append和前面讲的write操作的控制流差不多，只是在primary上多了一些逻辑判断。首先，client将数据发送到文件最后一块的所有副本  上。然后向primary发送请求。Primary检查添加操作是否会导致该块超过最大的规模（64M）。如果这样，它将该块扩充到最大规模，并告诉其它  副本做同样的事，同时通知client该操作需要在下一个块上重新尝试。如果记录满足最大规模的要求，primary就会将数据添加到它的副本上，并告诉  其它的副本在在同样的偏移处写数据，最后primary向client报告写操作成功。如果在任何一个副本上record  append操作失败，client将重新尝试该操作。这时候，同一个块的副本可能包含不同的数据，因为有的可能复制了全部的数据，有的可能只复制了部  分。GFS不能保证所有的副本每个字节都是一样的。它只保证每个数据作为一个原子单元被写过至少一次。这个是这样得出的：操作要是成功，数据必须在所有的  副本上的同样的偏移处被写过。进一步，从这以后，所有的副本至少和记录一样长，所以后续的记录将被指定到更高的偏移处或者一个不同的块上，即使另一个副本  成了primary。根据一致性保证，成功的record  append操作的区间是已定义的。而受到干扰的区间是不一致的。<br />
（4）快照（snapshot）<br />
快照操作几乎在瞬间构造一个文件和目录树的副本，同时将正在进行的其他修改操作对它的影响减至最小。<br />
我们使用copy-on-write技术来实现snapshot。当master受到一个snapshot请求时，  它首先将要snapshot的文件上块上的lease。这使得任何一个向这些块写数据的操作都必须和master交互以找到拥有lease的副本。这就给  master一个创建这个块的副本的机会。<br />
副本被撤销或终止后，master在磁盘上登记执行的操作，然后复制源文件或目录树的metadata以对它的内存状态实施登记的操作。这个新创建的snapshot文件和源文件（其metadata）指向相同的块（chunk）。<br />
Snapshot之后，客户第一次向chunk  c写的时候，它发一个请求给master以找到拥有lease的副本。Master注意到chunk c的引用记数比1大，它延迟对用户的响应，选择一个chunk  handle C’,然后要求每一有chunk c的副本的chunkserver创建一个块C’。每个chunkserver在本地创建chunk  C’避免了网络开销。从这以后和对别的块的操作没有什么区别。<br />
3、MASTER操作<br />
MASTER执行所有名字空间的操作，除此之外，他还在系统范围管理数据块的复制：决定数据块的放置方案，产生新数据块并将其备份，和其他系统范围的操作协同来确保数据备份的完整性，在所有的数据块服务器之间平衡负载并收回没有使用的存储空间。<br />
3.1  名字空间管理和加锁<br />
与传统文件系统不同的是，GFS没有与每个目录相关的能列出其所有文件的数据结构，它也不支持别名（unix中的硬连接或符号连接），不管是对文件或是目录。GFS的名字空间逻辑上是从文件元数据到路径名映射的一个查用表。<br />
MASTER在执行某个操作前都要获得一系列锁，例如，它要对/d1/d2…/dn/leaf执行操作，则它必须获  得/d1，/d1/d2，…，  /d1/d2/…/dn的读锁，/d1/d2…/dn/leaf的读锁或写锁（其中leaf可以使文件也可以是目录）。MASTER操作的并行性和数据的  一致性就是通过这些锁来实现的。<br />
3.2  备份存储放置策略<br />
一个GFS集群文件系统可能是多层分布的。一般情况下是成千上万个文件块服务器分布于不同的机架上，而这些文件块服  务器又被分布于不同机架上的客户来访问。因此，不同机架上的两台机器之间的通信可能通过一个或多个交换机。数据块冗余配置策略要达到连个目的：最大的数据  可靠性和可用性，最大的网络带宽利用率。因此，如果仅仅把数据的拷贝置于不同的机器上很难满足这两个要求，必须在不同的机架上进行数据备份。这样即使整个  机架被毁或是掉线，也能确保数据的正常使用。这也使数据传输，尤其是读数据，可以充分利用带宽，访问到多个机架，而写操作，则不得不涉及到更多的机架。<br />
3.3  产生、重复制、重平衡数据块<br />
当MASTER产生新的数据块时，如何放置新数据块，要考虑如下几个因素：（1）尽量放置在磁盘利用率低的数据块服  务器上，这样，慢慢地各服务器的磁盘利用率就会达到平衡。（2）尽量控制在一个服务器上的“新创建”的次数。（3）由于上一小节讨论的原因，我们需要把数  据块放置于不同的机架上。<br />
MASTER在可用的数据块备份低于用户设定的数目时需要进行重复制。这种情况源于多种原因：服务器不可用，数据被  破坏，磁盘被破坏，或者备份数目被修改。每个被需要重复制的数据块的优先级根据以下几项确定：第一是现在的数目距目标的距离，对于能阻塞用户程序的数据  块，我们也提高它的优先级。最后，  MASTER按照产生数据块的原则复制数据块，并把它们放到不同的机架内的服务器上。<br />
MASTER周期性的平衡各服务器上的负载：它检查chunk分布和负载平衡，通过这种方式来填充一个新的服务器而  不是把其他的内容统统放置到它上面带来大量的写数据。数据块放置的原则与上面讨论的相同，此外，MASTER还决定那些数据块要被移除，原则上他会清除那  些空闲空间低于平均值的那些服务器。<br />
3.4  垃圾收集<br />
在一个文件被删除之后，GFS并不立即收回磁盘空间，而是等到垃圾收集程序在文件和数据块级的的检查中收回。<br />
当一个文件被应用程序删除之后，MASTER会立即记录下这些变化，但文件所占用的资源却不会被立即收回，而是重新  给文件命了一个隐藏的名字，并附上了删除的时间戳。在MASTER定期检查名字空间时，它删除超过三天（可以设定）的隐藏的文件。在此之前，可以以一个新  的名字来读文件，还可以以前的名字恢复。当隐藏的文件在名字空间中被删除以后，它在内存中的元数据即被擦除，这就有效地切断了他和所有数据块的联系。<br />
在一个相似的定期的名字空间检查中，MASTER确认孤儿数据块（不属于任何文件）并擦除他的元数据，在和MASTER的心跳信息交换中，每个服务器报告他所拥有的数据块，MASTER返回元数据不在内存的数据块，服务器即可以删除这些数据块。<br />
3.5  过时数据的探测<br />
在数据更新时如果服务器停机了，那么他所保存的数据备份就会过时。对每个数据块，MASTER设置了一个版本号来区别更新过的数据块和过时的数据块。<br />
当MASTER授权一个新的lease时，他会增加数据块的版本号并会通知更新数据备份。MASTER和备份都会记  录下当前的版本号，如果一个备份当时不可用，那么他的版本号不可能提高，当ChunkServer重新启动并向MASTER报告他的数据块集时，  MASTER就会发现过时的数据。<br />
MASTER在定期的垃圾收集程序中清除过时的备份，在此以前，处于效率考虑，在各客户及英大使，他会认为根本不存  在过时的数据。作为另一个安全措施，  MASTER在给客户及关于数据块的应答或是另外一个读取数据的服务器数据是都会带上版本信息，在操作前客户机和服务器会验证版本信息以确保得到的是最新  的数据。<br />
4、容错和诊断<br />
4.1 高可靠性<br />
4.1.1  快速恢复<br />
不管如何终止服务，MASTER和数据块服务器都会在几秒钟内恢复状态和运行。实际上，我们不对正常终止和不正常终止进行区分，服务器进程都会被切断而终止。客户机和其他的服务器会经历一个小小的中断，然后它们的特定请求超时，重新连接重启的服务器，重新请求。<br />
4.1.2  数据块备份<br />
如上文所讨论的，每个数据块都会被备份到放到不同机架上的不同服务器上。对不同的名字空间，用户可以设置不同的备份级别。在数据块服务器掉线或是数据被破坏时，MASTER会按照需要来复制数据块。<br />
4.1.3  MASTER备份<br />
为确保可靠性，MASTER的状态、操作记录和检查点都在多台机器上进行了备份。一个操作只有在数据块服务器硬盘上  刷新并被记录在MASTER和其备份的上之后才算是成功的。如果MASTER或是硬盘失败，系统监视器会发现并通过改变域名启动它的一个备份机，而客户机  则仅仅是使用规范的名称来访问，并不会发现MASTER的改变。<br />
4.2  数据完整性<br />
每个数据块服务器都利用校验和来检验存储数据的完整性。原因：每个服务器随时都有发生崩溃的可能性，并且在两个服务器间比较数据块也是不现实的，同时，在两台服务器间拷贝数据并不能保证数据的一致性。<br />
每个Chunk按64kB的大小分成块，每个块有32位的校验和，校验和和日志存储在一起，和用户数据分开。<br />
在读数据时，服务器首先检查与被读内容相关部分的校验和，因此，服务器不会传播错误的数据。如果所检查的内容和校验  和不符，服务器就会给数据请求者返回一个错误的信息，并把这个情况报告给MASTER。客户机就会读其他的服务器来获取数据，而MASTER则会从其他的  拷贝来复制数据，等到一个新的拷贝完成时，MASTER就会通知报告错误的服务器删除出错的数据块。<br />
附加写数据时的校验和计算优化了，因为这是主要的写操作。我们只是更新增加部分的校验和，即使末尾部分的校验和数据已被损坏而我们没有检查出来，新的校验和与数据会不相符，这种冲突在下次使用时将会被检查出来。<br />
相反，如果是覆盖现有数据的写，在写以前，我们必须检查第一和最后一个数据块，然后才能执行写操作，最后计算和记录校验和。如果我们在覆盖以前不先检查首位数据块，计算出的校验和则会因为没被覆盖的数据而产生错误。<br />
在空闲时间，服务器会检查不活跃的数据块的校验和，这样可以检查出不经常读的数据的错误。一旦错误被检查出来，服务器会拷贝一个正确的数据块来代替错误的。<br />
4.3  诊断工具<br />
广泛而细致的诊断日志以微小的代价换取了在问题隔离、诊断、性能分析方面起到了重大的作用。GFS服务器用日志来记  录显著的事件（例如服务器停机和启动）和远程的应答。远程日志记录机器之间的请求和应答，通过收集不同机器上的日志记录，并对它们进行分析恢复，我们可以  完整地重现活动的场景，并用此来进行错误分析。<br />
5 测量<br />
5.1  测试环境<br />
一台主控机，两台主控机备份，16台数据块服务器，16台客户机。<br />
每台机器：2块PIII1.4G处理器，2G内存，2块80G5400rpm的硬盘，1块100Mbps全双工网卡<br />
19台服务器连接到一个HP2524交换机上，16台客户机俩接到领外一台交换机上，两台交换机通过1G的链路相连。</p>
<p>原文转自：http://bigweb.group.javaeye.com/group/topic/5749</p>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/504.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转] 移动互联网的终端革命</title>
		<link>http://www.donevii.com/post/402.html</link>
		<comments>http://www.donevii.com/post/402.html#comments</comments>
		<pubDate>Thu, 01 May 2008 18:10:51 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[mobile]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[手机]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[生活]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[芯片]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/402.html</guid>
		<description><![CDATA[From: http://www.bouhe.com/mobile-marketing/1694 诺基亚、爱立信公司的首席技术技官都已驻扎在硅谷办公，他们不得不来到这里，因为他们突然发现苹果和谷歌已经开始威胁到他们的业务。2007年，iPhone、... ]]></description>
			<content:encoded><![CDATA[<p>From: <a title="http://www.bouhe.com/mobile-marketing/1694" href="http://www.bouhe.com/mobile-marketing/1694">http://www.bouhe.com/mobile-marketing/1694</a>    </p>
<p>诺基亚、爱立信公司的首席技术技官都已驻扎在硅谷办公，他们不得不来到这里，因为他们突然发现苹果和谷歌已经开始威胁到他们的业务。2007年，iPhone、Gphone无疑是最热门的词汇，就连微软也提出了Mphone(Multi-phone)的概念，认为手机应该融合多种无线技术、多种输入技术和多种应用功能。 </p>
<p>&#8220;2008年会成为MID(移动互联网设备)元年。&#8221;业内人士预测。这一判断主要是基于明年上半年英特尔公司即将推出可以放在口袋里的移动终端，而45纳米技术的突破以及多核技术的应用，可以满足MID芯片的移动性和无处不在的计算，并同时降低功耗和尺寸。作为PC互联网时代的最大赢家，英特尔希望将其霸主地位延伸到移动终端领域。就连全球最大的网络书店亚马逊也新推出了手持终端&#8212;&#8212;电子书阅读器，开始软硬兼备。</p>
<p>随着手机和互联网的融合，越来越多的公司开始涉足手机及其他移动终端的生意，硅谷已然变成了手机谷。而围绕着终端的变革，原有的硬件、软件、服务、内容等公司都开始跨越产业链上的多个环节，致使原有的产业边界越来越模糊。以往我们耳熟能详的公司如诺基亚、苹果、谷歌等已经不能单一定位了，它们早已不单是手机、电脑和搜索公司了，而是在移动互联网的大背景下，走到了同一条竞争线上。</p>
<p>随着微软、英特尔、诺基亚、谷歌、苹果等全球巨头加入到移动互联网的战役中，既有的产业格局也会出现新的变化。苹果和谷歌已经成为事实上的虚拟运营商，在它们的平台上会开发和开放各种应用，移动运营商未来的地位会下降。事实上，苹果公司一直和AT&amp;T等移动运营商捆绑销售，并和运营商进行分成。而谷歌打算用46亿美元竞拍美国700MHz的一个无线频段，更有传言，谷歌会收购美国第三大电信运营商Sprint Nextel。 </p>
<p> 相比苹果、谷歌对移动通信市场的入侵，中国的移动互联网行业还呈现比较封闭的状态，但这并没影响到国内众多创业公司的参与热情。曾有VC感慨，现在只要是和手机相关的商业计划书，十之八九都是手机客户端软件，例如手机聊天、手机下载等各色各样的手机软件。这些公司几乎百分之百都号称要做成手机平台，以黏住用户。 </p>
<p>移动终端的影响力之大，在于其离用户最近。作为最终用户，人们与外界的联系节点已由最早家庭的邮政编码，到PC时代的一串IP地址，再转移到移动电话的手机号码。联系的终端越变越小，但却越来越跟随、紧贴用户。在众多公司抢占终端的过程中，用户需求成为最终的变量。在这一点上，无论是iPhone还是Wii的热卖，在于其最先洞察并引领用户需求，通过改善人际交互的界面方式，将用户从键盘中解放出来，满足用户对于自由的渴望。 </p>
<p> 但仅仅成为一个酷品还不足够，用户更需要终端上的服务和内容。在这一点上，iPhone还远远不够。移动互联网不仅仅是将互联网搬到手机上，看YouTube、上Facebook，随着医疗健康、教育、金融等服务在手机上实现，一定会诞生新的商业模式。 </p>
<p>一台便携的移动终端，随时随地连接互联网，围绕这个目标，移动运营商、芯片厂商、终端厂商、互联网服务商、内容提供商史无前例地集体参战，成为过去一年最热闹的场景。而在这场战争背后，首先是对终端的争夺，人机交互界面从键盘到多点触控(multi-touch)率先成为风潮。 </p>
<p>当苹果公司的iPhone在欧美手机市场刮起旋风的时候，另外一个手机&#8220;门外汉&#8221;也把自己的利益触角伸向了这个领域，这家公司就是全球最大的互联网公司谷歌。</p>
<p> 对于这家在互联网上取得了巨大成就的公司来说，谷歌进入手机市场并不需要像苹果公司一样自己设计并制造出一款特立独行的酷品手机GPhone来争取用户，而是采用了它更为擅长的方式&#8212;&#8212;用开放的软件平台使自己成为一个联盟的组织者，就像在互联网上用AdSense建立了自己的广告联盟一样。</p>
<p> 在2007年11月5日谷歌宣布的手机联盟中有34家企业成员，包括摩托罗拉、T-Mobile、HTC(宏达电)、高通及中国移动等34家全世界不同国家和地区的移动运营商、手机芯片提供商和手机生产商等等。谷歌将为联盟的成员免费开放一个基于Linux的手机操作系统平台&#8220;Android&#8221;。谷歌称，这个平台是完全开放的综合性移动平台，供联盟成员在此基础上开发新产品、改进服务和压缩用户成本。 </p>
<p> 在互联网上，谷歌用这种免费提供软件的方式获得了大量的广告收入，2007年前三个季度，谷歌的广告收入达到了约118亿美元，它的这种模式将一直靠卖操作系统软件为生的微软搞得非常被动，如今谷歌又希望能在手机平台上重演这一幕。 </p>
<p> 在苹果、谷歌这样的新势力纷纷用新模式、新平台进入封闭的电信市场的背景下，以手机为最终表现形式的移动通信市场会发生怎样的深刻变化？也许我们可以从25年前发生在PC行业上的一切来寻找答案。 </p>
<p>25年前，整个PC行业还处于群雄割据的状态，没有一个整体性的产业标准，各个PC制造商都有属于自己的一套独立的操作系统，他们靠自己强大的渠道和产品设计能力去争取用户，而微软在这个时候却用软件捆绑机器的销售方式统一了大多数的PC平台，一定意义上这也统一了PC行业。 </p>
<p>回到现在正在发生剧烈变化的手机行业里，当手机作为基本语音通话工具时，固定标准是存在的，而当手机逐渐发展成为一个个人随身数据平台，类似于一台小型PC时，这似乎又回到了25年前PC行业的混乱时代，在这个混乱的各自为政的时代，用何种模式、何种平台来统一手机终端就成为了未来这个产业中最大的悬念，在这个悬念破解之后，又会在其背后引发一系列更大更复杂的产业变革。 </p>
<p>统一平台下的入侵计划 </p>
<p> 中国是全球最大的移动市场，在2007年11月底，中国手机用户数已经超过了5.39亿。如此庞大的市场也驱使众多的风险投资及创业者在其中淘金，北京天腾时空信息科技有限公司就是其中之一。 </p>
<p> 这家由创业基金扶持的公司成立于 2005 年，号称拥有领先的移动多媒体通信和软件研发实力，他们正在开发适用于不同型号手机的第三方客户端软件。这项工作并不容易，因为中国市场上的手机型号实在是太多了，他们要根据系统的不同、机型的不同来对开发出的软件做出调整。尽管如此，天腾时空的CEO马腾对未来还是充满着信心：&#8220;我们正在构建一个移动生活圈，把互联网上的东西都带到手机上，我们的目标就是让所有人自由，让大家的网络生活摆脱那根网线和电源线。&#8221; </p>
<p> 把互联网上的所有东西带进小小的手机终端，这个梦想驱使着中国无数的创业公司在其中寻找着机会，尽管他们坚信手机与互联网的结合是未来的趋势，但是以他们的力量，在中国移动把持的移动运营市场上还是显得太薄弱，现在有了全球最大的互联网公司谷歌的加入，互联网侵入手机的征程就显得不再那么坎坷了。</p>
<p> 在谷歌看来，全球有30亿部手机，但是在大多数情况下，手机只被设定为语音通信和发送短消息的一种工具，通过手机上网获取及发布信息的功能被忽略掉了。&#8220;这给用户带来了不必要的麻烦，并限制了他们可以享用到的服务。&#8221;谷歌移动服务总监Andy Rubin认为，这样的缺陷实际上是过去几十年间移动通信行业迅猛发展的副产品，尤其是移动电话的市场割据更加深了这一点。在这种情况下，手机制造商有充足的商业理由来开发他们各自的受保护的软件，而且运营商也更在意他们眼前所能获得的利益。 </p>
<p>这种&#8220;各自为政&#8221;的发展方式意味着应用服务的提供商必须花大量的精力来实现不同系统的兼容，他们大量的精力和技术人员都花在了如何让自己的软件能顺畅地运行在不同型号的手机上，而不是从用户的利益出发进行创新。 </p>
<p> 缺乏共享软件平台，也打击了移动领域外的商业力量的积极性，他们不愿意投入更多的时间和资金来挖掘手机更广泛的应用潜力。每当提起正在做手机客户端及免费WAP的公司，风险投资商们都会苦笑着摇摇头。&#8220;这种缺乏统一平台的做法使研发的成本居高不下，从而阻碍了创新。&#8221;Andy Rubin说。 </p>
<p>谷歌的Android平台至少让我们能看到这样一种可能，即在开放的互联网运作模式下，一个如YouTube、Facebook这样的创意，都能通过一个统一的平台将服务惠泽于互联网上的每个人。这也是互联网上能产生对用户有利并促进商业公司收入增长的新服务及新模式的主要原因。 </p>
<p> 现在，要让互联网顺利的入侵进手机，谷歌所要做的就是用免费提供软件的方式先尽可能地统一手机的平台，然后再依托互联网向这个被自己洞开的门户源源不断地输入各种应用和内容。</p>
<p> &#8220;用这款诺基亚6131i手机坐公交车可以直接当公交卡使用&#8221;，&#8220;可以直接在诺基亚手机上学习新东方的英语课程。&#8221;2007年12月11日，在北京举办的诺基亚&#8220;完全互联生活2008&#8221;大会的一侧，众多诺基亚旗下的公司及合作伙伴展示着诺基亚基于移动互联网推出的新的应用服务。其全球副总裁邓元鋆也宣布诺基亚从2007年开始已经成为了一家互联网公司。2007年8月，诺基亚就已经在全球宣布推出了Ovi(芬兰语中意为&#8220;门户&#8221;)网络服务，服务内容包括了在线地图、在线游戏和在线音乐商店。 </p>
<p> 最近几年来，诺基亚在全球收购了一大批提供各种互联网应用服务的公司：2006年8月，斥资6000万美元收购了拥有160万首歌曲的音乐网站Loudeye；2007年7月，收购美国多媒体公司Twango，Twango主要提供为互联网上共享、发布多媒体信息搭建平台的服务；紧接着在9月份，他们又收购了一家名为Enpocket的移动广告公司；10月初，诺基亚又宣布以81亿美元收购美国的Navteq公司，Navteq是汽车导航系统、个人导航设备和其他企业数字地图及基于网络地图系统的主要供应商，同时还拥有提供美国交通信息的Traffic.com网站，这也是诺基亚迄今为止最大的一笔收购； 12月，诺基亚又收购了为手机用户提供共享电脑文件的Avvenu公司。 </p>
<p> 一系列的收购行动使这家全球最大的手机制造商逐渐成为了一家提供互联网综合服务的公司，通过整合一系列的应用，他们将互联网服务引入到了自己生产的手机平台上。 </p>
<p> 诺基亚是全球手机行业的霸主，在全球手机市场上的市场占有率接近40%，在中国，2007年的前三季度，他们就卖出了5050万部手机，目前约有超过1.3亿中国用户在使用诺基亚。作为一家处于产业链下游的公司，诺基亚已经不满足于只是制造和销售手机了，它不仅要牢牢掌握住终端，而且还想把握住这个未来的信息渠道。 </p>
<p> 谷歌成立的Android联盟在一定程度上也在威胁着诺基亚。诺基亚是另一个手机软件平台Symbian的控制者，虽然诺基亚控制的Symbian联盟不具备像Android联盟这样将移动终端、<a href="http://www.donevii.com/post/tag/%e8%8a%af%e7%89%87" class="st_tag internal_tag" rel="tag" title="Posts tagged with 芯片">芯片</a>、运营、软件应用、设计等产业链的各个层面的力量都集成在一起的能力。不过依靠市场占有率的绝对优势，诺基亚并不惧怕手机领域的新闯入者，2007年上半年Symbian智能手机在全球共出货3460万部，Symbian智能手机累计出货量已经达到1.45亿部，在智能手机市场的份额达到72%。目前诺基亚、摩托罗拉和索尼爱立信手握全球六成以上的市场占有率，这意味着Symbian至少将在相当一段时间内横行智能手机市场。 </p>
<p> 诺基亚想用自己的实际行动证明，诺基亚的手机霸主地位是任何一家公司都无法撼动的，现在它在维持霸主地位的同时正主动向互联网靠拢。显然不用怀疑诺基亚适应变化的能力，它已有将近140年的历史，之前卖过劳保用品，也制造过电视机，因此，诺基亚公司坚信，与其自身所经历过的困难相比，目前向互联网转型的所有困难都显得微不足道。 </p>
<p>重构手机价值链 </p>
<p>整个移动通信领域，2007年最受关注的公司是苹果。自从6月份推出iPhone以来，iPhone至今已销售了超过140万部。更为重要的是，iPhone的出现开启了一个新型的与电信运营商分享收益的模式。 </p>
<p> 苹果运用的是彻底封闭型平台策略，苹果通过与当地电信运营商的业务捆绑销售产品，从用户收入中抽取一定分成。苹果在美国与运营商AT&amp;T、在德国与T-Mobile、在英国与O2、在法国与Orange都采取了这样的合作方式。以苹果与AT&amp;T的合作为例：苹果公司每月从已经与AT&amp;T签署iPhone服务协议的每位用户中可抽取3美元费用；而新用户，苹果每月则可从每位用户中抽取高达11美元的费用。 </p>
<p>为什么苹果能与移动运营商分成？答案之一就是，苹果的iPhone能通过WiFi的方式接入互联网，使得每位用户的ARPU值增加，为运营商带来除语音通话之外的数据业务收入。</p>
<p> 曾传言苹果在2007年11月份与中国移动探讨过这种合作模式，但是对于靠语音及短信业务为主、且在移动产业链中处于主导地位的中国移动来说，他们并不喜欢手机产业中新出现的一些商业模式。这也证明了谷歌移动服务总监Andy Rubin对封闭的电信行业的判断，利润流向了少数的几家具有垄断性质的手机制造商与移动运营商那里，这种方式有碍于这个行业创新应用的产生。</p>
<p>随着谷歌这样的互联网公司将触角逐渐伸向电信领域，并且诺基亚也在主动向着互联网转型，曾经强硬封闭的运营商也只能慢慢地走向开放。移动业务价值链势必会有一个重构的过程。 </p>
<p>据悉，谷歌打算用46亿美元竞拍美国700MHz的一个无线频段，并向美国联邦通信委员会(FCC)提出了参加拍卖的四个条件：要求获胜者向手机用户开放应用，开放设备，向移动运营商开放批发服务，向内容提供商开放网络。这就意味着，谷歌决意要彻底改变原有电信业的商业模式。除此之外，又有传言说谷歌会收购美国第三大电信运营商Sprint Nextel。 </p>
<p>全球最大的互联网公司反过来收购一家电信运营商，如果这样的事情真的在2008年发生，那整个电信行业就会步入一个新的时代，不管怎么说，互联网入侵移动通信市场的序幕已经真的拉开了。</p>
<p>狂欢吧，身体！ </p>
<p> 在电影《迷失东京》中，女主角斯佳丽&#183;约翰逊用好奇的眼神观看着一名玩着跳舞游戏的日本男子，他大汗淋漓，正操作一种需要手舞足蹈甚至360度旋转身体才能配合画面节拍的游戏。在东京，拥有这种运动式大型游戏机的游戏厅非常多，电影选择这个场景也是有意突出西方人对奇特的日式娱乐的惊讶。 </p>
<p>然而，从2006年底开始，一款由日本任天堂公司开发的名为&#8220;Wii&#8221;的游戏机迅速风靡欧美家庭，这款电视遥控器般大小的游戏机正像上世纪80年代的卡拉OK一样，它凭借更接近自然的交互方式让全家老少都能参与到游戏中来。一时间，自由交互式操控的复兴给整个游戏机产业注入了新的活力。</p>
<p> 在Wii诞生之前，游戏业多少有些尴尬：上班族没时间玩，老年人上手又太难，小孩子受到家长的严格监督和管制。实际上，大多数人的游戏经验都只停留在童年，游戏随着玩家长大成人遭到放弃甚至厌恶和抵制。多数人后天的发展不仅没有打开体验的更广阔天地，反而是一步步进入专业化、职业化的路径，再也没机会感受自身生活范围以外的世界。</p>
<p>Wii的出现改变了这一切，Wii的设计师宫本茂也因此成为英国《经济学人》的&#8220;年度创新大奖&#8221;的获得者，&#8220;当今电子游戏界的关键人物&#8221;的评价也从另一个角度证明了Wii的创新价值。 </p>
<p>Wii的重要性在于，设计者宫本茂坚信，游戏必须与每个人的日常生活发生联系；游戏必须更加深入社会，得到整个社会的理解。这可以看作这位游戏设计大师的游戏世界观，正是这样的信仰产生了Wii的颠覆性创想。这种信仰也正好道出了游戏的本质&#8212;&#8212;游戏本来就是生活的延伸，是人们工作之余的一种经验扩展，游戏让人获得习以为常的生活里难以体会到的感受，甚至是经验和知识，怎么能将大部分人隔离于游戏之外呢？ </p>
<p>被束缚的自由 </p>
<p> &#8220;硅谷海盗&#8221;乔布斯似乎比一般人更加难以忍受密密麻麻的按键和狭小的屏幕给人的束缚感，素来爱凭自我感受来判断技术趋势的他坚信：技术进步不应该成为束缚人自由的枷锁。iPhone证明乔布斯又&#8220;赌&#8221;对了。这款手机在把实体按键简化到只剩一个之后，更是利用多点触控(multi-touch)屏幕让自由轻松的触控体验发挥到了极致。他曾经描述，自己过去在使用手机时，看着密密麻麻的按键却找不到一个简单的功能时，头脑中就只有把手机砸掉的冲动，何谈使用它正常工作。</p>
<p>交互设计大师、&#8220;Macintosh&#8221;之父杰夫&#183;拉斯基(Jaf Raskin)早就指出，好的设计不会让使用者养成对今后工作不利的习惯，但设计人员却经常有意无意地给用户设下坏习惯的陷阱。事实上，良好的设计应该在给用户带来帮助的同时，把对其未来可能出现的限制性障碍降到最低，保持使用者自由的可扩展性。 </p>
<p>实际上，界面的不自由长久以来是一个被忽视并且有点积重难返的问题，越来越复杂的设备和使用者界面，大大增加了拉斯基所说的陷阱的数量，而解放使用者自由就需要下更大的决心和冒更大的风险颠覆传统。 </p>
<p>当互联网信息过载让人们淹没在信息海洋中时，谷歌充当了颠覆者的角色，它只在网页上显示一个输入框；当手机开始附加各种原来PC的功能而变得日益复杂，学习成本上升时，iPhone开创性地只保留一个实体按钮。大刀阔斧地删减界面信息、按钮和选项，使用户不用进行多余的思考，即可快速进入想要达成的任务。 </p>
<p> Wii mote被设计成客厅里最具亲和力的电视遥控器样式，让你接触无距离感，拿起它你就知道如何使用，而玩好它却又需要时间和技巧。正如谷歌简单的输入框背后有复杂的运算；苹果电脑薄薄的《使用说明》上第一句话是，&#8220;你的苹果电脑被设计为你能很快设置并且马上开始使用&#8221;，说明书的封面上印着一行灰字：&#8220;Congratulations，you and your MacBook were made for each other.&#8221;很有从此&#8220;人机合一，形影相随，只有我懂你&#8221;的性感意味。</p>
<p>正是因为这样，根据互补资源价值相关的原理，心理需求上互补的、无线的、可移动的和能带来真实触感的资源将变得稀缺而更有价值。这就是解放四肢的Wii和自由触控的iPhone之所以获得极大成功的深层社会心理学原理。</p>
<p>从芯片到设备的连锁爆发</p>
<p> 基于社会心理学上的新需求是商业应用市场爆发的充分而非必要条件，Wii、iPhone等颠覆键盘和鼠标操作方式的产品之所以在2007年爆发，也必须有一些上游技术方面的必要条件。</p>
<p>实际上，从1980年施乐实验室帕洛阿尔托研究中心(PARC)的计算机&#8220;STAR&#8221;算起，&#8220;鼠标+图像化界面&#8221;在计算机上的应用已经走过了37年历史，最早的鼠标连线还是在其后部，更像是老鼠的尾巴 。其实正是CPU芯片计算能力的增强，使得计算机可以模拟出更丰富的图形化界面效果，鼠标这种低技术含量但实用的发明才有用武之地。 </p>
<p> 同样，iPhone的多点触控技术和更简单的Wii mote手柄的单点触控技术都不是新发明，它们之所以在2007年大行其道，除了符合人们普遍对密密麻麻的键盘和狭小屏幕的厌烦心理，更根本的原因是芯片计算能力的进一步增强。 LUPA开源社区&#8217;B!I*G2^ m5T-P1^</p>
<p>英特尔高级副总裁、前CTO 帕特&#183;基辛格在谈到45纳米芯片的突破以及&#8220;多核&#8221;技术的未来影响时表示，更低功耗、更强性能和更丰富功能的芯片为无线的、模拟图形的和能感知触觉的计算提供了更好的技术准备。用户可以不用为了机器刻意改变行为习惯，而机器具备了更精确的识别人的动作并做出更丰富反馈的能力。 </p>
<p>简单说，计算能力的进步使得我们已经可以抛弃&#8220;计算机中古时代&#8221;的键盘和鼠标，拥抱更现代、更性感和更自由的多点触控技术。它为交互性更强的应用打开了新的天地，比如，当iPhone靠近人脸时，多点触控屏幕会自动感应，让触控点全部关闭，因为这时候你正在接听电话，不需要任何按键，而当机器感受到你进入其他场景的信号时，你正好需要的按键又会聪明地出现。当机器能以人的自然动作为中心时，机器的操作大大降低了门槛，几乎不需要学习。 </p>
<p>从购买和使用Wii的人群也可以看出，难以掌握复杂操作的学龄前儿童和对新的电子设备接受度最差的中老年人，都可以轻松学会使用Wii。这更加证明了过去繁琐的操作和束缚自然动作的低级交互方式扼杀了许多人获得新鲜体验的自由。Wii也成为了圣诞节流行的一款送给小孩子的礼品。虽然这会让使用者耗费更多的动作及体力，不过，却能够在操控过程中获得更多的乐趣。这种操作方式抛弃了目前所使用的键盘(按钮)、鼠标，将进一步体现出人性化操控界面的未来趋势。 </p>
<p> 到目前为止，Wii已经销售了超过1300万台，Xbox360早推出1年，但是已经不敌Wii的风头，以几十万台的差距落后，而PS3则只有500多万台售出，而且增长缓慢。虽然市场上开始出现拙劣模仿者和盗版游戏，就像当初MP3厂商模仿iPod一样，而且同类厂商开始降价应对Wii的冲击，但Wii依然在营造产品紧缺的状态，坚持不降价，保证了极高的利润率。任天堂的市值也一路攀升，超过索尼近一倍，成为日本证券市场上仅次于丰田的第二大市值的公司。</p>
<p>宫本茂已经沿着解放身体的思路把Wii驶入家庭健身器市场，Wii fit系列游戏包括了滑雪、跳舞、瑜伽、俯卧撑、呼啦圈和英式足球等。设备上，Wii又推出了平衡板(Wii balance)，可通过在上面运动和改变姿势来操控屏幕上的虚拟角色。 </p>
<p> 微软的客厅战争也打算以多点触控技术作为杀手锏。去年春天，网上开始流传一段Microsoft Surface的视频，其演示的多点触控技术令观者赞叹不已。Surface大大的触摸屏可以响应很多种手势，而且，当你把电子设备放在它的触摸屏上时，Surface电脑马上会&#8220;看到&#8221;你的设备(通过隐藏的多个摄像头)，并即时在屏幕上展现出各种可能的操作。比如你放个数码相机上去，它就会问你是否要查看照片，这种简单的操作一旦推向市场很可能像Wii一样改变家庭PC产业。</p>
<p> 苹果公司也已经展示了下一代应用触控屏幕的台式电脑iMac，惠普公司的PDA产品iPaq在触控技术上也做了独特的尝试，主要是实现了在小小屏幕上用手指实现精确到毫米的控制。三星电子全球CEO尹钟龙也认为下一代手机要性感并且易于操作，今年他们推出了触摸屏手机SGH-P520，以应对来势汹汹的iPhone。而iPhone也在触感互动上深入探索，在申请按压虚拟按钮仍然有弹回感觉的技术专利。 </p>
<p> 苹果公司预计iPhone的销量在2008年1月14日的Mac world大会之前达到500万部，但从近20%的产品被购买后没有开通AT&amp;T的服务，可推测这些产品是流入了非美国市场破解后使用，看来iPhone病毒式的扩散威力还只是开了个头。 </p>
<p>iPhone的热销引发了同类厂商的跟随效应。去年，在芯片技术领域，国内厂商美芯半导体的销售人员惊奇地发现，他们投资的适合计算空间平衡改变的芯片，其销售业绩大大高于其他芯片产品。这些芯片主要应用于交互式的游戏机、可感知使用者行为的手机等设备上。</p>
<p> 实际上，通过iPhone的热卖我们完全可以预见到，下一阶段的芯片必须为下一代手机等电子产品的这些特点做足准备：用容量有限的电池(意味着芯片的功耗必须是低的)，功能越来越丰富(意味着芯片支持更多任务同时运行)，越来越便携(意味着更好的支持无线通信)而且更讲求视觉和触感体验(意味着模拟计算等能力更强)。目前，唯一独立存在的GPU(图形处理器)厂商Nvidia公司，就是由于专注于这个越来越重视视觉体验的消费电子市场，才取得了这两年的高速成长。可以预测适合这些特点的芯片的爆发性需求应该会在2008年日渐明显。 </p>
<p> 另外，IT咨询顾问公司InStart的殷建松认为，2008年值得期待的是46英寸屏幕的电子设备的出现，iPhone和iPod Touch的屏幕是3.5英寸,而46英寸的屏幕视觉和触觉体验效果更好，并且仍然能放入口袋中。像iPod Touch一样仅仅作为游戏、娱乐和信息浏览器的工具也是46英寸的屏幕最为合适，它恰好是体验和便携性的平衡。</p>
<p> 以上种种都为我们描绘出一个能够更加全面和丰富地感知人的自然行为的未来世界，这个世界的基础应该是一种无所不在的感知、收集、识别，最后做出更丰富和人性化反馈的计算机。在这个用户体验极大丰富和更自由交互的世界面前，引领时代的厂商们已经占好了跑道，各就各位。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/402.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转] C语言的setjmp: 异常处理与构建协作式多任务系统</title>
		<link>http://www.donevii.com/post/356.html</link>
		<comments>http://www.donevii.com/post/356.html#comments</comments>
		<pubDate>Sun, 02 Dec 2007 23:57:48 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[c/c++/c#]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[goto]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[longjmp]]></category>
		<category><![CDATA[setjmp]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[堆栈]]></category>
		<category><![CDATA[多任务]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[异常]]></category>
		<category><![CDATA[测试]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[缓冲]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/356.html</guid>
		<description><![CDATA[转至 http://www.upsdn.net/html/2004-11/47.html 在C标准库中有一对非常有趣的函数setjmp()函数与longjmp()函数,用来实现代替goto实现一些非常重要的功能，如异常处理。C语言中，标准库函数setjmp和longjmp形成... ]]></description>
			<content:encoded><![CDATA[<p>转至 <a href="http://www.upsdn.net/html/2004-11/47.html" title="http://www.upsdn.net/html/2004-11/47.html">http://www.upsdn.net/html/2004-11/47.html</a></p>
<p>在C标准库中有一对非常有趣的函数setjmp()函数与longjmp()函数,用来实现代替goto实现一些非常重要的功能，如异常处理。C语言中，标准库函数setjmp和longjmp形成了结构化异常工具的基础。简单的说即setjmp实例化异常处理程序，而longjmp产生异常。</p>
<p>先介绍setjmp<br />
int <a href="http://www.donevii.com/post/tag/setjmp" class="st_tag internal_tag" rel="tag" title="Posts tagged with setjmp">setjmp</a>(jmp_buf envbuf)<br />
宏函数setjmp()在缓冲区envbuf中保存系统堆栈里的内容，供longjmp()以后使用,setjmp()必须使用头文件setjmp.h。<br />
调用setjmp()宏时，返回值为0，然而longjmp()把一个变原传递给setjmp(),该值（恒不为0）就是调用longjmp()后出现的setjmp()的值。<br />
setjmp函数用于保存程序的运行时的堆栈环境，接下来的其它地方，你可以通过调用longjmp函数来恢复先前被保存的程序堆栈环境。当setjmp和longjmp组合一起使用时，它们能提供一种在程序中实现“非本地局部跳转”（&#8221;non-local <a href="http://www.donevii.com/post/tag/goto" class="st_tag internal_tag" rel="tag" title="Posts tagged with goto">goto</a>&#8221;）的机制。并且这种机制常常被用于来实现，把程序的控制流传递到错误处理模块之中；或者程序中不采用正常的返回（return）语句，或函数的正常调用等方法，而使程序能被恢复到先前的一个调用例程（也即函数）中。<br />
对setjmp函数的调用时，会保存程序当前的堆栈环境到env参数中；接下来调用longjmp时，会根据这个曾经保存的变量来恢复先前的环境， 并且当前的程序控制流，会因此而返回到先前调用setjmp时的程序执行点。此时，在接下来的控制流的例程中，所能访问的所有的变量（除寄存器类型的变量 以外），包含了longjmp函数调用时，所拥有的变量。<br />
void <a href="http://www.donevii.com/post/tag/longjmp" class="st_tag internal_tag" rel="tag" title="Posts tagged with longjmp">longjmp</a>(jmp_buf envbuf,int status);<br />
     函数longjmp()使程序在最近一次调用setjmp()出重新执行。setjmp()和longjmp()提供了一种在函数间调转的手段，必须使用头部文件setjmp.h。<br />
     函数longjmp()通过把堆栈复位成envbuf中描述的状态进行操作，envbuf的设置是由预先调用setjmp()生成的。这样使程序的执行在 setjmp()调用后的下一个语句从新开始，使计算机认为从未离开调用setjmp()的函数。从效果上看，longjmp()函数似乎“绕”过了时间 和空间（内存）回到程序的原点，不必执行正常的函数返回过程。<br />
     缓冲区envbuf具有&lt;setjmp.h&gt;中定义的buf_jmp类型，它必须调用longjmp()前通过调用setjmp()来设置好。<br />
    值status变成setjmp()的返回值，由此确定长调转的来处。不允许的唯一值是0，0是程序直接调用函数setjmp()时由该函数返回的，不是间接通过执行函数longjmp()返回的。<br />
     longjmp()函数最常用于在一个错误发生时，从一组深层嵌套的实用程序中返回。<br />
longjmp函数用于恢复先前程序中调用的setjmp函数时所保存的堆栈环境。setjmp和longjmp组合一起使用时，它们能提供一种在程序中实现“非本地局部跳转”（&#8221;non-local goto&#8221;）的机制。并且这种机制常常被用于来实现，把程序的控制流传递到错误处理模块，或者不采用正常的返回（return）语句，或函数的正常调用等方法，使程序能被恢复到先前的一个调用例程（也即函数）中。</p>
<p>　 　对setjmp函数的调用时，会保存程序当前的堆栈环境到env参数中；接下来调用longjmp时，会根据这个曾经保存的变量来恢复先前的环境，并且 因此当前的程序控制流，会返回到先前调用setjmp时的执行点。此时，value参数值会被setjmp函数所返回，程序继续得以执行。并且，在接下来 的控制流的例程中，它所能够访问到的所有的变量（除寄存器类型的变量以外），包含了longjmp函数调用时，所拥有的变量；而寄存器类型的变量将不可预 料。setjmp函数返回的值必须是非零值，如果longjmp传送的value参数值为0，那么实际上被setjmp返回的值是1。</p>
<p>　　在调用setjmp的函数返回之前，调用longjmp，否则结果不可预料。</p>
<p>在使用longjmp时，请遵守以下规则或限制：<br />
　　· 不要假象寄存器类型的变量将总会保持不变。在调用longjmp之后，通过setjmp所返回的控制流中，例程中寄存器类型的变量将不会被恢复。<br />
　　· 不要使用longjmp函数，来实现把控制流，从一个中断处理例程中传出，除非被捕获的异常是一个浮点数异常。在后一种情况下，如果程序通过调用 _fpreset函数，来首先初始化浮点数包后，它是可以通过longjmp来实现从中断处理例程中返回。<br />
如何实现异常处理<br />
首先设置一个跳转点（setjmp() 函数可以实现这一功能），然后在其后的代码中任意地方调用 longjmp() 跳转回这个跳转点上，以此来实现当发生异常时，转到处理异常的程序上，在其后的介绍中将介绍如何实现。 setjmp() 为跳转返回保存现场并为异常提供处理程序，longjmp() 则进行跳转（抛出异常），setjmp() 与 longjmp() 可以在函数间进行跳转，这就像一个全局的 goto 语句，可以跨函数跳转。</p>
<p><strong>jmp_buf 异常结构</strong></p>
<p>        使用 setjmp() 及 longjmp() 函数前，需要先认识一下 jmp_buf 异常结构。jmp_buf 将使用在 setjmp() 函数中，用于保存当前程序现场（保存当前需要用到的寄存器的值），jmp_buf 结构在 setjmp.h 文件内声明：</p>
<p>        typedef struct<br />
        {<br />
                unsigned j_sp;  // 堆栈指针寄存器<br />
                unsigned j_ss;  // 堆栈段<br />
                unsigned j_flag;  // 标志寄存器<br />
                unsigned j_cs;  // 代码段<br />
                unsigned j_ip;  // 指令指针寄存器<br />
                unsigned j_bp; // 基址指针<br />
                unsigned j_di;  // 目的指针<br />
                unsigned j_es; // 附加段<br />
                unsigned j_si;  // 源变址<br />
                unsigned j_ds; // 数据段<br />
        } jmp_buf;</p>
<p>        jmp_buf 结构存放了程序当前寄存器的值，以确保使用 longjmp() 后可以跳回到该执行点上继续执行。</p>
<p>setjmp() 与 longjmp() 函数都使用了 jmp_buf 结构作为形参，它们的调用关系是这样的：<br />
        首先调用 setjmp() 函数来初始化 jmp_buf 结构变量 jmpb，将当前CPU中的大部分影响到程序执行的积存器存入 jmpb，为 longjmp() 函数提供跳转，setjmp() 函数是一个有趣的函数，它能返回两次，它应该是所有库函数中唯一一个能返回两次的函数，第一次是初始化时，返回零，第二次遇到 longjmp() 函数调用后，longjmp() 函数使 setjmp() 函数发生第二次返回，返回值由 longjmp() 的第二个参数给出（整型，这时不应该再返回零）。<br />
        在使用 setjmp() 初始化 jmpb 后，可以其后的程序中任意地方使用 longjmp() 函数跳转会 setjmp() 函数的位置，longjmp() 的第一个参数便是 setjmp() 初始化的 jmpb，若想跳转回刚才设置的 setjmp() 处，则 longjmp() 函数的第一个参数是 setjmp() 所初始化的 jmpb 这个异常，这也说明一件事，即 jmpb 这个异常，一般需要定义为全局变量，否则，若是局部变量，当跨函数调用时就几乎无法使用（除非每次遇到函数调用都将 jmpb 以参数传递，然而明显地，是不值得这样做的）；longjmp() 函数的第二个参数是传给 setjmp() 的第二次返回值，这在介绍 setjmp() 函数时已经介绍过。<br />
        下面是 setjmp() 函数与 longjmp() 函数的一个示意图：<br />
<img border="0" src="http://blog.bc-cn.net/UploadFiles/2006-9/926566599.bmp" /></p>
<p>通过示意图可以看出 setjmp() 函数与 longjmp() 函数的关系，如何跳转。</p>
<p><img width="554" src="http://51cmm.csai.cn/ExpertEyes/Images/no152.gif" height="480" /></p>
<p>呵呵！现在是否对程序的执行流程一目了然，其中最关键的就是setjjmp和longjmp函数的调用处理。我们分别来分析之。<br />
　　当程序运行到第②步时，调用setjmp函数，这个函数会保存程序当前运行的一些状态信息，主要是一些系统寄存器的值，如ss，cs，eip， eax，ebx，ecx，edx，eflags等寄存器，其中尤其重要的是eip的值，因为它相当于保存了一个程序运行的执行点。这些信息被保存到 mark变量中，这是一个C标准库中所定义的特殊结构体类型的变量。<br />
　　调用setjmp函数保存程序状态之后，该函数返回0值，于是接下来程序执行到第③步和第④步中。在第④步中语句执行时，如果变量n2为0值，于是便 引发了一个浮点数计算异常，，导致控制流转入fphandler函数中，也即进入到第⑤步。<br />
　　然后运行到第⑥步，调用longjmp函数，这个函数内部会从先前的setjmp所保存的程序状态，也即mark变量中，来恢复到以前的系统寄存器的 值。于是便进入到了第⑦步，注意，这非常有点意思，实际上，通过longjmp函数的调用后，程序控制流（尤其是eip的值）再次戏剧性地进入到了 setjmp函数的处理内部中，但是这一次setjmp返回的值是longjmp函数调用时，所传入的第2个参数，也即-1，因此程序接下来进入到了第⑧ 步的执行之中。<br />
因为 jmp_buf 全局只能存放一个 jmpb 结构，使得只有最后一组宏可以响应异常；    这是无法嵌套异常的原因，要实现多重嵌套可以建立一个全局堆栈来维护一组 jmpb 结构<br />
实现机理<br />
setjmp()保存其调用返回点的ebx, esi, edi, ebp, esp, eip,对于sigsetjmp(), 还保存当前的信号屏蔽字, longjmp恢复这些寄存器及信号屏蔽字,同时传递一个返回值。<br />
ongjmp只是使CPU的状态和setjmp时的状态一致，相同的CPU初始状态执行相同的代码并不意味着产生相同的结果,setjmp, longjmp并不关心内存变量的变化，而只关心CPU的状态,<br />
内存变量的确定性应该根据上下文来灵活处理.<br />
如果将函数之间的调用关系看成一种树型结构, 将可执行文件的入口函数看成它的最内层节点(根节点), 那么setjmp提供了标记节点的方法, longjmp提供了从外层节点直接返回到更内层节点的方法,<br />
在同一层节点之间或者从内层节点到外层节点的longjmp是没有意义的, 因为目标节点在此时根本不存在.<br />
<strong>摘要：</strong>讨论一个利用标准C语言setjmp库函烽实现查询式协作多任务系统，给出完整的内核和样例程序并对源代码进行说明。该系统具有简单易用的特点，只需要编写存取堆栈指针的宏就可方便地移植到新的平台上。文章详述了系统的优缺点，讨论一些性能扩展的方法。该内核适用<br />
于中小规模的嵌入式软件。</p>
<p><strong>关键词：</strong>协作式多任务 C语言 setjmp</p>
<p><strong>引言</strong></p>
<p>本文介绍的是利用标准C语言setjmp库函数实现的具备此特点的协作式多任务系统。从本 质上讲，实时多任务操作系统应该具备按照优先级抢占调度的内核。然而，在实际应用中，抢中式的多任务某种程序上带来了用户程序设计时数据保护的困难，并 且，具备抢占功能的多任务内核设计时困难也比较多，这会增加操作系统自身的代码，也使它在小资源单片机系统中应用较少；而协作多任务系统的调度只在用户指 定的时机发生，这会大大简化内核和用户系统的设计，尤其本文实现的系统通过条件查询来放弃CPU，既符合传统单片机程序设计的思维，又带来了多任务、模块 化、可重入的编程便利。</p>
<p>Setjmp是标准C语言库函数的组成部分，它可以实现程序执行中的远程转操作。具体来 说，它可以在一个函数中使用setjmp来初始化一个全局标号，然后只要该函数未曾返回，那么在其它任何地方都可以通过longjmp调用来跳转到 setjmp的下一条语句执行。实际上，setjmp函数将发生调用处的局部环境保存在一个jmp_buf的结构当中，只要主调函数中对应的内存未曾释放 （函数返回时局部内存就失效了），那么在调用longjmp的时候就可以根据已保存的jmp_buf参数恢复到setjmp的地方执行。我们的系统中就是 分析了setjmp标准库函数的特点，以简单的方式实现了协作式多任务。</p>
<p><strong>1 演示程序</strong></p>
<p>为了便于理解，首先给出多任务演示程序的源代码。这个程序演示了协作式多任务切换、任务的 动态生成、多任务共用代码等功能，一共使用了init_coos初始化根任务（也就是C语言main函数）、creat_task创建新任务和 WAITFOR查询条件这3个基本的系统调用。由于面向嵌入式系统，因而程序不会中止并且运行中也没有进行任何输出，需要借助适合的调试工具来理解多任务 系统的运行。</p>
<p>example.c文件清单：</p>
<p>#include&lt;stdlib.h&gt;</p>
<p>#include“co-os.h”</p>
<p>void tskfunc1(int argc,void *argv);</p>
<p>void tskfunc2(int argc,void *argv);</p>
<p>void subfunc(void);</p>
<p>volatile int cnt,test;</p>
<p>int main(void){</p>
<p>int i;</p>
<p>init_coos(400);</p>
<p>creat_tsk(tskfunc1,12,NULL,400);</p>
<p>creat_tsk(tskfunc2,0,NULL,400);</p>
<p>i=0；</p>
<p>while(1){</p>
<p>WAITFOR(cnt= =8);</p>
<p>while(i++&lt;cnt)test=i;</p>
<p>cnt++;</p>
<p>}</p>
<p>}</p>
<p>void tskfunc1(int argc,void *argv){</p>
<p>int i;</p>
<p>static int creat=0;</p>
<p>if(!creat){</p>
<p>creat_tsk(tskfunc1,9,NULL,400);</p>
<p>creat=1;</p>
<p>}</p>
<p>i=0；</p>
<p>while(1){</p>
<p>WAITFOR(cnt&gt;argc);</p>
<p>test=0&#215;55;</p>
<p>/*使用函数调用在子程序中测试WAITFOR*/</p>
<p>subfunc()；</p>
<p>while(i++&lt;cnt)test=i^0xAA;</p>
<p>}</p>
<p>}</p>
<p>void tskfunc2(int argc,void *argv){</p>
<p>while(1){</p>
<p>WAITFOR(++cnt&gt;15);</p>
<p>cnt=0;</p>
<p>}</p>
<p>}</p>
<p>void subfunc(void){</p>
<p>int i;</p>
<p>WAITFOR(cnt&lt;5);</p>
<p>for(i=0;i&lt;++)test=0&#215;10*i；</p>
<p>}</p>
<p><strong>2 内核构成</strong></p>
<p>内核包括一个供外部用户程序包含的头文件（co-os.h）和具体实现的源文件（co-os.c），它们提供了演示程序中用到的3个系统调用。</p>
<p>内核的实现代码假定了CPU堆栈是向下增长的，并且通过宏来直接操作堆栈指针。以下代码在 Microsoft VC6 for x86、Borland C++ Builder 5.5、SDS CrossCode7.0 for 68K和GCC3.2 for AVR四种平台中测试过，只需在co-os.h头文件中定义相应的平台类型即可顺利编译。</p>
<p>（1）co-os.h文件清单</p>
<p>#include&lt;setjmp.h&gt;</p>
<p>/*选择X86_VC6，X86_BC5，AVR_GCC或M68H_SDS.*/</p>
<p>#define X86_VC6</p>
<p>#define MAX_TSK 10</p>
<p>typedef struct {</p>
<p>void (*entry)(int argc,void *argv);</p>
<p>jmp_buf env;</p>
<p>int argc;</p>
<p>void *argv;</p>
<p>}TVB;</p>
<p>extern TCB tcb[MAX_TSK];</p>
<p>extern int task_num,tskid;</p>
<p>void init_coos(int mainstk);</p>
<p>int creat_tsk(void(*entry)(int argc,void *argv),int argc,void *argv,int stksize);</p>
<p>#define WAITFOR(condition)do{</p>
<p>setjmp(tcb[tskid].env);</p>
<p>if(!(condition)){</p>
<p>tskid++;</p>
<p>if(tskid&gt;=task_num)tskid=0;</p>
<p>longijmp(tcb[tskid].env,1);</p>
<p>}</p>
<p>}while(0)</p>
<p>(2)co-os.c文件清单</p>
<p>#include &#8220;co-os.h&#8221;</p>
<p>#if defined(X86_VC6)||defined(X86_BC5)</p>
<p>#define SAVE_SP(p) _asm mov p,esp</p>
<p>#define RESTORE_SP(p) _asm mov esp,p</p>
<p>#elif defined(AVR_GCC)</p>
<p>#include&lt;io.h&gt;</p>
<p>#define SAVE_SP(p) p=(int*)SP</p>
<p>#define RESTORE_SP(p) SP=(int)p</p>
<p>#elif defined(M68K_SDS)</p>
<p>#define SAVE_SP(p) asm(&#8220;MOVE.L A7,{&#8220;#p&#8221;}&#8221;)</p>
<p>#define RESTORE_SP(p) asm(&#8220;MOVE.L {&#8220;#p&#8221;},A7&#8243;)</p>
<p>#endif</p>
<p>TCB tcb[MAX_TSK];</p>
<p>Int task_num=1;</p>
<p>Int tskid;</p>
<p>Static int stktop,oldsp;</p>
<p>Void init_coos(int mainstk){</p>
<p>SAVE_SP(stktop);</p>
<p>stktop=stktop+sizeof(void(*)(void))/sizeof(int)</p>
<p>-(mainstk+sizeof(int)-1)/sizeof(int);</p>
<p>}</p>
<p>int creat_tsk(void(*entry)(int argc,void *argv),</p>
<p>int argc,void *argv,int stksize){</p>
<p>if(task_num&gt;=MAX_TSK)terurn-1;</p>
<p>SAVE_SP(oldsp);</p>
<p>RESTORE_SP(stktop);</p>
<p>If(!setjmp(tcb[task_num].env)){</p>
<p>RESTORE_SP(oldsp);</p>
<p>tcb[task_num].entry=entry;</p>
<p>tcb[task_num].argc=argc;</p>
<p>tcb[task_num].argv=argv;</p>
<p>task_num++;</p>
<p>stktop-=(stksize+sizeof(int)-1)/sizeof(int);</p>
<p>}</p>
<p>else tcb[tskid].entry(tcb[tskid].argc,tcb[tskid].argv);</p>
<p>return 0;</p>
<p>}</p>
<p><strong>3 代码说明</strong></p>
<p>任务代码通过执行setjmp设置本任务下次查询时的返回点，然后在等待条件放弃掉CPU 跳转到下一任务的返回点处执行。如此周而复始，让各任务都获得轮转运行的机会，也要求各任务都需要主动通过等待条件的方式放弃掉CPU。系统中除了中断服 务程序之外，所有任务都是平等的，都应该遵循同样的规则和其它任务一起协作运行。基本系统中没有设计杀死任务的调用，这要求各任务都应当设计成某种形式的 无限循环。</p>
<p>任务中等待的条件可以是任务复杂的表达式或都函数调用，也可以是中断服务程序设置的全局变 量（注意加volatile定义）。一般在任务执行时会让下次等待的条件不再满足，避免某个任务一直霸占CPU将系统饿死。在嵌入式软件中还经常会遇到任 务定时启动和超时等待在I/O操作上，在我们的系统中可以维护一个时间计数器，只需在适当的地方记录时刻，然后在任务查询条件中判断当前计数器和记录时刻 之间的差值就可以了。</p>
<p>内核实现的代码则相当简法。由于主要的保护和恢复任务现场的工作都由C语言标准库 setjmp实现了，我们就只需要操纵一下堆栈指针防止不同的任务使用了重叠的局部环境，这个工作在初始化和创建任务的时候通过预定义的两个宏来实现。在 init_coos函数中，记录了主任务（main函数）保留mainstk字节堆栈后的新栈顶位置（stktop），然后在每次creat_task时 都根据要求为每个任务保留stksize字节的堆栈并重新计算下一个stktop。在creat_task函数中利用了setjmp函数的返回值（直接返 回时为0，longjmp跳转返回时非0），使得一方面creat_task能正常回到调用者，又让下次轮转到新任务时能够找到创建时的入口。</p>
<p>co-os.h中定义的WAITFOR使用了一个do{…while(0)实现多语句宏的 C语言小技巧，这样能保证在任何情况下WAITFOR都可以如单条语句一样在源程序中使用，需要担心多了或者少了大括弧破坏if/else匹配之类的问 题，并且，所有的编译器都会优化掉这个假循环。</p>
<p>为了尽量使程序简单并说明问题，以上代码中没有考虑中断相关的数据保护问题。实际运行的系 统中，如果首先写堆栈指针不能一步完成（如AVR这样的8位机），那么，在写操作正在进行的时候绝对不能允许中断；另外，在任务中查询的条件如果和中断有 关，那么也必须考虑数据的完整性。</p>
<p><strong>4 性能分析</strong></p>
<p>所有的协作式多任务系统中任务切换时间都和用户代码（是否长期占用CPU）、就绪时刻有 关，比标准系统略差的是，我们的简单系统中不支持任务优先级，并且完成一轮查询调度的时间还和任务数目有关。这也是为了达到简单和可移植性目标而不得已作 出的牺牲。在各任务间切换查询条件通过C语言标准库函数longjmp实现，一般来讲longjmp函数要从内存中恢复大部分CPU寄存器，执行它也需要 若干条指令的时间。</p>
<p>为了面向嵌入式系统应用，任务控制块（TCB）采用静态数组来实现，这样要求预先确定系统 的最大任务数（co-os.h中的MAX_TSK）。如果需要，也可以通过环波链表来动态管理任务控制块（TCB），这时可以简单实现任务的动态创建和删 除，并且通过指针来访问TCB也要比通过下标（tskid）略快一点。</p>
<p>在某些情况下，如果在中断返回后需要执行某关键任务，可以考虑通过设置“高级中断”的方法 来实现。具体地讲，就是在中断返回前改变返回地址到某函数入口（“高级中断服务程序”），同时保留原返回地址到堆栈中，这样在“高级中断服务程序”完成后 执行return就又回到了正常的多任务查询流程。使用“高级中断”时要注意现场保护的衔接，并且这种技巧显然和CPU和体系结构有关。</p>
<p><strong>5 结论</strong></p>
<p>setjmp是标准C语言中用于远程跳转的库函数，利用它可方便实现一个简单易移植的协作式多任务系统。该系统功能完备、编程简单、易于学习，适合一些中小规模的嵌入式软件使用；并且，以此为基础，还可以用一些与平台相关的编程技巧提高其实时性和灵活性。</p>
<p>部分资料来源于希赛,单片机与嵌入式系统应用</p>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/356.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2007软件开发2.0大会－29日</title>
		<link>http://www.donevii.com/post/355.html</link>
		<comments>http://www.donevii.com/post/355.html#comments</comments>
		<pubDate>Thu, 29 Nov 2007 18:37:22 +0000</pubDate>
		<dc:creator>dengwei</dc:creator>
				<category><![CDATA[life]]></category>
		<category><![CDATA[csdn]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[sd2c]]></category>
		<category><![CDATA[sd2china]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[手机]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[软件开发]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/355.html</guid>
		<description><![CDATA[很兴奋能参加这个会议，可惜公司这面早上只有我一个人去，大清早收拾好就在楼下的 984 车站等车，结果等了好久才来车，车上好几个人都是同去开会的，有两位先生还不错，下车后给我领了... ]]></description>
			<content:encoded><![CDATA[<p>很兴奋能参加这个会议，可惜公司这面早上只有我一个人去，大清早收拾好就在楼下的 984 车站等车，结果等了好久才来车，车上好几个人都是同去开会的，有两位先生还不错，下车后给我领了路，在开会过程中还见过几回，好像是 TechExcel 公司的(希望没有记错)，先表示感谢一下吧。</p>
<p>还好没有迟到很多，蒋总(CSDN总裁 蒋涛)刚上台，我就进入主会场了。蒋总的开幕辞解释了为什么要办这次会议，以及这次会议的主题《揭示下一代软件开发趋势》。</p>
<p>查看 <a href="http://www.donevii.com/post/tag/csdn" class="st_tag internal_tag" rel="tag" title="Posts tagged with csdn">CSDN</a> 报道 &gt;&gt; <a target="_blank" href="http://news.csdn.net/n/20071129/111178.html">【SD2.0大会】CSDN总裁蒋涛致大会开幕辞</a></p>
<p>第一场孟岩(CSDN &amp; 《程序员》总编)和Jon Erickson(DDJ杂志主编)讨论了一下什么是软件开发2.0，很可惜，进去时太匆忙，忘了向工作MM要同声传译的耳机了，本来我的听力就巨烂无比，再加上做在我旁边和后台的几位仁兄肆无忌惮的聊天，让我感觉像在菜市场里看哑剧一样。我只注意到了一个词组，“Tipping Point”，关键点？顶点？突破点？</p>
<p>查看 CSDN 报道 &gt;&gt; <a target="_blank" href="http://news.csdn.net/n/20071129/111179.html">【SD2.0大会】DDJ总编与程序员总编孟岩对话SD2.0</a></p>
<p>第二场Ivar Jacobson(现代软件开发之父)的《软件开发大趋势》也不错，主要阐述了三个观点。</p>
<blockquote><p>1、如何开发更优秀、高效和低成本的软件？<br />
高效由多构件和积极主动的开发者来完成，低成本由大规模的构件重用实现，优秀的软件必然是实用性强，可扩展性高和稳定的产品。</p>
<p>2、<a href="http://www.donevii.com/post/tag/%e8%bd%af%e4%bb%b6%e5%bc%80%e5%8f%91" class="st_tag internal_tag" rel="tag" title="Posts tagged with 软件开发">软件开发</a>，实践为王<br />
程序员需要更多的高级技术实践。重要的构件重用技术包括工程流，SOA，企业架构技术。实践是过程的基础，过程只是组成实践的一个元素。优秀的实践之所以成本低是由大规模的构件重用来实现。</p>
<p>3、如何获得实践<br />
传统的方法是通过书籍和网络，这样获得的数据相关性不够。先进的实践来源是没有限制的，包多基本和高级的方式，但只要是实用的就可以自由选择并合并这些实践组成一个新的过程。这种方法为开发人员提供了一种比较新的模式。</p></blockquote>
<p>查看 CSDN 报道 &gt;&gt; <a target="_blank" href="http://news.csdn.net/n/20071129/111184.html">【SD2.0大会】Ivar：为什么我做的架构比你的年龄还长</a></p>
<p><img src="http://images.csdn.net/20071129/ivar.gif" /> </p>
<p>第三场林斌(谷歌工程研究院副院长)介绍了 Google 的新手机平台 Android。之前我仅因为 Android 开源免费，并且出于对 Google 产品先量一贯的信任也装了一个 SDK 和 emulator 来玩一下，今天开会才了解到，Android 相对于其它平台的四个优势：及时响应；不会死机；通话切换；保存状态。不过同时公布的几项数据很有趣。“发布前两周，共250，000下载；下载量中国第一，超过美国1.5倍；”不知道 Google 用于奖励 Android 开发的一千万美元会有多少被中国人拿到？</p>
<p>查看 CSDN 报道 &gt;&gt; <a target="_blank" href="http://news.csdn.net/n/20071129/111182.html" class="broken_link">【SD2.0大会】数据现Andriod惊人影响力，Google林斌公布架构图</a></p>
<p><img width="500" src="http://images.csdn.net/20071129/20071129104857.gif" height="375" /></p>
<p>茶歇时外面的展台也不出，可惜送的东西太少，广告性质的东西太多。中途问过几次是否可以使用 wi-fi ，结果被告知因为太多的媒体在使用 wi-fi 造成网络拥挤。我在主会场内开机试了几回，也始终无法连上，本来手机欠费，准备有什么事再从网上通知其它人，或者先在淘宝上充下话费，唉。总能在不经意的回头间看到李梁一闪而过的身影，每当我再凝神寻找时又不见踪影，还好茶歇时在外面看到了，当我在展台转了一圈，他又不见了，不知道是我眼神不好，还是他“打一枪换一个地方”、“四处流窜”。几次去会场服务区询问，恰恰每次都是总到同一个MM，结果当我最后一次询问完后离开时，那个MM注视我好长一段时间，旁边有人询问她问题时，那个MM也没有反应，让我小小的陶醉了一下，俺也终于能吸引别人的眼球了。（早上起得太早，顶着鸡窝头去的，希望不是这个原因吸引她的目光）</p>
<p>第四场Jim Douglas(CodeGear公司CEO)……i forget……明天再去找别人的笔记回忆一下吧。</p>
<p>第五场陈榕(北京科泰世纪科技有限公司首席科学家、技术总监)对手机的独到见解让我眼前为之一亮。陈总认为计算机内部只有两样东西，数据和处理数据的程序，那么，既然数据可以通过短信、红外和蓝牙等方式传递，那么程序也可以。在陈总的话语中多次听到 1-click，即用户通过一次点击来使用某个服务，而这个服务处理数据的程序是在后台执行，让程序对用户是不可见的，也就没办法要求用户来为程序付费，仅会对服务消费。并且通过一定的方式，比如 metadata 的方式，可以使 handset 自动的去执行一些操作。比如 A 给 B 发送了一个视频文件，但是 B 的手机上没有这个解码器，如果 A 的视频文件的 metadata 中有一个类似于 URL 或者 URI 的东西能指定到解码器的 dll ，那么 B 机器会在后台自动下载安装。正如某位通过<a target="_blank" href="http://jiwai.de/sd2c" class="broken_link">叽歪</a>与现场互动的那位朋友说的，如果手机可以 DIY ，那么按照陈总的想法，手机可以和新组装的电脑一样，是裸机，通过我们安装服务来使用，这样大大的扩展了手机的功能。</p>
<p>查看 CSDN 报道 &gt;&gt; <a target="_blank" href="http://news.csdn.net/n/20071129/111187.html">【软件开发2.0大会】陈榕：让软件消失在用户面前</a></p>
<p>第五场Jim Reinders(Intel软件产品部首席宣传官居兼市场总监)讲的内容好像是关于并行计算这方面的，不过因为我在考虑其它事情，所以内容记得不是很清，也得明天找别人去“补课”了。</p>
<p>第六场Andrie Alexandrescu(<a href="http://www.donevii.com/post/tag/c" class="st_tag internal_tag" rel="tag" title="Posts tagged with c">C</a>++天才作家、D语言领袖人物，华尔街咨询师)给我的感觉就是风趣……并且他那一长串长场介绍时的 title 也吸引了我，“某国伞兵”。</p>
<p>中午的午餐不过，就是同时就餐的人太多，要排好长的队，流量大，并发高，这也是我们这次会议要解决的问题。伙食不错，不过因为时间太紧，我也没有多吃，可能是因为菜有点咸，口味有点重(某些人看到这里不要误会，p囧q )。</p>
<p>下午第一场听的范路(Product Evangelist, China Mainland CodeGear)的《2.0时代软件开发新方法、新工具》，对 RAD STUDIO 2007 有了一些初步的了解。不过范路对软件体系结构、开发过程、语言以及开发工具的演变做了一些很有意思的分析和讲解。</p>
<p>查看 CSDN 报道 &gt;&gt; <a target="_blank" href="http://news.csdn.net/n/20071129/111193.html">【SD2.0大会】2.0开发时代的四大特性改变</a></p>
<p>在听下午第一场的中间，去洗手间的时候，顺路去博文视点的展台看了一下，看到一堆书上放了“请赐名片”和一位老兄把一张名片给了博文的工作人员，然后拿起一本书就走了，我以为是“以名片免费换书”，因为之前博文的传单上有写“免费赠书”。我兴致勃勃的回去找杨恺和老高要名牌，准备回来领书，结果回来一问，原来书是卖的，名片给不给都行……我被鄙视了……我看到的那个人之前给付钱了，不过我没看到。T_T</p>
<p>第二场幺宝刚讲解 Google Gear，之前有了解过 Gear 所以没有用太多的注意力来听，一直在紧紧的盯着“老钱”(钱宏武)，期待着下一场他的演讲。</p>
<p>第三场，终于等到钱宏武演讲了，上回在悠视(悠视网 <a href="http://www.uusee.com">www.uusee.com</a>)听他讲的时候，就感觉比较有内容，不像其它人讲的都是空泛的概念之类的，而他讲的是经验、问题和方法。其实，我这回自费来参加 SD2.0 大会，就是想来听他的演讲。钱宏武针对以前在 SOHU 做互动产品的经验介绍了如何开发和维护。其中他对于团队的构成和任务分配的特殊见解真是让我眼前豁然开朗，“给开发人员定一个稍高一点的目标”，“培养水平低的员工”等等……</p>
<p>查看 CSDN 报道 &gt;&gt; <a target="_blank" href="http://news.csdn.net/n/20071129/111208.html">【SD2.0大会】钱宏武谈做互联网产品就像娶老婆 贤惠又要好看</a></p>
<p><img src="http://info-database.csdn.net/Upload/2007-11-29/IMG_1790.jpg" /></p>
<p>第四场听了前一半的董大伟的《ASP.NET AJAX 与 Silverlight 的完美结合》和冯彦文的《利用 Ajax/Java 技术建立高流量的即时双向沟通网站》。</p>
<p>终于等到我发挥特长的时候了，吃……晚宴时间。</p>
<p>晚上的沙龙算是这些大会给我的一个惊喜，原本订课时的沙龙是管理方面的，但是上午突然看到传单上有临时安排 Ben Wang(王文斌？当时离得太远，没看清，Taobao 首席架构师)、老钱(钱宏武)、雷鸣(前 Baidu 架构师)、汤道生(腾讯开发副总裁)四人合讲的《大型网站架构》收益良多啊……</p>
<p>总的来看 CSDN 这次举办的活动还是不错，真希望能坚持下去。</p>
<blockquote><p>有几点让我感觉不太爽的地方：<br />
１、每个人太早的发包，以至于，包比人多（有人来的时候自己本身就带包了，比如我），所以好多座位上都是放的包，让一些人没有地方座，吃饭、听课时都有这种情况。<br />
２、经常在开会过程中听到手机响，真是搞不懂，开会前明明主持人已经让大家把手机都调成振动了。并且有人接到电话后还在原位置大声的煲电话粥，如果是工作就算了，竟然长时间打电话，还在闲聊！真是损人不利已。<br />
３、原本晚上的沙龙是 VIP 才有的？但是看到好像没有什么限制，大家随便听。然后领东西时 VIP 也没有什么多的，就是一本增刊还是外面可以买到的，让我郁闷不已，VIP 和普通的没有什么区别嘛。<br />
４、开会时，一直有人在下边聊天、拉关系，无比烦人！</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/355.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转] MTK平台芯片概说</title>
		<link>http://www.donevii.com/post/354.html</link>
		<comments>http://www.donevii.com/post/354.html#comments</comments>
		<pubDate>Mon, 26 Nov 2007 05:11:28 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[doc]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[mtk]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[手机]]></category>
		<category><![CDATA[联发科技]]></category>
		<category><![CDATA[芯片]]></category>
		<category><![CDATA[蕊片]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/354.html</guid>
		<description><![CDATA[转至 http://blog.sina.com.cn/mobliephone 目前联发科技已开发出MT6205、MT6217、MT6218、MT6219、MT6226、MT6227、MT6228等系列平台,其中MT6205、MT6217、MT6218、MT6219、MT6226、MT6227、MT6228、MT6229、MT6225、MT6223、MT6230均... ]]></description>
			<content:encoded><![CDATA[<p>转至 <a href="http://blog.sina.com.cn/mobliephone" title="http://blog.sina.com.cn/mobliephone">http://blog.sina.com.cn/mobliephone</a></p>
<p>目前联发科技已开发出MT6205、MT6217、MT6218、MT6219、MT6226、MT6227、MT6228等系列平台,其中MT6205、MT6217、MT6218、MT6219、MT6226、MT6227、MT6228、MT6229、MT6225、MT6223、MT6230均为基带芯片，所有芯片均采用ARM7的核。<br />
MT6305为电源管理芯片，有MT6305、MT6305N、MT6305BN；</p>
<p>MT6129、MT6139是射频芯片。MT6129为早期的射频RF芯片，一般与MT6205的CPU一起使用。现在用的多的是MT6129<a href="http://www.donevii.com/post/tag/c" class="st_tag internal_tag" rel="tag" title="Posts tagged with c">C</a>、MT6129N、MT6129D，其中MT6129<a href="http://www.donevii.com/post/tag/c" class="st_tag internal_tag" rel="tag" title="Posts tagged with c">C</a>、MT6129N一般用在MT6217、MT6218、MT6219的CPU的机器上，MT6129D一般用在MT6226、MT6227的CPU的机器上。RF3146（7×7mm）、RF3146D（双频）、RF3166（6×6mm）为RFMD的PA。</p>
<p>MT6205为最早的方案，只有GSM的基本功能，不支持GPRS、WAP、MP3等功能。(2003年MP)；<br />
MT6218为在MT6205基础上增加GPRS、WAP、MP3功能。MT6217为MT6218的cost down方案，与MT6128 PIN TO PIN，只是软件不同而已，另外MT6217支持16bit数据。（2004年MP）<br />
MT6219为MT6218上增加内置AIT的1.3M camera处理IC，增加MP4功能。8bit数据。（2005年MP）<br />
MT6226为MT6219 cost 升级产品，内置0.3M 摄相处理IC，支持GPRS、WAP、MP3、MP4等，内部配置比MT6219优化及改善，比如配蓝牙是可用很便宜的芯片CSR的BC03模块USD3即可支持数据传输（如听立体声MP3等）功能。<br />
MT6226M为MT6226高配置设计，内置的是1.3M摄像处理IC。（2006年MP）<br />
MT6227与MT6226功能基本一样，PIN TO PIN，只是内置的是2.0M 摄像处理IC。（2006年MP）<br />
MT6228比MT6227增加TV OUT功能，内置3.0M 摄像处理IC，支持支持GPRS、WAP、MP3、MP4。（2006年MP）<br />
从MT6226后软件均可支持网络摄像头功能，也就是说你的机子可以用于QQ视频。</p>
<p>MT6229平台支持EDGE功能，其他功能和6228基本一致。</p>
<p>MT6225是6217的代替产品，可以接cam但是没有isp，也就是没有特效，变焦，但是其主频很高和6228/6229一样达到了104mhz,可以接wifi,并且给设计公司提出了更高的要求——如何利用104m的资源去实现mp4的编解码，如何用104m的资源跑更多的应用，这些都是设计公司做的,对设计公司的要求也非常得高。</p>
<p>MT6223是6205的替代，支持语音，短信，MP3,不支持T_F卡，USB盘，没有集成ISP，PMIC内签。</p>
<p>目前市面上出的双卡双待手机,一般是采取的方案分为以下几种:MT6226+6205,MT6225+6205和MT6225+6223</p>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/354.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[分享] 某知名手机平台的XML Parser源代码</title>
		<link>http://www.donevii.com/post/353.html</link>
		<comments>http://www.donevii.com/post/353.html#comments</comments>
		<pubDate>Sun, 25 Nov 2007 06:21:27 +0000</pubDate>
		<dc:creator>gavinkwoe</dc:creator>
				<category><![CDATA[c/c++/c#]]></category>
		<category><![CDATA[debug]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[ror]]></category>
		<category><![CDATA[xml]]></category>
		<category><![CDATA[平台]]></category>
		<category><![CDATA[手机]]></category>
		<category><![CDATA[源代码]]></category>

		<guid isPermaLink="false">http://www.donevii.com/post/353.html</guid>
		<description><![CDATA[    今天心情不错~ 分享一下小弟06年在某手机公司写的XML parser. 虽然当时脑子里还没有FSM的概念, 但代码逻辑还算清晰, 颇有成就感! 结构比较简单, 按DOM方式把指定文件解析成节点树, 另外提... ]]></description>
			<content:encoded><![CDATA[<p>    今天心情不错~ 分享一下小弟06年在某手机公司写的XML <a href="http://www.donevii.com/post/tag/parser" class="st_tag internal_tag" rel="tag" title="Posts tagged with parser">parser</a>.<br />
虽然当时脑子里还没有FSM的概念, 但代码逻辑还算清晰, 颇有成就感!<br />
结构比较简单, 按DOM方式把指定文件解析成节点树, 另外提供几个简单的查找函数.<br />
部分功能等完善, 过段时间再发一份功能比较完善的C++版本.</p>
<h3><strong>PLX_XMLParser.h</strong></h3>
<p>#if _MSC_VER &gt; 1000<br />
#pragma once<br />
#endif</p>
<p>#ifndef __XMLPARSE_H<br />
#define __XMLPARSE_H</p>
<p>#include</p>
<p>//////////////////////////////<br />
// Configure</p>
<p>#define USE_INLINE_FUNCTION<br />
#define USE_FILEBUFFER</p>
<p>//////////////////////////////<br />
// Constants</p>
<p>typedef enum {<br />
XMLERR_OK       = 0&#215;0,<br />
XMLERR_EFILE,       // failed to open file<br />
XMLERR_ALRDOPEN,    // already opened<br />
XMLERR_EDOC,<br />
XMLERR_EPARSE,<br />
} XMLERR;</p>
<p>typedef enum {<br />
NODETYPE_UNKN    = 0&#215;0,<br />
NODETYPE_ELEM   = 0&#215;1,<br />
NODETYPE_TEXT   = 0&#215;2,<br />
NODETYPE_COMM   = 0&#215;4,<br />
NODETYPE_INST   = 0&#215;8, // Not support<br />
//NODETYPE_USEFUL = NODETYPE_ELEM|NODETYPE_TEXT,<br />
//NODETYPE_ALL    = NODETYPE_ELEM|NODETYPE_TEXT|NODETYPE_COMM|NODETYPE_INST,<br />
} NODETYPE;</p>
<p>typedef enum {<br />
DSTAT_UNOPEN    = 0&#215;0,<br />
DSTAT_OPENED    = 0&#215;1,<br />
} DOCSTAT;</p>
<p>typedef enum {<br />
ISTAT_STOP      = 0&#215;0,<br />
ISTAT_CONTINUE  = 0&#215;1,<br />
ISTAT_PASS      = 0&#215;2,<br />
} ITERSTAT;</p>
<p>enum {    MAXLEN_BSTR    = 256 };</p>
<p>//////////////////////////////<br />
// Structures</p>
<p>struct tagBString<br />
{<br />
LONG    m_lLength;<br />
union   {<br />
CHAR    m_paStr[1];<br />
LPCSTR  m_pszStr;<br />
};<br />
};</p>
<p>typedef struct tagBString       BSTRING;<br />
typedef struct tagBString       *LPBSTRING;<br />
typedef struct tagBString const *LPCBSTRING;</p>
<p>struct tagXMLAttrib;<br />
typedef struct tagXMLAttrib            XMLATTRIB;<br />
typedef struct tagXMLAttrib            *LPXMLATTRIB;<br />
typedef struct tagXMLAttrib    const    *LPCXMLATTRIB;</p>
<p>struct tagXMLAttrib<br />
{<br />
LPBSTRING   m_pbstrName;<br />
LPBSTRING   m_pbstrValue;<br />
LPXMLATTRIB    m_pNext;<br />
};</p>
<p>struct tagXMLNode;<br />
typedef struct tagXMLNode       XMLNODE;<br />
typedef struct tagXMLNode       *LPXMLNODE;<br />
typedef struct tagXMLNode const *LPCXMLNODE;</p>
<p>struct tagXMLNode<br />
{<br />
NODETYPE    m_eNodeType;<br />
LONG        m_lDepth;</p>
<p>LPBSTRING   m_pbstrTag;</p>
<p>LONG        m_lChildNum;<br />
LONG        m_lChildNum_Elem;</p>
<p>LPXMLNODE    m_pRoot;<br />
LPXMLNODE   m_pParent;<br />
LPXMLNODE   m_pFirstChild;<br />
LPXMLNODE   m_pLastChild;<br />
LPXMLNODE   m_pPrevSibling;<br />
LPXMLNODE   m_pNextSibling;</p>
<p>LONG        m_lAttribNum;<br />
LPXMLATTRIB m_pFirstAttrib;<br />
};</p>
<p>struct tagXMLDocument<br />
{<br />
DOCSTAT     m_eDocStat;<br />
LPXMLNODE   m_lpRootNode;<br />
};</p>
<p>typedef struct tagXMLDocument       XMLDOCUMENT;<br />
typedef struct tagXMLDocument       *LPXMLDOCUMENT;<br />
typedef struct tagXMLDocument const *LPCXMLDOCUMENT;</p>
<p>//////////////////////////////<br />
// Types</p>
<p>typedef ITERSTAT    (CALLBACK *LPFNNODEPROC)( LPCXMLNODE pNode, LPVOID pvParam );</p>
<p>//////////////////////////////<br />
// Macros</p>
<p>#if    defined(USE_INLINE_FUNCTION)</p>
<p>#define BSTR_<a href="http://www.donevii.com/post/tag/c" class="st_tag internal_tag" rel="tag" title="Posts tagged with c">C</a>( pBStr )                     (&amp;((pBStr)-&gt;m_paStr[0]))<br />
#define BSTR_CAST( pvAnyType )              ((LPBSTRING)pvAnyType)<br />
#define    BSTR_LEN( pBStr )                   ((pBStr)-&gt;m_lLength)<br />
#define BSTR_ALLOC( pszStr )                AllocBString( pszStr, (NULL != (pszStr) ? ((LONG)strlen(pszStr)) : (0L)) )<br />
#define BSTR_ALLOCEX( pszStr, nLen )        AllocBString( pszStr, (LONG)nLen )<br />
#define BSTR_FREE( pBStr )                  free( pBStr )<br />
#define BSTR_SAFEFREE( pBStr )              if ( NULL != pBStr ) { free( pBStr ); pBStr = NULL; }<br />
#define BSTR_EQUAL( pBStrL, pBStrR ) \<br />
( (BSTR_LEN(pBStrL) == BSTR_LEN(pBStrR) &amp;&amp; 0 == strcmp(BSTR_C(pBStrL), BSTR_C(pBStrL))) ? \<br />
TRUE : FALSE )<br />
#define BSTR_EQUAL_STATIC( pBStr, szStatic ) \<br />
( (BSTR_LEN(pBStr) == (LONG)(sizeof(szStatic) &#8211; 1) &amp;&amp; 0 == strcmp(BSTR_C(pBStr), szStatic)) ? \<br />
TRUE : FALSE )<br />
#define BSTR_EQUAL_CSTR( pBStr, pszStr ) \<br />
( (BSTR_LEN(pBStr) == (LONG)strlen(pszStr) &amp;&amp; 0 == strcmp(BSTR_C(pBStr), pszStr)) ? \<br />
TRUE : FALSE )</p>
<p>#define    <a href="http://www.donevii.com/post/tag/xml" class="st_tag internal_tag" rel="tag" title="Posts tagged with xml">XML</a>_GetRootNode( pDoc )             ((NULL == (pDoc) || (pDoc)-&gt;m_eDocStat != DSTAT_OPENED) ? NULL : (pDoc)-&gt;m_lpRootNode)<br />
#define    XML_GetNodeType( pNode )             (NULL != (pNode) ? (pNode)-&gt;m_eNodeType : NODETYPE_UNKN)<br />
#define    XML_GetNodeDepth( pNode )            (NULL != (pNode) ? (pNode)-&gt;m_lDepth : (-1L))<br />
#define    XML_GetNodeParent( pNode )             (NULL != (pNode) ? (pNode)-&gt;m_pParent : NULL)<br />
#define    XML_GetNodeFirstChild( pNode )         (NULL != (pNode) ? (pNode)-&gt;m_pFirstChild : NULL)<br />
#define    XML_GetNodeLastChild( pNode )         (NULL != (pNode) ? (pNode)-&gt;m_pLastChild : NULL)<br />
#define    XML_GetNodeChildNum( pNode )         (NULL != (pNode) ? (pNode)-&gt;m_lChildNum : (0L))<br />
#define    XML_GetNodeChildNum_Elem( pNode )     (NULL != (pNode) ? (pNode)-&gt;m_lChildNum_Elem : (0L))<br />
#define    XML_GetNodePrevSibling( pNode )     (NULL != (pNode) ? (pNode)-&gt;m_pPrevSibling : NULL)<br />
#define    XML_GetNodeNextSibling( pNode )     (NULL != (pNode) ? (pNode)-&gt;m_pNextSibling : NULL)<br />
#define    XML_GetNodeTagName( pNode )         (NULL != (pNode) ? (pNode)-&gt;m_pbstrTag : NULL)<br />
#define    XML_GetNodeAttribNum( pNode )         (NULL != (pNode) ? (pNode)-&gt;m_lAttribNum : (0L))<br />
#define    XML_GetNodeFirstAttrib( pNode )     (NULL != (pNode) ? (pNode)-&gt;m_pFirstAttrib : NULL)<br />
#define    XML_GetNodeNextAttrib( pAttr )         (NULL != (pAttr) ? (pAttr)-&gt;m_pNext : NULL)<br />
#define XML_GetAttribValueBString( pAttr )  (NULL != (pAttr) ? (pAttr)-&gt;m_pbstrValue) : NULL)<br />
#define XML_GetAttribValueCString( pAttr )  (NULL != (pAttr) ? BSTR_C((pAttr)-&gt;m_pbstrValue) : NULL)<br />
#define XML_GetAttribValueLong( pAttr )     (NULL != (pAttr) ? (LONG)strtol(BSTR_C((pAttr)-&gt;m_pbstrValue), NULL, 0) : (0L))<br />
#define XML_GetAttribValueInt( pAttr )      (NULL != (pAttr) ? (int)strtol(BSTR_C((pAttr)-&gt;m_pbstrValue), NULL, 0) : (0))</p>
<p>#endif</p>
<p>//////////////////////////////<br />
// Function prototypes</p>
<p>#if defined(__cplusplus)<br />
extern &#8220;C&#8221; {<br />
#endif</p>
<p>size_t      strlen_when( LPCSTR lpszStr, CHAR ch );<br />
size_t        strlen_notin( LPCSTR lpszStr, LPCSTR lpszSet );<br />
LPCSTR        strchr_notin( LPCSTR lpszStr, LPCSTR lpszSet );<br />
LPCSTR        strchr_skipws( LPCSTR lpszStr );</p>
<p>LPBSTRING   AllocBString( LPCSTR lpszStr, LONG lLen );</p>
<p>XMLERR      XML_OpenDocument( LPXMLDOCUMENT pDoc, LPCSTR lpszFileName, DWORD dwReserve );<br />
XMLERR      XML_CloseDocument( LPXMLDOCUMENT pDoc );<br />
LPXMLNODE    XML_GetNode( LPXMLDOCUMENT pDoc, LPXMLNODE pStartPoint, LPCSTR lpszTag, LONG lLen );<br />
LPXMLATTRIB    XML_GetNodeAttrib( LPXMLNODE pNode, LPCSTR lpszAttr, LONG lLen );<br />
LPXMLNODE    XML_GetNodeSibling( LPXMLNODE pNode, LPCSTR lpszTag, LONG lLen, BOOL IncludeThis );<br />
LONG         XML_ForEachNode( LPXMLDOCUMENT pDoc, LPXMLNODE pStartPoint, LPFNNODEPROC pfnNodeProc, LPVOID pvParam );</p>
<p>#if defined(__cplusplus)<br />
}   // extern &#8220;C&#8221; {<br />
#endif</p>
<p>#endif // #ifndef __XMLPARSE_H</p>
<h3>PLX_XMLParser.c</h3>
<p>#include &lt;assert.h&gt;<br />
#include &lt;fcntl.h&gt;<br />
#include &lt;io.h&gt;<br />
#if defined(<a href="http://www.donevii.com/post/tag/debug" class="st_tag internal_tag" rel="tag" title="Posts tagged with debug">DEBUG</a>) || defined(_<a href="http://www.donevii.com/post/tag/debug" class="st_tag internal_tag" rel="tag" title="Posts tagged with debug">DEBUG</a>)<br />
#include &lt;stdio.h&gt;<br />
#endif</p>
<p>#include &#8220;XMLParse.h&#8221;</p>
<p>//#pragma warning(disable:4305)</p>
<p>//////////////////////////////<br />
// Configure</p>
<p>//#define  USE_MEMORY_HEAP</p>
<p>#ifdef  USE_FILEBUFFER  // Whether use file system with os-layer buffer</p>
<p>#define INVALID_FILE_HANDLE         ((int)-1)<br />
#define FILE_HANDLE                 int</p>
<p>#define MODE_RDONLY                 (O_RDONLY)<br />
#define FILE_OPEN(pszFile,mode)     open( pszFile, mode )<br />
#define FILE_READ(hFile,pbuf,size)  read(hFile, (LPVOID)pbuf, size)<br />
#define FILE_SEEK(hFile,off,pos)    lseek( hFile, off, pos )<br />
#define FILE_CLOSE(hFile)           close( hFile )</p>
<p>#else</p>
<p>#define INVALID_FILE_HANDLE         ((FILE *)NULL)<br />
#define FILE_HANDLE                 FILE *</p>
<p>#define MODE_RDONLY                 (&#8220;r&#8221;)<br />
#define FILE_OPEN(pszFile,mode)     fopen( pszFile, mode )<br />
#define FILE_READ(hFile,pbuf,size)  fread((LPVOID)pbuf, size, 1, hFile)<br />
#define FILE_SEEK(hFile,off,pos)    fseek( hFile, off, pos )<br />
#define FILE_CLOSE(hFile)           fclose( hFile )</p>
<p>#endif</p>
<p>//////////////////////////////<br />
// Constants</p>
<p>enum {    MAXLEN_READBUF    = 1024 };</p>
<p>typedef enum {<br />
PSTAT_STOP      = 0&#215;0,<br />
PSTAT_INITIAL,<br />
PSTAT_FINAL,<br />
PSTAT_DECL_BEG,<br />
PSTAT_DECL_END,<br />
PSTAT_ELEM_BEG,<br />
PSTAT_ELEM_END,<br />
PSTAT_TEXT_BEG,<br />
PSTAT_TEXT_END,<br />
PSTAT_CDATA_BEG,<br />
PSTAT_CDATA_END,<br />
PSTAT_COMM_BEG,<br />
PSTAT_COMM_END,<br />
PSTAT_ERROR,<br />
} PARSESTAT;</p>
<p>//////////////////////////////<br />
// Macros</p>
<p>#define ZERO_MEMORY(p, size) \<br />
( memset((LPVOID)(p), 0&#215;0, (size_t)size) )</p>
<p>#define is_WhiteSpace(ch)   (((ch) == &#8216; &#8216;  || (ch) == &#8216;\t&#8217;) ? TRUE : FALSE)<br />
#define is_LineBreak(ch)    (((ch) == &#8216;\r&#8217; || (ch) == &#8216;\n&#8217;) ? TRUE : FALSE)<br />
#define is_LeftBracket(ch)    ((ch) == &#8216;&lt;&#8217; ? TRUE : FALSE)<br />
#define is_RightBracket(ch)    ((ch) == &#8216;&gt;&#8217; ? TRUE : FALSE)</p>
<p>#define is_BufferEmpty(ps)  ((ps)-&gt;m_lReadCursor &gt;= (ps)-&gt;m_lReadSize ? TRUE : FALSE)<br />
#define get_BufferChar(ps)  ((CHAR)((ps)-&gt;m_aReadBuf[(ps)-&gt;m_lReadCursor]))</p>
<p>#define is_FirstChild(pn)    ((pn)-&gt;m_pPrevSibling == NULL ? TRUE : FALSE)<br />
#define is_LastChild(pn)    ((pn)-&gt;m_pNextSibling == NULL ? TRUE : FALSE)</p>
<p>//////////////////////////////<br />
// Structures</p>
<p>struct tagXMLParseStat<br />
{<br />
PARSESTAT   m_eParseStat;<br />
LONG        m_lDepth;<br />
LPXMLNODE    m_pRootNode;<br />
LPXMLNODE    m_pLastNode;</p>
<p>FILE_HANDLE    m_hOpenFile;<br />
LONG        m_lFileCursor;<br />
LONG        m_lFileLength;</p>
<p>LONG        m_lLineNo;<br />
LONG        m_lReadCursor;<br />
LONG        m_lReadSize;<br />
BYTE        m_aReadBuf[MAXLEN_READBUF];<br />
};<br />
typedef struct tagXMLParseStat            XMLPARSESTAT;<br />
typedef struct tagXMLParseStat            *LPXMLPARSESTAT;<br />
typedef struct tagXMLParseStat    const    *LPCXMLPARSESTAT;</p>
<p>struct tagXMLIterator<br />
{<br />
LONG            m_lCount;<br />
LPFNNODEPROC    m_pfnProc;<br />
LPVOID          m_pvParam;<br />
LPXMLNODE       m_pStation;<br />
};<br />
typedef struct tagXMLIterator       XMLITERFATOR;<br />
typedef struct tagXMLIterator       *LPXMLITERFATOR;<br />
typedef struct tagXMLIterator const *LPCXMLITERFATOR;</p>
<p>/*struct tagMemoryPage<br />
{<br />
LONG    lGranu ;<br />
LONG    lSize;<br />
LPVOID  pvPage;<br />
BYTE    bUseFlags[1];<br />
};<br />
typedef struct tagMemoryPage        MEMORYPAGE;<br />
typedef struct tagMemoryPage        *LPMEMORYPAGE;<br />
typedef struct tagMemoryPage const  *LPCMEMORYPAGE;*/</p>
<p>//////////////////////////////<br />
// Function prototypes</p>
<p>#if defined(__cplusplus)<br />
extern &#8220;C&#8221; {<br />
#endif</p>
<p>static void        XML_FreeNodeTree( LPXMLNODE pNode );<br />
static void        XML_FreeAttribList( LPXMLATTRIB pAttrib );<br />
static void     XML_IterateTree( LPXMLNODE pTree, LPXMLITERFATOR pIterator );<br />
static BOOL     XMLCmpNode_EqualTag( LPCXMLNODE pNode, LPVOID pvParam );</p>
<p>static BOOL        Parser_RoutineStart( LPXMLPARSESTAT pParseStat, LPXMLDOCUMENT pResult );<br />
static int        Parser_GetTagString( LPXMLPARSESTAT pParseStat, LPSTR lpszBuf );<br />
static BOOL        Parser_ReadStream( LPXMLPARSESTAT pParseStat );<br />
static void        Parser_OnInitial( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnElemBegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnElemEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnTextBegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnTextEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnCDATABegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnCDATAEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnCommBegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnCommEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnDeclBegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static void        Parser_OnDeclEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag );<br />
static BOOL        Parser_OnFinal( LPXMLPARSESTAT pParseStat );<br />
static BOOL        Parser_OnError( LPXMLPARSESTAT pParseStat );</p>
<p>//#if defined(USE_MEMORY_HEAP)<br />
//static LPVOID   MemoryHeap_Create( LONG lInitialGranu, LONG lInitialSize );<br />
//static BOOL     MemoryHeap_Destroy( LPVOID );<br />
//static LPVOID   MemoryHeap_Alloc( void );<br />
//static void     MemoryHeap_Free( LPVOID pvBlock );<br />
//#endif</p>
<p>#if defined(__cplusplus)<br />
}   // extern &#8220;C&#8221; {<br />
#endif</p>
<p>//////////////////////////////<br />
// Function implementations</p>
<p>#if defined(__cplusplus)<br />
extern &#8220;C&#8221; {<br />
#endif</p>
<p>/*********************************************************************\<br />
* Function: strlen_when<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
size_t strlen_when( LPCSTR lpszStr, CHAR ch )<br />
{<br />
register size_t nLen;<br />
assert( NULL != lpszStr );<br />
for ( nLen = 0; *lpszStr != ch &amp;&amp; *lpszStr != &#8216;\0&#8242;;    lpszStr++, nLen++ )<br />
;<br />
return nLen;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: strlen_notin<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
size_t strlen_notin( LPCSTR lpszStr, LPCSTR lpszSet )<br />
{<br />
register size_t nLen;<br />
assert( NULL != lpszStr );<br />
for ( nLen = 0; *lpszStr != &#8216;\0&#8242; &amp;&amp; NULL == strchr(lpszSet, *lpszStr); lpszStr++, nLen++ )<br />
;<br />
return nLen;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: strchr_notin<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
LPCSTR strchr_notin( LPCSTR lpszStr, LPCSTR lpszSet )<br />
{<br />
assert( NULL != lpszStr );<br />
for ( ; *lpszStr != &#8216;\0&#8242; &amp;&amp; NULL == strchr(lpszSet, *lpszStr); lpszStr++ )<br />
;<br />
return lpszStr;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: strchr_skipws<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
LPCSTR strchr_skipws( LPCSTR lpszStr )<br />
{<br />
assert( NULL != lpszStr );<br />
for ( ; *lpszStr != &#8216;\0&#8242; &amp;&amp; is_WhiteSpace(*lpszStr); lpszStr++ )<br />
;<br />
return lpszStr;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: AllocBString<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
LPBSTRING AllocBString( LPCSTR lpszStr, LONG lLen )<br />
{<br />
LPBSTRING pBStr = (LPBSTRING)malloc( sizeof(LONG) + lLen + 1 );<br />
assert( NULL != pBStr );</p>
<p>pBStr-&gt;m_lLength = lLen;<br />
strncpy( &amp;pBStr-&gt;m_paStr[0], lpszStr, lLen );<br />
pBStr-&gt;m_paStr[lLen] = &#8216;\0&#8242;;</p>
<p>return pBStr;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_OpenDocument<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
XMLERR XML_OpenDocument( LPXMLDOCUMENT pDoc, LPCSTR lpszFileName, DWORD dwReserve )<br />
{<br />
BOOL            bRet;<br />
FILE_HANDLE        hFile;<br />
XMLPARSESTAT    xmlParseStat;</p>
<p>assert( NULL != pDoc );<br />
assert( NULL != lpszFileName );</p>
<p>if ( pDoc-&gt;m_eDocStat == DSTAT_OPENED )<br />
return XMLERR_ALRDOPEN;</p>
<p>ZERO_MEMORY( &amp;xmlParseStat, sizeof(XMLPARSESTAT) );</p>
<p>hFile = FILE_OPEN( lpszFileName, MODE_RDONLY );<br />
if ( INVALID_FILE_HANDLE == hFile )<br />
return XMLERR_EFILE;</p>
<p>xmlParseStat.m_hOpenFile    = hFile;<br />
xmlParseStat.m_lFileCursor    = 0;<br />
xmlParseStat.m_lFileLength    = FILE_SEEK( hFile, 0, SEEK_END );<br />
FILE_SEEK( hFile, 0, SEEK_SET );</p>
<p>xmlParseStat.m_lDepth = 0;<br />
xmlParseStat.m_lLineNo = 1;<br />
bRet = Parser_RoutineStart( &amp;xmlParseStat, pDoc );<br />
if ( FALSE == bRet )<br />
{<br />
FILE_CLOSE( hFile );<br />
return XMLERR_EPARSE;<br />
}</p>
<p>pDoc-&gt;m_eDocStat    = DSTAT_OPENED;<br />
pDoc-&gt;m_lpRootNode    = xmlParseStat.m_pRootNode;<br />
FILE_CLOSE( hFile );<br />
return XMLERR_OK;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_CloseDocument<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
XMLERR XML_CloseDocument( LPXMLDOCUMENT pDoc )<br />
{<br />
if ( NULL != pDoc &amp;&amp; pDoc-&gt;m_eDocStat == DSTAT_OPENED )<br />
{<br />
XML_FreeNodeTree( pDoc-&gt;m_lpRootNode );<br />
pDoc-&gt;m_lpRootNode = NULL;<br />
pDoc-&gt;m_eDocStat = DSTAT_UNOPEN;<br />
}<br />
return XMLERR_OK;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_GetTagString<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
int Parser_GetTagString( LPXMLPARSESTAT pParseStat, LPSTR lpszBuf )<br />
{<br />
int        nLen;<br />
CHAR    ch;<br />
CHAR    chTerm;<br />
BOOL    bInclude;</p>
<p>// Should to ensure barket match Here!</p>
<p>nLen = 0;<br />
for ( ;; )<br />
{<br />
if ( is_BufferEmpty(pParseStat) &amp;&amp; FALSE == Parser_ReadStream(pParseStat) )<br />
<a href="http://www.donevii.com/post/tag/goto" class="st_tag internal_tag" rel="tag" title="Posts tagged with goto">goto</a> __RET;</p>
<p>ch = get_BufferChar(pParseStat);<br />
if ( ch == &#8216;\n&#8217; )<br />
pParseStat-&gt;m_lLineNo++;</p>
<p>if ( !is_WhiteSpace(ch) &amp;&amp; !is_LineBreak(ch) )<br />
break;</p>
<p>pParseStat-&gt;m_lReadCursor++;<br />
}</p>
<p>if ( is_LeftBracket(ch) )<br />
{<br />
chTerm = &#8216;&gt;&#8217;;<br />
bInclude = TRUE;<br />
}<br />
else<br />
{<br />
chTerm = &#8216;&lt;&#8217;;<br />
bInclude = FALSE;<br />
}</p>
<p>for ( ;; )<br />
{<br />
if ( is_BufferEmpty(pParseStat) &amp;&amp; FALSE == Parser_ReadStream(pParseStat) )<br />
break;</p>
<p>ch = get_BufferChar(pParseStat);<br />
if ( ch == &#8216;\n&#8217; )<br />
pParseStat-&gt;m_lLineNo++;</p>
<p>if ( ch == chTerm )<br />
{<br />
if ( FALSE != bInclude )<br />
{<br />
lpszBuf[nLen++] = chTerm;<br />
pParseStat-&gt;m_lReadCursor++;<br />
}<br />
break;<br />
}</p>
<p>lpszBuf[nLen++] = ch;<br />
pParseStat-&gt;m_lReadCursor++;<br />
}</p>
<p>__RET:<br />
lpszBuf[nLen] = &#8216;\0&#8242;;<br />
return nLen;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_ReadStream<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
BOOL Parser_ReadStream( LPXMLPARSESTAT pParseStat )<br />
{<br />
size_t    nSize;</p>
<p>pParseStat-&gt;m_lReadCursor    = 0;<br />
pParseStat-&gt;m_lReadSize    = 0;<br />
nSize = FILE_READ( pParseStat-&gt;m_hOpenFile, (LPVOID)&amp;pParseStat-&gt;m_aReadBuf[0], MAXLEN_READBUF );<br />
if ( nSize &lt;= 0 )<br />
return FALSE;</p>
<p>pParseStat-&gt;m_lReadSize        = (LONG)nSize;<br />
pParseStat-&gt;m_lFileCursor  += (LONG)nSize;<br />
return TRUE;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_RoutineStart<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
BOOL Parser_RoutineStart( LPXMLPARSESTAT pParseStat, LPXMLDOCUMENT pResult )<br />
{<br />
BOOL    bRet;<br />
CHAR    szTag[256];</p>
<p>assert( NULL != pParseStat );</p>
<p>pParseStat-&gt;m_eParseStat = PSTAT_INITIAL;<br />
while ( pParseStat-&gt;m_eParseStat != PSTAT_STOP )<br />
{<br />
switch ( pParseStat-&gt;m_eParseStat )<br />
{<br />
case PSTAT_INITIAL:     Parser_OnInitial( pParseStat, &amp;szTag[0] );        break;</p>
<p>case PSTAT_DECL_BEG:    Parser_OnDeclBegin( pParseStat, &amp;szTag[0] );    break;<br />
case PSTAT_DECL_END:    Parser_OnDeclEnd( pParseStat, &amp;szTag[0] );        break;</p>
<p>case PSTAT_ELEM_BEG:    Parser_OnElemBegin( pParseStat, &amp;szTag[0] );    break;<br />
case PSTAT_ELEM_END:    Parser_OnElemEnd( pParseStat, &amp;szTag[0] );      break;</p>
<p>case PSTAT_TEXT_BEG:    Parser_OnTextBegin( pParseStat, &amp;szTag[0] );    break;<br />
case PSTAT_TEXT_END:    Parser_OnTextEnd( pParseStat, &amp;szTag[0] );        break;</p>
<p>case PSTAT_CDATA_BEG:    Parser_OnCDATABegin( pParseStat, &amp;szTag[0] );    break;<br />
case PSTAT_CDATA_END:    Parser_OnCDATAEnd( pParseStat, &amp;szTag[0] );        break;</p>
<p>case PSTAT_COMM_BEG:    Parser_OnCommBegin( pParseStat, &amp;szTag[0] );    break;<br />
case PSTAT_COMM_END:    Parser_OnCommEnd( pParseStat, &amp;szTag[0] );        break;</p>
<p>case PSTAT_FINAL:        bRet = Parser_OnFinal( pParseStat );            break;<br />
case PSTAT_ERROR:        bRet = Parser_OnError( pParseStat );            break;</p>
<p>default:<br />
assert( !&#8221;Unknown parsing status!&#8221; );<br />
break;<br />
}<br />
}<br />
return bRet;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnInitial<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnInitial( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
int     nLen;<br />
assert( NULL != lpszTag );</p>
<p>lpszTag[0] = &#8216;\0&#8242;;</p>
<p>nLen = Parser_GetTagString( pParseStat, lpszTag );<br />
if ( nLen &lt;= 0 )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_STOP;<br />
return;<br />
}</p>
<p>if ( lpszTag[0] == &#8216;&lt;&#8217;  )<br />
{<br />
if ( lpszTag[1] == &#8216;?&#8217;)<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_DECL_BEG;<br />
}<br />
else if ( !strncmp(&amp;lpszTag[1], &#8220;!&#8211;&#8221;, 3) )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_COMM_BEG;<br />
}<br />
else if ( lpszTag[1] == &#8216;/&#8217; )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_ELEM_END;<br />
}<br />
else if ( isalpha(lpszTag[1]) )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_ELEM_BEG;<br />
}<br />
else if ( !strncmp(&amp;lpszTag[1], &#8220;![CDATA[", 8) )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_CDATA_BEG;<br />
}<br />
else<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_ERROR;<br />
}<br />
}<br />
else if ( NULL != pParseStat-&gt;m_pLastNode )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_TEXT_BEG;<br />
}<br />
else<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_ERROR;<br />
}<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnFinal<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
BOOL Parser_OnFinal( LPXMLPARSESTAT pParseStat )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_STOP;<br />
return TRUE;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnError<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
BOOL Parser_OnError( LPXMLPARSESTAT pParseStat )<br />
{<br />
XML_FreeNodeTree( pParseStat-&gt;m_pRootNode );<br />
pParseStat-&gt;m_pRootNode     = NULL;<br />
pParseStat-&gt;m_eParseStat = PSTAT_STOP;<br />
TRACE( "[Xml Parser]: Syntax error @ %s #%ld.\n&#8221;, &#8220;&#8221;, pParseStat-&gt;m_lLineNo);<br />
return FALSE;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnDeclBegin<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnDeclBegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
int nLenTag;</p>
<p>if ( NULL != pParseStat-&gt;m_pRootNode )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_ERROR;<br />
return;<br />
}</p>
<p>lpszTag = (LPSTR)strchr_skipws( (LPCSTR)(lpszTag + 2) );<br />
if ( lpszTag == &#8216;\0&#8242; )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_ERROR;<br />
return;<br />
}</p>
<p>nLenTag = (int)strlen_notin(lpszTag, &#8221; \t\r\n&gt;&#8221;);<br />
if ( nLenTag == 3 &amp;&amp; !strncmp(lpszTag, &#8220;xml&#8221;, 3) )<br />
{<br />
// Here, dispose version and coding infomation in XML document header<br />
}<br />
else if ( nLenTag == 14 &amp;&amp; !strncmp(lpszTag, &#8220;xml-stylesheet&#8221;, 3) )<br />
{<br />
// Unsupport<br />
}<br />
else<br />
{<br />
// Unknown declaretion<br />
}</p>
<p>pParseStat-&gt;m_eParseStat = PSTAT_INITIAL;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnDeclEnd<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnDeclEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
// Do noting<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnElemBegin<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnElemBegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
int         nLenTag;<br />
int         nLenValue;<br />
LPSTR       lpszValue;<br />
LPXMLNODE   pNode;<br />
LPXMLATTRIB    pAttrib;<br />
LPXMLATTRIB pPrevAttr;</p>
<p>assert( is_LeftBracket(lpszTag[0]) );</p>
<p>// Multi-root node is not supproted<br />
if ( 0 == pParseStat-&gt;m_lDepth &amp;&amp; NULL != pParseStat-&gt;m_pRootNode )<br />
goto __ERROR;</p>
<p>pNode = (LPXMLNODE)malloc(sizeof(XMLNODE));<br />
if ( NULL == pNode )<br />
goto __ERROR;</p>
<p>ZERO_MEMORY( pNode, sizeof(XMLNODE) );<br />
pNode-&gt;m_eNodeType    = NODETYPE_ELEM;<br />
pNode-&gt;m_lDepth        = pParseStat-&gt;m_lDepth;<br />
if ( NULL != pParseStat-&gt;m_pLastNode )<br />
{<br />
assert( pNode-&gt;m_lDepth &gt;= pParseStat-&gt;m_pLastNode-&gt;m_lDepth );<br />
if ( pNode-&gt;m_lDepth &gt; pParseStat-&gt;m_pLastNode-&gt;m_lDepth ) // new child<br />
{<br />
pNode-&gt;m_pParent = pParseStat-&gt;m_pLastNode;<br />
pParseStat-&gt;m_pLastNode-&gt;m_pFirstChild = pNode;<br />
}<br />
else if ( pNode-&gt;m_lDepth == pParseStat-&gt;m_pLastNode-&gt;m_lDepth ) // new sibling<br />
{<br />
pNode-&gt;m_pParent = pParseStat-&gt;m_pLastNode-&gt;m_pParent;<br />
pParseStat-&gt;m_pLastNode-&gt;m_pNextSibling = pNode;<br />
pNode-&gt;m_pPrevSibling = pParseStat-&gt;m_pLastNode;<br />
}</p>
<p>if ( NULL != pNode-&gt;m_pParent )<br />
{<br />
pNode-&gt;m_pParent-&gt;m_lChildNum++;<br />
pNode-&gt;m_pParent-&gt;m_lChildNum_Elem++;<br />
}<br />
}</p>
<p>if ( NULL == pParseStat-&gt;m_pRootNode )<br />
{<br />
pParseStat-&gt;m_pRootNode = pNode;<br />
}<br />
pNode-&gt;m_pRoot = pParseStat-&gt;m_pRootNode;</p>
<p>pParseStat-&gt;m_pLastNode    = pNode;</p>
<p>lpszTag++;<br />
nLenTag = (int)strlen_notin(lpszTag, &#8221; \t\r\n&gt;&#8221;);<br />
if ( nLenTag &lt;= 0 )<br />
goto __ERROR;</p>
<p>pNode-&gt;m_pbstrTag = BSTR_ALLOCEX(lpszTag, nLenTag);<br />
if ( NULL == pNode-&gt;m_pbstrTag )<br />
goto __ERROR;</p>
<p>lpszTag = (LPSTR)(lpszTag + nLenTag);</p>
<p>for ( pPrevAttr = NULL;; )<br />
{<br />
lpszTag = (LPSTR)strchr_skipws( lpszTag );</p>
<p>if ( is_RightBracket(*lpszTag) )<br />
{<br />
pParseStat-&gt;m_lDepth++; // Move down one layer<br />
pParseStat-&gt;m_eParseStat = PSTAT_INITIAL;<br />
break;<br />
}<br />
else if ( !strncmp(lpszTag, &#8220;/&gt;&#8221;, 2) )<br />
{<br />
//pParseStat-&gt;m_lDepth&#8211;; //<br />
pParseStat-&gt;m_eParseStat = PSTAT_INITIAL;<br />
break;<br />
}</p>
<p>nLenTag = (int)strlen_when( lpszTag, &#8216;=&#8217; );<br />
if ( nLenTag &lt;= 0 )<br />
goto __ERROR;</p>
<p>lpszValue = strchr( (LPCSTR)(lpszTag + nLenTag), &#8216;\&#8221;&#8216; );<br />
if ( NULL == lpszValue )<br />
goto __ERROR;</p>
<p>lpszValue++;<br />
nLenValue = (int)strlen_when( lpszValue, &#8216;\&#8221;&#8216; );</p>
<p>//if ( nLenValue &lt;= 0 )<br />
//    goto __ERROR;</p>
<p>pAttrib = (LPXMLATTRIB)malloc(sizeof(XMLATTRIB));<br />
if ( NULL == pAttrib )<br />
goto __ERROR;<br />
ZERO_MEMORY( pAttrib, sizeof(XMLATTRIB) );</p>
<p>pAttrib-&gt;m_pbstrName    = BSTR_ALLOCEX( lpszTag, nLenTag );<br />
pAttrib-&gt;m_pbstrValue    = BSTR_ALLOCEX( lpszValue, nLenValue );<br />
if ( NULL == pAttrib-&gt;m_pbstrName || NULL == pAttrib-&gt;m_pbstrValue )<br />
goto __ERROR;</p>
<p>if ( NULL == pNode-&gt;m_pFirstAttrib )<br />
pNode-&gt;m_pFirstAttrib = pAttrib;</p>
<p>if ( NULL != pPrevAttr )<br />
pPrevAttr-&gt;m_pNext = pAttrib;<br />
pPrevAttr = pAttrib;</p>
<p>pNode-&gt;m_lAttribNum++;<br />
lpszTag = lpszValue + nLenValue + 1;<br />
}</p>
<p>return;</p>
<p>__ERROR:<br />
XML_FreeNodeTree( pNode );<br />
pParseStat-&gt;m_eParseStat = PSTAT_ERROR;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnElemEnd<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnElemEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
pParseStat-&gt;m_lDepth&#8211;;</p>
<p>if ( NULL != pParseStat-&gt;m_pLastNode )<br />
{<br />
if ( NULL != pParseStat-&gt;m_pLastNode-&gt;m_pParent )<br />
pParseStat-&gt;m_pLastNode-&gt;m_pParent-&gt;m_pLastChild = pParseStat-&gt;m_pLastNode;</p>
<p>if ( pParseStat-&gt;m_pLastNode-&gt;m_lDepth &gt; pParseStat-&gt;m_lDepth )<br />
pParseStat-&gt;m_pLastNode = pParseStat-&gt;m_pLastNode-&gt;m_pParent;<br />
}</p>
<p>if ( 0 == pParseStat-&gt;m_lDepth )<br />
pParseStat-&gt;m_eParseStat = PSTAT_FINAL;<br />
else<br />
pParseStat-&gt;m_eParseStat = PSTAT_INITIAL;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnTextBegin<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnTextBegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
LPXMLNODE pNode;</p>
<p>pNode = (LPXMLNODE)malloc(sizeof(XMLNODE));<br />
if ( NULL == pParseStat-&gt;m_pLastNode )<br />
goto __ERROR;</p>
<p>ZERO_MEMORY( pNode, sizeof(XMLNODE) );<br />
pNode-&gt;m_eNodeType    = NODETYPE_TEXT;<br />
pNode-&gt;m_lDepth        = pParseStat-&gt;m_lDepth;</p>
<p>if ( NULL != pParseStat-&gt;m_pLastNode )<br />
{<br />
assert( pNode-&gt;m_lDepth &gt;= pParseStat-&gt;m_pLastNode-&gt;m_lDepth );<br />
if ( pNode-&gt;m_lDepth &gt; pParseStat-&gt;m_pLastNode-&gt;m_lDepth )<br />
{<br />
pNode-&gt;m_pParent = pParseStat-&gt;m_pLastNode;<br />
pParseStat-&gt;m_pLastNode-&gt;m_pFirstChild = pNode;<br />
}<br />
else if ( pNode-&gt;m_lDepth == pParseStat-&gt;m_pLastNode-&gt;m_lDepth )<br />
{<br />
pNode-&gt;m_pParent = pParseStat-&gt;m_pLastNode-&gt;m_pParent;<br />
pParseStat-&gt;m_pLastNode-&gt;m_pNextSibling = pNode;<br />
pNode-&gt;m_pPrevSibling = pParseStat-&gt;m_pLastNode;<br />
}</p>
<p>if ( NULL != pNode-&gt;m_pParent )<br />
pNode-&gt;m_pParent-&gt;m_lChildNum++;</p>
<p>pNode-&gt;m_pRoot = pParseStat-&gt;m_pLastNode-&gt;m_pRoot;<br />
}</p>
<p>pParseStat-&gt;m_pLastNode    = pNode;</p>
<p>pNode-&gt;m_pbstrTag = BSTR_ALLOC(lpszTag);<br />
if ( NULL == pNode-&gt;m_pbstrTag )<br />
goto __ERROR;</p>
<p>pParseStat-&gt;m_lDepth++; // Move down one layer<br />
pParseStat-&gt;m_eParseStat = PSTAT_TEXT_END;<br />
return;</p>
<p>__ERROR:<br />
XML_FreeNodeTree( pNode );<br />
pParseStat-&gt;m_eParseStat = PSTAT_ERROR;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnTextEnd<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnTextEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
// Move up one layer<br />
pParseStat-&gt;m_lDepth&#8211;;</p>
<p>if ( NULL != pParseStat-&gt;m_pLastNode &amp;&amp;<br />
pParseStat-&gt;m_pLastNode-&gt;m_lDepth &gt; pParseStat-&gt;m_lDepth )<br />
pParseStat-&gt;m_pLastNode = pParseStat-&gt;m_pLastNode-&gt;m_pParent;</p>
<p>pParseStat-&gt;m_eParseStat = PSTAT_INITIAL;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnCommBegin<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnCommBegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_INITIAL;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnCommEnd<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnCommEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnCDATABegin<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnCDATABegin( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
pParseStat-&gt;m_eParseStat = PSTAT_INITIAL;<br />
// Should convert to text here<br />
}</p>
<p>/*********************************************************************\<br />
* Function: Parser_OnCDATAEnd<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void Parser_OnCDATAEnd( LPXMLPARSESTAT pParseStat, LPSTR lpszTag )<br />
{<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_IterateTree<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void XML_IterateTree( LPXMLNODE pTree, LPXMLITERFATOR pIterator )<br />
{<br />
register ITERSTAT eStat;<br />
if ( NULL != pTree )<br />
{<br />
pIterator-&gt;m_pStation = pTree;<br />
eStat = pIterator-&gt;m_pfnProc( pTree, pIterator-&gt;m_pvParam );<br />
if ( eStat == ISTAT_STOP )<br />
return;<br />
if ( eStat != ISTAT_PASS )<br />
pIterator-&gt;m_lCount++;</p>
<p>XML_IterateTree( pTree-&gt;m_pFirstChild, pIterator );<br />
XML_IterateTree( pTree-&gt;m_pNextSibling, pIterator );<br />
}<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_ForEachNode<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
LONG XML_ForEachNode( LPXMLDOCUMENT pDoc, LPXMLNODE pStartPoint, LPFNNODEPROC pfnNodeProc, LPVOID pvParam )<br />
{<br />
XMLITERFATOR    iter;</p>
<p>assert( NULL != pDoc );<br />
assert( NULL != pfnNodeProc );</p>
<p>if ( pDoc-&gt;m_eDocStat == DSTAT_UNOPEN || (NULL != pStartPoint &amp;&amp; pStartPoint-&gt;m_pRoot != pDoc-&gt;m_lpRootNode) )<br />
return 0;</p>
<p>iter.m_lCount   = 0;<br />
iter.m_pStation = NULL;<br />
iter.m_pvParam  = pvParam;<br />
iter.m_pfnProc  = pfnNodeProc;</p>
<p>pStartPoint = (NULL == pStartPoint ? pDoc-&gt;m_lpRootNode : pStartPoint);<br />
XML_IterateTree( pStartPoint, &amp;iter );<br />
return iter.m_lCount;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XMLCmpNode_EqualTag<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
ITERSTAT XMLCmpNode_EqualTag( LPCXMLNODE pNode, LPVOID pvParam )<br />
{<br />
if ( FALSE != BSTR_EQUAL(pNode-&gt;m_pbstrTag, BSTR_CAST(pvParam)) )<br />
return ISTAT_STOP;<br />
else<br />
return ISTAT_CONTINUE;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_GetNode<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
LPXMLNODE XML_GetNode( LPXMLDOCUMENT pDoc, LPXMLNODE pStartPoint, LPCSTR lpszTag, LONG lLen )<br />
{<br />
BSTRING         bStr;<br />
XMLITERFATOR    iter;</p>
<p>assert( NULL != lpszTag );<br />
assert( NULL != pDoc );</p>
<p>if ( pDoc-&gt;m_eDocStat == DSTAT_UNOPEN || pStartPoint-&gt;m_pRoot != pDoc-&gt;m_lpRootNode )<br />
return NULL;</p>
<p>bStr.m_lLength  = lLen;<br />
bStr.m_pszStr   = lpszTag;</p>
<p>iter.m_lCount   = 0;<br />
iter.m_pfnProc  = &amp;XMLCmpNode_EqualTag;<br />
iter.m_pStation = NULL;<br />
iter.m_pvParam  = (LPVOID)&amp;bStr;</p>
<p>pStartPoint = (NULL == pStartPoint ? pDoc-&gt;m_lpRootNode : pStartPoint);<br />
XML_IterateTree( pStartPoint, &amp;iter );<br />
return (iter.m_pStation);<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_GetNodeAttrib<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
LPXMLATTRIB    XML_GetNodeAttrib( LPXMLNODE pNode, LPCSTR lpszAttr, LONG lLen )<br />
{<br />
BSTRING     bStr;<br />
LPXMLATTRIB pAttrib;</p>
<p>assert( NULL != lpszAttr );<br />
assert( NULL != pNode );<br />
bStr.m_lLength  = lLen;<br />
bStr.m_pszStr   = lpszAttr;</p>
<p>for ( pAttrib = XML_GetNodeFirstAttrib(pNode); NULL != pAttrib; pAttrib = XML_GetNodeNextAttrib(pAttrib) )<br />
{<br />
if ( FALSE != BSTR_EQUAL(pAttrib-&gt;m_pbstrName, &amp;bStr) )<br />
break;<br />
}</p>
<p>return pAttrib;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_GetNodeSibling<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
LPXMLNODE XML_GetNodeSibling( LPXMLNODE pNode, LPCSTR lpszTag, LONG lLen, BOOL bIncludeThis )<br />
{<br />
BSTRING     bStr;</p>
<p>assert( NULL != lpszTag );<br />
assert( NULL != pNode );<br />
bStr.m_lLength  = lLen;<br />
bStr.m_pszStr   = lpszTag;</p>
<p>if ( FALSE != bIncludeThis &amp;&amp; FALSE != BSTR_EQUAL(pNode-&gt;m_pbstrTag, &amp;bStr) )<br />
return pNode;</p>
<p>for ( pNode = XML_GetNodeNextSibling(pNode); NULL != pNode; pNode = XML_GetNodeNextSibling(pNode) )<br />
{<br />
if ( FALSE != BSTR_EQUAL(pNode-&gt;m_pbstrTag, &amp;bStr) )<br />
break;<br />
}</p>
<p>return pNode;<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_FreeNodeTree<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void XML_FreeNodeTree( LPXMLNODE pNode )<br />
{<br />
if ( NULL != pNode )<br />
{<br />
BSTR_FREE( pNode-&gt;m_pbstrTag );</p>
<p>XML_FreeAttribList( pNode-&gt;m_pFirstAttrib );</p>
<p>if ( is_FirstChild(pNode) &amp;&amp; NULL != pNode-&gt;m_pParent )<br />
pNode-&gt;m_pParent-&gt;m_pFirstChild = pNode-&gt;m_pNextSibling;</p>
<p>if ( NULL != pNode-&gt;m_pNextSibling )<br />
pNode-&gt;m_pNextSibling-&gt;m_pPrevSibling = pNode-&gt;m_pPrevSibling;<br />
if ( NULL != pNode-&gt;m_pPrevSibling )<br />
pNode-&gt;m_pPrevSibling-&gt;m_pNextSibling = pNode-&gt;m_pNextSibling;</p>
<p>XML_FreeNodeTree( pNode-&gt;m_pFirstChild );<br />
XML_FreeNodeTree( pNode-&gt;m_pNextSibling );</p>
<p>free( pNode );<br />
}<br />
}</p>
<p>/*********************************************************************\<br />
* Function: XML_FreeAttribList<br />
* Purpose:<br />
* Params:<br />
* Return<br />
* Remarks<br />
**********************************************************************/<br />
void XML_FreeAttribList( LPXMLATTRIB pAttrib )<br />
{<br />
LPXMLATTRIB pNext;</p>
<p>while ( NULL != pAttrib )<br />
{<br />
pNext = pAttrib-&gt;m_pNext;<br />
BSTR_FREE( pAttrib-&gt;m_pbstrName );<br />
BSTR_FREE( pAttrib-&gt;m_pbstrValue );<br />
free( pAttrib );<br />
pAttrib = pNext;<br />
}<br />
}</p>
<p>#if defined(__cplusplus)<br />
}   // extern &#8220;C&#8221; {<br />
#endif</p>
]]></content:encoded>
			<wfw:commentRss>http://www.donevii.com/post/353.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

