﻿<?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-一江春水向东流-文章分类-算法及数据结构</title><link>http://www.blogjava.net/huyi2006/category/17679.html</link><description>                            做一个有思想的人</description><language>zh-cn</language><lastBuildDate>Thu, 10 Apr 2008 21:56:05 GMT</lastBuildDate><pubDate>Thu, 10 Apr 2008 21:56:05 GMT</pubDate><ttl>60</ttl><item><title>基于C语言的内存池设计与实现[转]</title><link>http://www.blogjava.net/huyi2006/articles/192023.html</link><dc:creator>胡意</dc:creator><author>胡意</author><pubDate>Thu, 10 Apr 2008 15:26:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/192023.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/192023.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/192023.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/192023.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/192023.html</trackback:ping><description><![CDATA[
		<div class="content" id="fontzoom">
				<p>
				</p>
				<p>介绍：</p>
				<p>       设计内存池的目标是为了保证服务器长时间高效的运行，通过对申请空间小而申请频繁的对象进行有效管理，减少内存碎片的产生，合理分配管理用户内存，从而减少系统中出现有效空间足够，而无法分配大块连续内存的情况。</p>
				<p>目标：</p>
				<p>    此次设计内存池的基本目标，需要满足线程安全性（多线程），适量的内存泄露越界检查，运行效率不太低于malloc/free方式，实现对4-128字节范围内的内存空间申请的内存池管理（非单一固定大小对象管理的内存池）。</p>
				<p>内存池技术设计与实现</p>
				<p>    本内存池的设计方法主要参考SGI的alloc的设计方案，为了适合一般的应用，并在alloc的基础上做一些简单的修改。</p>
				<p>    Mempool的内存池设计方案如下（也可参考候捷《深入剖析STL》）</p>
				<p>    从系统申请大块heap内存，在此内存上划分不同大小的区块，并把具有相同大小的区块连接起来，组成一个链表。比如A大小的块，组成链表L，当申请A大小时，直接从链表L头部（如果不为空）上取到一块交给申请者，当释放A大小的块时，直接挂接到L的头部。内存池的原理比较简单，但是在具体实现过程中大量的细节需要注意。</p>
				<p>    1：字节对齐。</p>
				<p>    为了方便内存池中对象的管理，需要对申请内存空间的进行调整，在Mempool中，字节对齐的大小为最接近8倍数的字节数。比如，用户申请5个字节，Mempool首先会把它调整为8字节。比如申请22字节，会调整为24，对比关系如下</p>
				<table cellspacing="0" cellpadding="0" border="1">
						<tbody>
								<tr>
										<td valign="top" width="56">
												<p>序号</p>
										</td>
										<td valign="top" width="108">
												<p>对齐字节</p>
										</td>
										<td valign="top" width="177">
												<p>范围</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>0</p>
										</td>
										<td valign="top" width="108">
												<p>8</p>
										</td>
										<td valign="top" width="177">
												<p>1-8</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>1</p>
										</td>
										<td valign="top" width="108">
												<p>16</p>
										</td>
										<td valign="top" width="177">
												<p>9-16</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>2</p>
										</td>
										<td valign="top" width="108">
												<p>24</p>
										</td>
										<td valign="top" width="177">
												<p>17-24</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>3</p>
										</td>
										<td valign="top" width="108">
												<p>32</p>
										</td>
										<td valign="top" width="177">
												<p>25-32</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>4</p>
										</td>
										<td valign="top" width="108">
												<p>40</p>
										</td>
										<td valign="top" width="177">
												<p>33-40</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>5</p>
										</td>
										<td valign="top" width="108">
												<p>48</p>
										</td>
										<td valign="top" width="177">
												<p>41-48</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>6</p>
										</td>
										<td valign="top" width="108">
												<p>56</p>
										</td>
										<td valign="top" width="177">
												<p>49-56</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>7</p>
										</td>
										<td valign="top" width="108">
												<p>64</p>
										</td>
										<td valign="top" width="177">
												<p>57-64</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>8</p>
										</td>
										<td valign="top" width="108">
												<p>72</p>
										</td>
										<td valign="top" width="177">
												<p>65-72</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>9</p>
										</td>
										<td valign="top" width="108">
												<p>80</p>
										</td>
										<td valign="top" width="177">
												<p>73-80</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>10</p>
										</td>
										<td valign="top" width="108">
												<p>88</p>
										</td>
										<td valign="top" width="177">
												<p>81-88</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>11</p>
										</td>
										<td valign="top" width="108">
												<p>96</p>
										</td>
										<td valign="top" width="177">
												<p>89-96</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>12</p>
										</td>
										<td valign="top" width="108">
												<p>104</p>
										</td>
										<td valign="top" width="177">
												<p>97-104</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>13</p>
										</td>
										<td valign="top" width="108">
												<p>112</p>
										</td>
										<td valign="top" width="177">
												<p>105-112</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>14</p>
										</td>
										<td valign="top" width="108">
												<p>120</p>
										</td>
										<td valign="top" width="177">
												<p>113-120</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>15</p>
										</td>
										<td valign="top" width="108">
												<p>128</p>
										</td>
										<td valign="top" width="177">
												<p>121-128</p>
										</td>
								</tr>
						</tbody>
				</table>
				<p align="center">（图1）</p>
				<p>对于超过128字节的申请，直接调用malloc函数申请内存空间。这里设计的内存池并不是对所有的对象进行内存管理，只是对申请内存空间小，而申请频繁的对象进行管理，对于超过128字节的对象申请，不予考虑。这个需要与实际项目结合，并不是固定不变的。实现对齐操作的函数如下</p>
				<p align="left">static size_t round_up(size_t size)<br />{<br />        return (((size)+7) &amp;~ 7);// 按8字节对齐<br />}</p>
				<p>2：构建索引表</p>
				<p>内存池中管理的对象都是固定大小，现在要管理0-128字节的范围内的对象申请空间，除了采用上面提到的字节对齐外，还需要变通一下，这就是建立索引表，做法如下；<br />static _obj*  free_list[16];<br />创建一个包含16个_obj*指针的数组，关于_obj结构后面详细讲解。free_list[0]记录所有空闲空间为8字节的链表的首地址；free_list[1]对应16字节的链表，free_list[2]对应24字节的列表。free_list中的下标和字节链表对应关系参考图1中的“序号”和“对齐字节”之间的关系。这种关系，我们很容易用算法计算出来。如下</p>
				<p align="left">static size_t freelist_index(size_t size)<br />{<br />        return (((size)+7)/7-1);// 按8字节对齐<br />}</p>
				<p>    所以，这样当用户申请空间A时，我们只是通过上面简单的转换，就可以跳转到包含A字节大小的空闲链表上,如下；<br />_obj** p = free_list[freelist_index(A)];<br /><br />3:构建空闲链表</p>
				<p>通过索引表，我们知道mempool中维持着16条空闲链表，这些空闲链表中管理的空闲对象大小分别为8，16，24，32，40…128。这些空闲链表链接起来的方式完全相同。一般情况下我们构建单链表时需要创建如下的一个结构体。</p>
				<p>struct Obj<br />{<br />    Obj *next;<br />    Char* p;<br />    Int iSize;<br />}</p>
				<p>next指针指向下一个这样的结构，p指向真正可用空间,iSize用于只是可用空间的大小，在其他的一些内存池实现中，还有更复杂的结构体，比如还包括记录此结构体的上级结构体的指针，结构体中当前使用空间的变量等，当用户申请空间时，把此结构体添加的用户申请空间中去，比如用户申请12字节的空间，可以这样做</p>
				<p>Obj *p = (Obj*)malloc(12+sizeof(Obj));<br />p-&gt;next = NULL;<br />p-&gt;p = (char*)p+sizeof(Obj);<br />p-&gt;iSize = 12;</p>
				<p>但是，我们并没有采用这种方式，这种方式的一个缺点就是，用户申请小空间时，内存池加料太多了。比如用户申请12字节时，而真实情况是内存池向内存申请了12+ sizeof(Obj)=12+12=24字节的内存空间，这样浪费大量内存用在标记内存空间上去，并且也没有体现索引表的优势。Mempool采用的是union方式</p>
				<p>union Obj<br />{<br />    Obj *next;<br />    char client_data[1];<br />}</p>
				<p>这里除了把上面的struct修改为union，并把int iSize去掉，同时把char*p，修改为char client_data[1]，并没有做太多的修改。而优势也恰恰体现在这里。如果采用struct方式，我们需要维护两条链表，一条链表是，已分配内存空间链表，另一条是未分配（空闲）空间链表。而我们使用索引表和union结构体，只需要维护一条链表，即未分配空间链表。具体如下</p>
				<p>索引表的作用有两条1：如上所说，维护16条空闲链表2：变相记录每条链表上空间的大小，比如下标为3的索引表内维持着是大小为24字节的空闲链表。这样我们通过索引表减少在结构体内记录p所指向空间大小的iSize变量。从而减少4个字节。</p>
				<p>Union的特性是，结构内的变量是互斥存在的。再运行状态下，只是存在一种变量类型。所以在这里sizeof(Obj)的大小为4，难道这里我们也需要把这4字节也加到用户申请空间中去嘛？其实不是，如果这样，我们又抹杀了union的特性。</p>
				<p>当我们构建空闲分配链表时，我们通过next指向下一个union结构体，这样我们不使用p指针。当把这个结构体分配出去时，我们直接返回client_data的地址，此时client_data正好指向申请空间的首字节。所以这样，我们就不用在用户申请空间上添加任何东西。</p>
				<p align="center">
						<a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000px8" target="_blank">
								<img alt="" src="http://album.sina.com.cn/pic/3fe3099c02000px8" border="0" />
						</a>
						<a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000px8" target="_blank">
								<img alt="" src="http://album.sina.com.cn/pic/3fe3099c02000px8" border="0" />
						</a>
						<br />图2</p>
				<p>    Obj的连接方式如上所示，这样我们无需为用户申请空间添加任何内容。   </p>
				<p>4：记录申请空间字节数</p>
				<p>如果采用面向对象方式，或者我们在释放内存池的空间时能够明确知道释放空间的大小，无需采用这种方式。</p>
				<p align="center">
						<a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000px9" target="_blank">
								<img alt="" src="http://album.sina.com.cn/pic/3fe3099c02000px9" border="0" />
						</a>
						<br />图3</p>
				<p>在C语言中的free没有传递释放空间大小，而可以正确释放，在这里也是模仿这种方式，采用这种记录申请空间大小的方式去释放内存。用户申请空间+1操作将在字节对齐之前执行，找到合适空间后，把首字节改写为申请空间的大小，当然1个字节最多纪录256个数，如果项目需要，可以设置为short类型或者int类型，不过这样就需要占用用户比较大的空间。当释放内存空间时，首先读取这个字节，获取空间大小，进行释放。为了便于对大于128字节对象的大小进行合适的释放，同时也对大于128字节的内存申请，添加1字节记录大小。所以现在这里限制了用户内存申请空间不得大于255字节，不过现在已经满足项目要求。当然也可以修改为用short类型记录申请空间的大小。</p>
				<p>    // 申请<br />    *(( unsigned char *)result) = (size_t)n;<br />    unsigned char * pTemp = (unsigned char*)result;<br />    ++pTemp;<br />    result = (_obj*)pTemp;<br />    return result;</p>
				<p>    // 释放<br />    unsigned char * pTemp = (unsigned char *)ptr;<br />    --pTemp;<br />    ptr = (void*)pTemp;<br />    n = (size_t)(*( unsigned char *)ptr);</p>
				<p>5：内存池的分配原理</p>
				<p>在内存池的设计中，有两个重要的操作过程1：chunk_alloc，申请大块内存，2：refill回填操作，内存池初始化化时并不是为索引表中的每一项都创建空闲分配链表，这个过程会推迟到，只有用户提取请求时才会创建这样的分配链表。详细参考如下代码（在sgi中stl_alloc.h文件中你也可以看到这两个函数），主要步骤在注释中已经说明。</p>
				<p align="left">/**<br />* @bri: 申请大块内存，并返回size*(*nobjs)大小的内存块<br />* @param: size,round_up对齐后的大小,nobjs<br />* @return: 返回指向第一个对象内存指针<br />*/<br />static char* chunk_alloc(size_t size, int *nobjs)<br />{<br />     /**&lt; 返回指针 */<br />     char* __result;<br />     /**&lt; 申请内存块大小 */<br />     size_t __total_bytes = size *(*nobjs);<br />     /**&lt; 当前内存可用空间 */<br />     size_t __bytes_left = _end_free - _start_free;<br /></p>
				<p align="left">     /**&lt; 内存池中还有大片可用内存 */<br />     if (__bytes_left &gt;= __total_bytes)<br />     {<br />         __result = _start_free;<br />         _start_free += __total_bytes;<br />         return (__result);<br />     }<br />     /**&lt; 至少还有一个对象大小的内存空间 */<br />     else if (__bytes_left &gt;= size)<br />     {<br />         *nobjs = (int)(__bytes_left/size);<br />         __total_bytes = size * (*nobjs);<br />         __result = _start_free;<br />         _start_free += __total_bytes;<br />         return (__result);<br />     }<br />     /**&lt; 内存池中没有任何空间 */<br />     else<br />     {<br />         /**&lt; 重新申请内存池的大小 */<br />         size_t __bytes_to_get = 2 * __total_bytes + round_up(_heap_size &gt;&gt; 4);<br />         /**&lt; 把内存中剩余的空间添加到freelist中 */<br />         if(__bytes_left &gt; 0)<br />         {<br />              _obj *VOLATILE* __my_free_list = <br />                   _free_list + freelist_index(__bytes_left);<br />              ((_obj*)_start_free)-&gt;free_list_link =<br />*__my_free_list;<br />              *__my_free_list = (_obj*)_start_free;<br />         }<br />         // 申请新的大块空间<br />         _start_free = (char*)malloc(__bytes_to_get);<br />         /*=======================================================================*/<br />         memset(_start_free,0,__bytes_to_get);<br />         /*=======================================================================*/<br />         // 系统内存已经无可用内存，那么从内存池中压缩内存<br />         if(0 == _start_free)<br />         {<br />              size_t __i;<br />              _obj *VOLATILE* __my_free_list;<br />              _obj *__p;<br />              /**&lt; 从freelist中逐项检查可用空间(此时只收集比size对象大的内存空间) */<br />              for (__i = size; __i &lt;= (size_t)__MAX_BYTES; __i += __ALIGN)<br />              {<br />                   __my_free_list = _free_list + freelist_index(__i);<br />                   __p = *__my_free_list;<br />                   /**&lt; 找到空闲块 */<br />                   if (__p != 0)<br />                   {<br />                       *__my_free_list = __p-&gt;free_list_link;<br />                       _start_free = (char*)__p;<br />                       _end_free = _start_free + __i;<br />                       return (chunk_alloc(size,nobjs));<br />                   }<br />              }<br />              _end_free = 0;<br />              /**&lt; 再次申请内存，可能触发一个异常 */<br />              _start_free = (char*)malloc(__bytes_to_get);<br />         }<br />         /**&lt; 记录当前内存池的容量 */<br />         _heap_size += __bytes_to_get;<br />         _end_free = _start_free + __bytes_to_get;<br />         return (chunk_alloc(size,nobjs));<br />     }<br />}</p>
				<p align="left">/*=======================================================================*/<br />/**<br /> * @bri: 填充freelist的连接，默认填充20个<br /> * @param: __n，填充对象的大小，8字节对齐后的value<br /> * @return: 空闲<br /> */<br />static void* refill(size_t n)<br />{<br />     int __nobjs = 20;<br />     char* __chunk = (char*)chunk_alloc(n, &amp;__nobjs);<br />     _obj *VOLATILE* __my_free_list;<br />     _obj *VOLATILE* __my_free_list1;<br />     _obj * __result;<br />     _obj * __current_obj;<br />     _obj * __next_obj;<br />     int __i;<br />     // 如果内存池中仅有一个对象<br />     if (1 == __nobjs) <br />         return(__chunk);<br />     __my_free_list = _free_list + freelist_index(n);<br />     /* Build free list in chunk */<br />     __result = (_obj*)__chunk;<br />     *__my_free_list = __next_obj = (_obj*)(__chunk + n);<br />     __my_free_list1 = _free_list + freelist_index(n);<br />     for (__i = 1;; ++__i)<br />     {<br />         __current_obj = __next_obj;<br />         __next_obj = (_obj*)((char*)__next_obj+n);<br />         if(__nobjs - 1 == __i)<br />         {<br />              __current_obj-&gt;free_list_link = 0;<br />              break;<br />         }else{<br />              __current_obj-&gt;free_list_link = __next_obj;<br />         }<br />     }<br />     return(__result);<br />}<br /></p>
				<p>经过上面操作后，内存池可能会成为如下的一种状态。从图上我们可以看到，已经构建了8，24，88，128字节的空闲分配链表，而其他没有分配空闲分配链表的他们的指针都指向NULL。我们通过判断索引表中的指针是否为NULL，知道是否已经构建空闲分配表或者空闲分配表是否用完，如果此处指针为NULL，我们调用refill函数，重新申请20个这样大小的内存空间，并把他们连接起来。在refill函数内，我们要查看大内存中是否有可用内存，如果有，并且大小合适，就返回给refill函数。<br /></p>
				<p align="center">
						<a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxg" target="_blank">
								<img alt="" src="http://album.sina.com.cn/pic/3fe3099c02000pxg" border="0" />
						</a>
						<br />图4</p>
				<p> </p>
				<p>    6：线程安全<br />    采用互斥体，保证线程安全。<br /><br />内存池测试</p>
				<p>    内存池的测试主要分两部分测试1：单线程下malloc与mempool的分配速度对比2：多线程下malloc和mempool的分配速度对比，我们分为4，10，16个线程进行测试了。<br />    测试环境：操作系统：windows2003+sp1，VC7.1+sp1，硬件环境：intel(R) Celeron(R) CPU 2.53GHz,512M物理内存。<br /><br />    申请内存空间设定如下<br />#define ALLOCNUMBER0 4<br />#define ALLOCNUMBER1 7<br />#define ALLOCNUMBER2 23<br />#define ALLOCNUMBER3 56<br />#define ALLOCNUMBER4 10<br />#define ALLOCNUMBER5 60<br />#define ALLOCNUMBER6 5<br />#define ALLOCNUMBER7 80<br />#define ALLOCNUMBER8 9<br />#define ALLOCNUMBER9 100</p>
				<p>    Malloc方式和mempool方式均使用如上数据进行内存空间的申请和释放。申请过程，每次循环申请释放上述数据20次<br />    我们对malloc和mempool，分别进行了如下申请次数的测试（单位为万）</p>
				<p>
				</p>
				<table cellspacing="0" cellpadding="0" width="325" border="0">
						<tbody>
								<tr>
										<td nowrap="" width="25">
												<p align="right">2</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">10</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">20</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">30</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">40</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">50</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">80</p>
										</td>
										<td nowrap="" width="38">
												<p align="right">100</p>
										</td>
										<td nowrap="" width="38">
												<p align="right">150</p>
										</td>
										<td nowrap="" width="41">
												<p align="right">200</p>
										</td>
								</tr>
						</tbody>
				</table>malloc和mempool在单线程，多线程，release，debug版的各种测试数据，形成如下的统计图 
<p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxh" target="_blank"></a><br />图5</p><p>可以看到mempool无论在多线程还是在单线程情况下，mempool的速度都优于malloc方式的直接分配。</p><p>    Malloc方式debug模式下，在不同的线程下，运行时间如下，通过图片可知，malloc方式，在debug模式下，申请空间的速度和多线程的关系不大。多线程方式，要略快于单线程的运行实现。</p><p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxi" target="_blank"></a><br />图6</p><p>    Malloc方式release模式测试结果如下。</p><p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxj" target="_blank"></a><br />图7</p><p>多线程的优势，逐渐体现出来。当执行200w次申请和释放时，多线程要比单线程快1500ms左右，而4，10，16个线程之间的差别并不是特别大。不过整体感觉4个线程的运行时间要稍微高于10，16个线程的情况下，意味着进程中线程越多用在线程切换上的时间就越多。</p><p>下面是mempool在debug测试结果<br /></p><p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxk" target="_blank"></a><br />图8</p><p>    下面是mempool在release模式下的测试结果</p><p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxl" target="_blank"></a><br />图9</p><p>    以上所有统计图中所用到的数据，是我们测试三次后平均值。</p><p>通过上面的测试，可以知道mempool的性能基本上超过直接malloc方式，在200w次申请和释放的情况下，单线程release版情况下，mempool比直接malloc快110倍。而在4个线程情况下，mempool要比直接malloc快7倍左右。以上测试只是申请速度的测试，在不同的压力情况下，测试结果可能会不同，测试结果也不能说明mempool方式比malloc方式稳定。<br /><br />    小结：内存池基本上满足初期设计目标，但是她并不是完美的，有缺陷，比如,不能申请大于256字节的内存空间，无内存越界检查，无内存自动回缩功能等。只是这些对我们的影响还不是那么重要。</p><p>由于这是一个公司项目，代码涉及版权，所以不能发布出来。如果你想做自己的内存池，可以与我联系ugg_xchj#hotmail.com.</p></div>
<img src ="http://www.blogjava.net/huyi2006/aggbug/192023.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">胡意</a> 2008-04-10 23:26 <a href="http://www.blogjava.net/huyi2006/articles/192023.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>若干经典的字符串哈希函数 </title><link>http://www.blogjava.net/huyi2006/articles/191348.html</link><dc:creator>胡意</dc:creator><author>胡意</author><pubDate>Mon, 07 Apr 2008 11:57:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/191348.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/191348.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/191348.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/191348.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/191348.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: //				 RS Hash Function																								unsigned 				int				 RSHash(				char				 				*				str)																								{        unsigned 						int						 b ...&nbsp;&nbsp;<a href='http://www.blogjava.net/huyi2006/articles/191348.html'>阅读全文</a><img src ="http://www.blogjava.net/huyi2006/aggbug/191348.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">胡意</a> 2008-04-07 19:57 <a href="http://www.blogjava.net/huyi2006/articles/191348.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQLite数据库文件格式分析(B树的基本组织)</title><link>http://www.blogjava.net/huyi2006/articles/189050.html</link><dc:creator>胡意</dc:creator><author>胡意</author><pubDate>Thu, 27 Mar 2008 08:37:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/189050.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/189050.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/189050.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/189050.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/189050.html</trackback:ping><description><![CDATA[
		<span id="ContentBody" style="PADDING-RIGHT: 10px; DISPLAY: block; PADDING-LEFT: 10px; PADDING-BOTTOM: 10px; PADDING-TOP: 10px">此分析称为简易版，因为后面还计划分析一个更复杂的数据库文件，以深入理解SQLite数据库B树实现的结构，从简易的开始不失为一种好的学习方法，这里的简易版本文件是指大小为2K字节，即每个B树页1K字节，共两个B树页，补充说明一下，这里的B树页就是指经典数据结构书上所讲的B树节点，在这里称为页是因为SQLite在实现B树时就是使用页page的概念来组织的。 
<div class="content imgzoom"></div><div class="content imgzoom">创建方法如下：<br />CREATE TABLE tbl1(one varchar(10),two varchar(10));<br />INSERT INTO "tbl1" VALUES('first', 'xxx');<br />INSERT INTO "tbl1" VALUES('second', 'yyy');</div><div class="content imgzoom"><br />然后退出，用UltraEdit打开这个数据库文件：<br />00000000h: 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 ; SQLite format 3.<br />00000010h: 04 00 01 01 00 40 20 20 00 00 00 07 00 00 00 00 ; .....@  ........<br />00000020h: 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 01 ; ................<br />00000030h: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ; ................<br />00000040h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................<br />00000050h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................<br />00000060h: 00 00 00 00 0D 00 00 00 01 03 B8 00 03 B8 00 00 ; ..........?.?.<br />00000070h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................<br />这中间部分全部都是零。省去！<br />000003a0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................<br />000003b0h: 00 00 00 00 00 00 00 00 46 01 06 17 15 15 01 71 ; ........F......q<br />000003c0h: 74 61 62 6C 65 74 62 6C 31 74 62 6C 31 02 43 52 ; tabletbl1tbl1.CR<br />000003d0h: 45 41 54 45 20 54 41 42 4C 45 20 74 62 6C 31 28 ; EATE TABLE tbl1(<br />000003e0h: 6F 6E 65 20 76 61 72 63 68 61 72 28 31 30 29 2C ; one varchar(10),<br />000003f0h: 74 77 6F 20 76 61 72 63 68 61 72 28 31 30 29 29 ; two varchar(10))<br /></div><div class="content imgzoom">     这是第一个B树页，这个B树页里存放了表sqlite_master的信息，这就是SQLite数据库的系统表了。<br />下面分析一下这些二进制的具体涵义，SQLite统一采用大端法来表示数据，不同与一般intel机器的小端法了：<br />偏移地址   大小    涵义<br /> 0          16     "SQLite format 3\000"<br />16           2     400H＝1024个字节，每个页面的字节数  <br />18           2     0101H表示版本号而已<br />20           1     每页末端的未用空间，这里为零表示数据都是从每页最后一个字节开始存放<br />21           1     最大负载分片数，类似与IP分片，一页存不下，要分片<br />22           1     最小负载分片数<br />23           1     最小叶子负载分片数<br />24           4     文件修改计数，用于实现并行访问<br />28           4     保留未用<br />32           4     第一个freelist页<br />36           4     文件中的freelist页数<br />40          60     这里未用<br />上面的这一百个字节称为数据库文件的文件头，这个文件头只有第一个B树页才有，后面的每一个B树页都没有这个结构，后面每一页结构都相同：<br />依次为：B树页头结构，B树指针结构，未用空间，B树实际数据负载。<br />这里和经典数据结构书上的B树结构有些出入，这里的目的是实际应用方便，而书上的结构目的是解释清楚B树的原理。所以有些不同：<br />一般书上讲的一个B树页的结构为：指针，数据，指针，数据，指针，数据，...，指针<br />而SQLite组织为：指针，指针，指针，...，指针，数据，数据，...数据。<br />第一个页面中从00000060h行第五个字节开始就表示B树页头结构了：<br />偏移地址   大小    涵义<br />0           1      0Dh＝1101b各位意义为1: intkey, 2: zerodata, 4: leafdata, 8: leaf<br />1           2      第一个空闲块的字节偏移量，这里为0<br />3           2      01，这个B树页存放的记录数为1个，即系统表中只存放了一条记录，因为只创建了一个表tbl1<br />5           2      负载区首地址，03B8，往下看到000003b0h行那个46就是负载区的开始了<br />7           1      分片数，这里数据少，不考虑，所以为0<br />到0000006Bh偏移处B数头结束了，接下来的就是B数指针结构了，此处只有一项，只有一个指针03B8h处。<br />从000003B8h偏移到结束都是sqlite_master表的实际数据了。当然这些数据也是有结构的。46h表示这条记录有70个字节，除去其本身46，和后面的01是索引外，整个记录刚好是70个字节，01索引后面都是payload负载数据了。<br />如法炮制，下面列出第二个B树页：<br />00000400h: 0D 00 00 00 02 03 E5 00 03 F3 03 E5 00 00 00 00 ; ......?.??...<br />00000410h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................<br />这中间部分全部为零。省去！<br />000007d0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................<br />000007e0h: 00 00 00 00 00 0C 02 03 19 13 73 65 63 6F 6E 64 ; ..........second<br />000007f0h: 79 79 79 0B 01 03 17 13 66 69 72 73 74 78 78 78 ; yyy.....firstxxx<br />由于不是第一页，所以不存在文件头的100个字节了，一开始就是B树页头结构了，这里有两个指针03F3和03E5，其它的和上面一样。整个数据库管理系统就是准确无误地对这个文件进行管理。<br />进一步的工作：只有数据多了，才能看出B树组织的好处：查找，删除，增加的快速！把这个文件变大再分析！</div></span>
<img src ="http://www.blogjava.net/huyi2006/aggbug/189050.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">胡意</a> 2008-03-27 16:37 <a href="http://www.blogjava.net/huyi2006/articles/189050.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>行编辑器的数据结构运用－－－堆栈</title><link>http://www.blogjava.net/huyi2006/articles/160348.html</link><dc:creator>胡意</dc:creator><author>胡意</author><pubDate>Tue, 13 Nov 2007 14:00:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/160348.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/160348.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/160348.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/160348.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/160348.html</trackback:ping><description><![CDATA[
		<p>/****************a simple stack**********************************/</p>
		<p>#include &lt;stdio.h&gt;</p>
		<p>#define STACK_INIT_SIZE 100<br />#define STACKINCREMENT  10<br />typedef int bool;<br />#define TRUE   1<br />#define FALSE  0<br />typedef struct <br />{<br /> char *base;<br /> char *top;<br /> int stacksize;<br />}sqStack;</p>
		<p>//=======================function protoType=============================<br />bool InitStack(sqStack *stk);<br />bool DestroyStack(sqStack *stk);<br />bool ClearStack(sqStack *stk);<br />bool StackEmpty(sqStack stk);<br />int  StackLength(sqStack stk);<br />bool GetTop(sqStack stk, char *item);<br />bool Push(sqStack *s, char item);<br />bool Pop(sqStack *s, char *item);</p>
		<p>bool InitStack(sqStack *stk)<br />{<br /> stk-&gt;base = (char *)malloc(STACK_INIT_SIZE * sizeof(char));<br /> if (!stk-&gt;base)<br /> return FALSE;<br /> stk-&gt;top = stk-&gt;base;<br /> stk-&gt;stacksize = STACK_INIT_SIZE;<br /> return TRUE;<br />}</p>
		<p>bool GetTop(sqStack stk, char *item)<br />{<br /> if (stk.top == stk.base)<br /> return FALSE;<br /> item = (stk.top-1);<br /> return TRUE;<br />}</p>
		<p>bool Push(sqStack *stk, char item)<br />{<br /> if (stk-&gt;top - stk-&gt;base &gt;= stk-&gt;stacksize)<br /> {<br />  printf("allocate New Mem\n");<br />  stk-&gt;base = (char *)realloc(stk-&gt;base, (stk-&gt;stacksize + STACKINCREMENT)* sizeof(char));<br />  if (!stk-&gt;base)<br />  return FALSE;<br />  stk-&gt;top = stk-&gt;base + stk-&gt;stacksize;<br />  stk-&gt;stacksize += STACKINCREMENT;<br /> }<br /> *stk-&gt;top = item;<br /> stk-&gt;top++;<br /> return TRUE;<br />}</p>
		<p>bool Pop(sqStack *stk, char* item)<br />{<br /> if (stk-&gt;top == stk-&gt;base)<br /> return FALSE;<br /> stk-&gt;top--;<br /> *item = *(stk-&gt;top);<br /> return TRUE;<br />}</p>
		<p>bool StackEmpty(sqStack stk)<br />{<br /> if (stk.top == stk.base)<br /> return TRUE;<br /> else<br /> return FALSE;<br />}</p>
		<p>bool ClearStack(sqStack *stk)<br />{<br /> stk-&gt;top = stk-&gt;base;<br /> memset(stk-&gt;base, 0, sizeof(char));<br /> if (stk-&gt;top)<br /> return TRUE;<br /> else<br /> return FALSE;<br />}<br />bool DestroyStack(sqStack *stk)<br />{<br /> free(stk-&gt;base);<br /> printf("free memery\n");<br /> return TRUE;<br />}    </p>
		<p>int main(int argc, char** argv)<br />{<br /> sqStack stk;<br /> char ch;<br /> InitStack(&amp;stk);<br /> ch = getchar();<br /> while(ch != EOF)<br /> {<br />  while(ch != EOF &amp;&amp; ch != '\n')<br />  {<br />   switch (ch)<br />   {<br />      case '#':<br />    Pop(&amp;stk, &amp;ch);<br />    break;<br />      case <a href="mailto:'@'">'@'</a>:<br />       ClearStack(&amp;stk);<br />       break;<br />      case 'q':        <br />        DestroyStack(&amp;stk);<br />        exit(1);<br />        break;        <br />      default:<br />       Push(&amp;stk, ch);<br />   }<br />   ch = getchar();<br />  }<br />  ClearStack(&amp;stk);<br />  if (ch != EOF)<br />  ch = getchar();<br /> }<br /> DestroyStack(&amp;stk); <br /> return 1;<br />}<br /><br />特别要注意的是堆栈的中操作栈顶的值，在就是对内存的操作</p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/160348.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">胡意</a> 2007-11-13 22:00 <a href="http://www.blogjava.net/huyi2006/articles/160348.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>KMP字符串匹配算法</title><link>http://www.blogjava.net/huyi2006/articles/114588.html</link><dc:creator>胡意</dc:creator><author>胡意</author><pubDate>Sun, 29 Apr 2007 07:52:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/114588.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/114588.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/114588.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/114588.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/114588.html</trackback:ping><description><![CDATA[
		<p>KMP算法的关键是根据给定的模式串W[1,m],定义一个next函数。next函数包含了模式串本身局部匹配的信息。<br />   KMP算法的基本思想是：假设在模式匹配的进程中，执行T[i]和W[j]的匹配检查。若T[i]=W[j]，则继续检查T[i+1]和W[j+1]是否匹配。若T[i]&lt;&gt;W[j]，则分成两种情况：若j=1，则模式串右移一位，检查T[i+1]和W[1]是否匹配；若1&lt;j&lt;=m，则模式串右移j-next(j)位，检查T[i]和W[next(j)]是否匹配。重复此过程直到j=m或i=n结束。</p>
		<p>/**************************************<br /> *<br /> *  KMP字符串匹配算法<br /> *<br /> *<br /> **************************************/<br />#include &lt;stdio.h&gt;<br />#include &lt;string.h&gt;<br />int next[10];<br />void getnext(char *p)<br />{<br /> int idx_p, len_p, k;<br /> <br /> idx_p = 0;<br /> len_p = strlen(p);<br /> k = -1;<br /> next[0] = -1;<br /> <br /> while(idx_p &lt; len_p -1)<br /> {<br />  if ((k == -1) || *(p+idx_p) == *(p+k)) <br />  {<br />   idx_p = idx_p + 1;<br />   k = k + 1;<br />   next[idx_p] = k; <br />  }<br />  else<br />  {<br />   k = next[k]; <br />  }<br /> } <br />}<br />int kmp(char *s, char *p)<br />{<br /> int len_p, len_s, idx_p, idx_s;<br /> <br /> len_p = strlen(p);<br /> len_s = strlen(s);<br /> idx_p = idx_s = 0;<br /> <br /> while ((idx_p &lt; len_p) &amp;&amp; (idx_s &lt; len_s))<br /> {<br />  /* 字符匹配或者要比较p中的第一个字符 */<br />  if ((idx_p == -1) || *(p+idx_p) == *(s+idx_s))<br />  {<br />   idx_p = idx_p + 1; idx_s = idx_s +1; <br />  } <br />  else<br />  {<br />   idx_p = next[idx_p]; <br />  }<br /> }<br /> if (idx_p &gt;= len_p)<br /> {<br />  return (idx_s - len_p); <br /> }<br /> else <br /> {<br />  return -1; <br /> }<br />}<br />int main()<br />{<br /> int pos, i;<br /> char s[50] = "abaaaabcabcacb";<br /> char p[10] = "aaaabca";<br /> <br /> getnext(p);<br /> if ((pos = kmp(s, p)) == -1)<br /> {<br />  fprintf(stderr, "String \"%s\" doesn't contain Pattern \"%s\"!\n", s, p); <br /> }<br /> else<br /> {<br />  printf("String \"%s\" contains Pattern \"%s\".\n The position of fisrt occur is %d\n", s, p, pos);<br />  printf("%s\n", s);<br />  for (i = 0; i &lt; pos; i++)<br />   printf(" "); <br />  printf("%s\n", p);<br /> }<br /> return 0; <br />}<br /></p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/114588.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">胡意</a> 2007-04-29 15:52 <a href="http://www.blogjava.net/huyi2006/articles/114588.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>