﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-xiaomage234-随笔分类-c/c++</title><link>http://www.blogjava.net/xiaomage234/category/28353.html</link><description>生命本就是一次凄美的漂流，记忆中放不下的，永远是孩提时代的那一份浪漫与纯真！</description><language>zh-cn</language><lastBuildDate>Mon, 16 Mar 2015 17:28:00 GMT</lastBuildDate><pubDate>Mon, 16 Mar 2015 17:28:00 GMT</pubDate><ttl>60</ttl><item><title>13 款开源的全文搜索引擎［转］</title><link>http://www.blogjava.net/xiaomage234/archive/2015/03/16/423495.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 16 Mar 2015 10:37:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2015/03/16/423495.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/423495.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2015/03/16/423495.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/423495.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/423495.html</trackback:ping><description><![CDATA[<article id="post-detail-14" post="" type-post="" status-publish="" format-standard="" hentry=""  category-uncategorized"="" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; color: #444444; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; line-height: 14px; background-color: #ffffff;"><div style="margin: 0px; border: 0px; vertical-align: baseline; line-height: 1.714285714;"><div id="mid-content" style="margin: 10px; border: 0px; vertical-align: baseline;"><div id="h-0" style="margin: 0px; border: 0px; vertical-align: baseline;"><h3><span style="color: #333333; font-size: 1rem; line-height: 1.714285714;">本文转载自</span><a href="http://blog.csdn.net/xum2008/article/details/8740063" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">xum2008的博客</a><span style="color: #333333; font-size: 1rem; line-height: 1.714285714;">，主要介绍13款现有的开源搜索引擎，你可以将它们用在你的项目中以实现检索功能。&nbsp;</span></h3></div><div clearfix"="" style="margin: 0px; border: 0px; font-size: 1rem; vertical-align: baseline; color: #333333;"><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">1.&nbsp; Lucene</strong>&nbsp;<br /><br />Lucene的开发语言是Java，也是Java家族中最为出名的一个开源搜索引擎，在Java世界中已经是标准的全文检索程序，它提供了完整的查询引擎和索引引擎，没有中文分词引擎，需要自己去实现，因此用Lucene去做一个搜素引擎需要自己去架构.另外它不支持实时搜索，但linkedin和twitter有分别对Lucene改进的实时搜素. 其中Lucene有一个C++移植版本叫CLucene，CLucene因为使用C++编写，所以理论上要比lucene快.&nbsp;<br /><br />官方主页：<a href="http://lucene.apache.org/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://lucene.apache.org/</a>&nbsp;<br /><br />CLucene官方主页：<a href="http://sourceforge.net/projects/clucene/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://sourceforge.net/projects/clucene/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">2.&nbsp; Sphinx</strong>&nbsp;<br /><br />Sphinx是一个用C++语言写的开源搜索引擎，也是现在比较主流的搜索引擎之一，在建立索引的事件方面比Lucene快50%，但是索引文件比Lucene要大一倍，因此Sphinx在索引的建立方面是空间换取事件的策略，在检索速度上，和lucene相差不大，但检索精准度方面Lucene要优于Sphinx，另外在加入中文分词引擎难度方面，Lucene要优于Sphinx.其中Sphinx支持实时搜索，使用起来比较简单方便.&nbsp;<br /><br />官方主页：<a href="http://sphinxsearch.com/about/sphinx/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://sphinxsearch.com/about/sphinx/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">3.&nbsp; Xapian</strong>&nbsp;<br /><br />Xapian是一个用C++编写的全文检索程序，它的api和检索原理和lucene在很多方面都很相似，算是填补了lucene在C++中的一个空缺.&nbsp;<br /><br />官方主页：<a href="http://xapian.org/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://xapian.org/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">4.&nbsp; Nutch</strong>&nbsp;<br /><br />Nutch是一个用java实现的开源的web搜索引擎，包括爬虫crawler，索引引擎，查询引擎. 其中Nutch是基于Lucene的，Lucene为Nutch提供了文本索引和搜索的API.&nbsp;<br /><br />对于应该使用Lucene还是使用Nutch，应该是如果你不需要抓取数据的话，应该使用Lucene，最常见的应用是：你有数据源，需要为这些数据提供一个搜索页面，在这种情况下，最好的方式是直接从数据库中取出数据，并用Lucene API建立索引.&nbsp;<br /><br />官方主页：<a href="http://nutch.apache.org/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://nutch.apache.org/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">5.&nbsp; DataparkSearch</strong>&nbsp;<br /><br />DataparkSearch是一个用C语言实现的开源的搜索引擎. 其中网页排序是采用神经网络模型.&nbsp; 其中支持HTTP，HTTPS，FTP，NNTP等下载网页.包括索引引擎，检索引擎和中文分词引擎(这个也是唯一的一个开源的搜索引擎里有中文分词引擎).能个性化定制搜索结果，拥有完整的日志记录.&nbsp;<br /><br />官方主页：<a href="http://www.dataparksearch.org/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://www.dataparksearch.org/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">6.&nbsp; Zettair</strong>&nbsp;<br /><br />Zettair是根据Justin Zobel的研究成果为基础的全文检索实验系统.它是用C语言实现的. 其中Justin Zobel在全文检索领域很有名气，是业界第一个系统提出倒排序索引差分压缩算法的人，倒排列表的压缩大大提高了检索和加载的性能，同时空间膨胀率也缩小到相当优秀的水平. 由于Zettair是源于学术界，代码是由RMIT University的搜索引擎组织写的，因此它的代码简洁精炼，算法高效，是学习倒排索引经典算法的非常好的实例. 其中支持linux，windows，mac os等系统.&nbsp;<br /><br />官方主页：<a href="http://www.seg.rmit.edu.au/zettair/about.html" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://www.seg.rmit.edu.au/zettair/about.html</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">7.&nbsp; Indri</strong>&nbsp;<br /><br />Indri是一个用C语言和C++语言写的全文检索引擎系统，是由University of Massachusetts和Carnegie Mellon University合作推出的一个开源项目. 特点是跨平台，API接口支持Java，PHP，C++.&nbsp;<br /><br />官方主页：<a href="http://www.lemurproject.org/indri/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://www.lemurproject.org/indri/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">8.&nbsp; Terrier</strong>&nbsp;<br /><br />Terrier是由School of Computing Science，Universityof Glasgow用java开发的一个全文检索系统.&nbsp;<br /><br />官方主页：<a href="http://terrier.org/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://terrier.org/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">9.&nbsp; Galago</strong>&nbsp;<br /><br />Galago是一个用java语言写的关于文本搜索的工具集. 其中包括索引引擎和查询引擎，还包括一个叫TupleFlow的分布式计算框架(和google的MapReduce很像).这个检索系统支持很多Indri查询语言.&nbsp;<br /><br />官方主页：<a href="http://www.galagosearch.org/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://www.galagosearch.org/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">10.&nbsp; Zebra</strong>&nbsp;<br /><br />Zebra是一个用C语言实现的检索程序，特点是对大数据的支持，支持EMAIL，XML，MARC等格式的数据.&nbsp;<br /><br />官方主页：<a href="https://www.indexdata.com/zebra" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">https://www.indexdata.com/zebra</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">11.&nbsp; Solr</strong>&nbsp;<br /><br />Solr是一个用java开发的独立的企业级搜索应用服务器，它提供了类似于Web-service的API接口，它是基于Lucene的全文检索服务器，也算是Lucene的一个变种，很多一线互联网公司都在使用Solr，也算是一种成熟的解决方案.&nbsp;<br /><br />官方主页：<a href="http://lucene.apache.org/solr/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://lucene.apache.org/solr/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">12.&nbsp; Elasticsearch</strong>&nbsp;<br /><br />Elasticsearch是一个采用java语言开发的，基于Lucene构造的开源，分布式的搜索引擎. 设计用于云计算中，能够达到实时搜索，稳定可靠. Elasticsearch的数据模型是JSON.&nbsp;<br /><br />官方主页：<a href="http://www.elasticsearch.org/" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">http://www.elasticsearch.org/</a>&nbsp;<br /><br /><strong style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline;">13.&nbsp; Whoosh</strong>&nbsp;<br /><br />Whoosh是一个用纯python写的开源搜索引擎.&nbsp;<br /><br />官方主页：<a href="https://bitbucket.org/mchaput/whoosh/wiki/Home" target="_blank" style="margin: 0px; padding: 0px; border: 0px; font-size: 14px; vertical-align: baseline; outline: none; color: #9f9f9f; text-decoration: none;">https://bitbucket.org/mchaput/whoosh/wiki/Home</a>&nbsp;　</div></div><p style="margin: 1.3em 0px 1.5em; padding: 0px; border: 0px; font-size: 16px; vertical-align: baseline; line-height: 1.6;"></p><p style="margin: 1.3em 0px 1.5em; padding: 0px; border: 0px; font-size: 16px; vertical-align: baseline; line-height: 1.6;"></p></div><footer style="margin: 1.714285714rem 0px 12px; padding: 0px; border: 0px; font-size: 0.928571429rem; vertical-align: baseline; color: #757575; clear: both; line-height: 1.846153846;">发布在[ 技术 ]&nbsp;<a href="http://songwie.com/articlelist/14#" title="" rel="bookmark" style="margin: 0px; padding: 0px; border-width: 0px 0px 1px; border-bottom-style: dotted; border-bottom-color: #cccccc; font-size: 13px; vertical-align: baseline; outline: none; color: #757575; text-decoration: none;"><time datetime="2014/12/06 21:07:33" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline;">2014-12-30 12:00:00.0</time></a>&nbsp;<span style="margin: 0px; padding: 0px; border: 0px; font-size: 13px; vertical-align: baseline;">by&nbsp;<span vcard"="" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline;"><a fn=""  n"="" href="http://www.songwie.com/blog/author/1" title="" rel="author" style="margin: 0px; padding: 0px; border-width: 0px 0px 1px; border-bottom-style: dotted; border-bottom-color: #cccccc; vertical-align: baseline; outline: none; color: #757575; text-decoration: none;">从零开始</a></span></span></footer></article><nav id="nav-single" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; color: #444444; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; line-height: 14px; background-color: #ffffff;"><h3>Post navigation</h3><span style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; float: left; width: 480px;">上一篇<a href="http://songwie.com/articlelist/" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #0f3647; text-decoration: none;"></a></span><span style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; float: right; text-align: right; width: 480px;">下一篇<a href="http://songwie.com/articlelist/" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #0f3647; text-decoration: none;"></a></span></nav><div id="comments" style="margin: 3.428571429rem 0px; border: 0px; vertical-align: baseline; color: #444444; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; line-height: 14px; background-color: #ffffff;"><ol style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; list-style: none;"><div style="margin: 20px 0px 0px; border: 0px; vertical-align: baseline; min-height: 20px;"><li style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline;"><div style="margin: 0px 0px 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-width: 0px 0px 1px; border-bottom-style: dashed; border-bottom-color: #cccccc; vertical-align: baseline; min-height: 50px;"><img avatar"="" alt="" src="http://songwie.com/resources/img/reply.png" style="margin: 0px 10px 20px 0px; padding: 0px; border: 0px; vertical-align: top; border-radius: 6px; width: 42px; height: 42px; float: left;" /><div id="comment-42" style="margin: 0px 0px 0px 35px; padding-left: 5px; border: 0px; vertical-align: baseline;"><div style="margin: 0px; border: 0px; vertical-align: baseline;"><a href="http://songwie.com/articlelist/article/reply/14#42" rel="external nofollow" style="margin: 0px; padding: 0px; border: 0px; font-size: 1em; vertical-align: baseline; outline: none; color: #0f3647; text-decoration: none; font-weight: bold; font-family: 'Helvetica Neue', Helvetica, Tahoma, Arial, STXihei, 'Microsoft YaHei', 微软雅黑, sans-serif; line-height: 1.5em;">不错</a>&nbsp;<span style="margin: 0px 0px 0px 10px; padding: 0px; border: 0px; font-size: 12px; vertical-align: baseline; color: #999999;"><a href="http://songwie.com/articlelist/article/reply/14#42" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #0f3647; text-decoration: none;"><time datetime="2014/12/09 09:24:13" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline;">在2014-12-30 18:12:42.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</time></a></span><span style="margin: 0px; padding: 0px; border: 0px; font-size: 12px; vertical-align: baseline; color: #999999;"><a href="http://songwie.com/articlelist/article/reply/14#42" style="margin: 0px; padding: 0px; border: 0px; font-size: 0.928571429rem; vertical-align: baseline; outline: none; color: #686868; text-decoration: none; line-height: 1.846153846;"><span fa-comment-o=""  "="" style="margin: 0px; padding: 0px; border: 0px; font-size: 13px; vertical-align: baseline; display: inline-block; font-family: FontAwesome; line-height: 1; -webkit-font-smoothing: antialiased;">回复</span></a></span></div><div style="margin: 5px 0px 0px; border: 0px; vertical-align: baseline; word-break: break-all; color: #333333;">不错</div></div></div></li></div></ol><ol style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; list-style: none;"><div style="margin: 20px 0px 0px; border: 0px; vertical-align: baseline; min-height: 20px;"><li style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline;"><div style="margin: 0px 0px 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-width: 0px 0px 1px; border-bottom-style: dashed; border-bottom-color: #cccccc; vertical-align: baseline; min-height: 50px;"><img avatar"="" alt="" src="http://songwie.com/resources/img/reply.png" style="margin: 0px 10px 20px 0px; padding: 0px; border: 0px; vertical-align: top; border-radius: 6px; width: 42px; height: 42px; float: left;" /><div id="comment-943" style="margin: 0px 0px 0px 35px; padding-left: 5px; border: 0px; vertical-align: baseline;"><div style="margin: 0px; border: 0px; vertical-align: baseline;"><a href="http://songwie.com/articlelist/article/reply/14#943" rel="external nofollow" style="margin: 0px; padding: 0px; border: 0px; font-size: 1em; vertical-align: baseline; outline: none; color: #0f3647; text-decoration: none; font-weight: bold; font-family: 'Helvetica Neue', Helvetica, Tahoma, Arial, STXihei, 'Microsoft YaHei', 微软雅黑, sans-serif; line-height: 1.5em;"></a><span style="margin: 0px 0px 0px 10px; padding: 0px; border: 0px; font-size: 12px; vertical-align: baseline; color: #999999;"><a href="http://songwie.com/articlelist/article/reply/14#943" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #0f3647; text-decoration: none;"><time datetime="2014/12/09 09:24:13" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline;">在2015-03-16 18:18:40.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</time></a></span><span style="margin: 0px; padding: 0px; border: 0px; font-size: 12px; vertical-align: baseline; color: #999999;"><a href="http://songwie.com/articlelist/article/reply/14#943" style="margin: 0px; padding: 0px; border: 0px; font-size: 0.928571429rem; vertical-align: baseline; outline: none; color: #686868; text-decoration: none; line-height: 1.846153846;"><span fa-comment-o=""  "="" style="margin: 0px; padding: 0px; border: 0px; font-size: 13px; vertical-align: baseline; display: inline-block; font-family: FontAwesome; line-height: 1; -webkit-font-smoothing: antialiased;">回复</span></a></span></div><div style="margin: 5px 0px 0px; border: 0px; vertical-align: baseline; word-break: break-all; color: #333333;">增加一个，SolrCloud是基于Solr和Zookeeper的分布式搜索方案，是正在开发中的Solr4.0的核心组件之一，它的主要思想是使用Zookeeper作为集群的配置信息中心。它有几个特色功能：1）集中式的配置信息 2）自动容错 3）近实时搜索 4）查询时自动负载均衡</div></div></div></li></div></ol></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/423495.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2015-03-16 18:37 <a href="http://www.blogjava.net/xiaomage234/archive/2015/03/16/423495.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebSocket协议分析</title><link>http://www.blogjava.net/xiaomage234/archive/2014/04/19/412684.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Sat, 19 Apr 2014 07:16:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2014/04/19/412684.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/412684.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2014/04/19/412684.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/412684.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/412684.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body" style="margin-bottom: 20px; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19.5px; background-color: #ffffff;"><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">内容不断更新，目前包括协议中握手和数据帧的分析</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">1.1 背景</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">1.2 协议概览</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">协议包含两部分：握手，数据传输。</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">客户端的握手如下：<br />GET /chat HTTP/1.1<br />Host: server.example.com<br />Upgrade: websocket<br />Connection: Upgrade<br />Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==<br />Origin: http://example.com<br />Sec-WebSocket-Protocol: chat, superchat<br />Sec-WebSocket-Version: 13</p><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">服务端的握手如下：<br />        HTTP/1.1 101 Switching Protocols         Upgrade: websocket         Connection: Upgrade         Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=         Sec-WebSocket-Protocol: chat</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">客户端和服务端都发送了握手，并且成功，数据传输即可开始。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">1.3 发起握手</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">发起握手是为了兼容基于HTTP的服务端程序，这样一个端口可以同时处理HTTP客户端和WebSocket客户端</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">因此WebSocket客户端握手是一个HTTP Upgrade请求：<br />        GET /chat HTTP/1.1         Host: server.example.com         Upgrade: websocket         Connection: Upgrade         Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==         Origin: http://example.com         Sec-WebSocket-Protocol: chat, superchat         Sec-WebSocket-Version: 13</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">握手中的域的顺序是任意的。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">5 数据帧</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">5.1 概述</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">WebScoket协议中，数据以帧序列的形式传输。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">考虑到数据安全性，客户端向服务器传输的数据帧<strong>必须</strong>进行掩码处理。服务器若接收到未经过掩码处理的数据帧，则必须主动关闭连接。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">服务器向客户端传输的数据帧<strong>一定不能</strong>进行掩码处理。客户端若接收到经过掩码处理的数据帧，则必须主动关闭连接。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">针对上情况，发现错误的一方可向对方发送close帧（状态码是1002，表示协议错误），以关闭连接。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">5.2 帧协议</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">WebSocket数据帧结构如下图所示：</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">      0                   1                   2                   3       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1      +-+-+-+-+-------+-+-------------+-------------------------------+      |F|R|R|R| opcode|M| Payload len |    Extended payload length    |      |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |      |N|V|V|V|       |S|             |   (if payload len==126/127)   |      | |1|2|3|       |K|             |                               |      +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +      |     Extended payload length continued, if payload len == 127  |      + - - - - - - - - - - - - - - - +-------------------------------+      |                               |Masking-key, if MASK set to 1  |      +-------------------------------+-------------------------------+      | Masking-key (continued)       |          Payload Data         |      +-------------------------------- - - - - - - - - - - - - - - - +      :                     Payload Data continued ...                :      + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +      |                     Payload Data continued ...                |      +---------------------------------------------------------------+</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">FIN：1位<br />表示这是消息的最后一帧（结束帧），一个消息由一个或多个数据帧构成。若消息由一帧构成，起始帧即结束帧。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">RSV1，RSV2，RSV3：各1位<br />MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.<br />这里我翻译不好，大致意思是如果未定义扩展，各位是0；如果定义了扩展，即为非0值。如果接收的帧此处非0，扩展中却没有该值的定义，那么关闭连接。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">OPCODE：4位<br />解释PayloadData，如果接收到未知的opcode，接收端必须关闭连接。<br />0x0表示附加数据帧<br />0x1表示文本数据帧<br />0x2表示二进制数据帧<br />0x3-7暂时无定义，为以后的非控制帧保留<br />0x8表示连接关闭<br />0x9表示ping<br />0xA表示pong<br />0xB-F暂时无定义，为以后的控制帧保留</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">MASK：1位<br />用于标识PayloadData是否经过掩码处理。如果是1，Masking-key域的数据即是掩码密钥，用于解码PayloadData。客户端发出的数据帧需要进行掩码处理，所以此位是1。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">Payload length：7位，7+16位，7+64位<br />PayloadData的长度（以字节为单位）。<br />如果其值在0-125，则是payload的真实长度。<br />如果值是126，则后面2个字节形成的16位无符号整型数的值是payload的真实长度。注意，网络字节序，需要转换。<br />如果值是127，则后面8个字节形成的64位无符号整型数的值是payload的真实长度。注意，网络字节序，需要转换。<br />长度表示遵循一个原则，用最少的字节表示长度（我理解是尽量减少不必要的传输）。举例说，payload真实长度是124，在0-125之间，必须用前7位表示；不允许长度1是126或127，然后长度2是124，这样违反原则。<br />Payload长度是ExtensionData长度与ApplicationData长度之和。ExtensionData长度可能是0，这种情况下，Payload长度即是ApplicationData长度。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">WebSocket协议规定数据通过帧序列传输。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">客户端必须对其发送到服务器的所有帧进行掩码处理。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">服务器一旦收到无掩码帧，将关闭连接。服务器可能发送一个状态码是1002（表示协议错误）的Close帧。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">而服务器发送客户端的数据帧不做掩码处理，一旦客户端发现经过掩码处理的帧，将关闭连接。客户端可能使用状态码1002。</pre><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"> </pre><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;"><strong>消息分片</strong></p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">分片目的是发送长度未知的消息。如果不分片发送，即一帧，就需要缓存整个消息，计算其长度，构建frame并发送；使用分片的话，可使用一个大小合适的buffer，用消息内容填充buffer，填满即发送出去。</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">分片规则：</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">1.一个未分片的消息只有一帧（FIN为1，opcode非0）</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">2.一个分片的消息由起始帧（FIN为0，opcode非0），若干（0个或多个）帧（FIN为0，opcode为0），结束帧（FIN为1，opcode为0）。</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">3.控制帧可以出现在分片消息中间，但控制帧本身不允许分片。</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">4.分片消息必须按次序逐帧发送。</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">5.如果未协商扩展的情况下，两个分片消息的帧之间不允许交错。</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">6.能够处理存在于分片消息帧之间的控制帧</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">7.发送端为非控制消息构建长度任意的分片</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">8.client和server兼容接收分片消息与非分片消息</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">9.控制帧不允许分片，中间媒介不允许改变分片结构（即为控制帧分片）</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">10.如果使用保留位，中间媒介不知道其值表示的含义，那么中间媒介不允许改变消息的分片结构</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">11.如果协商扩展，中间媒介不知道，那么中间媒介不允许改变消息的分片结构，同样地，如果中间媒介不了解一个连接的握手信息，也不允许改变该连接的消息的分片结构</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">12.由于上述规则，一个消息的所有分片是同一数据类型（由第一个分片的opcode定义）的数据。因为控制帧不允许分片，所以一个消息的所有分片的数据类型是文本、二进制、opcode保留类型中的一种。</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">需要注意的是，如果控制帧不允许夹杂在一个消息的分片之间，延迟会较大，比如说当前正在传输一个较大的消息，此时的ping必须等待消息传输完成，才能发送出去，会导致较大的延迟。为了避免类似问题，需要允许控制帧夹杂在消息分片之间。</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;"><strong>控制帧</strong></p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;"><strong></strong>&nbsp;</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;"><strong></strong></p><p style="font-size: 13px; line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">根据官方文档整理，官方文档参考<a href="http://datatracker.ietf.org/doc/rfc6455/?include_text=1" style="color: navy; text-decoration: none;">http://datatracker.ietf.org/doc/rfc6455/?include_text=1</a></p></div><div id="MySignature" style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19.5px; background-color: #ffffff;">转载请注明出处&nbsp;<a href="http://www.cnblogs.com/caosiyang/archive/2012/08/14/2637721.html" style="color: navy; text-decoration: none;">http://www.cnblogs.com/caosiyang/archive/2012/08/14/2637721.html</a></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/412684.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2014-04-19 15:16 <a href="http://www.blogjava.net/xiaomage234/archive/2014/04/19/412684.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Websocket协议简介</title><link>http://www.blogjava.net/xiaomage234/archive/2014/04/19/412683.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Sat, 19 Apr 2014 07:00:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2014/04/19/412683.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/412683.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2014/04/19/412683.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/412683.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/412683.html</trackback:ping><description><![CDATA[<div></div><div>今天@julyclyde 在微博上问我websocket的细节。但是这个用70个字是无法说清楚的，所以就整理在这里吧。恰好我最近要重构年前写的websocket的代码。</div><div></div><div>众所周知，HTTP是一种基于消息(message)的请求(request )/应答(response)协议。当我们在网页中点击一条链接（或者提交一个表单）的时候，浏览器给服务器发一个request message，然后服务器算啊算，答复一条response message。主动发起TCP连接的是client，接受TCP连接的是server。HTTP消息只有两种：request和response。client只能发送request message，server只能发送response message。一问一答，因此按HTTP协议本身的设计，服务器不能主动的把消息推给客户端。而像微博、网页聊天、网页游戏等都需要服务器主动给客户端推东西，现在只能用long polling等方式模拟，很不方便。</div><div></div><div>&nbsp;</div><div></div><div>OK，来看看internet的另一边，网络游戏是怎么工作的？</div><div></div><div>我之前在一个游戏公司工作。我们做游戏的时候，普遍采用的模式是双向、异步消息模式。</div><div></div><div>首先通信的最基本单元是message。（这点和HTTP一样）</div><div></div><div>其次，是双向的。client和server都可以给对方发消息（这点和HTTP不一样）</div><div></div><div>最后，消息是异步的。我给服务器发一条消息出去，然后可能有一条答复，也可能有多条答复，也可能根本没有答复。无论如何，调用完send方法我就不管了，我不会傻乎乎的在这里等答复。服务器和客户端都会有一个线程专门负责read，以及一个大大的switch&#8230; case，根据message id做相应的action。</div><div></div><div>while( msg=myconnection.readMessage()){</div><div></div><div>switch(msg.id()){</div><div></div><div>case LOGIN: do_login(); break;</div><div>case TALK: do_talk(); break;</div><div>&#8230;</div><div></div><div>}</div><div></div><div>}</div><div></div><div>Websocket就是把这样一种模式，搬入到HTTP/WEB的框架内。它主要解决两个问题：</div><div></div><div>从服务器给客户端主动推东西。</div><div>HTTP协议传输效率低下的问题。这一点在web service中尤为突出。每个请求和应答都得带上很长的http header！</div><div>websocket协议在RFC 6455中定义，这个RFC在上个月（2011年12月）才终于定稿、提交。所以目前没有任何一个浏览器是能完全符合这个RFC的最终版的。Google是websocket协议的主力支持者，目前主流的浏览器中，对websocket支持最好的就是chrome。chrome目前的最新版本是16，支持的是RFC 6455的draft 13，http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-13 。IE9则是完全不支持websocket。而server端，只有jetty和Node.js对websocket的支持比较好。</div><div></div><div>&nbsp;</div><div></div><div>Websocket协议可以分为两个阶段，一个是握手阶段，一个是数据传输阶段。</div><div></div><div>在建立TCP连接之后，首先是websocket层的握手。这阶段很简单，client给server发一个http request，server给client一个http response。这个阶段，所有数据传输都是基于文本的，和现有的HTTP/1.1协议保持兼容。</div><div></div><div>这是一个请求的例子：</div><div>Connection:Upgrade</div><div></div><div>Host:echo.websocket.org</div><div></div><div>Origin:http://websocket.org</div><div></div><div>Sec-WebSocket-Key:ov0xgaSDKDbFH7uZ1o+nSw==</div><div></div><div>Sec-WebSocket-Version:13</div><div></div><div>Upgrade:websocket</div><div></div><div>&nbsp;</div><div></div><div>（其中Host和Origin不是必须的）</div><div></div><div>Connection是HTTP/1.1中常用的一个header，以前经常填的是keepalive或close。这里填的是Upgrade。在设计HTTP/1.1的时候，委员们就想到一个问题，假如以后出HTTP 2.0了，那么现有的这套东西怎么办呢？所以HTTP协议中就预定义了一个header叫Upgrade。如果客户端发来的请求中有这个，那么意思就是说，我支持某某协议，并且我更偏向于用这个协议，你看你是不是也支持？你要是支持，咱们就换新协议吧！</div><div></div><div>然后就是websocket协议中所定义的两个特殊的header，Sec-WebSocket-Key和Sec-WebSocket-Version。</div><div></div><div>其中Sec-WebSocket-Key是客户端生的一串随机数，然后base64之后填进去的。Sec-WebSocket-Version是指协议的版本号。这里的13，代表draft 13。下面给出，我年前写的发送握手请求的JAVA代码：</div><div></div><div>// 生一个随机字符串，作为Sec-WebSocket-Key</div><div></div><div>?View Code JAVA</div><div>&nbsp; &nbsp; &nbsp; &nbsp; byte[] nonce = new byte[16]; &nbsp; &nbsp; &nbsp; &nbsp;rand.nextBytes(nonce); &nbsp; &nbsp; &nbsp; &nbsp;BASE64Encoder encode = new BASE64Encoder(); &nbsp; &nbsp; &nbsp; &nbsp;String chan = encode.encode(nonce); &nbsp; &nbsp; &nbsp; &nbsp; HttpRequest request = new BasicHttpRequest("GET", "/someurl"); &nbsp; &nbsp; &nbsp; &nbsp;request.addHeader("Host", host); &nbsp; &nbsp; &nbsp; &nbsp;request.addHeader("Upgrade", "websocket"); &nbsp; &nbsp; &nbsp; &nbsp;request.addHeader("Connection", "Upgrade"); &nbsp; &nbsp; &nbsp; &nbsp;request.addHeader("Sec-WebSocket-Key", chan); // 生的随机串 &nbsp; &nbsp; &nbsp; &nbsp; request.addHeader("Sec-WebSocket-Version", "13"); &nbsp; &nbsp; &nbsp; &nbsp;HttpResponse response; &nbsp; &nbsp; &nbsp; &nbsp;try { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;conn.sendRequestHeader(request); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;conn.flush(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;request.toString(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;response = conn.receiveResponseHeader(); &nbsp; &nbsp; &nbsp; &nbsp;} catch (HttpException ex) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;throw new RuntimeException("handshake fail", ex); &nbsp; &nbsp; &nbsp; &nbsp;}</div><div>&nbsp;</div><div></div><div>服务器在收到握手请求之后需要做相应的答复。消息的例子如下：</div><div></div><div>HTTP/1.1 101 Switching Protocols</div><div></div><div>Connection:Upgrade</div><div></div><div>Date:Sun, 29 Jan 2012 18:05:49 GMT</div><div></div><div>Sec-WebSocket-Accept:7vI97qQ5QRxq6lD6E5RRX36mOBc=</div><div></div><div>Server:jetty</div><div></div><div>Upgrade:websocket</div><div></div><div>（其中Date、Server都不是必须的）</div><div></div><div>第一行是HTTP的Status-Line。注意，这里的Status Code是101。很少见吧！Sec-WebSocket-Accept字段是一个根据client发来的Sec-WebSocket-Key得到的计算结果。</div><div></div><div>算法为：</div><div></div><div>把客户端发来的key作为字符串，与&#8221; 258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#8243;这个字符串连接起来，然后做sha1 Hash，将计算结果base64编码。注意，用来和&#8221; 258EAFA5-E914-47DA-95CA-C5AB0DC85B11&#8243;做连接操作的字符串，是base64编码后的。也就是说，客户端发来什么样就是什么样，不要试图去做base64解码。</div><div></div><div>示例代码如下：</div><div></div><div>?View Code CPP</div><div>&nbsp; &nbsp; std::string value=request.get("Sec-WebSocket-Key"); &nbsp; &nbsp;value+="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; &nbsp; &nbsp;unsigned char hash[20]; &nbsp; &nbsp;sha1::calc(value.c_str(),value.length(),hash); &nbsp; &nbsp;std::string res=base64_encode(hash,sizeof(hash)); &nbsp; &nbsp;std::ostringstream oss; &nbsp; &nbsp;oss&lt;&lt;"HTTP/1.1 101 Switching Protocols\r\n" &nbsp; &nbsp; &nbsp; &nbsp;"Upgrade: websocket\r\n" &nbsp; &nbsp; &nbsp; &nbsp;"Connection: Upgrade\r\n" &nbsp; &nbsp; &nbsp; &nbsp;"Sec-WebSocket-Accept: "&lt;&lt;res&lt;&lt;"\r\n"; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; connection.send(oss.str());</div><div>握手成功后，进入数据流阶段。这个阶段就和http协议没什么关系了。是在TCP流的基础上，把数据分成frame而已。首先，websocket的一个message，可以被分成多个frame。从逻辑上来看，frame的格式如下</div><div></div><div>isFinal</div><div></div><div>opcode</div><div></div><div>isMasked</div><div></div><div>Data length</div><div></div><div>Mask key</div><div></div><div>Data（可以是文本，也可以是二进制）</div><div></div><div>&nbsp;</div><div></div><div>isFinal:</div><div></div><div>每个frame的第一个字节的最高位，代表这个frame是不是该message的最后一个frame。1代表是最后一个，0代表后面还有。</div><div></div><div>opcode:</div><div></div><div>指明这个frame的类型。目前定义了这么几类continuation、text 、binary 、connection close、ping、pong。对于应用而言，最关心的就是，这个message是binary的呢，还是text的？因为html5中，对于这两种message的接口有细微不一样。</div><div></div><div>isMasked:</div><div></div><div>客户端发给服务器的消息，要求加扰之后发送。加扰的方式是：客户端生一个32位整数（mask key），然后把data和这32位整数做异或。</div><div></div><div>mask key:</div><div></div><div>前面已经说过了，就是用来做异或的随机数。</div><div></div><div>Data:</div><div></div><div>这才是我们真正要传输的数据啊！！</div><div></div><div>发送frame时加扰的代码如下：</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; java.util.Random rand ;</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; ByteBuffer buffer;</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; byte[] dataToSend;</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &#8230;</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; byte[] mask = new byte[4];</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; rand.nextBytes(mask);</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; buffer.put(mask);</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; int oldpos = buffer.position(); &nbsp; &nbsp; &nbsp; &nbsp;</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; buffer.put(data);</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; int newpos = buffer.position();</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; // 按位异或</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; for (int i = oldpos; i != newpos; ++i) {</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int maskIndex = (i &#8211; oldpos) % mask.length;</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buffer.put(i, (byte) (buffer.get(i) ^ (byte) mask[maskIndex]));</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div></div><div>下面讨论一下这个协议的某些设计：</div><div></div><div>为什么要做这个异或操作呢？</div><div></div><div>说来话长。首先从Connection:Upgrade这个header讲起。本来它是留给TLS用的。就是，假如我从80端口去连接一个服务器，然后通过发送Connection:Upgrade，仿照上面所说的流程，把http协议&#8221;升级&#8221;成https协议。但是实际上根本没人这么用。你要用https，你就去连接443。我80端口根本不处理https。由于这个header只是出现在rfc中，并未实际使用，于是大多数cache server看不懂这个header。这样的结果是，cache server可能以为后面的传输数据依然是普通的http协议，然后按照原来的规则做cache。那么，如果这个client和server都已经被黑客很好的操控，他就可以往这个cache server上投毒。比如，从client发送一个websocket frame，但是伪装成普通的http GET请求，指向一个JS文件。但是这个GET请求的目的地未必是之前那个websocket server，可能是另外一台web server。然后他再去操控这个web server，做好配合，给一个看起来像http response的答复（实际是websocket frame），里面放的是被修改过的js文件。然后cache server就会把这个js文件错误的缓存下来，以后发给其他人。</div><div></div><div>首先，client是谁？是浏览器。它在一个不很安全的环境中，很容易受到XSS或者流氓插件的攻击。假如我们的页面遭到了xss，使得攻击者可以利用JS从受害者的页面上发送任意字符串给服务器，如果没有这个异或操作，那么他就可以控制什么样的二进制数据出现在信道上，从而实现上述攻击。但是我还是觉得有点问题。proxy server一般都会对目的地做严格的限制，比如，sina的squid肯定不会帮new.163.com做cache。那么既然你已经控制了一个web server，为什么不让js直接这么做呢？那篇paper的名字叫《Talking to Yourself for Fun and Pro?t》，有空我继续看。貌似是中国人写的。</div><div></div><div>还有，为什么要把message分成frame呢？ 因为HTTP协议有chunk功能，可以让服务器一边生数据，一边发。而websocket协议也考虑到了这点。如果没有framing功能，那么我必须知道整个message的长度之后，才能开始发送message的data。</div><img src ="http://www.blogjava.net/xiaomage234/aggbug/412683.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2014-04-19 15:00 <a href="http://www.blogjava.net/xiaomage234/archive/2014/04/19/412683.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何將Int轉String? (C/C++) (C)</title><link>http://www.blogjava.net/xiaomage234/archive/2014/04/11/412286.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 11 Apr 2014 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2014/04/11/412286.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/412286.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2014/04/11/412286.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/412286.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/412286.html</trackback:ping><description><![CDATA[<p style="margin-top: 10px; margin-bottom: 10px; font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: normal;">C/C++並沒有提供內建的int轉string函數，這裡提供幾個方式達到這個需求。</p><p style="margin-top: 10px; margin-bottom: 10px; font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: normal;">1.若用C語言，且想將int轉char *，可用sprintf()，sprintf()可用類似printf()參數轉型。</p><div style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: normal; padding: 4px 5px 4px 4px; width: 1321.03125px; word-break: break-all; background-color: #eeeeee; border: 1px solid #cccccc;"><span style="color: #008080;">&nbsp;1</span><img id="Codehighlighter1_0_209_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span id="Codehighlighter1_0_209_Open_Text"><span style="color: #008000;">/*</span><span style="color: #008000;">&nbsp;<br /></span><span style="color: #008080;">&nbsp;2</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />(C)&nbsp;OOMusou&nbsp;2007&nbsp;</span><span style="color: #008000; text-decoration: underline;">http://oomusou.cnblogs.com</span><span style="color: #008000;"><br /></span><span style="color: #008080;">&nbsp;3</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" /><br /></span><span style="color: #008080;">&nbsp;4</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Filename&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;int2str_sprintf.cpp<br /></span><span style="color: #008080;">&nbsp;5</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Compiler&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;Visual&nbsp;C++&nbsp;8.0&nbsp;/&nbsp;ANSI&nbsp;C<br /></span><span style="color: #008080;">&nbsp;6</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Description&nbsp;:&nbsp;Demo&nbsp;the&nbsp;how&nbsp;to&nbsp;convert&nbsp;int&nbsp;to&nbsp;const&nbsp;char&nbsp;*<br /></span><span style="color: #008080;">&nbsp;7</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Release&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;01/06/2007&nbsp;1.0<br /></span><span style="color: #008080;">&nbsp;8</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" /></span><span style="color: #008000;">*/</span></span><br /><span style="color: #008080;">&nbsp;9</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" />#include&nbsp;"stdio.h"<br /><span style="color: #008080;">10</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">11</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">void</span>&nbsp;int2str(<span style="color: #0000ff;">int</span>&nbsp;,&nbsp;<span style="color: #0000ff;">char</span>&nbsp;*);<br /><span style="color: #008080;">12</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">13</span><img id="Codehighlighter1_271_330_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">int</span>&nbsp;main()&nbsp;<span id="Codehighlighter1_271_330_Open_Text">{<br /><span style="color: #008080;">14</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;<span style="color: #0000ff;">int</span>&nbsp;i&nbsp;=&nbsp;123;<br /><span style="color: #008080;">15</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;<span style="color: #0000ff;">char</span>&nbsp;s[64];<br /><span style="color: #008080;">16</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;int2str(i,&nbsp;s);<br /><span style="color: #008080;">17</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;puts(s);<br /><span style="color: #008080;">18</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" />}</span><br /><span style="color: #008080;">19</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">20</span><img id="Codehighlighter1_362_385_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">void</span>&nbsp;int2str(<span style="color: #0000ff;">int</span>&nbsp;i,&nbsp;<span style="color: #0000ff;">char</span>&nbsp;*s)&nbsp;<span id="Codehighlighter1_362_385_Open_Text">{<br /><span style="color: #008080;">21</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;sprintf(s,"%d",i);<br /><span style="color: #008080;">22</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" />}</span></div><p style="margin-top: 10px; margin-bottom: 10px; font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: normal;"><br />2.若用C語言，還有另外一個寫法，使用_itoa()，Microsoft將這個function擴充成好幾個版本，可參考MSDN Library。</p><div style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: normal; padding: 4px 5px 4px 4px; width: 1321.03125px; word-break: break-all; background-color: #eeeeee; border: 1px solid #cccccc;"><span style="color: #008080;">&nbsp;1</span><img id="Codehighlighter1_0_206_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span id="Codehighlighter1_0_206_Open_Text"><span style="color: #008000;">/*</span><span style="color: #008000;">&nbsp;<br /></span><span style="color: #008080;">&nbsp;2</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />(C)&nbsp;OOMusou&nbsp;2007&nbsp;</span><span style="color: #008000; text-decoration: underline;">http://oomusou.cnblogs.com</span><span style="color: #008000;"><br /></span><span style="color: #008080;">&nbsp;3</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" /><br /></span><span style="color: #008080;">&nbsp;4</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Filename&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;int2str_itoa.cpp<br /></span><span style="color: #008080;">&nbsp;5</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Compiler&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;Visual&nbsp;C++&nbsp;8.0&nbsp;/&nbsp;ANSI&nbsp;C<br /></span><span style="color: #008080;">&nbsp;6</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Description&nbsp;:&nbsp;Demo&nbsp;the&nbsp;how&nbsp;to&nbsp;convert&nbsp;int&nbsp;to&nbsp;const&nbsp;char&nbsp;*<br /></span><span style="color: #008080;">&nbsp;7</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Release&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;01/06/2007&nbsp;1.0<br /></span><span style="color: #008080;">&nbsp;8</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" /></span><span style="color: #008000;">*/</span></span><br /><span style="color: #008080;">&nbsp;9</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" />#include&nbsp;"stdio.h"&nbsp;&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;puts()</span><span style="color: #008000;"><br /></span><span style="color: #008080;">10</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /></span>#include&nbsp;"stdlib.h"&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;_itoa()</span><span style="color: #008000;"><br /></span><span style="color: #008080;">11</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /></span><br /><span style="color: #008080;">12</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">void</span>&nbsp;int2str(<span style="color: #0000ff;">int</span>&nbsp;,&nbsp;<span style="color: #0000ff;">char</span>&nbsp;*);<br /><span style="color: #008080;">13</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">14</span><img id="Codehighlighter1_310_369_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">int</span>&nbsp;main()&nbsp;<span id="Codehighlighter1_310_369_Open_Text">{<br /><span style="color: #008080;">15</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;<span style="color: #0000ff;">int</span>&nbsp;i&nbsp;=&nbsp;123;<br /><span style="color: #008080;">16</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;<span style="color: #0000ff;">char</span>&nbsp;s[64];<br /><span style="color: #008080;">17</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;int2str(i,&nbsp;s);<br /><span style="color: #008080;">18</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;puts(s);<br /><span style="color: #008080;">19</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" />}</span><br /><span style="color: #008080;">20</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">21</span><img id="Codehighlighter1_401_422_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">void</span>&nbsp;int2str(<span style="color: #0000ff;">int</span>&nbsp;i,&nbsp;<span style="color: #0000ff;">char</span>&nbsp;*s)&nbsp;<span id="Codehighlighter1_401_422_Open_Text">{<br /><span style="color: #008080;">22</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;_itoa(i,&nbsp;s,&nbsp;10);<br /><span style="color: #008080;">23</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" />}</span></div><p style="margin-top: 10px; margin-bottom: 10px; font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: normal;"><br />3.若用C++，stringstream是個很好用的東西，stringstream無論是&lt;&lt;或&gt;&gt;，都會自動轉型，要做各型別間的轉換，stringstream是個很好的媒介。</p><div style="font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: normal; padding: 4px 5px 4px 4px; width: 1321.03125px; word-break: break-all; background-color: #eeeeee; border: 1px solid #cccccc;"><span style="color: #008080;">&nbsp;1</span><img id="Codehighlighter1_0_204_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span id="Codehighlighter1_0_204_Open_Text"><span style="color: #008000;">/*</span><span style="color: #008000;">&nbsp;<br /></span><span style="color: #008080;">&nbsp;2</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />(C)&nbsp;OOMusou&nbsp;2007&nbsp;</span><span style="color: #008000; text-decoration: underline;">http://oomusou.cnblogs.com</span><span style="color: #008000;"><br /></span><span style="color: #008080;">&nbsp;3</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" /><br /></span><span style="color: #008080;">&nbsp;4</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Filename&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;int2str_sstream.cpp<br /></span><span style="color: #008080;">&nbsp;5</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Compiler&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;Visual&nbsp;C++&nbsp;8.0&nbsp;/&nbsp;ISO&nbsp;C++<br /></span><span style="color: #008080;">&nbsp;6</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Description&nbsp;:&nbsp;Demo&nbsp;the&nbsp;how&nbsp;to&nbsp;convert&nbsp;int&nbsp;to&nbsp;string<br /></span><span style="color: #008080;">&nbsp;7</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />Release&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;01/06/2007&nbsp;1.0<br /></span><span style="color: #008080;">&nbsp;8</span><span style="color: #008000;"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" /></span><span style="color: #008000;">*/</span></span><br /><span style="color: #008080;">&nbsp;9</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">10</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" />#include&nbsp;&lt;iostream&gt;<br /><span style="color: #008080;">11</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" />#include&nbsp;&lt;<span style="color: #0000ff;">string</span>&gt;<br /><span style="color: #008080;">12</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" />#include&nbsp;&lt;sstream&gt;<br /><span style="color: #008080;">13</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">14</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">using</span>&nbsp;<span style="color: #0000ff;">namespace</span>&nbsp;std;<br /><span style="color: #008080;">15</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">16</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">string</span>&nbsp;int2str(<span style="color: #0000ff;">int &amp;</span>);<br /><span style="color: #008080;">17</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">18</span><img id="Codehighlighter1_324_393_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">int</span>&nbsp;main(<span style="color: #0000ff;">void</span>)&nbsp;<span id="Codehighlighter1_324_393_Open_Text">{<br /><span style="color: #008080;">19</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;<span style="color: #0000ff;">int</span>&nbsp;i&nbsp;=&nbsp;123;<br /><span style="color: #008080;">20</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;<span style="color: #0000ff;">string</span>&nbsp;s;<br /><span style="color: #008080;">21</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;s&nbsp;=&nbsp;int2str(i);<br /><span style="color: #008080;">22</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">23</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;cout&nbsp;&lt;&lt;&nbsp;s&nbsp;&lt;&lt;&nbsp;endl;<br /><span style="color: #008080;">24</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" />}</span><br /><span style="color: #008080;">25</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">26</span><img id="Codehighlighter1_418_485_Open_Image" src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" alt="" align="top" style="border: 0px;" /><span style="color: #0000ff;">string</span>&nbsp;int2str(<span style="color: #0000ff;">int</span>&nbsp;&amp;i)&nbsp;<span id="Codehighlighter1_418_485_Open_Text">{<br /><span style="color: #008080;">27</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;<span style="color: #0000ff;">string</span>&nbsp;s;<br /><span style="color: #008080;">28</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;stringstream&nbsp;ss(s);<br /><span style="color: #008080;">29</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;ss&nbsp;&lt;&lt;&nbsp;i;<br /><span style="color: #008080;">30</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" /><br /><span style="color: #008080;">31</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" alt="" align="top" style="border: 0px;" />&nbsp;&nbsp;<span style="color: #0000ff;">return</span>&nbsp;ss.str();<br /><span style="color: #008080;">32</span><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" alt="" align="top" style="border: 0px;" />}</span></div><p style="margin-top: 10px; margin-bottom: 10px; font-family: Tahoma, Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: normal;"><br />4.若用C++，據稱boost有更好的方法，不過我還沒有裝boost，所以無從測試</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/412286.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2014-04-11 11:50 <a href="http://www.blogjava.net/xiaomage234/archive/2014/04/11/412286.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux使用msgpack及测试</title><link>http://www.blogjava.net/xiaomage234/archive/2014/04/10/412214.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 10 Apr 2014 05:43:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2014/04/10/412214.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/412214.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2014/04/10/412214.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/412214.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/412214.html</trackback:ping><description><![CDATA[<p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">在网络程序的开发中，免不了会涉及到服务器与客户端之间的协议交互，由于客户端与服务器端的平台的差异性（有可能是windows，android，linux等等），以及网络字节序等问题，通信包一般会做序列化与反序列化的处理，也就是通常说的打包解包工作。google的protobuf是一个很棒的东西，它不仅提供了多平台的支持，而且直接支持从配置文件生成代码。但是这么强大的功能，意味着它的代码量以及编译生成的库文件等等都不会小，如果相对于手机游戏一两M的安装包来说，这个显然有点太重量级了（ps：查了一下protobuf2.4.1的jar的包大小有400k左右），而且在手机游戏客户端与服务器的交互过程中，很多时候基本的打包解包功能就足够了。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">今天经同事推荐，查阅了一下msgpack相关的资料。msgpack提供快速的打包解包功能，官网上给出的图号称比protobuf快4倍，可以说相当高效了。最大的好处在与msgpack支持多语言，服务器端可以用C++，android客户端可以用java，能满足实际需求。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">在实际安装msgpack的过程中，碰到了一点小问题，因为开发机器是32位机器，i686的，所以安装完后跑一个简单的sample时，c++编译报错，错误的表现为链接时报错：<span style="line-height: 25px;">undefined reference to `__sync_sub_and_fetch_4'，后来参考了下面的博客，在configure时指定<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 21px;">CFLAGS="-march=i686"解决，注意要make clean先。</span></span></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">msgpack官网地址：<a href="http://msgpack.org/" style="color: #ff9900; text-decoration: none;">http://msgpack.org/</a></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">安装过程中参考的博客地址：<a href="http://msgpack.org/" style="color: #ff9900; text-decoration: none;"></a><a href="http://blog.csdn.net/xiarendeniao/article/details/6801338" style="color: #ff9900; text-decoration: none;">http://blog.csdn.net/xiarendeniao/article/details/6801338</a></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;"><br /></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">====================================================================================</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">====================================================================================</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">补上近期简单的测试结果</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">测试机器，cpu 4核&nbsp;</span><span style="color: #3333ff; font-size: 18px;">Dual-Core AMD Opteron(tm) Processor 2212，单核2GHz，1024KB cache, 内存4G。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="color: #3333ff; font-size: 18px;"><br /></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;"><strong>1. 简单包测试</strong></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><div bg_cpp"="" style="width: 936.53125px; line-height: 26px;"><div><div><strong>[cpp]</strong>&nbsp;<a href="http://blog.csdn.net/sunny3106/article/details/7375907#" title="view plain" style="background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-position: 0% 0%; background-repeat: no-repeat no-repeat;">view plain</a><a href="http://blog.csdn.net/sunny3106/article/details/7375907#" title="copy" style="background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-position: 0% 0%; background-repeat: no-repeat no-repeat;">copy</a><div style="position: absolute; left: 550px; top: 1039px; width: 18px; height: 18px; z-index: 99;"></div></div></div><ol start="1"><li style="line-height: 18px;">struct&nbsp;ProtoHead&nbsp;&nbsp;</li><li style="line-height: 18px;">{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;uint16_t&nbsp;uCmd;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;命令字&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;uint16_t&nbsp;uBodyLen;&nbsp;&nbsp;//&nbsp;包体长度(打包之后的字符串长度)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;uint32_t&nbsp;uSeq;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//消息的序列号，唯一标识一个请求&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;uint32_t&nbsp;uCrcVal;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;对包体的crc32校验值(如果校验不正确，服务器会断开连接)&nbsp;&nbsp;</li><li style="line-height: 18px;">};&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">struct&nbsp;ProtoBody&nbsp;&nbsp;</li><li style="line-height: 18px;">{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2e8b57; font-weight: bold;">int</span>&nbsp;num;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;std::string&nbsp;str;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;std::vector&lt;uint64_t&gt;&nbsp;uinlst;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;MSGPACK_DEFINE(num,&nbsp;str,&nbsp;uinlst);&nbsp;&nbsp;</li><li style="line-height: 18px;">};&nbsp;&nbsp;</li></ol></div><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">测试中省略了包头本地字节序与网络字节序之间的转化，只有包体做msgpack打包处理.</span></p><span style="font-family: Arial; line-height: 26px; font-size: 18px; color: #3333ff; background-color: #ffffff;">vector数组中元素数量为16个，每次循环做一次打包与解包，并验证前后数据是否一致，得到的测试结果如下:</span><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><table border="1" cellspacing="0" cellpadding="0" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><tbody><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">总耗时(s)</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">循环次数</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">平均每次耗时(ms)</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.004691</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">100</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.04691</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.044219</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">1000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.044219</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.435725</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">10000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.043573</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">4.473818</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">100000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.044738</span></p></td></tr></tbody></table><span style="font-family: Arial; line-height: 26px; font-size: 18px; color: #3333ff; background-color: #ffffff;">总结：基本每次耗时0.045ms左右，每秒可以打包解包22k次，速度很理想。</span><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;"><strong>2. 复杂包测试(vector嵌套)</strong></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><div bg_cpp"="" style="width: 936.53125px; line-height: 26px;"><div><div><strong>[cpp]</strong>&nbsp;<a href="http://blog.csdn.net/sunny3106/article/details/7375907#" title="view plain" style="background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_plain.gif); padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-position: 0% 0%; background-repeat: no-repeat no-repeat;">view plain</a><a href="http://blog.csdn.net/sunny3106/article/details/7375907#" title="copy" style="background-image: url(http://static.blog.csdn.net/scripts/SyntaxHighlighter/styles/images/default/ico_copy.gif); padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px; background-position: 0% 0%; background-repeat: no-repeat no-repeat;">copy</a><div style="position: absolute; left: 550px; top: 1650px; width: 18px; height: 18px; z-index: 99;"></div></div></div><ol start="1"><li style="line-height: 18px;">struct&nbsp;test_node&nbsp;&nbsp;</li><li style="line-height: 18px;">{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;std::string&nbsp;str;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;std::vector&lt;uint32_t&gt;&nbsp;idlst;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;test_node()&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str&nbsp;=&nbsp;"it's&nbsp;a&nbsp;test&nbsp;node";&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(<span style="color: #2e8b57; font-weight: bold;">int</span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i++;&nbsp;i&nbsp;&lt;&nbsp;10)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;idlst.push_back(i);&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2e8b57; font-weight: bold;">bool</span>&nbsp;operator&nbsp;==&nbsp;(const&nbsp;test_node&amp;&nbsp;node)&nbsp;const&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(node.str&nbsp;!=&nbsp;str)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;false;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(node.idlst.size()&nbsp;!=&nbsp;idlst.size())&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;false;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(<span style="color: #2e8b57; font-weight: bold;">int</span>&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;idlst.size();&nbsp;i++)&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(idlst[i]&nbsp;!=&nbsp;node.idlst[i])&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;false;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;MSGPACK_DEFINE(str,&nbsp;idlst);&nbsp;&nbsp;</li><li style="line-height: 18px;">};&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">struct&nbsp;ProtoBody&nbsp;&nbsp;</li><li style="line-height: 18px;">{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2e8b57; font-weight: bold;">int</span>&nbsp;num;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;std::string&nbsp;str;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;std::vector&lt;uint64_t&gt;&nbsp;uinlst;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;std::vector&lt;test_node&gt;&nbsp;nodelst;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;MSGPACK_DEFINE(num,&nbsp;str,&nbsp;uinlst,&nbsp;nodelst);&nbsp;&nbsp;</li><li style="line-height: 18px;">};&nbsp;&nbsp;</li></ol></div><span style="font-family: Arial; line-height: 26px; background-color: #ffffff; font-size: 18px; color: #3333ff;">每个nodelst中插入16个node，每个node中的idlst插入16个id，同1中的测试方法，得到测试结果如下：</span><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><table border="1" cellspacing="0" cellpadding="0" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><tbody><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">总耗时(s)</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">循环次数</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">平均每次耗时(ms)</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.025401</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">100</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.25401</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.248396</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">1000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.248396</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">2.533385</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">10000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.253339</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">25.823562</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">100000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.258236</span></p></td></tr></tbody></table><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">基本上每次打包解包一次要耗时0.25ms，每秒估算可以做4k次打包解包，速度还是不错的。</span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;"><br /></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">3.&nbsp;<strong>加上crc校验</strong></span></p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">如果每个循环中，打包过程加上crc的计算，解包过程中加上crc校验，得到测试结果如下：</span></p><table border="1" cellspacing="0" cellpadding="0" style="color: #000000; font-family: Arial; font-size: 14px; line-height: 26px; background-color: #ffffff;"><tbody><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">总耗时(s)</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">循环次数</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">平均每次耗时(ms)</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.025900</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">100</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.25900</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.260424</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">1000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.260424</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">2.649585</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">10000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.264959</span></p></td></tr><tr><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">26.855452</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">100000</span></p></td><td valign="top"><p style="margin: 0px; padding: 0px;"><span style="font-size: 18px; color: #3333ff;">0.268555</span></p></td></tr></tbody></table><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-size: 18px; color: #3333ff;">基本上每次打包解包耗时0.26ms左右，与没有crc差别不大；</span></p><img src ="http://www.blogjava.net/xiaomage234/aggbug/412214.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2014-04-10 13:43 <a href="http://www.blogjava.net/xiaomage234/archive/2014/04/10/412214.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对象序列化类库MsgPack介绍</title><link>http://www.blogjava.net/xiaomage234/archive/2014/04/10/412213.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 10 Apr 2014 05:42:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2014/04/10/412213.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/412213.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2014/04/10/412213.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/412213.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/412213.html</trackback:ping><description><![CDATA[<p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">MessagePack(以下简称<a href="http://www.cppfans.org/tag/msgpack" title="" target="_blank" data-original-title="查看MsgPack中的全部文章" style="color: #428bca;">MsgPack</a>)一个基于二进制<a href="http://www.cppfans.org/tag/%e9%ab%98%e6%95%88" title="" target="_blank" data-original-title="查看高效中的全部文章" style="color: #428bca;">高效</a>的对象<a href="http://www.cppfans.org/tag/%e5%ba%8f%e5%88%97%e5%8c%96" title="" target="_blank" data-original-title="查看序列化中的全部文章" style="color: #428bca;">序列化</a><a href="http://www.cppfans.org/tag/%e7%b1%bb%e5%ba%93" title="" target="_blank" data-original-title="查看类库中的全部文章" style="color: #428bca;">类库</a>，可用于跨语言通信。它可以像JSON那样，在许多种语言之间交换结构对象；但是它比JSON更快速也更轻巧。支持Python、Ruby、Java、C/C++等众<a href="http://www.cppfans.org/tag/%e5%a4%9a%e8%af%ad%e8%a8%80" title="" target="_blank" data-original-title="查看多语言中的全部文章" style="color: #428bca;">多语言</a>。比Google Protocol Buffers还要快4倍。</p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">代码：<br />&gt; require &#8216;msgpack&#8217;<br />&gt; msg = [1,2,3].to_msgpack&nbsp; #=&gt; &#8220;\x93\x01\x02\x03&#8243;<br />&gt; MessagePack.unpack(msg)&nbsp;&nbsp; #=&gt; [1,2,3]</p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;"><span style="color: #ff6600;">以上摘自oschina介绍。</span></p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">msgpack官方主页：<a href="http://msgpack.org/" data-original-title="" title="" style="color: #428bca;">http://msgpack.org/</a></p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">github主页：<a href="https://github.com/msgpack/msgpack" target="_blank" data-original-title="" title="" style="color: #428bca;">https://github.com/msgpack/msgpack</a></p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">因我只使用C++版本，故只下载了CPP部分，大家请按需下载。</p><h5>源码安装msgpack</h5><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">打开终端下载msgpac 4 cpp最新版本0.5.7</p><pre style="padding: 10px 20px; font-family: 'courier new'; font-size: 12px; color: #f8f8d4; border-top-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; margin-top: 20px; margin-bottom: 20px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; background-color: #4a4a4a; border: none;">wget http://msgpack.org/releases/cpp/msgpack-0.5.7.tar.gz</pre><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">解压</p><pre style="padding: 10px 20px; font-family: 'courier new'; font-size: 12px; color: #f8f8d4; border-top-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; margin-top: 20px; margin-bottom: 20px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; background-color: #4a4a4a; border: none;">tar zxvf msgpack-0.5.7.tar.gz</pre><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">进入解压后的文件夹中进行安装</p><pre style="padding: 10px 20px; font-family: 'courier new'; font-size: 12px; color: #f8f8d4; border-top-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; margin-top: 20px; margin-bottom: 20px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; background-color: #4a4a4a; border: none;">cd msgpack-0.5.7 ./configure make sudo make install</pre><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">当然了，你也可以使用git和svn直接抓取源代码进行编译，不过需要安装版本控制工具。</p><h5>自动安装msgpack</h5><pre style="padding: 10px 20px; font-family: 'courier new'; font-size: 12px; color: #f8f8d4; border-top-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; margin-top: 20px; margin-bottom: 20px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; background-color: #4a4a4a; border: none;">apt-get install libmsgpack-dev</pre><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">(安装过程中会将头文件拷贝到 /usr/local/include/ 库文件拷贝到/usr/local/lib/)</p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">安装好了，我们直接使用用它看看效果。</p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">直接包含msgpack.hpp即可使用。</p><h5>simple using</h5><pre style="padding: 10px 20px; font-family: 'courier new'; font-size: 12px; color: #f8f8d4; border-top-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; margin-top: 20px; margin-bottom: 20px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; background-color: #4a4a4a; border: none;">#include &lt;msgpack.hpp&gt; #include &lt;vector&gt; #include &lt;string&gt; #include &lt;iostream&gt;  int main() { 	std::vector&lt;std::string&gt; _vecString; 	_vecString.push_back("Hello"); 	_vecString.push_back("world");  	// pack 	msgpack::sbuffer _sbuffer; 	msgpack::pack(_sbuffer, _vecString); 	std::cout &lt;&lt; _sbuffer.data() &lt;&lt; std::endl;  	// unpack 	msgpack::unpacked msg; 	msgpack::unpack(&amp;msg, _sbuffer.data(), _sbuffer.size()); 	msgpack::object obj = msg.get(); 	std::cout &lt;&lt; obj &lt;&lt; std::endl;  	// convert 	std::vector&lt;std::string&gt; _vecRString; 	obj.convert(&amp;_vecRString);  	// print 	for(size_t i = 0; i &lt; _vecRString.size(); ++i) 	{ 		std::cout &lt;&lt; _vecRString[i] &lt;&lt; std::endl; 	}      return 0; }</pre><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">结果就不贴了，大家自己运行下便知。</p><h5>using stream</h5><pre style="padding: 10px 20px; font-family: 'courier new'; font-size: 12px; color: #f8f8d4; border-top-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; margin-top: 20px; margin-bottom: 20px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; background-color: #4a4a4a; border: none;">#include &lt;msgpack.hpp&gt; #include &lt;vector&gt; #include &lt;string&gt; #include &lt;iostream&gt;  int main() { 	// msgpack stream  	// use msgpack::packer to pack multiple objects. 	msgpack::sbuffer buffer_; 	msgpack::packer pack_(&amp;buffer_); 	pack_.pack(std::string("this is 1st string")); 	pack_.pack(std::string("this is 2nd string")); 	pack_.pack(std::string("this is 3th string"));  	// use msgpack::unpacker to unpack multiple objects. 	msgpack::unpacker unpack_; 	unpack_.reserve_buffer(buffer_.size()); 	memcpy(unpack_.buffer(), buffer_.data(), buffer_.size()); 	unpack_.buffer_consumed(buffer_.size());  	msgpack::unpacked result_; 	while (unpack_.next(&amp;result_)) 	{ 		std::cout &lt;&lt; result_.get() &lt;&lt; std::endl; 	}  	return 0; }</pre><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">使用sbuffer stream<a href="http://www.cppfans.org/tag/%e5%ba%8f%e5%88%97%e5%8c%96" title="" target="_blank" data-original-title="查看序列化中的全部文章" style="color: #428bca;">序列化</a>多个对象。</p><h5>如何序列化自定义数据结构</h5><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">msgpack支持序列化/<a href="http://www.cppfans.org/tag/%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96" title="" target="_blank" data-original-title="查看反序列化中的全部文章" style="color: #428bca;">反序列化</a>自定义数据结构，只需要简单的使用MSGPACK_DEFINE宏即可。</p><pre style="padding: 10px 20px; font-family: 'courier new'; font-size: 12px; color: #f8f8d4; border-top-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; margin-top: 20px; margin-bottom: 20px; line-height: 20px; word-break: break-all; word-wrap: break-word; white-space: pre-wrap; background-color: #4a4a4a; border: none;">##include &lt;msgpack.hpp&gt; #include &lt;vector&gt; #include &lt;string&gt;  class my_class { private: 	std::string my_string; 	std::vector vec_int; 	std::vector vec_string; public: 	MSGPACK_DEFINE(my_string, vec_int, vec_string); };  int main() { 	std::vector&lt;my_class&gt; my_class_vec;  	// add some data  	msgpack::sbuffer buffer; 	msgpack::pack(buffer, my_class_vec);  	msgpack::unpacked msg; 	msgpack::unpack(&amp;msg, buffer.data(), buffer.size());  	msgpack::object obj = msg.get(); 	std::vector&lt;my_class&gt; my_class_vec_r; 	obj.convert(&amp;my_class_vec_r);  	return 0; }</pre><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">这样我们就可以在网络通讯等地方可以使用msgpack来序列化我们的数据结构，完全可以做到安全<a href="http://www.cppfans.org/tag/%e9%ab%98%e6%95%88" title="" target="_blank" data-original-title="查看高效中的全部文章" style="color: #428bca;">高效</a>，并且可以在接收方使用别的语言来处理结构做逻辑。完全是 多种语言-多种语言，现在支持的语言如下：</p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">Ruby Perl Python C/C++ Java PHP JS OC C# Lua Scala D Haskell Erlang Ocaml Smallalk GO LabVIEW</p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">完全够我们使用了，当然了，如果没有你要的语言，建议看源代码模仿一个。</p><p style="margin: 0px 0px 15px; color: #555555; font-family: 'Microsoft Yahei', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 25px;">关于性能测试结果可以查看：<a href="http://blog.csdn.net/sunny3106/article/details/7375907" data-original-title="" title="" style="color: #428bca;">linux使用msgpack及测试&nbsp;</a></p><img src ="http://www.blogjava.net/xiaomage234/aggbug/412213.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2014-04-10 13:42 <a href="http://www.blogjava.net/xiaomage234/archive/2014/04/10/412213.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>thrift rpc 使用常见问题解答和经验</title><link>http://www.blogjava.net/xiaomage234/archive/2014/02/14/409868.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 14 Feb 2014 08:42:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2014/02/14/409868.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/409868.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2014/02/14/409868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/409868.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/409868.html</trackback:ping><description><![CDATA[<p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">原文：<a href="http://blog.rushcj.com/2010/08/21/try-thrift/" style="color: #336699; text-decoration: none;">http://blog.rushcj.com/2010/08/21/try-thrift/</a></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;"><a href="http://incubator.apache.org/thrift/" style="color: #0070c5; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: 'trebuchet ms', verdana, sans-serif;">Thrift</a>是一个非常棒的工具，是Facebook的开源项目，目前的开发非常的活跃，由Apache管理，所以用的是Apache Software License，这非常重要，因为可以放心的对其修改并用到自己的项目中。</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">谈到修改Thrift,这非常重要。因为我觉得如果要严肃的使用Thrift，不可避免的要深入了解它，并几乎都要修改Thrift的代码。一个通信框架，它不可能帮你做到所有的事情，也不可能在不了解的情况下就贸然的使用。</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">1.Thrift 的Java Server/Client有个较为严重的bug(https://issues.apache.org/jira/browse/THRIFT-601 )，随机向thrift&nbsp; sever的监听端口发些数据，可能会导致Server OutOfMemory，细细看看代码，这个bug有点土。</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">2.Thrift Client线程不安全，多线程下使用可能导致Server和客户端程序崩溃。Client的每次调用远程方法其实是有多次Socket写操作，因此每个线程中使用的Client要保证独立，如果多个线程混用同一个Client(其实是用同一个Socket)，可能会导致传输的字节顺序混乱，使得Server OutOfMemory(参考1）</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">3.Thrift定义数据结构时，尽量避免用map, 或者set。在cpp下, map被对应为std::map(rb tree)和std::set，thrift生成的类不会重载&#8221;&lt;&#8221;，因此需要手动修改生成类，否则link没法通过。较为麻烦。</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">4.如果Client端基于效率考虑，要缓存Socket，需要重新实现其TTransport类，以支持 Socket缓存池。当然，这个实现其实跟thrift没多大关系，算是2次开发。但一般都要这么做的吧？</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">5.如果Client基于效率考虑，缓存了Socket，那么thrift Server端的模式选择就较为重要了。如果使用同步的TThreadPoolServer，那么无可避免的，客户端缓存1个Socket，Server端就会有一个线程一直处于Server状态，等待peek这个Socket上的数据。这个线程就不能用于其它请求了。所以，及时清理Client端的Socket及控制Socket池的大小是非常必要的。</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">6.听同事说CPP Thrift Server的Epoll NonBlocking模式有效率问题。其实，并发要求不高的Server用LT模式的EPoll其实很方便的，当然，这个要自己给Thrfit Server做patch了，不过也不麻烦。开发起来也是很方便的。我想给我们的Server加个<strong>EPOLLONESHOT</strong>的同步EPoll实现。</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">7.CPP下的 TThreadPoolServer和TThreadServer由一个有趣的问题，如果有客户端维护长连接，那么对这个Server实例做析构的时候会堵塞（前面说过了，在peek中&#8230;）。</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">8.用valgrind看，thrift cpp似乎有一些内存问题。没细看。</p><p style="color: #333333; font-family: Arial; line-height: 26px; margin: 0px 0px 1.5385em; padding: 0px; border: 0px; outline: 0px; font-size: 13px; vertical-align: baseline;">9.无论是Java，还是CPP，Server端都无法通过合法的方式获取Client的ip, port。可以通过编写ThriftServerEventHandler可以处理这件事情。如果想要获取Client ip, port的话，可以看看这个东西。</p><img src ="http://www.blogjava.net/xiaomage234/aggbug/409868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2014-02-14 16:42 <a href="http://www.blogjava.net/xiaomage234/archive/2014/02/14/409868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从Lua中调用C函数</title><link>http://www.blogjava.net/xiaomage234/archive/2013/09/17/404165.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 17 Sep 2013 04:30:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2013/09/17/404165.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/404165.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2013/09/17/404165.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/404165.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/404165.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 成功命令：＃&nbsp;cc&nbsp;-o&nbsp;my_math.so&nbsp;my_math.c&nbsp;-Wall&nbsp;-llua&nbsp;-ldl&nbsp;-lm&nbsp;-shared＃&nbsp;lua&nbsp;callc.lua过程中出现的问题：1.&nbsp;无法编译过去，&#8220;　warning:&nbsp;implicit&nbsp;declarat...&nbsp;&nbsp;<a href='http://www.blogjava.net/xiaomage234/archive/2013/09/17/404165.html'>阅读全文</a><img src ="http://www.blogjava.net/xiaomage234/aggbug/404165.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2013-09-17 12:30 <a href="http://www.blogjava.net/xiaomage234/archive/2013/09/17/404165.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>更好的内存管理-jemalloc  </title><link>http://www.blogjava.net/xiaomage234/archive/2013/09/12/403984.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 12 Sep 2013 04:05:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2013/09/12/403984.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/403984.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2013/09/12/403984.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/403984.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/403984.html</trackback:ping><description><![CDATA[from:<a href="http://wangkaisino.blog.163.com/blog/static/1870444202011431112323846/">http://wangkaisino.blog.163.com/blog/static/1870444202011431112323846/<br /><br /><br /><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">今年年初由于</span>facebook<span style="font-family: 宋体;">而火起</span><span style="font-family: 宋体;">来</span><span style="font-family: 宋体;">的</span>jemalloc<span style="font-family: 宋体;">广</span><span style="font-family: 宋体;">为</span><span style="font-family: 宋体;">人之，但殊不知，</span><span style="font-family: 宋体;">它</span><span style="font-family: 宋体;">在</span>malloc<span style="font-family: 宋体;">界里面很早就出名了。</span>Jemalloc<span style="font-family: 宋体;">的</span><span style="font-family: 宋体;">创</span><span style="font-family: 宋体;">始人</span>Jason Evans<span style="font-family: 宋体;">也是在</span>FreeBSD<span style="font-family: 宋体;">很有名的</span><span style="font-family: 宋体;">开发</span><span style="font-family: 宋体;">人</span><span style="font-family: 宋体;">员</span><span style="font-family: 宋体;">。此人就在</span>2006<span style="font-family: 宋体;">年</span><span style="font-family: 宋体;">为</span><span style="font-family: 宋体;">提高低性能的</span>malloc<span style="font-family: 宋体;">而</span><span style="font-family: 宋体;">写</span><span style="font-family: 宋体;">的</span>jemalloc<span style="font-family: 宋体;">。</span>Jemalloc<span style="font-family: 宋体;">是</span><span style="font-family: 宋体;">从</span>2007<span style="font-family: 宋体;">年</span><span style="font-family: 宋体;">开</span><span style="font-family: 宋体;">始以</span>FreeBSD<span style="font-family: 宋体;">标</span><span style="font-family: 宋体;">准引</span><span style="font-family: 宋体;">进来</span><span style="font-family: 宋体;">的。</span><span style="font-family: 宋体;">软</span><span style="font-family: 宋体;">件技</span><span style="font-family: 宋体;">术</span><span style="font-family: 宋体;">革新很多是</span>FreeBSD<span style="font-family: 宋体;">发</span><span style="font-family: 宋体;">起的。在</span>FreeBSD<span style="font-family: 宋体;">应</span><span style="font-family: 宋体;">用广泛的技</span><span style="font-family: 宋体;">术会</span><span style="font-family: 宋体;">慢慢</span><span style="font-family: 宋体;">导</span><span style="font-family: 宋体;">入到</span>linux<span style="font-family: 宋体;">。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">目前</span>jemalloc<span style="font-family: 宋体;">在</span>firefox<span style="font-family: 宋体;">中也在使用。在</span>firefox2<span style="font-family: 宋体;">中出</span><span style="font-family: 宋体;">现</span><span style="font-family: 宋体;">了</span><span style="font-family: 宋体;">内</span><span style="font-family: 宋体;">存碎片</span><span style="font-family: 宋体;">问题</span><span style="font-family: 宋体;">之后，便在</span>firefox3<span style="font-family: 宋体;">中使用了</span>jemalloc<span style="font-family: 宋体;">。在</span>safari<span style="font-family: 宋体;">和</span>chrome<span style="font-family: 宋体;">中使用的是</span>google<span style="font-family: 宋体;">的</span>tcmalloc<span style="font-family: 宋体;">。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><strong><span style="line-height: 28px; font-size: 12pt;">Jemalloc</span></strong><strong><span style="line-height: 28px; font-size: 12pt; font-family: 宋体;">的技</span></strong><strong><span style="line-height: 28px; font-size: 12pt; font-family: 宋体;">术</span></strong><strong><span style="line-height: 28px; font-size: 12pt; font-family: 宋体;">特性</span></strong><strong></strong></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;">Jemalloc<span style="font-family: 宋体;">聚集了</span>malloc<span style="font-family: 宋体;">的使用</span><span style="font-family: 宋体;">过</span><span style="font-family: 宋体;">程中所</span><span style="font-family: 宋体;">验证</span><span style="font-family: 宋体;">的很多技</span><span style="font-family: 宋体;">术</span><span style="font-family: 宋体;">。忽略</span><span style="font-family: 宋体;">细节</span><span style="font-family: 宋体;">，</span><span style="font-family: 宋体;">从</span><span style="font-family: 宋体;">架</span><span style="font-family: 宋体;">构</span><span style="font-family: 宋体;">着眼，最出色的部分仍是</span>arena<span style="font-family: 宋体;">和</span>thread cache<span style="font-family: 宋体;">。（事</span><span style="font-family: 宋体;">实</span><span style="font-family: 宋体;">上，</span><span style="font-family: 宋体;">这两个与</span>tcmalloc<span style="font-family: 宋体;">的架</span><span style="font-family: 宋体;">构</span><span style="font-family: 宋体;">几乎相同。</span>Jemalloc only<span style="font-family: 宋体;">的部分</span><span style="font-family: 宋体;">将会</span><span style="font-family: 宋体;">在另一次</span>posting<span style="font-family: 宋体;">中</span><span style="font-family: 宋体;">继续</span><span style="font-family: 宋体;">探</span><span style="font-family: 宋体;">讨</span><span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">）</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><strong>Arena</strong></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">与</span><span style="font-family: 宋体;">其像</span>malloc<span style="font-family: 宋体;">一</span><span style="font-family: 宋体;">样</span><span style="font-family: 宋体;">集中管理一整</span><span style="font-family: 宋体;">块内</span><span style="font-family: 宋体;">存，不如</span><span style="font-family: 宋体;">将</span><span style="font-family: 宋体;">其分成</span><span style="font-family: 宋体;">许</span><span style="font-family: 宋体;">多</span><span style="font-family: 宋体;">个</span><span style="font-family: 宋体;">小</span><span style="font-family: 宋体;">块来</span><span style="font-family: 宋体;">分而治之。此小</span><span style="font-family: 宋体;">块</span><span style="font-family: 宋体;">便</span><span style="font-family: 宋体;">称为</span>arena<span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">让</span><span style="font-family: 宋体;">我</span><span style="font-family: 宋体;">们</span><span style="font-family: 宋体;">想象一下，</span><span style="font-family: 宋体;">给</span><span style="font-family: 宋体;">几</span><span style="font-family: 宋体;">个</span><span style="font-family: 宋体;">小朋友一</span><span style="font-family: 宋体;">张</span><span style="font-family: 宋体;">大</span><span style="font-family: 宋体;">图纸</span><span style="font-family: 宋体;">，</span><span style="font-family: 宋体;">让</span><span style="font-family: 宋体;">他</span><span style="font-family: 宋体;">们随</span><span style="font-family: 宋体;">意地</span><span style="font-family: 宋体;">画</span><span style="font-family: 宋体;">点。</span><span style="font-family: 宋体;">结</span><span style="font-family: 宋体;">果可想而知，他</span><span style="font-family: 宋体;">们</span><span style="font-family: 宋体;">肯定相互</span><span style="font-family: 宋体;">顾</span><span style="font-family: 宋体;">忌</span><span style="font-family: 宋体;">对</span><span style="font-family: 宋体;">方而不敢肆意地</span><span style="font-family: 宋体;">画</span><span style="font-family: 宋体;">（</span>synchronization<span style="font-family: 宋体;">），</span><span style="font-family: 宋体;">从</span><span style="font-family: 宋体;">而影</span><span style="font-family: 宋体;">响画图</span><span style="font-family: 宋体;">效率。但是如果老</span><span style="font-family: 宋体;">师</span><span style="font-family: 宋体;">事先在大</span><span style="font-family: 宋体;">图纸</span><span style="font-family: 宋体;">上</span><span style="font-family: 宋体;">划</span><span style="font-family: 宋体;">分好每</span><span style="font-family: 宋体;">个</span><span style="font-family: 宋体;">人的</span><span style="font-family: 宋体;">区</span><span style="font-family: 宋体;">域，小朋友</span><span style="font-family: 宋体;">们</span><span style="font-family: 宋体;">就可以又快又准地在各自地</span><span style="font-family: 宋体;">领</span><span style="font-family: 宋体;">域上</span><span style="font-family: 宋体;">画图</span><span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">这样</span><span style="font-family: 宋体;">的</span><span style="font-family: 宋体;">概</span><span style="font-family: 宋体;">念就是</span>arena<span style="font-family: 宋体;">。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><strong>Thread cache</strong></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">如果是</span><span style="font-family: 宋体;">开</span><span style="font-family: 宋体;">辟小</span><span style="font-family: 宋体;">块内</span><span style="font-family: 宋体;">存，</span><span style="font-family: 宋体;">为</span><span style="font-family: 宋体;">使不</span><span style="font-family: 宋体;">参</span><span style="font-family: 宋体;">照</span>arena<span style="font-family: 宋体;">而直接</span>malloc<span style="font-family: 宋体;">，</span><span style="font-family: 宋体;">给</span><span style="font-family: 宋体;">各自的</span><span style="font-family: 宋体;">线</span><span style="font-family: 宋体;">程</span>thread cache<span style="font-family: 宋体;">领</span><span style="font-family: 宋体;">域。此</span>idea<span style="font-family: 宋体;">是</span>google<span style="font-family: 宋体;">的</span>tcmalloc<span style="font-family: 宋体;">的核心部分，亦在</span>jemalloc<span style="font-family: 宋体;">中体</span><span style="font-family: 宋体;">现</span><span style="font-family: 宋体;">。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">再拿上面的例子，</span><span style="font-family: 宋体;">这</span><span style="font-family: 宋体;">次</span><span style="font-family: 宋体;">给</span><span style="font-family: 宋体;">小朋友</span><span style="font-family: 宋体;">们</span><span style="font-family: 宋体;">除了一</span><span style="font-family: 宋体;">张</span><span style="font-family: 宋体;">大</span><span style="font-family: 宋体;">图纸</span><span style="font-family: 宋体;">外，再各自</span><span style="font-family: 宋体;">给</span>A4<span style="font-family: 宋体;">纸</span><span style="font-family: 宋体;">一</span><span style="font-family: 宋体;">张</span><span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">这样</span><span style="font-family: 宋体;">，小朋友</span><span style="font-family: 宋体;">们</span><span style="font-family: 宋体;">在不</span><span style="font-family: 宋体;">画</span><span style="font-family: 宋体;">大面</span><span style="font-family: 宋体;">积</span><span style="font-family: 宋体;">的点</span><span style="font-family: 宋体;">时</span><span style="font-family: 宋体;">，只在自己的</span>A4<span style="font-family: 宋体;">纸</span><span style="font-family: 宋体;">上心情地</span><span style="font-family: 宋体;">画</span><span style="font-family: 宋体;">即可（</span>no arena seeking<span style="font-family: 宋体;">）。可以在自己手上的</span><span style="font-family: 宋体;">纸</span><span style="font-family: 宋体;">上</span><span style="font-family: 宋体;">画</span><span style="font-family: 宋体;">或涂（</span>using thread cache<span style="font-family: 宋体;">），完全不用</span><span style="font-family: 宋体;">顾</span><span style="font-family: 宋体;">忌</span><span style="font-family: 宋体;">别</span><span style="font-family: 宋体;">人（</span>no synchronization, no locking<span style="font-family: 宋体;">），迅速有效地</span><span style="font-family: 宋体;">画</span><span style="font-family: 宋体;">。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">下</span><span style="font-family: 宋体;">图</span><span style="font-family: 宋体;">是</span>jemalloc<span style="font-family: 宋体;">的核心</span>layout<span style="font-family: 宋体;">。看着</span><span style="font-family: 宋体;">复杂</span><span style="font-family: 宋体;">，其</span><span style="font-family: 宋体;">实</span><span style="font-family: 宋体;">都是上面</span><span style="font-family: 宋体;">说</span><span style="font-family: 宋体;">明的部分。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"></p><div style="line-height: 25px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><img alt="更好的内存管理-jemalloc - Alex - wangkaisino的博客" src="http://img246.ph.126.net/hzhqIFgCQwJj6zbC52VGLw==/2232940990247174397.jpg" style="border: 0px; max-width: 100%; margin: 0px 10px 0px 0px;" /></div>&nbsp;<p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">实际</span>jemalloc<span style="font-family: 宋体;">的性能呢？</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><strong></strong></p><div style="line-height: 25px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><strong><img alt="更好的内存管理-jemalloc - Alex - wangkaisino的博客" src="http://img.ph.126.net/MnzyhfLLyEesD98wHZIZZA==/2298806134796631393.jpg" style="border: 0px; max-width: 100%; margin: 0px 10px 0px 0px;" /></strong></div><strong style="line-height: 25px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;">&nbsp;</strong><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">最左</span><span style="font-family: 宋体;">边</span><span style="font-family: 宋体;">的就是</span>glibc<span style="font-family: 宋体;">的</span>malloc<span style="font-family: 宋体;">，最右</span><span style="font-family: 宋体;">边</span><span style="font-family: 宋体;">的就是</span>jemalloc<span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">从图</span><span style="font-family: 宋体;">表上可以看出，</span>jemalloc<span style="font-family: 宋体;">的性能有</span>glibc<span style="font-family: 宋体;">的</span><span style="font-family: 宋体;">两</span><span style="font-family: 宋体;">倍以上。非常</span><span style="font-family: 宋体;">压</span><span style="font-family: 宋体;">倒性的性能差</span><span style="font-family: 宋体;">异</span><span style="font-family: 宋体;">。因此，使用了</span>jemalloc<span style="font-family: 宋体;">的</span><span style="font-family: 宋体;">应</span><span style="font-family: 宋体;">用程序自然</span><span style="font-family: 宋体;">会</span><span style="font-family: 宋体;">快很多。</span>Jemalloc<span style="font-family: 宋体;">旁</span><span style="font-family: 宋体;">边</span><span style="font-family: 宋体;">的就是</span>tcmalloc<span style="font-family: 宋体;">。</span>Tcmalloc<span style="font-family: 宋体;">的性能</span><span style="font-family: 宋体;">与</span><span style="font-family: 宋体;">其相差甚微，低</span>jemalloc2.1.0<span style="font-family: 宋体;">慢</span>4.5%<span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">图</span><span style="font-family: 宋体;">上和</span>tcmalloc<span style="font-family: 宋体;">的</span>1.4<span style="font-family: 宋体;">版本，而如今</span><span style="font-family: 宋体;">它</span><span style="font-family: 宋体;">已</span><span style="font-family: 宋体;">经</span><span style="font-family: 宋体;">到了</span>1.6<span style="font-family: 宋体;">版本，因此</span><span style="font-family: 宋体;">实际</span><span style="font-family: 宋体;">上</span><span style="font-family: 宋体;">这两</span><span style="font-family: 宋体;">者</span><span style="font-family: 宋体;">应该</span><span style="font-family: 宋体;">是不相仲伯的。</span>Jemalloc<span style="font-family: 宋体;">的</span><span style="font-family: 宋体;">创</span><span style="font-family: 宋体;">始人</span>jason evans<span style="font-family: 宋体;">也意</span><span style="font-family: 宋体;">识</span><span style="font-family: 宋体;">到</span><span style="font-family: 宋体;">这</span><span style="font-family: 宋体;">一点，</span><span style="font-family: 宋体;">说</span><span style="font-family: 宋体;">在</span>cpu core 8<span style="font-family: 宋体;">以上的</span><span style="font-family: 宋体;">计</span><span style="font-family: 宋体;">算机上</span>jemalloc<span style="font-family: 宋体;">效率更高。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><strong><span style="font-family: 宋体;">给</span></strong><strong><span style="font-family: 宋体;">程序</span></strong><strong><span style="font-family: 宋体;">员</span></strong><strong><span style="font-family: 宋体;">的最后的免</span></strong><strong><span style="font-family: 宋体;">费</span></strong><strong><span style="font-family: 宋体;">午餐</span>&nbsp;</strong><strong><span">&#8211;&nbsp;kth</span"></strong><strong><span style="font-family: 宋体;">分布式技</span></strong><strong><span style="font-family: 宋体;">术</span>lab</strong><strong><span style="font-family: 宋体;">的</span></strong><strong><span style="font-family: 宋体;">实</span></strong><strong><span style="font-family: 宋体;">例</span></strong><strong><span style="color: #1f497d;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></strong></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;">2005<span style="font-family: 宋体;">年</span><span style="font-family: 宋体;">发</span><span style="font-family: 宋体;">表了一篇文章</span>&#8220;<a rel="nofollow" href="http://www.gotw.ca/publications/concurrency-ddj.htm" style="color: #f58456; text-decoration: none;"><span style="font-family: 宋体;">免费午餐的时代结束了</span></a>&#8221;<span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">在之前，程序就算不用</span><span style="font-family: 宋体;">费脑</span><span style="font-family: 宋体;">子，</span><span style="font-family: 宋体;">随</span><span style="font-family: 宋体;">着</span>cpu<span style="font-family: 宋体;">时钟</span><span style="font-family: 宋体;">速度增加，程序性能自己就</span><span style="font-family: 宋体;">会</span><span style="font-family: 宋体;">上去。但</span><span style="font-family: 宋体;">现</span><span style="font-family: 宋体;">在不同，</span><span style="font-family: 宋体;">现</span><span style="font-family: 宋体;">在</span>cpu<span style="font-family: 宋体;">时钟趋</span><span style="font-family: 宋体;">于</span><span style="font-family: 宋体;">稳</span><span style="font-family: 宋体;">定，而核</span><span style="font-family: 宋体;">数</span><span style="font-family: 宋体;">不</span><span style="font-family: 宋体;">断</span><span style="font-family: 宋体;">地增加。程序</span><span style="font-family: 宋体;">员</span><span style="font-family: 宋体;">需要适</span><span style="font-family: 宋体;">应这样</span><span style="font-family: 宋体;">的多</span><span style="font-family: 宋体;">线</span><span style="font-family: 宋体;">程多</span><span style="font-family: 宋体;">进</span><span style="font-family: 宋体;">程的</span><span style="font-family: 宋体;">环</span><span style="font-family: 宋体;">境，</span><span style="font-family: 宋体;">并</span><span style="font-family: 宋体;">要</span><span style="font-family: 宋体;">开发</span><span style="font-family: 宋体;">出适合的程序。文章</span><span style="font-family: 宋体;">讲</span><span style="font-family: 宋体;">的大</span><span style="font-family: 宋体;">概</span><span style="font-family: 宋体;">是</span><span style="font-family: 宋体;">这样</span><span style="font-family: 宋体;">的</span><span style="font-family: 宋体;">内</span><span style="font-family: 宋体;">容。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;">6<span style="font-family: 宋体;">年之后的如今，</span><span style="font-family: 宋体;">这</span><span style="font-family: 宋体;">篇文章完全</span><span style="font-family: 宋体;">变</span><span style="font-family: 宋体;">成</span><span style="font-family: 宋体;">现实</span><span style="font-family: 宋体;">了。事</span><span style="font-family: 宋体;">实</span><span style="font-family: 宋体;">上</span>cpu<span style="font-family: 宋体;">时钟</span><span style="font-family: 宋体;">停留在</span>3GHz<span style="font-family: 宋体;">，而核不</span><span style="font-family: 宋体;">断</span><span style="font-family: 宋体;">上升。</span><span style="font-family: 宋体;">现</span><span style="font-family: 宋体;">在程序要适</span><span style="font-family: 宋体;">应</span><span style="font-family: 宋体;">多</span><span style="font-family: 宋体;">线</span><span style="font-family: 宋体;">程多</span><span style="font-family: 宋体;">进</span><span style="font-family: 宋体;">程的分布式</span><span style="font-family: 宋体;">计</span><span style="font-family: 宋体;">算，速度才能上升。但是</span><span style="font-family: 宋体;">这样</span><span style="font-family: 宋体;">的程序很</span><span style="font-family: 宋体;">难</span><span style="font-family: 宋体;">。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">现</span><span style="font-family: 宋体;">在在多</span><span style="font-family: 宋体;">线</span><span style="font-family: 宋体;">程的</span><span style="font-family: 宋体;">环</span><span style="font-family: 宋体;">境下，</span><span style="font-family: 宋体;">给</span><span style="font-family: 宋体;">程序</span><span style="font-family: 宋体;">员们</span><span style="font-family: 宋体;">的最后一道午餐便是</span>tcmalloc<span style="font-family: 宋体;">，</span>jemalloc<span style="font-family: 宋体;">这样</span><span style="font-family: 宋体;">的</span>malloc library<span style="font-family: 宋体;">。</span><span style="font-family: 宋体;">对</span><span style="font-family: 宋体;">于使用多</span><span style="font-family: 宋体;">线</span><span style="font-family: 宋体;">程的程序而言，性能</span><span style="font-family: 宋体;">会</span><span style="font-family: 宋体;">提高</span><span style="font-family: 宋体;">数</span><span style="font-family: 宋体;">十</span>%<span style="font-family: 宋体;">。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">共享一下我本人的</span><span style="font-family: 宋体;">经验</span><span style="font-family: 宋体;">。我本人在</span>kth<span style="font-family: 宋体;">技</span><span style="font-family: 宋体;">术研</span><span style="font-family: 宋体;">究所分布式技</span><span style="font-family: 宋体;">术</span>lab<span style="font-family: 宋体;">中承担</span>iLock<span style="font-family: 宋体;">（分布式同步工具，</span><span style="font-family: 宋体;">请参</span><span style="font-family: 宋体;">考</span>google<span style="font-family: 宋体;">的</span>chubby<span style="font-family: 宋体;">）。在</span>iLock<span style="font-family: 宋体;">中用了</span>google<span style="font-family: 宋体;">的</span>tcmalloc<span style="font-family: 宋体;">的</span><span style="font-family: 宋体;">结</span><span style="font-family: 宋体;">果，性能提升了</span>18~22%<span style="font-family: 宋体;">。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">最大的</span><span style="font-family: 宋体;">优</span><span style="font-family: 宋体;">点就是</span><span style="font-family: 宋体;">你</span><span style="font-family: 宋体;">不需要做任何</span><span style="font-family: 宋体;">复杂</span><span style="font-family: 宋体;">的工作便可得到</span><span style="font-family: 宋体;">这样</span><span style="font-family: 宋体;">的效果。不需要代</span><span style="font-family: 宋体;">码</span><span style="font-family: 宋体;">重</span><span style="font-family: 宋体;">编译</span><span style="font-family: 宋体;">。只需在</span><span style="font-family: 宋体;">执</span><span style="font-family: 宋体;">行二</span><span style="font-family: 宋体;">进</span><span style="font-family: 宋体;">制之前，在</span>cmd<span style="font-family: 宋体;">窗口中</span><span style="font-family: 宋体;">输</span><span style="font-family: 宋体;">入</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-size: 10.5pt;">$ LD_PRELOAD=</span><span style="font-size: 10.5pt; font-family: 宋体;">&#8221;</span><span style="font-size: 10.5pt;">tcmalloc</span><span style="font-size: 10.5pt; font-family: 宋体;">所设置的文件夹</span><span style="font-size: 10.5pt;">/libtcmalloc.so</span><span style="font-size: 10.5pt; font-family: 宋体;">&#8221;</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">这样</span><span style="font-family: 宋体;">在之后</span><span style="font-family: 宋体;">执</span><span style="font-family: 宋体;">行的</span><span style="font-family: 宋体;">应</span><span style="font-family: 宋体;">用程序</span><span style="font-family: 宋体;">会</span><span style="font-family: 宋体;">使用</span>tcmalloc<span style="font-family: 宋体;">或</span>jemalloc<span style="font-family: 宋体;">，</span><span style="font-family: 宋体;">从</span><span style="font-family: 宋体;">而代替</span>glibc<span style="font-family: 宋体;">标</span><span style="font-family: 宋体;">准</span>malloc<span style="font-family: 宋体;">（</span>ptmalloc<span style="font-family: 宋体;">）。</span><span style="font-family: 宋体;">这</span><span style="font-family: 宋体;">需</span><span style="font-family: 宋体;">设</span><span style="font-family: 宋体;">置此</span><span style="font-family: 宋体;">处</span><span style="font-family: 宋体;">，我</span><span style="font-family: 宋体;">们</span><span style="font-family: 宋体;">便可得到性能</span>20%<span style="font-family: 宋体;">的提升，</span><span style="font-family: 宋体;">这真</span><span style="font-family: 宋体;">可</span><span style="font-family: 宋体;">谓</span><span style="font-family: 宋体;">是送</span><span style="font-family: 宋体;">给</span><span style="font-family: 宋体;">我</span><span style="font-family: 宋体;">们</span><span style="font-family: 宋体;">的最后的免</span><span style="font-family: 宋体;">费</span><span style="font-family: 宋体;">午餐。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><span style="font-family: 宋体;">如今，在分布式技</span><span style="font-family: 宋体;">术</span>lab<span style="font-family: 宋体;">中使用</span>google<span style="font-family: 宋体;">的</span>tcmalloc<span style="font-family: 宋体;">。原因在于性能上</span><span style="font-family: 宋体;">两</span><span style="font-family: 宋体;">者差不多，但</span>google<span style="font-family: 宋体;">的</span>tcmalloc<span style="font-family: 宋体;">所提供的程序分析工具非常（</span>heap profiler, cpu profiler<span style="font-family: 宋体;">）丰富。所以</span>tcmalloc<span style="font-family: 宋体;">可能更方便一些。</span></p><p style="line-height: 25px; margin: 0px 0px 10px; padding: 0px; color: #8b8b8b; font-family: Arial, Helvetica, simsun, u5b8bu4f53; background-color: #b6d1ee;"><strong><span style="font-family: 宋体;">一定要使用最新的</span>malloc</strong><strong><span style="font-family: 宋体;">么</span></strong><strong><span style="font-family: 宋体;">？一定要的！</span></strong></p></a><img src ="http://www.blogjava.net/xiaomage234/aggbug/403984.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2013-09-12 12:05 <a href="http://www.blogjava.net/xiaomage234/archive/2013/09/12/403984.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Darts: Double-ARray Trie System  [zz]</title><link>http://www.blogjava.net/xiaomage234/archive/2013/08/13/402750.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 13 Aug 2013 08:45:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2013/08/13/402750.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/402750.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2013/08/13/402750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/402750.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/402750.html</trackback:ping><description><![CDATA[<p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">Darts 是用于构建双数组 Double-Array&nbsp;<a href="http://zhjin.spaces.live.com/default.aspx?_c01_BlogPart=blogentry&amp;_c=BlogPart&amp;handle=cns%212FEA4AA78B600980%21692#aoe" style="color: #444444; text-decoration: none;">[Aoe 1989]</a>&nbsp;的简单的 C++ Template Library . 双数组 (Double-Array) 是用于实现 Trie 的一种数据结构, 比其它的类 Trie 实现方式(Hash-Tree, Digital Trie, Patricia Tree, Suffix Array) 速度更快。 原始的 Double-Array 使能够支持动态添加删除 key, 但是 Darts 只支持把排好序的词典文件转换为静态的 Double-Array.</p><p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">Darts 既可以像 Hash 一样作为简单的词典使用，也能非常高效的执行分词词典中必须的 Common Prefix Search 操作。</p><p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">自2003年7月起， 两个开源的日语分词系统&nbsp;<a href="http://mecab.sourceforge.jp/" style="color: #444444; text-decoration: none;">MeCab</a>,&nbsp;<a href="http://chasen.org/" style="color: #444444; text-decoration: none;">ChaSen</a>&nbsp;都使用了 Darts .</p><h2><a>下载</a></h2><ul style="margin-left: 45px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;"><li style="list-style: inherit;"><strong>Darts</strong>&nbsp;是自由软件．遵循&nbsp;<a>LGPL</a>(Lesser GNU General Public License) 和 BSD 协议, 可以修改后重新发布.</li></ul><h3><a>Source</a></h3><ul style="margin-left: 45px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;"><li style="list-style: inherit;">darts-0.32.tar.gz:&nbsp;<a href="/Files/xiaomage234/darts-0.32.rar" style="color: #444444; text-decoration: none;">HTTP</a></li></ul><h2><a>安装</a></h2><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; color: #333333; font-style: italic; line-height: 22px; background-color: #ffffff;">% ./configure <br />% make<br />% make check<br />% make install<br />然后在程序中  include /usr/local/include/darts.h  <br /></pre><h2><a>使用方法</a></h2><p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">Darts 只提供了 darts.h 这个 C++ 模板文件。每次使用的时候 include 该文件即可.<br />使用这样的发布方式是希望通过内联函数实现高效率。</p><h3>类接口</h3><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; color: #333333; font-style: italic; line-height: 22px; background-color: #ffffff;">namespace Darts {<br /><br />   template &lt;class NodeType, class NodeUType class ArrayType, <br />                class ArrayUType, class LengthFunc = Length&lt;NodeType&gt; &gt;<br /><br />   class DobuleArrayImpl<br />   {<br />    public:<br />     typedef ArrayType   result_type;<br />     typedef NodeType    key_type;<br /><br />     DoubleArrayImpl();<br />     ~DoubleArrayImpl();<br /><br />     int    set_array(void *ptr, size_t = 0);<br />     void   *array();<br />     void   clear();<br />     size_t size ();<br />     size_t unit_size ();<br />     size_t nonzero_size ();<br />     size_t total_size ();<br /><br />     int build (size_t        key_size,<br />                key_type      **key,<br />                size_t        *len = 0,<br />                result_type   *val = 0,<br />                int (*pg)(size_t, size_t) = 0);<br /><br />     int open (const char *file,<br />               const char *mode = "rb",<br />               size_t offset = 0,<br />               size_t _size = 0);<br /><br />     int save (const char *file,<br />               const char *mode = "wb",<br />               size_t offset = 0);<br /><br />     result_type exactMatchSearch (const key_type *key,<br />                                   size_t len = 0,<br />                                   size_t node_pos = 0)<br />      <br />     size_t commonPrefixSearch  (const key_type *key,<br />                                 result_type *result,<br />                                 size_t result_size,<br />                                 size_t len = 0,<br />                                 size_t node_pos = 0)<br /><br />     result_type traverse (const key_type *key, <br />                           size_t &amp;node_pos, <br />                           size_t &amp;key_pos, <br />                           size_t len = 0)<br />   };<br /><br />   typedef Darts::DoubleArrayImpl&lt;char, unsigned char,<br />              int, unsigned int&gt; DoubleArray;<br />};<br /></pre><h3>模板参数说明</h3><p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">&nbsp;</p><table border="1" style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; font-family: verdana, sans-serif; background-color: #ffffff;"><tbody><tr><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">NodeType</td><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">Trie 节点类型， 对普通的 C 字符串检索， 设置为 char 型即可.</td></tr><tr><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">NodeUType</td><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">Trie 节点转为无符号整数的类型, 对普通的 C 字符串检索， 设置为 unsigned char 型即可.</td></tr><tr><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">ArrayType</td><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">Double-Array 的 Base 元素使用的类型, 通常设置为有符号 32bit 整数</td></tr><tr><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">ArrayUType</td><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">Double-Array 的 Check 元素使用的类型, 通常设置为无符号 32bit 整数</td></tr><tr><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">LengthFunc</td><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">使用 NodeType 数组的时候，使用该函数对象获取数组的大小, 在该函数对象中对 operator() 进行重载.&nbsp;<br />NodeType 是 char 的时候， 缺省使用 strlen 函数， 否则以 0 作为数组结束标志计算数组的大小 .</td></tr></tbody></table><h3>typedef 说明</h3><p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">模板参数类型的别名. 在外部需要使用这些类型的时候使用 .</p><table border="1" style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; font-family: verdana, sans-serif; background-color: #ffffff;"><tbody><tr><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">key_type</td><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">待检索的 key 的单个元素的类型. 等同于 NodeType.</td></tr><tr><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">result_type</td><td style="border-style: solid; border-color: #c0c0c0; border-collapse: collapse; padding: 3px; word-break: normal !important;">单个结果的类型. 等同于 ArrayType .</td></tr></tbody></table><h3>方法说明</h3><dl style="color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;"><dt><code><span style="color: #dc143c;">int Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">build</span>(size_t size, const key_type **str, const size_t *len = 0, const result_type *val = 0, int (*progress_func)(size_t, size_t) = 0)</code></dt><dd>构建 Double Array .<br /><br />size 词典大小 (记录的词条数目),<br />str 指向各词条的指针 (共 size个指针)<br />len 用于记录各个词条的长度的数组(数组大小为 size)<br />val 用于保存各词条对应的 value 的数组 (数组大小为 size)<br />progress_func 构建进度函数.<br /><br />str 的各个元素必须按照字典序排好序.<br />另外 val 数组中的元素不能有负值.<br />len, val, progress_func 可以省略,<br />省略的时候， len 使用 LengthFunc 计算,<br />val 的各元素的值为从 0 开始的计数值。&nbsp;<br /><br /><br />构建成功，返回 0； 失败的时候返回值为负.<br />进度函数 progress_func 有两个参数.<br />第一个 size_t 型参数表示目前已经构建的词条数&nbsp;<br />第二个 size_t 型参数表示所有的词条数&nbsp;<br /><br /></dd><dt><code><span style="color: #dc143c;">result_type Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">exactMatchSearch</span>(const key_type *key, size_t len = 0, size_t node_pos = 0)</code></dt><dd>进行精确匹配(exact match) 检索, 判断给定字符串是否为词典中的词条.<br /><br />key 待检索字符串,<br />len 字符串长度,<br />node_pos 指定从 Double-Array 的哪个节点位置开始检索.<br /><br />len, 和 node_pos 都可以省略, 省略的时候， len 缺省使用 LengthFunc 计算,<br />node_pos 缺省为 root 节点.<br /><br />检索成功时， 返回 key 对应的 value 值, 失败则返回 -1.&nbsp;<br /><br /></dd><dt><code><span style="color: #dc143c;">size_t Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">commonPrefixSearch</span>&nbsp;(const key_type *key, result_type *result, size_t result_size, size_t len = 0, size_t node_pos = 0)</code></dt><dd>执行 common prefix search. 检索给定字符串的哪些的前缀是词典中的词条<br /><br />key 待检索字符串,<br />result 用于保存多个命中结果的数组,<br />result_size 数组 result 大小,<br />len 待检索字符串长度,<br />node_pos 指定从 Double-Array 的哪个节点位置开始检索.<br /><br />len, 和 node_pos 都可以省略, 省略的时候， len 缺省使用 LengthFunc 计算,<br />node_pos 缺省为 root 节点.<br /><br />函 数返回命中的词条个数. 对于每个命中的词条， 词条对应的 value 值存依次放在 result 数组中. 如果命中的词条个数超过 result_size 的大小， 则 result 数组中只保存 result_size 个结果。函数的返回值为实际的命中词条个数， 可能超过 result_size 的大小。&nbsp;<br /><br /></dd><dt><code><span style="color: #dc143c;">result_t Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">traverse</span>&nbsp;(const key_type *key, size_t &amp;node_pos, size_t &amp;key_pos, size_t len = 0)</code></dt><dd>traverse Trie， 检索当前字符串并记录检索后到达的位置&nbsp;<br /><br />key 待检索字符串,<br />node_pos 指定从 Double-Array 的哪个节点位置开始检索.<br />key_pos 从待检索字符串的哪个位置开始检索<br />len 待检索字符串长度,<br /><br />该函数和 exactMatchSearch 很相似. traverse 过程是按照检索串 key 在 TRIE 的节点中进行转移.<br />但是函数执行后, 可以获取到最后到达的 Trie 节点位置，最后到达的字符串位置 . 这和 exactMatchSearch 是有区别的.&nbsp;<br /><br />node_pos 通常指定为 root 位置 (0) . 函数调用后， node_pos 的值记录最后到达的 DoubleArray 节点位置。&nbsp;<br />key_pos 通常指定为 0. 函数调用后， key_pos 保存最后到达的字符串 key 中的位置。&nbsp;<br /><br />检索失败的时候， 返回 -1 或者 -2 .<br />-1 表示再叶子节点失败, -2 表示在中间节点失败,.<br />检索成功的时候， 返回 key 对应的 value.&nbsp;<br /><br /></dd><dt><code><span style="color: #dc143c;">int Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">save</span>(const char *file, const char *mode = "wb", size_t offset = 0)</code></dt><dd>把 Double-Array 保存为文件.<br /><br />file 保存文件名,<br />mode 文件打开模式&nbsp;<br />offset 保存的文件位置偏移量, 预留将来使用， 目前没有实现 .<br /><br />成功返回 0 ， 失败返回 -1&nbsp;<br /><br /></dd><dt><code><span style="color: #dc143c;">int Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">open</span>&nbsp;(const char *file, const char *mode = "rb", size_t offset = 0, size_t size = 0)</code></dt><dd>读入 Double-Array 文件.<br /><br />file 读取文件名,<br />mode 文件打开模式&nbsp;<br />offset 读取的文件位置偏移量&nbsp;<br /><br />size 为 0 的时候, size 使用文件的大小 .<br /><br />成功返回 0 ， 失败返回 -1&nbsp;<br /><br /></dd><dt><code><span style="color: #dc143c;">size_t Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">size</span>()</code></dt><dd>返回 Double-Array 大小.&nbsp;<br /><br /></dd><dt><code><span style="color: #dc143c;">size_t Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">unit_size</span>()</code></dt><dd>Double-Array 一个元素的大小(byte).<br /><br />size() * unit_size() 是, 存放 Double-Array 所需要的内存(byte) 大小.&nbsp;<br /><br /></dd><dt><code><span style="color: #dc143c;">size_t Darts::DoubleArrayImpl</span>::<span style="color: #00008b;">nonzero_size</span>()</code></dt><dd>Double-Array 的所有元素中， 被使用的元素的数目, .<br />nonezero_size()/size() 用于计算压缩率.&nbsp;<br /><br /></dd></dl><h2>例子程序</h2><p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">从静态词典构建双数组 Double-Array.</p><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; color: #333333; font-style: italic; line-height: 22px; background-color: #ffffff;">#include &lt;iostream&gt;<br />#include &lt;darts.h&gt;<br /><br />int main (int argc, char **argv)<br />{<br />  using namespace std;<br /><br />  Darts::DoubleArray::key_type   *str[] = { "ALGOL", "ANSI", "ARCO",  "ARPA", "ARPANET", "ASCII" }; // same as char*<br />  Darts::DobuleArray::result_type val[] = { 1, 2, 3, 4, 5, 6 }; // same as int <br /><br />  Darts::DoubleArray da;<br />  da.build (6, str, 0, val); <br /><br />  cout &lt;&lt; da.exactMatchSearch("ALGOL") &lt;&lt; endl;<br />  cout &lt;&lt; da.exactMatchSearch("ANSI") &lt;&lt; endl;<br />  cout &lt;&lt; da.exactMatchSearch("ARCO") &lt;&lt; endl;;<br />  cout &lt;&lt; da.exactMatchSearch("ARPA") &lt;&lt; endl;;<br />  cout &lt;&lt; da.exactMatchSearch("ARPANET") &lt;&lt; endl;;<br />  cout &lt;&lt; da.exactMatchSearch("ASCII") &lt;&lt; endl;;<br />  cout &lt;&lt; da.exactMatchSearch("APPARE") &lt;&lt; endl;<br /><br />  da.save("some_file");<br />}<br /><br />执行结果<br />1<br />2<br />3<br />4<br />5<br />6<br />-1<br /></pre><p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">从标准输入读取字符串, 对 Double-Array 执行 Common Prefix Search</p><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; color: #333333; font-style: italic; line-height: 22px; background-color: #ffffff;">#include &lt;iostream&gt;<br />#include &lt;string&gt;<br />#include &lt;algorithm&gt;<br />#include &lt;darts.h&gt;<br /><br />int main (int argc, char **argv)<br />{<br />  using namespace std;<br /><br />  Darts::DoubleArray da;<br />  if (da.open("some_file") == -1) return -1;<br /><br />  Darts::DoubleArray::result_type  r [1024];<br />  Darts::DoubleArray::key_type     buf [1024];<br /><br />  while (cin.getline (buf, 1024)) {<br />    size_t result = da.commonPrefixSearch(buf, r, 1024);<br />    if (result == 0) {<br />       cout &lt;&lt; buf &lt;&lt; ": not found" &lt;&lt; endl;<br />    } else {<br />       cout &lt;&lt; buf &lt;&lt; ": found, num=" &lt;&lt; result &lt;&lt; " ";<br />       copy (r, r + result, ostream_iterator&lt;Darts::DoubleArray::result_type&gt;(cout, " "));<br />       cout &lt;&lt; endl;<br />    }<br />  }<br /><br />  return 0;<br />}<br /></pre><h2>付属程序说明</h2><h3>mkdarts</h3><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; color: #333333; font-style: italic; line-height: 22px; background-color: #ffffff;">% ./mkdarts DictionaryFile DoubleArrayFile <br /></pre><span style="color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">把排序好的词典 DictionaryFile 转换为 DoubleArrayFile</span><h3>darts</h3><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; color: #333333; font-style: italic; line-height: 22px; background-color: #ffffff;">% ./darts DoubleArrayFile <br /></pre><p style="margin-top: 10px; margin-bottom: 10px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;">使用 DoubleArrayFile 做 common prefix search .</p><h3>使用例子</h3><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; color: #333333; font-style: italic; line-height: 22px; background-color: #ffffff;">% cd tests<br />% head -10 linux.words<br />ALGOL<br />ANSI<br />ARCO<br />ARPA<br />ARPANET<br />ASCII<br /> .. <br /><br />% ../mkdarts linux.words dar<br />Making Double Array: 100% |*******************************************|<br />Done!, Compression Ratio: 94.6903 %<br /><br />% ../darts dar<br />Linux<br />Linux: found, num=2 3697 3713<br />Windows<br />Windows: not found<br />LaTeX<br />LaTeX: found, num=1 3529<br /></pre><h2>参考文献, 链接</h2><ul style="margin-left: 45px; color: #333333; font-family: verdana, sans-serif; font-style: italic; line-height: 22px; background-color: #ffffff;"><li style="list-style: inherit;"><a><strong>[Aoe1989]</strong>&nbsp;Aoe, J.&nbsp;<cite>An Efficient Digital Search Algorithm by Using a Double-Array Structure.</cite>&nbsp;<strong>IEEE Transactions on Software Engineering.</strong>&nbsp;Vol. 15, 9 (Sep 1989). pp. 1066-1077.</a></li><li style="list-style: inherit;"><strong>[Datrie]</strong>&nbsp;Theppitak Karoonboonyanan&nbsp;<cite>An Implementation of Double-Array Trie</cite><a href="http://www.links.nectec.or.th/~thep/datrie/" style="color: #444444; text-decoration: none;">http://www.links.nectec.or.th/~thep/datrie/</a></li><li style="list-style: inherit;"><strong>[単語と辞書]</strong>&nbsp;松本裕治 ほか.&nbsp;<cite>単語と辞書</cite>&nbsp;<strong>岩波講座言語の科学</strong>&nbsp;Vol.3 pp.79-81</li></ul><img src ="http://www.blogjava.net/xiaomage234/aggbug/402750.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2013-08-13 16:45 <a href="http://www.blogjava.net/xiaomage234/archive/2013/08/13/402750.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>栈和堆的区别【总结】</title><link>http://www.blogjava.net/xiaomage234/archive/2013/05/20/399513.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 20 May 2013 08:40:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2013/05/20/399513.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/399513.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2013/05/20/399513.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/399513.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/399513.html</trackback:ping><description><![CDATA[<h2><br /></h2><div style="font-family: Arial; background-color: #ffffff;"><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 19px; word-break: break-all; font-family: arial; font-size: 12px;"><strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;">1.1内存分配方面</span></strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;">：</span></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>堆</strong>：一般由程序员分配释放，若程序员不释放，程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事，分配方式是类似于链表。可能用到的关键字如下：new、malloc、delete、free等等。</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>栈</strong>：由编译器(Compiler)自动分配释放，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈。</p><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 19px; word-break: break-all; font-family: arial; font-size: 12px;"><strong>1.2</strong><strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;">申请方式方面：</span></strong></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>堆</strong>：需要程序员自己申请，并指明大小。在c中malloc函数如p1 = (char *)malloc(10)；在C++中用new运算符，但是注意p1、p2本身是在栈中的。因为他们还是可以认为是局部变量。</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>栈</strong>：由系统自动分配。 例如，声明在函数中一个局部变量&nbsp;int b；系统自动在栈中为b开辟空间。</p><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 19px; word-break: break-all; font-family: arial; font-size: 12px;"><strong>1.3</strong><strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;">系统响应方面：</span></strong></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>堆</strong>：操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样代码中的delete语句才能正确的释放本内存空间。另外由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>栈</strong>：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。</p><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 19px; word-break: break-all; font-family: arial; font-size: 12px;"><strong>1.4</strong><strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;">大小限制方面：</span></strong></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>堆</strong>：是向高地址扩展的数据结构，是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见，堆获得的空间比较灵活，也比较大。</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>栈</strong>：在Windows下,&nbsp;栈是向低地址扩展的数据结构，是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在WINDOWS下，栈的大小是固定的（是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间较小。</p><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 19px; word-break: break-all; font-family: arial; font-size: 12px;"><strong>1.5</strong><strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;">效率方面：</span></strong></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>堆</strong>：是由new分配的内存，一般速度比较慢，而且容易产生内存碎片，不过用起来最方便，另外，在WINDOWS下，最好的方式是用VirtualAlloc分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活。</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>栈</strong>：由系统自动分配，速度较快。但程序员是无法控制的。</p><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 19px; word-break: break-all; font-family: arial; font-size: 12px;"><strong>1.6</strong><strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;">存放内容方面：</span></strong></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>堆</strong>：一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>栈</strong>：在函数调用时第一个进栈的是<strong>主函数中后的下一条指令</strong>（函数调用语句的下一条可执行语句）的地址然后是函数的各个<strong>参数</strong>，在大多数的C编译器中，参数是由右往左入栈，然后是函数中的<strong>局部变量</strong>。&nbsp;注意:&nbsp;<strong>静态变量是不入栈的</strong>。当本次函数调用结束后，局部变量先出栈，然后是参数，最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，程序由该点继续运行。</p><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 19px; word-break: break-all; font-family: arial; font-size: 12px;"><strong>1.7</strong><strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;">存取效率方面：</span></strong></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>堆</strong>：char *s1 = "Hellow Word"；是在编译时就确定的；</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>栈</strong>：char s1[] = "Hellow Word"；&nbsp;是在运行时赋值的；用数组比用指针速度要快一些，因为指针在底层汇编中需要用edx寄存器中转一下，而数组在栈上直接读取。</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong><span style="line-height: 22px; color: #4b4b4b; font-size: 10.5pt;"><br />小结</span></strong><strong></strong></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;">1、静态变量不入栈。&nbsp;<br />2、栈由编译器自动分配和释放。栈中存放局部变量和参数，函数调用结束后，局部变量先出栈，然后是参数。&nbsp;<br />3、数组比用指针速度要快一些，因为指针在底层汇编中需要用edx寄存器中转一下，而数组在栈上直接读取。&nbsp;<br />4、堆是由程序员通过new、malloc、free、delete等指令进行分配和释放。如果程序员没有进行释放，程序结束时可能有OS回收。&nbsp;<br />5、堆是由new分配的内存，速度较慢；栈是由系统自动分配，速度较快。&nbsp;<br />6、比如存放在栈里面的数组，是在运行时赋值。而存在堆里面的指针数据，是在编译时就确定的。</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"></p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;">附：</p><p style=" padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"></p><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><strong>一. 在c中分为这几个存储区</strong></p><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;">1.栈 - 由编译器自动分配释放<br />2.堆 - 一般由程序员分配释放，若程序员不释放，程序结束时可能由OS回收<br />3.全局区（静态区），全局变量和静态变量的存储是放在一块的，初始化的全局变量和静态变量在一块区域，未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放<br />4.另外还有一个专门放常量的地方。- 程序结束释放<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />在函数体中定义的变量通常是在栈上，用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量，加了static修饰符后不管在哪里都存放在全局区（静态区）,在所有函数体外定义的static变量表示在该文件中有效，不能extern到别的文件用，在函数体内定义的static表示只在该函数体内有效。另外，函数中的"adgfdf"这样的字符串存放在常量区。比如：<br /></p><div style="word-wrap: break-word; border-left-color: #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; width: 750px; font-size: 13px; word-break: break-all; color: #9a9a9a; font-family: arial; line-height: 18px;"><img src="http://hiphotos.baidu.com/youngky2008/pic/item/c92aa0caf50ac74f90457e79.jpg" alt="" style="border: none;" /><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;a&nbsp;</span><span style="color: #000000;">=</span>&nbsp;<span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">全局初始化区</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/c22b9f07aab2edc4d63f7c79.jpg" alt="" style="border: none;" /></span><span style="color: #0000ff;">char</span>&nbsp;<span style="color: #000000;">*</span><span style="color: #000000;">p1;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">全局未初始化区</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/9393b58ceec8bcd8a6c27279.jpg" alt="" style="border: none;" /></span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main()<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/d5e9e49098354652d01b7079.jpg" alt="" style="border: none;" /></span><span style="color: #000000;">{<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/1a03ce6a9f336a9683cb4a79.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">int</span>&nbsp;b;&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">栈</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/dc1232dec53c0297b4fd4879.jpg" alt="" style="border: none;" /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">char</span>&nbsp;s[]&nbsp;=&nbsp;"abc";&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">栈</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/b8c4abf8f639f8b1fe037f79.jpg" alt="" style="border: none;" /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">char</span>&nbsp;*p2;&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">栈</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/801d5adff00a692ad2164e79.jpg" alt="" style="border: none;" /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">char</span>&nbsp;*p3&nbsp;=&nbsp;"123456";&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">123456{post.content}在常量区，p3在栈上</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/b72b316298e13268a9184c79.jpg" alt="" style="border: none;" /></span>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">static</span>&nbsp;<span style="color: #0000ff;">int</span>&nbsp;c&nbsp;=&nbsp;0;&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">全局（静态）初始化区</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/d1c06a20e1efac67d6074279.jpg" alt="" style="border: none;" /></span>&nbsp;&nbsp;&nbsp;&nbsp; p1&nbsp;=&nbsp;(<span style="color: #0000ff;">char</span>&nbsp;*)malloc(10);&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">分配得来得10字节的区域在堆区</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/aacef42f98f00eb6e4cd4079.jpg" alt="" style="border: none;" /></span>&nbsp;&nbsp;&nbsp;&nbsp; p2&nbsp;=&nbsp;(<span style="color: #0000ff;">char</span>&nbsp;*)malloc(20);&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">分配得来得20字节的区域在堆区</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/d5d156fea83a40c59c514679.jpg" alt="" style="border: none;" /></span>&nbsp;&nbsp;&nbsp;&nbsp; strcpy(p1,&nbsp;"123456");<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/fdd5f141ef7fef7385947379.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #008000;">//</span><span style="color: #008000;">123456{post.content}放在常量区，编译器可能会将它与p3所指向的"123456"优化成一块</span><span style="color: #008000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/e71b188dcea634ff0d244479.jpg" alt="" style="border: none;" /></span>}</span><span style="color: #000000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/9f876cb75dd379ebdb335a79.jpg" alt="" style="border: none;" /></span></div><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;"><br /><strong><span style="color: red;"><strong>二.在C++中，内存分成5个区，他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区<br /></strong></span>1.栈，</strong>就是那些由编译器在需要的时候分配，在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。<br /><strong>2.堆，</strong>就是那些由new分配的内存块，他们的释放编译器不去管，由我们的应用程序去控制，一般一个new就要对应一个delete。如果程序员没有释放掉，那么在程序结束后，操作系统会自动回收。<br /><strong>3.自由存储区，</strong>就是那些由malloc等分配的内存块，他和堆是十分相似的，不过它是用free来结束自己的生命的。<br /><strong>4.全局/静态存储区</strong>，全局变量和静态变量被分配到同一块内存中，在以前的C语言中，全局变量又分为初始化的和未初始化的，在C++里面没有这个区分了，他们共同占用同一块内存区。<br /><strong>5.常量存储区，</strong>这是一块比较特殊的存储区，他们里面存放的是常量，不允许修改（当然，你要通过非正当手段也可以修改）<br /><br /><strong>三. 谈谈堆与栈的关系与区别<br /></strong>具体地说，现代计算机(串行执行机制)，都直接在代码底层支持栈的数据结构。这体现在，有专门的寄存器指向栈所在的地址，有专门的机器指令完成数据入栈出栈的操作。这种机制的特点是效率高，支持的数据有限，一般是整数，指针，浮点数等系统直接支持的数据类型，并不直接支持其他的数据结构。因为栈的这种特点，对栈的使用在程序中是非常频繁的。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈，然后跳转至子程序地址的操作，而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。C/C++中的自动变量是直接利用栈的例子，这也就是为什么当函数返回时，该函数的<strong>自动变量自动失效</strong>的原因。&nbsp;<br /><br />和栈不同，堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的，而是由函数库提供的。基本的malloc/realloc/free 函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存空间时，这套函数首先试图从内部堆中寻找可用的内存空间，如果没有可以使用的内存空间，则试图利用系统调用来动态增加程序数据段的内存大小，新分配得到的空间首先被组织进内部堆中去，然后再以适当的形式返回给调用者。当程序释放分配的内存空间时，这片内存空间被返回内部堆结构中，可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间)，以更适合下一次内存分配申请。这套复杂的分配机制实际上相当于一个<strong>内存分配的缓冲池</strong>(Cache)，使用这套机制有如下若干原因：<br />1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配)；这样的话对于大量的小内存分类来说会造成浪费。<br />2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。<br />3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。<br /><br /><strong>堆和栈的对比<br /></strong>从以上知识可知，栈是系统提供的功能，特点是快速高效，缺点是有限制，数据不灵活；而栈是函数库提供的功能，特点是灵活方便，数据适应面广泛，但是效率有一定降低。栈是系统数据结构，对于进程/线程是唯一的；堆是函数库内部数据结构，不一定唯一。不同堆分配的内存无法互相操作。栈空间分静态分配和动态分配两种。静态分配是编译器完成的，比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配无需释放(是自动的)，也就没有释放函数。为可移植的程序起见，栈的动态分配操作是不被鼓励的！堆空间的分配总是动态的，虽然程序结束时所有的数据空间都会被释放回系统，但是精确的申请内存/ 释放内存匹配是良好程序的基本要素。<br /><br />&nbsp;&nbsp;&nbsp; 1.碎片问题：对于堆来讲，<strong>频繁的new/delete势必会造成内存空间的不连续，从而造成大量的碎片，使程序效率降低</strong>。对于栈来讲，则不会存在这个问题，因为栈是先进后出的队列，他们是如此的一一对应，以至于永远都不可能有一个内存块从栈中间弹出，在他弹出之前，在他上面的后进的栈内容已经被弹出，详细的可以&gt;参考数据结构，这里我们就不再一一讨论了。<br />&nbsp;&nbsp;&nbsp; 2.生长方向：对于堆来讲，生长方向是向上的，也就是向着内存地址增加的方向；对于栈来讲，它的生长方向是向下的，是向着内存地址减小的方向增长。<br />&nbsp;&nbsp;&nbsp; 3.分配方式：堆都是动态分配的，没有静态分配的堆。栈有2种分配方式：静态分配和动态分配。静态分配是编译器完成的，比如局部变量的分配。动态分配由alloca函数进行分配，但是栈的动态分配和堆是不同的，他的动态分配是由编译器进行释放，无需我们手工实现。<br />&nbsp;&nbsp;&nbsp; 4.分配效率：栈是机器系统提供的数据结构，计算机会在底层对栈提供支持：分配专门的寄存器存放栈的地址，压栈出栈都有专门的指令执行，这就决定了<strong>栈的效率比较高</strong>。堆则是C/C++函数库提供的，它的机制是很复杂的，例如为了分配一块内存，库函数会按照一定的算法（具体的算法可以参考数据结构/操作系统）在堆内存中搜索可用的足够大小的空间，如果没有足够大小的空间（可能是由于内存碎片太多），就有可能调用系统功能去增加程序数据段的内存空间，这样就有机会分到足够大小的内存，然后进行返回。显然，堆的效率比栈要低得多。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;<strong>明确区分堆与栈</strong>:<br />&nbsp;&nbsp;&nbsp; 在bbs上，堆与栈的区分问题，似乎是一个永恒的话题，由此可见，初学者对此往往是混淆不清的，所以我决定拿他第一个开刀。<br />&nbsp;&nbsp;&nbsp; 首先，我们举一个例子：<br /></p><div style="word-wrap: break-word; border-left-color: #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; width: 750px; font-size: 13px; word-break: break-all; color: #9a9a9a; font-family: arial; line-height: 18px;"><img src="http://hiphotos.baidu.com/youngky2008/pic/item/0ef221a38dc4f3d9d3435879.jpg" alt="" style="border: none;" /><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;f()<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/d8e5ab9187b49607d3135e79.jpg" alt="" style="border: none;" /></span><span style="color: #000000;">{&nbsp;<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/83d439abf8f870b7f9ed5079.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">int</span>*&nbsp;p=<span style="color: #0000ff;">new</span>&nbsp;<span style="color: #0000ff;">int</span>[5];<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/a5d928ffa51a9028014f5679.jpg" alt="" style="border: none;" />}</span><span style="color: #000000;"><br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/02991bf495c1948f7b31aa7a.jpg" alt="" style="border: none;" /></span></div><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;">这条短短的一句话就包含了堆与栈，看到new，我们首先就应该想到，我们分配了一块堆内存，那么指针p呢？他分配的是一块栈内存，所以这句话的意思就是：<strong>在栈内存中存放了一个指向一块堆内存的指针p</strong>。在程序会先确定在堆中分配内存的大小，然后调用operator new分配内存，然后返回这块内存的首地址，放入栈中，他在VC6下的汇编代码如下：<br />&nbsp;&nbsp;&nbsp; 00401028&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 14h<br />&nbsp;&nbsp;&nbsp; 0040102A&nbsp;&nbsp;&nbsp; call&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; operator new (00401060)<br />&nbsp;&nbsp;&nbsp; 0040102F&nbsp;&nbsp;&nbsp; add&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esp,4<br />&nbsp;&nbsp;&nbsp; 00401032&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-8],eax<br />&nbsp;&nbsp;&nbsp; 00401035&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eax,dword ptr [ebp-8]<br />&nbsp;&nbsp;&nbsp; 00401038&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dword ptr [ebp-4],eax<br />&nbsp;&nbsp;&nbsp; 这里，我们为了简单并没有释放内存，那么该怎么去释放呢？是delete p么？澳，错了，应该是<strong>delete []p</strong>，这是为了告诉编译器：我删除的是一个数组，VC6就会根据相应的Cookie信息去进行释放内存的工作。<br />&nbsp;&nbsp;&nbsp; 好了，我们回到我们的主题：堆和栈究竟有什么区别？<br />&nbsp;&nbsp;&nbsp; 主要的区别由以下几点：<br />&nbsp;&nbsp;&nbsp; 1、管理方式不同；<br />&nbsp;&nbsp;&nbsp; 2、空间大小不同；<br />&nbsp;&nbsp;&nbsp; 3、能否产生碎片不同；<br />&nbsp;&nbsp;&nbsp; 4、生长方向不同；<br />&nbsp;&nbsp;&nbsp; 5、分配方式不同；<br />&nbsp;&nbsp;&nbsp; 6、分配效率不同；<br />&nbsp;&nbsp;&nbsp; 管理方式：对于栈来讲，是由编译器自动管理，无需我们手工控制；对于堆来说，释放工作由程序员控制，容易产生memory leak。<br />&nbsp;&nbsp;&nbsp; 空间大小：一般来讲在32位系统下，堆内存可以达到4G的空间，从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲，一般都是有一定的空间大小的，例如，在VC6下面，默认的栈空间大小是1M（好像是，记不清楚了）。当然，我们可以修改：<br />&nbsp;&nbsp;&nbsp; 打开工程，依次操作菜单如下：Project-&gt;Setting-&gt;Link，在Category 中选中Output，然后在Reserve中设定堆栈的最大值和commit。<br />注意：reserve最小值为4Byte；commit是保留在虚拟内存的页文件里面，它设置的较大会使栈开辟较大的值，可能增加内存的开销和启动时间。<br />&nbsp;&nbsp;&nbsp; 堆和栈相比，由于大量new/delete的使用，容易造成大量的内存碎片；由于没有专门的系统支持，效率很低；由于可能引发用户态和核心态的切换，内存的申请，代价变得更加昂贵。所以栈在程序中是应用最广泛的，就算是函数的调用也利用栈去完成，函数调用过程中的参数，返回地址，EBP和局部变量都采用栈的方式存放。所以，我们推荐大家尽量用栈，而不是用堆。<br /><br />另外对存取效率的比较:<br />代码:<br /></p><div style="word-wrap: break-word; border-left-color: #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; width: 750px; font-size: 13px; word-break: break-all; color: #9a9a9a; font-family: arial; line-height: 18px;"><img src="http://hiphotos.baidu.com/youngky2008/pic/item/f6e0ccc71dc63119b119a87a.jpg" alt="" style="border: none;" /><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;s1[]&nbsp;</span><span style="color: #000000;">=</span>&nbsp;<span style="color: #000000;">"</span><span style="color: #000000;">aaaaaaaaaaaaaaa</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/78e76951d5eed088b545ae7a.jpg" alt="" style="border: none;" /></span><span style="color: #0000ff;">char</span>&nbsp;<span style="color: #000000;">*</span><span style="color: #000000;">s2&nbsp;</span><span style="color: #000000;">=</span>&nbsp;<span style="color: #000000;">"</span><span style="color: #000000;">bbbbbbbbbbbbbbbbb</span><span style="color: #000000;">"</span><span style="color: #000000;">;<img src="http://hiphotos.baidu.com/youngky2008/pic/item/b2cf88c0dfb278e58026ac7a.jpg" alt="" style="border: none;" /></span></div><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;">aaaaaaaaaaa是在运行时刻赋值的；<br />而bbbbbbbbbbb是在编译时就确定的；<br />但是，在以后的存取中，<strong>在栈上的数组比指针所指向的字符串(例如堆)快</strong>。<br />比如：<br /></p><div style="word-wrap: break-word; border-left-color: #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; width: 750px; font-size: 13px; word-break: break-all; color: #9a9a9a; font-family: arial; line-height: 18px;"><img src="http://hiphotos.baidu.com/youngky2008/pic/item/b69320ade8d1f4b01c17a27a.jpg" alt="" style="border: none;" /><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main()<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/83f0acf872e0b1cebb01a07a.jpg" alt="" style="border: none;" /></span><span style="color: #000000;">{<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/f7d51089db80025eb11bba7a.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">char</span>&nbsp;a&nbsp;=&nbsp;1;<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/a8a15a16c7ec8b1dfa19b87a.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">char</span>&nbsp;c[]&nbsp;=&nbsp;"1234567890";<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/b2cdd3558eeebd54347abe7a.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">char</span>&nbsp;*p&nbsp;="1234567890";<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/f9cfe51c4e8de1f848edbc7a.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp; a&nbsp;=&nbsp;c[1];<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/37acb9b0301a153f89d4b27a.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp; a&nbsp;=&nbsp;p[1];<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/4b3b4d77f72388508501b07a.jpg" alt="" style="border: none;" />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff;">return</span>;<br /><img src="http://hiphotos.baidu.com/youngky2008/pic/item/8a02d018f9f69ac785d6b67a.jpg" alt="" style="border: none;" />}</span></div><p style="margin: 0px 0px 1em; padding: 0px; color: #6d6d6d; line-height: 26px; font-family: arial; font-size: 12px;">对应的汇编代码<br />10: a = c[1];<br />00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]<br />0040106A 88 4D FC mov byte ptr [ebp-4],cl<br />11: a = p[1];<br />0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]<br />00401070 8A 42 01 mov al,byte ptr [edx+1]<br />00401073 88 45 FC mov byte ptr [ebp-4],al<br />第一种在读取时直接就把字符串中的元素读到寄存器cl中，而第二种则要先把指针值读到edx中，在根据edx读取字符，显然慢了.<br />&nbsp;&nbsp;&nbsp; 无论是堆还是栈，都要防止越界现象的发生（除非你是故意使其越界），因为越界的结果要么是程序崩溃，要么是摧毁程序的堆、栈结构，产生以想不到的结果,就算是在你的程序运行过程中，没有发生上面的问题，你还是要小心，说不定什么时候就崩掉,编写稳定安全的代码才是最重要的</p></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/399513.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2013-05-20 16:40 <a href="http://www.blogjava.net/xiaomage234/archive/2013/05/20/399513.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下GDB调试 </title><link>http://www.blogjava.net/xiaomage234/archive/2012/12/12/392852.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Wed, 12 Dec 2012 04:24:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2012/12/12/392852.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/392852.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2012/12/12/392852.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/392852.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/392852.html</trackback:ping><description><![CDATA[<div style="color: #444444; font-family: simsun; background-color: #f3f8fb; "><h2>Linux下GDB调试</h2></div><div style="color: #444444; font-family: simsun; background-color: #f3f8fb; "><table style="margin: 0px; padding: 0px; "><tbody><tr><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; "><br /></td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; "><br /></td></tr></tbody></table></div><div style="color: #444444; font-family: simsun; background-color: #f3f8fb; "><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">本文写给主要工作在Windows操作系统下而又需要开发一些跨平台软件的程序员朋友，以及程序爱好者。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">GDB中的命令固然很多，但我们只需掌握其中十个左右的命令，就大致可以完成日常的基本的程序调试工作。</p><table border="1" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td bgcolor="#D4D0C8" width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; "><strong>命令</strong></td><td bgcolor="#D4D0C8" width="500" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; "><strong>解释</strong></td><td bgcolor="#D4D0C8" nowrap="nowrap" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; "><strong>示例</strong></td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">file &lt;文件名&gt;</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">加载被调试的可执行程序文件。<br />因为一般都在被调试程序所在目录下执行GDB，因而文本名不需要带路径。</td><td width="220" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) file gdb-sample</td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">r</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">Run的简写，运行被调试的程序。<br />如果此前没有下过断点，则执行完整个程序；如果有断点，则程序暂停在第一个可用断点处。</td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) r</td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">c</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">Continue的简写，继续执行被调试程序，直至下一个断点或程序结束。</td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) c</td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">b &lt;行号&gt;<br />b &lt;函数名称&gt;<br />b *&lt;函数名称&gt;<br />b *&lt;代码地址&gt;<p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">d [编号]</p></td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">b: Breakpoint的简写，设置断点。两可以使用&#8220;行号&#8221;&#8220;函数名称&#8221;&#8220;执行地址&#8221;等方式指定断点位置。<br />其中在函数名称前面加&#8220;*&#8221;符号表示将断点设置在&#8220;由编译器生成的prolog代码处&#8221;。如果不了解汇编，可以不予理会此用法。<p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">d: Delete breakpoint的简写，删除指定编号的某个断点，或删除所有断点。断点编号从1开始递增。</p></td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) b 8<br />(gdb) b main<br />(gdb) b *main<br />(gdb) b *0x804835c<p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">(gdb) d</p></td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">s, n</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">s: 执行一行源程序代码，如果此行代码中有函数调用，则进入该函数；<br />n: 执行一行源程序代码，此行代码中的函数调用也一并执行。<p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">s 相当于其它调试器中的&#8220;Step Into (单步跟踪进入)&#8221;；<br />n 相当于其它调试器中的&#8220;Step Over (单步跟踪)&#8221;。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">这两个命令必须在有源代码调试信息的情况下才可以使用（GCC编译时使用&#8220;-g&#8221;参数）。</p></td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) s<br />(gdb) n</td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">si, ni</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">si命令类似于s命令，ni命令类似于n命令。所不同的是，这两个命令（si/ni）所针对的是汇编指令，而s/n针对的是源代码。</td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) si<br />(gdb) ni</td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">p &lt;变量名称&gt;</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">Print的简写，显示指定变量（临时变量或全局变量）的值。</td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) p i<br />(gdb) p nGlobalVar</td></tr><tr><td nowrap="nowrap" width="155" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">display ...<p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">undisplay &lt;编号&gt;</p></td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">display，设置程序中断后欲显示的数据及其格式。<br />例如，如果希望每次程序中断后可以看到即将被执行的下一条汇编指令，可以使用命令<br />&#8220;display /i $pc&#8221;<br />其中 $pc 代表当前汇编指令，/i 表示以十六进行显示。当需要关心汇编代码时，此命令相当有用。<p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">undispaly，取消先前的display设置，编号从1开始递增。</p></td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) display /i $pc<p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">(gdb) undisplay 1</p></td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">i</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">Info的简写，用于显示各类信息，详情请查阅&#8220;help i&#8221;。</td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) i r</td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">q</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">Quit的简写，退出GDB调试环境。</td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) q</td></tr><tr><td width="138" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">help [命令名称]</td><td style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">GDB帮助命令，提供对GDB名种命令的解释说明。<br />如果指定了&#8220;命令名称&#8221;参数，则显示该命令的详细说明；如果没有指定参数，则分类显示所有GDB命令，供用户进一步浏览和查询。</td><td width="180" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) help display</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; "><strong>/add************************************/</strong></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; "><strong>j命令&nbsp;<wbr><wbr><wbr></strong>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;回跳</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; "><strong>ret&nbsp;<wbr><wbr><wbr></strong>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;设置返回值&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;(例ret 0/ret -1)</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; "><strong>/add************************************/</strong></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">废话不多说，下面开始实践。<strong>gdb-sample.c</strong></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">&nbsp;<wbr><wbr></p><table bgcolor="#EEEEEE" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td align="left" bgcolor="#CCCCCC" nowrap="nowrap" valign="top" width="38" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; "><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br /></p></td><td align="left" nowrap="nowrap" valign="top" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; "><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">#include&nbsp;&lt;&lt;/font&gt;stdio.h&gt;<br /><br />int&nbsp;nGlobalVar&nbsp;=&nbsp;0;<br /><br />int&nbsp;tempFunction(int&nbsp;a,&nbsp;int&nbsp;b)<br />{<br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>printf("tempFunction is called, a = %d, b = %d \n",&nbsp;a,&nbsp;b);<br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>return&nbsp;(a&nbsp;+&nbsp;b);<br />}<br /><br />int&nbsp;main()<br />{<br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>int&nbsp;n;<br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;n&nbsp;=&nbsp;1;<br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;n++;<br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;n--;<br /><br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;nGlobalVar&nbsp;+=&nbsp;100;<br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;nGlobalVar&nbsp;-=&nbsp;12;<br /><br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>printf("n = %d, nGlobalVar = %d \n",&nbsp;n,&nbsp;nGlobalVar);<br /><br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;n&nbsp;=&nbsp;tempFunction(1,&nbsp;2);<br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>printf("n = %d",&nbsp;n);<br /><br />&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>&nbsp;<wbr><wbr><wbr>return&nbsp;0;<br />}</p></td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">&nbsp;<wbr><wbr></p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">gcc gdb-sample.c -o gdb-sample -g</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">使用参数 -g 表示将源代码信息编译到可执行文件中。如果不使用参数 -g，会给后面的GDB调试造成不便。当然，如果我们没有程序的源代码，自然也无从使用 -g 参数，调试/跟踪时也只能是汇编代码级别的调试/跟踪。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">下面&#8220;gdb&#8221;命令启动GDB，将首先显示GDB说明，不管它：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)<br />Copyright 2003 Free Software Foundation, Inc.<br />GDB is free software, covered by the GNU General Public License, and you are<br />welcome to change it and/or distribute copies of it under certain conditions.<br />Type "show copying" to see the conditions.<br />There is absolutely no warranty for GDB. Type "show warranty" for details.<br />This GDB was configured as "i386-redhat-linux-gnu".<br />(gdb)</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">上面最后一行&#8220;(gdb) &#8221;为GDB内部命令引导符，等待用户输入GDB命令。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">下面使用&#8220;file&#8221;命令载入被调试程序 gdb-sample（这里的 gdb-sample 即前面 GCC 编译输出的可执行文件）：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) file gdb-sample<br />Reading symbols from gdb-sample...done.</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">上面最后一行提示已经加载成功。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">下面使用&#8220;r&#8221;命令执行（Run）被调试文件，因为尚未设置任何断点，将直接执行到程序结束：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) r<br />Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample<br />n = 1, nGlobalVar = 88<br />tempFunction is called, a = 1, b = 2<br />n = 3<br />Program exited normally.</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">下面使用&#8220;b&#8221;命令在 main 函数开头设置一个断点（Breakpoint）：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) b main<br />Breakpoint 1 at 0x804835c: file gdb-sample.c, line 19.</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">上面最后一行提示已经成功设置断点，并给出了该断点信息：在源文件 gdb-sample.c 第19行处设置断点；这是本程序的第一个断点（序号为1）；断点处的代码地址为 0x804835c（此值可能仅在本次调试过程中有效）。回过头去看源代码，第19行中的代码为&#8220;n = 1&#8221;，恰好是 main 函数中的第一个可执行语句（前面的&#8220;int n;&#8221;为变量定义语句，并非可执行语句）。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">再次使用&#8220;r&#8221;命令执行（Run）被调试程序：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) r<br />Starting program: /home/liigo/temp/gdb-sample<br /><br />Breakpoint 1, main () at gdb-sample.c:19<br />19 n = 1;</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">程序中断在gdb-sample.c第19行处，即main函数是第一个可执行语句处。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">上面最后一行信息为：下一条将要执行的源代码为&#8220;n = 1;&#8221;，它是源代码文件gdb-sample.c中的第19行。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">下面使用&#8220;s&#8221;命令（Step）执行下一行代码（即第19行&#8220;n = 1;&#8221;）：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) s<br />20 n++;</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">上面的信息表示已经执行完&#8220;n = 1;&#8221;，并显示下一条要执行的代码为第20行的&#8220;n++;&#8221;。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">既然已经执行了&#8220;n = 1;&#8221;，即给变量 n 赋值为 1，那我们用&#8220;p&#8221;命令（Print）看一下变量 n 的值是不是 1 ：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) p n<br />$1 = 1</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">果然是 1。（$1大致是表示这是第一次使用&#8220;p&#8221;命令&#8212;&#8212;再次执行&#8220;p n&#8221;将显示&#8220;$2 = 1&#8221;&#8212;&#8212;此信息应该没有什么用处。）</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">下面我们分别在第26行、tempFunction 函数开头各设置一个断点（分别使用命令&#8220;b 26&#8221;&#8220;b tempFunction&#8221;）：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) b 26<br />Breakpoint 2 at 0x804837b: file gdb-sample.c, line 26.<br />(gdb) b tempFunction<br />Breakpoint 3 at 0x804832e: file gdb-sample.c, line 12.</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">使用&#8220;c&#8221;命令继续（Continue）执行被调试程序，程序将中断在第二 个断点（26行），此时全局变量 nGlobalVar 的值应该是 88；再一次执行&#8220;c&#8221;命令，程序将中断于第三个断点（12行，tempFunction 函数开头处），此时tempFunction 函数的两个参数 a、b 的值应分别是 1 和 2：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) c<br />Continuing.<br /><br />Breakpoint 2, main () at gdb-sample.c:26<br />26 printf("n = %d, nGlobalVar = %d \n", n, nGlobalVar);<br />(gdb) p nGlobalVar<br />$2 = 88<br />(gdb) c<br />Continuing.<br />n = 1, nGlobalVar = 88<br /><br />Breakpoint 3, tempFunction (a=1, b=2) at gdb-sample.c:12<br />12 printf("tempFunction is called, a = %d, b = %d \n", a, b);<br />(gdb) p a<br />$3 = 1<br />(gdb) p b<br />$4 = 2</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">上面反馈的信息一切都在我们预料之中，哈哈~~~</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">再一次执行&#8220;c&#8221;命令（Continue），因为后面再也没有其它断点，程序将一直执行到结束：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) c<br />Continuing.<br />tempFunction is called, a = 1, b = 2<br />n = 3<br />Program exited normally.</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">　</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">有时候需要看到编译器生成的汇编代码，以进行汇编级的调试或跟踪，又该如何操作呢？</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">这就要用到display命令&#8220;display /i $pc&#8221;了（此命令前面已有详细解释）：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) display /i $pc<br />(gdb)</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">此后程序再中断时，就可以显示出汇编代码了：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) r<br />Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample<br /><br />Breakpoint 1, main () at gdb-sample.c:19<br />19 n = 1;<br />1: x/i $pc 0x804835c : movl $0x1,0xfffffffc(�p)</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">看到了汇编代码，&#8220;n = 1;&#8221;对应的汇编代码是&#8220;movl $0x1,0xfffffffc(�p)&#8221;。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">并且以后程序每次中断都将显示下一条汇编指定（&#8220;si&#8221;命令用于执行一条汇编代码&#8212;&#8212;区别于&#8220;s&#8221;执行一行C代码）：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) si<br />20 n++;<br />1: x/i $pc 0x8048363 : lea 0xfffffffc(�p),�x<br />(gdb) si<br />0x08048366 20 n++;<br />1: x/i $pc 0x8048366 : incl (�x)<br />(gdb) si<br />21 n--;<br />1: x/i $pc 0x8048368 : lea 0xfffffffc(�p),�x<br />(gdb) si<br />0x0804836b 21 n--;<br />1: x/i $pc 0x804836b : decl (�x)<br />(gdb) si<br />23 nGlobalVar += 100;<br />1: x/i $pc 0x804836d : addl $0x64,0x80494fc</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">　</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">接下来我们试一下命令&#8220;b *&lt;函数名称&gt;&#8221;。</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">为了更简明，有必要先删除目前所有断点（使用&#8220;d&#8221;命令&#8212;&#8212;Delete breakpoint）：</p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) d<br />Delete all breakpoints? (y or n) y<br />(gdb)</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">当被询问是否删除所有断点时，输入&#8220;y&#8221;并按回车键即可。<br /></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">下面使用命令&#8220;b *main&#8221;在 main 函数的 prolog 代码处设置断点（prolog、epilog，分别表示编译器在每个函数的开头和结尾自行插入的代码）：</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">&nbsp;<wbr><wbr></p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) b *main<br />Breakpoint 4 at 0x804834c: file gdb-sample.c, line 17.<br />(gdb) r<br />The program being debugged has been started already.<br />Start it from the beginning? (y or n) y<br />Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample<br /><br />Breakpoint 4, main () at gdb-sample.c:17<br />17 {<br />1: x/i $pc 0x804834c : push �p<br />(gdb) si<br />0x0804834d 17 {<br />1: x/i $pc 0x804834d : mov %esp,�p<br />(gdb) si<br />0x0804834f in main () at gdb-sample.c:17<br />17 {<br />1: x/i $pc 0x804834f : sub $0x8,%esp<br />(gdb) si<br />0x08048352 17 {<br />1: x/i $pc 0x8048352 : and $0xfffffff0,%esp<br />(gdb) si<br />0x08048355 17 {<br />1: x/i $pc 0x8048355 : mov $0x0,�x<br />(gdb) si<br />0x0804835a 17 {<br />1: x/i $pc 0x804835a : sub �x,%esp<br />(gdb) si<br />19 n = 1;<br />1: x/i $pc 0x804835c : movl $0x1,0xfffffffc(�p)</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">&nbsp;<wbr><wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">此时可以使用&#8220;i r&#8221;命令显示寄存器中的当前值&#8212;&#8212;&#8212;&#8220;i r&#8221;即&#8220;Infomation Register&#8221;：</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">&nbsp;<wbr><wbr></p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) i r<br />eax 0xbffff6a4 -1073744220<br />ecx 0x42015554 1107383636<br />edx 0x40016bc8 1073834952<br />ebx 0x42130a14 1108544020<br />esp 0xbffff6a0 0xbffff6a0<br />ebp 0xbffff6a8 0xbffff6a8<br />esi 0x40015360 1073828704<br />edi 0x80483f0 134513648<br />eip 0x8048366 0x8048366<br />eflags 0x386 902<br />cs 0x23 35<br />ss 0x2b 43<br />ds 0x2b 43<br />es 0x2b 43<br />fs 0x0 0<br />gs 0x33 51</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">&nbsp;<wbr><wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">当然也可以显示任意一个指定的寄存器值：</p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">&nbsp;<wbr><wbr></p><table bgcolor="#000000" border="0" cellpadding="2" cellspacing="0" width="100%" style="margin: 0px; padding: 0px; "><tbody><tr><td width="100%" style="margin: 0px; padding: 0px; font-size: 12px; font-family: Verdana, 宋体, sans-serif; line-height: 18px; ">(gdb) i r eax<br />eax 0xbffff6a4 -1073744220</td></tr></tbody></table><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">&nbsp;<wbr><wbr></p><p style="margin-top: 0px; margin-right: 0px; margin-left: 0px; padding: 0px; border: 0px; list-style: none; word-wrap: normal; word-break: normal; ">最后一个要介绍的命令是&#8220;q&#8221;，退出（Quit）GDB调试环境：</p></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/392852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2012-12-12 12:24 <a href="http://www.blogjava.net/xiaomage234/archive/2012/12/12/392852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How to Detect Memory Leaks Using Valgrind memcheck Tool for C / C++  </title><link>http://www.blogjava.net/xiaomage234/archive/2012/11/05/390792.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 05 Nov 2012 03:22:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2012/11/05/390792.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/390792.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2012/11/05/390792.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/390792.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/390792.html</trackback:ping><description><![CDATA[from：<a href="http://www.thegeekstuff.com/2011/11/valgrind-memcheck/">http://www.thegeekstuff.com/2011/11/valgrind-memcheck/<br /><br /><br /><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">One major aspect of system programming is to handle memory related issues effectively. The more you work close to the system, the more memory related issues you need to face.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Sometimes these issues are very trivial while many times it becomes a nightmare to debug memory related issues. So, as a practice many tools are used for debugging memory related issues.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">In this article, we will discuss the most popular open source memory management framework VALGRIND.<br style="padding: 0px; margin: 0px; " /></p><blockquote style="padding: 0px 0px 0px 0.786em; margin: 0px 0px 1.571em 0.786em; border-left-width: 1px; border-left-color: #dddddd; color: #666666; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; "><p style="padding: 0px; margin: 0px 0px 1.571em; ">From&nbsp;<a href="http://valgrind.org/" style="padding: 0px; margin: 0px; color: #2361a1; ">Valgrind.org</a>&nbsp;:</p><p style="padding: 0px; margin: 0px 0px 1.571em; ">Valgrind is an instrumentation framework for building dynamic analysis tools. It comes with a set of tools each of which performs some kind of debugging, profiling, or similar task that helps you improve your programs. Valgrind&#8217;s architecture is modular, so new tools can be created easily and without disturbing the existing structure.</p></blockquote><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">A number of useful tools are supplied as standard.</p><ol style="padding: 0px; margin: 0px 0px 1.571em 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; "><li style="padding: 0px; margin: 0px; ">Memcheck is a memory error detector. It helps you make your programs, particularly those written in C and C++, more correct.</li><li style="padding: 0px; margin: 0px; ">Cachegrind is a cache and branch-prediction profiler. It helps you make your programs run faster.</li><li style="padding: 0px; margin: 0px; ">Callgrind is a call-graph generating cache profiler. It has some overlap with Cachegrind, but also gathers some information that Cachegrind does not.</li><li style="padding: 0px; margin: 0px; ">Helgrind is a thread error detector. It helps you make your multi-threaded programs more correct.</li><li style="padding: 0px; margin: 0px; ">DRD is also a thread error detector. It is similar to Helgrind but uses different analysis techniques and so may find different problems.</li><li style="padding: 0px; margin: 0px; ">Massif is a heap profiler. It helps you make your programs use less memory.</li><li style="padding: 0px; margin: 0px; ">DHAT is a different kind of heap profiler. It helps you understand issues of block lifetimes, block utilisation, and layout inefficiencies.</li><li style="padding: 0px; margin: 0px; ">SGcheck is an experimental tool that can detect overruns of stack and global arrays. Its functionality is complementary to that of Memcheck: SGcheck finds problems that Memcheck can&#8217;t, and vice versa..</li><li style="padding: 0px; margin: 0px; ">BBV is an experimental SimPoint basic block vector generator. It is useful to people doing computer architecture research and development.</li></ol><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">There are also a couple of minor tools that aren&#8217;t useful to most users: Lackey is an example tool that illustrates some instrumentation basics; and Nulgrind is the minimal Valgrind tool that does no analysis or instrumentation, and is only useful for testing purposes.</p><center style="padding: 0px; margin: 0px; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; "><div style="margin: 10px 0px 10px 2px; "><iframe allowtransparency="true" frameborder="0" height="280" hspace="0" marginwidth="0" marginheight="0" scrolling="no" vspace="0" width="336" id="aswift_2" name="aswift_2" style="padding: 0px; margin: 0px; left: 0px; position: absolute; top: 0px; "></iframe></div></center><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Here in this article we will focus on the tool &#8216;memcheck&#8217;.</p><h3>Using Valgrind Memcheck</h3><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">The memcheck tool is used as follows :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">valgrind --tool=memcheck ./a.out</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">As clear from the command above, the main binary is &#8216;Valgrind&#8217; and the tool which we want to use is specified by the option &#8216;&#8211;tool&#8217;. The &#8216;a.out&#8217; above signifies the executable over which we want to run memcheck.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">This tool can detect the following memory related problems :</p><ul style="padding: 0px; margin: 0px 0px 1.571em 1.571em; list-style: square; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; "><li style="padding: 0px; margin: 0px; ">Use of uninitialized memory</li><li style="padding: 0px; margin: 0px; ">Reading/writing memory after it has been freed</li><li style="padding: 0px; margin: 0px; ">Reading/writing off the end of malloc&#8217;d blocks</li><li style="padding: 0px; margin: 0px; ">Memory leaks</li><li style="padding: 0px; margin: 0px; ">Mismatched use of malloc/new/new[] vs free/delete/delete[]</li><li style="padding: 0px; margin: 0px; ">Doubly freed memory</li></ul><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Note : The above list is not exhaustive but contains the popular problems detected by this tool.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Lets discuss the above scenarios one by one:</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Note : All the test code described below should be compiled using gcc with -g option(to generate line numbers in memcheck output) enabled. As we discussed earlier for a&nbsp;<a href="http://www.thegeekstuff.com/2011/10/c-program-to-an-executable/" style="padding: 0px; margin: 0px; color: #2361a1; ">C program to get compiled into an executable</a>, it has to go through 4 different stages.</p><h3>1. Use of uninitialized memory</h3><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Code :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt;   int main(void) {     char *p;       char c = *p;       printf("\n [%c]\n",c);       return 0; }</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">In the above code, we try to use an uninitialized pointer &#8216;p&#8217;.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Lets run memcheck and see the result.</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">$ valgrind --tool=memcheck ./val ==2862== Memcheck, a memory error detector ==2862== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2862== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2862== Command: ./val ==2862== ==2862== Use of uninitialised value of size 8 ==2862==    at 0x400530: main (valgrind.c:8) ==2862==  [#] ==2862== ==2862== HEAP SUMMARY: ==2862==     in use at exit: 0 bytes in 0 blocks ==2862==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==2862== ==2862== All heap blocks were freed -- no leaks are possible ==2862== ==2862== For counts of detected and suppressed errors, rerun with: -v ==2862== Use --track-origins=yes to see where uninitialized values come from ==2862== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">As seen from the output above, Valgrind detects the uninitialized variable and gives a warning(see the lines in bold above).</p><h3>2. Reading/writing memory after it has been freed</h3><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Code :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt;   int main(void) {     char *p = malloc(1);     *p = 'a';       char c = *p;       printf("\n [%c]\n",c);       free(p);     c = *p;     return 0; }</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">In the above piece of code, we have freed a pointer &#8216;p&#8217; and then again we have tried to access the value help by the pointer.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Lets run memcheck and see what Valgrind has to offer for this scenario.</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">$ valgrind --tool=memcheck ./val ==2849== Memcheck, a memory error detector ==2849== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2849== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2849== Command: ./val ==2849==    [a] ==2849== Invalid read of size 1 ==2849==    at 0x400603: main (valgrind.c:30) ==2849==  Address 0x51b0040 is 0 bytes inside a block of size 1 free'd ==2849==    at 0x4C270BD: free (vg_replace_malloc.c:366) ==2849==    by 0x4005FE: main (valgrind.c:29) ==2849== ==2849== ==2849== HEAP SUMMARY: ==2849==     in use at exit: 0 bytes in 0 blocks ==2849==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated ==2849== ==2849== All heap blocks were freed -- no leaks are possible ==2849== ==2849== For counts of detected and suppressed errors, rerun with: -v ==2849== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">As seen in the output above, the tool detects the invalid read and prints the warning &#8216;Invalid read of size 1&#8242;.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">On a side note, to&nbsp;<a href="http://www.thegeekstuff.com/2010/03/debug-c-program-using-gdb/" style="padding: 0px; margin: 0px; color: #2361a1; ">debug a c program use gdb</a>.</p><h3>3. Reading/writing off the end of malloc&#8217;d blocks</h3><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Code :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt;   int main(void) {     char *p = malloc(1);     *p = 'a';       char c = *(p+1);       printf("\n [%c]\n",c);       free(p);     return 0; }</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">In the above piece of code, we have allocated 1 byte for &#8216;p&#8217; but we access the the address p+1 while reading the value into &#8216;c&#8217;.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Now we run Valgrind on this piece of code :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">$ valgrind --tool=memcheck ./val ==2835== Memcheck, a memory error detector ==2835== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2835== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2835== Command: ./val ==2835== ==2835== Invalid read of size 1 ==2835==    at 0x4005D9: main (valgrind.c:25) ==2835==  Address 0x51b0041 is 0 bytes after a block of size 1 alloc'd ==2835==    at 0x4C274A8: malloc (vg_replace_malloc.c:236) ==2835==    by 0x4005C5: main (valgrind.c:22) ==2835==    [] ==2835== ==2835== HEAP SUMMARY: ==2835==     in use at exit: 0 bytes in 0 blocks ==2835==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated ==2835== ==2835== All heap blocks were freed -- no leaks are possible ==2835== ==2835== For counts of detected and suppressed errors, rerun with: -v ==2835== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Again, this tool detects the invalid read done in this case.</p><h3>4. Memory leaks</h3><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Code:</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt;   int main(void) {     char *p = malloc(1);     *p = 'a';       char c = *p;       printf("\n [%c]\n",c);       return 0; }</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">In this code, we have malloced one byte but haven&#8217;t freed it. Now lets run Valgrind and see what happens :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">$ valgrind --tool=memcheck --leak-check=full ./val ==2888== Memcheck, a memory error detector ==2888== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2888== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2888== Command: ./val ==2888==    [a] ==2888== ==2888== HEAP SUMMARY: ==2888==     in use at exit: 1 bytes in 1 blocks ==2888==   total heap usage: 1 allocs, 0 frees, 1 bytes allocated ==2888== ==2888== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==2888==    at 0x4C274A8: malloc (vg_replace_malloc.c:236) ==2888==    by 0x400575: main (valgrind.c:6) ==2888== ==2888== LEAK SUMMARY: ==2888==    definitely lost: 1 bytes in 1 blocks ==2888==    indirectly lost: 0 bytes in 0 blocks ==2888==      possibly lost: 0 bytes in 0 blocks ==2888==    still reachable: 0 bytes in 0 blocks ==2888==         suppressed: 0 bytes in 0 blocks ==2888== ==2888== For counts of detected and suppressed errors, rerun with: -v ==2888== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">The lines (in bold above) shows that this tool was able to detect the leaked memory.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Note: In this case we added an extra option &#8216;&#8211;leak-check=full&#8217; to get verbose details of the memory leak.</p><h3>5. Mismatched use of malloc/new/new[] vs free/delete/delete[]</h3><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Code:</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include&lt;iostream&gt;   int main(void) {     char *p = (char*)malloc(1);     *p = 'a';       char c = *p;       printf("\n [%c]\n",c);     delete p;     return 0; }</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">In the above code, we have used malloc() to allocate memory but used delete operator to delete the memory.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Note : Use g++ to compile the above code as delete operator was introduced in C++ and to compile c++ code, g++ tool is used.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Lets run this tool and see :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">$ valgrind --tool=memcheck --leak-check=full ./val ==2972== Memcheck, a memory error detector ==2972== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==2972== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==2972== Command: ./val ==2972==    [a] ==2972== Mismatched free() / delete / delete [] ==2972==    at 0x4C26DCF: operator delete(void*) (vg_replace_malloc.c:387) ==2972==    by 0x40080B: main (valgrind.c:13) ==2972==  Address 0x595e040 is 0 bytes inside a block of size 1 alloc'd ==2972==    at 0x4C274A8: malloc (vg_replace_malloc.c:236) ==2972==    by 0x4007D5: main (valgrind.c:7) ==2972== ==2972== ==2972== HEAP SUMMARY: ==2972==     in use at exit: 0 bytes in 0 blocks ==2972==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated ==2972== ==2972== All heap blocks were freed -- no leaks are possible ==2972== ==2972== For counts of detected and suppressed errors, rerun with: -v ==2972== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">We see from the output above (see lines in bold), the tool clearly states &#8216;Mismatched free() / delete / delete []&#8216;</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">You can try and use the combination &#8216;new&#8217; and &#8216;free&#8217; in a test code and see what result this tool gives.</p><h3>6. Doubly freed memory</h3><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">Code :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt;   int main(void) {     char *p = (char*)malloc(1);     *p = 'a';       char c = *p;     printf("\n [%c]\n",c);     free(p);     free(p);     return 0; }</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">In the above peice of code, we have freed the memory pointed by &#8216;p&#8217; twice. Now, lets run the tool memcheck :</p><pre style="padding: 0.667em 0.917em; margin-top: 0px; margin-bottom: 1.833em; background-color: #eeeeee; border: 1px solid #dddddd; overflow: auto; clear: both; font-family: Consolas, 'Andale Mono', Monaco, Courier, 'Courier New', Verdana, sans-serif; font-size: 0.857em; line-height: 1.5em; color: #111111; ">$ valgrind --tool=memcheck --leak-check=full ./val ==3167== Memcheck, a memory error detector ==3167== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==3167== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==3167== Command: ./val ==3167==    [a] ==3167== Invalid free() / delete / delete[] ==3167==    at 0x4C270BD: free (vg_replace_malloc.c:366) ==3167==    by 0x40060A: main (valgrind.c:12) ==3167==  Address 0x51b0040 is 0 bytes inside a block of size 1 free'd ==3167==    at 0x4C270BD: free (vg_replace_malloc.c:366) ==3167==    by 0x4005FE: main (valgrind.c:11) ==3167== ==3167== ==3167== HEAP SUMMARY: ==3167==     in use at exit: 0 bytes in 0 blocks ==3167==   total heap usage: 1 allocs, 2 frees, 1 bytes allocated ==3167== ==3167== All heap blocks were freed -- no leaks are possible ==3167== ==3167== For counts of detected and suppressed errors, rerun with: -v ==3167== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)</pre><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">As seen from the output above(lines in bold), the tool detects that we have called free twice on the same pointer.</p><p style="padding: 0px; margin: 0px 0px 1.571em; color: #111111; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 21.983333587646484px; background-color: #ffffff; ">In this article, we concentrated on memory management framework Valgrind and used the tool memcheck (provided by this framework) to describe how it makes life easy for a developer working close to memory. This tool can detect many memory related problems that are very hard to find manually.</p></a><img src ="http://www.blogjava.net/xiaomage234/aggbug/390792.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2012-11-05 11:22 <a href="http://www.blogjava.net/xiaomage234/archive/2012/11/05/390792.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Chrome源码剖析 下《转》</title><link>http://www.blogjava.net/xiaomage234/archive/2012/02/16/370123.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 16 Feb 2012 09:10:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2012/02/16/370123.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/370123.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2012/02/16/370123.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/370123.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/370123.html</trackback:ping><description><![CDATA[<div><div id="cnblogs_post_body"><span style="font-family: Verdana; font-size: 13px; "> <h1>【四】Chrome的UI绘制</h1> <h2>1. Chrome的窗口控件</h2> <div style="margin-top: 0px; margin-bottom: 0px; ">Chrome提供了自己的一个UI控件库，相关文档可以参见<a id="mav3" href="http://sites.google.com/a/chromium.org/dev/developers/design-documents/chromeviews" title="这里" style="color: #551a8b; ">这里</a>。用Chrome自己的话来说，我觉得市面上的七荤八素的图形控件库都不好用，于是自己倒腾倒腾实现了一套。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">广告虽如此说，不过，Chrome的图形控件结构，我还未发现有啥非常非常特别的地方。Chrome的窗口、按钮、菜单之类的控件，都直接或间接派生自<strong>View</strong>，这个是控件基类。<strong>Chrome的View具有树形结构</strong>，其内部有一个子View数组，由此构成一个控件常用的组合模式。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">有一个比较特殊的View子类，叫做<strong>RootView</strong>，顾名思义，它是整个View控件树的根，在Chrome中，一个正确的树形的控件结构，必须由RootView作为根。之所以要这样设计，是因为RootView有一个比较特殊的功能，那就是分发消息。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">我们知道，一般的Windows控件，都有一个HWND，用与占据一块屏幕，捕获系统消息。Chrome中的View只是保存控件相关信息和绘制控件，里面没有HWND句柄，因此不能够捕获系统消息。在Chrome中，完整的控件架构是这样的，首先需要有一个<strong>ViewContainer</strong>，它里面包含一个RootView。ViewContainer是一个抽象类，在Window中的一个子类是<strong>HWNDViewContainer</strong>，同时，HWNDViewContainer还是MessageLoopForUI::Observer的子类。如果你看过本文第一部分描述的线程通信的内容的话，你就应该还记得，Observer是用于监听本线程内系统消息的东东。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">当有系统消息进入此线程消息循环 后，HWNDViewContainer会监听到这个情况，如果和View相关的消息，它就会调用RootView的相关方法，传递给控件。在 RootView的内部，会遍历整个控件树上的控件，将消息传递给各个控件。当然，有的消息是可以独占的，比如鼠标移动发送在某个View所管辖的范围 内，它会告知RootView（通过方法的返回值...），这个消息我要了，那么RootView会停止遍历。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">在设计的时候，<strong>View对消息的处理，采取的是大而全的接口模式</strong>。 就是说在View内部，提供了所有可能的消息处理接口，并提供了默认实现，所有子类只需要覆盖自己需要的消息处理函数即可。如果对MFC的消息映射有了解 的话，可以知道两者的区别。MFC在设计的时候，觉得无法提供大而全的接口，因为消息总类实在太多，而且还是可扩展的，于是就有了消息映射着一套繁琐的 宏。但Chrome的图形框架，显然没有做一个通用的Framework的打算，因此，可以采用这样的策略，使得子类的派生变得简单而自然。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">每一个View的子类控件，比如Button之类的，会存储一些数据，根据消息做一些行为，并且绘制出自己。在Chrome中，画图的东西是ChromeCanvas这个类，在其内部，<strong>通过Skia和GDI实现绘制</strong>。 Skia是Android团队开发的一个跨平台的图形引擎，在Chrome中负责除了文字之外，所有内容的绘制；而文字绘制的重担，在Windows中交 到了GDI的手上。这样的设计会给跨平台带来一些困难，估计是由Skia实现文本绘制会比较繁琐，才会带出如此一个设计的模式。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">另外一个历史遗留产物，就是在Windows下的图形控件，还有一些是原生的，就是说带有HWND那种传统的控件，这是Chrome身上不多的赶工期的痕迹，随着时间的宽裕，这样的原生控件会被淘汰进历史的垃圾箱，而全部变为从View派生的控件。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">其实，对于Chrome这套控件架构我还没算摸得很熟悉，估计等到做一次插件之后会了解的更透彻，因此，只说了点皮毛，聊表心意。。。</div> <h2>2. Chrome的页面加载和绘制</h2> <div style="margin-top: 0px; margin-bottom: 0px; ">上面这些UI控件，都是用在窗口上的（比如浏 览器的外框，菜单，对话框之类的...）。我们在浏览器中看到的大部分内容，是网页页面。页面的绘制（绘制，就是把一个HTML文件变成一个活灵活现的页 面展示的过程...），只有一半轮子是Chrome自己做的，还有一部分来自于<span style="font-weight: bold; ">WebKit</span>，这个Apple打造的Web渲染器。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">之所以说是一半轮子来源于WebKit，是因为WebKit本身包含两部分主要内容，一部分是做Html渲染的，另一部分是做JavaScript解析的。<span style="font-weight: bold; ">在Chrome中，只有Html的渲染采用了WebKit的代码，而在JavaScript上，重新搭建了一个NB哄哄的V8引擎</span>。目标是，用WebKit + V8的强强联手，打造一款上网冲浪的法拉利，从效果来看，还着实做的不错。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">不过，虽说Chrome和WebKit都是开 源的，并联手工作。但是，Chrome还是刻意的和WebKit保持了距离，为其始乱终弃埋下了伏笔。Chrome在WebKit上封装了一层，称为 WebKit Glue。Glue层中，大部分类型的结构和接口都和WebKit类似，Chrome中依托WebKit的组件，都只是调用WebKit  Glue层的接口，而不是直接调用WebKit中的类型。按照Chrome自己文档中的话来说，就是，虽然我们再用WebKit实现页面的渲染，但通过这 个设计（加一个间接层...）已经从某种程度大大降低了与WebKit的耦合，使得可以很容易将WebKit换成某个未来可能出现的更好的渲染引擎。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; "><br /> </div> <div style="margin-top: 0px; margin-bottom: 0px; "> <table id="di2y" style="font-size: 1em; line-height: inherit; " bgcolor="#cccccc" border="2" cellpadding="3" cellspacing="0" width="100%">     <tbody><tr style="text-align: left; ">         <td width="100%"><blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; border-width: initial; border-color: initial; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; "><span style="font-family: 'Comic Sans MS'; font-size: 18px; font-weight: bold; ">重用</span><br />         <span style="font-family: 'Comic Sans MS'; ">在《梦断代码》中，有一坨调侃重用的文字。他觉着软件重用的困难一方面来自于场景本身很多变，很难设计出一套包罗万象的东西；另一方面来自于人，程序员总是瞅着别人写的代码不顺眼，总喜欢自己写一套。。。<br />          于是，解决重用这个问题也就只有两种，写最NB人见人服无所不能的代码，或者是有很多很多NB代码共君任选。Google无疑在这两个方面做得都不 错，Map/Reduce，Big Table之类的一套东西，强大到可以适合太多的场景，大大简化了N多上层应用的开发。而对开源的<span style="text-decoration: line-through; ">利用</span>使用，使得其可以随意挑一个巨人站到他肩膀上跳舞，每看到这种场景，MS估计都会气得拍着胸口吐血。。。<br />         Google本身在服务端的基础底层，有很深积累，随着Chrome，Android等等客户端应用的开发，客户端的积累也逐步提升，也许，拥抱开源才是MS的正道？。。。</span></blockquote></td>     </tr> </tbody></table> </div> <div style="margin-top: 0px; margin-bottom: 0px; "><br /> </div> <div style="margin-top: 0px; margin-bottom: 0px; ">当你键入一个Url并敲下回车后，<span style="font-weight: bold; ">Chrome会在Browser进程中下载Url对应的页面资源（包括Web页面和Cookie），</span>而 不是直接将Url发送给Render进程让它们自行下载（你会越来越发现，Render进程绝对是100%的名符其实，除了绘制，几乎啥多余的事情都不会 干的...）。与各个Render进程各自为站，各自管好自己所需的资源相比，这种策略仿佛会增加大量的进程间通信。之所以采用，按照<a id="qz.y" href="http://sites.google.com/a/chromium.org/dev/developers/design-documents/multi-process-resource-loading" title="这篇文档" style="color: #551a8b; ">这篇文档</a>的 解释，主要有三个优点，一个是避免子进程与网络通信，从而将网络通信的权限牢牢握在主进程手中，Render进程能力弱了，想造反干坏事的可能性就降低了 （可以更好控制各个Render进程的权限...）；另一个是有利于Cookie等持久化资源在不同页面中的共享，否则在不同Render进程中传递 Cookie这样的事情，做起来更麻烦；还有一点很重要的，是可以控制与网络建立HTTP连接的数量，以Browser为代表与网络各方进行通信，各种优 化策略都比较好开展（比如池化）。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">当然，在Browser进程中进行统一的资源管理，也就意味着不再方便用WebKit进行资源下载（WebKit当然有此能力，不过再次被Chrome抛弃了...），而是依托WinHTTP来做的。<span style="font-weight: bold; ">WinHTTP在接受数据的过程中，会不停的把数据和相关的消息通过IPC，发送给负责绘制此页面的Render进程中对应的RenderView</span>。在这里，路由消息中的那个ID值起了关键的作用，系统依照此ID，能够准确的将相关的消息发送到相关的View头上，这玩意发错了地方还真不是和有人把钱错到你账户上一样，因为错收的进程基本上无福消受这个意外来客，轻者页面显示混乱，重者消化不良直接噎死。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">RenderView接收到页面信息，会一边 绘制一边等待更多的资源到来，在用户看来，所请求的页面正在一点一点显示出来。当然，如果是一个通知传输开始、传输结束这样的消息，通过序列化到消息参数 里面，经由IPC发过来，代价还是可以承受的，但是，想资源内容这样大段大段的字节流，如果通过消息发过来，浪费两边进程大量空间和时间，就不合适了。于 是这里用到了<span style="font-weight: bold; ">共享内存</span>。Browser进程将下载到的资源写到共享 内存中，并将共享内存的句柄和共享区域的大小序列化在消息中发送给Render进程。Render进程拿到这个句柄，就可以通过它访问到共享内存相关的区 域，读取信息并进行绘制。通过这样的方式，即享用到了统一资源管理的优点，由避免了很高的进程通信开销，左右逢源，好不快活。。。</div> <h2>3. Chrome页面的消息响应</h2> <div style="margin-top: 0px; margin-bottom: 0px; ">Render进程是一个娇生惯养的进程，这一点从上面一段已经可以看出来了。它自己的资源它自己都不下载，而是由Browser进程来帮忙。不过Render进程也许比你想象的还要懒惰一些，它不但不自己下载资源，甚至，连自己的系统消息都不接收。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">Render进程中不包含HWND，当你鼠标 在页面上划来划去，点上点下，这些消息其实都发到了Browser进程，它们拥有页面呈现部分的HWND。Browser会将这些消息转手通过IPC发送 给对应的Render进程中的RenderView，很多时候WebKit会处理此类消息，当它发现出现了某种值得告诉Browser进程的事情，它会组 个报回赠给Browser进程。举个例子，你打开一个页面，然后拿鼠标在页面上乱晃。Browser这时候就像一个碎嘴大婶，不厌其烦的告诉Render 进程，&#8220;鼠标动了，鼠标动了&#8221;。如果Render对这个信息无所谓，就会很无聊的应答着：&#8220;哦，哦&#8221;（发送一个回包...）。但是，当鼠标划过链接的时 候，矜持的Render进程坐不住了，会大声告诉Browser进程：&#8220;换鼠标，换鼠标~~&#8221;，Browser听到后，会将鼠标从箭头状换成手指状，然后 继续以上过程。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">比较麻烦的是Paint消息，重新绘制页面是 一个太频繁发生的事情，不可能重绘一次就序列化一坨字节流过去。于是策略也很清楚了，就是依然用共享内存读写，用消息发句柄。在Render进程中，会有 一个共享内存池（默认值为2...），以size为key，以共享内存为值，简单的先入先出淘汰算法，利用局部性的特征，避免反复的创建和销毁共享内存 （这和资源传递不一样，因为资源传递可以开一块固定大小的共享内存...）。Render进程从共享内存池中拿起一块（二维字节数组...），就好像拿着 一块屏幕似的，拼了命往上绘制，为了让Render安心觉着有成就感，Browser会偷偷帮Render把这些内容绘制到屏幕上，造成Render进程 直接绘制屏幕的假象。这可就苦了屏幕取词的工具们，因为在HWND上压根就没啥字符信息，全部就是一坨图像而已，啥也取不着。于是Google金山词霸， 网易有道词霸各自发挥智慧，另辟蹊径，也算是都利用Chrome做了一把广告。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">为什么不让Render进程自己拥有HWND，自己管理自己的消息，既快捷又便利。在Chrome的官方Blog上，有一篇<a id="w:e6" href="http://blog.chromium.org/2008/10/responsiveness-for-plugins-and-renderer.html" title="解释的文章" style="color: #551a8b; ">解释的文章</a>， 基本上是这个意思，速度是必须快的发指的，但是为了用户响应，放弃一些速度是必要的，毕竟，没有人喜欢总假死的浏览器。在Browser进程中，基本上是 杜绝任何同步Render进程的工作，所有操作都是异步完成。因为Render进程是不靠谱的，随时可能牺牲掉，同步它们往往导致主进程停止响应，从而导 致整个浏览器停下来甚至挂掉，这个代价是不可以容忍的。但是，Windows有一个恶习，喜欢往整个HWND继承体系中发送同步消息（我不是很清楚这个状 况，有人能解释么？...），这时候，如果HWND在Render进程中，就务必会导致主进程与Render进程的同步，Chrome无法控制 Windows，于是，它们只能够控制Render，把它们的HWND搬到主进程中，避免同步操作，换取用户响应的速度。。。</div> <h2>4. 结论</h2> <div style="margin-top: 0px; margin-bottom: 0px; ">整个Chrome的UI架构，就是一个权责分 配的问题。可以把Browser进程看成是一个类似于朱元璋般的勤劳皇帝（详见《明朝那些事  一》...），把大多数的权利都牢牢把握在手中，这样，虽然Browser很操劳，但是整体上的协调和同步，都进行的非常顺畅。Render进程就是皇帝 手下的傀儡宰相们，只负责自己的一亩三分地，听从皇帝的调配即可。这这样的环境下，Render进程的生死变得无足轻重，Render的死亡，只是少了一 个绘制页面的工具而已，其他一切如故。通过控制权力，换取天下太平，这招在coding界，同样是一个不错的策略，但是，唯一的意外来自于Plugin。 按照规范，Chrome的Plugin是可以创立窗口的（HWND），这必然导致同步问题，Chrome没有办法通过控制权力的方式解决这个问题，只能想 些别的亡羊补牢的招来搞定。。。</div> </span></div></div><br /><br /><div><div id="cnblogs_post_body"><span style="font-family: Verdana; font-size: 13px; "> <h1>【五】 Chrome的插件模型</h1> <h2>1. NPAPI</h2> <div style="margin-top: 0px; margin-bottom: 0px; ">为了紧密的与各个开源浏览器团结起来，共同抗击IE的垄断，Chrome的插件，也遵循了<strong>NPAPI</strong>(Netscape Plugin Application Programming Interface)标准，支持这个标准的浏览器需要实现一组规定的API供插件调用，这组API形如<strong>NPN_XXX</strong>，比如NPN_GetURL，插件可以利用这些API进行二次开发。而NPAPI插件以一个Dll之类的作为物理载体（windows下dll，linux下是so...）进行提供，里面同样也实现了一组规定的API。形式包括<strong>NP_XXX</strong>和<strong>NPP_XXX</strong>，NP_XXX是系统需要默认调用的方法，用于认知这个插件，比如NP_Initialize， 而NPP_XXX是用于插件完成一些实际功能，比如NPP_New。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">所有的插件dll都需要放置在指定目录下（根 据操作系统的不同而不同...），每个插件可以处理一种或多种MIME格式的数据，比如application/pdf，说明该插件可以处理pdf相关的 文档。在Chrome中键入about:plugins，可以查看当前Chrome中具有的插件信息。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">NPAPI是一个很经典的插件方案，<strong>用dll进行注入，用协定的API进行通信，用字符串描述插件能力</strong>。 插件宿主（在这里就是浏览器...），会根据能力描述，动态加载插件，并负责插件调用的流程和生命周期管理。而插件中，负责真实逻辑的处理，并可以构造 UI与用户交流。以此类方式实现的插件系统，往往是处理的逻辑比较固定适用范围一般（用API写死了逻辑...），但可扩展性不错（用字符串描述能力，可 无限扩展...）。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">在Chrome中<span style="font-weight: bold; ">nphostapi.h</span>中，定义了所有NPAPI相关的函数指针和结构，这个文件放置在glue目录下，如果看过前面碰过的文章就知道，在WebKit内肯定也有一套相同的东西；在<span style="font-weight: bold; ">npapi.h/.cc</span>中，提供了Chrome浏览器端的NPN_XXX系列函数的实现；每一个插件物理实例，用<span style="font-weight: bold; ">PluginLib</span>类来表示，而每一个插件的逻辑实例，用<span style="font-weight: bold; ">PluginInstance</span>类 来表示。这个概念牵强附会的可以用windows中的句柄来类比，当你想操作一个内核对象，你需要获得一个内核对象的句柄，每个进程中的句柄肯定不相同， 但后面的内核对象却是同一个，内核对象的生命周期通过句柄的计数来控制，有人用则或，无人用则死（当然这个类比相当的牵强，主要是想说明引用计数和逻辑与 物理的关系，但一个关键性的区别在于，<span style="font-weight: bold; ">PluginLib与PluginInstance都是在一个进程内的</span>，不能跨越进程边界...）。在Chrome中，PluginLib负责加载和销毁一个dll，拿到所有导出函数的函数指针，PluginInstance对这些东西进行了封装，可以更好的来调用。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">关于NPAPI的更多细节，Chrome并没有提供任何文档，但是，各个先驱的浏览器们都提供了大量丰富的文档。比如，你可以到<a id="d9.2" href="https://developer.mozilla.org/en/Gecko_Plugin_API_Reference" title="这里" style="color: #551a8b; ">这里</a>，查看firefox中的NPAPI文档，基本通用。。。</div> <h2>2. Chrome的多进程插件模型</h2> <div style="margin-top: 0px; margin-bottom: 0px; ">Chrome的插件模型，与早先的浏览器的最大不同，是它采用了<span style="font-weight: bold; ">多进程</span>的 方式，每一个插件，都有一个单独的进程来承载（Shift +  Esc打开Chrome进程管理器，可以看到现在已经加载的插件进程...）。当WebKit进行页面渲染的时候，发现了未知的MIME类型数据，它会告 知给Browser进程，召唤它提供一个插件来解析。如果该插件还未加载，Browser会在指定目录中搜寻出具有此实力的插件（如果没有此类人才只能作 罢...），并为它创建一个进程，让它负责所有的该插件相关的任务，然后建立起一个IPC通路，与它&#8220;保持通话&#8221;。这套流程一定不会太陌生，因为它与 Render进程的创建大同小异换汤不换药。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">Plugin进程与Render进程最大的区 别在于，Render需要与Browser进程大量通信，因为它的HWND归Browser老大掌管着，相关所有内容都需要通信完成。但Plugin不需 要与Browser频繁联系，它大部分的通信都是与Render进程发生的。如果Plugin与Render之间的通信，还需要走Browser中转一 下，这就显得有些脱裤子放屁了，虽然Browser是大头，但不是冤大头，它不会干这种吃力不讨好的事情。他只是做了一回Render与Plugin间的 媒婆而已。当Plugin与Browser建立好了IPC通路后，它会让Render建立一个新IPC通路，用以与Plugin通信，IPC的有名管道 名，经由Browser通知给Plugin。完成名字协商后，Render与Plugin的通信关系就建立好了，它们之间就可以直接进行通信了。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">整个通信模式，可以看<a id="aw1_" href="http://sites.google.com/a/chromium.org/dev/developers/design-documents/plugin-architecture" title="这里" style="color: #551a8b; ">这里</a>。这是一个很标准的代理模式的应用，稍有了解的都可以跳过我后面会做的一段罗嗦的描述，一看官方文档中的图便能知晓。在Render进程端，<span style="font-weight: bold; ">WebPluginImpl</span>是<span style="font-weight: bold; ">WebPlugin</span>的一个子类，WebPlugin是供Webkit进行调用的一个接口，利用依赖倒置，实现了扩展。在Plugin进程端，实现了一个<span style="font-weight: bold; ">WebPluginDelegateImpl</span>类， 该类会调用PluginInstance的相关接口实现真实的插件功能。这样的话，只需要WebPluginImpl调用 WebPluginDelegateImpl中的相应方法，就可以实现功能。但问题是WebPluginImpl与 WebPluginDelegateImpl天各一方各处于一个进程，很显然，这里需要一个代理模式。这里沿用了COM的架构，Delegate +  Stub + Proxy。WebPluginImpl调用代理<span style="font-weight: bold; ">WebPluginDelegateProxy</span>，该代理会将调用转换成消息，通过IPC发送给Plugin进程，在Plugin端，通过<span style="font-weight: bold; ">WebPluginDelegateStub</span>监听消息，并转换成对真实WebPluginDelegateImpl的调用，从而完成了跨进程的一个调用，反之亦然。。。</div> <h2>3. Chrome的可扩展性</h2> <div style="margin-top: 0px; margin-bottom: 0px; ">总所周知，firefox通过三种方式进行自定义，插件、扩展和皮肤。其中，插件是使得浏览器能用，不会出现一大块一大块的无法显示的区域；扩展是使得浏览器好用，可以简单方便的进行功能的定制和个性化配置；皮肤是帮助浏览器变得好看，毕竟罗卜白菜，给有所爱。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">与之对比，来看Chrome。Chrome有 了插件，有了皮肤，但是没有扩展。这就意味着，你很难为Chrome定制一些特色的功能。目前，所有对Chrome的功能扩展，都是通过书签抑或是修改内 核来实现的。前者能力太弱，后者开发起来太麻烦，容易出错不提，还必须要与时俱进，跟上版本的变化，并且还不能自由的选择或关闭。因此，这都不是长远之 计，Chrome提供一套类似于firefox的扩展机制，也许才是正道。据传说，Chrome团队正在琢磨这件事，不知道最终会出来个怎么样的结果，是 尽力接近firefox降低移植成本，还是另立门户特立独行，我想可以拭目以待一把。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">在多进程模式下，Chrome的插件还有一个 问题，前面提到过，就是关于UI控件的。由于NPAPI的标准，是允许插件创建HWND窗口的，这就使得当Plugin繁忙，且Browser进程发起 HWND的同步的时候，主进程被挂起，这个浏览器停滞。在Render进程中，解决这个问题的思路是控制权限，不然Render创建HWND，到了 Plugin中，这招不能使用，只能够使用另一招，就是监管。不停的检查Plugin是否太繁忙，无法响应，一旦发现，立即杀死该Plugin及其所处的 页面。这就好比你想解决奶中有三氯氰胺的问题，要么控制奶源，不从奶站购买全部用自家的，要么加强监管，提高检查力度防止隐患。两种策略的优缺点一眼便 知，依照不同环境采取不同策略即可。。。</div> <div style="margin-top: 0px; margin-bottom: 0px; ">总体说来，Chrome的可扩展性着实一般，不过Chrome还处于Beta中，我们可以继续期待。。。</div> </span></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/370123.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2012-02-16 17:10 <a href="http://www.blogjava.net/xiaomage234/archive/2012/02/16/370123.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Chrome源码剖析 上《转》</title><link>http://www.blogjava.net/xiaomage234/archive/2012/02/16/370122.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 16 Feb 2012 09:08:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2012/02/16/370122.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/370122.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2012/02/16/370122.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/370122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/370122.html</trackback:ping><description><![CDATA[<div><div id="content"><p style="padding-left: 30px">原著：duguguiyu。<br />整理：July。<br />时间：二零一一年四月二日。<br />出处：<span style="color: #002d93"><strong>http://blog.csdn.net/v_JULY_v</strong></span>。<br />说明：此Chrome源码剖析很大一部分编辑整理自此博客：<span style="color: #002d93">http://flyvenus.net/</span>。我对写原创文章的作者向来是以最大的尊重的。近期想好好研究和学习下Chrome源码，正巧看到了此duguguiyu兄台的源码剖析，处于学习的目的，就不客气的根据他的博客整理了此文。若有诸多冒犯之处，还望海涵。</p><table border="0" width="97%" align="center"><tbody><tr><td colspan="3"><br /></td></tr></tbody></table><br />-------------------------------- <p style="padding-left: 30px"><br /><strong><span style="font-size: medium">前言：</span></strong></p> <p style="padding-left: 30px">1、之所以整理此文，有俩个目的：一是为了供自己学习研究之用；二是为了备份，以作日后反复研究。除此之外，无它。<br />2、此文的形式其实是有点俩不像的，既不是个人首创即原创，又非单纯的转载（有加工），无奈之下，权且称作翻译吧。有不妥之处，还望原作者，及读者见谅。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 文中加入了我自己的一些见解，请自行辨别。顺便再说一句，duguguiyu写的这个Chrome源码剖析，真不错，勾起了偶对源码剖析的莫大兴趣。</p> <p style="padding-left: 30px"><strong>&nbsp;&nbsp;&nbsp; </strong>顺便透露下：在此份Chrome源码剖析之后，<strong>互联网上即将，首次出现sgi stl v3.3版的源码剖析拉。作者：本人July。</strong>是的，本人最近在研究sgi stl v3.3版的源码，正在做源码剖析，个人首创，敬请期待。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  在具体针对源码剖析之前，再粗略回答一下网友可能关心的问题：chrome速度维护如此之快？据网上资料显示：有几个主要的关键技术：DNS预解析、 Google自主开发的V8 Javacript引擎、DOM绑定技术以及多进程架构等等。但这不是本文的重点，所以略过不谈。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; ok，激动人心的Chrome源码剖析旅程，即刻开始。</p> <p style="padding-left: 30px"><br /><strong><span style="font-size: medium"><span style="color: #000000">Chrome源码剖析【序】</span></span></strong></p> <p style="padding-left: 30px">此序成于08年末，Chrome刚刚推出之际。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  duguguiyu：&#8220;有的人一看到Chrome用到多进程就说垃圾废物肯定低能。拜托，大家都是搞技术的，你知道多进程的缺点，Google也知道，他 们不是政客，除了搞个噱头扯个蛋就一无所知了，人家也是有脸有皮的，写一坨屎一样的开源代码放出来遭世人耻笑难道会很开心？所谓技术的优劣，是不能一概而 论的，同样的技术在不同场合不同环境不同代码实现下，效果是有所不同的。....&#8221;</p> <p style="padding-left: 30px">Chrome对我来说，有吸引力的地方在于（排名分先后&#8230;）：<br />&nbsp; 1、它是如何利用多进程（其实也会有多线程一起）做并发的，又是如何解决多进程间的一些问题的，比如进程间通信，进程的开销；<br />&nbsp; 2、做为一个后来者，它的扩展能力如何，如何去权衡对原有插件的兼容，提供怎么样的一个插件模型；<br />&nbsp; 3、它的整体框架是怎样，有没有很NB的架构思想；<br />&nbsp; 4、它如何实现跨平台的UI控件系统；<br />&nbsp; 5、传说中的V8，为啥那么快。<br />&nbsp;&nbsp;&nbsp; 但Chrome是一个跨平台的浏览器，其Linux和Mac版本正在开发过程中，所以我把所有的眼光都放在了windows版本中，所有的代码剖析都是基于windows版本的。有错误请指正。</p> <p style="padding-left: 30px"><br />&nbsp;&nbsp;&nbsp; 关于Chrome的源码下载和环境配置，大家可自行查找资料，强调一点，一定要严格按照说明来配置环境，特别是vs2005的补丁和windows SDK的安装，否则肯定是编译不过的。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 最后，写这部分唯一不是废话的内容，请记住以下这幅图，这是Chrome最精华的一个缩影：<img alt="" src="http://www.linuxidc.com/upload/2011_04/110403072730141.jpg" height="649" width="700" /></p> <p style="padding-left: 270px"><strong>图1 Chrome的线程和进程模型</strong></p> <p style="padding-left: 30px"><br /><span style="color: #ff0000"><strong><span style="font-size: medium"><span style="color: #000000">Chrome源码剖析【一】&#8212;&#8212; 多线程模型</span></span></strong></span></p> <p style="padding-left: 30px"><span style="font-size: medium">【一】 Chrome的多线程模型<br /></span><strong>0. Chrome的并发模型<br /></strong>&nbsp;&nbsp;&nbsp;   如果你仔细看了前面的图，对Chrome的线程和进程框架应该有了个基本的了解。Chrome有一个主进程，称为Browser进程，它是老大，管理 Chrome大部分的日常事务；其次，会有很多Renderer进程，它们圈地而治，各管理一组站点的显示和通信（Chrome在宣传中一直宣称一个 tab对应一个进程，其实是很不确切的&#8230;），它们彼此互不搭理，只和老大说话，由老大负责权衡各方利益。它们和老大说话的渠道，称做IPC（Inter- Process Communication），这是Google搭的一套进程间通信的机制，基本的实现后面自会分解。</p> <p style="padding-left: 60px"><span style="color: #808080"><strong>Chrome的进程模型 <br /></strong></span>Google 在宣传的时候一直都说，Chrome是one tab one  process的模式，其实，这只是为了宣传起来方便如是说而已，基本等同广告，实际疗效，还要从代码中来看。实际上，Chrome支持的进程模型远比宣 传丰富，简单的说，Chrome支持以下几种进程模型：</p> <p style="padding-left: 60px"><strong>1.</strong>Process-per-site-instance：就是你打开一个网站，然后从这个网站链开的一系列网站都属于一个进程。这是Chrome的默认模式。 <br /><strong>2.</strong>Process-per-site：同域名范畴的网站放在一个进程，比如<a href="http://www.www.google.com/" target="_blank"><span style="color: #002d93">www.google.com</span></a>（<span style="color: #808080"><strong><span style="color: #999999">由于此文形成于08年，所以无法访问，你懂的</span></strong></span>）和<a href="http://www.www.google.com/bookmarks" target="_blank"><span style="color: #002d93">www.google.com/bookmarks</span></a>就属于一个域名内（google有自己的判定机制），不论有没有互相打开的关系，都算作是一个进程中。用命令行&#8211;process-per-site开启。 <br /><strong>3.</strong>Process-per-tab：这个简单，一个tab一个process，不论各个tab的站点有无联系，就和宣传的那样。用&#8211;process-per-tab开启。 <br /><strong>4.</strong>Single Process：这个很熟悉了吧，即传统浏览器的模式：没有多进程只有多线程，用&#8211;single-process开启。</p> <p style="padding-left: 60px">关于各种模式的优缺点，官方有官方的说法，大家自己也会有自己的评述。不论如何，至少可以说明，Google不是由于白痴而采取多进程的策略，而是实验出来的效果。</p> <p style="padding-left: 60px">大家可以用Shift+Esc观察各模式下进程状况，至少我是观察失败了（每种都和默认的一样&#8230;），原因待跟踪。&nbsp;</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 不论是Browser进程还是Renderer进程，都不只是光杆司令，它们都有一系列的线程为自己打理各种业务。对于<strong>Renderer进程</strong>，它们通常有两个线程：一个是Main thread，它负责与老大进行联系，有一些幕后黑手的意思；另一个是Render thread，它们负责页面的渲染和交互，一看就知道是这个帮派的门脸级人物。<br />&nbsp;&nbsp;&nbsp; 相比之下，<strong>Browser进程</strong>既 然是老大，小弟自然要多一些，除了大脑般的Main thread，和负责与各Renderer帮派通信的IO  thread，其实还包括负责管文件的file thread，负责管数据库的db  thread等等，它们各尽其责，齐心协力为老大打拼。它们和各Renderer进程的之间的关系不一样，同一个进程内的线程，往往需要很多的协同工作， 这一坨线程间的并发管理，是Chrome最出彩的地方之一了。</p> <p style="padding-left: 60px"><strong><span style="color: #808080">闲话并发<br /></span></strong>单进程单线程的编程是最惬意的事情，所看即所得，一维的思考即可。但程序员的世界总是没有那么美好，在很多的场合，我们都需要有多线程、多进程、多机器携起手来一齐上阵共同完成某项任务，统称：并发（非官方版定义&#8230;）。在我看来，需要并发的场合主要是要两类：</p> <p style="padding-left: 60px"><strong>1.</strong>为了更好的用户体验。有的事情处理起来太慢，比如 数据库读写、远程通信、复杂计算等等，如果在一个线程一个进程里面来做，往往会影响用户感受，因此需要另开一个线程或进程转到后台进行处理。它之所以能够 生效，仰仗的是单CPU的分时机制，或者是多CPU协同工作。在单CPU的条件下，两个任务分成两拨完成的总时间，是大于两个任务轮流完成的，但是由于彼 此交错，给人的感觉更自然一些。</p> <p style="padding-left: 60px"><strong>2.</strong>为了加速完成某项工作。大名鼎鼎的 Map/Reduce，做的就是这样的事情，它将一个大的任务，拆分成若干个小的任务，分配个若干个进程去完成，各自收工后，再汇集在一起，更快地得到最 后的结果。为了达到这个目的，只有在多CPU的情形下才有可能，在单CPU的场合（单机单CPU&#8230;），是无法实现的。<br /><span style="font-size: medium"><strong><span style="color: #999999">在</span></strong></span>第二种场合下，我们会自然而然的关注数据的分离，从而很好的利用上多CPU的能力；而在第一种场合，我们习惯了单CPU的模式，往往不注重数据与行为的对应关系，导致在多CPU的场景下，性能不升反降。</p> <p style="padding-left: 30px"><br /><strong>1. Chrome的线程模型<br /></strong>&nbsp;&nbsp;&nbsp;   仔细回忆一下我们大部分时候是怎么来用线程的，在我足够贫瘠的多线程经历中，往往都是这样用的：起一个线程，传入一个特定的入口函数，看一下这个函数是否 是有副作用的（Side Effect），如果有，并且还会涉及到多线程的数据访问，仔细排查，在可疑地点上锁伺候。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; Chrome的线程模型走的是另一个路子，即，<strong>极力规避锁的存在</strong>。 换更精确的描述方式来说，Chrome的线程模型，将锁限制了极小的范围内（仅仅在将Task放入消息队列的时候才存在&#8230;），并且使得上层完全不需要关心 锁的问题（当然，前提是遵循它的编程模型，将函数用Task封装并发送到合适的线程去执行&#8230;），大大简化了开发的逻辑。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 不过，从实现来说，Chrome的线程模型并没有什么神秘的地方，它用到了消息循环的手段。每一个Chrome的线程，入口函数都差不多，都是<strong>启动一个消息循环</strong>（参见MessagePump类），等待并执行任务。<br />&nbsp;&nbsp;&nbsp;   而其中，唯一的差别在于，根据线程处理事务类别的不同，所起的消息循环有所不同。比如处理进程间通信的线程（注意，在Chrome中，这类线程都叫做IO 线程）启用的是MessagePumpForIO类，处理UI的线程用的是MessagePumpForUI类，一般的线程用到的是 MessagePumpDefault类（只讨论windows）。<br />&nbsp;&nbsp;&nbsp;  不同的消息循环类，主要差异有两个，一是消息循环中需要处理什么样的消息和任务，第二个是循环流程（比如是死循环还是阻塞在某信号量上&#8230;）。下图是一个完 整版的Chrome消息循环图，包含处理Windows的消息，处理各种Task（Task是什么，稍后揭晓，敬请期待），处理各个信号量观察者 （Watcher），然后阻塞在某个信号量上等待唤醒。</p> <p style="padding-left: 120px"><img alt="" src="http://www.linuxidc.com/upload/2011_04/110403072730142.jpg" height="436" width="271" /></p> <p style="padding-left: 210px"><strong>图2 Chrome的消息循环</strong></p> <p style="padding-left: 30px"><br />&nbsp;&nbsp;&nbsp; 当然，不是每一个消息循环类都需要跑那么一大圈的，有些线程，它不会涉及到那么多的事情和逻辑，白白浪费体力和时间，实在是不可饶恕的。因此，在实际中，不同的MessagePump类，实现是有所不同的，详见下表：<img alt="" src="http://www.linuxidc.com/upload/2011_04/110403072730145.jpg" height="177" width="700" /></p> <p style="padding-left: 30px"><br /><strong>2. Chrome中的Task<br /></strong>&nbsp;&nbsp;&nbsp;   从上面的表不难看出，不论是哪一种消息循环，必须处理的，就是Task（暂且遗忘掉系统消息的处理和Watcher，以后，我们会缅怀它们的&#8230;）。刨去其 它东西的干扰，只留下Task的话，我们可以这样认为：Chrome中的线程从实现层面来看没有任何区别，它的区别只存在于职责层面，不同职责的线程，会 处理不同的Task。最后，在铺天盖地西红柿来临之前，我说一下啥是Task。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 简单的看，Task就是一个类，一个包含了void  Run()抽象方法的类（参见Task类&#8230;）。一个真实的任务，可以派生Task类，并实现其Run方法。每个MessagePump类中，会有一个 MessagePump::Delegate的类的对象（MessagePump::Delegate的一个实现，请参见MessageLoop类&#8230;）， 在这个对象中，会维护若干个Task的队列。当你期望，你的一个逻辑在某个线程内执行的时候，你可以派生一个Task，把你的逻辑封装在Run方法中，然 后实例一个对象，调用期望线程中的PostTask方法，将该Task对象放入到其Task队列中去，等待执行。我知道很多人已经抄起了板砖，因为这种手 法实在是太常见了，就不是一个简单的依赖倒置，在线程池，Undo\Redo等模块的实现中，用的太多了。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  但，我想说的是，虽说谁家过年都是吃顿饺子，这饺子好不好吃还是得看手艺，不能一概而论。在Chrome中，线程模型是统一且唯一的，这就相当于有了一套 标准，它需要满足在各个线程上执行的几十上百种任务的需求，因此，必须在灵活行和易用性上有良好的表现，这就是设计标准的难度。为了满足这些需 求，Chrome在底层库上做了足够的功夫：<br />&nbsp; 1.它提供了一大套的模板封装（参见task.h），可以将Task摆脱继承结构、函数名、函数参数等限制（就是基于模板的伪function实现，想要更深入了解，建议直接看鼻祖《Modern C++》和它的Loki库&#8230;）；<br />&nbsp; 2.同时派生出CancelableTask、ReleaseTask、DeleteTask等子类，提供更为良好的默认实现；<br />&nbsp; 3.在消息循环中，按逻辑的不同，将Task又分成即时处理的Task、延时处理的Task、Idle时处理的Task，满足不同场景的需求；<br />&nbsp; 4.Task派生自tracked_objects::Tracked，Tracked是为了实现多线程环境下的日志记录、统计等功能，使得Task天生就有良好的可调试性和可统计性；<br />这一套七荤八素的都搭建完，这才算是一个完整的Task模型，由此可知，这饺子，做的还是很费功夫的。</p> <p style="padding-left: 30px"><br /><strong>3. Chrome的多线程模型<br /></strong>&nbsp;&nbsp;&nbsp;   工欲善其事，必先利其器。Chrome之所以费了老鼻子劲去磨底层框架这把刀，就是为了面对多线程这坨怪兽的时候杀的更顺畅一些。在Chrome的多线程 模型下，加锁这个事情只发生在将Task放入某线程的任务队列中，其他对任何数据的操作都不需要加锁。当然，天下没有免费的午餐，为了合理传递Task， 你需要了解每一个数据对象所管辖的线程，不过这个事情，与纷繁的加锁相比，真是小儿科了不知道多少倍。</p> <p style="padding-left: 120px"><img alt="" src="http://www.linuxidc.com/upload/2011_04/110403072730143.jpg" height="417" width="387" /></p> <p style="padding-left: 210px"><strong>图3 Task的执行模型</strong></p> <p style="padding-left: 30px"><br />&nbsp;&nbsp;&nbsp;  如果你熟悉设计模式，你会发现这是一个Command模式，将创建于执行的环境相分离，在一个线程中创建行为，在另一个线程中执行行为。Command模 式的优点在于，将实现操作与构造操作解耦，这就避免了锁的问题，使得多线程与单线程编程模型统一起来，其次，Command还有一个优点，就是有利于命令 的组合和扩展，在Chrome中，它有效统一了同步和异步处理的逻辑。</p> <p style="padding-left: 60px"><span style="color: #808080"><strong>Command模式<br /></strong></span>Command 模式，是一种看上去很酷的模式，传统的面向对象编程，我们封装的往往都是数据，在Command模式下，我们希望封装的是行为。这件事在函数式编程中很正 常，封装一个函数作为参数，传来传去，稀疏平常的事儿；但在面向对象的编程中，我们需要通过继承、模板、函数指针等手法，才能将其实现。</p> <p style="padding-left: 60px">应用Command模式，我们是期望这个行为能到一个不同于它出生的环境中去执行，简而言 之，这是一种想生不想养的行为。我们做Undo/Redo的时候，会把在任一一个环境中创建的Command，放到一个队列环境中去，供统一的调度；在 Chrome中，也是如此，我们在一个线程环境中创建了Task，却把它放到别的线程中去执行，这种寄居蟹似的生活方式，在很多场合都是有用武之地的。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  在一般的多线程模型中，我们需要分清楚啥是同步啥是异步，在同步模式下，一切看上去和单线程没啥区别，但同时也丧失了多线程的优势（沦落成为多线程串 行&#8230;）。而如果采用异步的模式，那写起来就麻烦多了，你需要注册回调，小心管理对象的生命周期，程序写出来是嗷嗷恶心。在Chrome的多线程模型下，同 步和异步的编程模型区别就不复存在了，如果是这样一个场景：A线程需要B线程做一些事情，然后回到A线程继续做一些事情；在Chrome下你可以这样来 做：生成一个Task，放到B线程的队列中，在该Task的Run方法最后，会生成另一个Task，这个Task会放回到A的线程队列，由A来执行。如此 一来，同步异步，天下一统，都是Task传来传去，想不会，都难了。</p> <p style="padding-left: 120px"><img alt="" src="http://www.linuxidc.com/upload/2011_04/110403072730144.jpg" height="304" width="417" /></p> <p style="padding-left: 210px"><strong>图4 Chrome的一种异步执行的解决方案</strong></p> <p style="padding-left: 30px"><br /><strong>4. Chrome多线程模型的优缺点<br /></strong>&nbsp;&nbsp;&nbsp;   一直在说Chrome在规避锁的问题，那到底锁是哪里不好，犯了何等滔天罪责，落得如此人见人嫌恨不得先杀而后快的境地。《代码之美》的第二十四章&#8220;美丽 的并发&#8221;中，Haskell设计人之一的Simon Peyton Jones总结了一下用锁的困难之处，如下：</p> <p style="padding-left: 30px">1.锁少加了，导致两个线程同时修改一个变量；<br />2.锁多加了，轻则妨碍并发，重则导致死锁；<br />3.锁加错了，由于锁和需要锁的数据之间的联系，只存在于程序员的大脑中，这种事情太容易发生了；<br />4.加锁的顺序错了，维护锁的顺序是一件困难而又容易出错的问题；<br />5.错误恢复；<br />6.忘记唤醒和错误的重试；<br />7. 而最根本的缺陷，是锁和条件变量不支持模块化的编程。比如一个转账业务中，A账户扣了100元钱，B账户增加了100元，即使这两个动作单独用锁保护维持 其正确性，你也不能将两个操作简单的串在一起完成一个转账操作，你必须让它们的锁都暴露出来，重新设计一番。好好的两个函数，愣是不能组在一起用，这就是 锁的最大悲哀；</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 通过这些缺点的描述，也就可以明白Chrome多线程模型的优点。它解决了锁的最根本缺陷，即，支持模块化的编程，你只需要维护对象和线程之间的职能关系即可，这个摊子，比之锁的那个烂摊子，要简化了太多。对于程序员来说，负担一瞬间从泰山降成了鸿毛。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 而Chrome多线程模型的一个主要难点，在于线程与数据关系的设计上，你需要良好的划分各个线程的职责，如果有一个线程所管辖的数据，几乎占据了大半部分的Task，那么它就会从多线程沦为单线程，Task队列的锁也将成为一个大大的瓶颈。</p> <p style="padding-left: 60px"><span style="color: #808080"><strong>设计者的职责<br /></strong></span>一 个底层结构设计是否成功，这个设计者是否称职，我一直觉得是有一个很简单的衡量标准的。你不需要看这个设计人用了多少NB的技术，你只需要关心，他的设 计，是否给其他开发人员带来了困难。一个NB的设计，是将所有困难都集中在底层搞定，把其他开发人员换成白痴都可以工作的那种；一个SB的设计，是自己弄 了半天，只是为了给其他开发人员一个长达250条的注意事项，然后很NB的说，你们按照这个手册去开发，就不会有问题了。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  从根本上来说，Chrome的线程模型解决的是并发中的用户体验问题而不是联合工作的问题（参见我前面喷的&#8220;闲话并发&#8221;），它不是和Map/Reduce 那样将关注点放在数据和执行步骤的拆分上，而是放在线程和数据的对应关系上，这是和浏览器的工作环境相匹配的。设计总是和所处的环境相互依赖的，毕竟，在 客户端，不会和服务器一样，存在超规模的并发处理任务，而只是需要尽可能的改善用户体验，从这个角度来说，Chrome的多线程模型，至少看上去很美。</p> <p style="padding-left: 30px">&nbsp;</p> <p style="padding-left: 30px"><span style="font-size: medium"><strong>Chrome源码剖析【二】&#8212;&#8212; 进程通信</strong></span></p> <p style="padding-left: 30px"><span style="font-size: medium">【二】Chrome的进程间通信<br /></span><strong>1. Chrome进程通信的基本模式<br /></strong>&nbsp;&nbsp;&nbsp;  进程间通信，叫做IPC（Inter-Process  Communication）。Chrome最主要有三类进程，一类是Browser主进程，我们一直尊称它老人家为老大；还有一类是各个Render进 程，前面也提过了；另外还有一类一直没说过，是Plugin进程，每一个插件，在Chrome中都是以进程的形式呈现，等到后面说插件的时候再提罢了。 Render进程和Plugin进程都与老大保持进程间的通信，Render进程与Plugin进程之间也有彼此联系的通路，唯独是多个Render进程 或多个Plugin进程直接，没有互相联系的途径，全靠老大协调。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 进程与进程间通信，需要仰仗操作系统的特性，能玩的花着实不多，在Chrome中，用到的就是有名的<strong>管道</strong>（Named   Pipe），只不过，它用一个IPC::Channel类，封装了具体的实现细节。Channel可以有两种工作模式，一种是Client，一种是 Server，Server和Client分属两个进程，维系一个共同的管道名，Server负责创建该管道，Client会尝试连接该管道，然后双发往 各自管道缓冲区中读写数据（在Chrome中，用的是二进制流，异步IO&#8230;），完成通信。</p> <p style="padding-left: 60px"><strong><span style="color: #808080">管道名字的协商<br /></span></strong>在 Socket中，我们会事先约定好通信的端口，如果不按照这个端口进行访问，走错了门，会被直接乱棍打出门去的。与之类似，有名管道期望在两个进程间游 走，就需要拿一个两个进程都能接受的进门暗号，这个就是有名管道的名字。在Chrome中（windows下&#8230;），有名管道的名字格式都是：<a>\\.\pipe\chrome.ID</a>。其中的ID，自然是要求独一无二，比如：进程ID.实例地址.随机数。通常，这个ID是由一个Process生成（往往是Browser Process），然后在创建另一个进程的时候，作为命令行参数传进去，从而完成名字的协商。</p> <p style="padding-left: 60px">如果不了解并期待了解有关Windows下有名管道和信号量的知识，建议去看一些专业的书 籍，比如圣经级别的《Windows核心编程》和《深入解析Windows操作系统》，当然也可以去查看SDK，你需要了解的API可能包 括：CreateNamedPipe, CreateFile, ConnectNamedPipe, WaitForMultipleObjects,  WaitForSingleObject, SetEvent, 等等。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  Channel中，有三个比较关键的角色，一个是Message::Sender，一个是Channel::Listener，最后一个是 MessageLoopForIO::Watcher。Channel本身派生自Sender和Watcher，身兼两角，而Listener是一个抽象 类，具体由Channel的使用者来实现。顾名思义，Sender就是发送消息的接口，Listener就是处理接收到消息的具体实现，但这个 Watcher是啥？如果你觉得Watcher这东西看上去很眼熟的话，我会激动的热泪盈眶的，没错，在前面（第一部分第一小节&#8230;）说消息循环的时候，从 那个表中可以看到，IO线程（记住，在Chrome中，IO指的是网络IO，*_*）的循环会处理注册了的Watcher。其实Watcher很简单，可 以视为一个信号量和一个带有OnObjectSignaled方法对象的对，当消息循环检测到信号量开启，它就会调用相应的 OnObjectSignaled方法。</p> <p style="padding-left: 60px"><img alt="" src="http://www.linuxidc.com/upload/2011_04/110403072730147.jpg" height="626" width="606" /></p> <p style="padding-left: 270px"><strong>图5 Chrome的IPC处理流程图</strong></p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  一图解千语，如上图所示，整个Chrome最核心的IPC流程都在图上了，期间，刨去了一些错误处理等逻辑，如果想看原汁原味的，可以自查Channel 类的实现。当有消息被Send到一个发送进程的Channel的时候，Channel会把它放在发送消息队列中，如果此时还正在发送以前的消息（发送端被 阻塞&#8230;），则看一下阻塞是否解除（用一个等待0秒的信号量等待函数&#8230;），然后将消息队列中的内容序列化并写道管道中去。操作系统会维护异步模式下管道的这 一组信号量，当消息从发送进程缓冲区写到接收进程的缓冲区后，会激活接收端的信号量。当接收进程的消息循环，循到了检查Watcher这一步，并发现有信 号量激活了，就会调用该Watcher相应的OnObjectSignaled方法，通知接受进程的Channel，有消息来了！Channel会尝试从 管道中收字节，组消息，并调用Listener来解析该消息。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  从上面的描述不难看出，Chrome的进程通信，最核心的特点，就是利用消息循环来检查信号量，而不是直接让管道阻塞在某信号量上。这样就与其多线程模型 紧密联系在了一起，用一种统一的模式来解决问题。并且，由于是消息循环统一检查，线程不会随便就被阻塞了，可以更好的处理各种其他工作，从理论上讲，这是 通过增加CPU工作时间，来换取更好的体验，颇有资本家的派头。</p> <p style="padding-left: 60px"><strong><span style="color: #808080">温柔的消息循环<br /></span></strong>其实，Chrome的很多消息循环，也不是都那么霸道，也是会被阻塞在某些信号量或者某种场景上的，毕竟客户端不是它家的服务器，CPU不能被全部归在它家名下。</p> <p style="padding-left: 60px">比如IO线程，当没有消息来到，又没有信号量被激活的时候，就会被阻塞，具体实现可以去看MessagePumpForIO的WaitForWork方法。</p> <p style="padding-left: 60px">不过这种阻塞是集中式的，可随时修改策略的，比起Channel直接阻塞在信号量上，停工的时间更短。</p> <p style="padding-left: 30px"><br /><strong>2. 进程间的跨线程通信和同步通信<br /></strong>&nbsp;&nbsp;&nbsp;   在Chrome中，任何底层的数据都是线程非安全的，Channel不是太上老君（抑或中国足球？&#8230;），它也没有例外。在每一个进程中，只能有一个线程来 负责操作Channel，这个线程叫做IO线程（名不符实真是一件悲凉的事情&#8230;）。其它线程要是企图越俎代庖，是会出大乱子的。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  但是有时候（其实是大部分时候&#8230;），我们需要从非IO线程与别的进程相通信，这该如何是好？如果，你有看过我前面写的线程模型，你一定可以想到，做法很简 单，先将对Channel的操作放到Task中，将此Task放到IO线程队列里，让IO线程来处理即可。当然，由于这种事情发生的太频繁，每次都人肉做 一次颇为繁琐，于是有一个代理类，叫做ChannelProxy，来帮助你完成这一切。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  从接口上看，ChannelProxy的接口和Channel没有大的区别（否则就不叫Proxy了&#8230;），你可以像用Channel一样，用 ChannelProxy来Send你的消息，ChannelProxy会辛勤的帮你完成剩余的封装Task等工作。不仅如此，ChannelProxy 还青出于蓝胜于蓝，在这个层面上做了更多的事情，比如：发送同步消息。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  不过能发送同步消息的类不是ChannelProxy，而是它的子类，SyncChannel。在Channel那里，所有的消息都是异步的（在 Windows中，也叫Overlapped&#8230;），其本身也不支持同步逻辑。为了实现同步，SyncChannel并没有另造轮子，而只是在 Channel的层面上加了一个等待操作。当ChannelProxy的Send操作返回后，SyncChannel会把自己阻塞在一组信号量上，等待回 包，直到永远或超时。从外表上看同步和异步没有什么区别，但在使用上还是要小心，在UI线程中使用同步消息，是容易被发指的。</p> <p style="padding-left: 30px"><br /><strong>3. Chrome中的IPC消息格式<br /></strong>&nbsp;&nbsp;&nbsp;   说了半天，还有一个大头没有提过，那就是消息包。如果说，多线程模式下，对数据的访问开销来自于锁，那么在多进程模式下，大部分的额外开销都来自于进程间 的消息拆装和传递。不论怎么样的模式，只要进程不同，消息的打包，序列化，反序列化，组包，都是不可避免的工作。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  在Chrome中，IPC之间的通信消息，都是派生自IPC::Message类的。对于消息而言，序列化和反序列化是必须要支持的，Message的基 类Pickle，就是干这个活的。Pickle提供了一组的接口，可以接受int，char，等等各种数据的输入，但是在Pickle内部，所有的一切都 没有区别，都转化成了一坨二进制流。这个二进制流是32位齐位的，比如你只传了一个bool，也是最少占32位的，同时，Pickle的流是有自增逻辑的 （就是说它会先开一个Buffer，如果满了的话，会加倍这个Buffer&#8230;），使其可以无限扩展。Pickle本身不维护任何二进制流逻辑上的信息，这 个任务交到了上级处理（后面会有说到&#8230;），但Pickle会为二进制流添加一个头信息，这个里面会存放流的长度，Message在继承Pickle的时 候，扩展了这个头的定义，完整的消息格式如下：</p> <p style="padding-left: 60px"><img alt="" src="http://www.linuxidc.com/upload/2011_04/110403072730146.jpg" height="96" width="644" /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>&nbsp;&nbsp;图6 Chrome的IPC消息格式</strong></p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  其中，黄色部分是包头，定长96个bit，绿色部分是包体，二进制流，由payload_size指明长度。从大小上看这个包是很精简的了，除了 routing位在消息不为路由消息的时候会有所浪费。消息本身在有名管道中是按照二进制流进行传输的（有名管道可以传输两种类型的字符流，分别是二进制 流和消息流&#8230;），因此由payload_size + 96bits，就可以确定是否收了一个完整的包。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 从逻辑上来看，IPC消息分成两类，一类是路由消息（routed  message），还有一类是控制消息（control  message）。路由消息是私密的有目的地的，系统会依照路由信息将消息安全的传递到目的地，不容它人窥视；控制消息就是一个广播消息，谁想听等能够听 得到。</p> <p style="padding-left: 60px"><strong><span style="color: #808080">消息的序列化<br /></span></strong>前不久读了Google Protocol Buffers的源码，是用在服务器端，用做内部机器通信协议的标准、代码生成工具和框架。它主要的思想是揉合了key/value的内容到二进制中，帮助生成更为灵活可靠的二进制协议。</p> <p style="padding-left: 60px">在Chrome中，没有使用这套东西，而是用到了纯二进制流作为消息序列化的方式。我想这 是由于应用场景不同使然。在服务端，我们更关心协议的稳定性，可扩展性，并且，涉及到的协议种类很多。但在一个Chrome中，消息的格式很统一，这方面 没有扩展性和灵活性的需求，而在序列化上，虽然key/value的方式很好很强大，但是在Chrome中需要的不是灵活性而是精简性，因此宁可不用 Protocol Buffers造好的轮子，而是另立炉灶，花了好一把力气提供了一套纯二进制的消息机制。<br />&nbsp;</p> <p style="padding-left: 30px"><strong>4. 定义IPC消息<br /></strong>&nbsp;&nbsp;&nbsp;  如果你写过MFC程序，对MFC那里面一大堆宏有所忌惮的话，那么很不幸，在Chrome中的IPC消息定义中，你需要再吃一点苦头了，甚至，更苦大仇深 一些；如果你曾经领教过用模板的特化偏特化做Traits、用模板做函数重载、用编译期的Tuple做变参数支持，之类机制的种种麻烦的话，那么，同样很 遗憾，在Chrome中，你需要再感受一次。。。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 不过，先让我们忘记宏和模板，看人肉一个消息，到底需要哪些操作。一个标准的IPC消息定义应该是类似于这样的：</p> <p style="padding-left: 60px">class SomeMessage: public IPC::Message<br />{<br />&nbsp; public:<br />&nbsp;&nbsp;&nbsp; enum { ID = &#8230;; }<br />&nbsp;&nbsp;&nbsp; SomeMessage(SomeType &amp; data)<br />&nbsp;&nbsp;&nbsp; : IPC::Message(MSG_ROUTING_CONTROL, ID, ToString(data))<br />&nbsp;&nbsp;&nbsp; {&#8230;}<br />&nbsp;&nbsp;&nbsp; &#8230;<br />};</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 大概意思是这样的，你需要从Message（或者其他子类）派生出一个子类，该子类有一个独一无二的ID值，该子类接受一个参数，你需要对这个参数进行序列化。两个麻烦的地方看的很清楚，如果生成独一无二的ID值？如何更方便的对任何参数可以自动的序列化？。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 在Chrome中，解决这两个问题的答案，就是宏 +  模板。Chrome为每个消息安排了一种ID规格，用一个16bits的值来表示，高4位标识一个Channel，低12位标识一个消息的子id，也就是 说，最多可以有16种Channel存在不同的进程之间，每一种Channel上可以定义4k的消息。目前，Chrome已经用掉了8种 Channel（如果A、B进程需要双向通信，在Chrome中，这是两种不同的Channel，需要定义不同的消息，也就是说，一种双向的进程通信关 系，需要耗费两个Channel种类&#8230;），他们已经觉得，16bits的ID格式不够用了，在将来的某一天，估计就被扩展成了32bits的。书归正 传，Chrome是这么来定义消息ID的，用一个枚举类，让它从高到低往下走，就像这样：</p> <p style="padding-left: 60px">enum SomeChannel_MsgType<br />{<br />&nbsp; SomeChannelStart = 5 &lt;&lt; 12,<br />&nbsp; SomeChannelPreStart = (5 &lt;&lt; 12) &#8211; 1,<br />&nbsp; Msg1,<br />&nbsp; Msg2,<br />&nbsp; Msg3,<br />&nbsp; &#8230;<br />&nbsp; MsgN,<br />&nbsp; SomeChannelEnd<br />};</p> <p style="padding-left: 30px"><br />&nbsp;&nbsp;&nbsp;  这是一个类型为5的Channel的消息ID声明，由于指明了最开始的两个值，所以后续枚举的值会依次递减，如此，只要维护Channel类型的唯一性， 就可以维护所有消息ID的唯一性了（当然，前提是不能超过消息上限&#8230;）。但是，定义一个ID还不够，你还需要定义一个使用该消息ID的Message子 类。这个步骤不但繁琐，最重要的，是违反了DIY原则，为了添加一个消息，你需要在两个地方开工干活，是可忍孰不可忍，于是Google祭出了宏这颗原子 弹，需要定义消息，格式如下：</p> <p style="padding-left: 60px">IPC_BEGIN_MESSAGES(PluginProcess, 3)<br />IPC_MESSAGE_CONTROL2(PluginProcessMsg_CreateChannel,<br />int /* process_id */,<br />HANDLE /* renderer handle */)<br />IPC_MESSAGE_CONTROL1(PluginProcessMsg_ShutdownResponse,<br />bool /* ok to shutdown */)<br />IPC_MESSAGE_CONTROL1(PluginProcessMsg_PluginMessage,<br />std::vector&lt;uint8&gt; /* opaque data */)<br />IPC_MESSAGE_CONTROL0(PluginProcessMsg_BrowserShutdown)<br />IPC_END_MESSAGES(PluginProcess)</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  这是Chrome中，定义PluginProcess消息的宏，我挖过来放在这了，如果你想添加一条消息，只需要添加一条类似与 IPC_MESSAGE_CONTROL0东东即可，这说明它是一个控制消息，参数为0个。你基本上可以这样理解，IPC_BEGIN_MESSAGES 就相当于完成了一个枚举开始的声明，然后中间的每一条，都会在枚举里面增加一个ID，并声明一个子类。这个一宏两吃，直逼北京烤鸭两吃的高超做法，可以参 看ipc_message_macros.h，或者看下面一宏两吃的一个举例。</p> <p style="padding-left: 60px"><span style="color: #808080"><span style="color: #000000"><strong><span style="color: #999999">多次展开宏的技巧<br /></span></strong>这是Chrome中用到的一个技巧，定义一次宏，展开多段代码，我孤陋寡闻，第一次见，一个类似的例子，如下：</span></span></p> <p style="padding-left: 60px">首先，定义一个macro.h，里面放置宏的定义：<br />#undef SUPER_MACRO<br />#if defined(FIRST_TIME)<br />#undef FIRST_TIME<br />#define SUPER_MACRO(label, type) \</p> <p style="padding-left: 60px">enum IDs { \<br />&nbsp; label##__ID = 10 \<br />};</p> <p style="padding-left: 60px">#elif defined(SECOND_TIME)<br />#undef SECOND_TIME</p> <p style="padding-left: 60px">#define SUPER_MACRO(label, type) \<br />class TestClass \<br />{ \<br />&nbsp; public: \<br />&nbsp;&nbsp;&nbsp; enum {ID = label##__ID}; \<br />&nbsp;&nbsp;&nbsp; TestClass(type value) : _value(value) {} \<br />&nbsp;&nbsp;&nbsp; type _value; \<br />};<br />#endif</p> <p style="padding-left: 60px">可以看到，这个头文件是可重入的，每一次先undef掉之前的定义，然后判断进行新的定义。然后，你可以创建一个use_macro.h文件，利用这个宏，定义具体内容：</p> <p style="padding-left: 60px">#include &#8220;macros.h&#8221;<br />SUPER_MACRO(Test, int)</p> <p style="padding-left: 60px">这个头文件在利用宏的部分不需要放到ifundef&#8230;define&#8230;这样的头文件保护中，目的就是为了可重入。在主函数中，你可以多次define + include，实现多次展开的目的：</p> <p style="padding-left: 60px">#define FIRST_TIME<br />#include &#8220;use_macro.h&#8221;<br />#define SECOND_TIME<br />#include &#8220;use_macro.h&#8221;<br />#include &lt;iostream&gt;<br />int _tmain(int argc, _TCHAR* argv[])<br />{<br />&nbsp; TestClass t(5);<br />&nbsp; std::cout &lt;&lt; TestClass::ID &lt;&lt; std::endl;<br />&nbsp; std::cout &lt;&lt; t._value &lt;&lt; std::endl;<br />&nbsp; return 0;<br />}</p> <p style="padding-left: 60px">这样，你就成功的实现，一次定义，生成多段代码了。</p> <p style="padding-left: 60px">此外，当接收到消息后，你还需要处理消息。接收消息的函数，是 IPC::Channel::Listener子类的OnMessageReceived函数。在这个函数中，会放置一坨的宏，这一套宏，一定能让你想起 MFC的Message Map机制（关于此消息机制原理更具体的介绍，可参考侯捷的深入浅出MFC一书。）：</p> <p style="padding-left: 60px">IPC_BEGIN_MESSAGE_MAP_EX(RenderProcessHost, msg, msg_is_ok)</p> <p style="padding-left: 60px">IPC_MESSAGE_HANDLER(ViewHostMsg_PageContents, OnPageContents)<br />IPC_MESSAGE_HANDLER(ViewHostMsg_UpdatedCacheStats,<br />OnUpdatedCacheStats)<br />IPC_MESSAGE_UNHANDLED_ERROR()<br />IPC_END_MESSAGE_MAP_EX()</p> <p style="padding-left: 30px">&nbsp;</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 这个东西很简单，展开后基本可以视为一个Switch循环，判断消息ID，然后将消息，传递给对应的函数。与MFC的Message Map比起来，做的事情少多了。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  通过宏的手段，可以解决消息类声明和消息的分发问题，但是自动的序列化还不能支持（所谓自动的序列化，就是不论你是什么类型的参数，几个参数，都可以直接 序列化，不需要另写代码&#8230;）。在C++这种语言中，所谓自动的序列化，自动的类型识别，自动的XXX，往往都是通过模板来实现的。这些所谓的自动化，其实 就是通过事前的大量人肉劳作，和模板自动递推来实现的，如果说.Net或Java中的自动序列化是过山轨道，这就是那挑夫的骄子，虽然最后都是两腿不动到 了山顶，这底下费得力气真是天壤之别啊。具体实现技巧，有兴趣的看看侯捷的《STL源码剖析》，或者是《C++新思维》，或者Chrome中的 ipc_message_utils.h，这要说清楚实在不是一两句的事情。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  总之通过宏和模板，你可以很简单的声明一个消息，这个消息可以传入各式各样的参数（这里用到了夸张的修辞手法，其实，只要是模板实现的自动化，永远都是有 限制的，在Chrome的模板实现中，参数数量不要超过5个，类型需要是基本类型、STL容器等，在不BT的场合，应该够用了&#8230;），你可以调用 Channel、ChannelProxy、SyncChannel之类的Send方法，将消息发送给其他进程，并且，实现一个Listener类，用 Message Map来分发消息给对应的处理函数。如此，整个IPC体系搭建完成。</p> <p style="padding-left: 60px"><strong><span style="color: #808080">苦力的宏和模板<br /></span></strong>不论是宏还是模板，为了实现这套机制，都需要写大量的类似代码，比如为了支持0~N个参数的Control消息，你就需要写N+1个类似的宏；为了支持各种基础数据结构的序列化，你就需要写上十来个类似的Write函数和Traits。</p> <p style="padding-left: 60px">之所以做如此苦力的活，都是为了用这些东西的人能够尽可能的简单方便，符合DIY原则。规 约到之前说的设计者的职责上来，这是一个典型的苦了我一个幸福千万人的负责任的行为。在Chrome中，如此的代码随处可见，光Tuple那一套拳法，我 现在就看到了使了不下三次（我曾经做过一套，直接吐血&#8230;），如此兢兢业业，真是可歌可泣啊。<br />&nbsp;</p> <p style="padding-left: 30px"><strong><span style="font-size: medium">【三】 Chrome的进程模型<br /></span>1. 基本的进程结构<br /></strong>&nbsp;&nbsp;&nbsp;   Chrome是一个多进程的架构，不过所有的进程都会由老大，Browser进程来管理，走的是集中化管理的路子。在Browser进程中，有 xxxProcessHost，每一个host，都对应着一个Process，比如RenderProcessHost对应着 RenderProcess，PluginProcessHost对应着PluginProcess，有多少个host的实例，就有多少个进程在运行。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 这是一个比较典型的代理模式，Browser对Host的操作，都会被Host封装成IPC消息，传递给对应的Process来处理，对于大部分上层的类，也就隔离了多进程细节。</p> <p style="padding-left: 30px"><br /><strong>2. Render进程<br /></strong>&nbsp;&nbsp;&nbsp;  先不扯Plugin的进程，只考虑Render进程。前面说了，一个Process一个tab，只是广告用语，实际上，每一个web页面内容（包括在 tab中的和在弹出窗口中的&#8230;），在Chrome中，用RenderView表示一个web页面，每一个RenderView可以寄宿在任一一个 RenderProcess中，它只是依托RenderProcess帮助它进行通信。每一个RenderProcess进程都可以有1到N个 RenderView实例。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; Chrome支持不同的进程模型，可以一个tab一个进程，一个site  instance一个进程等等。但基本模式都是一致的，当需要创建一个新的RenderView的时候，Chrome会尝试进行选择或者是创建进程。比 如，在one site one  process的模式下，如果存在此site，就会选择一个已有的RenderProcessHost，让它管理这个新的RenderView，否则，会 创建一个RenderProcessHost（同时也就创建了一个Process），把RenderView交给它。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp; 在默认的one site instance one  process的模式中，Chrome会为每个新的site instance创建一个进程（从一个页面链开来的页面，属于同一个site  instance），但，Render进程总数是有个上限的。这个上限，根据内存大小的不同而异，比如，在我的机器上（2G内存），最多可以容纳20个 Render进程，当达到这个上限后，你再开新的网站，Chrome会随机为你选择一个已有的进程，把这个网站对应的RenderView给扔进去。。。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  每一次你新输入一个站点信息，在默认模式下，都必然导致一个进程的诞生，很可能，伴随着另一个进程的死亡（如果这个进程没有其他承载的 RenderView的话，他就自然死亡了，RenderView的个数，就相当于这个进程的引用计数&#8230;）。比如，你打开一个新标签页的时候，系统为你创 造了一个进程来承载这个新标签页，你输入<a href="http://www.baidu.com/" target="_blank"><span style="color: #002d93">http://www.baidu.com/</span></a>，于是新标签页进程死亡，承载<a href="http://www.baidu.com/" target="_blank"><span style="color: #002d93">http://www.baidu.com/</span></a>的进程诞生。你用baidu搜索了一下，毫无疑问，你基本对它的搜索结果很失望，于是你重新输入<a href="http://www.google.com.hk/" target="_blank"><span style="color: #002d93">http://www.google.com.hk/</span></a>， 老的承载baidu的进程死亡，承载google的进程被构建出来。这时候你想回退到之前baidu的搜索结果，乐呵乐呵的话，一个新的承载baidu的 进程被创造，之前Google的进程死亡。同样，你再次点击前进，又来到Google搜索结果的时候，一个新的进程有取代老的进程出现了。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  以上现象，你都可以自己来检验，通过观察about:memory页面的信息，你可以了解整个过程（记得每做一步，需要刷新一下about:memory 页面）。我唧唧歪歪说了半天，其实想表达的是，Chrome并没有像我YY的一样做啥进程池之类的特殊机制，而是简单的履行有就创建、没有就销毁的策略。 我并不知道有没有啥很有效的多进程模型，这方面一点都没玩过，猜测Chrome之所以采取这样的策略，是经过琢磨的，觉得进程生死的代价可以承受，比较可 行。</p> <p style="padding-left: 30px"><br /><strong>3. 进程开销控制算法<br /></strong>&nbsp;&nbsp;&nbsp; 说开销无外乎两方面的内容，一为时间，二则空间。Chrome没有在进程创建和销毁上做功夫，但是当进程运行起来后，还是做了一些工作的。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  节约工作首先从CPU耗时上做起，优先级越高的进程中的线程，越容易被调度，从而耗费CPU时间，于是，当一个页面不再直接面对用户的时候，Chrome 会将它的进程优先级切到Below Normal的级别，反之，则切回Normal级别。通过这个步骤，小节约了一把时间。</p> <p style="padding-left: 60px"><strong><span style="color: #808080">进程的优先级<br /></span></strong>在 windows中，进程是有优先级的，当然，这个优先级不是真实的调度优先级，而是该进程中，线程优先级计算的基准。在《Windows via  C/C++》（也就是《windows核心编程》的第五版）中，有一张详细的表，表述了线程优先级和进程优先级的具体对应关系，感觉设计的很不错，在此就 不再赘述了，有兴趣的自行动手翻书。</p> <p style="padding-left: 30px"><br />&nbsp;&nbsp;&nbsp;  当然这只是一道开胃小菜，满汉全席是控制进程的工作集大小，以达到降低进程实际内存消耗的目的（Chrome为了体现它对内存的节约，用了&#8220;更为精确&#8221;的 内存消耗计算方法&#8230;）。提到这一点，Chrome颇为自豪，在文档中，顺着道把单进程的模式鄙视了一下，基本意思是：在多进程的模式下，各个页面实际占用 的内存数量，更容易被控制，而在单进程的模式下，几乎是不能作出控制的，所以，很多时候，多进程模式耗费的内存，是会小于多线程模式的。这个说法靠不靠 谱，大家心里都有谱，就不多说了。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  具体说来，Chrome对进程工作集的控制算法还是比较简单的。首先，在进程启动的时候，需要指明进程工作的内存环境，是高内存，低内存，还是中等内存， 默认模式下，是中等内存（我以为Chrome会动态计算的，没想到竟然是启动时指定&#8230;）。在高内存模式，不存在对工作集的调整，使劲用就完事了；在低内存 的模式下，调整也很简单，一旦一个进程不再有页面面对观众了，尝试释放其所有工作集。相比来说，中等模式下，算法相对复杂一些，当一个进程从直接面对观 众，沦落到切换到后台的悲惨命运，其工作集会缩减，算法为： TargetWorkingSetSize = (LastWorkingSet/2 +  CurrentWorkingSet)  /2；其中，TargetWorkingSetSize指的是预期降到的工作集大小，CurrentWorkingSet指的是进程当前的工作集（在 Chrome中，工作集的大小，包含私有的和可共享的两部分内存，而不包含已经共享了的内存空间&#8230;），LastWorkingSet，等于上一次的 CurrentWorkingSet除以DampingFactor，默认的DampingFactor为2。而反之，当一个进程从幕后走向台前，它的工 作集会被放大为 LastWorkingSet * DampingFactor *  2，了解过LastWorkingSet的含义，你已经知道，这就是将工作集放大两倍的另类版写法。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  Chrome的Render进程工作集调整，除了发生在tab切换（或新页面建立）的时候，还会发生在整个Chrome的idle事件触发后。 Chrome有个计时器，统计Chrome空闲的时长，当时长超过30s后（此工作会反复进行&#8230;），Chrome会做一系列工作，其中就包括，调整进程的 工作集。被调整的进程，不仅仅是Render进程，还包括Plugin进程和Browser进程，换句话描述，就是所有Chrome进程。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  这个算法导致一个很悲凉的状况，当你去蹲了个厕所回到电脑前，切换了一个Chrome页面，你发现页面一片惨白，一阵硬盘的骚动过后，好不容易恢复了原 貌。如果再切，相同的事情又会发生，孜孜不倦，直到你切过每一个进程。这个惨案发生的主要原因，就是由于所有Chrome进程的工作集都被释放了，页面的 重载和Render需要不少的一坨时间，这就大大影响了用户感受，毕竟，总看到惨白的画面，容易产生不好的情绪。强烈感觉这个不算一个很出色的策略，应该 有一个工作集切换的底限，或者是在Chrome从idle中被激活的时候，偷偷摸摸的统一扩大工作集，发几个事件刺激一下，把该加载的东西加载起来。</p> <p style="padding-left: 30px">&nbsp;&nbsp;&nbsp;  整体感觉，Chrome对进程开销的控制，并不像想象中的有非常精妙绝伦的策略在里面，通过工作集这总手段并不算华丽，而且，如果想很好的工作的话，有一 个非常非常重要的前提，就是被切换的页面，很少再被继续浏览。个人觉得这个假设并不是十分可靠，这就使得在某些情况下，产生非常不好的用户体验，也许 Chrome需要进一步在这个地方琢磨点方法的。</p> <p style="padding-left: 30px">本文Chrome源码剖析、上，完。</p></div></div><img src ="http://www.blogjava.net/xiaomage234/aggbug/370122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2012-02-16 17:08 <a href="http://www.blogjava.net/xiaomage234/archive/2012/02/16/370122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用C/C++扩展你的PHP[转,很全面很具体]</title><link>http://www.blogjava.net/xiaomage234/archive/2009/09/01/293433.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Tue, 01 Sep 2009 04:21:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2009/09/01/293433.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/293433.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2009/09/01/293433.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/293433.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/293433.html</trackback:ping><description><![CDATA[<div id="copyright">
<p>&nbsp;</p>
&#183; 作者:laruence(<a title="风雪之隅" href="http://www.laruence.com/">http://www.laruence.com/)</a><br />
&#183; 本文地址: <a title="用C/C++扩展你的PHP" href="http://www.laruence.com/2009/04/28/719.html">http://www.laruence.com/2009/04/28/719.html</a><br />
&#183; 转载请注明出处 </div>
<!-- 转载声明结束 -->
<div id="xianguo_recommend"><script type="text/javascript" charset="utf8">cT="0";nc="#444444";nBgc="";nBorder="#196FCB";tc="#FFFFFF";tBgc="#19A1FE";tBorder="#196FCB";tDigg="%E6%8E%A8%E8%8D%90";tDugg="%E5%B7%B2%E8%8D%90";defaultItemUrl="WEB_URL";defaultFeedUrl ="http://www.laruence.com/feed";</script><script src="http://re.xianguo.com/api/diggthis.js" type="text/javascript" charset="utf8"></script><iframe name="xianguo_digg_frame" src="http://re.xianguo.com/diggButtonInterface.php?pageName=initDiggButton&amp;defaultFeedUrl=http%3A%2F%2Fwww.laruence.com%2Ffeed&amp;cT=0&amp;nc=%23444444&amp;nBorder=%23196FCB&amp;tc=%23FFFFFF&amp;tBgc=%2319A1FE&amp;tBorder=%23196FCB&amp;tDigg=%25E6%258E%25A8%25E8%258D%2590&amp;tDugg=%25E5%25B7%25B2%25E8%258D%2590" frameborder="0" width="89" scrolling="no" height="22"></iframe></div>
<style>
#article p{
text-indent:2em;
}
#article h3{
margin-left:auto;
}
#article table{
border: 1px #cccccc solid;
border-collapse:collapse;
}
#article td{
border: 1px #cccccc solid;
}
#article h2{
text-align:center;
}
</style>
<div class="hl-surround">
<div class="hl-main">翻译:taft at wjl.cn<br />
校对:laruence at yahoo.com.cn<br />
最后更新日期:2009/04/29</div>
</div>
<div id="article">
<h2>简 介</h2>
<p>PHP取得成功的一个主要原因之一是她拥有大量的可用扩展。web开发者无论有何种需求，这种需求最有可能在PHP发行包里找到。PHP发行包包括支持各种数据库，图形文件格式，压缩，XML技术扩展在内的许多扩展。</p>
<p>扩展API的引入使PHP3取得了巨大的进展，扩展API机制使PHP开发社区很容易的开发出几十种扩展。现在，两个版本过去了，API仍然和PHP3时的非常相似。扩展主要的思想是：尽可能的从扩展编写者那里隐藏PHP的内部机制和脚本引擎本身，仅仅需要开发者熟悉API。</p>
<p>有两个理由需要自己编写PHP扩展。第一个理由是：PHP需要支持一项她还未支持的技术。这通常包括包裹一些现成的C函数库，以便提供PHP接口。例如，如果一个叫FooBase的数据库已推出市场，你需要建立一个PHP扩展帮助你从PHP里调用FooBase的C函数库。这个工作可能仅由一个人完成，然后被整个PHP社区共享（如果你愿意的话）。第二个不是很普遍的理由是：你需要从性能或功能的原因考虑来编写一些商业逻辑。 </p>
<p>如果以上的两个理由都和你没什么关系，同时你感觉自己没有冒险精神，那么你可以跳过本章。</p>
<p>本章教你如何编写相对简单的PHP扩展，使用一部分扩展API函数。对于大多数打算开发自定义PHP扩展开发者而言，它含概了足够的资料。学习一门编程课程的最好方法之一就是动手做一些极其简单的例子，这些例子正是本章的线索。一旦你明白了基础的东西，你就可以在互联网上通过阅读文挡、原代码或参加邮件列表新闻组讨论来丰富自己。因此，本章集中在让你如何开始的话题。在UNIX下一个叫ext_skel的脚本被用于建立扩展的骨架，骨架信息从一个描述扩展接口的定义文件中取得。因此你需要利用UNIX来建立一个骨架。Windows开发者可以使用Windows ext_skel_win32.php代替ext_skel。</p>
<p>然而，本章关于用你开发的扩展编译PHP的指导仅涉及UNIX编译系统。本章中所有的对API的解释与UNIX和Windows下开发的扩展都有联系。 </p>
<p>当你阅读完这章，你能学会如何</p>
<ul>
    <li>建立一个简单的商业逻辑扩展。
    <li>建议个C函数库的包裹扩展，尤其是有些标准C文件操作函数比如fopen() </li>
</ul>
<p>
<h3>快速开始</h3>
<p>本节没有介绍关于脚本引擎基本构造的一些知识，而是直接进入扩展的编码讲解中，因此不要担心你无法立刻获得对扩展整体把握的感觉。假设你正在开发一个网站，需要一个把字符串重复n次的函数。下面是用PHP写的例子： </p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline"><span style="color: green">function</span><span style="color: gray">&nbsp;</span><span style="color: blue">self_concat</span><span style="color: olive">(</span><span style="color: #00008b">$string</span><span style="color: gray">, </span><span style="color: #00008b">$n</span><span style="color: olive">){</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: #00008b">$result</span><span style="color: gray"> = </span><span style="color: #8b0000">""</span><span style="color: gray">;</span>
    <li><span style="color: green">for</span><span style="color: olive">(</span><span style="color: #00008b">$i</span><span style="color: gray"> = </span><span style="color: maroon">0</span><span style="color: gray">; </span><span style="color: #00008b">$i</span><span style="color: gray"> &lt; </span><span style="color: #00008b">$n</span><span style="color: gray">; </span><span style="color: #00008b">$i</span><span style="color: gray">++</span><span style="color: olive">){</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: #00008b">$result</span><span style="color: gray"> .= </span><span style="color: #00008b">$string</span><span style="color: gray">;</span>
    <li><span style="color: olive">}</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: green">return</span><span style="color: gray">&nbsp;</span><span style="color: #00008b">$result</span><span style="color: gray">;</span>
    <li><span style="color: olive">}</span>
    <li><span style="color: gray">&nbsp;</span>
    <li><span style="color: blue">self_concat</span><span style="color: olive">(</span><span style="color: #8b0000">"</span><span style="color: red">One</span><span style="color: #8b0000">"</span><span style="color: gray">, </span><span style="color: maroon">3</span><span style="color: olive">)</span><span style="color: gray">&nbsp;</span><span style="color: blue">returns</span><span style="color: gray"> </span><span style="color: #8b0000">"</span><span style="color: red">OneOneOne</span><span style="color: #8b0000">"</span><span style="color: gray">.</span>
    <li><span style="color: gray">&nbsp;</span>
    <li><span style="color: blue">self_concat</span><span style="color: olive">(</span><span style="color: #8b0000">"</span><span style="color: red">One</span><span style="color: #8b0000">"</span><span style="color: gray">, </span><span style="color: maroon">1</span><span style="color: olive">)</span><span style="color: gray">&nbsp;</span><span style="color: blue">returns</span><span style="color: gray"> </span><span style="color: #8b0000">"</span><span style="color: red">One</span><span style="color: #8b0000">"</span><span style="color: gray">.</span> </li>
</ol>
</div>
<p>假设由于一些奇怪的原因，你需要时常调用这个函数，而且还要传给函数很长的字符串和大值n。这意味着在脚本里有相当巨大的字符串连接量和内存重新分配过程，以至显著地降低脚本执行速度。如果有一个函数能够更快地分配大量且足够的内存来存放结果字符串，然后把$string重复n次，就不需要在每次循环迭代中分配内存。</p>
<p>为扩展建立函数的第一步是写一个函数定义文件，该函数定义文件定义了扩展对外提供的函数原形。该例中，定义函数只有一行函数原形self_concat() :</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">string self_concat(string str, int n)</li>
</ol>
</div>
<p>函数定义文件的一般格式是一个函数一行。你可以定义可选参数和使用大量的PHP类型，包括: bool, float, int, array等。</p>
<p>保存为myfunctions.def文件至PHP原代码目录树下。 </p>
<p>该是通过扩展骨架(skeleton)构造器运行函数定义文件的时机了。该构造器脚本叫ext_skel，放在PHP原代码目录树的ext/目录下（PHP原码主目录下的README.EXT_SKEL提供了更多的信息）。假设你把函数定义保存在一个叫做myfunctions.def的文件里，而且你希望把扩展取名为myfunctions，运行下面的命令来建立扩展骨架</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">./ext_skel --extname=myfunctions --proto=myfunctions.def</li>
</ol>
</div>
<p>这个命令在ext/目录下建立了一个myfunctions/目录。你要做的第一件事情也许就是编译该骨架，以便编写和测试实际的C代码。编译扩展有两种方法：</p>
<ul>
    <li>作为一个可装载模块或者DSO（动态共享对象）
    <li>静态编译到PHP </li>
</ul>
<div class="wp-caption alignnone" id="attachment_738" style="width: 310px"><a href="http://www.laruence.com/wp-content/uploads/php_extension.jpg"><img class="size-medium wp-image-738" title="PHP扩展开发导图" height="213" alt="PHP扩展开发导图" src="http://www.laruence.com/wp-content/uploads/php_extension-300x213.jpg" width="300" /></a>
<p class="wp-caption-text">PHP扩展开发导图</p>
</div>
<p>因为第二种方法比较容易上手，所以本章采用静态编译。如果你对编译可装载扩展模块感兴趣，可以阅读PHP原代码根目录下的README.SELF-CONTAINED_EXTENSIONS文件。为了使扩展能够被编译，需要修改扩展目录ext/myfunctions/下的config.m4文件。扩展没有包裹任何外部的C库，你需要添加支持&#8211;enable-myfunctions配置开关到PHP编译系统里（&#8211;with-extension 开关用于那些需要用户指定相关C库路径的扩展）。可以去掉自动生成的下面两行的注释来开启这个配置。</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">./ext_skel --extname=myfunctions --proto=myfunctions.def
    <li>PHP_ARG_ENABLE(myfunctions, whether to enable myfunctions support,
    <li>&nbsp;
    <li>[ --enable-myfunctions&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Include myfunctions support])</li>
</ol>
</div>
<p>&nbsp;</p>
<p>现在剩下的事情就是在PHP原代码树根目录下运行./buildconf，该命令会生成一个新的配置脚本。通过查看./configure &#8211;help输出信息，可以检查新的配置选项是否被包含到配置文件中。现在，打开你喜好的配置选项开关和&#8211;enable-myfunctions重新配置一下PHP。最后的但不是最次要的是，用make来重新编译PHP。</p>
<p>ext_skel应该把两个PHP函数添加到你的扩展骨架了：打算实现的self_concat()函数和用于检测myfunctions 是否编译到PHP的confirm_myfunctions_compiled()函数。完成PHP的扩展开发后，可以把后者去掉。</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline"><span style="color: blue">&lt;?php</span>
    <li><span style="color: blue">print</span><span style="color: gray">&nbsp;</span><span style="color: blue">confirm_myfunctions_compiled</span><span style="color: olive">(</span><span style="color: #8b0000">"</span><span style="color: red">myextension</span><span style="color: #8b0000">"</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: blue">?&gt;</span></li>
</ol>
</div>
<p>运行这个脚本会出现类似下面的输出：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">"Congratulations! You have successfully modified ext/myfunctions
    <li>&nbsp;
    <li>config.m4. Module myfunctions is now compiled into PHP."</li>
</ol>
</div>
<p>另外，ext_skel脚本生成一个叫myfunctions.php的脚本，你也可以利用它来验证扩展是否被成功地编译到PHP。它会列出该扩展所支持的所有函数。 </p>
<p>现在你学会如何编译扩展了，该是真正地研究self_concat()函数的时候了。<br />
下面就是ext_skel脚本生成的骨架结构：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">/* {{{ proto string self_concat(string str, int n)
    <li>&nbsp;
    <li>*/
    <li>&nbsp;
    <li>PHP_FUNCTION(self_concat)
    <li>&nbsp;
    <li>}
    <li>&nbsp;
    <li>char *str = NULL;
    <li>&nbsp;
    <li>int argc = ZEND_NUM_ARGS();
    <li>&nbsp;
    <li>int str_len;
    <li>&nbsp;
    <li>long n;
    <li>&nbsp;
    <li>if (zend_parse_parameters(argc TSRMLS_CC, "sl", &amp;str, &amp;str_len, &amp;n) == FAILURE)
    <li>&nbsp;
    <li>return;
    <li>&nbsp;
    <li>php_error(E_WARNING, "self_concat: not yet implemented");
    <li>&nbsp;
    <li>}
    <li>&nbsp;
    <li>/* }}} */</li>
</ol>
</div>
<p>&nbsp;</p>
<p>自动生成的PHP函数周围包含了一些注释，这些注释用于自动生成代码文档和vi、Emacs等编辑器的代码折叠。函数自身的定义使用了宏PHP_FUNCTION()，该宏可以生成一个适合于Zend引擎的函数原型。逻辑本身分成语义各部分，取得调用函数的参数和逻辑本身。</p>
<p>为了获得函数传递的参数，可以使用zend_parse_parameters()API函数。下面是该函数的原型：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, &#8230;);</li>
</ol>
</div>
<p>第一个参数是传递给函数的参数个数。通常的做法是传给它ZEND_NUM_ARGS()。这是一个表示传递给函数参数总个数的宏。第二个参数是为了线程安全，总是传递TSRMLS_CC宏，后面会讲到。第三个参数是一个字符串，指定了函数期望的参数类型，后面紧跟着需要随参数值更新的变量列表。因为PHP采用松散的变量定义和动态的类型判断，这样做就使得把不同类型的参数转化为期望的类型成为可能。例如，如果用户传递一个整数变量，可函数需要一个浮点数，那么zend_parse_parameters()就会自动地把整数转换为相应的浮点数。如果实际值无法转换成期望类型（比如整形到数组形），会触发一个警告。</p>
<p>下表列出了可能指定的类型。我们从完整性考虑也列出了一些没有讨论到的类型。</p>
<table>
    <tbody>
        <tr>
            <td>类型指定符 </td>
            <td>对应的C类型 </td>
            <td>描述</td>
        </tr>
        <tr>
            <td>l </td>
            <td>long </td>
            <td>符号整数 </td>
        </tr>
        <tr>
            <td>d </td>
            <td>double </td>
            <td>浮点数 </td>
        </tr>
        <tr>
            <td>s </td>
            <td>char *, int </td>
            <td>二进制字符串，长度</td>
        </tr>
        <tr>
            <td>b </td>
            <td>zend_bool </td>
            <td>逻辑型（1或0）</td>
        </tr>
        <tr>
            <td>r </td>
            <td>zval * </td>
            <td>资源（文件指针，数据库连接等）</td>
        </tr>
        <tr>
            <td>a </td>
            <td>zval * </td>
            <td>联合数组 </td>
        </tr>
        <tr>
            <td>o </td>
            <td>zval * </td>
            <td>任何类型的对象 </td>
        </tr>
        <tr>
            <td>O </td>
            <td>zval * </td>
            <td>指定类型的对象。需要提供目标对象的类类型</td>
        </tr>
        <tr>
            <td>z </td>
            <td>zval * </td>
            <td>无任何操作的zval </td>
        </tr>
    </tbody>
</table>
<p>为了容易地理解最后几个选项的含义，你需要知道zval是Zend引擎的值容器[1]。无论这个变量是布尔型，字符串型或者其他任何类型，其信息总会包含在一个zval联合体中。本章中我们不直接存取zval，而是通过一些附加的宏来操作。下面的是或多或少在C中的zval, 以便我们能更好地理解接下来的代码。</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">typedef union _zval{
    <li>long lval;
    <li>double dval;
    <li>struct {
    <li>char *val;
    <li>int len;
    <li>}str;
    <li>&nbsp;
    <li>HashTable *ht;
    <li>zend_object_value obj;
    <li>&nbsp;
    <li>}zval;</li>
</ol>
</div>
<p>&nbsp;</p>
<p>在我们的例子中，我们用基本类型调用zend_parse_parameters()，以本地C类型的方式取得函数参数的值，而不是用zval容器。</p>
<p>为了让zend_parse_parameters()能够改变传递给它的参数的值，并返回这个改变值，需要传递一个引用。仔细查看一下self_concat()：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">if (zend_parse_parameters(argc TSRMLS_CC, "sl", &amp;str, &amp;str_len, &amp;n) == FAILURE)
    <li>return;</li>
</ol>
</div>
<p>注意到自动生成的代码会检测函数的返回值FAILUER(成功即SUCCESS)来判断是否成功。如果没有成功则立即返回，并且由zend_parse_parameters()负责触发警告信息。因为函数打算接收一个字符串l和一个整数n，所以指定 &#8221;sl&#8221; 作为其类型指示符。s需要两个参数，所以我们传递参考char * 和 int (str 和 str_len)给zend_parse_parameters()函数。无论什么时候，记得总是在代码中使用字符串长度str_len来确保函数工作在二进制安全的环境中。不要使用strlen()和strcpy()，除非你不介意函数在二进制字符串下不能工作。二进制字符串是包含有nulls的字符串。二进制格式包括图象文件，压缩文件，可执行文件和更多的其他文件。&#8221;l&#8221; 只需要一个参数，所以我们传递给它n的引用。尽管为了清晰起见，骨架脚本生成的C变量名与在函数原型定义文件中的参数名一样；这样做不是必须的，尽管在实践中鼓励这样做。</p>
<p>回到转换规则中来。下面三个对self_concat()函数的调用使str, str_len和n得到同样的值：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">self_concat("321", 5);
    <li>&nbsp;
    <li>self_concat(321, "5");
    <li>&nbsp;
    <li>self_concat("321", "5");
    <li>&nbsp;
    <li>str points to the string "321", str_len equals 3, and n equals 5.
    <li>&nbsp;
    <li>str 指向字符串"321"，str_len等于3，n等于5。</li>
</ol>
</div>
<p>&nbsp;</p>
<p>在我们编写代码来实现连接字符串返回给PHP的函数前，还得谈谈两个重要的话题：内存管理、从PHP内部返回函数值所使用的API。</p>
<h3>内存管理</h3>
<p>用于从堆中分配内存的PHP API几乎和标准C API一样。在编写扩展的时候，使用下面与C对应（因此不必再解释）的API函数：</p>
<div class="hl-surround">
<div class="hl-main">emalloc(size_t size);<br />
<br />
efree(void *ptr);<br />
<br />
ecalloc(size_t nmemb, size_t size);<br />
<br />
erealloc(void *ptr, size_t size);<br />
<br />
estrdup(const char *s);<br />
<br />
estrndup(const char *s, unsigned int length);</div>
</div>
<p>在这一点上，任何一位有经验的C程序员应该象这样思考一下：&#8220;什么？标准C没有strndup()？&#8221;是的，这是正确的，因为GNU扩展通常在Linux下可用。estrndup()只是PHP下的一个特殊函数。它的行为与estrdup()相似，但是可以指定字符串重复的次数（不需要结束空字符），同时是二进制安全的。这是推荐使用estrndup()而不是estrdup()的原因。</p>
<p>在几乎所有的情况下，你应该使用这些内存分配函数。有一些情况，即扩展需要分配在请求中永久存在的内存，从而不得不使用malloc()，但是除非你知道你在做什么，你应该始终使用以上的函数。如果没有使用这些内存函数，而相反使用标准C函数分配的内存返回给脚本引擎，那么PHP会崩溃。</p>
<p>这些函数的优点是：任何分配的内存在偶然情况下如果没有被释放，则会在页面请求的最后被释放。因此，真正的内存泄漏不会产生。然而，不要依赖这一机制，从调试和性能两个原因来考虑，应当确保释放应该释放的内存。剩下的优点是在多线程环境下性能的提高，调试模式下检测内存错误等。</p>
<p>还有一个重要的原因，你不需要检查这些内存分配函数的返回值是否为null。当内存分配失败，它们会发出E_ERROR错误，从而决不会返回到扩展。</p>
<p>
<h3>从PHP函数中返回值</h3>
<p>扩展API包含丰富的用于从函数中返回值的宏。这些宏有两种主要风格：第一种是RETVAL_type()形式，它设置了返回值但C代码继续执行。这通常使用在把控制交给脚本引擎前还希望做的一些清理工作的时候使用，然后再使用C的返回声明 &#8221;return&#8221; 返回到PHP；后一个宏更加普遍，其形式是RETURN_type()，他设置了返回类型，同时返回控制到PHP。下表解释了大多数存在的宏。</p>
<table>
    <tbody>
        <tr>
            <td>设置返回值并且结束函数 </td>
            <td>设置返回值 </td>
            <td>宏返回类型和参数</td>
        </tr>
        <tr>
            <td>RETURN_LONG(l) </td>
            <td>RETVAL_LONG(l) </td>
            <td>整数</td>
        </tr>
        <tr>
            <td>RETURN_BOOL(b) </td>
            <td>RETVAL_BOOL(b) </td>
            <td>布尔数(1或0)</td>
        </tr>
        <tr>
            <td>RETURN_NULL() </td>
            <td>RETVAL_NULL() </td>
            <td>NULL</td>
        </tr>
        <tr>
            <td>RETURN_DOUBLE(d) </td>
            <td>RETVAL_DOUBLE(d) </td>
            <td>浮点数</td>
        </tr>
        <tr>
            <td>RETURN_STRING(s, dup) </td>
            <td>RETVAL_STRING(s, dup) </td>
            <td>字符串。如果dup为1，引擎会调用estrdup()重复s，使用拷贝。如果dup为0，就使用s</td>
        </tr>
        <tr>
            <td>RETURN_STRINGL(s, l, dup) </td>
            <td>RETVAL_STRINGL(s, l, dup) </td>
            <td>长度为l的字符串值。与上一个宏一样，但因为s的长度被指定，所以速度更快。</td>
        </tr>
        <tr>
            <td>RETURN_TRUE </td>
            <td>RETVAL_TRUE </td>
            <td>返回布尔值true。注意到这个宏没有括号。</td>
        </tr>
        <tr>
            <td>RETURN_FALSE </td>
            <td>RETVAL_FALSE </td>
            <td>返回布尔值false。注意到这个宏没有括号。 </td>
        </tr>
        <tr>
            <td>RETURN_RESOURCE(r) </td>
            <td>RETVAL_RESOURCE(r) </td>
            <td>资源句柄。</td>
        </tr>
    </tbody>
</table>
<p>
<h3>完成self_concat()</h3>
<p>现在你已经学会了如何分配内存和从PHP扩展函数里返回函数值，那么我们就能够完成self_concat()的编码：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">/* {{{ proto string self_concat(string str, int n)
    <li>&nbsp;
    <li>*/
    <li>&nbsp;
    <li>PHP_FUNCTION(self_concat)
    <li>&nbsp;
    <li>}
    <li>&nbsp;
    <li>char *str = NULL;
    <li>&nbsp;
    <li>int argc = ZEND_NUM_ARGS();
    <li>&nbsp;
    <li>int str_len;
    <li>&nbsp;
    <li>long n;
    <li>&nbsp;
    <li>char *result; /* Points to resulting string */
    <li>&nbsp;
    <li>char *ptr; /* Points at the next location we want to copy to */
    <li>&nbsp;
    <li>int result_length; /* Length of resulting string */
    <li>&nbsp;
    <li>if (zend_parse_parameters(argc TSRMLS_CC, "sl", &amp;str, &amp;str_len, &amp;n) == FAILURE)
    <li>&nbsp;
    <li>return;
    <li>&nbsp;
    <li>/* Calculate length of result */
    <li>&nbsp;
    <li>result_length = (str_len * n);
    <li>&nbsp;
    <li>/* Allocate memory for result */
    <li>&nbsp;
    <li>result = (char *) emalloc(result_length + 1);
    <li>&nbsp;
    <li>/* Point at the beginning of the result */
    <li>&nbsp;
    <li>ptr = result;
    <li>&nbsp;
    <li>while (n--) {
    <li>&nbsp;
    <li>/* Copy str to the result */
    <li>&nbsp;
    <li>memcpy(ptr, str, str_len);
    <li>&nbsp;
    <li>/* Increment ptr to point at the next position we want to write to */
    <li>&nbsp;
    <li>ptr += str_len;
    <li>&nbsp;
    <li>}
    <li>&nbsp;
    <li>/* Null terminate the result. Always null-terminate your strings
    <li>&nbsp;
    <li>even if they are binary strings */
    <li>&nbsp;
    <li>*ptr = '\0';
    <li>&nbsp;
    <li>/* Return result to the scripting engine without duplicating it*/
    <li>&nbsp;
    <li>RETURN_STRINGL(result, result_length, 0);
    <li>&nbsp;
    <li>}
    <li>&nbsp;
    <li>/* }}} */</li>
</ol>
</div>
<p>现在要做的就是重新编译一下PHP，这样就完成了第一个PHP函数。</p>
<p>让我门检查函数是否真的工作。在最新编译过的PHP树下执行[2]下面的脚本：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline"><span style="color: blue">&lt;?php</span>
    <li><span style="color: green">for</span><span style="color: gray">&nbsp;</span><span style="color: olive">(</span><span style="color: #00008b">$i</span><span style="color: gray"> = </span><span style="color: maroon">1</span><span style="color: gray">; </span><span style="color: #00008b">$i</span><span style="color: gray"> &lt;= </span><span style="color: maroon">3</span><span style="color: gray">; </span><span style="color: #00008b">$i</span><span style="color: gray">++</span><span style="color: olive">){</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: blue">print</span><span style="color: gray">&nbsp;</span><span style="color: blue">self_concat</span><span style="color: olive">(</span><span style="color: #8b0000">"</span><span style="color: red">ThisIsUseless</span><span style="color: #8b0000">"</span><span style="color: gray">, </span><span style="color: #00008b">$i</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: blue">print</span><span style="color: gray">&nbsp;</span><span style="color: #8b0000">"</span><span style="color: navy">\n</span><span style="color: #8b0000">"</span><span style="color: gray">;</span>
    <li><span style="color: olive">}</span>
    <li><span style="color: blue">?&gt;</span></li>
</ol>
</div>
<p>你应该得到下面的结果：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">ThisIsUseless
    <li>&nbsp;
    <li>ThisIsUselessThisIsUseless
    <li>&nbsp;
    <li>ThisIsUselessThisIsUselessThisIsUseless</li>
</ol>
</div>
<p>&nbsp;</p>
<p>
<h3>实例小结</h3>
<p>你已经学会如何编写一个简单的PHP函数。回到本章的开头，我们提到用C编写PHP功能函数的两个主要的动机。第一个动机是用C实现一些算法来提高性能和扩展功能。前一个例子应该能够指导你快速上手这种类型扩展的开发。第二个动机是包裹三方函数库。我们将在下一步讨论。 </p>
<p>
<h2>包裹第三方的扩展</h2>
<p>本节中你将学到如何编写更有用和更完善的扩展。该节的扩展包裹了一个C库，展示了如何编写一个含有多个互相依赖的PHP函数扩展。</p>
<h3>动机</h3>
<p>也许最常见的PHP扩展是那些包裹第三方C库的扩展。这些扩展包括MySQL或Oracle的数据库服务库，libxml2的 XML技术库，ImageMagick 或GD的图形操纵库。</p>
<p>在本节中，我们编写一个扩展，同样使用脚本来生成骨架扩展，因为这能节省许多工作量。这个扩展包裹了标准C函数fopen(), fclose(), fread(), fwrite()和 feof().</p>
<p>扩展使用一个被叫做资源的抽象数据类型，用于代表已打开的文件FILE*。你会注意到大多数处理比如数据库连接、文件句柄等的PHP扩展使用了资源类型，这是因为引擎自己无法直接&#8220;理解&#8221;它们。我们计划在PHP扩展中实现的C API列表如下：</p>
<div class="hl-surround">
<div class="hl-main">FILE *fopen(const char *path, const char *mode);<br />
<br />
int fclose(FILE *stream);<br />
<br />
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);<br />
<br />
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);<br />
<br />
int feof(FILE *stream);</div>
</div>
<p>&nbsp;</p>
<p>我们实现这些函数，使它们在命名习惯和简单性上符合PHP脚本。如果你曾经向PHP社区贡献过代码，你被期望遵循一些公共习俗，而不是跟随C库里的API。并不是所有的习俗都写在PHP代码树的CODING_STANDARDS文件里。这即是说，此功能已经从PHP发展的很早阶段即被包含在PHP中，并且与C库API类似。PHP安装已经支持fopen(), fclose()和更多的PHP函数。</p>
<p>以下是PHP风格的API：</p>
<div class="hl-surround">
<div class="hl-main">resource file_open(string filename, string mode)<br />
<br />
file_open() //接收两个字符串（文件名和模式），返回一个文件的资源句柄。<br />
<br />
bool file_close(resource filehandle)<br />
<br />
file_close() //接收一个资源句柄，返回真/假指示是否操作成功。<br />
<br />
string file_read(resource filehandle, int size)<br />
<br />
file_read() //接收一个资源句柄和读入的总字节数，返回读入的字符串。<br />
<br />
bool file_write(resource filehandle, string buffer)<br />
<br />
file_write()&nbsp; &nbsp;//接收一个资源句柄和被写入的字符串，返回真/假指示是否操作成功。<br />
<br />
bool file_eof(resource filehandle)<br />
<br />
file_eof() //接收一个资源句柄，返回真/假指示是否到达文件的尾部。</div>
</div>
<p>因此，我们的函数定义文件——保存为ext/目录下的myfile.def——内容如下：</p>
<div class="hl-surround">
<div class="hl-main">resource file_open(string filename, string mode)<br />
<br />
bool file_close(resource filehandle)<br />
<br />
string file_read(resource filehandle, int size)<br />
<br />
bool file_write(resource filehandle, string buffer)<br />
<br />
bool file_eof(resource filehandle)</div>
</div>
<p>&nbsp;</p>
<p>下一步，利用ext_skel脚本在ext./ 原代码目录执行下面的命令：</p>
<div class="hl-surround">
<div class="hl-main">./ext_skel --extname=myfile --proto=myfile.def</div>
</div>
<p>然后，按照前一个例子的关于编译新建立脚本的步骤操作。你会得到一些包含FETCH_RESOURCE()宏行的编译错误，这样骨架脚本就无法顺利完成编译。为了让骨架扩展顺利通过编译，把那些出错行[3]注释掉即可。</p>
<h3>资源</h3>
<p>资源是一个能容纳任何信息的抽象数据结构。正如前面提到的，这个信息通常包括例如文件句柄、数据库连接结构和其他一些复杂类型的数据。</p>
<p>使用资源的主要原因是因为：资源被一个集中的队列所管理，该队列可以在PHP开发人员没有在脚本里面显式地释放时可以自动地被释放。</p>
<p>举个例子，考虑到编写一个脚本，在脚本里调用mysql_connect()打开一个MySQL连接，可是当该数据库连接资源不再使用时却没有调用mysql_close()。在PHP里，资源机制能够检测什么时候这个资源应当被释放，然后在当前请求的结尾或通常情况下更早地释放资源。这就为减少内存泄漏赋予了一个&#8220;防弹&#8221;机制。如果没有这样一个机制，经过几次web请求后，web服务器也许会潜在地泄漏许多内存资源，从而导致服务器当机或出错。</p>
<h3>注册资源类型</h3>
<p>如何使用资源？Zend引擎让使用资源变地非常容易。你要做的第一件事就是把资源注册到引擎中去。使用这个API函数：</p>
<div class="hl-surround">
<div class="hl-main">int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number)</div>
</div>
<p>这个函数返回一个资源类型id，该id应当被作为全局变量保存在扩展里，以便在必要的时候传递给其他资源API。ld：该资源释放时调用的函数。pld用于在不同请求中始终存在的永久资源，本章不会涉及。type_name是一个具有描述性类型名称的字符串，module_number为引擎内部使用，当我们调用这个函数时，我们只需要传递一个已经定义好的module_number变量。 </p>
<p>回到我们的例子中来：我们会添加下面的代码到myfile.c原文件中。该文件包括了资源释放函数的定义，此资源函数被传递给zend_register_list_destructors_ex()注册函数（资源释放函数应该提早添加到文件中，以便在调用zend_register_list_destructors_ex()时该函数已被定义）：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">static void myfile_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC){
    <li>FILE *fp = (FILE *) rsrc-&gt;ptr;
    <li>fclose(fp);
    <li>}</li>
</ol>
</div>
<p>把注册行添加到PHP_MINIT_FUNCTION()后，看起来应该如下面的代码：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">PHP_MINIT_FUNCTION(myfile){
    <li>/* If you have INI entries, uncomment these lines
    <li>ZEND_INIT_MODULE_GLOBALS(myfile, php_myfile_init_globals,NULL);
    <li>&nbsp;
    <li>REGISTER_INI_ENTRIES();
    <li>*/
    <li>&nbsp;
    <li>le_myfile = zend_register_list_destructors_ex(myfile_dtor,NULL,"standard-c-file", module_number);
    <li>&nbsp;
    <li>return SUCCESS;
    <li>}</li>
</ol>
</div>
<p>l 注意到le_myfile是一个已经被ext_skel脚本定义好的全局变量。</p>
<p>PHP_MINIT_FUNCTION()是一个先于模块（扩展）的启动函数，是暴露给扩展的一部分API。下表提供可用函数简要的说明。</p>
<table>
    <tbody>
        <tr>
            <td>函数声明宏 </td>
            <td>语义</td>
        </tr>
        <tr>
            <td>PHP_MINIT_FUNCTION() </td>
            <td>当PHP被装载时，模块启动函数即被引擎调用。这使得引擎做一些例如资源类型，注册INI变量等的一次初始化。 </td>
        </tr>
        <tr>
            <td>PHP_MSHUTDOWN_FUNCTION() </td>
            <td>当PHP完全关闭时，模块关闭函数即被引擎调用。通常用于注销INI条目</td>
        </tr>
        <tr>
            <td>PHP_RINIT_FUNCTION() </td>
            <td>在每次PHP请求开始，请求前启动函数被调用。通常用于管理请求前逻辑。</td>
        </tr>
        <tr>
            <td>PHP_RSHUTDOWN_FUNCTION() </td>
            <td>在每次PHP请求结束后，请求前关闭函数被调用。经常应用在清理请求前启动函数的逻辑。</td>
        </tr>
        <tr>
            <td>PHP_MINFO_FUNCTION() </td>
            <td>调用phpinfo()时模块信息函数被呼叫，从而打印出模块信息。</td>
        </tr>
    </tbody>
</table>
<p>新建和注册新资源 我们准备实现file_open()函数。当我们打开文件得到一个FILE *，我们需要利用资源机制注册它。下面的主要宏实现注册功能：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">ZEND_REGISTER_RESOURCE(rsrc_result, rsrc_pointer, rsrc_type);</li>
</ol>
</div>
<p>参考表格对宏参数的解释</p>
<h3>ZEND_REGISTER_RESOURCE 宏参数</h3>
<table>
    <tbody>
        <tr>
            <td>宏参数 </td>
            <td>参数类型</td>
        </tr>
        <tr>
            <td>rsrc_result </td>
            <td>zval *, which should be set with the registered resource information. zval * 设置为已注册资源信息</td>
        </tr>
        <tr>
            <td>rsrc_pointer </td>
            <td>Pointer to our resource data. 资源数据指针</td>
        </tr>
        <tr>
            <td>rsrc_type </td>
            <td>The resource id obtained when registering the resource type. 注册资源类型时获得的资源id</td>
        </tr>
    </tbody>
</table>
<p>
<h3>文件函数</h3>
<p>现在你知道了如何使用ZEND_REGISTER_RESOURCE()宏，并且准备好了开始编写file_open()函数。还有一个主题我们需要讲述。</p>
<p>当PHP运行在多线程服务器上，不能使用标准的C文件存取函数。这是因为在一个线程里正在运行的PHP脚本会改变当前工作目录，因此另外一个线程里的脚本使用相对路径则无法打开目标文件。为了阻止这种错误发生，PHP框架提供了称作VCWD （virtual current working directory 虚拟当前工作目录）宏，用来代替任何依赖当前工作目录的存取函数。这些宏与被替代的函数具备同样的功能，同时是被透明地处理。在某些没有标准C函数库平台的情况下，VCWD框架则不会得到支持。例如，Win32下不存在chown()，就不会有相应的VCWD_CHOWN()宏被定义。</p>
<h4>VCWD列表</h4>
<table>
    <tbody>
        <tr>
            <td>标准C库 </td>
            <td>VCWD宏 </td>
        </tr>
        <tr>
            <td>getcwd() </td>
            <td>VCWD_GETCWD()</td>
        </tr>
        <tr>
            <td>fopen() </td>
            <td>VCWD_FOPEN</td>
        </tr>
        <tr>
            <td>open() </td>
            <td>VCWD_OPEN() //用于两个参数的版本</td>
        </tr>
        <tr>
            <td>open() </td>
            <td>VCWD_OPEN_MODE() //用于三个参数的open()版本</td>
        </tr>
        <tr>
            <td>creat() </td>
            <td>VCWD_CREAT()</td>
        </tr>
        <tr>
            <td>chdir() </td>
            <td>VCWD_CHDIR()</td>
        </tr>
        <tr>
            <td>getwd() </td>
            <td>VCWD_GETWD()</td>
        </tr>
        <tr>
            <td>realpath() </td>
            <td>VCWD_REALPATH()</td>
        </tr>
        <tr>
            <td>rename() </td>
            <td>VCWD_RENAME()</td>
        </tr>
        <tr>
            <td>stat() </td>
            <td>VCWD_STAT()</td>
        </tr>
        <tr>
            <td>lstat() </td>
            <td>VCWD_LSTAT()</td>
        </tr>
        <tr>
            <td>unlink() </td>
            <td>VCWD_UNLINK()</td>
        </tr>
        <tr>
            <td>mkdir() </td>
            <td>VCWD_MKDIR() </td>
        </tr>
        <tr>
            <td>rmdir() </td>
            <td>VCWD_RMDIR()</td>
        </tr>
        <tr>
            <td>opendir() </td>
            <td>VCWD_OPENDIR()</td>
        </tr>
        <tr>
            <td>popen() </td>
            <td>VCWD_POPEN()</td>
        </tr>
        <tr>
            <td>access() </td>
            <td>VCWD_ACCESS()</td>
        </tr>
        <tr>
            <td>utime() </td>
            <td>VCWD_UTIME()</td>
        </tr>
        <tr>
            <td>chmod() </td>
            <td>VCWD_CHMOD()</td>
        </tr>
        <tr>
            <td>chown() </td>
            <td>VCWD_CHOWN()</td>
        </tr>
    </tbody>
</table>
<p>
<h3>编写利用资源的第一个PHP函数</h3>
<p>实现file_open()应该非常简单，看起来像下面的样子：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">PHP_FUNCTION(file_open){
    <li>char *filename = NULL;
    <li>char *mode = NULL;
    <li>int argc = ZEND_NUM_ARGS();
    <li>int filename_len;
    <li>int mode_len;
    <li>FILE *fp;
    <li>&nbsp;
    <li>if (zend_parse_parameters(argc TSRMLS_CC, "ss", &amp;filename,&amp;filename_len, &amp;mode, &amp;mode_len) == FAILURE) {
    <li>return;
    <li>}
    <li>&nbsp;
    <li>fp = VCWD_FOPEN(filename, mode);
    <li>&nbsp;
    <li>if (fp == NULL) {
    <li>RETURN_FALSE;
    <li>}
    <li>&nbsp;
    <li>ZEND_REGISTER_RESOURCE(return_value, fp, le_myfile);
    <li>}</li>
</ol>
</div>
<p>&nbsp;</p>
<p>你可能会注意到资源注册宏的第一个参数return_value，可此地找不到它的定义。这个变量自动的被扩展框架定义为zval * 类型的函数返回值。先前讨论的、能够影响返回值的RETURN_LONG() 和RETVAL_BOOL()宏确实改变了return_value的值。因此很容易猜到程序注册了我们取得的文件指针fp，同时设置return_value为该注册资源。</p>
<p>访问资源 需要使用下面的宏访问资源（参看表对宏参数的解释）</p>
<div class="hl-surround">
<div class="hl-main">ZEND_FETCH_RESOURCE(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type);</div>
</div>
<h4>ZEND_FETCH_RESOURCE 宏参数</h4>
<table>
    <tbody>
        <tr>
            <td>参数 </td>
            <td>含义</td>
        </tr>
        <tr>
            <td>rsrc </td>
            <td>资源值保存到的变量名。它应该和资源有相同类型。</td>
        </tr>
        <tr>
            <td>rsrc_type </td>
            <td>rsrc的类型，用于在内部把资源转换成正确的类型</td>
        </tr>
        <tr>
            <td>passed_id </td>
            <td>寻找的资源值(例如zval **)</td>
        </tr>
        <tr>
            <td>default_id </td>
            <td>如果该值不为-1，就使用这个id。用于实现资源的默认值。</td>
        </tr>
        <tr>
            <td>resource_type_name </td>
            <td>资源的一个简短名称，用于错误信息。</td>
        </tr>
        <tr>
            <td>resource_type </td>
            <td>注册资源的资源类型id</td>
        </tr>
    </tbody>
</table>
<p>使用这个宏，我们现在能够实现file_eof()：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">PHP_FUNCTION(file_eof){
    <li>int argc = ZEND_NUM_ARGS();
    <li>zval *filehandle = NULL;
    <li>FILE *fp;
    <li>&nbsp;
    <li>if (zend_parse_parameters(argc TSRMLS_CC, "r", &amp;filehandle) ==FAILURE) {
    <li>return;
    <li>}
    <li>&nbsp;
    <li>ZEND_FETCH_RESOURCE(fp, FILE *, &amp;filehandle, -1, "standard-c-file",le_myfile);
    <li>&nbsp;
    <li>if (fp == NULL){
    <li>RETURN_FALSE;
    <li>}
    <li>&nbsp;
    <li>if (feof(fp) &lt;= 0) {
    <li>/* Return eof also if there was an error */
    <li>RETURN_TRUE;
    <li>}
    <li>&nbsp;
    <li>RETURN_FALSE;
    <li>}</li>
</ol>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h4>删除一个资源</h4>
<p>通常使用下面这个宏删除一个资源：</p>
<div class="hl-surround">
<div class="hl-main">int zend_list_delete(int id)</div>
</div>
<p>传递给宏一个资源id，返回SUCCESS或者FAILURE。如果资源存在，优先从Zend资源列队中删除，该过程中会调用该资源类型的已注册资源清理函数。因此，在我们的例子中，不必取得文件指针，调用fclose()关闭文件，然后再删除资源。直接把资源删除掉即可。</p>
<p>使用这个宏，我们能够实现file_close()：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">PHP_FUNCTION(file_close){
    <li>int argc = ZEND_NUM_ARGS();
    <li>zval *filehandle = NULL;
    <li>&nbsp;
    <li>if (zend_parse_parameters(argc TSRMLS_CC, "r", &amp;filehandle) == FAILURE) {
    <li>return;
    <li>}
    <li>&nbsp;
    <li>if (zend_list_delete(Z_RESVAL_P(filehandle)) == FAILURE) {
    <li>RETURN_FALSE;
    <li>}
    <li>&nbsp;
    <li>RETURN_TRUE;
    <li>}</li>
</ol>
</div>
<p>&nbsp;</p>
<p>你肯定会问自己Z_RESVAL_P()是做什么的。当我们使用zend_parse_parameters()从参数列表中取得资源的时候，得到的是zval的形式。为了获得资源id，我们使用Z_RESVAL_P()宏得到id，然后把id传递给zend_list_delete()。<br />
有一系列宏用于访问存储于zval值（参考表的宏列表）。尽管在大多数情况下zend_parse_parameters()返回与c类型相应的值，我们仍希望直接处理zval，包括资源这一情况。</p>
<h4>Zval访问宏</h4>
<table>
    <tbody>
        <tr>
            <td>宏 </td>
            <td>访问对象 </td>
            <td>C 类型</td>
        </tr>
        <tr>
            <td>Z_LVAL, Z_LVAL_P, Z_LVAL_PP </td>
            <td>整型值 </td>
            <td>long</td>
        </tr>
        <tr>
            <td>Z_BVAL, Z_BVAL_P, Z_BVAL_PP </td>
            <td>布尔值 </td>
            <td>zend_bool</td>
        </tr>
        <tr>
            <td>Z_DVAL, Z_DVAL_P, Z_DVAL_PP </td>
            <td>浮点值 </td>
            <td>double</td>
        </tr>
        <tr>
            <td>Z_STRVAL, Z_STRVAL_P, Z_STRVAL_PP </td>
            <td>字符串值 </td>
            <td>char *</td>
        </tr>
        <tr>
            <td>Z_STRLEN, Z_STRLEN_P, Z_STRLEN_PP </td>
            <td>字符串长度值 </td>
            <td>int</td>
        </tr>
        <tr>
            <td>Z_RESVAL, Z_RESVAL_P,Z_RESVAL_PP </td>
            <td>资源值 </td>
            <td>long</td>
        </tr>
        <tr>
            <td>Z_ARRVAL, Z_ARRVAL_P, Z_ARRVAL_PP </td>
            <td>联合数组 </td>
            <td>HashTable *</td>
        </tr>
        <tr>
            <td>Z_TYPE, Z_TYPE_P, Z_TYPE_PP </td>
            <td>Zval类型 </td>
            <td>Enumeration (IS_NULL, IS_LONG, IS_DOUBLE, IS_STRING, IS_ARRAY, IS_OBJECT, IS_BOOL, IS_RESOURCE)</td>
        </tr>
        <tr>
            <td>Z_OBJPROP, Z_OBJPROP_P, Z_OBJPROP_PP </td>
            <td>对象属性hash（本章不会谈到）
            <td>HashTable *</td>
        </tr>
        <tr>
            <td>Z_OBJCE, Z_OBJCE_P, Z_OBJCE_PP </td>
            <td>对象的类信息 </td>
            <td>zend_class_entry</td>
        </tr>
    </tbody>
</table>
<h4>用于访问zval值的宏</h4>
<p>所有的宏都有三种形式：一个是接受zval s，另外一个接受zval *s，最后一个接受zval **s。它们的区别是在命名上，第一个没有后缀，zval *有后缀_P（代表一个指针），最后一个 zval **有后缀_PP（代表两个指针）。<br />
现在，你有足够的信息来独立完成 file_read()和 file_write()函数。这里是一个可能的实现：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">PHP_FUNCTION(file_read){
    <li>int argc = ZEND_NUM_ARGS();
    <li>long size;
    <li>zval *filehandle = NULL;
    <li>FILE *fp;
    <li>char *result;
    <li>size_t bytes_read;
    <li>&nbsp;
    <li>if (zend_parse_parameters(argc TSRMLS_CC, "rl", &amp;filehandle,&amp;size) == FAILURE) {
    <li>return;
    <li>}
    <li>&nbsp;
    <li>ZEND_FETCH_RESOURCE(fp, FILE *, &amp;filehandle, -1, "standard-cfile", le_myfile);
    <li>&nbsp;
    <li>result = (char *) emalloc(size+1);
    <li>&nbsp;
    <li>bytes_read = fread(result, 1, size, fp);
    <li>&nbsp;
    <li>result[bytes_read] = '\0';
    <li>&nbsp;
    <li>RETURN_STRING(result, 0);
    <li>}
    <li>&nbsp;
    <li>PHP_FUNCTION(file_write){
    <li>char *buffer = NULL;
    <li>int argc = ZEND_NUM_ARGS();
    <li>int buffer_len;
    <li>zval *filehandle = NULL;
    <li>FILE *fp;
    <li>&nbsp;
    <li>if (zend_parse_parameters(argc TSRMLS_CC, "rs", &amp;filehandle,&amp;buffer, &amp;buffer_len) == FAILURE) {
    <li>return;
    <li>}
    <li>&nbsp;
    <li>ZEND_FETCH_RESOURCE(fp, FILE *, &amp;filehandle, -1, "standard-cfile", le_myfile);
    <li>&nbsp;
    <li>if (fwrite(buffer, 1, buffer_len, fp) != buffer_len) {
    <li>RETURN_FALSE;
    <li>}
    <li>&nbsp;
    <li>RETURN_TRUE;
    <li>}</li>
</ol>
</div>
<h3>测试扩展</h3>
<p>你现在可以编写一个测试脚本来检测扩展是否工作正常。下面是一个示例脚本，该脚本打开文件test.txt，输出文件类容到标准输出，建立一个拷贝test.txt.new。</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline"><span style="color: blue">&lt;?php</span>
    <li><span style="color: #00008b">$fp_in</span><span style="color: gray"> = </span><span style="color: blue">file_open</span><span style="color: olive">(</span><span style="color: #8b0000">"</span><span style="color: red">test.txt</span><span style="color: #8b0000">"</span><span style="color: gray">, </span><span style="color: #8b0000">"</span><span style="color: red">r</span><span style="color: #8b0000">"</span><span style="color: olive">)</span><span style="color: gray">&nbsp;</span><span style="color: green">or</span><span style="color: gray"> </span><span style="color: green">die</span><span style="color: olive">(</span><span style="color: #8b0000">"</span><span style="color: red">Unable to open input file</span><span style="color: navy">\n</span><span style="color: #8b0000">"</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp;</span>
    <li><span style="color: #00008b">$fp_out</span><span style="color: gray"> = </span><span style="color: blue">file_open</span><span style="color: olive">(</span><span style="color: #8b0000">"</span><span style="color: red">test.txt.new</span><span style="color: #8b0000">"</span><span style="color: gray">, </span><span style="color: #8b0000">"</span><span style="color: red">w</span><span style="color: #8b0000">"</span><span style="color: olive">)</span><span style="color: gray">&nbsp;</span><span style="color: green">or</span><span style="color: gray"> </span><span style="color: green">die</span><span style="color: olive">(</span><span style="color: #8b0000">"</span><span style="color: red">Unable to open output file</span><span style="color: navy">\n</span><span style="color: #8b0000">"</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp;</span>
    <li><span style="color: green">while</span><span style="color: gray">&nbsp;</span><span style="color: olive">(</span><span style="color: gray">!</span><span style="color: blue">file_eof</span><span style="color: olive">(</span><span style="color: #00008b">$fp_in</span><span style="color: olive">))</span><span style="color: gray"> </span><span style="color: olive">{</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: #00008b">$str</span><span style="color: gray"> = </span><span style="color: blue">file_read</span><span style="color: olive">(</span><span style="color: #00008b">$fp_in</span><span style="color: gray">, </span><span style="color: maroon">1024</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: blue">print</span><span style="color: olive">(</span><span style="color: #00008b">$str</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: gray">&nbsp; &nbsp; </span><span style="color: blue">file_write</span><span style="color: olive">(</span><span style="color: #00008b">$fp_out</span><span style="color: gray">, </span><span style="color: #00008b">$str</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: olive">}</span>
    <li><span style="color: gray">&nbsp;</span>
    <li><span style="color: blue">file_close</span><span style="color: olive">(</span><span style="color: #00008b">$fp_in</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: blue">file_close</span><span style="color: olive">(</span><span style="color: #00008b">$fp_out</span><span style="color: olive">)</span><span style="color: gray">;</span>
    <li><span style="color: blue">?&gt;</span></li>
</ol>
</div>
<h3>全局变量</h3>
<p>你可能希望在扩展里使用全局C变量，无论是独自在内部使用或访问php.ini文件中的INI扩展注册标记（INI在下一节中讨论）。因为PHP是为多线程环境而设计，所以不必定义全局变量。PHP提供了一个创建全局变量的机制，可以同时应用在线程和非线程环境中。我们应当始终利用这个机制，而不要自主地定义全局变量。用一个宏访问这些全局变量，使用起来就像普通全局变量一样。</p>
<p>用于生成myfile工程骨架文件的ext_skel脚本创建了必要的代码来支持全局变量。通过检查php_myfile.h文件，你应当发现类似下面的被注释掉的一节，</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">ZEND_BEGIN_MODULE_GLOBALS(myfile)
    <li>&nbsp;
    <li>int global_value;
    <li>char *global_string;
    <li>&nbsp;
    <li>ZEND_END_MODULE_GLOBALS(myfile)</li>
</ol>
</div>
<p>你可以把这一节的注释去掉，同时添加任何其他全局变量于这两个宏之间。文件后部的几行，骨架脚本自动地定义一个MYFILE_G(v)宏。这个宏应当被用于所有的代码，以便访问这些全局变量。这就确保在多线程环境中，访问的全局变量仅是一个线程的拷贝，而不需要互斥的操作。 </p>
<p>为了使全局变量有效，最后需要做的是把myfile.c：</p>
<div class="hl-surround">
<div class="hl-main">ZEND_DECLARE_MODULE_GLOBALS(myfile)</div>
</div>
<p>注释去掉。</p>
<p>你也许希望在每次PHP请求的开始初始化全局变量。另外，做为一个例子，全局变量已指向了一个已分配的内存，在每次PHP请求结束时需要释放内存。为了达到这些目的，全局变量机制提供了一个特殊的宏，用于注册全局变量的构造和析构函数（参考表对宏参数的说明）：</p>
<div class="hl-surround">
<div class="hl-main">ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor, globals_dtor)</div>
</div>
<h4>表 ZEND_INIT_MODULE_GLOBALS 宏参数</h4>
<table>
    <tbody>
        <tr>
            <td>参数 </td>
            <td>含义 </td>
        </tr>
        <tr>
            <td>module_name </td>
            <td>与传递给ZEND_BEGIN_MODULE_GLOBALS()宏相同的扩展名称。</td>
        </tr>
        <tr>
            <td>globals_ctor </td>
            <td>构造函数指针。在myfile扩展里，函数原形与void php_myfile_init_globals(zend_myfile_globals *myfile_globals)类似</td>
        </tr>
        <tr>
            <td>globals_dtor </td>
            <td>析构函数指针。例如，php_myfile_init_globals(zend_myfile_globals *myfile_globals)</td>
        </tr>
    </tbody>
</table>
<p>你可以在myfile.c里看到如何使用构造函数和ZEND_INIT_MODULE_GLOBALS()宏的示例。 </p>
<p>
<h3>添加自定义INI指令</h3>
<p>INI文件(php.ini)的实现使得PHP扩展注册和监听各自的INI条目。如果这些INI条目由php.ini、Apache的htaccess或其他配置方法来赋值，注册的INI变量总是更新到正确的值。整个INI框架有许多不同的选项以实现其灵活性。我们涉及一些基本的（也是个好的开端），借助本章的其他材料，我们就能够应付日常开发工作的需要。</p>
<p>通过在PHP_INI_BEGIN()/PHP_INI_END()宏之间的STD_PHP_INI_ENTRY()宏注册PHP INI指令。例如在我们的例子里，myfile.c中的注册过程应当如下：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">PHP_INI_BEGIN()
    <li>&nbsp;
    <li>STD_PHP_INI_ENTRY("myfile.global_value", "42", PHP_INI_ALL, OnUpdateInt, global_value, zend_myfile_globals, myfile_globals)
    <li>&nbsp;
    <li>STD_PHP_INI_ENTRY("myfile.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_myfile_globals, myfile_globals)
    <li>&nbsp;
    <li>PHP_INI_END()</li>
</ol>
</div>
<p>&nbsp;</p>
<p>除了STD_PHP_INI_ENTRY()其他宏也能够使用，但这个宏是最常用的，可以满足大多数需要（参看表对宏参数的说明）：</p>
<div class="hl-surround">
<div class="hl-main">STD_PHP_INI_ENTRY(name, default_value, modifiable, on_modify, property_name, struct_type, struct_ptr)</div>
</div>
<h4>STD_PHP_INI_ENTRY 宏参数表</h4>
<table>
    <tbody>
        <tr>
            <td>参数 </td>
            <td>含义</td>
        </tr>
        <tr>
            <td>name </td>
            <td>INI条目名</td>
        </tr>
        <tr>
            <td>default_value </td>
            <td>如果没有在INI文件中指定，条目的默认值。默认值始终是一个字符串。</td>
        </tr>
        <tr>
            <td>modifiable </td>
            <td>设定在何种环境下INI条目可以被更改的位域。可以的值是：<br />
            &#8226; PHP_INI_SYSTEM. 能够在php.ini或http.conf等系统文件更改<br />
            &#8226; PHP_INI_PERDIR. 能够在 .htaccess中更改<br />
            &#8226; PHP_INI_USER. 能够被用户脚本更改<br />
            &#8226; PHP_INI_ALL. 能够在所有地方更改</td>
        </tr>
        <tr>
            <td>on_modify </td>
            <td>处理INI条目更改的回调函数。你不需自己编写处理程序，使用下面提供的函数。包括：<br />
            &#8226; OnUpdateInt<br />
            &#8226; OnUpdateString<br />
            &#8226; OnUpdateBool<br />
            &#8226; OnUpdateStringUnempty<br />
            &#8226; OnUpdateReal</td>
        </tr>
        <tr>
            <td>property_name </td>
            <td>应当被更新的变量名</td>
        </tr>
        <tr>
            <td>struct_type </td>
            <td>变量驻留的结构类型。因为通常使用全局变量机制，所以这个类型自动被定义，类似于zend_myfile_globals。</td>
        </tr>
        <tr>
            <td>struct_ptr </td>
            <td>全局结构名。如果使用全局变量机制，该名为myfile_globals。</td>
        </tr>
    </tbody>
</table>
<p>最后，为了使自定义INI条目机制正常工作，你需要分别去掉PHP_MINIT_FUNCTION(myfile)中的REGISTER_INI_ENTRIES()调用和PHP_MSHUTDOWN_FUNCTION(myfile)中的UNREGISTER_INI_ENTRIES()的注释。</p>
<p>访问两个示例全局变量中的一个与在扩展里编写MYFILE_G(global_value) 和MYFILE_G(global_string)一样简单。</p>
<p>如果你把下面的两行放在php.ini中，MYFILE_G(global_value)的值会变为99。</p>
<div class="hl-surround">
<div class="hl-main">; php.ini &#8211; The following line sets the INI entry myfile.global_value to 99.<br />
myfile.global_value = 99</div>
</div>
<p>&nbsp;</p>
<p>
<h3>线程安全资源管理宏</h3>
<p>现在，你肯定注意到以TSRM（线程安全资源管理器）开头的宏随处使用。这些宏提供给扩展拥有独自的全局变量的可能，正如前面提到的。</p>
<p>当编写PHP扩展时，无论是在多进程或多线程环境中，都是依靠这一机制访问扩展自己的全局变量。如果使用全局变量访问宏（例如MYFILE_G()宏），需要确保TSRM上下文信息出现在当前函数中。基于性能的原因，Zend引擎试图把这个上下文信息作为参数传递到更多的地方，包括PHP_FUNCTION()的定义。正因为这样，在PHP_FUNCTION()内当编写的代码使用访问宏（例如MYFILE_G()宏）时，不需要做任何特殊的声明。然而，如果PHP函数调用其他需要访问全局变量的C函数，要么把上下文作为一个额外的参数传递给C函数，要么提取上下文（要慢点）。</p>
<p>在需要访问全局变量的代码块开头使用TSRMLS_FETCH()来提取上下文。例如：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">void myfunc(){
    <li>TSRMLS_FETCH();
    <li>&nbsp;
    <li>MYFILE_G(myglobal) = 2;
    <li>}</li>
</ol>
</div>
<p>如果希望让代码更加优化，更好的办法是直接传递上下文给函数（正如前面叙述的，PHP_FUNCTION()范围内自动可用）。可以使用TSRMLS_C（C表示调用Call）和TSRMLS_CC（CC边式调用Call和逗号Comma）宏。前者应当用于仅当上下文作为一个单独的参数，后者应用于接受多个参数的函数。在后一种情况中，因为根据取名，逗号在上下文的前面，所以TSRMLS_CC不能是第一个函数参。</p>
<p>在函数原形中，可以分别使用TSRMLS_D和TSRMLS_DC宏声名正在接收上下文。 </p>
<p>下面是前一例子的重写，利用了参数传递上下文。</p>
<div class="hl-surround">
<ol class="hl-main ln-show" ondblclick="linenumber(this)" title="Double click to hide line number.">
    <li class="hl-firstline">void myfunc(TSRMLS_D){
    <li>MYFILE_G(myglobal) = 2;
    <li>}
    <li>&nbsp;
    <li>PHP_FUNCTION(my_php_function)
    <li>{
    <li>&#8230;
    <li>myfunc(TSRMLS_C);
    <li>&#8230;
    <li>}</li>
</ol>
</div>
<p>&nbsp;</p>
<p>
<h3>总 结</h3>
<p>现在，你已经学到了足够的东西来创建自己的扩展。本章讲述了一些重要的基础来编写和理解PHP扩展。Zend引擎提供的扩展API相当丰富，使你能够开发面向对象的扩展。几乎没有文档谈几许多高级特性。当然，依靠本章所学的基础知识，你可以通过浏览现有的原码学到很多。</p>
<p>更多关于信息可以在PHP手册的扩展PHP章节http://www.php.net/manual/en/zend.php中找到。另外，你也可以考虑加入PHP开发者邮件列表internals@ lists.php.net，该邮件列表围绕开发PHP 本身。你还可以查看一下新的扩展生成工具——PECL_Gen(http://pear.php.net/package/PECL_Gen)，这个工具正在开发之中，比起本章使用的ext_skel有更多的特性。</p>
<p>此外你还可以关注<a href="http://www.laruence.com/">风雪之隅</a>, 会有更多相关知识更新. </p>
<h3>词汇表</h3>
<table>
    <tbody>
        <tr>
            <td>binary safe </td>
            <td>二进制安全 </td>
        </tr>
        <tr>
            <td>context </td>
            <td>上下文 </td>
        </tr>
        <tr>
            <td>extensions </td>
            <td>扩展 </td>
        </tr>
        <tr>
            <td>entry </td>
            <td>条目 </td>
        </tr>
        <tr>
            <td>skeleton </td>
            <td>骨架 </td>
        </tr>
    </tbody>
</table>
<p>Thread-Safe Resource Manager TSRM 线程安全资源管理器<br />
</coolcode></p>
<div class="hl-surround">
<div class="hl-main">Contact info:<br />
Email: taft at wjl.cn / laruence at yahoo.com.cn<br />
http://www.laruence.com</div>
</div>
<p>——————————————————————————&#8211;<br />
[1] 可参考译者写的<br />
[2] 译者：可以使用phpcli程序在控制台里执行php文件。<br />
[3] 译者：可以查看到生成的FETCH_RESOURCE()宏参数是一些&#8217;???&#8217;。</p>
</div>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/293433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2009-09-01 12:21 <a href="http://www.blogjava.net/xiaomage234/archive/2009/09/01/293433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下C++实现PHP扩展中级应用 (转)</title><link>http://www.blogjava.net/xiaomage234/archive/2009/08/31/293353.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 31 Aug 2009 09:57:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2009/08/31/293353.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/293353.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2009/08/31/293353.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/293353.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/293353.html</trackback:ping><description><![CDATA[<p>（一）:<br />
此篇文章准备分2个部分来讲述：<br />
第一部分主要详细讲述一下怎么构建一个完成的C++应用扩展模块；<br />
第二部分主要讲述在PHP及Zend框架下怎么使用Zend API和C++语言来实现自己所要的功能以及项目的开发；<br />
此篇文章所运用的环境在Linux 2.4.21-4.ELsmp(Red Hat Linux 3.2.3-20)，Apache/2.2.8，gcc version 3.2.3 20030502，PHP 5.2.5 (cli)，Zend Engine v2.2.0下进行。</p>
<p>一、前言<br />
以前写过一些使用C语言来扩展PHP的应用[1]。在淘宝使用C++做PHP的扩展做项目的过程中，遇到了一些问题，从Google中查找，使用C++来开发PHP的中文文章少之又少，而且没有一个手册来告诉用户怎么写m4[2]文件，怎么使用zend[3]引擎的一套api函数去写相关PHP的接口，这里就怎么用C++语言来开发PHP的一些心得介绍给大家，希望有心人能够有所收获；<br />
二、为什么要用C++开发PHP<br />
使用C++比用C语言开发PHP主要有2个好处：<br />
使用C++能够很方便的操作string类型，本身的一些容器和模板[4]、以及面对对象的功能让开发者能够节省大量开发的时间，这是比较重要的一点；<br />
C++可以直接使用C的库，只需要extern &#8220;C&#8221; {}将其C的头文件和库定义包含起来就可以，不需要太多的移植工作，可以重复利用前人的代码或者库进行后续的工作；<br />
用C++开发PHP是快速、迅捷的，熟悉了相关的定义以及语法，相信开发PHP不是难事。<br />
<br />
三、书写config文件</p>
<p>config.m4[5]或config.w32[6]文件是编译基础中最核心的文件，这个文件主要是用autoconf[7]来产生configure[8]配置文件，继而自动生成大家所熟悉的Makefile文件，以Linux系统为例： </p>
<p>你可以自己书写config.m4文件，也可以由Shell脚本 ext_skel[9] 来生成样板：<br />
[cnangel@localhost ~]$wget http://docs.php.net/get/php-5.2.5.tar.bz2/from/cn.php.net/mirror<br />
[cnangel@localhost ~]$tar -jxf php-5.2.5.tar.bz2<br />
[cnangel@localhost ~]$cd php-5.2.6/ext<br />
[cnangel@localhost ext]./ext_skel &#8211;extname=extern_name</p>
<p>接着你会发现在ext目录下多了一个叫extern_name的目录。进入该目录，会发现目录下有几个文件：<br />
[cnangel@localhost ext_name]$ls -l<br />
总计 32<br />
-rw-r&#8211;r&#8211; 1 cnangel cnangel 2103 06-29 19:00 config.m4<br />
-rw-r&#8211;r&#8211; 1 cnangel cnangel 310 06-29 19:00 config.w32<br />
-rw-r&#8211;r&#8211; 1 cnangel cnangel 8 06-29 19:00 CREDITS<br />
-rw-r&#8211;r&#8211; 1 cnangel cnangel 0 06-29 19:00 EXPERIMENTAL<br />
-rw-r&#8211;r&#8211; 1 cnangel cnangel 5305 06-29 19:00 ext_name.c<br />
-rw-r&#8211;r&#8211; 1 cnangel cnangel 508 06-29 19:00 ext_name.php<br />
-rw-r&#8211;r&#8211; 1 cnangel cnangel 2766 06-29 19:00 php_ext_name.h<br />
drwxr-xr-x 2 cnangel cnangel 4096 06-29 19:00 tests</p>
<p>然后可以根据提示来修改config.m4文件，这里有几个重要的宏命令如下：<br />
dnl 是注释；<br />
PHP_ARG_WITH 或者 PHP_ARG_ENABLE 指定了PHP扩展模块的工作方式，前者意味着不需要第三方库，后者正好相反；<br />
PHP_REQUIRE_CXX 用于指定这个扩展用到了C++；<br />
PHP_ADD_INCLUDE 指定PHP扩展模块用到的头文件目录；<br />
PHP_CHECK_LIBRARY 指定PHP扩展模块PHP_ADD_LIBRARY_WITH_PATH定义以及库连接错误信息等；<br />
PHP_ADD_LIBRARY(stdc++,&#8221;",EXTERN_NAME_LIBADD)用于将标准C++库链接进入扩展<br />
PHP_SUBST(EXTERN_NAME_SHARED_LIBADD) 用于说明这个扩展编译成动态链接库的形式；<br />
PHP_NEW_EXTENSION 用于指定有哪些源文件应该被编译,文件和文件之间用空格隔开； </p>
<p>ext_skel默认生成的模块框架是针对C的,我们要使用C++进行PHP扩展, 那除以上的PHP_REQUIRE_CXX, PHP_ADD_LIBRARY两个宏必需外，还要把extern_name.c改名成extern_name.cpp。 </p>
<p>需要注意的是，在config.m4里面可以使用类似的Makefile语法，片段如下：<br />
PHP_REQUIRE_CXX()<br />
INCLUDES=&#8221;$INCLUDES `mysql_config &#8211;cflags`&#8221;<br />
PHP_ADD_LIBRARY(stdc++, &#8220;&#8221;, EXTRA_LDFLAGS)<br />
EXTRA_LDFLAGS=&#8221;$EXTRA_LDFLAGS `mysql_config &#8211;libs` -lmemcached&#8221;<br />
AC_CHECK_HEADERS([mysql/mysql.h])<br />
CPPFILE=&#8221;ext_name.cpp antiForbitWord.cpp antiBaseDict.cpp Trie.cpp Logger.cpp antiEncodeConverter.cpp strnormalize.cpp&#8221;<br />
PHP_NEW_EXTENSION(ext_name, $CPPFILE, $ext_shared)<br />
四、书写.h文件</p>
<p>这里指修改php_ext_name.h这个头文件。 </p>
<p>由于TSRM.h这个文件所包含的函数和类都是用纯C语言写的，故应该使用extern来说明如下：<br />
extern &#8220;C&#8221; {<br />
#ifdef ZTS<br />
#include &#8220;TSRM.h&#8221;<br />
#endif<br />
}</p>
<p>如果该php_ext_name.h头文件或者ext_name.cpp文件用到了C++语言中的一些容器或者一些函数，则需要在头文件中包含相应的c++库的头文件，否则会出现找不到相应的C++函数错误。<br />
五、书写.cpp文件</p>
<p>这里指修改ext_name.cpp这个cpp文件。 </p>
<p>由于config.h、php.h、php_ini.h和ext/standard/info.h中包含的函数和类如TSRM.h一样，都是用纯C语言写的，所以也需要用extern说明如下：<br />
extern &#8220;C&#8221; {<br />
#ifdef HAVE_CONFIG_H<br />
#include &#8220;config.h&#8221;<br />
#endif<br />
#include &#8220;php.h&#8221;<br />
#include &#8220;php_ini.h&#8221;<br />
#include &#8220;ext/standard/info.h&#8221;<br />
}</p>
<p>而 #include &#8220;php_ext_name.h&#8221; 这句则已经不需要包含在extern &#8220;C&#8221;内，另外，ZEND_GET_MODULE这个宏命令也是需要特别申明如下：<br />
#ifdef COMPILE_DL_EXT_NAME<br />
BEGIN_EXTERN_C()<br />
ZEND_GET_MODULE(ext_name)<br />
END_EXTERN_C()<br />
#endif</p>
<p>总之，把一些C写的库或轰用兼容的方式给解决。<br />
六、初步执行</p>
<p>这里需要用到一个命令：phpize[10]，命令如下：<br />
[cnangel@localhost ext_name]$phpize<br />
[cnangel@localhost ext_name]$./configure<br />
[cnangel@localhost ext_name]$make</p>
<p>注意：可以使用用phpize生成configure执行文件后，可以使用./configure &#8211;help查看帮助信息，修改config.m4文件可以修改configure的帮助信息。每次修改了config.m4文件，需要使用清除临时文件 命令phpize &#8211;clean来完成消除configure。<br />
七、初步应用</p>
<p>怎么应用到php上，把刚才的扩展模块当作一个普通的php函数调用呢？简单的应用直接使用命令：<br />
[cnangel@localhost ext_name]$sudo make install</p>
<p>如果有多个php版本，则寻找扩展库目录显得没有那么好找了，比如，你的php执行文件的路径在/usr/local/php/bin/目录下，想知道php扩展模块所在的目录的话，那么执行(PHP5.0以上)：<br />
[cnangel@localhost ext_name]$/usr/local/php/bin/php-config | grep extension-dir | sed &#8217;s/.*\[\(.*\)]/\1/&#8217;`</p>
<p>PHP5.0以下执行：<br />
[cnangel@localhost ext_name]$/usr/local/php/bin/php-config &#8211;extension-dir</p>
<p>这样你可以发现你的扩展库的路径：<br />
/usr/local/php/lib/php/extensions/no-debug-non-zts-20060613</p>
<p>当然，你可以修改php.ini，找到php安装的配置文件，修改extension_dir的值为你想要的一个路径另外，需要将你的扩展写入php.ini，像这样： </p>
<p>extension=ext_name.so</p>
<p>最后，找到扩展库的路径后，将modules下面的extern_name.so文件复制到扩展库的目录下，重新启动一下Apache进程：<br />
[cnangel@localhost ext_name]$which httpd<br />
/usr/bin/httpd<br />
[cnangel@localhost ext_name]$sudo /usr/bin/httpd -k stop<br />
[cnangel@localhost ext_name]$sudo /usr/bin/httpd -k start</p>
<p>把这个样例ext_name.php复制到web路径上去，看看是否好使啦？下一节我们将详细讲一些Zend API的宏在ext_name.cpp中的一些复杂应用。</p>
<p>——————————————————————————-<br />
（二）</p>
<p>这里主要讲述在PHP及Zend框架下怎么使用Zend API和C++语言来实现自己所要的功能以及项目的开发。<br />
此篇文章所运用的环境在Linux 2.4.21-4.ELsmp(Red Hat Linux<br />
3.2.3-20)，Apache/2.2.8，gcc version 3.2.3 20030502，PHP 5.2.5 (cli)，Zend<br />
Engine v2.2.0下进行。<br />
前言</p>
<p>上次我们说到使用c++写一个完整的php扩展，这里以ext_name模块为例复习一下： </p>
<p>首先仍然修改config.m4文件，由于没有引用外面的模块或者相关库，所以不需要使用PHP_ARG_WITH的方式，使用PHP_ARG_ENABLE方式。找到<br />
PHP_NEW_EXTENSION(ext_name, ext_name.c, $ext_shared)</p>
<p>修改成<br />
PHP_REQUIRE_CXX()<br />
PHP_ADD_LIBRARY(stdc++, &#8220;&#8221;, EXTRA_LDFLAGS)<br />
PHP_NEW_EXTENSION(ext_name, ext_name.cpp, $ext_shared)</p>
<p>并将ext_name.c重新命名为ext_name.cpp，接着修改其内容，将<br />
#include &#8220;php.h&#8221;<br />
#include &#8220;php_ini.h&#8221;<br />
#include &#8220;ext/standard/info.h&#8221;</p>
<p>用extern &#8220;C&#8221;将其用大括号括起来，修改<br />
ZEND_GET_MODULE(ext_name)</p>
<p>为<br />
BEGIN_EXTERN_C()<br />
ZEND_GET_MODULE(ext_name)<br />
END_EXTERN_C()</p>
<p>到此为止，这就是我们第一章内容，第二章比较庞大，这里还是分节来叙述吧。<br />
概述</p>
<p>概述里面主要简单介绍PHP扩展中的一些大致结构和需要注意的事项，做过C扩展PHP的都会知道 PHP_FE是一个宏把这个宏标识的函数，例如：helloworld，这个函数可以直接作用于PHP解释器，比如<br />
&lt; ?php<br />
helloworld();<br />
?&gt;</p>
<p>安装ext_name样板后，系统会自动有一个函数confirm_ext_name_compiled，这个函数是可以自行修改的，当然，PHP_FE可以定义多个函数，这些函数都必须在之前进行申明，一般在php_ext_name.h头文件进行申明。 </p>
<p>我们还知道，仅仅有头文件和PHP_FE宏来申明这个函数是不行的，这个函数还没有内容，怎么编写这个函数的内容呢？这个在接下来会讲到。 </p>
<p>其实，稍微细心的人看了ext_name.cpp就知道，去掉注释后，还有很多的宏命令，比如zend_module_entry、ZEND_GET_MODULE、PHP_MINIT_FUNCTION等等，读者不要着急，下面会一一道来。 </p>
<p>关于ext_name.cpp文件中一些变量的命名，通常是PHP模块名（eg:ext_name）前面或者后面有一串字符，比如 le_ext_name、ext_name_functions、这是一种习惯，最好我们在书写的时候遵循这种习惯，这样写出来的代码不仅仅让你自己明 白，让其他的开发人员也能够很快熟悉你的代码。通常一些定义的常量会大写，比如要定义这个模块的名字和版本，可以在头文件中添加：<br />
#define PHP_EXT_NAME_EXTNAME &#8220;ext_name&#8221;<br />
#define PHP_EXT_NAME_VERSION &#8220;0.1&#8243;</p>
<p>然后修改ext_name_module_entry的内容，将&#8221;ext_name&#8221;和&#8221;0.1&#8243;分别用PHP_EXT_NAME_EXTNAME和PHP_EXT_NAME_VERSION来替换，这样具有方便且通用。 </p>
<p>如果你可能在代码中可能需要用到stl之类的或者c++的一些库，那么你可以在ext_name.cpp文件中添加<br />
#ifndef __APP_CPP__<br />
#define __APP_CPP__<br />
#include <iostream><br />
#include <fstream><br />
#include <string><br />
/*<br />
#include <sstream><br />
#include
<list>#include <vector><br />
#include<br />
<map>#include <hashmap><br />
#include <set><br />
#include <bitset><br />
*/<br />
#endif<br />
PHP 与 Zend API</p>
<p>引用一句经典的原文来说明PHP和Zend API之间的关系<br />
PHP的核心由两部分组成。最底层是Zend引擎（ZE）。ZE把人类易读的脚本解析成机器可读的符号，<br />
然后在进程空间内执行这些符号。ZE也处理内存管理、变量作用域及调度程序调用。另一部分是PHP内核，<br />
它绑定了SAPI层（Server Application Programming Interface，通常涉及主机环境，如Apache，IIS，CLI，CGI等），<br />
并处理与它的通信。它同时对safe_mode和open_basedir的检测提供一致的控制层，就像流层将fopen()、fread()和<br />
fwrite()等用户空间的函数与文件和网络I/O联系起来一样。<br />
模块信息</p>
<p>模块信息主要体现在ext_name_module_entry结构上，它包含了 </p>
<p>1, 标准模块的头 </p>
<p>通常用 &#8220;STANDARD_MODULE_HEADER&#8221; 来填充，它指定了模块的四个成员：<br />
标识整个模块结构大小的 size<br />
值为 ZEND_MODULE_API_NO 常量的 zend_api<br />
标识是否为调试版本（使用 ZEND_DEBUG 进行编译）的 zend_debug<br />
还有一个用来标识是否启用了 ZTS （Zend 线程安全，使用 ZTS 或USING_ZTS 进行编译）的 zts。 </p>
<p>2, 模块名称 </p>
<p>模块名称这个名字就是使用 phpinfo() 函数后在&#8220;Additional Modules&#8221;部分所显示的名称。 </p>
<p>3, PHP扩展可用到的函数或类 </p>
<p>zend函数块的指针 </p>
<p>4, 模块启动函数 </p>
<p>5, 模块关闭函数 </p>
<p>6, 请求启动函数 </p>
<p>7, 请求关闭函数 </p>
<p>8, 模块信息函数 </p>
<p>9, 模块的版本号 </p>
<p>10,　其它结构元素</bitset></set></hashmap></map>
<p></vector></list>
</sstream></string></fstream></iostream></p>
<p>原文：http://my.huhoo.net/archives/2008/02/php_ip.html#1<br />
参考：<br />
快速开发一个PHP扩展：http://blog.csdn.net/heiyeshuwu/archive/2008/12/05/3453854.aspx<br />
如何编写PHP扩展：http://blog.csdn.net/taft/archive/2006/02/10/596291.aspx</p>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/293353.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2009-08-31 17:57 <a href="http://www.blogjava.net/xiaomage234/archive/2009/08/31/293353.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用C语言写PHP扩展的步骤[转]</title><link>http://www.blogjava.net/xiaomage234/archive/2009/08/31/293352.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 31 Aug 2009 09:55:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2009/08/31/293352.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/293352.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2009/08/31/293352.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/293352.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/293352.html</trackback:ping><description><![CDATA[<p>用C语言，php的扩展的书写格式(ZEND API)写PHP扩展的步骤：<br />
我用的php环境 php 5.2.5，完成最基本功能 helloword</p>
<p>cd /php5.2.5/ext</p>
<p>生成骨架 ./ext_skel --extname=cltest 会在当前目录建立一个cltest的目录</p>
<p>进入该目录 cd cltest</p>
<p>修改 配置文件config.m4<br />
vi cltest/config.m4&nbsp; </p>
<p>注释3个dnl [dnl代表注释,对应shell的#]<br />
PHP_ARG_WITH(my_module, for my_module support,<br />
Make sure that the comment is aligned:<br />
[ --with-my_module Include my_module support])</p>
<p>修改完毕。</p>
<p>vi cltest.c<br />
改成这样:<br />
function_entry my_module_functions[] = {<br />
PHP_FE(say_hello, NULL) /* ?添加这一行代码&nbsp;&nbsp; 注册函数say_hello() */<br />
PHP_FE(confirm_my_module_compiled, NULL) /* For testing, remove later. */<br />
{NULL, NULL, NULL} /* Must be the last line in my_module_functions[] */<br />
};</p>
<p>另外在文件的最后添加下列代码 函数程序主体<br />
PHP_FUNCTION(say_hello)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RETURN_STRINGL("hello world",100,1);<br />
}</p>
<p>修改完毕。</p>
<p>&nbsp;</p>
<p>vi php_cltest.h</p>
<p>在文件中PHP_FUNCTION(confirm_my_module_compiled);一行前面添加下面的代码 函数定义<br />
PHP_FUNCTION(say_hello);</p>
<p>修改完毕。</p>
<p>&nbsp;</p>
<p>找到这个执行文件phpize ，在cltest目录下执行命令，用于加载动态链接库</p>
<p>/usr/local/php/bin/phpize<br />
./configure --enable-cltest --with-apxs2=/usr/local/apache2/bin/apxs --with-php-config=/usr/local/php/bin/php-config</p>
<p>make<br />
会在当前的目录下生成一个目录叫modules他的下面就放着你要的cltest.so文件</p>
<p>make install <br />
会cp modules/cltest.so /usr/local/php/include/php/ext/<br />
其余的就是修改 php.ini加载该.so webserver要生效需要重启。</p>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/293352.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2009-08-31 17:55 <a href="http://www.blogjava.net/xiaomage234/archive/2009/08/31/293352.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux静态/动态链接库的创建和使用 [转]</title><link>http://www.blogjava.net/xiaomage234/archive/2009/07/10/286216.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 10 Jul 2009 03:54:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2009/07/10/286216.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/286216.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2009/07/10/286216.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/286216.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/286216.html</trackback:ping><description><![CDATA[<p>from : http://hi.baidu.com/xiaochongs/blog/item/23a70b5592e662c4b645aef2.html<br />
<br />
和<span style="line-height: 150%">Windows</span><span style="line-height: 150%">系统一样</span><span style="line-height: 150%">Linux</span><span style="line-height: 150%">也有静态</span><span style="line-height: 150%">/</span><span style="line-height: 150%">动态链接库，下面介绍创建和使用方法：</span><span style="line-height: 150%">&nbsp;
<table height="250" cellspacing="0" cellpadding="0" width="300" align="right" border="0">
    <tbody>
        <tr>
            <td>&nbsp; &nbsp; </td>
        </tr>
    </tbody>
</table>
<br />
</span><span style="line-height: 150%">假设有下面几个文件：</span><span style="line-height: 150%">&nbsp;<br />
</span><span style="line-height: 150%">头文件</span><span style="line-height: 150%">String.h</span><span style="line-height: 150%">，声明相关函数原形，内容如下：</span><span style="line-height: 150%">&nbsp;<br />
Strlen.c</span><span style="line-height: 150%">：函数</span><span style="line-height: 150%">Strlen</span><span style="line-height: 150%">的实现，获取给定字符串的长度，内容如下：</span><span style="line-height: 150%">&nbsp;<br />
Strlnen.c</span><span style="line-height: 150%">：函数</span><span style="line-height: 150%">StrNlen</span><span style="line-height: 150%">的实现，获取给定字符串的长度，如果输入字符串的长度大于指定的最大长度，则返回最大长度，否者返回字符串的实际长度，内容如下：</span><span style="line-height: 150%">&nbsp;<br />
</span><span style="line-height: 150%">生成静态库：</span><span style="line-height: 150%">&nbsp;<br />
&nbsp;</span><span style="line-height: 150%">利用</span><span style="line-height: 150%">GCC</span><span style="line-height: 150%">生成对应目标文件：</span><span style="line-height: 150%">&nbsp;<br />
gcc&nbsp;&#8211;c&nbsp;Strlen.c&nbsp;Strnlen.c&nbsp;<br />
</span><span style="line-height: 150%">如果对应的文件没有错误，</span><span style="line-height: 150%">gcc</span><span style="line-height: 150%">会对文件进行编译生成</span><span style="line-height: 150%">Strlen.o</span><span style="line-height: 150%">和</span><span style="line-height: 150%">Strnlen.o</span><span style="line-height: 150%">两个目标文件</span><span style="line-height: 150%">(</span><span style="line-height: 150%">相当于</span><span style="line-height: 150%">windows</span><span style="line-height: 150%">下的</span><span style="line-height: 150%">obj</span><span style="line-height: 150%">文件</span><span style="line-height: 150%">)</span><span style="line-height: 150%">。然后用</span><span style="line-height: 150%">ar</span><span style="line-height: 150%">创建一个名字为</span><span style="line-height: 150%">libstr.a</span><span style="line-height: 150%">的库文件，并把</span><span style="line-height: 150%">Strlen.o&nbsp;</span><span style="line-height: 150%">和</span><span style="line-height: 150%">Strnlen.o</span><span style="line-height: 150%">的内容插入到对应的库文件中。，相关命令如下：</span><span style="line-height: 150%">&nbsp;<br />
ar&nbsp;&#8211;rc&nbsp;libstr.a&nbsp;Strlen.o&nbsp;Strnlen.o&nbsp;<br />
</span><span style="line-height: 150%">命令执行成功以后，对应的静态库</span><span style="line-height: 150%">libstr.a</span><span style="line-height: 150%">已经成功生成。</span><span style="line-height: 150%">&nbsp;<br />
<br />
</span>
<table style="border-right: medium none; border-top: medium none; background: #f3f3f3; border-left: medium none; border-bottom: medium none; border-collapse: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td valign="top" width="568">
            <div style="line-height: 150%"><span style="line-height: 150%">/***********************************&nbsp;<br />
            Filename&nbsp;:&nbsp;String.h&nbsp;<br />
            Description&nbsp;:&nbsp;<br />
            Author&nbsp;&nbsp;&nbsp;:&nbsp;HCJ&nbsp;<br />
            Date&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;2006-5-7&nbsp;<br />
            ************************************/&nbsp;<br />
            <br />
            int&nbsp;Strlen(char&nbsp;*pStr);&nbsp;<br />
            int&nbsp;StrNlen(char&nbsp;*pStr,&nbsp;unsigned&nbsp;long&nbsp;ulMaxLen);&nbsp;<br />
            <br />
            </span></div>
            </td>
        </tr>
    </tbody>
</table>
</p>
<div style="line-height: 150%"><span style="line-height: 150%"><br />
<br />
</span></div>
<p>
<table style="border-right: medium none; border-top: medium none; background: #f3f3f3; border-left: medium none; border-bottom: medium none; border-collapse: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td valign="top" width="568">
            <div style="line-height: 150%"><span style="line-height: 150%">/**************************************&nbsp;<br />
            Filename&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;get&nbsp;string&nbsp;length&nbsp;<br />
            Description&nbsp;&nbsp;:&nbsp;&nbsp;<br />
            Author&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;HCJ&nbsp;<br />
            Date&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;2006/5/7&nbsp;<br />
            **************************************/&nbsp;<br />
            #include&lt;stdio.h&gt;&nbsp;<br />
            #include&lt;assert.h&gt;&nbsp;<br />
            int&nbsp;Strlen(char&nbsp;*pStr)&nbsp;<br />
            {&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;long&nbsp;ulLength;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;assert(NULL&nbsp;!=&nbsp;pStr);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;ulLength&nbsp;=&nbsp;0;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;while(*pStr++)&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ulLength++;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ulLength;&nbsp;<br />
            }&nbsp;<br />
            <br />
            </span></div>
            </td>
        </tr>
    </tbody>
</table>
</p>
<div style="line-height: 150%"><span style="line-height: 150%"><br />
<br />
</span></div>
<p>
<table style="border-right: medium none; border-top: medium none; background: #f3f3f3; border-left: medium none; border-bottom: medium none; border-collapse: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td valign="top" width="568">
            <div style="line-height: 150%"><span style="line-height: 150%">**********************************************&nbsp;<br />
            Fileneme:&nbsp;mystrnlen.c&nbsp;<br />
            Description:&nbsp;get&nbsp;input&nbsp;string&nbsp;length,if&nbsp;string&nbsp;large&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max&nbsp;length&nbsp;input&nbsp;return&nbsp;max&nbsp;length,&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;real&nbsp;length&nbsp;<br />
            Author:&nbsp;HCJ&nbsp;<br />
            Date&nbsp;&nbsp;:&nbsp;2006-5-7&nbsp;<br />
            **********************************************/&nbsp;<br />
            #include&lt;stdio.h&gt;&nbsp;<br />
            #include&lt;assert.h&gt;&nbsp;<br />
            int&nbsp;StrNlen(char&nbsp;*pStr,&nbsp;unsigned&nbsp;long&nbsp;ulMaxLen)&nbsp;<br />
            {&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;long&nbsp;ulLength;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;assert(NULL&nbsp;!=&nbsp;pStr);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;if(ulMaxLen&nbsp;&lt;=&nbsp;0)&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Wrong&nbsp;Max&nbsp;Length!\n");&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;-1;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;ulLength&nbsp;=&nbsp;0;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;while(*pStr++&nbsp;&amp;&amp;&nbsp;&nbsp;ulLength&nbsp;&lt;&nbsp;ulMaxLen)&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ulLength++;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ulLength;&nbsp;<br />
            }</span></div>
            </td>
        </tr>
    </tbody>
</table>
</p>
<div style="line-height: 150%"><span style="line-height: 150%"><br />
<br />
<br />
</span><span style="line-height: 150%">生成动态链接库：</span><span style="line-height: 150%">&nbsp;<br />
&nbsp;gcc&nbsp;&nbsp;-fpic&nbsp;-shared&nbsp;-o&nbsp;libstr.so&nbsp;&nbsp;Strlen.c&nbsp;Strnlen.c&nbsp;<br />
-fpic&nbsp;</span><span style="line-height: 150%">使输出的对象模块是按照可重定位地址方式生成的。</span><span style="line-height: 150%">&nbsp;<br />
-shared</span><span style="line-height: 150%">指定把对应的源文件生成对应的动态链接库文件</span><span style="line-height: 150%">libstr.so</span><span style="line-height: 150%">文件。</span><span style="line-height: 150%">&nbsp;<br />
</span><span style="line-height: 150%">对应的链接库已经生成，下面看一下如何使用对应的链接库。</span><span style="line-height: 150%">&nbsp;<br />
</span><span style="line-height: 150%">静态库的使用：</span><span style="line-height: 150%">&nbsp;<br />
</span><span style="line-height: 150%">假设有下面的文件要使用对应的的静态库</span><span style="line-height: 150%">:&nbsp;<br />
</span><span style="line-height: 150%">编译生成对应的目标文件：</span><span style="line-height: 150%">&nbsp;<br />
gcc&nbsp;-c&nbsp;-I/home/hcj/xxxxxxxx&nbsp;main.c&nbsp;&nbsp;<br />
</span><span style="line-height: 150%">生成可执行文件：</span><span style="line-height: 150%">&nbsp;<br />
gcc&nbsp;-o&nbsp;main1&nbsp;-L/home/hcj/xxxxxxxx&nbsp;main.o&nbsp;libstr.a&nbsp;&nbsp;<br />
</span><span style="line-height: 150%">其中</span><span style="line-height: 150%">-I/home/hcj/xxxxxxxx</span><span style="line-height: 150%">和</span><span style="line-height: 150%">-L/home/hcj/xxxxxxxx</span><span style="line-height: 150%">是通过</span><span style="line-height: 150%">-I</span><span style="line-height: 150%">和</span><span style="line-height: 150%">-L</span><span style="line-height: 150%">指定对应的头文件和库文件的路径。</span><span style="line-height: 150%">libstr.a</span><span style="line-height: 150%">是对应的静态库的名称。这样对应的静态库已经编译到对应的可执行程序中。执行对应的可执行文件便可以对应得函数调用的结果。</span><span style="line-height: 150%">&nbsp;<br />
<br />
</span></div>
<p>
<table style="border-right: medium none; border-top: medium none; background: #f3f3f3; border-left: medium none; border-bottom: medium none; border-collapse: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td valign="top" width="568">
            <div style="line-height: 150%"><span style="line-height: 150%">/*****************************************&nbsp;<br />
            FileName:&nbsp;main.c&nbsp;<br />
            Description:&nbsp;test&nbsp;static/dynamic&nbsp;library&nbsp;<br />
            Author:&nbsp;HCJ&nbsp;<br />
            Date&nbsp;&nbsp;:&nbsp;2005-5-7&nbsp;<br />
            ******************************************/&nbsp;<br />
            #include&lt;stdio.h&gt;&nbsp;<br />
            #include&nbsp;&lt;String.h&gt;&nbsp;&nbsp;&nbsp;//</span><span style="line-height: 150%">静态库对应函数的头文件</span><span style="line-height: 150%">&nbsp;<br />
            int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[])&nbsp;<br />
            {&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;str[]&nbsp;=&nbsp;{"hello&nbsp;world"};&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;long&nbsp;ulLength&nbsp;=&nbsp;0;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;printf("The&nbsp;string&nbsp;is&nbsp;:&nbsp;%s\n",&nbsp;str);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;ulLength&nbsp;=&nbsp;Strlen(str);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;printf("The&nbsp;string&nbsp;length&nbsp;is&nbsp;:&nbsp;%d(use&nbsp;Strlen)\n",&nbsp;ulLength);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;ulLength&nbsp;=&nbsp;StrNlen(str,&nbsp;10);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;printf("The&nbsp;string&nbsp;length&nbsp;is&nbsp;:&nbsp;%d(use&nbsp;StrNlen)\n",&nbsp;ulLength);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;&nbsp;<br />
            }&nbsp;<br />
            <br />
            </span></div>
            </td>
        </tr>
    </tbody>
</table>
</p>
<div style="line-height: 150%"><span style="line-height: 150%"><br />
<br />
</span><span style="line-height: 150%">动态库的分为隐式调用和显式调用两种调用方法：</span><span style="line-height: 150%">&nbsp;<br />
</span><span style="line-height: 150%">隐式调用的使用使用方法和静态库的调用差不多，具体方法如下：</span><span style="line-height: 150%">&nbsp;<br />
gcc&nbsp;-c&nbsp;-I/home/hcj/xxxxxxxx&nbsp;main.c&nbsp;&nbsp;<br />
gcc&nbsp;-o&nbsp;main1&nbsp;-L/home/hcj/xxxxxxxx&nbsp;main.o&nbsp;libstr.so&nbsp;&nbsp;//</span><span style="line-height: 150%">这里是</span><span style="line-height: 150%">*.so&nbsp;<br />
</span><span style="line-height: 150%">在这种调用方式中，需要维护动态链接库的配置文件</span><span style="line-height: 150%">/etc/ld.so.conf</span><span style="line-height: 150%">来让动态链接库为系统所使用，通常将动态链接库所在目录名追加到动态链接库配置文件中。否则在执行相关的可执行文件的时候就会出现载入动态链接库失败的现象。在编译所引用的动态库时，可以在</span><span style="line-height: 150%">gcc</span><span style="line-height: 150%">采用</span><span style="line-height: 150%">&nbsp;&#8211;l</span><span style="line-height: 150%">或</span><span style="line-height: 150%">-L</span><span style="line-height: 150%">选项或直接引用所需的动态链接库方式进行编译。在</span><span style="line-height: 150%">Linux</span><span style="line-height: 150%">里面，可以采用</span><span style="line-height: 150%">ldd</span><span style="line-height: 150%">命令来检查程序依赖共享库。</span><span style="line-height: 150%">&nbsp;<br />
</span><span style="line-height: 150%">显式调用：</span><span style="line-height: 150%">&nbsp;<br />
<br />
</span></div>
<p>
<table style="border-right: medium none; border-top: medium none; background: #f3f3f3; border-left: medium none; border-bottom: medium none; border-collapse: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td valign="top" width="568">
            <div style="line-height: 150%"><span style="line-height: 150%">/*****************************************&nbsp;<br />
            FileName:&nbsp;main2.c&nbsp;<br />
            Description:&nbsp;test&nbsp;static/dynamic&nbsp;library&nbsp;<br />
            Author:&nbsp;HCJ&nbsp;<br />
            Date&nbsp;&nbsp;:&nbsp;2005-5-7&nbsp;<br />
            ******************************************/&nbsp;<br />
            #include&lt;stdio.h&gt;&nbsp;<br />
            #include&lt;dlfcn.h&gt;&nbsp;<br />
            int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[])&nbsp;<br />
            {&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;//define&nbsp;function&nbsp;pointor&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;(*pStrlenFun)(char*&nbsp;pStr);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//</span><span style="line-height: 150%">声明对应的函数的函数指针</span><span style="line-height: 150%">&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;(*pStrnlenFun)(char*&nbsp;pStr,&nbsp;int&nbsp;ulMaxLen);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;str[]&nbsp;=&nbsp;{"hello&nbsp;world"};&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;long&nbsp;ulLength&nbsp;=&nbsp;0;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;*pdlHandle;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;*pszErr;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;pdlHandle&nbsp;=&nbsp;dlopen("./libstr.so",&nbsp;RTLD_LAZY);&nbsp;&nbsp;//</span><span style="line-height: 150%">加载链接库</span><span style="line-height: 150%">/libstr.so&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;if(!pdlHandle)&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Failed&nbsp;load&nbsp;library\n");&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;pszErr&nbsp;=&nbsp;dlerror();&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;if(pszErr&nbsp;!=&nbsp;NULL)&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%s\n",&nbsp;pszErr);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;//get&nbsp;function&nbsp;from&nbsp;lib&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;pStrlenFun&nbsp;=&nbsp;dlsym(pdlHandle,&nbsp;"Strlen");&nbsp;//</span><span style="line-height: 150%">获取函数的地址</span><span style="line-height: 150%">&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;pszErr&nbsp;=&nbsp;dlerror();&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;if(pszErr&nbsp;!=&nbsp;NULL)&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%s\n",&nbsp;pszErr);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;pStrnlenFun&nbsp;=&nbsp;dlsym(pdlHandle,&nbsp;"StrNlen");&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;pszErr&nbsp;=&nbsp;dlerror();&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;if(pszErr&nbsp;!=&nbsp;NULL)&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%s\n",&nbsp;pszErr);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;printf("The&nbsp;string&nbsp;is&nbsp;:&nbsp;%s\n",&nbsp;str);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;ulLength&nbsp;=&nbsp;pStrlenFun(str);&nbsp;&nbsp;&nbsp;//</span><span style="line-height: 150%">调用相关的函数</span><span style="line-height: 150%">&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;printf("The&nbsp;string&nbsp;length&nbsp;is&nbsp;:&nbsp;%d(use&nbsp;Strlen)\n",&nbsp;ulLength);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;ulLength&nbsp;=&nbsp;pStrnlenFun(str,&nbsp;10);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;printf("The&nbsp;string&nbsp;length&nbsp;is&nbsp;:&nbsp;%d(use&nbsp;StrNlen)\n",&nbsp;ulLength);&nbsp;<br />
            &nbsp;dlclose(pdlHandle);&nbsp;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;&nbsp;<br />
            }&nbsp;<br />
            <br />
            </span></div>
            </td>
        </tr>
    </tbody>
</table>
<span style="font-size: 10.5pt"></p>
<p><br />
<br />
<br />
gcc&nbsp;-o&nbsp;mian2&nbsp;-ldl&nbsp;main2.c&nbsp;<br />
</p>
<p></span><span style="font-size: 10.5pt">用</span><span style="font-size: 10.5pt">gcc</span><span style="font-size: 10.5pt">编译对应的源文件生成可执行文件，</span><span style="font-size: 10.5pt">-ldl</span><span style="font-size: 10.5pt">选项，表示生成的对象模块需要使用共享库。执行对应得文件同样可以得到正确的结果。</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">相关函数的说明如下：</span><span style="font-size: 10.5pt">&nbsp;<br />
(1)dlopen()&nbsp;<br />
</span><span style="font-size: 10.5pt">第一个参数：指定共享库的名称，将会在下面位置查找指定的共享库。</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">－环境变量</span><span style="font-size: 10.5pt">LD_LIBRARY_PATH</span><span style="font-size: 10.5pt">列出的用分号间隔的所有目录。</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">－文件</span><span style="font-size: 10.5pt">/etc/ld.so.cache</span><span style="font-size: 10.5pt">中找到的库的列表，用</span><span style="font-size: 10.5pt">ldconfig</span><span style="font-size: 10.5pt">维护。</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">－目录</span><span style="font-size: 10.5pt">usr/lib</span><span style="font-size: 10.5pt">。</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">－目录</span><span style="font-size: 10.5pt">/lib</span><span style="font-size: 10.5pt">。</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">－当前目录。</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">第二个参数：指定如何打开共享库。</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">－</span><span style="font-size: 10.5pt">RTLD_NOW</span><span style="font-size: 10.5pt">：将共享库中的所有函数加载到内存</span><span style="font-size: 10.5pt">&nbsp;<br />
</span><span style="font-size: 10.5pt">－</span><span style="font-size: 10.5pt">RTLD_LAZY</span><span style="font-size: 10.5pt">：会推后共享库中的函数的加载操作，直到调用</span><span style="font-size: 10.5pt">dlsym()</span><span style="font-size: 10.5pt">时方加载某函数</span><span style="font-size: 10.5pt">&nbsp;<br />
(2)dlsym()&nbsp;<br />
</span><span style="font-size: 10.5pt">调用</span><span style="font-size: 10.5pt">dlsym</span><span style="font-size: 10.5pt">时，利用</span><span style="font-size: 10.5pt">dlopen()</span><span style="font-size: 10.5pt">返回的共享库的</span><span style="font-size: 10.5pt">phandle</span><span style="font-size: 10.5pt">以及函数名称作为参数，返回要加载函数的入口地址。</span><span style="font-size: 10.5pt">&nbsp;<br />
(3)dlerror()&nbsp;<br />
</span><span style="font-size: 10.5pt">该函数用于检查调用共享库的相关函数出现的错误。</span><span style="font-size: 10.5pt">&nbsp;<br />
&nbsp;</span><span style="font-size: 10.5pt">这样我们就用简单的例子说明了在</span><span style="font-size: 10.5pt">Linux</span><span style="font-size: 10.5pt">下静态</span><span style="font-size: 10.5pt">/</span><span style="font-size: 10.5pt">动态库的创建和使用。</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/286216.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2009-07-10 11:54 <a href="http://www.blogjava.net/xiaomage234/archive/2009/07/10/286216.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GCC精彩之旅--转帖</title><link>http://www.blogjava.net/xiaomage234/archive/2008/09/19/229938.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 19 Sep 2008 06:55:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2008/09/19/229938.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/229938.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2008/09/19/229938.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/229938.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/229938.html</trackback:ping><description><![CDATA[来源：http://ffmpeg.blogbus.com/logs/445361.html<br />
<br />
在为Linux开发应用程序时，绝大多数情况下使用的都是C语言，因此几乎每一位Linux程序员面临的首要问题都是如何灵活运用C编译器。目前Linux下最常用的C语言编译器是GCC（GNU Compiler Collection），它是GNU项目中符合ANSI C标准的编译系统，能够编译用C、C++和Object C等语言编写的程序。GCC不仅功能非常强大，结构也异常灵活。最值得称道的一点就是它可以通过不同的前端模块来支持各种语言，如Java、Fortran、Pascal、Modula-3和Ada等。
<p>开放、自由和灵活是Linux的魅力所在，而这一点在GCC上的体现就是程序员通过它能够更好地控制整个编译过程。在使用GCC编译程序时，编译过程可以被细分为四个阶段：</p>
<p>◆ 预处理（Pre-Processing）</p>
<p>◆ 编译（Compiling）</p>
<p>◆ 汇编（Assembling）</p>
<p>◆ 链接（Linking）</p>
<p>Linux程序员可以根据自己的需要让GCC在编译的任何阶段结束，以便检查或使用编译器在该阶段的输出信息，或者对最后生成的二进制文件进行控制，以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。和其它常用的编译器一样，GCC也提供了灵活而强大的代码优化功能，利用它可以生成执行效率更高的代码。</p>
<p>GCC提供了30多条警告信息和三个警告级别，使用它们有助于增强程序的稳定性和可移植性。此外，GCC还对标准的C和C++语言进行了大量的扩展，提高程序的执行效率，有助于编译器进行代码优化，能够减轻编程的工作量。</p>
<p>GCC起步</p>
<p>在学习使用GCC之前，下面的这个例子能够帮助用户迅速理解GCC的工作原理，并将其立即运用到实际的项目开发中去。首先用熟悉的编辑器输入清单1所示的代码：</p>
<p>清单1：hello.c</p>
<p>&nbsp;#include &lt;stdio.h&gt; <br />
&nbsp;int main(void) <br />
&nbsp;{ <br />
&nbsp;&nbsp;printf ("Hello world, Linux programming!\n"); <br />
&nbsp;&nbsp;return 0; <br />
&nbsp;} <br />
&nbsp;<br />
然后执行下面的命令编译和运行这段程序：</p>
<p>&nbsp;# gcc hello.c -o hello # ./hello Hello world, Linux programming! <br />
&nbsp;<br />
从程序员的角度看，只需简单地执行一条GCC命令就可以了，但从编译器的角度来看，却需要完成一系列非常繁杂的工作。首先，GCC需要调用预处理程序cpp，由它负责展开在源文件中定义的宏，并向其中插入&#8220;#include&#8221;语句所包含的内容；接着，GCC会调用ccl和as将处理后的源代码编译成目标代码；最后，GCC会调用链接程序ld，把生成的目标代码链接成一个可执行程序。</p>
<p>为了更好地理解GCC的工作过程，可以把上述编译过程分成几个步骤单独进行，并观察每步的运行结果。第一步是进行预编译，使用-E参数可以让GCC在预处理结束后停止编译过程：</p>
<p>&nbsp;# gcc -E hello.c -o hello.i <br />
&nbsp;<br />
此时若查看hello.cpp文件中的内容，会发现stdio.h的内容确实都插到文件里去了，而其它应当被预处理的宏定义也都做了相应的处理。下一步是将hello.i编译为目标代码，这可以通过使用-c参数来完成：</p>
<p>&nbsp;# gcc -c hello.i -o hello.o <br />
&nbsp;<br />
GCC默认将.i文件看成是预处理后的C语言源代码，因此上述命令将自动跳过预处理步骤而开始执行编译过程，也可以使用-x参数让GCC从指定的步骤开始编译。最后一步是将生成的目标文件链接成可执行文件：</p>
<p>&nbsp;# gcc hello.o -o hello <br />
&nbsp;<br />
在采用模块化的设计思想进行软件开发时，通常整个程序是由多个源文件组成的，相应地也就形成了多个编译单元，使用GCC能够很好地管理这些编译单元。假设有一个由foo1.c和foo2.c两个源文件组成的程序，为了对它们进行编译，并最终生成可执行程序foo，可以使用下面这条命令：</p>
<p>&nbsp;# gcc foo1.c foo2.c -o foo <br />
&nbsp;<br />
如果同时处理的文件不止一个，GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来，上面这条命令大致相当于依次执行如下三条命令：</p>
<p>&nbsp;# gcc -c foo1.c -o foo1.o # gcc -c foo2.c -o foo2.o # gcc foo1.o foo2.o -o foo <br />
&nbsp;<br />
在编译一个包含许多源文件的工程时，若只用一条GCC命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译，并且每个源文件中都包含10000行代码，如果像上面那样仅用一条GCC命令来完成编译工作，那么GCC需要将每个源文件都重新编译一遍，然后再全部连接起来。很显然，这样浪费的时间相当多，尤其是当用户只是修改了其中某一个文件的时候，完全没有必要将每个文件都重新编译一遍，因为很多已经生成的目标文件是不会改变的。要解决这个问题，关键是要灵活运用GCC，同时还要借助像Make这样的工具。</p>
<p>警告提示功能</p>
<p>GCC包含完整的出错检查和警告提示功能，它们可以帮助Linux程序员写出更加专业和优美的代码。先来读读清单2所示的程序，这段代码写得很糟糕，仔细检查一下不难挑出很多毛病：</p>
<p>◆main函数的返回值被声明为void，但实际上应该是int；</p>
<p>◆使用了GNU语法扩展，即使用long long来声明64位整数，不符合ANSI/ISO C语言标准；</p>
<p>◆main函数在终止前没有调用return语句。</p>
<p>清单2：illcode.c</p>
<p>&nbsp;#include &lt;stdio.h&gt; <br />
&nbsp;void main(void) <br />
&nbsp;{ <br />
&nbsp;&nbsp;long long int var = 1;<br />
&nbsp;&nbsp;printf("It is not standard C code!\n"); <br />
&nbsp;} </p>
<p>下面来看看GCC是如何帮助程序员来发现这些错误的。当GCC在编译不符合ANSI/ISO C语言标准的源代码时，如果加上了-pedantic选项，那么使用了扩展语法的地方将产生相应的警告信息：</p>
<p>&nbsp;# gcc -pedantic illcode.c -o illcode illcode.c: In function `main': illcode.c:9: ISO C89 does not support `long long' illcode.c:8: return type of `main' is not `int' </p>
<p>需要注意的是，-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容，它仅仅只能用来帮助Linux程序员离这个目标越来越近。或者换句话说，-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码，但不是全部，事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况，才有可能被GCC发现并提出警告。</p>
<p>除了-pedantic之外，GCC还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W开头，其中最有价值的当数-Wall了，使用它能够使GCC产生尽可能多的警告信息：</p>
<p>&nbsp;# gcc -Wall illcode.c -o illcode illcode.c:8: warning: return type of `main' is not `int' illcode.c: In function `main': illcode.c:9: warning: unused variable `var' </p>
<p>GCC给出的警告信息虽然从严格意义上说不能算作是错误，但却很可能成为错误的栖身之所。<font face="楷体_GB2312" color="#d52b6f"><strong>一个优秀的Linux程序员应该尽量避免产生警告信息，使自己的代码始终保持简洁、优美和健壮的特性</strong></font>。</p>
<p>在处理警告方面，另一个常用的编译选项是-Werror，它要求GCC将所有的警告当成错误进行处理，这在使用自动编译工具（如Make等）时非常有用。如果编译时带上-Werror选项，那么GCC会在所有产生警告的地方停止编译，迫使程序员对自己的代码进行修改。只有当相应的警告信息消除时，才可能将编译过程继续朝前推进。执行情况如下：</p>
<p>&nbsp;# gcc -Wall -Werror illcode.c -o illcode cc1: warnings being treated as errors illcode.c:8: warning: return type of `main' is not `int' illcode.c: In function `main': illcode.c:9: warning: unused variable `var' </p>
<p>对Linux程序员来讲，GCC给出的警告信息是很有价值的，它们不仅可以帮助程序员写出更加健壮的程序，而且还是跟踪和调试程序的有力工具。建议在用GCC编译源代码时始终带上-Wall选项，并把它逐渐培养成为一种习惯，这对找出常见的隐式编程错误很有帮助。</p>
<p>库依赖</p>
<p>在Linux下开发软件时，完全不使用第三方函数库的情况是比较少见的，通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从程序员的角度看，函数库实际上就是一些头文件（.h）和库文件（.so或者.a）的集合。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下，而库文件则放到/usr/lib/目录下，但并不是所有的情况都是这样。正因如此，GCC在编译时必须有自己的办法来查找所需要的头文件和库文件。</p>
<p>GCC采用搜索目录的办法来查找所需要的文件，-I选项可以向GCC的头文件搜索路径中添加新的目录。例如，如果在/home/xiaowp/include/目录下有编译时所需要的头文件，为了让GCC能够顺利地找到它们，就可以使用-I选项：</p>
<p>&nbsp;# gcc foo.c -I /home/xiaowp/include -o foo </p>
<p>同样，如果使用了不在标准位置的库文件，那么可以通过-L选项向GCC的库文件搜索路径中添加新的目录。例如，如果在/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so，为了让GCC能够顺利地找到它，可以使用下面的命令：</p>
<p>&nbsp;# gcc foo.c -L /home/xiaowp/lib -lfoo -o foo </p>
<p>值得好好解释一下的是-l选项，它指示GCC去连接库文件libfoo.so。Linux下的库文件在命名时有一个约定，那就是应该以lib三个字母开头，由于所有的库文件都遵循了同样的规范，因此在用-l选项指定链接的库文件名时可以省去lib三个字母，也就是说GCC在对-lfoo进行处理时，会自动去链接名为libfoo.so的文件。</p>
<p>Linux下的库文件分为两大类分别是动态链接库（通常以.so结尾）和静态链接库（通常以.a结尾），两者的差别仅在程序执行时所需的代码是在运行时动态加载的，还是在编译时静态加载的。默认情况下，GCC在链接时优先使用动态链接库，只有当动态链接库不存在时才考虑使用静态链接库，如果需要的话可以在编译时加上-static选项，强制使用静态链接库。例如，如果在/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a，为了让GCC在链接时只用到静态链接库，可以使用下面的命令：</p>
<p>&nbsp;# gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo <br />
&nbsp;<br />
代码优化</p>
<p>代码优化指的是编译器通过分析源代码，找出其中尚未达到最优的部分，然后对其重新进行组合，目的是改善程序的执行性能。GCC提供的代码优化功能非常强大，它通过编译选项-On来控制优化代码的生成，其中n是一个代表优化级别的整数。对于不同版本的GCC来讲，n的取值范围及其对应的优化效果可能并不完全相同，比较典型的范围是从0变化到2或3。</p>
<p>编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间，其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器，但一般都会包括线程跳转（Thread Jump）和延迟退栈（Deferred Stack Pops）两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外，同时还要进行一些额外的调整工作，如处理器指令调度等。选项-O3则除了完成所有-O2级别的优化之外，还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说，数字越大优化的等级越高，同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项，因为它在优化长度、编译时间和代码大小之间，取得了一个比较理想的平衡点。</p>
<p>下面通过具体实例来感受一下GCC的代码优化功能，所用程序如清单3所示。</p>
<p>清单3：optimize.c</p>
<p>&nbsp;#include &lt;stdio.h&gt;<br />
&nbsp;int main(void) <br />
&nbsp;{ <br />
&nbsp;&nbsp;double counter; <br />
&nbsp;&nbsp;double result; <br />
&nbsp;&nbsp;double temp; <br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;for (counter = 0; counter &lt; 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020; counter += (5 - 1) / 4) <br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp; temp = counter / 1979; result = counter; <br />
&nbsp;&nbsp;} <br />
&nbsp;&nbsp;printf("Result is %lf\n", result); return 0; <br />
&nbsp;} </p>
<p>首先不加任何优化选项进行编译：</p>
<p>&nbsp;# gcc -Wall optimize.c -o optimize <br />
&nbsp;<br />
借助Linux提供的time命令，可以大致统计出该程序在运行时所需要的时间：</p>
<p>&nbsp;# time ./optimize Result is 400002019.000000 real 0m14.942s user 0m14.940s sys 0m0.000s <br />
&nbsp;<br />
接下去使用优化选项来对代码进行优化处理：</p>
<p>&nbsp;# gcc -Wall -O optimize.c -o optimize <br />
&nbsp;<br />
在同样的条件下再次测试一下运行时间：</p>
<p>&nbsp;# time ./optimize Result is 400002019.000000 real 0m3.256s user 0m3.240s sys 0m0.000s <br />
&nbsp;<br />
对比两次执行的输出结果不难看出，程序的性能的确得到了很大幅度的改善，由原来的14秒缩短到了3秒。这个例子是专门针对GCC的优化功能而设计的，因此优化前后程序的执行速度发生了很大的改变。尽管GCC的代码优化功能非常强大，但作为一名优秀的Linux程序员，首先还是要力求能够手工编写出高质量的代码。如果编写的代码简短，并且逻辑性强，编译器就不会做更多的工作，甚至根本用不着优化。</p>
<p>优化虽然能够给程序带来更好的执行性能，但在如下一些场合中应该避免优化代码：</p>
<p>◆ 程序开发的时候 优化等级越高，消耗在编译上的时间就越长，因此在开发的时候最好不要使用优化选项，只有到软件发行或开发结束的时候，才考虑对最终生成的代码进行优化。</p>
<p>◆ 资源受限的时候 一些优化选项会增加可执行代码的体积，如果程序在运行时能够申请到的内存资源非常紧张（如一些实时嵌入式设备），那就不要对代码进行优化，因为由这带来的负面影响可能会产生非常严重的后果。</p>
<p>◆ 跟踪调试的时候 在对代码进行优化的时候，某些代码可能会被删除或改写，或者为了取得更佳的性能而进行重组，从而使跟踪和调试变得异常困难。</p>
<p>调试</p>
<p>一个功能强大的调试器不仅为程序员提供了跟踪程序执行的手段，而且还可以帮助程序员找到解决问题的方法。对于Linux程序员来讲，GDB（GNU Debugger）通过与GCC的配合使用，为基于Linux的软件开发提供了一个完善的调试环境。</p>
<p>默认情况下，GCC在编译时不会将调试符号插入到生成的二进制代码中，因为这样会增加可执行文件的大小。如果需要在编译时生成调试符号信息，可以使用GCC的-g或者-ggdb选项。GCC在产生调试符号时，同样采用了分级的思路，开发人员可以通过在-g选项后附加数字1、2或3来指定在代码中加入调试信息的多少。默认的级别是2（-g2），此时产生的调试信息包括扩展的符号表、行号、局部或外部变量信息。级别3（-g3）包含级别2中的所有调试信息，以及源代码中定义的宏。级别1（-g1）不包含局部变量和与行号有关的调试信息，因此只能够用于回溯跟踪和堆栈转储之用。回溯跟踪指的是监视程序在运行过程中的函数调用历史，堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法，两者都是经常用到的调试手段。</p>
<p>GCC产生的调试符号具有普遍的适应性，可以被许多调试器加以利用，但如果使用的是GDB，那么还可以通过-ggdb选项在生成的二进制代码中包含GDB专用的调试信息。这种做法的优点是可以方便GDB的调试工作，但缺点是可能导致其它调试器（如DBX）无法进行正常的调试。选项-ggdb能够接受的调试级别和-g是完全一样的，它们对输出的调试符号有着相同的影响。</p>
<p>需要注意的是，使用任何一个调试选项都会使最终生成的二进制文件的大小急剧增加，同时增加程序在执行时的开销，因此调试选项通常仅在软件的开发和调试阶段使用。调试选项对生成代码大小的影响从下面的对比过程中可以看出来：</p>
<p>&nbsp;# gcc optimize.c -o optimize # ls optimize -l -rwxrwxr-x 1 xiaowp xiaowp 11649 Nov 20 08:53 optimize (未加调试选项) # gcc -g optimize.c -o optimize # ls optimize -l -rwxrwxr-x 1 xiaowp xiaowp 15889 Nov 20 08:54 optimize (加入调试选项) <br />
&nbsp;<br />
虽然调试选项会增加文件的大小，但事实上Linux中的许多软件在测试版本甚至最终发行版本中仍然使用了调试选项来进行编译，这样做的目的是鼓励用户在发现问题时自己动手解决，是Linux的一个显著特色。</p>
<p>下面还是通过一个具体的实例说明如何利用调试符号来分析错误，所用程序见清单4所示。</p>
<p>清单4：crash.c</p>
<p>&nbsp;#include &lt;stdio.h&gt; <br />
&nbsp;int main(void) <br />
&nbsp;{ <br />
&nbsp;&nbsp;int input =0; <br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;printf("Input an integer:"); <br />
&nbsp;&nbsp;scanf("%d", input); <br />
&nbsp;&nbsp;printf("The integer you input is %d\n", input); <br />
&nbsp;&nbsp;return 0; <br />
&nbsp;} <br />
&nbsp;<br />
编译并运行上述代码，会产生一个严重的段错误（Segmentation fault）如下：</p>
<p>&nbsp;# gcc -g crash.c -o crash # ./crash Input an integer:10 Segmentation fault <br />
&nbsp;<br />
为了更快速地发现错误所在，可以使用GDB进行跟踪调试，方法如下：</p>
<p>&nbsp;# gdb crash GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) &#8230;&#8230; (gdb) <br />
&nbsp;<br />
当GDB提示符出现的时候，表明GDB已经做好准备进行调试了，现在可以通过run命令让程序开始在GDB的监控下运行：</p>
<p>&nbsp;(gdb) run Starting program: /home/xiaowp/thesis/gcc/code/crash Input an integer:10 Program received signal SIGSEGV, Segmentation fault. 0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6 <br />
&nbsp;<br />
仔细分析一下GDB给出的输出结果不难看出，程序是由于段错误而导致异常中止的，说明内存操作出了问题，具体发生问题的地方是在调用_IO_vfscanf_internal ( )的时候。为了得到更加有价值的信息，可以使用GDB提供的回溯跟踪命令backtrace，执行结果如下：</p>
<p>&nbsp;(gdb) backtrace #0 0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6 #1 0xbffff0c0 in ?? () #2 0x4008e0ba in scanf () from /lib/libc.so.6 #3 0x08048393 in main () at crash.c:11 #4 0x40042917 in __libc_start_main () from /lib/libc.so.6 <br />
&nbsp;<br />
跳过输出结果中的前面三行，从输出结果的第四行中不难看出，GDB已经将错误定位到crash.c中的第11行了。现在仔细检查一下：</p>
<p>&nbsp;(gdb) frame 3 #3 0x08048393 in main () at crash.c:11 11 scanf("%d", input); <br />
&nbsp;<br />
使用GDB提供的frame命令可以定位到发生错误的代码段，该命令后面跟着的数值可以在backtrace命令输出结果中的行首找到。现在已经发现错误所在了，应该将</p>
<p>&nbsp;scanf("%d", input); 改为 scanf("%d", &amp;input); <br />
&nbsp;<br />
完成后就可以退出GDB了，命令如下：</p>
<p>&nbsp;(gdb) quit <br />
&nbsp;<br />
GDB的功能远远不止如此，它还可以单步跟踪程序、检查内存变量和设置断点等。</p>
<p>调试时可能会需要用到编译器产生的中间结果，这时可以使用-save-temps选项，让GCC将预处理代码、汇编代码和目标代码都作为文件保存起来。如果想检查生成的代码是否能够通过手工调整的办法来提高执行性能，在编译过程中生成的中间文件将会很有帮助，具体情况如下：</p>
<p>&nbsp;# gcc -save-temps foo.c -o foo # ls foo* foo foo.c foo.i foo.s <br />
&nbsp;<br />
GCC支持的其它调试选项还包括-p和-pg，它们会将剖析（Profiling）信息加入到最终生成的二进制代码中。剖析信息对于找出程序的性能瓶颈很有帮助，是协助Linux程序员开发出高性能程序的有力工具。在编译时加入-p选项会在生成的代码中加入通用剖析工具（Prof）能够识别的统计信息，而-pg选项则生成只有GNU剖析工具（Gprof）才能识别的统计信息。</p>
<p>最后提醒一点，虽然GCC允许在优化的同时加入调试符号信息，但优化后的代码对于调试本身而言将是一个很大的挑战。代码在经过优化之后，在源程序中声明和使用的变量很可能不再使用，控制流也可能会突然跳转到意外的地方，循环语句有可能因为循环展开而变得到处都有，所有这些对调试来讲都将是一场噩梦。建议在调试的时候最好不使用任何优化选项，只有当程序在最终发行的时候才考虑对其进行优化。</p>
<p>上次的培训园地中介绍了GCC的编译过程、警告提示功能、库依赖、代码优化和程序调试六个方面的内容。这期是最后的一部分内容。</p>
<p>加速</p>
<p>在将源代码变成可执行文件的过程中，需要经过许多中间步骤，包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下GCC可以为Linux程序员完成所有的后台工作，自动调用相应程序进行处理。</p>
<p>这样做有一个很明显的缺点，就是GCC在处理每一个源文件时，最终都需要生成好几个临时文件才能完成相应的工作，从而无形中导致处理速度变慢。例如，GCC在处理一个源文件时，可能需要一个临时文件来保存预处理的输出、一个临时文件来保存编译器的输出、一个临时文件来保存汇编器的输出，而读写这些临时文件显然需要耗费一定的时间。当软件项目变得非常庞大的时候，花费在这上面的代价可能会变得很沉重。</p>
<p>解决的办法是，使用Linux提供的一种更加高效的通信方式—管道。它可以用来同时连接两个程序，其中一个程序的输出将被直接作为另一个程序的输入，这样就可以避免使用临时文件，但编译时却需要消耗更多的内存。</p>
<p>在编译过程中使用管道是由GCC的-pipe选项决定的。下面的这条命令就是借助GCC的管道功能来提高编译速度的：</p>
<p>&nbsp;# gcc -pipe foo.c -o foo <br />
&nbsp;<br />
在编译小型工程时使用管道，编译时间上的差异可能还不是很明显，但在源代码非常多的大型工程中，差异将变得非常明显。</p>
<p>文件扩展名</p>
<p>在使用GCC的过程中，用户对一些常用的扩展名一定要熟悉，并知道其含义。为了方便大家学习使用GCC，在此将这些扩展名罗列如下：</p>
<p>.c C原始程序；</p>
<p>.C C++原始程序；</p>
<p>.cc C++原始程序；</p>
<p>.cxx C++原始程序；</p>
<p>.m Objective-C原始程序；</p>
<p>.i 已经过预处理的C原始程序；</p>
<p>.ii 已经过预处理之C++原始程序；</p>
<p>.s 组合语言原始程序；</p>
<p>.S 组合语言原始程序；</p>
<p>.h 预处理文件(标头文件)；</p>
<p>.o 目标文件；</p>
<p>.a 存档文件。</p>
<p>GCC常用选项</p>
<p>GCC作为Linux下C/C++重要的编译环境，功能强大，编译选项繁多。为了方便大家日后编译方便，在此将常用的选项及说明罗列出来如下：</p>
<p>-c 通知GCC取消链接步骤，即编译源码并在最后生成目标文件；</p>
<p>-Dmacro 定义指定的宏，使它能够通过源码中的#ifdef进行检验；</p>
<p>-E 不经过编译预处理程序的输出而输送至标准输出；</p>
<p>-g3 获得有关调试程序的详细信息，它不能与-o选项联合使用；</p>
<p>-Idirectory 在包含文件搜索路径的起点处添加指定目录；</p>
<p>-llibrary 提示链接程序在创建最终可执行文件时包含指定的库；</p>
<p>-O、-O2、-O3 将优化状态打开，该选项不能与-g选项联合使用；</p>
<p>-S 要求编译程序生成来自源代码的汇编程序输出；</p>
<p>-v 启动所有警报；</p>
<p>-Wall 在发生警报时取消编译操作，即将警报看作是错误；</p>
<p>-Werror 在发生警报时取消编译操作，即把报警当作是错误；</p>
<p>-w 禁止所有的报警。</p>
<p>小结</p>
<p>GCC是在Linux下开发程序时必须掌握的工具之一。本文对GCC做了一个简要的介绍，主要讲述了如何使用GCC编译程序、产生警告信息、调试程序和加快GCC的编译速度。对所有希望早日跨入Linux开发者行列的人来说，GCC就是成为一名优秀的Linux程序员的起跑线。</p>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/229938.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2008-09-19 14:55 <a href="http://www.blogjava.net/xiaomage234/archive/2008/09/19/229938.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux上安装GCC编译器过程（zz）</title><link>http://www.blogjava.net/xiaomage234/archive/2008/09/19/229937.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Fri, 19 Sep 2008 06:53:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2008/09/19/229937.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/229937.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2008/09/19/229937.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/229937.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/229937.html</trackback:ping><description><![CDATA[来源:http://ffmpeg.blogbus.com/logs/578829.html<br />
<br />
<p>2004年4月20日最新版本的GCC编译器3.4.0发布了。目前，GCC可以用来编译C/C++、FORTRAN、JAVA、OBJC、ADA等语言的程序，可根据需要选择安装支持的语言。GCC 3.4.0比以前版本更好地支持了C++标准。本文以在Redhat Linux上安装GCC3.4.0为例，介绍了GCC的安装过程。</p>
<p>　　安装之前，系统中必须要有cc或者gcc等编译器，并且是可用的，或者用环境变量CC指定系统上的编译器。如果系统上没有编译器，不能安装源代码形式的GCC 3.4.0。如果是这种情况，可以在网上找一个与你系统相适应的如RPM等二进制形式的GCC软件包来安装使用。本文介绍的是以源代码形式提供的GCC软件包的安装过程，软件包本身和其安装过程同样适用于其它Linux和Unix系统。 </p>
<p>　　系统上原来的GCC编译器可能是把gcc等命令文件、库文件、头文件等分别存放到系统中的不同目录下的。与此不同，现在GCC建议我们将一个版本的GCC安装在一个单独的目录下。这样做的好处是将来不需要它的时候可以方便地删除整个目录即可（因为GCC没有uninstall功能）；缺点是在安装完成后要做一些设置工作才能使编译器工作正常。在本文中我采用这个方案安装GCC 3.4.0，并且在安装完成后，仍然能够使用原来低版本的GCC编译器，即一个系统上可以同时存在并使用多个版本的GCC编译器。 </p>
<p>　　按照本文提供的步骤和设置选项，即使以前没有安装过GCC，也可以在系统上安装上一个可工作的新版本的GCC编译器。 </p>
<p>　　1. 下载 </p>
<p>　　在GCC网站上（<a href="http://gcc.gnu.org/">http://gcc.gnu.org/</a>）或者通过网上搜索可以查找到下载资源。目前GCC的最新版本为 3.4.0。可供下载的文件一般有两种形式：gcc-3.4.0.tar.gz和gcc-3.4.0.tar.bz2，只是压缩格式不一样，内容完全一致，下载其中一种即可。 </p>
<p>　　2. 解压缩 </p>
<p>　　根据压缩格式，选择下面相应的一种方式解包（以下的&#8220;%&#8221;表示命令行提示符）： </p>
<p>　　% tar xzvf gcc-3.4.0.tar.gz <br />
　　或者 <br />
　　% bzcat gcc-3.4.0.tar.bz2 | tar xvf - </p>
<p>　　新生成的gcc-3.4.0这个目录被称为源目录，用${srcdir}表示它。以后在出现${srcdir}的地方，应该用真实的路径来替换它。用pwd命令可以查看当前路径。 </p>
<p>　　在${srcdir}/INSTALL目录下有详细的GCC安装说明，可用浏览器打开index.html阅读。 </p>
<p>　　3. 建立目标目录<br />
&nbsp;<br />
　　目标目录（用${objdir}表示）是用来存放编译结果的地方。GCC建议编译后的文件不要放在源目录${srcdir]中（虽然这样做也可以），最好单独存放在另外一个目录中，而且不能是${srcdir}的子目录。 </p>
<p>　　例如，可以这样建立一个叫 gcc-build 的目标目录（与源目录${srcdir}是同级目录）： </p>
<p>　　% mkdir gcc-build <br />
　　% cd gcc-build </p>
<p>　　以下的操作主要是在目标目录 ${objdir} 下进行。 </p>
<p>　　4. 配置<br />
&nbsp;<br />
　　配置的目的是决定将GCC编译器安装到什么地方（${destdir}），支持什么语言以及指定其它一些选项等。其中，${destdir}不能与${objdir}或${srcdir}目录相同。 </p>
<p>　　配置是通过执行${srcdir}下的configure来完成的。其命令格式为（记得用你的真实路径替换${destdir}）： </p>
<p>　　% ${srcdir}/configure --prefix=${destdir} [其它选项] </p>
<p>　　例如，如果想将GCC 3.4.0安装到/usr/local/gcc-3.4.0目录下，则${destdir}就表示这个路径。 </p>
<p>　　在我的机器上，我是这样配置的： </p>
<p>　　% ../gcc-3.4.0/configure --prefix=/usr/local/gcc-3.4.0 --enable-threads=posix --disable-checking --enable--long-long --host=i386-redhat-linux --with-system-zlib --enable-languages=c,c++,java </p>
<p>　　将GCC安装在/usr/local/gcc-3.4.0目录下，支持C/C++和JAVA语言，其它选项参见GCC提供的帮助说明。 </p>
<p>　　5. 编译 </p>
<p>　　% make </p>
<p>　　这是一个漫长的过程。在我的机器上（P4-1.6），这个过程用了50多分钟。 </p>
<p>　　6. 安装 </p>
<p>　　执行下面的命令将编译好的库文件等拷贝到${destdir}目录中（根据你设定的路径，可能需要管理员的权限）： </p>
<p>　　% make install </p>
<p>　　至此，GCC 3.4.0安装过程就完成了。 </p>
<p>　　6. 其它设置 </p>
<p>　　GCC 3.4.0的所有文件，包括命令文件（如gcc、g++）、库文件等都在${destdir}目录下分别存放，如命令文件放在bin目录下、库文件在lib下、头文件在include下等。由于命令文件和库文件所在的目录还没有包含在相应的搜索路径内，所以必须要作适当的设置之后编译器才能顺利地找到并使用它们。</p>
<p>　　6.1 gcc、g++、gcj的设置 </p>
<p>　　要想使用GCC 3.4.0的gcc等命令，简单的方法就是把它的路径${destdir}/bin放在环境变量PATH中。我不用这种方式，而是用符号连接的方式实现，这样做的好处是我仍然可以使用系统上原来的旧版本的GCC编译器。 </p>
<p>　　首先，查看原来的gcc所在的路径： </p>
<p>　　% which gcc </p>
<p>　　在我的系统上，上述命令显示：/usr/bin/gcc。因此，原来的gcc命令在/usr/bin目录下。我们可以把GCC 3.4.0中的gcc、g++、gcj等命令在/usr/bin目录下分别做一个符号连接： </p>
<p>　　% cd /usr/bin <br />
　　% ln -s ${destdir}/bin/gcc gcc34 <br />
　　% ln -s ${destdir}/bin/g++ g++34 <br />
　　% ln -s ${destdir}/bin/gcj gcj34 </p>
<p>　　这样，就可以分别使用gcc34、g++34、gcj34来调用GCC 3.4.0的gcc、g++、gcj完成对C、C++、JAVA程序的编译了。同时，仍然能够使用旧版本的GCC编译器中的gcc、g++等命令。 </p>
<p>　　6.2 库路径的设置 </p>
<p>　　将${destdir}/lib路径添加到环境变量LD_LIBRARY_PATH中，最好添加到系统的配置文件中，这样就不必要每次都设置这个环境变量了。 </p>
<p>　　例如，如果GCC 3.4.0安装在/usr/local/gcc-3.4.0目录下，在RH Linux下可以直接在命令行上执行或者在文件/etc/profile中添加下面一句： </p>
<p>　　setenv LD_LIBRARY_PATH /usr/local/gcc-3.4.0/lib:$LD_LIBRARY_PATH </p>
<p>　　7. 测试<br />
&nbsp;<br />
　　用新的编译命令（gcc34、g++34等）编译你以前的C、C++程序，检验新安装的GCC编译器是否能正常工作。 </p>
<p>　　8. 根据需要，可以删除或者保留${srcdir}和${objdir}目录。</p>
<img src ="http://www.blogjava.net/xiaomage234/aggbug/229937.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2008-09-19 14:53 <a href="http://www.blogjava.net/xiaomage234/archive/2008/09/19/229937.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怎样用GDB调试程序</title><link>http://www.blogjava.net/xiaomage234/archive/2008/06/19/209218.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Thu, 19 Jun 2008 09:55:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2008/06/19/209218.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/209218.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2008/06/19/209218.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/209218.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/209218.html</trackback:ping><description><![CDATA[<p>简单的可以这样：<br />
1,首先要打开core文件限制：ulimit -c unlimited<br />
2,gdb app corefile<br />
3,bt,显示堆栈信息</p>
<br />
有两个关于如何调试的详细页面：<br />
用GDB调试程序(zt)&nbsp; <br />
http://www.linuxsir.org/bbs/showthread.php?t=171156<br />
http://www.trucy.org/blog/archives/eoiae/000087.html<br />
<img src ="http://www.blogjava.net/xiaomage234/aggbug/209218.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2008-06-19 17:55 <a href="http://www.blogjava.net/xiaomage234/archive/2008/06/19/209218.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>google ctemplate库简介</title><link>http://www.blogjava.net/xiaomage234/archive/2007/12/24/170174.html</link><dc:creator>小马歌</dc:creator><author>小马歌</author><pubDate>Mon, 24 Dec 2007 14:32:00 GMT</pubDate><guid>http://www.blogjava.net/xiaomage234/archive/2007/12/24/170174.html</guid><wfw:comment>http://www.blogjava.net/xiaomage234/comments/170174.html</wfw:comment><comments>http://www.blogjava.net/xiaomage234/archive/2007/12/24/170174.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/xiaomage234/comments/commentRss/170174.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/xiaomage234/services/trackbacks/170174.html</trackback:ping><description><![CDATA[在Java的世界里，代码生成有velocity，其vtl语言功能强大，能有效分离数据逻辑和显示。<br />
linux下的web开发，动态页面生成很费周折，通常是 利用fastcgi接受请求，然后返回页面给请求端。<br />
代码逻辑和显示逻辑写在一起，是一件很痛苦的事情，c++里也有一个类似java中velocity的东东。<br />
它的名字叫 ctemplate，出自大名鼎鼎的google。目前最新版本是 0.8.<br />
它有四种变量表达方式：<br />
1，简单的值替换；<br />
2，&lt;#tag&gt;和&lt;/tag&gt;式的循环以及内嵌；<br />
3，"&gt;file"式的include文件；<br />
4，"!"开头的注释说明。<br />
<br />
在c++里有了这个工具，能很大程度提高开发效率，方便不少。
<img src ="http://www.blogjava.net/xiaomage234/aggbug/170174.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xiaomage234/" target="_blank">小马歌</a> 2007-12-24 22:32 <a href="http://www.blogjava.net/xiaomage234/archive/2007/12/24/170174.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>