<?xml version="1.0" encoding="UTF-8"?><!-- generator="WordPress/2.9.2" -->
<rss version="0.92">
<channel>
	<title>庆亮的博客</title>
	<link>http://www.qingliangcn.com</link>
	<description>关注LAMP&#124;PHP源代码分析&#124;web架构&#124;PHP扩展&#124;Erlang&#124;服务端架构</description>
	<lastBuildDate>Tue, 09 Mar 2010 01:45:36 +0000</lastBuildDate>
	<docs>http://backend.userland.com/rss092</docs>
	<language>en</language>
	
	<item>
		<title>QQ群：PHP内核与扩展研究 48448818</title>
		<description><![CDATA[QQ群：PHP内核与扩展研究 48448818
PHP内核与扩展研究。加群请说明自己感兴趣领域或者擅长领域！否则一律不予通过，请谅
]]></description>
		<link>http://www.qingliangcn.com/2010/03/qq%e7%be%a4%ef%bc%9aphp%e5%86%85%e6%a0%b8%e4%b8%8e%e6%89%a9%e5%b1%95%e7%a0%94%e7%a9%b6-48448818/</link>
			</item>
	<item>
		<title>list comprehensions与list map性能对比</title>
		<description><![CDATA[可以使用List&#160;comprehensions时不要&#160;使用map或者filter，简单的性能测试对比一下：

第一组，map和list&#160;comprehensions对比，代码如下：

%%map方式
-module(map_test).

-export([start/1]).

start(N)&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;statistics(runtime),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;erlang:statistics(wall_clock),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;lists:map(fun&#160;(X)&#160;-&#62;&#160;X*X&#160;end,&#160;lists:seq(1,&#160;N)),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{_,&#160;T1}&#160;=&#160;erlang:statistics(runtime),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{_,&#160;T2}&#160;=&#160;erlang:statistics(wall_clock),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;io:format(&#34;total&#160;times:&#160;~p,&#160;load&#160;time:&#160;~p&#160;(~p)&#34;,&#160;[N,&#160;T1,&#160;T2]).



%%list&#160;comprehensions方式

-module(list_comp_test).

-export([start/1]).

start(N)&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;statistics(runtime),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;erlang:statistics(wall_clock),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[X*X&#160;&#124;&#124;&#160;X&#160;&#60;-&#160;lists:seq(1,&#160;N)],
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{_,&#160;T1}&#160;=&#160;erlang:statistics(runtime),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{_,&#160;T2}&#160;=&#160;erlang:statistics(wall_clock),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;io:format(&#34;total&#160;times:&#160;~p,&#160;load&#160;time:&#160;~p&#160;(~p)&#34;,&#160;[N,&#160;T1,&#160;T2]).

测试N为1000000时结果对比：



结果取得是平均值，可以很明显的看出comprehension方式性能要高。

关于filter，则类似。不再作测试。

]]></description>
		<link>http://www.qingliangcn.com/2010/02/list-comprehensions%e4%b8%8elist-map%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/</link>
			</item>
	<item>
		<title>使用新的域名 www.qingliangcn.com</title>
		<description><![CDATA[即日起使用新的域名 www.qingliangcn.com，原有域名保留一定时间。
]]></description>
		<link>http://www.qingliangcn.com/2010/02/%e4%bd%bf%e7%94%a8%e6%96%b0%e7%9a%84%e5%9f%9f%e5%90%8d-www-qingliangcn-com/</link>
			</item>
	<item>
		<title>虎年好运,新春愉快!</title>
		<description><![CDATA[虎年好运,新春愉快!
]]></description>
		<link>http://www.qingliangcn.com/2010/02/%e8%99%8e%e5%b9%b4%e5%a5%bd%e8%bf%90%e6%96%b0%e6%98%a5%e6%84%89%e5%bf%ab/</link>
			</item>
	<item>
		<title>erlang tcp发包速度测试</title>
		<description><![CDATA[&#160;

这段时间我们的项目遇到广播包的一些性能问题，想起之前看到yufeng老大提到的1s广播40K包的问题，我也想测试测试我们机器的IO能力。
这次仅仅测试发包的能力，采用的是一对一的方式。

测试代码：

-module(socket_io_test).

-export([server/0,&#160;client/1]).

server()&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;spawn(fun()&#160;-&#62;&#160;do_server()&#160;end).

do_server()&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;case&#160;gen_tcp:listen(38888,&#160;[binary,&#160;{reuseaddr,&#160;true},&#160;{active,&#160;false}])&#160;of
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{ok,&#160;ListenSocket}&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;do_server_loop(ListenSocket);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{error,&#160;Reason}&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;io:format(&#34;listen&#160;38888&#160;failed:&#160;~p&#34;,&#160;[Reason])
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;end.

do_server_loop(LSock)&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;case&#160;gen_tcp:accept(LSock)&#160;of
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{ok,&#160;ClientSocket}&#160;-&#62;&#160;do_recv(ClientSocket);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{error,&#160;Reason}&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;io:format(&#34;accept&#160;failed:&#160;~p&#34;,&#160;[Reason])
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;end.

do_recv(ClientSocket)&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;gen_tcp:recv(ClientSocket,&#160;0),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;do_recv(ClientSocket).

client(N)&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;case&#160;gen_tcp:connect(&#34;127.0.0.1&#34;,&#160;38888,&#160;[binary])&#160;of
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{ok,&#160;Socket}&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;erlang:statistics(runtime),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;erlang:statistics(wall_clock),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;do_send(N,&#160;Socket),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{_,&#160;T1}&#160;=&#160;erlang:statistics(runtime),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{_,&#160;T2}&#160;=&#160;erlang:statistics(wall_clock),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;io:format(&#34;~p&#160;~p&#34;,&#160;[T1,&#160;T2]);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{error,&#160;Reason}&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;io:format(&#34;connect&#160;failed:&#160;~p&#34;,&#160;[Reason])
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;end.

do_send(N,&#160;Socket)&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;lists:foreach(fun(_)&#160;-&#62;&#160;gen_tcp:send(Socket,&#160;&#60;&#60;&#34;Hello&#160;world!&#34;&#62;&#62;)&#160;end,&#160;lists:seq(1,&#160;N)).

启动服务端:
socket_io_test:server().

启动发包客户端：
socket_io_test:client(100000).&#160;100000表示发包的数量，每个包的大小为12个字节，加上TCP头部，一共36字节。&#160;




测试包的数量：&#160;400000(40W)
服务器环境：&#160;
Intel(R)&#160;Xeon(R)&#160;CPU&#160;&#160;&#160;E5420&#160;&#160;@&#160;2.50GHz&#160;双四核
12G内存
100M带宽独享

测试结果：








runtime




wall_clock





-smp&#160;auto



650&#160;



1558





-smp&#160;auto&#160;+h&#160;99999



630



1641





+K&#160;true&#160;



670&#160;



1626





-smp&#160;disable



410



770





+K&#160;true&#160;-smp&#160;disable


430


797





+K&#160;true&#160;-smp&#160;disable&#160;+h&#160;99999


420


1133






结果表明&#160;smp&#160;disable模式下发包速度明显快于smp&#160;auto模式，原因在于多核模式下，CPU切换的代价是相当高的。另外由于是一对一的发包，所以+K参数也没有什么效果。

产生的问题：
多核并发IO是否没有优势？得去看看Linux底层的实现了。（原谅我对这块的无知）
1.如果多核并发进行网络IO的能力要弱于单核，那么在用erlang进行服务端设计的时候就应该尽量让网关独占某个CPU并设置为-smp&#160;disable模式。
2.如果有优势，如何利用呢？嗯，这又是一个问题。

]]></description>
		<link>http://www.qingliangcn.com/2010/02/erlang-tcp%e5%8f%91%e5%8c%85%e9%80%9f%e5%ba%a6%e6%b5%8b%e8%af%95/</link>
			</item>
	<item>
		<title>Erlang中计算16位的MD5字符串</title>
		<description><![CDATA[&#160;
erlang的bif中自带了md5计算函数，但是结果却是二进制的，即使转成list，也是10进制表示，google了一下得到一段代码用于获得字符串形式的md5结果（16位）：

md5(S)&#160;-&#62;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
Md5_bin&#160;=&#160;&#160;erlang:md5(S),&#160;
Md5_list&#160;=&#160;binary_to_list(Md5_bin),&#160;
lists:flatten(list_to_hex(Md5_list)).&#160;
&#160;
list_to_hex(L)&#160;-&#62;&#160;
lists:map(fun(X)&#160;-&#62;&#160;int_to_hex(X)&#160;end,&#160;L).&#160;
&#160;
int_to_hex(N)&#160;when&#160;N&#160;&#60;&#160;256&#160;-&#62;&#160;
[hex(N&#160;div&#160;16),&#160;hex(N&#160;rem&#160;16)].&#160;
hex(N)&#160;when&#160;N&#160;&#60;&#160;10&#160;-&#62;&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;$0+N;&#160;
hex(N)&#160;when&#160;N&#160;&#62;=&#160;10,&#160;N&#160;&#60;&#160;16&#160;-&#62;&#160;&#160;&#160;&#160;&#160;&#160;
$a&#160;+&#160;(N-10).

英文链接&#160;&#160;http://sacharya.com/md5-in-erlang/
]]></description>
		<link>http://www.qingliangcn.com/2010/01/erlang%e4%b8%ad%e8%ae%a1%e7%ae%9716%e4%bd%8d%e7%9a%84md5%e5%ad%97%e7%ac%a6%e4%b8%b2/</link>
			</item>
	<item>
		<title>Erlang中粘包处理</title>
		<description><![CDATA[erlang中快速简单的处理粘包问题。]]></description>
		<link>http://www.qingliangcn.com/2010/01/erlang%e4%b8%ad%e7%b2%98%e5%8c%85%e5%a4%84%e7%90%86-2/</link>
			</item>
	<item>
		<title>OTP中supervisor启动过程</title>
		<description><![CDATA[&#160;
从rabbit_sup模块开始看起：
rabbit_sup模块的start_link是被rabbit&#160;app模块的start/2方法所调用的

rabbit.erl文件：
start(normal,&#160;[])&#160;-&#62;

{ok,&#160;SupPid}&#160;=&#160;rabbit_sup:start_link(),

rabbit_sup.erl文件：
-define(SERVER,&#160;?MODULE).
start_link()&#160;-&#62;
supervisor:start_link({local,&#160;?SERVER},&#160;?MODULE,&#160;[]).

这里的?SERVER和?MODULE是一样的值，都为rabbit_sup&#160;。

跳转到supervisor.erl文件：

start_link(SupName,&#160;Mod,&#160;Args)&#160;-&#62;
gen_server:start_link(SupName,&#160;supervisor,&#160;{SupName,&#160;Mod,&#160;Args},&#160;[]).

看到supervisor的start_link实际上调用的是gen_server的start_link方法，调用的时候将supervisor作为一个参数传递过去了。

这样到目前为止我们的调用实际为：

gen_server:start_link({local,&#160;rabbit_sup},&#160;supervisor,&#160;{{local,&#160;rabbit_sup},&#160;rabbit_sup,&#160;[]},&#160;[]).

gen_server.erl文件：
start_link(Mod,&#160;Args,&#160;Options)&#160;-&#62;
&#160;&#160;&#160;&#160;gen:start(?MODULE,&#160;link,&#160;Mod,&#160;Args,&#160;Options).

start_link(Name,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
gen:start(?MODULE,&#160;link,&#160;Name,&#160;Mod,&#160;Args,&#160;Options).

gen_server的start_link方法调用了gen模块的start方法，实际调用为：
gen:start(gen_server,&#160;link,&#160;{local,&#160;rabbit_sup},supervisor,&#160;{{local,&#160;rabbit_sup},&#160;rabbit_sup,&#160;[]},&#160;[]).

gen.erl文件：
start(GenMod,&#160;LinkP,&#160;Name,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
&#160;&#160;&#160;&#160;case&#160;where(Name)&#160;of
undefined&#160;-&#62;
&#160;&#160;&#160;&#160;do_spawn(GenMod,&#160;LinkP,&#160;Name,&#160;Mod,&#160;Args,&#160;Options);
Pid&#160;-&#62;
&#160;&#160;&#160;&#160;{error,&#160;{already_started,&#160;Pid}}
end.

start之前判断是否已经创建了该process，没有则创建，有则返回错误！

do_spawn(GenMod,&#160;link,&#160;Name,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
&#160;&#160;&#160;&#160;Time&#160;=&#160;timeout(Options),
&#160;&#160;&#160;&#160;proc_lib:start_link(?MODULE,&#160;init_it,
[GenMod,&#160;self(),&#160;self(),&#160;Name,&#160;Mod,&#160;Args,&#160;Options],
Time,
spawn_opts(Options));
do_spawn(GenMod,&#160;_,&#160;Name,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
&#160;&#160;&#160;&#160;Time&#160;=&#160;timeout(Options),
&#160;&#160;&#160;&#160;proc_lib:start(?MODULE,&#160;init_it,
&#160;&#160;&#160;[GenMod,&#160;self(),&#160;self,&#160;Name,&#160;Mod,&#160;Args,&#160;Options],&#160;
&#160;&#160;&#160;Time,
&#160;&#160;&#160;spawn_opts(Options)).

实际又是调用了gen模块的init_it方法，采用的link的spawn方式。


init_it(GenMod,&#160;Starter,&#160;Parent,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
&#160;&#160;&#160;&#160;init_it2(GenMod,&#160;Starter,&#160;Parent,&#160;self(),&#160;Mod,&#160;Args,&#160;Options).

init_it(GenMod,&#160;Starter,&#160;Parent,&#160;Name,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
&#160;&#160;&#160;&#160;case&#160;name_register(Name)&#160;of
true&#160;-&#62;
&#160;&#160;&#160;&#160;init_it2(GenMod,&#160;Starter,&#160;Parent,&#160;Name,&#160;Mod,&#160;Args,&#160;Options);
{false,&#160;Pid}&#160;-&#62;
&#160;&#160;&#160;&#160;proc_lib:init_ack(Starter,&#160;{error,&#160;{already_started,&#160;Pid}})
&#160;&#160;&#160;&#160;end.

首先尝试注册这个名字，如果发现已经注册，则通知父进程已经创建了。否则跳转到init_in2，

init_it2(GenMod,&#160;Starter,&#160;Parent,&#160;Name,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
GenMod:init_it(Starter,&#160;Parent,&#160;Name,&#160;Mod,&#160;Args,&#160;Options).

这时又跳转到gen_server:init_it方法。

init_it(Starter,&#160;self,&#160;Name,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
&#160;&#160;&#160;&#160;init_it(Starter,&#160;self(),&#160;Name,&#160;Mod,&#160;Args,&#160;Options);
init_it(Starter,&#160;Parent,&#160;Name0,&#160;Mod,&#160;Args,&#160;Options)&#160;-&#62;
&#160;&#160;&#160;&#160;Name&#160;=&#160;name(Name0),
&#160;&#160;&#160;&#160;Debug&#160;=&#160;debug_options(Name,&#160;Options),
&#160;&#160;&#160;&#160;case&#160;catch&#160;Mod:init(Args)&#160;of
{ok,&#160;State}&#160;-&#62;
&#160;&#160;&#160;&#160;proc_lib:init_ack(Starter,&#160;{ok,&#160;self()}),&#160; &#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;loop(Parent,&#160;Name,&#160;State,&#160;Mod,&#160;infinity,&#160;Debug);
{ok,&#160;State,&#160;Timeout}&#160;-&#62;
&#160;&#160;&#160;&#160;proc_lib:init_ack(Starter,&#160;{ok,&#160;self()}),&#160; &#160;&#160;&#160;&#160;
&#160;&#160;&#160;&#160;loop(Parent,&#160;Name,&#160;State,&#160;Mod,&#160;Timeout,&#160;Debug);
{stop,&#160;Reason}&#160;-&#62;
&#160;&#160;&#160;&#160;%%&#160;For&#160;consistency,&#160;we&#160;must&#160;make&#160;sure&#160;that&#160;the
&#160;&#160;&#160;&#160;%%&#160;registered&#160;name&#160;(if&#160;any)&#160;is&#160;unregistered&#160;before
&#160;&#160;&#160;&#160;%%&#160;the&#160;parent&#160;process&#160;is&#160;notified&#160;about&#160;the&#160;failure.
&#160;&#160;&#160;&#160;%%&#160;(Otherwise,&#160;the&#160;parent&#160;process&#160;could&#160;get
&#160;&#160;&#160;&#160;%%&#160;an&#160;&#39;already_started&#39;&#160;error&#160;if&#160;it&#160;immediately
&#160;&#160;&#160;&#160;%%&#160;tried&#160;starting&#160;the&#160;process&#160;again.)
&#160;&#160;&#160;&#160;unregister_name(Name0),
&#160;&#160;&#160;&#160;proc_lib:init_ack(Starter,&#160;{error,&#160;Reason}),
&#160;&#160;&#160;&#160;exit(Reason);
ignore&#160;-&#62;
&#160;&#160;&#160;&#160;unregister_name(Name0),
&#160;&#160;&#160;&#160;proc_lib:init_ack(Starter,&#160;ignore),
&#160;&#160;&#160;&#160;exit(normal);
{&#39;EXIT&#39;,&#160;Reason}&#160;-&#62;
&#160;&#160;&#160;&#160;unregister_name(Name0),
&#160;&#160;&#160;&#160;proc_lib:init_ack(Starter,&#160;{error,&#160;Reason}),
&#160;&#160;&#160;&#160;exit(Reason);
Else&#160;-&#62;
&#160;&#160;&#160;&#160;Error&#160;=&#160;{bad_return_value,&#160;Else},
&#160;&#160;&#160;&#160;proc_lib:init_ack(Starter,&#160;{error,&#160;Error}),
&#160;&#160;&#160;&#160;exit(Error)
end.

又是调用了supervisor的init方法

&#160;&#160;&#160;&#160;init({SupName,&#160;Mod,&#160;Args})&#160;-&#62;
&#160;&#160;&#160;&#160;process_flag(trap_exit,&#160;true),
&#160;&#160;&#160;&#160;case&#160;Mod:init(Args)&#160;of
{ok,&#160;{SupFlags,&#160;StartSpec}}&#160;-&#62;
&#160;&#160;&#160;&#160;case&#160;init_state(SupName,&#160;SupFlags,&#160;Mod,&#160;Args)&#160;of
{ok,&#160;State}&#160;when&#160;?is_simple(State)&#160;-&#62;
&#160;&#160;&#160;&#160;init_dynamic(State,&#160;StartSpec);
{ok,&#160;State}&#160;-&#62;
&#160;&#160;&#160;&#160;init_children(State,&#160;StartSpec);
Error&#160;-&#62;
&#160;&#160;&#160;&#160;{stop,&#160;{supervisor_data,&#160;Error}}
&#160;&#160;&#160;&#160;end;
ignore&#160;-&#62;
&#160;&#160;&#160;&#160;ignore;
Error&#160;-&#62;
&#160;&#160;&#160;&#160;{stop,&#160;{bad_return,&#160;{Mod,&#160;init,&#160;Error}}}
&#160;&#160;&#160;&#160;end.

呵呵，到了这里终于调用了rabbit_sup的init方法了，根据不同的init返回值做不同的启动子进程的操作。

]]></description>
		<link>http://www.qingliangcn.com/2010/01/otp%e4%b8%adsupervisor%e5%90%af%e5%8a%a8%e8%bf%87%e7%a8%8b/</link>
			</item>
	<item>
		<title>rabbitmq代码摘录(1)</title>
		<description><![CDATA[
&#160;
诸多erlang应用都是基于erlang现有的一些application，rabbitmq也不例外，在rabbitmq中，需要的application有sasl、os_mon、mnesia。而rabbitmq启动这些application（包括其自身）的代码写的相当的巧妙：（源码基于rabbitmq&#160;1.7.0）


文件&#160;rabbit.erl

-define(APPS,&#160;[os_mon,&#160;mnesia,&#160;rabbit]).

start()&#160;-&#62;
&#160;&#160;&#160;&#160;try
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ok&#160;=&#160;prepare(),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ok&#160;=&#160;rabbit_misc:start_applications(?APPS)&#160;
&#160;&#160;&#160;&#160;after
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;%%give&#160;the&#160;error&#160;loggers&#160;some&#160;time&#160;to&#160;catch&#160;up
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;timer:sleep(100)
&#160;&#160;&#160;&#160;end.

stop()&#160;-&#62;
&#160;&#160;&#160;&#160;ok&#160;=&#160;rabbit_misc:stop_applications(?APPS).

文件&#160;rabbit_misc.erl

manage_applications(Iterate,&#160;Do,&#160;Undo,&#160;SkipError,&#160;ErrorTag,&#160;Apps)&#160;-&#62;
&#160;&#160;&#160;&#160;Iterate(fun&#160;(App,&#160;Acc)&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;case&#160;Do(App)&#160;of
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ok&#160;-&#62;&#160;[App&#160;&#124;&#160;Acc];
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{error,&#160;{SkipError,&#160;_}}&#160;-&#62;&#160;Acc;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;{error,&#160;Reason}&#160;-&#62;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;lists:foreach(Undo,&#160;Acc),
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;throw({error,&#160;{ErrorTag,&#160;App,&#160;Reason}})
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;end
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;end,&#160;[],&#160;Apps),
&#160;&#160;&#160;&#160;ok.

start_applications(Apps)&#160;-&#62;
&#160;&#160;&#160;&#160;manage_applications(fun&#160;lists:foldl/3,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;fun&#160;application:start/1,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;fun&#160;application:stop/1,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;already_started,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cannot_start_application,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Apps).

stop_applications(Apps)&#160;-&#62;
&#160;&#160;&#160;&#160;manage_applications(fun&#160;lists:foldr/3,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;fun&#160;application:stop/1,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;fun&#160;application:start/1,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;not_started,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cannot_stop_application,
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Apps).

顺序启动或者关闭几个application，在失败的情况下首先判断操作失败的原因，如果是已经启动或者已经停止，则跳过；如果是启动失败或者停止失败，则先依次停止或者启动之前已经操作成功的application，之后抛出异常结束进程。


]]></description>
		<link>http://www.qingliangcn.com/2009/12/rabbitmq_source_code_1/</link>
			</item>
	<item>
		<title>PHP中Exception性能简单测试及结论</title>
		<description><![CDATA[
关于是否使用exception风格（个人的说法，也就是非正常的返回值都以抛异常的形式返回）来编码，我产生了一些疑问，经过和同事的一些讨论，我决定做些简单的性能测试。

&#60;?php
/**
&#160;*&#160;Exception&#160;简单的性能测试
&#160;*&#160;@author&#160;Qingliang.Cn&#160;&#160;qing.liang.cn@gmail.com
&#160;*&#160;@created&#160;2009-11-18&#160;
&#160;*&#160;@lastmodified&#160;2009-11-18
&#160;*/

define(&#8216;T&#8217;,&#160;1000000);

function&#160;no_except($a,&#160;$b)
{
&#160;&#160;&#160;&#160;if&#160;(mt_rand(1,&#160;10)&#160;&#62;&#160;0){
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;return&#160;$a&#160;+&#160;$b;
&#160;&#160;&#160;&#160;}
}

function&#160;except($a,&#160;$b)
{
&#160;&#160;&#160;&#160;try&#160;{
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if&#160;(mt_rand(1,&#160;10)&#160;&#62;&#160;5){&#160;//&#160;&#160;0.5的概率抛出异常
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;return&#160;$a&#160;+&#160;$b;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;}else{
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;throw&#160;new&#160;Exception(1);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;}
&#160;&#160;&#160;&#160;}catch&#160;(Exception&#160;$e){
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;return&#160;$e-&#62;getMessage();
&#160;&#160;&#160;&#160;}
}

echo&#160;&#34;1.&#160;with&#160;no&#160;exception,&#160;time&#160;is:&#34;;

$begin&#160;=&#160;microtime(true);

for&#160;($i=0;&#160;$i&#60;&#160;T;&#160;$i++)
{
&#160;&#160;&#160;&#160;no_except(1,&#160;1);
}

echo&#160;microtime(true)&#160;-&#160;$begin;
echo&#160;&#34;\r\n&#34;;

echo&#160;&#34;2.&#160;with&#160;exception,&#160;time&#160;is:&#34;;

$begin&#160;=&#160;microtime(true);

for&#160;($i=0;&#160;$i&#60;&#160;T;&#160;$i++)
{
&#160;&#160;&#160;&#160;except(1,&#160;1);
}

echo&#160;microtime(true)&#160;-&#160;$begin;
echo&#160;&#34;\r\n&#34;;


结果：
100000&#160;(10W)
1.&#160;with&#160;no&#160;exception,&#160;time&#160;is:3.2554759979248
2.&#160;with&#160;exception,&#160;time&#160;is:4.2815051078796

1000000(100W)
1.&#160;with&#160;no&#160;exception,&#160;time&#160;is:31.89279794693
2.&#160;with&#160;exception,&#160;time&#160;is:39.047714948654


上面的测试结果可以看出消耗的时间是相当稳定的，直接的结果是exception比直接return要慢。&#160;

继续分析
抛出异常的概率为0.5，也就是说：50w次的异常处理导致了11秒的性能损失。每次exception处理的消耗大概是&#160;20&#160;微秒，这个消耗是相当的小的。&#160;因为一般情况下，web请求的时间都是ms级别的。

同时这里还有两点需要注意：
1.&#160;没有使用异常的时候，代码中的逻辑判断分值必然会加多，也是一定的消耗。
2.&#160;多数的程序exception命中率不会如上面代码中那么高（50%）。

所以在PHP中应用exception是不需要考虑性能问题的。

如有不同的观点，欢迎拍砖和讨论。&#160;email:&#160;qing.liang.cn&#160;at&#160;gmail.com



]]></description>
		<link>http://www.qingliangcn.com/2009/11/php%e4%b8%adexception%e6%80%a7%e8%83%bd%e7%ae%80%e5%8d%95%e6%b5%8b%e8%af%95%e5%8f%8a%e7%bb%93%e8%ae%ba/</link>
			</item>
	<item>
		<title>Windows7试用</title>
		<description><![CDATA[      这几天折腾了一下Windows7，总体的感觉不错。从体验上来说，各方面的设置都和XP类似，即使是有变化也是过渡的非常平稳。唯一感觉不太好的是Windows7的画面感觉不太舒服，看起来非常的累，我的显卡是蓝宝4830的，应该不是卡的问题，舍友安装后也觉得电影的画面看起来有格子的感觉，可能是驱动的问题。
     可能是因为我刚换的4核CPU，感觉windows7的操作似乎比xp还要流畅。
]]></description>
		<link>http://www.qingliangcn.com/2009/10/windows7%e8%af%95%e7%94%a8/</link>
			</item>
	<item>
		<title>本地Service browser中调试amfphp注意一则</title>
		<description><![CDATA[注意关闭本地web服务器的keepalive功能，否则可能会出现刚刚修改的代码要隔断时间才能失效的问题。目前只在apache环境下发现这个问题。
]]></description>
		<link>http://www.qingliangcn.com/2009/08/%e6%9c%ac%e5%9c%b0service-browser%e4%b8%ad%e8%b0%83%e8%af%95amfphp%e6%b3%a8%e6%84%8f%e4%b8%80%e5%88%99/</link>
			</item>
	<item>
		<title>PHP扩展之自定义全局变量</title>
		<description><![CDATA[

跟踪了一下PHP中$_GET、$_POST的产生过程，发现通过zend提供的函数和宏可以很方便注册自己的全局数组。相关的函数和宏：
END_API&#160;int&#160;zend_register_auto_global(
char&#160;*name,&#160;//全局数组名
uint&#160;name_len,&#160;&#160;//数组名称长度-&#160;1
zend_auto_global_callback&#160;auto_global_callback&#160;TSRMLS_DC&#160;//数组初始化回调函数
);
#define&#160;ZEND_SET_SYMBOL(symtable,&#160;name,&#160;var) \
{ \
char&#160;*_name&#160;=&#160;(name); \
\
ZEND_SET_SYMBOL_WITH_LENGTH(symtable,&#160;_name,&#160;strlen(_name)+1,&#160;var,&#160;1,&#160;0); \
}

下面来注册我们的全局变量，假设我们的扩展名为test，我们在MINIT中进行注册操作。
zend_bool&#160;ming_global_callback(char&#160;*name,&#160;uint&#160;name_len&#160;TSRMLS_DC)
{
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;zval&#160;*tmp;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;MAKE_STD_ZVAL(tmp);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;array_init(tmp);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;add_next_index_long(tmp,2222);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ZEND_SET_SYMBOL(&#38;EG(symbol_table),&#160;name,&#160;tmp);
}
PHP_MINIT_FUNCTION(test)
{
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;/*&#160;If&#160;you&#160;have&#160;INI&#160;entries,&#160;uncomment&#160;these&#160;lines&#160;
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;REGISTER_INI_ENTRIES();
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;*/
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;zend_register_auto_global(&#34;_MING&#34;,&#160;sizeof(&#34;_MING&#34;)&#160;-&#160;1,&#160;ming_global_callback&#160;&#160;TSRMLS_DC);
&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;return&#160;SUCCESS;
}
运行PHP代码&#160;
&#60;?php
var_dump($_MING);
结果：
array(1)&#160;{
&#160;&#160;[0]=&#62;
&#160;&#160;int(2222)
}

]]></description>
		<link>http://www.qingliangcn.com/2009/08/php%e6%89%a9%e5%b1%95%e4%b9%8b%e8%87%aa%e5%ae%9a%e4%b9%89%e5%85%a8%e5%b1%80%e5%8f%98%e9%87%8f/</link>
			</item>
	<item>
		<title>xampp下 Pear安装出错Call to undefined method PEAR_Error::set()</title>
		<description><![CDATA[简单的解决办法：删除PHP文件夹下的php.ini
]]></description>
		<link>http://www.qingliangcn.com/2009/08/xampp%e4%b8%8b-pear%e5%ae%89%e8%a3%85%e5%87%ba%e9%94%99call-to-undefined-method-pear_errorset/</link>
			</item>
	<item>
		<title>PHP源代码分析之HashTable</title>
		<description><![CDATA[从PHP的zval结构体可以看出PHP使用HashTable来保存数组信息，PHP的HashTable使用了一些技巧，这些技巧是PHP高效数组操作的直接原因，源代码在PHP源代码目录的Zend/zend_hash.h&#160;&#160;Zend/zend_hash.c&#160;中。先来看看Zend&#160;HashTable的定义：



参数解释：
nTableSize&#160;&#160;哈希表的大小
nTableMask&#160;&#160;数值上等于nTableSize&#160;-1&#160;
nNumOfElements&#160;记录了当前HashTable中保存的记录数
nNextFreeElement&#160;&#160;指向下一个空闲的Bucket（之后有解释）
pInternalPointer&#160;
pListHead&#160;&#160;指向Bucket列表头部
pListTail&#160;&#160;&#160;&#160;指向Bucket列表尾部
arBuckets
pDestructor&#160;&#160;&#160;一个函数指针，在HashTable发生增、删、改时自动调用，以完成某些清理工作。
persistent&#160;&#160;&#160;是否是持久
nApplyCount
aApplyProtection 这两个参数用于放置在遍历时发生无限递归
&#160;

&#160;

可以看到Bucket是一个双向链表，参数解释：
h&#160;&#160;当元素使用数字索引时使用
nKeyLength&#160;&#160;当使用字符串索引时，该选项表示字符串索引的长度，而字符串则保存在Bucket结构体的最后一个元素arKey中。尽管arKey被声明为一个只有一个元素的数组，但是这并不妨碍我们在其中保存字符串，因为数组名可以看做指针，将arKey作为结构体的最后一个元素则Bucket结构体就成了变长结构体，而该变长结构体的长度则需要nKeyLength的辅助才能确定，这是C语言中的常见技巧。
pNext指向具有相同hash值的下一个bucket元素，无论HashTable设计的如何完美，冲突都是难免的。当采用字符串索引时，h成员变量存放的就是字符串索引的hash值。
pData指向保存的数据，如果数据本身又为指针，则用pDataPtr来保存对应的指针，而辞此时pData则指向自身结构体的pDataPtr。
接着看Zend&#160;HashTable的一些相关函数:

#define&#160;HASH_PROTECT_RECURSION(ht) \
if&#160;((ht)-&#62;bApplyProtection)&#160;{ \
if&#160;((ht)-&#62;nApplyCount++&#160;&#62;=&#160;3)&#160;{ \
zend_error(E_ERROR,&#160;&#34;Nesting&#160;level&#160;too&#160;deep&#160;-&#160;recursive&#160;dependency?&#34;); \
} \
}
这个宏用于防止循环引用。

#define&#160;ZEND_HASH_IF_FULL_DO_RESIZE(ht) \
if&#160;((ht)-&#62;nNumOfElements&#160;&#62;&#160;(ht)-&#62;nTableSize)&#160;{ \
zend_hash_do_resize(ht); \
}
该宏用于判断HashTable中的元素是否超过了HashTable表的大小，如果超过则扩展HashTable的大小，查看zend_hash_do_resize的代码可以看到每次扩展大小都是成倍的。
看看Zend&#160;HashTable是如何初始化的
ZEND_API&#160;int&#160;_zend_hash_init(HashTable&#160;*ht,&#160;uint&#160;nSize,&#160;hash_func_t&#160;pHashFunction,&#160;dtor_func_t&#160;pDestructor,&#160;zend_bool&#160;persistent&#160;ZEND_FILE_LINE_DC)
{
uint&#160;i&#160;=&#160;3;&#160;//这里可以看出数组默认的初始化为8
Bucket&#160;**tmp;

SET_INCONSISTENT(HT_OK);&#160;&#160;&#160;//&#160;用于调试&#160;

if&#160;(nSize&#160;&#62;=&#160;0&#215;80000000)&#160;{
/*&#160;prevent&#160;overflow&#160;*/
ht-&#62;nTableSize&#160;=&#160;0&#215;80000000;
}&#160;else&#160;{
while&#160;((1U&#160;&#60;&#60;&#160;i)&#160;&#60;&#160;nSize)&#160;{
i++;
}
ht-&#62;nTableSize&#160;=&#160;1&#160;&#60;&#60;&#160;i;
}

ht-&#62;nTableMask&#160;=&#160;ht-&#62;nTableSize&#160;-&#160;1;
ht-&#62;pDestructor&#160;=&#160;pDestructor;
ht-&#62;arBuckets&#160;=&#160;NULL;
ht-&#62;pListHead&#160;=&#160;NULL;
ht-&#62;pListTail&#160;=&#160;NULL;
ht-&#62;nNumOfElements&#160;=&#160;0;
ht-&#62;nNextFreeElement&#160;=&#160;0;
ht-&#62;pInternalPointer&#160;=&#160;NULL;
ht-&#62;persistent&#160;=&#160;persistent;
ht-&#62;nApplyCount&#160;=&#160;0;
ht-&#62;bApplyProtection&#160;=&#160;1;

/*&#160;Uses&#160;ecalloc()&#160;so&#160;that&#160;Bucket*&#160;==&#160;NULL&#160;*/
if&#160;(persistent)&#160;{
tmp&#160;=&#160;(Bucket&#160;**)&#160;calloc(ht-&#62;nTableSize,&#160;sizeof(Bucket&#160;*));
if&#160;(!tmp)&#160;{
return&#160;FAILURE;
}
ht-&#62;arBuckets&#160;=&#160;tmp;
}&#160;else&#160;{
tmp&#160;=&#160;(Bucket&#160;**)&#160;ecalloc_rel(ht-&#62;nTableSize,&#160;sizeof(Bucket&#160;*));
if&#160;(tmp)&#160;{
ht-&#62;arBuckets&#160;=&#160;tmp;
}
}

return&#160;SUCCESS;
}

可以看到HashTable的大小被自动的初始化为2的n次方，persistent参数用于指示是否是&#8220;永久&#8221;方式分配内存，如果是则采用系统分配内存方法，否则采用ZendMM的内存分配方式，关于ZendMM请搜索PHP内存管理的相关内容。
申请得到的bucket指针内存块都放在HashTable的arBucket中，可以把这段内存块看成一个数组，数组中的每个元素都指向一个实际的bucket。

ZEND_API&#160;int&#160;_zend_hash_add_or_update(HashTable&#160;*ht,&#160;char&#160;*arKey,&#160;uint&#160;nKeyLength,&#160;void&#160;*pData,&#160;uint&#160;nDataSize,&#160;void&#160;**pDest,&#160;int&#160;flag&#160;ZEND_FILE_LINE_DC)
{
ulong&#160;h;
uint&#160;nIndex;
Bucket&#160;*p;

IS_CONSISTENT(ht);

if&#160;(nKeyLength&#160;&#60;=&#160;0)&#160;{
#if&#160;ZEND_DEBUG
ZEND_PUTS(&#34;zend_hash_update:&#160;Can&#8217;t&#160;put&#160;in&#160;empty&#160;key\n&#34;);
#endif
return&#160;FAILURE;
}

//根据索引值和索引长度生成hash值
h&#160;=&#160;zend_inline_hash_func(arKey,&#160;nKeyLength);
//用hash值和nTableMask进行按位于运算，用于索引的快速定位
//按位于后的结果不可能大于nTableMask的值
//结合下面的代码，可以看出这段代码的巧妙
nIndex&#160;=&#160;h&#160;&#38;&#160;ht-&#62;nTableMask;
p&#160;=&#160;ht-&#62;arBuckets[nIndex];
//如果p不为NULL，则产生了hash冲突
while&#160;(p&#160;!=&#160;NULL)&#160;{
if&#160;((p-&#62;h&#160;==&#160;h)&#160;&#38;&#38;&#160;(p-&#62;nKeyLength&#160;==&#160;nKeyLength))&#160;{
if&#160;(!memcmp(p-&#62;arKey,&#160;arKey,&#160;nKeyLength))&#160;{
if&#160;(flag&#160;&#38;&#160;HASH_ADD)&#160;{
return&#160;FAILURE;
}
HANDLE_BLOCK_INTERRUPTIONS();
#if&#160;ZEND_DEBUG
if&#160;(p-&#62;pData&#160;==&#160;pData)&#160;{
ZEND_PUTS(&#34;Fatal&#160;error&#160;in&#160;zend_hash_update:&#160;p-&#62;pData&#160;==&#160;pData\n&#34;);
HANDLE_UNBLOCK_INTERRUPTIONS();
return&#160;FAILURE;
}
#endif
//到了这里就说明是更新操作
//先调用原来的析构函数执行清理
if&#160;(ht-&#62;pDestructor)&#160;{
ht-&#62;pDestructor(p-&#62;pData);
}
UPDATE_DATA(ht,&#160;p,&#160;pData,&#160;nDataSize);
if&#160;(pDest)&#160;{
*pDest&#160;=&#160;p-&#62;pData;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
return&#160;SUCCESS;
}
}
p&#160;=&#160;p-&#62;pNext;
}
//来到这里说明是增加元素操作
p&#160;=&#160;(Bucket&#160;*)&#160;pemalloc(sizeof(Bucket)&#160;-&#160;1&#160;+&#160;nKeyLength,&#160;ht-&#62;persistent);
if&#160;(!p)&#160;{
return&#160;FAILURE;
}
memcpy(p-&#62;arKey,&#160;arKey,&#160;nKeyLength);
p-&#62;nKeyLength&#160;=&#160;nKeyLength;
INIT_DATA(ht,&#160;p,&#160;pData,&#160;nDataSize);
p-&#62;h&#160;=&#160;h;
CONNECT_TO_BUCKET_DLLIST(p,&#160;ht-&#62;arBuckets[nIndex]);
if&#160;(pDest)&#160;{
*pDest&#160;=&#160;p-&#62;pData;
}

HANDLE_BLOCK_INTERRUPTIONS();
CONNECT_TO_GLOBAL_DLLIST(p,&#160;ht);
ht-&#62;arBuckets[nIndex]&#160;=&#160;p;
HANDLE_UNBLOCK_INTERRUPTIONS();

ht-&#62;nNumOfElements++;
ZEND_HASH_IF_FULL_DO_RESIZE(ht); /*&#160;If&#160;the&#160;Hash&#160;table&#160;is&#160;full,&#160;resize&#160;it&#160;*/
return&#160;SUCCESS;
}
看到这里就可以发现多数代码都是类似的了，
#define&#160;CONNECT_TO_BUCKET_DLLIST(element,&#160;list_head) \
(element)-&#62;pNext&#160;=&#160;(list_head); \
(element)-&#62;pLast&#160;=&#160;NULL; \
if&#160;((element)-&#62;pNext)&#160;{ \
(element)-&#62;pNext-&#62;pLast&#160;=&#160;(element); \
}

这个宏用于将一个bucket加入到bucket链表中

#define&#160;CONNECT_TO_GLOBAL_DLLIST(element,&#160;ht) \
(element)-&#62;pListLast&#160;=&#160;(ht)-&#62;pListTail; \
(ht)-&#62;pListTail&#160;=&#160;(element); \
(element)-&#62;pListNext&#160;=&#160;NULL; \
if&#160;((element)-&#62;pListLast&#160;!=&#160;NULL)&#160;{ \
(element)-&#62;pListLast-&#62;pListNext&#160;=&#160;(element); \
} \
if&#160;(!(ht)-&#62;pListHead)&#160;{ \
(ht)-&#62;pListHead&#160;=&#160;(element); \
} \
if&#160;((ht)-&#62;pInternalPointer&#160;==&#160;NULL)&#160;{ \
(ht)-&#62;pInternalPointer&#160;=&#160;(element); \
}
该宏用于将一个bucket加入到HashTable的链表中
&#160;

下面列出zend&#160;封装好的函数或者宏：
zend_hash_add_empty_element&#160;&#160;&#160;给数组增加一个空元素
zend_hash_do_resize&#160;&#160;扩大哈希表的大小
_zend_hash_index_update_or_next_insert&#160;插入或者更新指定数字索引的元素
zend_hash_del_key_or_index&#160;&#160;根据索引删除HashTable中的元素
zend_hash_apply&#160;&#160;&#160;遍历HashTable，注意当中使用了两个宏HASH_PROTECT_RECURSION&#160;和&#160;HASH_UNPROTECT_RECURSION来防止遍历陷入死循环。
#define&#160;HASH_PROTECT_RECURSION(ht) \
if&#160;((ht)-&#62;bApplyProtection)&#160;{ \
if&#160;((ht)-&#62;nApplyCount++&#160;&#62;=&#160;3)&#160;{ \
zend_error(E_ERROR,&#160;&#34;Nesting&#160;level&#160;too&#160;deep&#160;-&#160;recursive&#160;dependency?&#34;); \
} \
}


#define&#160;HASH_UNPROTECT_RECURSION(ht) \
if&#160;((ht)-&#62;bApplyProtection)&#160;{ \
(ht)-&#62;nApplyCount&#8211;; \
}

zend_hash_reverse_apply&#160;&#160;反向遍历HashTable
zend_hash_copy&#160;&#160;拷贝
_zend_hash_merge&#160;&#160;合并
zend_hash_find&#160;&#160;字符串索引方式查找
zend_hash_index_find&#160;&#160;数值索引方法查找
zend_hash_quick_find&#160;&#160;&#160;上面两个函数的封装
zend_hash_exists&#160;&#160;是否存在索引
zend_hash_index_exists&#160;是否存在索引
zend_hash_quick_exists&#160;&#160;上面两个方法的封装
ZEND_API&#160;int&#160;zend_hash_num_elements(HashTable&#160;*ht)
{
IS_CONSISTENT(ht);

return&#160;ht-&#62;nNumOfElements;
}
获得数组大小
&#160;
为了更加方便的操作HashTable，Zend将上面的宏做了进一步的封装。

（上述内容截自 http://devzone.zend.com/node/view/id/1022#Heading5）
&#160;
呵呵，非常方便的操作数组的方式。

]]></description>
		<link>http://www.qingliangcn.com/2009/07/php%e6%ba%90%e4%bb%a3%e7%a0%81%e5%88%86%e6%9e%90%e4%b9%8bhashtable/</link>
			</item>
</channel>
</rss>

</head>