﻿<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>服务器运维及架构&#124;LINUX运维&#124;互联网分享</title>
	<atom:link href="http://www.411c.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.411c.com</link>
	<description>记录我的思想轨迹、工作学习、解决问题和关注的领域</description>
	<lastBuildDate>Thu, 17 May 2012 07:04:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>linux dd命令使用详解</title>
		<link>http://www.411c.com/linux-dd%e5%91%bd%e4%bb%a4%e4%bd%bf%e7%94%a8%e8%af%a6%e8%a7%a3/</link>
		<comments>http://www.411c.com/linux-dd%e5%91%bd%e4%bb%a4%e4%bd%bf%e7%94%a8%e8%af%a6%e8%a7%a3/#comments</comments>
		<pubDate>Thu, 17 May 2012 07:04:07 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[LINUX技术]]></category>
		<category><![CDATA[dd]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=465</guid>
		<description><![CDATA[<p><span style="font-size: 16px">dd 的主要选项：<br />
	指定数字的地方若以下列字符结尾乘以相应的数字:<br />
	b=512, c=1, k=1024, w=2, xm=number m</span></p>
<p><span style="font-size: 16px">if=file<br />
	输入文件名，缺省为标准输入。</span></p>
<p><span style="font-size: 16px">of=file<br />
	输出文件名，缺省为标准输出。</span></p>
<p><span style="font-size: 16px">ibs=bytes<br />
	一次读入 bytes 个字节(即一个块大小为 bytes 个字节)。</span></p>
<p><span style="font-size: 16px">obs=bytes<br />
	一次写 bytes 个字节(即一个块大小为 bytes 个字节)。</span></p>
<p><span style="font-size: 16px">bs=bytes<br />
	同时设置读写块的大小为 bytes ，可代替 ibs 和 obs 。</span></p>
<p><span style="font-size: 16px">cbs=bytes<br />
	一次转换 bytes 个字节，即转换缓冲区大小。</span></p>
<p><span style="font-size: 16px">skip=blocks<br />
	从输入文件开头跳过 blocks 个块后再开始复制。</span></p>
<p><span style="font-size: 16px">seek=blocks<br />
	从输出文件开头跳过 blocks 个块后再开始复制。(通常只有当输出文件是磁盘或磁带时才有效)。</span></p>
<p><span style="font-size: 16px">count=blocks<br />
	仅拷贝 blocks 个块，块大小等于 ibs 指定的字节数。</span></p>
<p><span style="font-size: 16px">conv=conversion[,conversion...]<br />
	用指定的参数转换文件。</span></p>
<p><span style="font-size: 16px">转换参数:<br />
	ascii 转换 EBCDIC 为 ASCII。<br />
	ebcdic 转换 ASCII 为 EBCDIC。<br />
	ibm 转换 ASCII 为 alternate EBCDIC.<br />
	block 把每一行转换为长度为 cbs 的记录，不足部分用空格填充。<br />
	unblock 使每一行的长度都为 cbs ，不足部分用空格填充。<br />
	lcase 把大写字符转换为小写字符。<br />
	ucase 把小写字符转换为大写字符。<br />
	swab 交换输入的每对字节。<br />
	noerror 出错时不停止。<br />
	notrunc 不截短输出文件。<br />
	sync 把每个输入块填充到ibs个字节，不足部分用空(NUL)字符补齐。</span></p>
<p><span style="font-size: 16px">&#160;<br />
	2.实例分析</span></p>
<p><span style="font-size: 16px">2.1.数据备份与恢复</span></p>
<p><span style="font-size: 16px">2.1.1整盘数据备份与恢复<br />
	备份：</span></p>
<p><span style="font-size: 16px">dd if=/dev/hdx of=/dev/hdy<br />
	将本地的/dev/hdx整盘备份到/dev/hdy</span></p>
<p><span style="font-size: 16px">dd if=/dev/hdx of=/path/to/image<br />
	将/dev/hdx全盘数据备份到指定路径的image文件</span></p>
<p><span style="font-size: 16px">dd if=/dev/hdx &#124; gzip &#62;/path/to/image.gz<br />
	备份/dev/hdx全盘数据，并利用gzip工具进行压缩，保存到指定路径<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br />
	恢复：<br />
	dd if=/path/to/image of=/dev/hdx<br />
	将备份文件恢复到指定盘</span></p>
<p><span style="font-size: 16px">gzip -dc /path/to/image.gz &#124; dd of=/dev/hdx<br />
	将压缩的备份文件恢复到指定盘</span></p>
<p><span style="font-size: 16px">&#160;<br />
	2.1.2.利用netcat远程备份</span></p>
<p><span style="font-size: 16px">dd if=/dev/hda bs=16065b &#124; netcat &#60; targethost-IP &#62; 1234<br />
	在源主机上执行此命令备份/dev/hda</span></p>
<p><span style="font-size: 16px">netcat -l -p 1234 &#124; dd of=/dev/hdc bs=16065b<br />
	在目的主机上执行此命令来接收数据并写入/dev/hdc</span></p>
<p><span style="font-size: 16px">netcat -l -p 1234 &#124; bzip2 &#62; partition.img<br />
	netcat -l -p 1234 &#124; gzip &#62; partition.img<br />
	以上两条指令是目的主机指令的变化分别采用bzip2&#160; gzip对数据进行压缩，并将备份文件保存在当前目录。</span></p>
<p>&#160;</p>
<p><span style="font-size: 16px">2.1.3.备份MBR<br />
	备份：<br />
	dd if=/dev/hdx of=/path/to/image count=1 bs=512<br />
	备份磁盘开始的512Byte大小的MBR信息到指定文件<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</span></p>
<p><span style="font-size: 16px">恢复：<br />
	dd if=/path/to/image of=/dev/hdx<br />
	将备份的MBR信息写到磁盘开始部分</span></p>
<p><span style="font-size: 16px">&#160;<br />
	2.1.4.备份软盘<br />
	dd if=/dev/fd0 of=disk.img count=1 bs=1440k<br />
	将软驱数据备份到当前目录的disk.img文件</span></p>
<p><span style="font-size: 16px">&#160;<br />
	2.1.5.拷贝内存资料到硬盘<br />
	dd if=/dev/mem of=/root/mem.bin bs=1024<br />
	将内存里的数据拷贝到root目录下的mem.bin文件</span></p>
<p><span style="font-size: 16px">&#160;<br />
	2.1.6.从光盘拷贝iso镜像<br />
	dd if=/dev/cdrom of=/root/cd.iso<br />
	拷贝光盘数据到root文件夹下，并保存为cd.iso文件&#160;&#160;&#160;&#160;</span></p>
<p><span style="font-size: 16px">&#160;<br />
	2.2.增加Swap分区文件大小<br />
	dd if=/dev/zero of=/swapfile bs=1024 count=262144<br />
	创建一个足够大的文件（此处为256M）</span></p>
<p><span style="font-size: 16px">mkswap /swapfile<br />
	把这个文件变成swap文件</span></p>
<p><span style="font-size: 16px">swapon /swapfile<br />
	启用这个swap文件</span></p>
<p><span style="font-size: 16px">/swapfile swap swap defaults 0 0<br />
	在每次开机的时候自动加载swap文件, 需要在 /etc/fstab 文件中增加一行</span></p>
<p><span style="font-size: 16px">2.3.销毁磁盘数据<br />
	dd if=/dev/urandom of=/dev/hda1<br />
	利用随机的数据填充硬盘，在某些必要的场合可以用来销毁数据。执行此操作以后，/dev/hda1将无法挂载，创建和拷贝操作无法执行。</span></p>
<p><span style="font-size: 16px">2.4磁盘管理<br />
	2.4.1.得到最恰当的block size<br />
	dd if=/dev/zero bs=1024 count=1000000 of=/root/1Gb.file<br />
	dd if=/dev/zero bs=2048 count=500000 of=/root/1Gb.file<br />
	dd if=/dev/zero bs=4096 count=250000 of=/root/1Gb.file&#160;&#160;&#160;&#160;<br />
	dd if=/dev/zero bs=8192 count=125000 of=/root/1Gb.file<br />
	通过比较dd指令输出中所显示的命令执行时间，即可确定系统最佳的block size大小<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<br />
	2.4.2测试硬盘读写速度<br />
	dd if=/root/1Gb.file bs=64k &#124; dd of=/dev/null<br />
	dd if=/dev/zero of=/root/1Gb.file bs=1024 count=1000000<br />
	通过上两个命令输出的执行时间，可以计算出测试硬盘的读／写速度&#160;&#160;&#160;&#160;</span></p>
<p><br />
	<span style="font-size: 16px">2.4.3.修复硬盘</span></p>
<p><span style="font-size: 16px">dd if=/dev/sda of=/dev/sda<br />
	当硬盘较长时间（比如1，2年）放置不使用后，磁盘上会产生magnetic flux point。当磁头读到这些区域时会遇到困难，并可能导致I/O错误。当这种情况影响到硬盘的第一个扇区时，可能导致硬盘报废。上边的命令有可能使这些数据起死回生。且这个过程是安全，高效的。</span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px">dd 的主要选项：<br />
	指定数字的地方若以下列字符结尾乘以相应的数字:<br />
	b=512, c=1, k=1024, w=2, xm=number m</span></p>
<p><span style="font-size: 16px">if=file<br />
	输入文件名，缺省为标准输入。</span></p>
<p><span style="font-size: 16px">of=file<br />
	输出文件名，缺省为标准输出。</span></p>
<p><span style="font-size: 16px">ibs=bytes<br />
	一次读入 bytes 个字节(即一个块大小为 bytes 个字节)。</span></p>
<p><span style="font-size: 16px">obs=bytes<br />
	一次写 bytes 个字节(即一个块大小为 bytes 个字节)。</span></p>
<p><span style="font-size: 16px">bs=bytes<br />
	同时设置读写块的大小为 bytes ，可代替 ibs 和 obs 。</span></p>
<p><span style="font-size: 16px">cbs=bytes<br />
	一次转换 bytes 个字节，即转换缓冲区大小。</span></p>
<p><span style="font-size: 16px">skip=blocks<br />
	从输入文件开头跳过 blocks 个块后再开始复制。</span></p>
<p><span style="font-size: 16px">seek=blocks<br />
	从输出文件开头跳过 blocks 个块后再开始复制。(通常只有当输出文件是磁盘或磁带时才有效)。</span></p>
<p><span style="font-size: 16px">count=blocks<br />
	仅拷贝 blocks 个块，块大小等于 ibs 指定的字节数。</span></p>
<p><span style="font-size: 16px">conv=conversion[,conversion...]<br />
	用指定的参数转换文件。</span></p>
<p><span style="font-size: 16px">转换参数:<br />
	ascii 转换 EBCDIC 为 ASCII。<br />
	ebcdic 转换 ASCII 为 EBCDIC。<br />
	ibm 转换 ASCII 为 alternate EBCDIC.<br />
	block 把每一行转换为长度为 cbs 的记录，不足部分用空格填充。<br />
	unblock 使每一行的长度都为 cbs ，不足部分用空格填充。<br />
	lcase 把大写字符转换为小写字符。<br />
	ucase 把小写字符转换为大写字符。<br />
	swab 交换输入的每对字节。<br />
	noerror 出错时不停止。<br />
	notrunc 不截短输出文件。<br />
	sync 把每个输入块填充到ibs个字节，不足部分用空(NUL)字符补齐。</span></p>
<p><span style="font-size: 16px">&nbsp;<br />
	2.实例分析</span></p>
<p><span style="font-size: 16px">2.1.数据备份与恢复</span></p>
<p><span style="font-size: 16px">2.1.1整盘数据备份与恢复<br />
	备份：</span></p>
<p><span style="font-size: 16px">dd if=/dev/hdx of=/dev/hdy<br />
	将本地的/dev/hdx整盘备份到/dev/hdy</span></p>
<p><span style="font-size: 16px">dd if=/dev/hdx of=/path/to/image<br />
	将/dev/hdx全盘数据备份到指定路径的image文件</span></p>
<p><span style="font-size: 16px">dd if=/dev/hdx | gzip &gt;/path/to/image.gz<br />
	备份/dev/hdx全盘数据，并利用gzip工具进行压缩，保存到指定路径<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	恢复：<br />
	dd if=/path/to/image of=/dev/hdx<br />
	将备份文件恢复到指定盘</span></p>
<p><span style="font-size: 16px">gzip -dc /path/to/image.gz | dd of=/dev/hdx<br />
	将压缩的备份文件恢复到指定盘</span></p>
<p><span style="font-size: 16px">&nbsp;<br />
	2.1.2.利用netcat远程备份</span></p>
<p><span style="font-size: 16px">dd if=/dev/hda bs=16065b | netcat &lt; targethost-IP &gt; 1234<br />
	在源主机上执行此命令备份/dev/hda</span></p>
<p><span style="font-size: 16px">netcat -l -p 1234 | dd of=/dev/hdc bs=16065b<br />
	在目的主机上执行此命令来接收数据并写入/dev/hdc</span></p>
<p><span style="font-size: 16px">netcat -l -p 1234 | bzip2 &gt; partition.img<br />
	netcat -l -p 1234 | gzip &gt; partition.img<br />
	以上两条指令是目的主机指令的变化分别采用bzip2&nbsp; gzip对数据进行压缩，并将备份文件保存在当前目录。</span></p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">2.1.3.备份MBR<br />
	备份：<br />
	dd if=/dev/hdx of=/path/to/image count=1 bs=512<br />
	备份磁盘开始的512Byte大小的MBR信息到指定文件<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p><span style="font-size: 16px">恢复：<br />
	dd if=/path/to/image of=/dev/hdx<br />
	将备份的MBR信息写到磁盘开始部分</span></p>
<p><span style="font-size: 16px">&nbsp;<br />
	2.1.4.备份软盘<br />
	dd if=/dev/fd0 of=disk.img count=1 bs=1440k<br />
	将软驱数据备份到当前目录的disk.img文件</span></p>
<p><span style="font-size: 16px">&nbsp;<br />
	2.1.5.拷贝内存资料到硬盘<br />
	dd if=/dev/mem of=/root/mem.bin bs=1024<br />
	将内存里的数据拷贝到root目录下的mem.bin文件</span></p>
<p><span style="font-size: 16px">&nbsp;<br />
	2.1.6.从光盘拷贝iso镜像<br />
	dd if=/dev/cdrom of=/root/cd.iso<br />
	拷贝光盘数据到root文件夹下，并保存为cd.iso文件&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p><span style="font-size: 16px">&nbsp;<br />
	2.2.增加Swap分区文件大小<br />
	dd if=/dev/zero of=/swapfile bs=1024 count=262144<br />
	创建一个足够大的文件（此处为256M）</span></p>
<p><span style="font-size: 16px">mkswap /swapfile<br />
	把这个文件变成swap文件</span></p>
<p><span style="font-size: 16px">swapon /swapfile<br />
	启用这个swap文件</span></p>
<p><span style="font-size: 16px">/swapfile swap swap defaults 0 0<br />
	在每次开机的时候自动加载swap文件, 需要在 /etc/fstab 文件中增加一行</span></p>
<p><span style="font-size: 16px">2.3.销毁磁盘数据<br />
	dd if=/dev/urandom of=/dev/hda1<br />
	利用随机的数据填充硬盘，在某些必要的场合可以用来销毁数据。执行此操作以后，/dev/hda1将无法挂载，创建和拷贝操作无法执行。</span></p>
<p><span style="font-size: 16px">2.4磁盘管理<br />
	2.4.1.得到最恰当的block size<br />
	dd if=/dev/zero bs=1024 count=1000000 of=/root/1Gb.file<br />
	dd if=/dev/zero bs=2048 count=500000 of=/root/1Gb.file<br />
	dd if=/dev/zero bs=4096 count=250000 of=/root/1Gb.file&nbsp;&nbsp;&nbsp;&nbsp;<br />
	dd if=/dev/zero bs=8192 count=125000 of=/root/1Gb.file<br />
	通过比较dd指令输出中所显示的命令执行时间，即可确定系统最佳的block size大小<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
	2.4.2测试硬盘读写速度<br />
	dd if=/root/1Gb.file bs=64k | dd of=/dev/null<br />
	dd if=/dev/zero of=/root/1Gb.file bs=1024 count=1000000<br />
	通过上两个命令输出的执行时间，可以计算出测试硬盘的读／写速度&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p>
	<span style="font-size: 16px">2.4.3.修复硬盘</span></p>
<p><span style="font-size: 16px">dd if=/dev/sda of=/dev/sda<br />
	当硬盘较长时间（比如1，2年）放置不使用后，磁盘上会产生magnetic flux point。当磁头读到这些区域时会遇到困难，并可能导致I/O错误。当这种情况影响到硬盘的第一个扇区时，可能导致硬盘报废。上边的命令有可能使这些数据起死回生。且这个过程是安全，高效的。</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/linux-dd%e5%91%bd%e4%bb%a4%e4%bd%bf%e7%94%a8%e8%af%a6%e8%a7%a3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>调整Ubuntu 12.04启动画面分辨率</title>
		<link>http://www.411c.com/%e8%b0%83%e6%95%b4ubuntu-12-04%e5%90%af%e5%8a%a8%e7%94%bb%e9%9d%a2%e5%88%86%e8%be%a8%e7%8e%87/</link>
		<comments>http://www.411c.com/%e8%b0%83%e6%95%b4ubuntu-12-04%e5%90%af%e5%8a%a8%e7%94%bb%e9%9d%a2%e5%88%86%e8%be%a8%e7%8e%87/#comments</comments>
		<pubDate>Tue, 15 May 2012 05:14:27 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[UBUNTU]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=463</guid>
		<description><![CDATA[<p><span style="font-size: 16px">ubuntu已经使用一段时间了，但是在开机的时候，出现登陆页面前，总是说显示器的分辨率太小，而输出无信号，其实我显示器也有21寸，后来查阅了资料，原来ubuntu 12.04安装后，这边默认的分辨率是没有设置的，现在就说下我是如何修改启动项设置，让开机画面显示正常的<br />
	第一步：打开终端，修改/etc/default/grub文件</span></p>
<p><span style="font-size: 16px">$ sudo gedit /etc/default/grub<br />
	取消#GRUB_GFXMODE=640&#215;480这一行前面的注释符号，并将后面的数字修改为一个合适的值，不需要太高，比如1024&#215;768。这个值同时会影响grub启动菜单和控制台里文字的分辨率。</span></p>
<p><span style="font-size: 16px">第二步：修改/etc/grub.d/00_header文件<br />
	$ sudo gedit /etc/grub.d/00_header<br />
	按下ctrl+F，查找关键字&#8221;set gfxmode=${GRUB_GFXMODE}&#8221;(去掉双引号)，<br />
	然后在这行下面添加新行，内容是：&#8221;set gfxpayload=keep&#8221; (去掉双引号)</span></p>
<p><span style="font-size: 16px">第三步：更新grub<br />
	$ sudo update-grub</span></p>
<p><span style="font-size: 16px">第四当然是重启了！</span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px">ubuntu已经使用一段时间了，但是在开机的时候，出现登陆页面前，总是说显示器的分辨率太小，而输出无信号，其实我显示器也有21寸，后来查阅了资料，原来ubuntu 12.04安装后，这边默认的分辨率是没有设置的，现在就说下我是如何修改启动项设置，让开机画面显示正常的<br />
	第一步：打开终端，修改/etc/default/grub文件</span></p>
<p><span style="font-size: 16px">$ sudo gedit /etc/default/grub<br />
	取消#GRUB_GFXMODE=640&times;480这一行前面的注释符号，并将后面的数字修改为一个合适的值，不需要太高，比如1024&times;768。这个值同时会影响grub启动菜单和控制台里文字的分辨率。</span></p>
<p><span style="font-size: 16px">第二步：修改/etc/grub.d/00_header文件<br />
	$ sudo gedit /etc/grub.d/00_header<br />
	按下ctrl+F，查找关键字&rdquo;set gfxmode=${GRUB_GFXMODE}&rdquo;(去掉双引号)，<br />
	然后在这行下面添加新行，内容是：&rdquo;set gfxpayload=keep&rdquo; (去掉双引号)</span></p>
<p><span style="font-size: 16px">第三步：更新grub<br />
	$ sudo update-grub</span></p>
<p><span style="font-size: 16px">第四当然是重启了！</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/%e8%b0%83%e6%95%b4ubuntu-12-04%e5%90%af%e5%8a%a8%e7%94%bb%e9%9d%a2%e5%88%86%e8%be%a8%e7%8e%87/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux文件查找命令find和xargs详解</title>
		<link>http://www.411c.com/linux%e6%96%87%e4%bb%b6%e6%9f%a5%e6%89%be%e5%91%bd%e4%bb%a4find%e5%92%8cxargs%e8%af%a6%e8%a7%a3/</link>
		<comments>http://www.411c.com/linux%e6%96%87%e4%bb%b6%e6%9f%a5%e6%89%be%e5%91%bd%e4%bb%a4find%e5%92%8cxargs%e8%af%a6%e8%a7%a3/#comments</comments>
		<pubDate>Sun, 13 May 2012 05:31:52 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[LINUX技术]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=458</guid>
		<description><![CDATA[<p><span style="font-size: 16px">一、find 命令格式</span></p>
<p><span style="font-size: 16px">1、find命令的一般形式为；<br />
	find pathname -options [-print -exec -ok ...]</span></p>
<p><br />
	<span style="font-size: 16px">2、find命令的参数；<br />
	pathname: find命令所查找的目录路径。例如用.来表示当前目录，用<br />
	/来表示系统根目录。</span></p>
<p><span style="font-size: 16px">-print： find命令将匹配的文件输出到标准输出。</span></p>
<p><span style="font-size: 16px">-exec： find命令对匹配的文件执行该参数所给出的shell命令。相应<br />
	命令的形式为&#39;command&#39; {} \;，注意{}和\；之间的空格。</span></p>
<p><span style="font-size: 16px">-ok： 和-exec的作用相同，只不过以一种更为安全的模式来执行该参<br />
	数所给出的shell命令，在执行每一个命令之前，都会给出提示，让用<br />
	户来确定是否执行。</span></p>
<p><br />
	<span style="font-size: 16px">3、find命令选项<br />
	-name<br />
	按照文件名查找文件。</span></p>
<p><span style="font-size: 16px">-perm<br />
	按照文件权限来查找文件。</span></p>
<p><span style="font-size: 16px">-prune<br />
	使用这一选项可以使find命令不在当前指定的目录中查找，如果同时<br />
	使用-depth选项，那么-prune将被find命令忽略。</span></p>
<p><span style="font-size: 16px">-user<br />
	按照文件属主来查找文件。</span></p>
<p><span style="font-size: 16px">-group<br />
	按照文件所属的组来查找文件。</span></p>
<p><span style="font-size: 16px">-mtime -n +n<br />
	按照文件的更改时间来查找文件， - n表示文件更改时间距现在n天以<br />
	内，+ n表示文件更改时间距现在n天以前。find命令还有-atime和-ctime<br />
	&#160;选项，但它们都和-m time选项。</span></p>
<p><span style="font-size: 16px">-nogroup<br />
	查找无有效所属组的文件，即该文件所属的组在/etc/groups中不存在。</span></p>
<p><span style="font-size: 16px">-nouser<br />
	查找无有效属主的文件，即该文件的属主在/etc/passwd中不存在。<br />
	-newer file1 ! file2</span></p>
<p><span style="font-size: 16px">查找更改时间比文件file1新但比文件file2旧的文件。<br />
	-type</span></p>
<p><span style="font-size: 16px">查找某一类型的文件，诸如：</span></p>
<p><span style="font-size: 16px">b - 块设备文件。<br />
	d - 目录。<br />
	c - 字符设备文件。<br />
	p - 管道文件。<br />
	l - 符号链接文件。<br />
	f - 普通文件。</span></p>
<p><span style="font-size: 16px">-size n：[c] 查找文件长度为n块的文件，带有c时表示文件长度以字<br />
	节计。</span></p>
<p><span style="font-size: 16px">-depth：在查找文件时，首先查找当前目录中的文件，然后再在其子目<br />
	录中查找。</span></p>
<p><span style="font-size: 16px">-fstype：查找位于某一类型文件系统中的文件，这些文件系统类型通<br />
	常可以在配置文件/etc/fstab中找到，该配置文件中包含了本系统中有<br />
	关文件系统的信息。</span></p>
<p><span style="font-size: 16px">-mount：在查找文件时不跨越文件系统mount点。</span></p>
<p><span style="font-size: 16px">-follow：如果find命令遇到符号链接文件，就跟踪至链接所指向的文件。</span></p>
<p><span style="font-size: 16px">-cpio：对匹配的文件使用cpio命令，将这些文件备份到磁带设备中。</span></p>
<p><span style="font-size: 16px">另外,下面三个的区别:</span></p>
<p><span style="font-size: 16px">-amin n<br />
	查找系统中最后N分钟访问的文件</span></p>
<p><span style="font-size: 16px">-atime n<br />
	查找系统中最后n*24小时访问的文件</span></p>
<p><span style="font-size: 16px">-cmin n<br />
	查找系统中最后N分钟被改变文件状态的文件</span></p>
<p><span style="font-size: 16px">-ctime n<br />
	查找系统中最后n*24小时被改变文件状态的文件</span></p>
<p><span style="font-size: 16px">-mmin n<br />
	查找系统中最后N分钟被改变文件数据的文件</span></p>
<p><span style="font-size: 16px">-mtime n<br />
	查找系统中最后n*24小时被改变文件数据的文件</span></p>
<p><br />
	<span style="font-size: 16px">4、使用exec或ok来执行shell命令</span></p>
<p><span style="font-size: 16px">使用find时，只要把想要的操作写在一个文件里，就可以用exec来配合<br />
	find查找，很方便的</span></p>
<p><span style="font-size: 16px">在有些操作系统中只允许-exec选项执行诸如l s或ls -l这样的命令。大<br />
	多数用户使用这一选项是为了查找旧文件并删除它们。建议在真正执行<br />
	rm命令删除文件之前，最好先用ls命令看一下，确认它们是所要删除的文件。</span></p>
<p><span style="font-size: 16px">exec选项后面跟随着所要执行的命令或脚本，然后是一对儿{ }，一个空<br />
	格和一个\，最后是一个分号。为了使用exec选项，必须要同时使用print<br />
	选项。如果验证一下find命令，会发现该命令只输出从当前路径起的相对<br />
	路径及文件名。</span></p>
<p><span style="font-size: 16px">例如：为了用ls -l命令列出所匹配到的文件，可以把ls -l命令放在find<br />
	命令的-exec选项中</span></p>
<p><span style="font-size: 16px"># find . -type f -exec ls -l {} \;<br />
	-rw-r--r--&#160; 1 root&#160; root&#160;&#160; 34928 2003-02-25&#160; ./conf/httpd.conf<br />
	-rw-r--r--&#160; 1 root&#160; root&#160;&#160; 12959 2003-02-25&#160; ./conf/magic<br />
	-rw-r--r--&#160; 1 root&#160; root&#160;&#160;&#160;&#160; 180 2003-02-25&#160; ./conf.d/README</span></p>
<p><span style="font-size: 16px">上面的例子中，find命令匹配到了当前目录下的所有普通文件，并在-exec<br />
	选项中使用ls -l命令将它们列出。<br />
	在/logs目录中查找更改时间在5日以前的文件并删除它们：</span></p>
<p><span style="font-size: 16px">$ find logs -type f -mtime +5 -exec rm {} \;</span></p>
<p><span style="font-size: 16px">记住：在shell中用任何方式删除文件之前，应当先查看相应的文件，一<br />
	定要小心！当使用诸如mv或rm命令时，可以使用-exec选项的安全模式。<br />
	它将在对每个匹配到的文件进行操作之前提示你。</span></p>
<p><span style="font-size: 16px">在下面的例子中， find命令在当前目录中查找所有文件名以.LOG结尾、<br />
	更改时间在5日以上的文件，并删除它们，只不过在删除之前先给出提示。</span></p>
<p><span style="font-size: 16px">$ find . -name &#34;*.log&#34;&#160; -mtime +5 -ok rm {} \;<br />
	&#60; rm ... ./conf/httpd.conf &#62; ? n</span></p>
<p><span style="font-size: 16px">按y键删除文件，按n键不删除。</span></p>
<p><span style="font-size: 16px">任何形式的命令都可以在-exec选项中使用。</span></p>
<p><span style="font-size: 16px">在下面的例子中我们使用grep命令。find命令首先匹配所有文件名为<br />
	&#8220;passwd*&#8221;的文件，例如passwd、passwd.old、passwd.bak，然后执<br />
	行grep命令看看在这些文件中是否存在一个sam用户。</span></p>
<p><span style="font-size: 16px"># find /etc -name &#34;passwd*&#34; -exec grep &#34;sam&#34; {} \;<br />
	sam:x:501:501::/usr/sam:/bin/bash</span></p>
<p><br />
	<span style="font-size: 16px">二、find命令的例子；</span></p>
<p><br />
	<span style="font-size: 16px">1、查找当前用户主目录下的所有文件：</span></p>
<p><span style="font-size: 16px">下面两种方法都可以使用</span></p>
<p><span style="font-size: 16px">$ find $HOME -print<br />
	$ find ~ -print</span></p>
<p><br />
	<span style="font-size: 16px">2、让当前目录中文件属主具有读、写权限，并且文件所属组的用户<br />
	和其他用户具有读权限的文件；</span></p>
<p><span style="font-size: 16px">$ find . -type f -perm 644 -exec ls -l {&#160; } \;</span></p>
<p><br />
	<span style="font-size: 16px">3、为了查找系统中所有文件长度为0的普通文件，并列出它们的完<br />
	整路径；</span></p>
<p><span style="font-size: 16px">$ find / -type f -size 0 -exec ls -l {&#160; } \;</span></p>
<p><br />
	<span style="font-size: 16px">4、查找/var/logs目录中更改时间在7日以前的普通文件，并在删<br />
	除之前询问它们；</span></p>
<p><span style="font-size: 16px">$ find /var/logs -type f -mtime +7 -ok rm {&#160; } \;</span></p>
<p><br />
	<span style="font-size: 16px">5、为了查找系统中所有属于root组的文件；</span></p>
<p><span style="font-size: 16px">$find . -group root -exec ls -l {&#160; } \;<br />
	-rw-r--r--&#160; 1 root&#160; root&#160;&#160;&#160;&#160; 595 10月 31 01:09 ./fie1</span></p>
<p><br />
	<span style="font-size: 16px">6、find命令将删除当目录中访问时间在7日以来、含有数字后缀的<br />
	admin.log文件。</span></p>
<p><span style="font-size: 16px">该命令只检查三位数字，所以相应文件的后缀不要超过999。先建几<br />
	个admin.log*的文件 ，才能使用下面这个命令</span></p>
<p><span style="font-size: 16px">$ find . -name &#34;admin.log[0-9][0-9][0-9]&#34; -atime -7&#160; -ok<br />
	rm {&#160; } \;<br />
	&#60; rm ... ./admin.log001 &#62; ? n<br />
	&#60; rm ... ./admin.log002 &#62; ? n<br />
	&#60; rm ... ./admin.log042 &#62; ? n<br />
	&#60; rm ... ./admin.log942 &#62; ? n</span></p>
<p><br />
	<span style="font-size: 16px">7、为了查找当前文件系统中的所有目录并排序；</span></p>
<p><span style="font-size: 16px">$ find . -type d &#124; sort</span></p>
<p><br />
	<span style="font-size: 16px">8、为了查找系统中所有的rmt磁带设备；</span></p>
<p><span style="font-size: 16px">$ find /dev/rmt -print</span></p>
<p><br />
	<span style="font-size: 16px">三、xargs</span></p>
<p><span style="font-size: 16px">xargs &#8211; build and execute command lines from standard input</span></p>
<p><span style="font-size: 16px">在使用find命令的-exec选项处理匹配到的文件时， find命令将所有<br />
	匹配到的文件一起传递给exec执行。但有些系统对能够传递给exec的<br />
	命令长度有限制，这样在find命令运行几分钟之后，就会出现溢出错<br />
	误。错误信息通常是&#8220;参数列太长&#8221;或&#8220;参数列溢出&#8221;。这就是xargs<br />
	命令的用处所在，特别是与find命令一起使用。</span></p>
<p><span style="font-size: 16px">find命令把匹配到的文件传递给xargs命令，而xargs命令每次只获取<br />
	一部分文件而不是全部，不像-exec选项那样。这样它可以先处理最先<br />
	获取的一部分文件，然后是下一批，并如此继续下去。</span></p>
<p><span style="font-size: 16px">在有些系统中，使用-exec选项会为处理每一个匹配到的文件而发起一<br />
	个相应的进程，并非将匹配到的文件全部作为参数一次执行；这样在<br />
	有些情况下就会出现进程过多，系统性能下降的问题，因而效率不高；</span></p>
<p><span style="font-size: 16px">而使用xargs命令则只有一个进程。另外，在使用xargs命令时，究竟<br />
	是一次获取所有的参数，还是分批取得参数，以及每一次获取参数的<br />
	数目都会根据该命令的选项及系统内核中相应的可调参数来确定。</span></p>
<p><span style="font-size: 16px">来看看xargs命令是如何同find命令一起使用的，并给出一些例子。</span></p>
<p><span style="font-size: 16px">下面的例子查找系统中的每一个普通文件，然后使用xargs命令来测试<br />
	它们分别属于哪类文件</span></p>
<p><span style="font-size: 16px">#find . -type f -print &#124; xargs file<br />
	./.kde/Autostart/Autorun.desktop: UTF-8 Unicode English text<br />
	./.kde/Autostart/.directory:&#160;&#160;&#160;&#160;&#160; ISO-8859 text\<br />
	......</span></p>
<p><span style="font-size: 16px">在整个系统中查找内存信息转储文件(core dump) ，然后把结果保存<br />
	到/tmp/core.log 文件中：</span></p>
<p><span style="font-size: 16px">$ find / -name &#34;core&#34; -print &#124; xargs echo &#34;&#34; &#62;/tmp/core.log</span></p>
<p><span style="font-size: 16px">上面这个执行太慢，我改成在当前目录下查找</span></p>
<p><span style="font-size: 16px">#find . -name &#34;file*&#34; -print &#124; xargs echo &#34;&#34; &#62; /temp/core.log<br />
	# cat /temp/core.log<br />
	./file6</span></p>
<p><span style="font-size: 16px">在当前目录下查找所有用户具有读、写和执行权限的文件，并收回相<br />
	应的写权限：</span></p>
<p><span style="font-size: 16px"># ls -l<br />
	drwxrwxrwx&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160; 4096 10月 30 20:14 file6<br />
	-rwxrwxrwx&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:01 http3.conf<br />
	-rwxrwxrwx&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:01 httpd.conf</span></p>
<p><span style="font-size: 16px"># find . -perm -7 -print &#124; xargs chmod o-w<br />
	# ls -l<br />
	drwxrwxr-x&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160; 4096 10月 30 20:14 file6<br />
	-rwxrwxr-x&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:01 http3.conf<br />
	-rwxrwxr-x&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:01 httpd.conf</span></p>
<p><span style="font-size: 16px">用grep命令在所有的普通文件中搜索hostname这个词：</span></p>
<p><span style="font-size: 16px"># find . -type f -print &#124; xargs grep &#34;hostname&#34;<br />
	./httpd1.conf:#&#160;&#160;&#160;&#160; different IP addresses or hostnames and<br />
	have them handled by the</span></p>
<p><span style="font-size: 16px">./httpd1.conf:# VirtualHost: If you want to maintain multiple<br />
	&#160;domains/hostnames on your</span></p>
<p><span style="font-size: 16px">用grep命令在当前目录下的所有普通文件中搜索hostnames这个词：</span></p>
<p><span style="font-size: 16px"># find . -name \* -type f -print &#124; xargs grep &#34;hostnames&#34;<br />
	./httpd1.conf:#&#160;&#160;&#160;&#160; different IP addresses or hostnames and<br />
	&#160;have them handled by the<br />
	./httpd1.conf:# VirtualHost: If you want to maintain multiple<br />
	&#160;domains/hostnames on your</span></p>
<p><span style="font-size: 16px">注意，在上面的例子中， \用来取消find命令中的*在shell中的特殊<br />
	含义。</span></p>
<p><span style="font-size: 16px">find命令配合使用exec和xargs可以使用户对所匹配到的文件执行几乎<br />
	所有的命令。</span></p>
<p><br />
	<span style="font-size: 16px">四、find 命令的参数</span></p>
<p><span style="font-size: 16px">下面是find一些常用参数的例子，有用到的时候查查就行了，像上面<br />
	前几个贴子，都用到了其中的的一些参数，也可以用man或查看论坛里<br />
	其它贴子有find的命令手册</span></p>
<p><br />
	<span style="font-size: 16px">1、使用name选项</span></p>
<p><span style="font-size: 16px">文件名选项是find命令最常用的选项，要么单独使用该选项，要么和<br />
	其他选项一起使用。</span></p>
<p><span style="font-size: 16px">可以使用某种文件名模式来匹配文件，记住要用引号将文件名模式引<br />
	起来。</span></p>
<p><span style="font-size: 16px">不管当前路径是什么，如果想要在自己的根目录$HOME中查找文件名<br />
	符合*.txt的文件，使用~作为 &#8216;pathname&#8217;参数，波浪号~代表了你<br />
	的$HOME目录。</span></p>
<p><span style="font-size: 16px">$ find ~ -name &#34;*.txt&#34; -print</span></p>
<p><span style="font-size: 16px">想要在当前目录及子目录中查找所有的&#8216; *.txt&#8217;文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find . -name &#34;*.txt&#34; -print</span></p>
<p><span style="font-size: 16px">想要的当前目录及子目录中查找文件名以一个大写字母开头的文件<br />
	，可以用：</span></p>
<p><span style="font-size: 16px">$ find . -name &#34;[A-Z]*&#34; -print</span></p>
<p><span style="font-size: 16px">想要在/etc目录中查找文件名以host开头的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find /etc -name &#34;host*&#34; -print</span></p>
<p><span style="font-size: 16px">想要查找$HOME目录中的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find ~ -name &#34;*&#34; -print 或find . -print</span></p>
<p><span style="font-size: 16px">要想让系统高负荷运行，就从根目录开始查找所有的文件。</span></p>
<p><span style="font-size: 16px">$ find / -name &#34;*&#34; -print</span></p>
<p><span style="font-size: 16px">如果想在当前目录查找文件名以两个小写字母开头，跟着是两个数<br />
	字，最后是.txt的文件，下面的命令就能够返回名为ax37.txt的文<br />
	件：</span></p>
<p><span style="font-size: 16px">$find . -name &#34;[a-z][a-z][0--9][0--9].txt&#34; -print</span></p>
<p><br />
	<span style="font-size: 16px">2、用perm选项</span></p>
<p><span style="font-size: 16px">按照文件权限模式用-perm选项,按文件权限模式来查找文件的话。最<br />
	好使用八进制的权限表示法。</span></p>
<p><span style="font-size: 16px">如在当前目录下查找文件权限位为755的文件，即文件属主可以读、<br />
	写、执行，其他用户可以读、执行的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find . -perm 755 -print</span></p>
<p><span style="font-size: 16px">还有一种表达方法：在八进制数字前面要加一个横杠-，表示都匹配，<br />
	如-007就相当于777，-006相当于666</span></p>
<p><span style="font-size: 16px"># ls -l<br />
	-rwxrwxr-x&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:01 http3.conf<br />
	-rw-rw-rw-&#160; 1 sam&#160;&#160; adm&#160;&#160; 34890 10月 31 00:57 httpd1.conf<br />
	-rwxrwxr-x&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:01 httpd.conf<br />
	drw-rw-rw-&#160; 2 gem&#160;&#160; group&#160; 4096 10月 26 19:48 sam<br />
	-rw-rw-rw-&#160; 1 root&#160; root&#160;&#160; 2792 10月 31 20:19 temp</span></p>
<p><span style="font-size: 16px"># find . -perm 006<br />
	# find . -perm -006<br />
	./sam<br />
	./httpd1.conf<br />
	./temp</span></p>
<p><span style="font-size: 16px">-perm mode:文件许可正好符合mode</span></p>
<p><span style="font-size: 16px">-perm +mode:文件许可部分符合mode</span></p>
<p><span style="font-size: 16px">-perm -mode: 文件许可完全符合mode</span></p>
<p><br />
	<span style="font-size: 16px">3、忽略某个目录</span></p>
<p><span style="font-size: 16px">如果在查找文件时希望忽略某个目录，因为你知道那个目录中没有<br />
	你所要查找的文件，那么可以使用-prune选项来指出需要忽略的目<br />
	录。在使用-prune选项时要当心，因为如果你同时使用了-depth选<br />
	项，那么-prune选项就会被find命令忽略。</span></p>
<p><span style="font-size: 16px">如果希望在/apps目录下查找文件，但不希望在/apps/bin目录下查<br />
	找，可以用：</span></p>
<p><span style="font-size: 16px">$ find /apps -path &#34;/apps/bin&#34; -prune -o -print</span></p>
<p><br />
	<span style="font-size: 16px">4、使用find查找文件的时候怎么避开某个文件目录</span></p>
<p><span style="font-size: 16px">比如要在/usr/sam目录下查找不在dir1子目录之内的所有文件</span></p>
<p><span style="font-size: 16px">find /usr/sam -path &#34;/usr/sam/dir1&#34; -prune -o -print</span></p>
<p><span style="font-size: 16px">find [-path ..] [expression] 在路径列表的后面的是表达式</span></p>
<p><span style="font-size: 16px">-path &#8220;/usr/sam&#8221; -prune -o -print 是 -path &#8220;/usr/sam&#8221;<br />
	-a -prune -o</span></p>
<p><span style="font-size: 16px">-print 的简写表达式按顺序求值, -a 和 -o 都是短路求值，与<br />
	shell 的 &#38;&#38; 和 &#124;&#124; 类似如果 -path &#8220;/usr/sam&#8221; 为真，则求值<br />
	&#160;-prune , -prune 返回真，与逻辑表达式为真；否则不求值 -prune，<br />
	与逻辑表达式为假。如果 -path &#8220;/usr/sam&#8221; -a -prune 为假，<br />
	则求值 -print ，-print返回真，或逻辑表达式为真；否则不求值<br />
	&#160;-print，或逻辑表达式为真。</span></p>
<p><span style="font-size: 16px">这个表达式组合特例可以用伪码写为</span></p>
<p><span style="font-size: 16px">if -path &#34;/usr/sam&#34;&#160; then<br />
	-prune<br />
	else<br />
	-print</span></p>
<p><span style="font-size: 16px">避开多个文件夹</span></p>
<p><span style="font-size: 16px">find /usr/sam \( -path /usr/sam/dir1 -o -path /usr/sam/file1 \) -prune -o -print</span></p>
<p><span style="font-size: 16px">圆括号表示表达式的结合。</span></p>
<p><span style="font-size: 16px">\ 表示引用，即指示 shell 不对后面的字符作特殊解释，而留给 find 命令去解释其意义。</span></p>
<p><span style="font-size: 16px">查找某一确定文件，-name等选项加在-o 之后</span></p>
<p><span style="font-size: 16px">#find /usr/sam&#160; \(-path /usr/sam/dir1 -o -path /usr/sam/file1 \) -prune -o -name &#34;temp&#34; -print</span></p>
<p><br />
	<span style="font-size: 16px">5、使用user和nouser选项</span></p>
<p><span style="font-size: 16px">按文件属主查找文件，如在$HOME目录中查找文件属主为sam的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find ~ -user sam -print</span></p>
<p><span style="font-size: 16px">在/etc目录下查找文件属主为uucp的文件：</span></p>
<p><span style="font-size: 16px">$ find /etc -user uucp -print</span></p>
<p><span style="font-size: 16px">为了查找属主帐户已经被删除的文件，可以使用-nouser选项。这样<br />
	就能够找到那些属主在/etc/passwd文件中没有有效帐户的文件。在<br />
	使用-nouser选项时，不必给出用户名； find命令能够为你完成相<br />
	应的工作。</span></p>
<p><span style="font-size: 16px">例如，希望在/home目录下查找所有的这类文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find /home -nouser -print</span></p>
<p><br />
	<span style="font-size: 16px">6、使用group和nogroup选项</span></p>
<p><span style="font-size: 16px">就像user和nouser选项一样，针对文件所属于的用户组， find命令<br />
	也具有同样的选项，为了在/apps目录下查找属于gem用户组的文件，<br />
	可以用：</span></p>
<p><span style="font-size: 16px">$ find /apps -group gem -print</span></p>
<p><span style="font-size: 16px">要查找没有有效所属用户组的所有文件，可以使用nogroup选项。<br />
	下面的find命令从文件系统的根目录处查找这样的文件</span></p>
<p><span style="font-size: 16px">$ find / -nogroup-print</span></p>
<p><br />
	<span style="font-size: 16px">7、按照更改时间或访问时间等查找文件</span></p>
<p><span style="font-size: 16px">如果希望按照更改时间来查找文件，可以使用mtime,atime或ctime<br />
	选项。如果系统突然没有可用空间了，很有可能某一个文件的长度<br />
	在此期间增长迅速，这时就可以用mtime选项来查找这样的文件。</span></p>
<p><span style="font-size: 16px">用减号-来限定更改时间在距今n日以内的文件，而用加号+来限定<br />
	更改时间在距今n日以前的文件。</span></p>
<p><span style="font-size: 16px">希望在系统根目录下查找更改时间在5日以内的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find / -mtime -5 -print</span></p>
<p><span style="font-size: 16px">为了在/var/adm目录下查找更改时间在3日以前的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find /var/adm -mtime +3 -print</span></p>
<p><br />
	<span style="font-size: 16px">8、查找比某个文件新或旧的文件</span></p>
<p><span style="font-size: 16px">如果希望查找更改时间比某个文件新但比另一个文件旧的所有文<br />
	件，可以使用-newer选项。它的一般形式为：</span></p>
<p><span style="font-size: 16px">newest_file_name ! oldest_file_name</span></p>
<p><span style="font-size: 16px">其中，！是逻辑非符号。</span></p>
<p><span style="font-size: 16px">查找更改时间比文件sam新但比文件temp旧的文件：</span></p>
<p><span style="font-size: 16px">例：有两个文件</span></p>
<p><span style="font-size: 16px">-rw-r--r--&#160; 1 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:07 fiel<br />
	-rw-rw-rw-&#160; 1 sam&#160;&#160; adm&#160;&#160; 34890 10月 31 00:57 httpd1.conf<br />
	-rwxrwxr-x&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:01 httpd.conf<br />
	drw-rw-rw-&#160; 2 gem&#160;&#160; group&#160; 4096 10月 26 19:48 sam<br />
	-rw-rw-rw-&#160; 1 root&#160; root&#160;&#160; 2792 10月 31 20:19 temp</span></p>
<p><span style="font-size: 16px"># find -newer httpd1.conf&#160; ! -newer temp -ls<br />
	1077669&#160; 0 -rwxrwxr-x&#160; 2 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:01 ./httpd.conf<br />
	1077671&#160; 4 -rw-rw-rw-&#160; 1 root&#160; root&#160; 2792 10月 31 20:19 ./temp<br />
	1077673&#160; 0 -rw-r--r--&#160; 1 sam&#160;&#160; adm&#160;&#160;&#160;&#160;&#160; 0 10月 31 01:07 ./fiel</span></p>
<p><span style="font-size: 16px">查找更改时间在比temp文件新的文件：</span></p>
<p><span style="font-size: 16px">$ find . -newer temp -print</span></p>
<p><br />
	<span style="font-size: 16px">9、使用type选项</span></p>
<p><span style="font-size: 16px">在/etc目录下查找所有的目录，可以用：</span></p>
<p><span style="font-size: 16px">$ find /etc -type d -print</span></p>
<p><span style="font-size: 16px">在当前目录下查找除目录以外的所有类型的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find . ! -type d -print</span></p>
<p><span style="font-size: 16px">在/etc目录下查找所有的符号链接文件，可以用</span></p>
<p><span style="font-size: 16px">$ find /etc -type l -print</span></p>
<p><br />
	<span style="font-size: 16px">10、使用size选项</span></p>
<p><span style="font-size: 16px">可以按照文件长度来查找文件，这里所指的文件长度既可以用块<br />
	（block）来计量，也可以用字节来计量。以字节计量文件长度的<br />
	表达形式为N c；以块计量文件长度只用数字表示即可。</span></p>
<p><span style="font-size: 16px">在按照文件长度查找文件时，一般使用这种以字节表示的文件长<br />
	度，在查看文件系统的大小，因为这时使用块来计量更容易转换。<br />
	在当前目录下查找文件长度大于1 M字节的文件：</span></p>
<p><span style="font-size: 16px">$ find . -size +1000000c -print</span></p>
<p><span style="font-size: 16px">在/home/apache目录下查找文件长度恰好为100字节的文件：</span></p>
<p><span style="font-size: 16px">$ find /home/apache -size 100c -print</span></p>
<p><span style="font-size: 16px">在当前目录下查找长度超过10块的文件（一块等于512字节）：</span></p>
<p><span style="font-size: 16px">$ find . -size +10 -print</span></p>
<p><br />
	<span style="font-size: 16px">11、使用depth选项</span></p>
<p><span style="font-size: 16px">在使用find命令时，可能希望先匹配所有的文件，再在子目录中<br />
	查找。使用depth选项就可以使find命令这样做。这样做的一个原<br />
	因就是，当在使用find命令向磁带上备份文件系统时，希望首先<br />
	备份所有的文件，其次再备份子目录中的文件。</span></p>
<p><span style="font-size: 16px">在下面的例子中， find命令从文件系统的根目录开始，查找一<br />
	个名为CON.FILE的文件。</span></p>
<p><span style="font-size: 16px">它将首先匹配所有的文件然后再进入子目录中查找。</span></p>
<p><span style="font-size: 16px">$ find / -name &#34;CON.FILE&#34; -depth -print</span></p>
<p><br />
	<span style="font-size: 16px">12、使用mount选项</span></p>
<p><span style="font-size: 16px">在当前的文件系统中查找文件（不进入其他文件系统），可以<br />
	使用find命令的mount选项。</span></p>
<p><span style="font-size: 16px">从当前目录开始查找位于本文件系统中文件名以XC结尾的文件：</span></p>
<p><span style="font-size: 16px">$ find . -name &#34;*.XC&#34; -mount -print</span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px">一、find 命令格式</span></p>
<p><span style="font-size: 16px">1、find命令的一般形式为；<br />
	find pathname -options [-print -exec -ok ...]</span></p>
<p>
	<span style="font-size: 16px">2、find命令的参数；<br />
	pathname: find命令所查找的目录路径。例如用.来表示当前目录，用<br />
	/来表示系统根目录。</span></p>
<p><span style="font-size: 16px">-print： find命令将匹配的文件输出到标准输出。</span></p>
<p><span style="font-size: 16px">-exec： find命令对匹配的文件执行该参数所给出的shell命令。相应<br />
	命令的形式为&#39;command&#39; {} \;，注意{}和\；之间的空格。</span></p>
<p><span style="font-size: 16px">-ok： 和-exec的作用相同，只不过以一种更为安全的模式来执行该参<br />
	数所给出的shell命令，在执行每一个命令之前，都会给出提示，让用<br />
	户来确定是否执行。</span></p>
<p>
	<span style="font-size: 16px">3、find命令选项<br />
	-name<br />
	按照文件名查找文件。</span></p>
<p><span style="font-size: 16px">-perm<br />
	按照文件权限来查找文件。</span></p>
<p><span style="font-size: 16px">-prune<br />
	使用这一选项可以使find命令不在当前指定的目录中查找，如果同时<br />
	使用-depth选项，那么-prune将被find命令忽略。</span></p>
<p><span style="font-size: 16px">-user<br />
	按照文件属主来查找文件。</span></p>
<p><span style="font-size: 16px">-group<br />
	按照文件所属的组来查找文件。</span></p>
<p><span style="font-size: 16px">-mtime -n +n<br />
	按照文件的更改时间来查找文件， &#8211; n表示文件更改时间距现在n天以<br />
	内，+ n表示文件更改时间距现在n天以前。find命令还有-atime和-ctime<br />
	&nbsp;选项，但它们都和-m time选项。</span></p>
<p><span style="font-size: 16px">-nogroup<br />
	查找无有效所属组的文件，即该文件所属的组在/etc/groups中不存在。</span></p>
<p><span style="font-size: 16px">-nouser<br />
	查找无有效属主的文件，即该文件的属主在/etc/passwd中不存在。<br />
	-newer file1 ! file2</span></p>
<p><span style="font-size: 16px">查找更改时间比文件file1新但比文件file2旧的文件。<br />
	-type</span></p>
<p><span style="font-size: 16px">查找某一类型的文件，诸如：</span></p>
<p><span style="font-size: 16px">b &#8211; 块设备文件。<br />
	d &#8211; 目录。<br />
	c &#8211; 字符设备文件。<br />
	p &#8211; 管道文件。<br />
	l &#8211; 符号链接文件。<br />
	f &#8211; 普通文件。</span></p>
<p><span style="font-size: 16px">-size n：[c] 查找文件长度为n块的文件，带有c时表示文件长度以字<br />
	节计。</span></p>
<p><span style="font-size: 16px">-depth：在查找文件时，首先查找当前目录中的文件，然后再在其子目<br />
	录中查找。</span></p>
<p><span style="font-size: 16px">-fstype：查找位于某一类型文件系统中的文件，这些文件系统类型通<br />
	常可以在配置文件/etc/fstab中找到，该配置文件中包含了本系统中有<br />
	关文件系统的信息。</span></p>
<p><span style="font-size: 16px">-mount：在查找文件时不跨越文件系统mount点。</span></p>
<p><span style="font-size: 16px">-follow：如果find命令遇到符号链接文件，就跟踪至链接所指向的文件。</span></p>
<p><span style="font-size: 16px">-cpio：对匹配的文件使用cpio命令，将这些文件备份到磁带设备中。</span></p>
<p><span style="font-size: 16px">另外,下面三个的区别:</span></p>
<p><span style="font-size: 16px">-amin n<br />
	查找系统中最后N分钟访问的文件</span></p>
<p><span style="font-size: 16px">-atime n<br />
	查找系统中最后n*24小时访问的文件</span></p>
<p><span style="font-size: 16px">-cmin n<br />
	查找系统中最后N分钟被改变文件状态的文件</span></p>
<p><span style="font-size: 16px">-ctime n<br />
	查找系统中最后n*24小时被改变文件状态的文件</span></p>
<p><span style="font-size: 16px">-mmin n<br />
	查找系统中最后N分钟被改变文件数据的文件</span></p>
<p><span style="font-size: 16px">-mtime n<br />
	查找系统中最后n*24小时被改变文件数据的文件</span></p>
<p>
	<span style="font-size: 16px">4、使用exec或ok来执行shell命令</span></p>
<p><span style="font-size: 16px">使用find时，只要把想要的操作写在一个文件里，就可以用exec来配合<br />
	find查找，很方便的</span></p>
<p><span style="font-size: 16px">在有些操作系统中只允许-exec选项执行诸如l s或ls -l这样的命令。大<br />
	多数用户使用这一选项是为了查找旧文件并删除它们。建议在真正执行<br />
	rm命令删除文件之前，最好先用ls命令看一下，确认它们是所要删除的文件。</span></p>
<p><span style="font-size: 16px">exec选项后面跟随着所要执行的命令或脚本，然后是一对儿{ }，一个空<br />
	格和一个\，最后是一个分号。为了使用exec选项，必须要同时使用print<br />
	选项。如果验证一下find命令，会发现该命令只输出从当前路径起的相对<br />
	路径及文件名。</span></p>
<p><span style="font-size: 16px">例如：为了用ls -l命令列出所匹配到的文件，可以把ls -l命令放在find<br />
	命令的-exec选项中</span></p>
<p><span style="font-size: 16px"># find . -type f -exec ls -l {} \;<br />
	-rw-r&#8211;r&#8211;&nbsp; 1 root&nbsp; root&nbsp;&nbsp; 34928 2003-02-25&nbsp; ./conf/httpd.conf<br />
	-rw-r&#8211;r&#8211;&nbsp; 1 root&nbsp; root&nbsp;&nbsp; 12959 2003-02-25&nbsp; ./conf/magic<br />
	-rw-r&#8211;r&#8211;&nbsp; 1 root&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp; 180 2003-02-25&nbsp; ./conf.d/README</span></p>
<p><span style="font-size: 16px">上面的例子中，find命令匹配到了当前目录下的所有普通文件，并在-exec<br />
	选项中使用ls -l命令将它们列出。<br />
	在/logs目录中查找更改时间在5日以前的文件并删除它们：</span></p>
<p><span style="font-size: 16px">$ find logs -type f -mtime +5 -exec rm {} \;</span></p>
<p><span style="font-size: 16px">记住：在shell中用任何方式删除文件之前，应当先查看相应的文件，一<br />
	定要小心！当使用诸如mv或rm命令时，可以使用-exec选项的安全模式。<br />
	它将在对每个匹配到的文件进行操作之前提示你。</span></p>
<p><span style="font-size: 16px">在下面的例子中， find命令在当前目录中查找所有文件名以.LOG结尾、<br />
	更改时间在5日以上的文件，并删除它们，只不过在删除之前先给出提示。</span></p>
<p><span style="font-size: 16px">$ find . -name &quot;*.log&quot;&nbsp; -mtime +5 -ok rm {} \;<br />
	&lt; rm &#8230; ./conf/httpd.conf &gt; ? n</span></p>
<p><span style="font-size: 16px">按y键删除文件，按n键不删除。</span></p>
<p><span style="font-size: 16px">任何形式的命令都可以在-exec选项中使用。</span></p>
<p><span style="font-size: 16px">在下面的例子中我们使用grep命令。find命令首先匹配所有文件名为<br />
	&ldquo;passwd*&rdquo;的文件，例如passwd、passwd.old、passwd.bak，然后执<br />
	行grep命令看看在这些文件中是否存在一个sam用户。</span></p>
<p><span style="font-size: 16px"># find /etc -name &quot;passwd*&quot; -exec grep &quot;sam&quot; {} \;<br />
	sam:x:501:501::/usr/sam:/bin/bash</span></p>
<p>
	<span style="font-size: 16px">二、find命令的例子；</span></p>
<p>
	<span style="font-size: 16px">1、查找当前用户主目录下的所有文件：</span></p>
<p><span style="font-size: 16px">下面两种方法都可以使用</span></p>
<p><span style="font-size: 16px">$ find $HOME -print<br />
	$ find ~ -print</span></p>
<p>
	<span style="font-size: 16px">2、让当前目录中文件属主具有读、写权限，并且文件所属组的用户<br />
	和其他用户具有读权限的文件；</span></p>
<p><span style="font-size: 16px">$ find . -type f -perm 644 -exec ls -l {&nbsp; } \;</span></p>
<p>
	<span style="font-size: 16px">3、为了查找系统中所有文件长度为0的普通文件，并列出它们的完<br />
	整路径；</span></p>
<p><span style="font-size: 16px">$ find / -type f -size 0 -exec ls -l {&nbsp; } \;</span></p>
<p>
	<span style="font-size: 16px">4、查找/var/logs目录中更改时间在7日以前的普通文件，并在删<br />
	除之前询问它们；</span></p>
<p><span style="font-size: 16px">$ find /var/logs -type f -mtime +7 -ok rm {&nbsp; } \;</span></p>
<p>
	<span style="font-size: 16px">5、为了查找系统中所有属于root组的文件；</span></p>
<p><span style="font-size: 16px">$find . -group root -exec ls -l {&nbsp; } \;<br />
	-rw-r&#8211;r&#8211;&nbsp; 1 root&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp; 595 10月 31 01:09 ./fie1</span></p>
<p>
	<span style="font-size: 16px">6、find命令将删除当目录中访问时间在7日以来、含有数字后缀的<br />
	admin.log文件。</span></p>
<p><span style="font-size: 16px">该命令只检查三位数字，所以相应文件的后缀不要超过999。先建几<br />
	个admin.log*的文件 ，才能使用下面这个命令</span></p>
<p><span style="font-size: 16px">$ find . -name &quot;admin.log[0-9][0-9][0-9]&quot; -atime -7&nbsp; -ok<br />
	rm {&nbsp; } \;<br />
	&lt; rm &#8230; ./admin.log001 &gt; ? n<br />
	&lt; rm &#8230; ./admin.log002 &gt; ? n<br />
	&lt; rm &#8230; ./admin.log042 &gt; ? n<br />
	&lt; rm &#8230; ./admin.log942 &gt; ? n</span></p>
<p>
	<span style="font-size: 16px">7、为了查找当前文件系统中的所有目录并排序；</span></p>
<p><span style="font-size: 16px">$ find . -type d | sort</span></p>
<p>
	<span style="font-size: 16px">8、为了查找系统中所有的rmt磁带设备；</span></p>
<p><span style="font-size: 16px">$ find /dev/rmt -print</span></p>
<p>
	<span style="font-size: 16px">三、xargs</span></p>
<p><span style="font-size: 16px">xargs &ndash; build and execute command lines from standard input</span></p>
<p><span style="font-size: 16px">在使用find命令的-exec选项处理匹配到的文件时， find命令将所有<br />
	匹配到的文件一起传递给exec执行。但有些系统对能够传递给exec的<br />
	命令长度有限制，这样在find命令运行几分钟之后，就会出现溢出错<br />
	误。错误信息通常是&ldquo;参数列太长&rdquo;或&ldquo;参数列溢出&rdquo;。这就是xargs<br />
	命令的用处所在，特别是与find命令一起使用。</span></p>
<p><span style="font-size: 16px">find命令把匹配到的文件传递给xargs命令，而xargs命令每次只获取<br />
	一部分文件而不是全部，不像-exec选项那样。这样它可以先处理最先<br />
	获取的一部分文件，然后是下一批，并如此继续下去。</span></p>
<p><span style="font-size: 16px">在有些系统中，使用-exec选项会为处理每一个匹配到的文件而发起一<br />
	个相应的进程，并非将匹配到的文件全部作为参数一次执行；这样在<br />
	有些情况下就会出现进程过多，系统性能下降的问题，因而效率不高；</span></p>
<p><span style="font-size: 16px">而使用xargs命令则只有一个进程。另外，在使用xargs命令时，究竟<br />
	是一次获取所有的参数，还是分批取得参数，以及每一次获取参数的<br />
	数目都会根据该命令的选项及系统内核中相应的可调参数来确定。</span></p>
<p><span style="font-size: 16px">来看看xargs命令是如何同find命令一起使用的，并给出一些例子。</span></p>
<p><span style="font-size: 16px">下面的例子查找系统中的每一个普通文件，然后使用xargs命令来测试<br />
	它们分别属于哪类文件</span></p>
<p><span style="font-size: 16px">#find . -type f -print | xargs file<br />
	./.kde/Autostart/Autorun.desktop: UTF-8 Unicode English text<br />
	./.kde/Autostart/.directory:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ISO-8859 text\<br />
	&#8230;&#8230;</span></p>
<p><span style="font-size: 16px">在整个系统中查找内存信息转储文件(core dump) ，然后把结果保存<br />
	到/tmp/core.log 文件中：</span></p>
<p><span style="font-size: 16px">$ find / -name &quot;core&quot; -print | xargs echo &quot;&quot; &gt;/tmp/core.log</span></p>
<p><span style="font-size: 16px">上面这个执行太慢，我改成在当前目录下查找</span></p>
<p><span style="font-size: 16px">#find . -name &quot;file*&quot; -print | xargs echo &quot;&quot; &gt; /temp/core.log<br />
	# cat /temp/core.log<br />
	./file6</span></p>
<p><span style="font-size: 16px">在当前目录下查找所有用户具有读、写和执行权限的文件，并收回相<br />
	应的写权限：</span></p>
<p><span style="font-size: 16px"># ls -l<br />
	drwxrwxrwx&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp; 4096 10月 30 20:14 file6<br />
	-rwxrwxrwx&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:01 http3.conf<br />
	-rwxrwxrwx&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:01 httpd.conf</span></p>
<p><span style="font-size: 16px"># find . -perm -7 -print | xargs chmod o-w<br />
	# ls -l<br />
	drwxrwxr-x&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp; 4096 10月 30 20:14 file6<br />
	-rwxrwxr-x&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:01 http3.conf<br />
	-rwxrwxr-x&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:01 httpd.conf</span></p>
<p><span style="font-size: 16px">用grep命令在所有的普通文件中搜索hostname这个词：</span></p>
<p><span style="font-size: 16px"># find . -type f -print | xargs grep &quot;hostname&quot;<br />
	./httpd1.conf:#&nbsp;&nbsp;&nbsp;&nbsp; different IP addresses or hostnames and<br />
	have them handled by the</span></p>
<p><span style="font-size: 16px">./httpd1.conf:# VirtualHost: If you want to maintain multiple<br />
	&nbsp;domains/hostnames on your</span></p>
<p><span style="font-size: 16px">用grep命令在当前目录下的所有普通文件中搜索hostnames这个词：</span></p>
<p><span style="font-size: 16px"># find . -name \* -type f -print | xargs grep &quot;hostnames&quot;<br />
	./httpd1.conf:#&nbsp;&nbsp;&nbsp;&nbsp; different IP addresses or hostnames and<br />
	&nbsp;have them handled by the<br />
	./httpd1.conf:# VirtualHost: If you want to maintain multiple<br />
	&nbsp;domains/hostnames on your</span></p>
<p><span style="font-size: 16px">注意，在上面的例子中， \用来取消find命令中的*在shell中的特殊<br />
	含义。</span></p>
<p><span style="font-size: 16px">find命令配合使用exec和xargs可以使用户对所匹配到的文件执行几乎<br />
	所有的命令。</span></p>
<p>
	<span style="font-size: 16px">四、find 命令的参数</span></p>
<p><span style="font-size: 16px">下面是find一些常用参数的例子，有用到的时候查查就行了，像上面<br />
	前几个贴子，都用到了其中的的一些参数，也可以用man或查看论坛里<br />
	其它贴子有find的命令手册</span></p>
<p>
	<span style="font-size: 16px">1、使用name选项</span></p>
<p><span style="font-size: 16px">文件名选项是find命令最常用的选项，要么单独使用该选项，要么和<br />
	其他选项一起使用。</span></p>
<p><span style="font-size: 16px">可以使用某种文件名模式来匹配文件，记住要用引号将文件名模式引<br />
	起来。</span></p>
<p><span style="font-size: 16px">不管当前路径是什么，如果想要在自己的根目录$HOME中查找文件名<br />
	符合*.txt的文件，使用~作为 &lsquo;pathname&rsquo;参数，波浪号~代表了你<br />
	的$HOME目录。</span></p>
<p><span style="font-size: 16px">$ find ~ -name &quot;*.txt&quot; -print</span></p>
<p><span style="font-size: 16px">想要在当前目录及子目录中查找所有的&lsquo; *.txt&rsquo;文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find . -name &quot;*.txt&quot; -print</span></p>
<p><span style="font-size: 16px">想要的当前目录及子目录中查找文件名以一个大写字母开头的文件<br />
	，可以用：</span></p>
<p><span style="font-size: 16px">$ find . -name &quot;[A-Z]*&quot; -print</span></p>
<p><span style="font-size: 16px">想要在/etc目录中查找文件名以host开头的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find /etc -name &quot;host*&quot; -print</span></p>
<p><span style="font-size: 16px">想要查找$HOME目录中的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find ~ -name &quot;*&quot; -print 或find . -print</span></p>
<p><span style="font-size: 16px">要想让系统高负荷运行，就从根目录开始查找所有的文件。</span></p>
<p><span style="font-size: 16px">$ find / -name &quot;*&quot; -print</span></p>
<p><span style="font-size: 16px">如果想在当前目录查找文件名以两个小写字母开头，跟着是两个数<br />
	字，最后是.txt的文件，下面的命令就能够返回名为ax37.txt的文<br />
	件：</span></p>
<p><span style="font-size: 16px">$find . -name &quot;[a-z][a-z][0--9][0--9].txt&quot; -print</span></p>
<p>
	<span style="font-size: 16px">2、用perm选项</span></p>
<p><span style="font-size: 16px">按照文件权限模式用-perm选项,按文件权限模式来查找文件的话。最<br />
	好使用八进制的权限表示法。</span></p>
<p><span style="font-size: 16px">如在当前目录下查找文件权限位为755的文件，即文件属主可以读、<br />
	写、执行，其他用户可以读、执行的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find . -perm 755 -print</span></p>
<p><span style="font-size: 16px">还有一种表达方法：在八进制数字前面要加一个横杠-，表示都匹配，<br />
	如-007就相当于777，-006相当于666</span></p>
<p><span style="font-size: 16px"># ls -l<br />
	-rwxrwxr-x&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:01 http3.conf<br />
	-rw-rw-rw-&nbsp; 1 sam&nbsp;&nbsp; adm&nbsp;&nbsp; 34890 10月 31 00:57 httpd1.conf<br />
	-rwxrwxr-x&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:01 httpd.conf<br />
	drw-rw-rw-&nbsp; 2 gem&nbsp;&nbsp; group&nbsp; 4096 10月 26 19:48 sam<br />
	-rw-rw-rw-&nbsp; 1 root&nbsp; root&nbsp;&nbsp; 2792 10月 31 20:19 temp</span></p>
<p><span style="font-size: 16px"># find . -perm 006<br />
	# find . -perm -006<br />
	./sam<br />
	./httpd1.conf<br />
	./temp</span></p>
<p><span style="font-size: 16px">-perm mode:文件许可正好符合mode</span></p>
<p><span style="font-size: 16px">-perm +mode:文件许可部分符合mode</span></p>
<p><span style="font-size: 16px">-perm -mode: 文件许可完全符合mode</span></p>
<p>
	<span style="font-size: 16px">3、忽略某个目录</span></p>
<p><span style="font-size: 16px">如果在查找文件时希望忽略某个目录，因为你知道那个目录中没有<br />
	你所要查找的文件，那么可以使用-prune选项来指出需要忽略的目<br />
	录。在使用-prune选项时要当心，因为如果你同时使用了-depth选<br />
	项，那么-prune选项就会被find命令忽略。</span></p>
<p><span style="font-size: 16px">如果希望在/apps目录下查找文件，但不希望在/apps/bin目录下查<br />
	找，可以用：</span></p>
<p><span style="font-size: 16px">$ find /apps -path &quot;/apps/bin&quot; -prune -o -print</span></p>
<p>
	<span style="font-size: 16px">4、使用find查找文件的时候怎么避开某个文件目录</span></p>
<p><span style="font-size: 16px">比如要在/usr/sam目录下查找不在dir1子目录之内的所有文件</span></p>
<p><span style="font-size: 16px">find /usr/sam -path &quot;/usr/sam/dir1&quot; -prune -o -print</span></p>
<p><span style="font-size: 16px">find [-path ..] [expression] 在路径列表的后面的是表达式</span></p>
<p><span style="font-size: 16px">-path &ldquo;/usr/sam&rdquo; -prune -o -print 是 -path &ldquo;/usr/sam&rdquo;<br />
	-a -prune -o</span></p>
<p><span style="font-size: 16px">-print 的简写表达式按顺序求值, -a 和 -o 都是短路求值，与<br />
	shell 的 &amp;&amp; 和 || 类似如果 -path &ldquo;/usr/sam&rdquo; 为真，则求值<br />
	&nbsp;-prune , -prune 返回真，与逻辑表达式为真；否则不求值 -prune，<br />
	与逻辑表达式为假。如果 -path &ldquo;/usr/sam&rdquo; -a -prune 为假，<br />
	则求值 -print ，-print返回真，或逻辑表达式为真；否则不求值<br />
	&nbsp;-print，或逻辑表达式为真。</span></p>
<p><span style="font-size: 16px">这个表达式组合特例可以用伪码写为</span></p>
<p><span style="font-size: 16px">if -path &quot;/usr/sam&quot;&nbsp; then<br />
	-prune<br />
	else<br />
	-print</span></p>
<p><span style="font-size: 16px">避开多个文件夹</span></p>
<p><span style="font-size: 16px">find /usr/sam \( -path /usr/sam/dir1 -o -path /usr/sam/file1 \) -prune -o -print</span></p>
<p><span style="font-size: 16px">圆括号表示表达式的结合。</span></p>
<p><span style="font-size: 16px">\ 表示引用，即指示 shell 不对后面的字符作特殊解释，而留给 find 命令去解释其意义。</span></p>
<p><span style="font-size: 16px">查找某一确定文件，-name等选项加在-o 之后</span></p>
<p><span style="font-size: 16px">#find /usr/sam&nbsp; \(-path /usr/sam/dir1 -o -path /usr/sam/file1 \) -prune -o -name &quot;temp&quot; -print</span></p>
<p>
	<span style="font-size: 16px">5、使用user和nouser选项</span></p>
<p><span style="font-size: 16px">按文件属主查找文件，如在$HOME目录中查找文件属主为sam的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find ~ -user sam -print</span></p>
<p><span style="font-size: 16px">在/etc目录下查找文件属主为uucp的文件：</span></p>
<p><span style="font-size: 16px">$ find /etc -user uucp -print</span></p>
<p><span style="font-size: 16px">为了查找属主帐户已经被删除的文件，可以使用-nouser选项。这样<br />
	就能够找到那些属主在/etc/passwd文件中没有有效帐户的文件。在<br />
	使用-nouser选项时，不必给出用户名； find命令能够为你完成相<br />
	应的工作。</span></p>
<p><span style="font-size: 16px">例如，希望在/home目录下查找所有的这类文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find /home -nouser -print</span></p>
<p>
	<span style="font-size: 16px">6、使用group和nogroup选项</span></p>
<p><span style="font-size: 16px">就像user和nouser选项一样，针对文件所属于的用户组， find命令<br />
	也具有同样的选项，为了在/apps目录下查找属于gem用户组的文件，<br />
	可以用：</span></p>
<p><span style="font-size: 16px">$ find /apps -group gem -print</span></p>
<p><span style="font-size: 16px">要查找没有有效所属用户组的所有文件，可以使用nogroup选项。<br />
	下面的find命令从文件系统的根目录处查找这样的文件</span></p>
<p><span style="font-size: 16px">$ find / -nogroup-print</span></p>
<p>
	<span style="font-size: 16px">7、按照更改时间或访问时间等查找文件</span></p>
<p><span style="font-size: 16px">如果希望按照更改时间来查找文件，可以使用mtime,atime或ctime<br />
	选项。如果系统突然没有可用空间了，很有可能某一个文件的长度<br />
	在此期间增长迅速，这时就可以用mtime选项来查找这样的文件。</span></p>
<p><span style="font-size: 16px">用减号-来限定更改时间在距今n日以内的文件，而用加号+来限定<br />
	更改时间在距今n日以前的文件。</span></p>
<p><span style="font-size: 16px">希望在系统根目录下查找更改时间在5日以内的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find / -mtime -5 -print</span></p>
<p><span style="font-size: 16px">为了在/var/adm目录下查找更改时间在3日以前的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find /var/adm -mtime +3 -print</span></p>
<p>
	<span style="font-size: 16px">8、查找比某个文件新或旧的文件</span></p>
<p><span style="font-size: 16px">如果希望查找更改时间比某个文件新但比另一个文件旧的所有文<br />
	件，可以使用-newer选项。它的一般形式为：</span></p>
<p><span style="font-size: 16px">newest_file_name ! oldest_file_name</span></p>
<p><span style="font-size: 16px">其中，！是逻辑非符号。</span></p>
<p><span style="font-size: 16px">查找更改时间比文件sam新但比文件temp旧的文件：</span></p>
<p><span style="font-size: 16px">例：有两个文件</span></p>
<p><span style="font-size: 16px">-rw-r&#8211;r&#8211;&nbsp; 1 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:07 fiel<br />
	-rw-rw-rw-&nbsp; 1 sam&nbsp;&nbsp; adm&nbsp;&nbsp; 34890 10月 31 00:57 httpd1.conf<br />
	-rwxrwxr-x&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:01 httpd.conf<br />
	drw-rw-rw-&nbsp; 2 gem&nbsp;&nbsp; group&nbsp; 4096 10月 26 19:48 sam<br />
	-rw-rw-rw-&nbsp; 1 root&nbsp; root&nbsp;&nbsp; 2792 10月 31 20:19 temp</span></p>
<p><span style="font-size: 16px"># find -newer httpd1.conf&nbsp; ! -newer temp -ls<br />
	1077669&nbsp; 0 -rwxrwxr-x&nbsp; 2 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:01 ./httpd.conf<br />
	1077671&nbsp; 4 -rw-rw-rw-&nbsp; 1 root&nbsp; root&nbsp; 2792 10月 31 20:19 ./temp<br />
	1077673&nbsp; 0 -rw-r&#8211;r&#8211;&nbsp; 1 sam&nbsp;&nbsp; adm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 10月 31 01:07 ./fiel</span></p>
<p><span style="font-size: 16px">查找更改时间在比temp文件新的文件：</span></p>
<p><span style="font-size: 16px">$ find . -newer temp -print</span></p>
<p>
	<span style="font-size: 16px">9、使用type选项</span></p>
<p><span style="font-size: 16px">在/etc目录下查找所有的目录，可以用：</span></p>
<p><span style="font-size: 16px">$ find /etc -type d -print</span></p>
<p><span style="font-size: 16px">在当前目录下查找除目录以外的所有类型的文件，可以用：</span></p>
<p><span style="font-size: 16px">$ find . ! -type d -print</span></p>
<p><span style="font-size: 16px">在/etc目录下查找所有的符号链接文件，可以用</span></p>
<p><span style="font-size: 16px">$ find /etc -type l -print</span></p>
<p>
	<span style="font-size: 16px">10、使用size选项</span></p>
<p><span style="font-size: 16px">可以按照文件长度来查找文件，这里所指的文件长度既可以用块<br />
	（block）来计量，也可以用字节来计量。以字节计量文件长度的<br />
	表达形式为N c；以块计量文件长度只用数字表示即可。</span></p>
<p><span style="font-size: 16px">在按照文件长度查找文件时，一般使用这种以字节表示的文件长<br />
	度，在查看文件系统的大小，因为这时使用块来计量更容易转换。<br />
	在当前目录下查找文件长度大于1 M字节的文件：</span></p>
<p><span style="font-size: 16px">$ find . -size +1000000c -print</span></p>
<p><span style="font-size: 16px">在/home/apache目录下查找文件长度恰好为100字节的文件：</span></p>
<p><span style="font-size: 16px">$ find /home/apache -size 100c -print</span></p>
<p><span style="font-size: 16px">在当前目录下查找长度超过10块的文件（一块等于512字节）：</span></p>
<p><span style="font-size: 16px">$ find . -size +10 -print</span></p>
<p>
	<span style="font-size: 16px">11、使用depth选项</span></p>
<p><span style="font-size: 16px">在使用find命令时，可能希望先匹配所有的文件，再在子目录中<br />
	查找。使用depth选项就可以使find命令这样做。这样做的一个原<br />
	因就是，当在使用find命令向磁带上备份文件系统时，希望首先<br />
	备份所有的文件，其次再备份子目录中的文件。</span></p>
<p><span style="font-size: 16px">在下面的例子中， find命令从文件系统的根目录开始，查找一<br />
	个名为CON.FILE的文件。</span></p>
<p><span style="font-size: 16px">它将首先匹配所有的文件然后再进入子目录中查找。</span></p>
<p><span style="font-size: 16px">$ find / -name &quot;CON.FILE&quot; -depth -print</span></p>
<p>
	<span style="font-size: 16px">12、使用mount选项</span></p>
<p><span style="font-size: 16px">在当前的文件系统中查找文件（不进入其他文件系统），可以<br />
	使用find命令的mount选项。</span></p>
<p><span style="font-size: 16px">从当前目录开始查找位于本文件系统中文件名以XC结尾的文件：</span></p>
<p><span style="font-size: 16px">$ find . -name &quot;*.XC&quot; -mount -print</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/linux%e6%96%87%e4%bb%b6%e6%9f%a5%e6%89%be%e5%91%bd%e4%bb%a4find%e5%92%8cxargs%e8%af%a6%e8%a7%a3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Memcached深度分析(转载)</title>
		<link>http://www.411c.com/memcached%e6%b7%b1%e5%ba%a6%e5%88%86%e6%9e%90%e8%bd%ac%e8%bd%bd/</link>
		<comments>http://www.411c.com/memcached%e6%b7%b1%e5%ba%a6%e5%88%86%e6%9e%90%e8%bd%ac%e8%bd%bd/#comments</comments>
		<pubDate>Sun, 13 May 2012 05:26:14 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[LINUX技术]]></category>
		<category><![CDATA[memcached]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=454</guid>
		<description><![CDATA[<p><span style="font-size: 16px">Memcached是danga.com（运营LiveJournal的技术团队）开发的一套分布式内存对象缓存系统，用于在动态系统中减少数据库负载，提升性能。关于这个东西，相信很多人都用过，本文意在通过对memcached的实现及代码分析，获得对这个出色的开源软件更深入的了解，并可以根据我们的需要对其进行更进一步的优化。末了将通过对BSM_Memcache扩展的分析，加深对memcached的使用方式理解。</span></p>
<p><span style="font-size: 16px">本文的部分内容可能需要比较好的数学基础作为辅助。</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached是什么</strong></span></p>
<p><span style="font-size: 16px">在阐述这个问题之前，我们首先要清楚它&#8220;不是什么&#8221;。很多人把它当作和SharedMemory那种形式的存储载体来使用，虽然memcached 使用了同样的&#8220;Key=&#62;Value&#8221;方式组织数据，但是它和共享内存、APC等本地缓存有非常大的区别。Memcached是分布式的，也就是说它不是本地的。它基于网络连接（当然它也可以使用localhost）方式完成服务，本身它是一个独立于应用的程序或守护进程（Daemon方式）。</span></p>
<p><span style="font-size: 16px">Memcached使用libevent库实现网络连接服务，理论上可以处理无限多的连接，但是它和Apache不同，它更多的时候是面向稳定的持续连接的，所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200，这和Linux线程能力有关系，这个数值是可以调整的。关于libevent可以参考相关文档。 Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的，memcachd有自己的内存分配算法和管理方式，它和共享内存没有关系，也没有共享内存的限制，通常情况下，每个memcached进程可以管理2GB的内存空间，如果需要更多的空间，可以增加进程数。&#160;</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached适合什么场合</strong></span></p>
<p><span style="font-size: 16px">在很多时候，memcached都被滥用了，这当然少不了对它的抱怨。我经常在论坛上看见有人发贴，类似于&#8220;如何提高效率&#8221;，回复是&#8220;用memcached&#8221;，至于怎么用，用在哪里，用来干什么一句没有。memcached不是万能的，它也不是适用在所有场合。</span></p>
<p><span style="font-size: 16px">Memcached是&#8220;分布式&#8221;的内存对象缓存系统，那么就是说，那些不需要&#8220;分布&#8221;的，不需要共享的，或者干脆规模小到只有一台服务器的应用，memcached不会带来任何好处，相反还会拖慢系统效率，因为网络连接同样需要资源，即使是UNIX本地连接也一样。 在我之前的测试数据中显示，memcached本地读写速度要比直接PHP内存数组慢几十倍，而APC、共享内存方式都和直接数组差不多。可见，如果只是本地级缓存，使用memcached是非常不划算的。</span></p>
<p><span style="font-size: 16px">Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销，而且它是使用内存来管理数据的，所以它可以提供比直接读取数据库更好的性能，在大型系统中，访问同样的数据是很频繁的，memcached可以大大降低数据库压力，使系统执行效率提升。另外，memcached也经常作为服务器之间数据共享的存储媒介，例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached 中，被多个应用共享。</span></p>
<p><span style="font-size: 16px">需要注意的是，memcached使用内存管理数据，所以它是易失的，当服务器重启，或者memcached进程中止，数据便会丢失，所以 memcached不能用来持久保存数据。很多人的错误理解，memcached的性能非常好，好到了内存和硬盘的对比程度，其实memcached使用内存并不会得到成百上千的读写速度提高，它的实际瓶颈在于网络连接，它和使用磁盘的数据库系统相比，好处在于它本身非常&#8220;轻&#8221;，因为没有过多的开销和直接的读写方式，它可以轻松应付非常大的数据交换量，所以经常会出现两条千兆网络带宽都满负荷了，memcached进程本身并不占用多少CPU资源的情况。</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的工作方式</strong></span></p>
<p><span style="font-size: 16px">以下的部分中，读者最好能准备一份memcached的源代码。</span></p>
<p><span style="font-size: 16px">Memcached是传统的网络服务程序，如果启动的时候使用了-d参数，它会以守护进程的方式执行。创建守护进程由daemon.c完成，这个程序只有一个daemon函数，这个函数很简单（如无特殊说明，代码以1.2.1为准）：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">#include &#60;fcntl.h&#62;<br />
	#include &#60;stdlib.h&#62;<br />
	#include &#60;unistd.h&#62;</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">int<br />
		daemon(nochdir, noclose)<br />
		&#160; &#160; int nochdir, noclose;<br />
		{<br />
		&#160; &#160; int fd;&#160;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; switch (fork()) {<br />
		&#160; &#160; case -1:<br />
		&#160; &#160;&#160; &#160;&#160;&#160;return (-1);<br />
		&#160; &#160; case 0:&#160;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;break;&#160;&#160;<br />
		&#160; &#160; default:<br />
		&#160; &#160;&#160; &#160;&#160;&#160;_exit(0);<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; if (setsid() == -1)<br />
		&#160; &#160;&#160; &#160;&#160;&#160;return (-1);</span></p>
	<p><span style="font-size: 16px">&#160; &#160; if (!nochdir)<br />
		&#160; &#160;&#160; &#160;&#160;&#160;(void)chdir(&#8221;/&#8221;);</span></p>
	<p><span style="font-size: 16px">&#160; &#160; if (!noclose &#38;&#38; (fd = open(&#8221;/dev/null&#8221;, O_RDWR, 0)) != -1) {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;(void)dup2(fd, STDIN_FILENO);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;(void)dup2(fd, STDOUT_FILENO);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;(void)dup2(fd, STDERR_FILENO);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (fd &#62; STDERR_FILENO)<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;(void)close(fd);<br />
		&#160; &#160; }<br />
		&#160; &#160; return (0);<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">这个函数 fork 了整个进程之后，父进程就退出，接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备， daemon 就建立成功了。&#160;</span></p>
<p><span style="font-size: 16px">Memcached 本身的启动过程，在 memcached.c 的 main 函数中顺序如下：&#160;</span></p>
<p><span style="font-size: 16px">1 、调用 settings_init() 设定初始化参数<br />
	2 、从启动命令中读取参数来设置 setting 值<br />
	3 、设定 LIMIT 参数<br />
	4 、开始网络 socket 监听（如果非 socketpath 存在）（ 1.2 之后支持 UDP 方式）<br />
	5 、检查用户身份（ Memcached 不允许 root 身份启动）<br />
	6 、如果有 socketpath 存在，开启 UNIX 本地连接（Sock 管道）<br />
	7 、如果以 -d 方式启动，创建守护进程（如上调用 daemon 函数）<br />
	8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab<br />
	9 、如设置中 managed 生效，创建 bucket 数组<br />
	10 、检查是否需要锁定内存页<br />
	11 、初始化信号、连接、删除队列<br />
	12 、如果 daemon 方式，处理进程 ID<br />
	13 、event 开始，启动过程结束， main 函数进入循环。&#160;</span></p>
<p><span style="font-size: 16px">在 daemon 方式中，因为 stderr 已经被定向到黑洞，所以不会反馈执行中的可见错误信息。&#160;</span></p>
<p><span style="font-size: 16px">memcached.c 的主循环函数是 drive_machine ，传入参数是指向当前的连接的结构指针，根据 state 成员的状态来决定动作。&#160;</span></p>
<p><span style="font-size: 16px">Memcached 使用一套自定义的协议完成数据交换，它的 protocol 文档可以参考： http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt</span></p>
<p><span style="font-size: 16px">在API中，换行符号统一为\r\n</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的内存管理方式</strong></span></p>
<p><span style="font-size: 16px">Memcached有一个很有特色的内存管理方式，为了提高效率，它使用预申请和分组的方式管理内存空间，而并不是每次需要写入数据的时候去malloc，删除数据的时候free一个指针。Memcached使用slab-&#62;chunk的组织方式管理内存。</span></p>
<p><span style="font-size: 16px">1.1和1.2的slabs.c中的slab空间划分算法有一些不同，后面会分别介绍。</span></p>
<p><span style="font-size: 16px">Slab可以理解为一个内存块，一个slab是memcached一次申请内存的最小单位，在memcached中，一个slab的大小默认为 1048576字节（1MB），所以memcached都是整MB的使用内存。每一个slab被划分为若干个chunk，每个chunk里保存一个 item，每个item同时包含了item结构体、key和value（注意在memcached中的value是只有字符串的）。slab按照自己的 id分别组成链表，这些链表又按id挂在一个slabclass数组上，整个结构看起来有点像二维数组。slabclass的长度在1.1中是21，在 1.2中是200。</span></p>
<p><span style="font-size: 16px">slab有一个初始chunk大小，1.1中是1字节，1.2中是80字节，1.2中有一个factor值，默认为1.25</span></p>
<p><span style="font-size: 16px">在1.1中，chunk大小表示为初始大小*2^n，n为classid，即：id为0的slab，每chunk大小1字节，id为1的slab，每chunk大小2字节，id为2的slab，每chunk大小4字节&#8230;&#8230;id为20的slab，每chunk大小为1MB，就是说id为20的slab 里只有一个chunk：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">void slabs_init(size_t limit) {<br />
	&#160; &#160; int i;<br />
	&#160; &#160; int size=1;</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">&#160; &#160; mem_limit = limit;<br />
		&#160; &#160; for(i=0; i&#60;=POWER_LARGEST; i++, size*=2) {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].size = size;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].perslab = POWER_BLOCK / size;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].slots = 0;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].end_page_ptr = 0;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].end_page_free = 0;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].slab_list = 0;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].list_size = 0;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].killing = 0;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; /* for the test suite:&#160;&#160;faking of how much we&#8217;ve already malloc&#8217;d */<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;char *t_initial_malloc = getenv(&#8221;T_MEMD_INITIAL_MALLOC&#8221;);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (t_initial_malloc) {<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;mem_malloced = atol(getenv(&#8221;T_MEMD_INITIAL_MALLOC&#8221;));<br />
		&#160; &#160;&#160; &#160;&#160;&#160;}<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; /* pre-allocate slabs by default, unless the environment variable<br />
		&#160; &#160;&#160; &#160; for testing is set to something non-zero */<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;char *pre_alloc = getenv(&#8221;T_MEMD_SLABS_ALLOC&#8221;);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (!pre_alloc &#124;&#124; atoi(pre_alloc)) {<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;slabs_preallocate(limit / POWER_BLOCK);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;}<br />
		&#160; &#160; }<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">在1.2中，chunk大小表示为初始大小*f^n，f为factor，在memcached.c中定义，n为classid，同时，201个头不是全部都要初始化的，因为factor可变，初始化只循环到计算出的大小达到slab大小的一半为止，而且它是从id1开始的，即：id为1的slab，每chunk大小80字节，id为2的slab，每chunk大小80*f，id为3的slab，每chunk大小80*f^2，初始化大小有一个修正值 CHUNK_ALIGN_BYTES，用来保证n-byte排列 （保证结果是CHUNK_ALIGN_BYTES的整倍数）。这样，在标准情况下，memcached1.2会初始化到id40，这个slab中每个 chunk大小为504692，每个slab中有两个chunk。最后，slab_init函数会在最后补足一个id41，它是整块的，也就是这个 slab中只有一个1MB大的chunk：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">void slabs_init(size_t limit, double factor) {<br />
	&#160; &#160; int i = POWER_SMALLEST &#8211; 1;<br />
	&#160; &#160; unsigned int size = sizeof(item) + settings.chunk_size;</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">&#160; &#160; /* Factor of 2.0 means use the default memcached behavior */<br />
		&#160; &#160; if (factor == 2.0 &#38;&#38; size &#60; 128)<br />
		&#160; &#160;&#160; &#160;&#160;&#160;size = 128;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; mem_limit = limit;<br />
		&#160; &#160; memset(slabclass, 0, sizeof(slabclass));</span></p>
	<p><span style="font-size: 16px">&#160; &#160; while (++i &#60; POWER_LARGEST &#38;&#38; size &#60;= POWER_BLOCK / 2) {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;/* Make sure items are always n-byte aligned */<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (size % CHUNK_ALIGN_BYTES)<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;size += CHUNK_ALIGN_BYTES &#8211; (size % CHUNK_ALIGN_BYTES);</span></p>
	<p><span style="font-size: 16px">&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].size = size;&#160;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;slabclass[i].perslab = POWER_BLOCK / slabclass[i].size;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;size *= factor;&#160;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (settings.verbose &#62; 1) {<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;fprintf(stderr, &#8220;slab class %3d: chunk size %6d perslab %5d\n&#8221;,<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;i, slabclass[i].size, slabclass[i].perslab);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;}&#160; &#160;&#160; &#160;&#160;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; power_largest = i;<br />
		&#160; &#160; slabclass[power_largest].size = POWER_BLOCK;<br />
		&#160; &#160; slabclass[power_largest].perslab = 1;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; /* for the test suite:&#160;&#160;faking of how much we&#8217;ve already malloc&#8217;d */<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;char *t_initial_malloc = getenv(&#8221;T_MEMD_INITIAL_MALLOC&#8221;);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (t_initial_malloc) {<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;mem_malloced = atol(getenv(&#8221;T_MEMD_INITIAL_MALLOC&#8221;));<br />
		&#160; &#160;&#160; &#160;&#160;&#160;}&#160; &#160;&#160; &#160;&#160;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">#ifndef DONT_PREALLOC_SLABS<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;char *pre_alloc = getenv(&#8221;T_MEMD_SLABS_ALLOC&#8221;);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (!pre_alloc &#124;&#124; atoi(pre_alloc)) {<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;slabs_preallocate(limit / POWER_BLOCK);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;}<br />
		&#160; &#160; }<br />
		#endif<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">由上可以看出，memcached的内存分配是有冗余的，当一个slab不能被它所拥有的chunk大小整除时，slab尾部剩余的空间就被丢弃了，如id40中，两个chunk占用了1009384字节，这个slab一共有1MB，那么就有39192字节被浪费了。</span></p>
<p><span style="font-size: 16px">Memcached使用这种方式来分配内存，是为了可以快速的通过item长度定位出slab的classid，有一点类似hash，因为item 的长度是可以计算的，比如一个item的长度是300字节，在1.2中就可以得到它应该保存在id7的slab中，因为按照上面的计算方法，id6的 chunk大小是252字节，id7的chunk大小是316字节，id8的chunk大小是396字节，表示所有252到316字节的item都应该保存在id7中。同理，在1.1中，也可以计算得到它出于256和512之间，应该放在chunk_size为512的id9中(32位系统）。</span></p>
<p><span style="font-size: 16px">Memcached初始化的时候，会初始化slab（前面可以看到，在main函数中调用了slabs_init()）。它会在 slabs_init()中检查一个常量DONT_PREALLOC_SLABS，如果这个没有被定义，说明使用预分配内存方式初始化slab，这样在所有已经定义过的slabclass中，每一个id创建一个slab。这样就表示，1.2在默认的环境中启动进程后要分配41MB的slab空间，在这个过程里，memcached的第二个内存冗余发生了，因为有可能一个id根本没有被使用过，但是它也默认申请了一个slab，每个slab会用掉1MB内存</span></p>
<p><span style="font-size: 16px">当一个slab用光后，又有新的item要插入这个id，那么它就会重新申请新的slab，申请新的slab时，对应id的slab链表就要增长，这个链表是成倍增长的，在函数grow_slab_list函数中，这个链的长度从1变成2，从2变成4，从4变成8&#8230;&#8230;：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">static int grow_slab_list (unsigned int id) {<br />
	&#160; &#160; slabclass_t *p = &#38;slabclass[id];<br />
	&#160; &#160; if (p-&#62;slabs == p-&#62;list_size) {<br />
	&#160; &#160;&#160; &#160;&#160;&#160;size_t new_size =&#160;&#160;p-&#62;list_size ? p-&#62;list_size * 2 : 16;&#160;<br />
	&#160; &#160;&#160; &#160;&#160;&#160;void *new_list = realloc(p-&#62;slab_list, new_size*sizeof(void*));<br />
	&#160; &#160;&#160; &#160;&#160;&#160;if (new_list == 0) return 0;<br />
	&#160; &#160;&#160; &#160;&#160;&#160;p-&#62;list_size = new_size;<br />
	&#160; &#160;&#160; &#160;&#160;&#160;p-&#62;slab_list = new_list;<br />
	&#160; &#160; }<br />
	&#160; &#160; return 1;<br />
	}</span></div>
<p><span style="font-size: 16px">在定位item时，都是使用slabs_clsid函数，传入参数为item大小，返回值为classid，由这个过程可以看出，memcached的第三个内存冗余发生在保存item的过程中，item总是小于或等于chunk大小的，当item小于chunk大小时，就又发生了空间浪费。</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的NewHash算法</strong></span></p>
<p><span style="font-size: 16px">Memcached的item保存基于一个大的hash表，它的实际地址就是slab中的chunk偏移，但是它的定位是依靠对key做hash的结果，在primary_hashtable中找到的。在assoc.c和items.c中定义了所有的hash和item操作。</span></p>
<p><span style="font-size: 16px">Memcached使用了一个叫做NewHash的算法，它的效果很好，效率也很高。1.1和1.2的NewHash有一些不同，主要的实现方式还是一样的，1.2的hash函数是经过整理优化的，适应性更好一些。</span></p>
<p><span style="font-size: 16px">NewHash的原型参考：http://burtleburtle.net/bob/hash/evahash.html。数学家总是有点奇怪，呵呵～</span></p>
<p><span style="font-size: 16px">为了变换方便，定义了u4和u1两种数据类型，u4就是无符号的长整形，u1就是无符号char(0-255)。</span></p>
<p><span style="font-size: 16px">具体代码可以参考1.1和1.2源码包。</span></p>
<p><span style="font-size: 16px">注意这里的hashtable长度，1.1和1.2也是有区别的，1.1中定义了HASHPOWER常量为20，hashtable表长为 hashsize(HASHPOWER)，就是4MB（hashsize是一个宏，表示1右移n位），1.2中是变量16，即hashtable表长 65536：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">typedef&#160;&#160;unsigned long&#160;&#160;int&#160;&#160;ub4;&#160; &#160;/* unsigned 4-byte quantities */<br />
	typedef&#160;&#160;unsigned&#160; &#160;&#160; &#160; char ub1;&#160; &#160;/* unsigned 1-byte quantities */</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">#define hashsize(n) ((ub4)1&#60;&#60;(n))<br />
		#define hashmask(n) (hashsize(n)-1)</span></p>
</div>
<p><span style="font-size: 16px">在assoc_init()中，会对primary_hashtable做初始化，对应的hash操作包括：assoc_find()、 assoc_expand()、assoc_move_next_bucket()、assoc_insert()、assoc_delete()，对应于item的读写操作。其中assoc_find()是根据key和key长寻找对应的item地址的函数（注意在C中，很多时候都是同时直接传入字符串和字符串长度，而不是在函数内部做strlen），返回的是item结构指针，它的数据地址在slab中的某个chunk上。</span></p>
<p><span style="font-size: 16px">items.c是数据项的操作程序，每一个完整的item包括几个部分，在item_make_header()中定义为：</span></p>
<p><span style="font-size: 16px">key：键<br />
	nkey：键长<br />
	flags：用户定义的flag（其实这个flag在memcached中没有启用）<br />
	nbytes：值长（包括换行符号\r\n）<br />
	suffix：后缀Buffer<br />
	nsuffix：后缀长</span></p>
<p><span style="font-size: 16px">一个完整的item长度是键长＋值长＋后缀长＋item结构大小（32字节），item操作就是根据这个长度来计算slab的classid的。</span></p>
<p><span style="font-size: 16px">hashtable中的每一个桶上挂着一个双链表，item_init()的时候已经初始化了heads、tails、sizes三个数组为0，这三个数组的大小都为常量LARGEST_ID（默认为255，这个值需要配合factor来修改），在每次item_assoc()的时候，它会首先尝试从slab中获取一块空闲的chunk，如果没有可用的chunk，会在链表中扫描50次，以得到一个被LRU踢掉的item，将它unlink，然后将需要插入的item插入链表中。</span></p>
<p><span style="font-size: 16px">注意item的refcount成员。item被unlink之后只是从链表上摘掉，不是立刻就被free的，只是将它放到删除队列中（item_unlink_q()函数）。</span></p>
<p><span style="font-size: 16px">item对应一些读写操作，包括remove、update、replace，当然最重要的就是alloc操作。</span></p>
<p><span style="font-size: 16px">item还有一个特性就是它有过期时间，这是memcached的一个很有用的特性，很多应用都是依赖于memcached的item过期，比如 session存储、操作锁等。item_flush_expired()函数就是扫描表中的item，对过期的item执行unlink操作，当然这只是一个回收动作，实际上在get的时候还要进行时间判断：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/* expires items that are more recent than the oldest_live setting. */<br />
	void item_flush_expired() {<br />
	&#160; &#160; int i;&#160;&#160;<br />
	&#160; &#160; item *iter, *next;<br />
	&#160; &#160; if (! settings.oldest_live)<br />
	&#160; &#160;&#160; &#160;&#160;&#160;return;&#160;<br />
	&#160; &#160; for (i = 0; i &#60; LARGEST_ID; i++) {<br />
	&#160; &#160;&#160; &#160;&#160;&#160;/* The LRU is sorted in decreasing time order, and an item&#8217;s timestamp<br />
	&#160; &#160;&#160; &#160;&#160; &#160;* is never newer than its last access time, so we only need to walk<br />
	&#160; &#160;&#160; &#160;&#160; &#160;* back until we hit an item older than the oldest_live time.<br />
	&#160; &#160;&#160; &#160;&#160; &#160;* The oldest_live checking will auto-expire the remaining items.<br />
	&#160; &#160;&#160; &#160;&#160; &#160;*/<br />
	&#160; &#160;&#160; &#160;&#160;&#160;for (iter = heads[i]; iter != NULL; iter = next) {&#160;<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (iter-&#62;time &#62;= settings.oldest_live) {<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; next = iter-&#62;next;<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; if ((iter-&#62;it_flags &#38; ITEM_SLABBED) == 0) {&#160;<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;item_unlink(iter);<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; }&#160; &#160;&#160; &#160;&#160;<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;} else {<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; /* We&#8217;ve hit the first old item. Continue to the next queue. */<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; break;&#160;&#160;<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}&#160; &#160;&#160; &#160;&#160;<br />
	&#160; &#160;&#160; &#160;&#160;&#160;}&#160; &#160;&#160; &#160;&#160;<br />
	&#160; &#160; }<br />
	}</span></div>
<p>&#160;</p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/* wrapper around assoc_find which does the lazy expiration/deletion logic */<br />
	item *get_item_notedeleted(char *key, size_t nkey, int *delete_locked) {<br />
	&#160; &#160; item *it = assoc_find(key, nkey);<br />
	&#160; &#160; if (delete_locked) *delete_locked = 0;<br />
	&#160; &#160; if (it &#38;&#38; (it-&#62;it_flags &#38; ITEM_DELETED)) {<br />
	&#160; &#160;&#160; &#160;&#160;&#160;/* it&#8217;s flagged as delete-locked.&#160;&#160;let&#8217;s see if that condition<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;is past due, and the 5-second delete_timer just hasn&#8217;t<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160;&#160;gotten to it yet&#8230; */<br />
	&#160; &#160;&#160; &#160;&#160;&#160;if (! item_delete_lock_over(it)) {<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (delete_locked) *delete_locked = 1;<br />
	&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;it = 0;&#160;<br />
	&#160; &#160;&#160; &#160;&#160;&#160;}&#160; &#160;&#160; &#160;&#160;<br />
	&#160; &#160; }<br />
	&#160; &#160; if (it &#38;&#38; settings.oldest_live &#38;&#38; settings.oldest_live &#60;= current_time &#38;&#38;<br />
	&#160; &#160;&#160; &#160;&#160;&#160;it-&#62;time &#60;= settings.oldest_live) {<br />
	&#160; &#160;&#160; &#160;&#160;&#160;item_unlink(it);<br />
	&#160; &#160;&#160; &#160;&#160;&#160;it = 0;&#160;<br />
	&#160; &#160; }<br />
	&#160; &#160; if (it &#38;&#38; it-&#62;exptime &#38;&#38; it-&#62;exptime &#60;= current_time) {<br />
	&#160; &#160;&#160; &#160;&#160;&#160;item_unlink(it);<br />
	&#160; &#160;&#160; &#160;&#160;&#160;it = 0;&#160;<br />
	&#160; &#160; }<br />
	&#160; &#160; return it;<br />
	}</span></div>
<p><span style="font-size: 16px">Memcached的内存管理方式是非常精巧和高效的，它很大程度上减少了直接alloc系统内存的次数，降低函数开销和内存碎片产生几率，虽然这种方式会造成一些冗余浪费，但是这种浪费在大型系统应用中是微不足道的。<br />
	<img alt="" border="0" src="http://www.54np.com/docs/slabs.png" /></span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的理论参数计算方式</strong></span></p>
<p><span style="font-size: 16px">影响 memcached 工作的几个参数有：&#160;</span></p>
<p><span style="font-size: 16px">常量REALTIME_MAXDELTA 60*60*24*30<br />
	最大30天的过期时间</span></p>
<p><span style="font-size: 16px">conn_init()中的freetotal（=200）<br />
	最大同时连接数</span></p>
<p><span style="font-size: 16px">常量KEY_MAX_LENGTH 250<br />
	最大键长</span></p>
<p><span style="font-size: 16px">settings.factor（=1.25）<br />
	factor将影响chunk的步进大小</span></p>
<p><span style="font-size: 16px">settings.maxconns（=1024）<br />
	最大软连接</span></p>
<p><span style="font-size: 16px">settings.chunk_size（=48）<br />
	一个保守估计的key+value长度，用来生成id1中的chunk长度（1.2）。id1的chunk长度等于这个数值加上item结构体的长度（32），即默认的80字节。</span></p>
<p><span style="font-size: 16px">常量POWER_SMALLEST 1<br />
	最小classid（1.2）</span></p>
<p><span style="font-size: 16px">常量POWER_LARGEST 200<br />
	最大classid（1.2）</span></p>
<p><span style="font-size: 16px">常量POWER_BLOCK 1048576<br />
	默认slab大小</span></p>
<p><span style="font-size: 16px">常量CHUNK_ALIGN_BYTES (sizeof(void *))<br />
	保证chunk大小是这个数值的整数倍，防止越界（void *的长度在不同系统上不一样，在标准32位系统上是4）</span></p>
<p><span style="font-size: 16px">常量ITEM_UPDATE_INTERVAL 60<br />
	队列刷新间隔</span></p>
<p><span style="font-size: 16px">常量LARGEST_ID 255<br />
	最大item链表数（这个值不能比最大的classid小）</span></p>
<p><span style="font-size: 16px">变量hashpower（在1.1中是常量HASHPOWER）<br />
	决定hashtable的大小</span></p>
<p><span style="font-size: 16px">根据上面介绍的内容及参数设定，可以计算出的一些结果：</span></p>
<p><span style="font-size: 16px">1、在memcached中可以保存的item个数是没有软件上限的，之前我的100万的说法是错误的。<br />
	2、假设NewHash算法碰撞均匀，查找item的循环次数是item总数除以hashtable大小（由hashpower决定），是线性的。<br />
	3、Memcached限制了可以接受的最大item是1MB，大于1MB的数据不予理会。<br />
	4、Memcached的空间利用率和数据特性有很大的关系，又与DONT_PREALLOC_SLABS常量有关。 在最差情况下，有198个slab会被浪费（所有item都集中在一个slab中，199个id全部分配满）。&#160;</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的定长优化</strong></span></p>
<p><span style="font-size: 16px">根据上面几节的描述，多少对memcached有了一个比较深入的认识。在深入认识的基础上才好对它进行优化。</span></p>
<p><span style="font-size: 16px">Memcached本身是为变长数据设计的，根据数据特性，可以说它是&#8220;面向大众&#8221;的设计，但是很多时候，我们的数据并不是这样的&#8220;普遍&#8221;，典型的情况中，一种是非均匀分布，即数据长度集中在几个区域内（如保存用户 Session）；另一种更极端的状态是等长数据（如定长键值，定长数据，多见于访问、在线统计或执行锁）。</span></p>
<p><span style="font-size: 16px">这里主要研究一下定长数据的优化方案（1.2），集中分布的变长数据仅供参考，实现起来也很容易。</span></p>
<p><span style="font-size: 16px">解决定长数据，首先需要解决的是slab的分配问题，第一个需要确认的是我们不需要那么多不同chunk长度的slab，为了最大限度地利用资源，最好chunk和item等长，所以首先要计算item长度。</span></p>
<p><span style="font-size: 16px">在之前已经有了计算item长度的算法，需要注意的是，除了字符串长度外，还要加上item结构的长度32字节。</span></p>
<p><span style="font-size: 16px">假设我们已经计算出需要保存200字节的等长数据。</span></p>
<p><span style="font-size: 16px">接下来是要修改slab的classid和chunk长度的关系。在原始版本中，chunk长度和classid是有对应关系的，现在如果把所有的 chunk都定为200个字节，那么这个关系就不存在了，我们需要重新确定这二者的关系。一种方法是，整个存储结构只使用一个固定的id，即只使用199 个槽中的1个，在这种条件下，就一定要定义DONT_PREALLOC_SLABS来避免另外的预分配浪费。另一种方法是建立一个hash关系，来从 item确定classid，不能使用长度来做键，可以使用key的NewHash结果等不定数据，或者直接根据key来做hash（定长数据的key也一定等长）。这里简单起见，选择第一种方法，这种方法的不足之处在于只使用一个id，在数据量非常大的情况下，slab链会很长（因为所有数据都挤在一条链上了），遍历起来的代价比较高。</span></p>
<p><span style="font-size: 16px">前面介绍了三种空间冗余，设置chunk长度等于item长度，解决了第一种空间浪费问题，不预申请空间解决了第二种空间浪费问题，那么对于第一种问题（slab内剩余）如何解决呢，这就需要修改POWER_BLOCK常量，使得每一个slab大小正好等于chunk长度的整数倍，这样一个slab 就可以正好划分成n个chunk。这个数值应该比较接近1MB，过大的话同样会造成冗余，过小的话会造成次数过多的alloc，根据chunk长度为 200，选择1000000作为POWER_BLOCK的值，这样一个slab就是100万字节，不是1048576。三个冗余问题都解决了，空间利用率会大大提升。</span></p>
<p><span style="font-size: 16px">修改 slabs_clsid 函数，让它直接返回一个定值（比如 1 ）：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">unsigned int slabs_clsid(size_t size) {<br />
	&#160; &#160; &#160; &#160; return 1;<br />
	}</span></div>
<p><span style="font-size: 16px">修改slabs_init函数，去掉循环创建所有classid属性的部分，直接添加slabclass[1]：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">slabclass[1].size = 200;&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; //每chunk200字节<br />
	slabclass[1].perslab = 5000;&#160; &#160; &#160; &#160; //1000000/200</span></div>
<p><span style="font-size: 16px"><strong>◎Memcached客户端</strong></span></p>
<p><span style="font-size: 16px">Memcached是一个服务程序，使用的时候可以根据它的协议，连接到memcached服务器上，发送命令给服务进程，就可以操作上面的数据。为了方便使用，memcached有很多个客户端程序可以使用，对应于各种语言，有各种语言的客户端。基于C语言的有libmemcache、 APR_Memcache；基于Perl的有Cache::Memcached；另外还有Python、Ruby、Java、C#等语言的支持。PHP的客户端是最多的，不光有mcache和PECL memcache两个扩展，还有大把的由PHP编写的封装类，下面介绍一下在PHP中使用memcached的方法：</span></p>
<p><span style="font-size: 16px">mcache扩展是基于libmemcache再封装的。libmemcache一直没有发布stable版本，目前版本是1.4.0-rc2，可以在这里找到。libmemcache有一个很不好的特性，就是会向stderr写很多错误信息，一般的，作为lib使用的时候，stderr一般都会被定向到其它地方，比如Apache的错误日志，而且libmemcache会自杀，可能会导致异常，不过它的性能还是很好的。</span></p>
<p><span style="font-size: 16px">mcache扩展最后更新到1.2.0-beta10，作者大概是离职了，不光停止更新，连网站也打不开了（~_~），只能到其它地方去获取这个不负责的扩展了。解压后安装方法如常：phpize &#38; configure &#38; make &#38; make install，一定要先安装libmemcache。使用这个扩展很简单：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px"><span><span>&#60;?php<br />
	$mc&#160;</span><span>=&#160;</span><span>memcache</span><span>();&#160;&#160;&#160;&#160;</span><span>//&#160;创建一个memcache连接对象，注意这里不是用new！</span><br />
	<span>$mc</span><span>-&#62;</span><span>add_server</span><span>(</span><span>&#8216;localhost&#8217;</span><span>,&#160;</span><span>11211</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;添加一个服务进程</span><br />
	<span>$mc</span><span>-&#62;</span><span>add_server</span><span>(</span><span>&#8216;localhost&#8217;</span><span>,&#160;</span><span>11212</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;添加第二个服务进程</span><br />
	<span>$mc</span><span>-&#62;</span><span>set</span><span>(</span><span>&#8216;key1&#8242;</span><span>,&#160;</span><span>&#8216;Hello&#8217;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;写入key1&#160;=&#62;&#160;Hello</span><br />
	<span>$mc</span><span>-&#62;</span><span>set</span><span>(</span><span>&#8216;key2&#8242;</span><span>,&#160;</span><span>&#8216;World&#8217;</span><span>,&#160;</span><span>10</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;写入key2&#160;=&#62;&#160;World，10秒过期</span><br />
	<span>$mc</span><span>-&#62;</span><span>set</span><span>(</span><span>&#8216;arr1&#8242;</span><span>,&#160;array(</span><span>&#8216;Hello&#8217;</span><span>,&#160;</span><span>&#8216;World&#8217;</span><span>));&#160;&#160;&#160;&#160;</span><span>//&#160;写入一个数组</span><br />
	<span>$key1&#160;</span><span>=&#160;</span><span>$mc</span><span>-&#62;</span><span>get</span><span>(</span><span>&#8216;key1&#8242;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;获取&#8217;key1&#8242;的值，赋给$key1</span><br />
	<span>$key2&#160;</span><span>=&#160;</span><span>$mc</span><span>-&#62;</span><span>get</span><span>(</span><span>&#8216;key2&#8242;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;获取&#8217;key2&#8242;的值，赋给$key2，如果超过10秒，就取不到了</span><br />
	<span>$arr1&#160;</span><span>=&#160;</span><span>$mc</span><span>-&#62;</span><span>get</span><span>(</span><span>&#8216;arr1&#8242;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;获取&#8217;arr1&#8242;数组</span><br />
	<span>$mc</span><span>-&#62;</span><span>delete</span><span>(</span><span>&#8216;arr1&#8242;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;删除&#8217;arr1&#8242;</span><br />
	<span>$mc</span><span>-&#62;</span><span>flush_all</span><span>();&#160;&#160;&#160;&#160;</span><span>//&#160;删掉所有数据</span><br />
	<span>$stats&#160;</span><span>=&#160;</span><span>$mc</span><span>-&#62;</span><span>stats</span><span>();&#160;&#160;&#160;&#160;</span><span>//&#160;获取服务器信息</span><br />
	<span>var_dump</span><span>(</span><span>$stats</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;服务器信息是一个数组</span><br />
	<span>?&#62;</span></span></span></div>
<p><span style="font-size: 16px">这个扩展的好处是可以很方便地实现分布式存储和负载均衡，因为它可以添加多个服务地址，数据在保存的时候是会根据hash结果定位到某台服务器上的，这也是libmemcache的特性。libmemcache支持集中hash方式，包括CRC32、ELF和Perl hash。</span></p>
<p><span style="font-size: 16px">PECL memcache是PECL发布的扩展，目前最新版本是2.1.0，可以在pecl网站得到。memcache扩展的使用方法可以在新一些的PHP手册中找到，它和mcache很像，真的很像：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px"><span><span>&#60;?php </span></span></span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px"><span><span>$memcache&#160;<span>=&#160;new&#160;</span><span>Memcache</span><span>;</span><br />
		<span>$memcache</span><span>-&#62;</span><span>connect</span><span>(</span><span>&#8216;localhost&#8217;</span><span>,&#160;</span><span>11211</span><span>)&#160;or&#160;die&#160;(</span><span>&#8220;Could&#160;not&#160;connect&#8221;</span><span>);</span></span></span></span></p>
	<p><span style="font-size: 16px"><span><span><span>$version&#160;</span><span>=&#160;</span><span>$memcache</span><span>-&#62;</span><span>getVersion</span><span>();<br />
		echo&#160;</span><span>&#8220;Server&#8217;s&#160;version:&#160;&#8221;</span><span>.</span><span>$version</span><span>.</span><span>&#8220;n&#8221;</span><span>;</span></span></span></span></p>
	<p><span style="font-size: 16px"><span><span><span>$tmp_object&#160;</span><span>=&#160;new&#160;</span><span>stdClass</span><span>;</span><br />
		<span>$tmp_object</span><span>-&#62;</span><span>str_attr&#160;</span><span>=&#160;</span><span>&#8216;test&#8217;</span><span>;</span><br />
		<span>$tmp_object</span><span>-&#62;</span><span>int_attr&#160;</span><span>=&#160;</span><span>123</span><span>;</span></span></span></span></p>
	<p><span style="font-size: 16px"><span><span><span>$memcache</span><span>-&#62;</span><span>set</span><span>(</span><span>&#8216;key&#8217;</span><span>,&#160;</span><span>$tmp_object</span><span>,&#160;</span><span>false</span><span>,&#160;</span><span>10</span><span>)&#160;or&#160;die&#160;(</span><span>&#8220;Failed&#160;to&#160;save&#160;data&#160;at&#160;the&#160;server&#8221;</span><span>);<br />
		echo&#160;</span><span>&#8220;Store&#160;data&#160;in&#160;the&#160;cache&#160;(data&#160;will&#160;expire&#160;in&#160;10&#160;seconds)n&#8221;</span><span>;</span></span></span></span></p>
	<p><span style="font-size: 16px"><span><span><span>$get_result&#160;</span><span>=&#160;</span><span>$memcache</span><span>-&#62;</span><span>get</span><span>(</span><span>&#8216;key&#8217;</span><span>);<br />
		echo&#160;</span><span>&#8220;Data&#160;from&#160;the&#160;cache:n&#8221;</span><span>;</span></span></span></span></p>
	<p><span style="font-size: 16px"><span><span><span>var_dump</span><span>(</span><span>$get_result</span><span>);</span></span></span></span></p>
	<p><span style="font-size: 16px"><span><span><span>?&#62;</span></span></span></span></p>
</div>
<p><span style="font-size: 16px">这个扩展是使用php的stream直接连接memcached服务器并通过socket发送命令的。它不像libmemcache那样完善，也不支持add_server这种分布操作，但是因为它不依赖其它的外界程序，兼容性要好一些，也比较稳定。至于效率，差别不是很大。</span></p>
<p><span style="font-size: 16px">另外，有很多的PHP class可以使用，比如MemcacheClient.inc.php，phpclasses.org上可以找到很多，一般都是对perl client API的再封装，使用方式很像。</span></p>
<p><span style="font-size: 16px">◎BSM_Memcache</span></p>
<p><span style="font-size: 16px">从C client来说，APR_Memcache是一个很成熟很稳定的client程序，支持线程锁和原子级操作，保证运行的稳定性。不过它是基于APR的（APR将在最后一节介绍），没有libmemcache的应用范围广，目前也没有很多基于它开发的程序，现有的多是一些Apache Module，因为它不能脱离APR环境运行。但是APR倒是可以脱离Apache单独安装的，在APR网站上可以下载APR和APR-util，不需要有Apache，可以直接安装，而且它是跨平台的。</span></p>
<p><span style="font-size: 16px">BSM_Memcache是我在BS.Magic项目中开发的一个基于APR_Memcache的PHP扩展，说起来有点拗口，至少它把APR扯进了PHP扩展中。这个程序很简单，也没做太多的功能，只是一种形式的尝试，它支持服务器分组。</span></p>
<p><span style="font-size: 16px">和mcache扩展支持多服务器分布存储不同，BSM_Memcache支持多组服务器，每一组内的服务器还是按照hash方式来分布保存数据，但是两个组中保存的数据是一样的，也就是实现了热备，它不会因为一台服务器发生单点故障导致数据无法获取，除非所有的服务器组都损坏（例如机房停电）。当然实现这个功能的代价就是性能上的牺牲，在每次添加删除数据的时候都要扫描所有的组，在get数据的时候会随机选择一组服务器开始轮询，一直到找到数据为止，正常情况下一次就可以获取得到。</span></p>
<p><span style="font-size: 16px">BSM_Memcache只支持这几个函数：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">zend_function_entry bsm_memcache_functions[] =<br />
	{<br />
	&#160; &#160; PHP_FE(mc_get,&#160; &#160;&#160; &#160;&#160; &#160; NULL)<br />
	&#160; &#160; PHP_FE(mc_set,&#160; &#160;&#160; &#160;&#160; &#160; NULL)<br />
	&#160; &#160; PHP_FE(mc_del,&#160; &#160;&#160; &#160;&#160; &#160; NULL)<br />
	&#160; &#160; PHP_FE(mc_add_group,&#160; &#160; NULL)<br />
	&#160; &#160; PHP_FE(mc_add_server,&#160; &#160;NULL)<br />
	&#160; &#160; PHP_FE(mc_shutdown,&#160; &#160;&#160;&#160;NULL)<br />
	&#160; &#160; {NULL, NULL, NULL}<br />
	};</span></div>
<p><span style="font-size: 16px">mc_add_group函数返回一个整形（其实应该是一个object，我偷懒了~_~）作为组ID，mc_add_server的时候要提供两个参数，一个是组ID，一个是服务器地址（ADDR<img align="absMiddle" alt="" border="0" src="http://www.phpx.com/happy/images/smilies/tongue.gif" />ORT）。</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Add a server group<br />
	*/<br />
	PHP_FUNCTION(mc_add_group)<br />
	{<br />
	&#160; &#160; apr_int32_t group_id;<br />
	&#160; &#160; apr_status_t rv;</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">&#160; &#160; if (0 != ZEND_NUM_ARGS())<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;WRONG_PARAM_COUNT;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;RETURN_NULL();<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; group_id = free_group_id();<br />
		&#160; &#160; if (-1 == group_id)<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;RETURN_FALSE;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; apr_memcache_t *mc;<br />
		&#160; &#160; rv = apr_memcache_create(p, MAX_G_SERVER, 0, &#38;mc);</span></p>
	<p><span style="font-size: 16px">&#160; &#160; add_group(group_id, mc);</span></p>
	<p><span style="font-size: 16px">&#160; &#160; RETURN_DOUBLE(group_id);<br />
		}</span></p>
</div>
<p>&#160;</p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Add a server into group<br />
	*/<br />
	PHP_FUNCTION(mc_add_server)<br />
	{<br />
	&#160; &#160; apr_status_t rv;<br />
	&#160; &#160; apr_int32_t group_id;<br />
	&#160; &#160; double g;<br />
	&#160; &#160; char *srv_str;<br />
	&#160; &#160; int srv_str_l;</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">&#160; &#160; if (2 != ZEND_NUM_ARGS())<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;WRONG_PARAM_COUNT;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, &#8220;ds&#8221;, &#38;g, &#38;srv_str, &#38;srv_str_l) == FAILURE)<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;RETURN_FALSE;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; group_id = (apr_int32_t) g;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; if (-1 == is_validate_group(group_id))<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;RETURN_FALSE;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; char *host, *scope;<br />
		&#160; &#160; apr_port_t port;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; rv = apr_parse_addr_port(&#38;host, &#38;scope, &#38;port, srv_str, p);<br />
		&#160; &#160; if (APR_SUCCESS == rv)<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;// Create this server object<br />
		&#160; &#160;&#160; &#160;&#160;&#160;apr_memcache_server_t *st;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;rv = apr_memcache_server_create(p, host, port, 0, 64, 1024, 600, &#38;st);<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (APR_SUCCESS == rv)<br />
		&#160; &#160;&#160; &#160;&#160;&#160;{<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (NULL == mc_groups[group_id])<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; RETURN_FALSE;<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}</span></p>
	<p><span style="font-size: 16px">&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;// Add server<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;rv = apr_memcache_add_server(mc_groups[group_id], st);</span></p>
	<p><span style="font-size: 16px">&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (APR_SUCCESS == rv)<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; RETURN_TRUE;<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}<br />
		&#160; &#160;&#160; &#160;&#160;&#160;}<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; RETURN_FALSE;<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">在set和del数据的时候，要循环所有的组：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Store item into all groups<br />
	*/<br />
	PHP_FUNCTION(mc_set)<br />
	{<br />
	&#160; &#160; char *key, *value;<br />
	&#160; &#160; int key_l, value_l;<br />
	&#160; &#160; double ttl = 0;<br />
	&#160; &#160; double set_ct = 0;</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">&#160; &#160; if (2 != ZEND_NUM_ARGS())<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;WRONG_PARAM_COUNT;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, &#8220;ss&#124;d&#8221;, &#38;key, &#38;key_l, &#38;value, &#38;value_l, ttl) == FAILURE)<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;RETURN_FALSE;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; // Write data into every object<br />
		&#160; &#160; apr_int32_t i = 0;<br />
		&#160; &#160; if (ttl &#60; 0)<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;ttl = 0;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; apr_status_t rv;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; for (i = 0; i &#60; MAX_GROUP; i++)<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (0 == is_validate_group(i))<br />
		&#160; &#160;&#160; &#160;&#160;&#160;{<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;// Write it!<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;rv = apr_memcache_add(mc_groups[i], key, value, value_l, (apr_uint32_t) ttl, 0);<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (APR_SUCCESS == rv)<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; set_ct++;<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}<br />
		&#160; &#160;&#160; &#160;&#160;&#160;}<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; RETURN_DOUBLE(set_ct);<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">在mc_get中，首先要随机选择一个组，然后从这个组开始轮询：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Fetch a item from a random group<br />
	*/<br />
	PHP_FUNCTION(mc_get)<br />
	{&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;<br />
	&#160; &#160; char *key, *value = NULL;<br />
	&#160; &#160; int key_l;<br />
	&#160; &#160; apr_size_t value_l;</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">&#160; &#160; if (1 != ZEND_NUM_ARGS())<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;WRONG_PARAM_COUNT;<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, &#8220;s&#8221;, &#38;key, &#38;key_l) == FAILURE)<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;RETURN_MULL();<br />
		&#160; &#160; }<br />
		&#160; &#160;&#160;<br />
		&#160; &#160; // I will try &#8230;<br />
		&#160; &#160; // Random read<br />
		&#160; &#160; apr_int32_t curr_group_id = random_group();<br />
		&#160; &#160; apr_int32_t i = 0;<br />
		&#160; &#160; apr_int32_t try = 0;<br />
		&#160; &#160; apr_uint32_t flag;<br />
		&#160; &#160; apr_memcache_t *oper;<br />
		&#160; &#160; apr_status_t rv;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; for (i = 0; i &#60; MAX_GROUP; i++)<br />
		&#160; &#160; {<br />
		&#160; &#160;&#160; &#160;&#160;&#160;try = i + curr_group_id;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;try = try % MAX_GROUP;<br />
		&#160; &#160;&#160; &#160;&#160;&#160;if (0 == is_validate_group(try))<br />
		&#160; &#160;&#160; &#160;&#160;&#160;{<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;// Get a value<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;oper = mc_groups[try];<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;rv = apr_memcache_getp(mc_groups[try], p, (const char *) key, &#38;value, &#38;value_l, 0);<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;if (APR_SUCCESS == rv)<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;{<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160; RETURN_STRING(value, 1);<br />
		&#160; &#160;&#160; &#160;&#160; &#160;&#160; &#160;}<br />
		&#160; &#160;&#160; &#160;&#160;&#160;}<br />
		&#160; &#160; }</span></p>
	<p><span style="font-size: 16px">&#160; &#160; RETURN_FALSE;<br />
		}</span></p>
</div>
<p>&#160;</p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Random group id<br />
	* For mc_get()<br />
	*/<br />
	apr_int32_t random_group()<br />
	{<br />
	&#160; &#160; struct timeval tv;<br />
	&#160; &#160; struct timezone tz;<br />
	&#160; &#160; int usec;</span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px">&#160; &#160; gettimeofday(&#38;tv, &#38;tz);</span></p>
	<p><span style="font-size: 16px">&#160; &#160; usec = tv.tv_usec;</span></p>
	<p><span style="font-size: 16px">&#160; &#160; int curr = usec % count_group();</span></p>
	<p><span style="font-size: 16px">&#160; &#160; return (apr_int32_t) curr;<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">BSM_Memcache的使用方式和其它的client类似：</span></p>
<div class="smalltxt">
	<div><span style="font-size: 16px">CODE:</span></div>
	<div>&#160;</div>
</div>
<div class="altbg2"><span style="font-size: 16px"><span><span>&#60;?php<br />
	$g1&#160;</span><span>=&#160;</span><span>mc_add_group</span><span>();&#160;&#160;&#160;&#160;</span><span>//&#160;添加第一个组</span><br />
	<span>$g2&#160;</span><span>=&#160;</span><span>mc_add_group</span><span>();&#160;&#160;&#160;&#160;</span><span>//&#160;添加第二个组</span><br />
	<span>mc_add_server</span><span>(</span><span>$g1</span><span>,&#160;</span><span>&#8216;localhost:11211&#8242;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;在第一个组中添加第一台服务器</span><br />
	<span>mc_add_server</span><span>(</span><span>$g1</span><span>,&#160;</span><span>&#8216;localhost:11212&#8242;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;在第一个组中添加第二台服务器</span><br />
	<span>mc_add_server</span><span>(</span><span>$g2</span><span>,&#160;</span><span>&#8216;10.0.0.16:11211&#8242;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;在第二个组中添加第一台服务器</span><br />
	<span>mc_add_server</span><span>(</span><span>$g2</span><span>,&#160;</span><span>&#8216;10.0.0.17:11211&#8242;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;在第二个组中添加第二台服务器 </span></span></span>
	<p>&#160;</p>
	<p>&#160;</p>
	<p><span style="font-size: 16px"><span><span><span>mc_set</span><span>(</span><span>&#8216;key&#8217;</span><span>,&#160;</span><span>&#8216;Hello&#8217;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;写入数据</span><br />
		<span>$key&#160;</span><span>=&#160;</span><span>mc_get</span><span>(</span><span>&#8216;key&#8217;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;读出数据</span><br />
		<span>mc_del</span><span>(</span><span>&#8216;key&#8217;</span><span>);&#160;&#160;&#160;&#160;</span><span>//&#160;删除数据</span><br />
		<span>mc_shutdown</span><span>();&#160;&#160;&#160;&#160;</span><span>//&#160;关闭所有组</span><br />
		<span>?&#62;</span></span></span></span></p>
</div>
<p><span style="font-size: 16px">APR_Memcache的相关资料可以在这里找到，BSM_Memcache可以在本站下载。</span></p>
<p><span style="font-size: 16px"><strong>◎APR环境介绍</strong></span></p>
<p><span style="font-size: 16px">APR的全称：Apache Portable Runtime。它是Apache软件基金会创建并维持的一套跨平台的C语言库。它从Apache httpd1.x中抽取出来并独立于httpd之外，Apache httpd2.x就是建立在APR上。APR提供了很多方便的API接口可供使用，包括如内存池、字符串操作、网络、数组、hash表等实用的功能。开发 Apache2 Module要接触很多APR函数，当然APR可以独立安装独立使用，可以用来写自己的应用程序，不一定是Apache httpd的相关开发。</span></p>
<p><span style="font-size: 16px"><strong>◎后记</strong></span></p>
<p><span style="font-size: 16px">这是我在农历丙戌年（我的本命年）的最后一篇文章，由于Memcached的内涵很多，仓促整理一定有很多遗漏和错误。感谢新浪网提供的研究机会，感谢部门同事的帮助。</span></p>
<p>&#160;</p>
<p><span style="font-size: 16px">转自：<a href="http://blog.developers.api.sina.com.cn/?p=124">http://blog.developers.api.sina.com.cn/?p=124</a></span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px">Memcached是danga.com（运营LiveJournal的技术团队）开发的一套分布式内存对象缓存系统，用于在动态系统中减少数据库负载，提升性能。关于这个东西，相信很多人都用过，本文意在通过对memcached的实现及代码分析，获得对这个出色的开源软件更深入的了解，并可以根据我们的需要对其进行更进一步的优化。末了将通过对BSM_Memcache扩展的分析，加深对memcached的使用方式理解。</span></p>
<p><span style="font-size: 16px">本文的部分内容可能需要比较好的数学基础作为辅助。</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached是什么</strong></span></p>
<p><span style="font-size: 16px">在阐述这个问题之前，我们首先要清楚它&ldquo;不是什么&rdquo;。很多人把它当作和SharedMemory那种形式的存储载体来使用，虽然memcached 使用了同样的&ldquo;Key=&gt;Value&rdquo;方式组织数据，但是它和共享内存、APC等本地缓存有非常大的区别。Memcached是分布式的，也就是说它不是本地的。它基于网络连接（当然它也可以使用localhost）方式完成服务，本身它是一个独立于应用的程序或守护进程（Daemon方式）。</span></p>
<p><span style="font-size: 16px">Memcached使用libevent库实现网络连接服务，理论上可以处理无限多的连接，但是它和Apache不同，它更多的时候是面向稳定的持续连接的，所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200，这和Linux线程能力有关系，这个数值是可以调整的。关于libevent可以参考相关文档。 Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的，memcachd有自己的内存分配算法和管理方式，它和共享内存没有关系，也没有共享内存的限制，通常情况下，每个memcached进程可以管理2GB的内存空间，如果需要更多的空间，可以增加进程数。&nbsp;</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached适合什么场合</strong></span></p>
<p><span style="font-size: 16px">在很多时候，memcached都被滥用了，这当然少不了对它的抱怨。我经常在论坛上看见有人发贴，类似于&ldquo;如何提高效率&rdquo;，回复是&ldquo;用memcached&rdquo;，至于怎么用，用在哪里，用来干什么一句没有。memcached不是万能的，它也不是适用在所有场合。</span></p>
<p><span style="font-size: 16px">Memcached是&ldquo;分布式&rdquo;的内存对象缓存系统，那么就是说，那些不需要&ldquo;分布&rdquo;的，不需要共享的，或者干脆规模小到只有一台服务器的应用，memcached不会带来任何好处，相反还会拖慢系统效率，因为网络连接同样需要资源，即使是UNIX本地连接也一样。 在我之前的测试数据中显示，memcached本地读写速度要比直接PHP内存数组慢几十倍，而APC、共享内存方式都和直接数组差不多。可见，如果只是本地级缓存，使用memcached是非常不划算的。</span></p>
<p><span style="font-size: 16px">Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销，而且它是使用内存来管理数据的，所以它可以提供比直接读取数据库更好的性能，在大型系统中，访问同样的数据是很频繁的，memcached可以大大降低数据库压力，使系统执行效率提升。另外，memcached也经常作为服务器之间数据共享的存储媒介，例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached 中，被多个应用共享。</span></p>
<p><span style="font-size: 16px">需要注意的是，memcached使用内存管理数据，所以它是易失的，当服务器重启，或者memcached进程中止，数据便会丢失，所以 memcached不能用来持久保存数据。很多人的错误理解，memcached的性能非常好，好到了内存和硬盘的对比程度，其实memcached使用内存并不会得到成百上千的读写速度提高，它的实际瓶颈在于网络连接，它和使用磁盘的数据库系统相比，好处在于它本身非常&ldquo;轻&rdquo;，因为没有过多的开销和直接的读写方式，它可以轻松应付非常大的数据交换量，所以经常会出现两条千兆网络带宽都满负荷了，memcached进程本身并不占用多少CPU资源的情况。</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的工作方式</strong></span></p>
<p><span style="font-size: 16px">以下的部分中，读者最好能准备一份memcached的源代码。</span></p>
<p><span style="font-size: 16px">Memcached是传统的网络服务程序，如果启动的时候使用了-d参数，它会以守护进程的方式执行。创建守护进程由daemon.c完成，这个程序只有一个daemon函数，这个函数很简单（如无特殊说明，代码以1.2.1为准）：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">#include &lt;fcntl.h&gt;<br />
	#include &lt;stdlib.h&gt;<br />
	#include &lt;unistd.h&gt;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">int<br />
		daemon(nochdir, noclose)<br />
		&nbsp; &nbsp; int nochdir, noclose;<br />
		{<br />
		&nbsp; &nbsp; int fd;&nbsp;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; switch (fork()) {<br />
		&nbsp; &nbsp; case -1:<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return (-1);<br />
		&nbsp; &nbsp; case 0:&nbsp;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;break;&nbsp;&nbsp;<br />
		&nbsp; &nbsp; default:<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;_exit(0);<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (setsid() == -1)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return (-1);</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (!nochdir)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(void)chdir(&rdquo;/&rdquo;);</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (!noclose &amp;&amp; (fd = open(&rdquo;/dev/null&rdquo;, O_RDWR, 0)) != -1) {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(void)dup2(fd, STDIN_FILENO);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(void)dup2(fd, STDOUT_FILENO);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;(void)dup2(fd, STDERR_FILENO);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (fd &gt; STDERR_FILENO)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;(void)close(fd);<br />
		&nbsp; &nbsp; }<br />
		&nbsp; &nbsp; return (0);<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">这个函数 fork 了整个进程之后，父进程就退出，接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备， daemon 就建立成功了。&nbsp;</span></p>
<p><span style="font-size: 16px">Memcached 本身的启动过程，在 memcached.c 的 main 函数中顺序如下：&nbsp;</span></p>
<p><span style="font-size: 16px">1 、调用 settings_init() 设定初始化参数<br />
	2 、从启动命令中读取参数来设置 setting 值<br />
	3 、设定 LIMIT 参数<br />
	4 、开始网络 socket 监听（如果非 socketpath 存在）（ 1.2 之后支持 UDP 方式）<br />
	5 、检查用户身份（ Memcached 不允许 root 身份启动）<br />
	6 、如果有 socketpath 存在，开启 UNIX 本地连接（Sock 管道）<br />
	7 、如果以 -d 方式启动，创建守护进程（如上调用 daemon 函数）<br />
	8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab<br />
	9 、如设置中 managed 生效，创建 bucket 数组<br />
	10 、检查是否需要锁定内存页<br />
	11 、初始化信号、连接、删除队列<br />
	12 、如果 daemon 方式，处理进程 ID<br />
	13 、event 开始，启动过程结束， main 函数进入循环。&nbsp;</span></p>
<p><span style="font-size: 16px">在 daemon 方式中，因为 stderr 已经被定向到黑洞，所以不会反馈执行中的可见错误信息。&nbsp;</span></p>
<p><span style="font-size: 16px">memcached.c 的主循环函数是 drive_machine ，传入参数是指向当前的连接的结构指针，根据 state 成员的状态来决定动作。&nbsp;</span></p>
<p><span style="font-size: 16px">Memcached 使用一套自定义的协议完成数据交换，它的 protocol 文档可以参考： http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt</span></p>
<p><span style="font-size: 16px">在API中，换行符号统一为\r\n</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的内存管理方式</strong></span></p>
<p><span style="font-size: 16px">Memcached有一个很有特色的内存管理方式，为了提高效率，它使用预申请和分组的方式管理内存空间，而并不是每次需要写入数据的时候去malloc，删除数据的时候free一个指针。Memcached使用slab-&gt;chunk的组织方式管理内存。</span></p>
<p><span style="font-size: 16px">1.1和1.2的slabs.c中的slab空间划分算法有一些不同，后面会分别介绍。</span></p>
<p><span style="font-size: 16px">Slab可以理解为一个内存块，一个slab是memcached一次申请内存的最小单位，在memcached中，一个slab的大小默认为 1048576字节（1MB），所以memcached都是整MB的使用内存。每一个slab被划分为若干个chunk，每个chunk里保存一个 item，每个item同时包含了item结构体、key和value（注意在memcached中的value是只有字符串的）。slab按照自己的 id分别组成链表，这些链表又按id挂在一个slabclass数组上，整个结构看起来有点像二维数组。slabclass的长度在1.1中是21，在 1.2中是200。</span></p>
<p><span style="font-size: 16px">slab有一个初始chunk大小，1.1中是1字节，1.2中是80字节，1.2中有一个factor值，默认为1.25</span></p>
<p><span style="font-size: 16px">在1.1中，chunk大小表示为初始大小*2^n，n为classid，即：id为0的slab，每chunk大小1字节，id为1的slab，每chunk大小2字节，id为2的slab，每chunk大小4字节&hellip;&hellip;id为20的slab，每chunk大小为1MB，就是说id为20的slab 里只有一个chunk：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">void slabs_init(size_t limit) {<br />
	&nbsp; &nbsp; int i;<br />
	&nbsp; &nbsp; int size=1;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">&nbsp; &nbsp; mem_limit = limit;<br />
		&nbsp; &nbsp; for(i=0; i&lt;=POWER_LARGEST; i++, size*=2) {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].size = size;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].perslab = POWER_BLOCK / size;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].slots = 0;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].end_page_ptr = 0;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].end_page_free = 0;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].slab_list = 0;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].list_size = 0;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].killing = 0;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; /* for the test suite:&nbsp;&nbsp;faking of how much we&rsquo;ve already malloc&rsquo;d */<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;char *t_initial_malloc = getenv(&rdquo;T_MEMD_INITIAL_MALLOC&rdquo;);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (t_initial_malloc) {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;mem_malloced = atol(getenv(&rdquo;T_MEMD_INITIAL_MALLOC&rdquo;));<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; /* pre-allocate slabs by default, unless the environment variable<br />
		&nbsp; &nbsp;&nbsp; &nbsp; for testing is set to something non-zero */<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;char *pre_alloc = getenv(&rdquo;T_MEMD_SLABS_ALLOC&rdquo;);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (!pre_alloc || atoi(pre_alloc)) {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;slabs_preallocate(limit / POWER_BLOCK);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<br />
		&nbsp; &nbsp; }<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">在1.2中，chunk大小表示为初始大小*f^n，f为factor，在memcached.c中定义，n为classid，同时，201个头不是全部都要初始化的，因为factor可变，初始化只循环到计算出的大小达到slab大小的一半为止，而且它是从id1开始的，即：id为1的slab，每chunk大小80字节，id为2的slab，每chunk大小80*f，id为3的slab，每chunk大小80*f^2，初始化大小有一个修正值 CHUNK_ALIGN_BYTES，用来保证n-byte排列 （保证结果是CHUNK_ALIGN_BYTES的整倍数）。这样，在标准情况下，memcached1.2会初始化到id40，这个slab中每个 chunk大小为504692，每个slab中有两个chunk。最后，slab_init函数会在最后补足一个id41，它是整块的，也就是这个 slab中只有一个1MB大的chunk：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">void slabs_init(size_t limit, double factor) {<br />
	&nbsp; &nbsp; int i = POWER_SMALLEST &ndash; 1;<br />
	&nbsp; &nbsp; unsigned int size = sizeof(item) + settings.chunk_size;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">&nbsp; &nbsp; /* Factor of 2.0 means use the default memcached behavior */<br />
		&nbsp; &nbsp; if (factor == 2.0 &amp;&amp; size &lt; 128)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;size = 128;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; mem_limit = limit;<br />
		&nbsp; &nbsp; memset(slabclass, 0, sizeof(slabclass));</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; while (++i &lt; POWER_LARGEST &amp;&amp; size &lt;= POWER_BLOCK / 2) {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;/* Make sure items are always n-byte aligned */<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (size % CHUNK_ALIGN_BYTES)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;size += CHUNK_ALIGN_BYTES &ndash; (size % CHUNK_ALIGN_BYTES);</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].size = size;&nbsp;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;slabclass[i].perslab = POWER_BLOCK / slabclass[i].size;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;size *= factor;&nbsp;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (settings.verbose &gt; 1) {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;fprintf(stderr, &ldquo;slab class %3d: chunk size %6d perslab %5d\n&rdquo;,<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;i, slabclass[i].size, slabclass[i].perslab);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; power_largest = i;<br />
		&nbsp; &nbsp; slabclass[power_largest].size = POWER_BLOCK;<br />
		&nbsp; &nbsp; slabclass[power_largest].perslab = 1;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; /* for the test suite:&nbsp;&nbsp;faking of how much we&rsquo;ve already malloc&rsquo;d */<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;char *t_initial_malloc = getenv(&rdquo;T_MEMD_INITIAL_MALLOC&rdquo;);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (t_initial_malloc) {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;mem_malloced = atol(getenv(&rdquo;T_MEMD_INITIAL_MALLOC&rdquo;));<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">#ifndef DONT_PREALLOC_SLABS<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;char *pre_alloc = getenv(&rdquo;T_MEMD_SLABS_ALLOC&rdquo;);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (!pre_alloc || atoi(pre_alloc)) {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;slabs_preallocate(limit / POWER_BLOCK);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<br />
		&nbsp; &nbsp; }<br />
		#endif<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">由上可以看出，memcached的内存分配是有冗余的，当一个slab不能被它所拥有的chunk大小整除时，slab尾部剩余的空间就被丢弃了，如id40中，两个chunk占用了1009384字节，这个slab一共有1MB，那么就有39192字节被浪费了。</span></p>
<p><span style="font-size: 16px">Memcached使用这种方式来分配内存，是为了可以快速的通过item长度定位出slab的classid，有一点类似hash，因为item 的长度是可以计算的，比如一个item的长度是300字节，在1.2中就可以得到它应该保存在id7的slab中，因为按照上面的计算方法，id6的 chunk大小是252字节，id7的chunk大小是316字节，id8的chunk大小是396字节，表示所有252到316字节的item都应该保存在id7中。同理，在1.1中，也可以计算得到它出于256和512之间，应该放在chunk_size为512的id9中(32位系统）。</span></p>
<p><span style="font-size: 16px">Memcached初始化的时候，会初始化slab（前面可以看到，在main函数中调用了slabs_init()）。它会在 slabs_init()中检查一个常量DONT_PREALLOC_SLABS，如果这个没有被定义，说明使用预分配内存方式初始化slab，这样在所有已经定义过的slabclass中，每一个id创建一个slab。这样就表示，1.2在默认的环境中启动进程后要分配41MB的slab空间，在这个过程里，memcached的第二个内存冗余发生了，因为有可能一个id根本没有被使用过，但是它也默认申请了一个slab，每个slab会用掉1MB内存</span></p>
<p><span style="font-size: 16px">当一个slab用光后，又有新的item要插入这个id，那么它就会重新申请新的slab，申请新的slab时，对应id的slab链表就要增长，这个链表是成倍增长的，在函数grow_slab_list函数中，这个链的长度从1变成2，从2变成4，从4变成8&hellip;&hellip;：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">static int grow_slab_list (unsigned int id) {<br />
	&nbsp; &nbsp; slabclass_t *p = &amp;slabclass[id];<br />
	&nbsp; &nbsp; if (p-&gt;slabs == p-&gt;list_size) {<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;size_t new_size =&nbsp;&nbsp;p-&gt;list_size ? p-&gt;list_size * 2 : 16;&nbsp;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;void *new_list = realloc(p-&gt;slab_list, new_size*sizeof(void*));<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (new_list == 0) return 0;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;p-&gt;list_size = new_size;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;p-&gt;slab_list = new_list;<br />
	&nbsp; &nbsp; }<br />
	&nbsp; &nbsp; return 1;<br />
	}</span></div>
<p><span style="font-size: 16px">在定位item时，都是使用slabs_clsid函数，传入参数为item大小，返回值为classid，由这个过程可以看出，memcached的第三个内存冗余发生在保存item的过程中，item总是小于或等于chunk大小的，当item小于chunk大小时，就又发生了空间浪费。</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的NewHash算法</strong></span></p>
<p><span style="font-size: 16px">Memcached的item保存基于一个大的hash表，它的实际地址就是slab中的chunk偏移，但是它的定位是依靠对key做hash的结果，在primary_hashtable中找到的。在assoc.c和items.c中定义了所有的hash和item操作。</span></p>
<p><span style="font-size: 16px">Memcached使用了一个叫做NewHash的算法，它的效果很好，效率也很高。1.1和1.2的NewHash有一些不同，主要的实现方式还是一样的，1.2的hash函数是经过整理优化的，适应性更好一些。</span></p>
<p><span style="font-size: 16px">NewHash的原型参考：http://burtleburtle.net/bob/hash/evahash.html。数学家总是有点奇怪，呵呵～</span></p>
<p><span style="font-size: 16px">为了变换方便，定义了u4和u1两种数据类型，u4就是无符号的长整形，u1就是无符号char(0-255)。</span></p>
<p><span style="font-size: 16px">具体代码可以参考1.1和1.2源码包。</span></p>
<p><span style="font-size: 16px">注意这里的hashtable长度，1.1和1.2也是有区别的，1.1中定义了HASHPOWER常量为20，hashtable表长为 hashsize(HASHPOWER)，就是4MB（hashsize是一个宏，表示1右移n位），1.2中是变量16，即hashtable表长 65536：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">typedef&nbsp;&nbsp;unsigned long&nbsp;&nbsp;int&nbsp;&nbsp;ub4;&nbsp; &nbsp;/* unsigned 4-byte quantities */<br />
	typedef&nbsp;&nbsp;unsigned&nbsp; &nbsp;&nbsp; &nbsp; char ub1;&nbsp; &nbsp;/* unsigned 1-byte quantities */</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">#define hashsize(n) ((ub4)1&lt;&lt;(n))<br />
		#define hashmask(n) (hashsize(n)-1)</span></p>
</div>
<p><span style="font-size: 16px">在assoc_init()中，会对primary_hashtable做初始化，对应的hash操作包括：assoc_find()、 assoc_expand()、assoc_move_next_bucket()、assoc_insert()、assoc_delete()，对应于item的读写操作。其中assoc_find()是根据key和key长寻找对应的item地址的函数（注意在C中，很多时候都是同时直接传入字符串和字符串长度，而不是在函数内部做strlen），返回的是item结构指针，它的数据地址在slab中的某个chunk上。</span></p>
<p><span style="font-size: 16px">items.c是数据项的操作程序，每一个完整的item包括几个部分，在item_make_header()中定义为：</span></p>
<p><span style="font-size: 16px">key：键<br />
	nkey：键长<br />
	flags：用户定义的flag（其实这个flag在memcached中没有启用）<br />
	nbytes：值长（包括换行符号\r\n）<br />
	suffix：后缀Buffer<br />
	nsuffix：后缀长</span></p>
<p><span style="font-size: 16px">一个完整的item长度是键长＋值长＋后缀长＋item结构大小（32字节），item操作就是根据这个长度来计算slab的classid的。</span></p>
<p><span style="font-size: 16px">hashtable中的每一个桶上挂着一个双链表，item_init()的时候已经初始化了heads、tails、sizes三个数组为0，这三个数组的大小都为常量LARGEST_ID（默认为255，这个值需要配合factor来修改），在每次item_assoc()的时候，它会首先尝试从slab中获取一块空闲的chunk，如果没有可用的chunk，会在链表中扫描50次，以得到一个被LRU踢掉的item，将它unlink，然后将需要插入的item插入链表中。</span></p>
<p><span style="font-size: 16px">注意item的refcount成员。item被unlink之后只是从链表上摘掉，不是立刻就被free的，只是将它放到删除队列中（item_unlink_q()函数）。</span></p>
<p><span style="font-size: 16px">item对应一些读写操作，包括remove、update、replace，当然最重要的就是alloc操作。</span></p>
<p><span style="font-size: 16px">item还有一个特性就是它有过期时间，这是memcached的一个很有用的特性，很多应用都是依赖于memcached的item过期，比如 session存储、操作锁等。item_flush_expired()函数就是扫描表中的item，对过期的item执行unlink操作，当然这只是一个回收动作，实际上在get的时候还要进行时间判断：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/* expires items that are more recent than the oldest_live setting. */<br />
	void item_flush_expired() {<br />
	&nbsp; &nbsp; int i;&nbsp;&nbsp;<br />
	&nbsp; &nbsp; item *iter, *next;<br />
	&nbsp; &nbsp; if (! settings.oldest_live)<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return;&nbsp;<br />
	&nbsp; &nbsp; for (i = 0; i &lt; LARGEST_ID; i++) {<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;/* The LRU is sorted in decreasing time order, and an item&rsquo;s timestamp<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;* is never newer than its last access time, so we only need to walk<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;* back until we hit an item older than the oldest_live time.<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;* The oldest_live checking will auto-expire the remaining items.<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;*/<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;for (iter = heads[i]; iter != NULL; iter = next) {&nbsp;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (iter-&gt;time &gt;= settings.oldest_live) {<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; next = iter-&gt;next;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; if ((iter-&gt;it_flags &amp; ITEM_SLABBED) == 0) {&nbsp;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;item_unlink(iter);<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;} else {<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; /* We&rsquo;ve hit the first old item. Continue to the next queue. */<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; break;&nbsp;&nbsp;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;<br />
	&nbsp; &nbsp; }<br />
	}</span></div>
<p>&nbsp;</p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/* wrapper around assoc_find which does the lazy expiration/deletion logic */<br />
	item *get_item_notedeleted(char *key, size_t nkey, int *delete_locked) {<br />
	&nbsp; &nbsp; item *it = assoc_find(key, nkey);<br />
	&nbsp; &nbsp; if (delete_locked) *delete_locked = 0;<br />
	&nbsp; &nbsp; if (it &amp;&amp; (it-&gt;it_flags &amp; ITEM_DELETED)) {<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;/* it&rsquo;s flagged as delete-locked.&nbsp;&nbsp;let&rsquo;s see if that condition<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;is past due, and the 5-second delete_timer just hasn&rsquo;t<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;gotten to it yet&hellip; */<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (! item_delete_lock_over(it)) {<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (delete_locked) *delete_locked = 1;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;it = 0;&nbsp;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;<br />
	&nbsp; &nbsp; }<br />
	&nbsp; &nbsp; if (it &amp;&amp; settings.oldest_live &amp;&amp; settings.oldest_live &lt;= current_time &amp;&amp;<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;it-&gt;time &lt;= settings.oldest_live) {<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;item_unlink(it);<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;it = 0;&nbsp;<br />
	&nbsp; &nbsp; }<br />
	&nbsp; &nbsp; if (it &amp;&amp; it-&gt;exptime &amp;&amp; it-&gt;exptime &lt;= current_time) {<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;item_unlink(it);<br />
	&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;it = 0;&nbsp;<br />
	&nbsp; &nbsp; }<br />
	&nbsp; &nbsp; return it;<br />
	}</span></div>
<p><span style="font-size: 16px">Memcached的内存管理方式是非常精巧和高效的，它很大程度上减少了直接alloc系统内存的次数，降低函数开销和内存碎片产生几率，虽然这种方式会造成一些冗余浪费，但是这种浪费在大型系统应用中是微不足道的。<br />
	<img alt="" border="0" src="http://www.54np.com/docs/slabs.png" /></span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的理论参数计算方式</strong></span></p>
<p><span style="font-size: 16px">影响 memcached 工作的几个参数有：&nbsp;</span></p>
<p><span style="font-size: 16px">常量REALTIME_MAXDELTA 60*60*24*30<br />
	最大30天的过期时间</span></p>
<p><span style="font-size: 16px">conn_init()中的freetotal（=200）<br />
	最大同时连接数</span></p>
<p><span style="font-size: 16px">常量KEY_MAX_LENGTH 250<br />
	最大键长</span></p>
<p><span style="font-size: 16px">settings.factor（=1.25）<br />
	factor将影响chunk的步进大小</span></p>
<p><span style="font-size: 16px">settings.maxconns（=1024）<br />
	最大软连接</span></p>
<p><span style="font-size: 16px">settings.chunk_size（=48）<br />
	一个保守估计的key+value长度，用来生成id1中的chunk长度（1.2）。id1的chunk长度等于这个数值加上item结构体的长度（32），即默认的80字节。</span></p>
<p><span style="font-size: 16px">常量POWER_SMALLEST 1<br />
	最小classid（1.2）</span></p>
<p><span style="font-size: 16px">常量POWER_LARGEST 200<br />
	最大classid（1.2）</span></p>
<p><span style="font-size: 16px">常量POWER_BLOCK 1048576<br />
	默认slab大小</span></p>
<p><span style="font-size: 16px">常量CHUNK_ALIGN_BYTES (sizeof(void *))<br />
	保证chunk大小是这个数值的整数倍，防止越界（void *的长度在不同系统上不一样，在标准32位系统上是4）</span></p>
<p><span style="font-size: 16px">常量ITEM_UPDATE_INTERVAL 60<br />
	队列刷新间隔</span></p>
<p><span style="font-size: 16px">常量LARGEST_ID 255<br />
	最大item链表数（这个值不能比最大的classid小）</span></p>
<p><span style="font-size: 16px">变量hashpower（在1.1中是常量HASHPOWER）<br />
	决定hashtable的大小</span></p>
<p><span style="font-size: 16px">根据上面介绍的内容及参数设定，可以计算出的一些结果：</span></p>
<p><span style="font-size: 16px">1、在memcached中可以保存的item个数是没有软件上限的，之前我的100万的说法是错误的。<br />
	2、假设NewHash算法碰撞均匀，查找item的循环次数是item总数除以hashtable大小（由hashpower决定），是线性的。<br />
	3、Memcached限制了可以接受的最大item是1MB，大于1MB的数据不予理会。<br />
	4、Memcached的空间利用率和数据特性有很大的关系，又与DONT_PREALLOC_SLABS常量有关。 在最差情况下，有198个slab会被浪费（所有item都集中在一个slab中，199个id全部分配满）。&nbsp;</span></p>
<p><span style="font-size: 16px"><strong>◎Memcached的定长优化</strong></span></p>
<p><span style="font-size: 16px">根据上面几节的描述，多少对memcached有了一个比较深入的认识。在深入认识的基础上才好对它进行优化。</span></p>
<p><span style="font-size: 16px">Memcached本身是为变长数据设计的，根据数据特性，可以说它是&ldquo;面向大众&rdquo;的设计，但是很多时候，我们的数据并不是这样的&ldquo;普遍&rdquo;，典型的情况中，一种是非均匀分布，即数据长度集中在几个区域内（如保存用户 Session）；另一种更极端的状态是等长数据（如定长键值，定长数据，多见于访问、在线统计或执行锁）。</span></p>
<p><span style="font-size: 16px">这里主要研究一下定长数据的优化方案（1.2），集中分布的变长数据仅供参考，实现起来也很容易。</span></p>
<p><span style="font-size: 16px">解决定长数据，首先需要解决的是slab的分配问题，第一个需要确认的是我们不需要那么多不同chunk长度的slab，为了最大限度地利用资源，最好chunk和item等长，所以首先要计算item长度。</span></p>
<p><span style="font-size: 16px">在之前已经有了计算item长度的算法，需要注意的是，除了字符串长度外，还要加上item结构的长度32字节。</span></p>
<p><span style="font-size: 16px">假设我们已经计算出需要保存200字节的等长数据。</span></p>
<p><span style="font-size: 16px">接下来是要修改slab的classid和chunk长度的关系。在原始版本中，chunk长度和classid是有对应关系的，现在如果把所有的 chunk都定为200个字节，那么这个关系就不存在了，我们需要重新确定这二者的关系。一种方法是，整个存储结构只使用一个固定的id，即只使用199 个槽中的1个，在这种条件下，就一定要定义DONT_PREALLOC_SLABS来避免另外的预分配浪费。另一种方法是建立一个hash关系，来从 item确定classid，不能使用长度来做键，可以使用key的NewHash结果等不定数据，或者直接根据key来做hash（定长数据的key也一定等长）。这里简单起见，选择第一种方法，这种方法的不足之处在于只使用一个id，在数据量非常大的情况下，slab链会很长（因为所有数据都挤在一条链上了），遍历起来的代价比较高。</span></p>
<p><span style="font-size: 16px">前面介绍了三种空间冗余，设置chunk长度等于item长度，解决了第一种空间浪费问题，不预申请空间解决了第二种空间浪费问题，那么对于第一种问题（slab内剩余）如何解决呢，这就需要修改POWER_BLOCK常量，使得每一个slab大小正好等于chunk长度的整数倍，这样一个slab 就可以正好划分成n个chunk。这个数值应该比较接近1MB，过大的话同样会造成冗余，过小的话会造成次数过多的alloc，根据chunk长度为 200，选择1000000作为POWER_BLOCK的值，这样一个slab就是100万字节，不是1048576。三个冗余问题都解决了，空间利用率会大大提升。</span></p>
<p><span style="font-size: 16px">修改 slabs_clsid 函数，让它直接返回一个定值（比如 1 ）：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">unsigned int slabs_clsid(size_t size) {<br />
	&nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
	}</span></div>
<p><span style="font-size: 16px">修改slabs_init函数，去掉循环创建所有classid属性的部分，直接添加slabclass[1]：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">slabclass[1].size = 200;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //每chunk200字节<br />
	slabclass[1].perslab = 5000;&nbsp; &nbsp; &nbsp; &nbsp; //1000000/200</span></div>
<p><span style="font-size: 16px"><strong>◎Memcached客户端</strong></span></p>
<p><span style="font-size: 16px">Memcached是一个服务程序，使用的时候可以根据它的协议，连接到memcached服务器上，发送命令给服务进程，就可以操作上面的数据。为了方便使用，memcached有很多个客户端程序可以使用，对应于各种语言，有各种语言的客户端。基于C语言的有libmemcache、 APR_Memcache；基于Perl的有Cache::Memcached；另外还有Python、Ruby、Java、C#等语言的支持。PHP的客户端是最多的，不光有mcache和PECL memcache两个扩展，还有大把的由PHP编写的封装类，下面介绍一下在PHP中使用memcached的方法：</span></p>
<p><span style="font-size: 16px">mcache扩展是基于libmemcache再封装的。libmemcache一直没有发布stable版本，目前版本是1.4.0-rc2，可以在这里找到。libmemcache有一个很不好的特性，就是会向stderr写很多错误信息，一般的，作为lib使用的时候，stderr一般都会被定向到其它地方，比如Apache的错误日志，而且libmemcache会自杀，可能会导致异常，不过它的性能还是很好的。</span></p>
<p><span style="font-size: 16px">mcache扩展最后更新到1.2.0-beta10，作者大概是离职了，不光停止更新，连网站也打不开了（~_~），只能到其它地方去获取这个不负责的扩展了。解压后安装方法如常：phpize &amp; configure &amp; make &amp; make install，一定要先安装libmemcache。使用这个扩展很简单：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px"><span><span>&lt;?php<br />
	$mc&nbsp;</span><span>=&nbsp;</span><span>memcache</span><span>();&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;创建一个memcache连接对象，注意这里不是用new！</span><br />
	<span>$mc</span><span>-&gt;</span><span>add_server</span><span>(</span><span>&lsquo;localhost&rsquo;</span><span>,&nbsp;</span><span>11211</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;添加一个服务进程</span><br />
	<span>$mc</span><span>-&gt;</span><span>add_server</span><span>(</span><span>&lsquo;localhost&rsquo;</span><span>,&nbsp;</span><span>11212</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;添加第二个服务进程</span><br />
	<span>$mc</span><span>-&gt;</span><span>set</span><span>(</span><span>&lsquo;key1&prime;</span><span>,&nbsp;</span><span>&lsquo;Hello&rsquo;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;写入key1&nbsp;=&gt;&nbsp;Hello</span><br />
	<span>$mc</span><span>-&gt;</span><span>set</span><span>(</span><span>&lsquo;key2&prime;</span><span>,&nbsp;</span><span>&lsquo;World&rsquo;</span><span>,&nbsp;</span><span>10</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;写入key2&nbsp;=&gt;&nbsp;World，10秒过期</span><br />
	<span>$mc</span><span>-&gt;</span><span>set</span><span>(</span><span>&lsquo;arr1&prime;</span><span>,&nbsp;array(</span><span>&lsquo;Hello&rsquo;</span><span>,&nbsp;</span><span>&lsquo;World&rsquo;</span><span>));&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;写入一个数组</span><br />
	<span>$key1&nbsp;</span><span>=&nbsp;</span><span>$mc</span><span>-&gt;</span><span>get</span><span>(</span><span>&lsquo;key1&prime;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;获取&rsquo;key1&prime;的值，赋给$key1</span><br />
	<span>$key2&nbsp;</span><span>=&nbsp;</span><span>$mc</span><span>-&gt;</span><span>get</span><span>(</span><span>&lsquo;key2&prime;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;获取&rsquo;key2&prime;的值，赋给$key2，如果超过10秒，就取不到了</span><br />
	<span>$arr1&nbsp;</span><span>=&nbsp;</span><span>$mc</span><span>-&gt;</span><span>get</span><span>(</span><span>&lsquo;arr1&prime;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;获取&rsquo;arr1&prime;数组</span><br />
	<span>$mc</span><span>-&gt;</span><span>delete</span><span>(</span><span>&lsquo;arr1&prime;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;删除&rsquo;arr1&prime;</span><br />
	<span>$mc</span><span>-&gt;</span><span>flush_all</span><span>();&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;删掉所有数据</span><br />
	<span>$stats&nbsp;</span><span>=&nbsp;</span><span>$mc</span><span>-&gt;</span><span>stats</span><span>();&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;获取服务器信息</span><br />
	<span>var_dump</span><span>(</span><span>$stats</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;服务器信息是一个数组</span><br />
	<span>?&gt;</span></span></span></div>
<p><span style="font-size: 16px">这个扩展的好处是可以很方便地实现分布式存储和负载均衡，因为它可以添加多个服务地址，数据在保存的时候是会根据hash结果定位到某台服务器上的，这也是libmemcache的特性。libmemcache支持集中hash方式，包括CRC32、ELF和Perl hash。</span></p>
<p><span style="font-size: 16px">PECL memcache是PECL发布的扩展，目前最新版本是2.1.0，可以在pecl网站得到。memcache扩展的使用方法可以在新一些的PHP手册中找到，它和mcache很像，真的很像：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px"><span><span>&lt;?php </span></span></span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px"><span><span>$memcache&nbsp;<span>=&nbsp;new&nbsp;</span><span>Memcache</span><span>;</span><br />
		<span>$memcache</span><span>-&gt;</span><span>connect</span><span>(</span><span>&lsquo;localhost&rsquo;</span><span>,&nbsp;</span><span>11211</span><span>)&nbsp;or&nbsp;die&nbsp;(</span><span>&ldquo;Could&nbsp;not&nbsp;connect&rdquo;</span><span>);</span></span></span></span></p>
<p><span style="font-size: 16px"><span><span><span>$version&nbsp;</span><span>=&nbsp;</span><span>$memcache</span><span>-&gt;</span><span>getVersion</span><span>();<br />
		echo&nbsp;</span><span>&ldquo;Server&rsquo;s&nbsp;version:&nbsp;&rdquo;</span><span>.</span><span>$version</span><span>.</span><span>&ldquo;n&rdquo;</span><span>;</span></span></span></span></p>
<p><span style="font-size: 16px"><span><span><span>$tmp_object&nbsp;</span><span>=&nbsp;new&nbsp;</span><span>stdClass</span><span>;</span><br />
		<span>$tmp_object</span><span>-&gt;</span><span>str_attr&nbsp;</span><span>=&nbsp;</span><span>&lsquo;test&rsquo;</span><span>;</span><br />
		<span>$tmp_object</span><span>-&gt;</span><span>int_attr&nbsp;</span><span>=&nbsp;</span><span>123</span><span>;</span></span></span></span></p>
<p><span style="font-size: 16px"><span><span><span>$memcache</span><span>-&gt;</span><span>set</span><span>(</span><span>&lsquo;key&rsquo;</span><span>,&nbsp;</span><span>$tmp_object</span><span>,&nbsp;</span><span>false</span><span>,&nbsp;</span><span>10</span><span>)&nbsp;or&nbsp;die&nbsp;(</span><span>&ldquo;Failed&nbsp;to&nbsp;save&nbsp;data&nbsp;at&nbsp;the&nbsp;server&rdquo;</span><span>);<br />
		echo&nbsp;</span><span>&ldquo;Store&nbsp;data&nbsp;in&nbsp;the&nbsp;cache&nbsp;(data&nbsp;will&nbsp;expire&nbsp;in&nbsp;10&nbsp;seconds)n&rdquo;</span><span>;</span></span></span></span></p>
<p><span style="font-size: 16px"><span><span><span>$get_result&nbsp;</span><span>=&nbsp;</span><span>$memcache</span><span>-&gt;</span><span>get</span><span>(</span><span>&lsquo;key&rsquo;</span><span>);<br />
		echo&nbsp;</span><span>&ldquo;Data&nbsp;from&nbsp;the&nbsp;cache:n&rdquo;</span><span>;</span></span></span></span></p>
<p><span style="font-size: 16px"><span><span><span>var_dump</span><span>(</span><span>$get_result</span><span>);</span></span></span></span></p>
<p><span style="font-size: 16px"><span><span><span>?&gt;</span></span></span></span></p>
</div>
<p><span style="font-size: 16px">这个扩展是使用php的stream直接连接memcached服务器并通过socket发送命令的。它不像libmemcache那样完善，也不支持add_server这种分布操作，但是因为它不依赖其它的外界程序，兼容性要好一些，也比较稳定。至于效率，差别不是很大。</span></p>
<p><span style="font-size: 16px">另外，有很多的PHP class可以使用，比如MemcacheClient.inc.php，phpclasses.org上可以找到很多，一般都是对perl client API的再封装，使用方式很像。</span></p>
<p><span style="font-size: 16px">◎BSM_Memcache</span></p>
<p><span style="font-size: 16px">从C client来说，APR_Memcache是一个很成熟很稳定的client程序，支持线程锁和原子级操作，保证运行的稳定性。不过它是基于APR的（APR将在最后一节介绍），没有libmemcache的应用范围广，目前也没有很多基于它开发的程序，现有的多是一些Apache Module，因为它不能脱离APR环境运行。但是APR倒是可以脱离Apache单独安装的，在APR网站上可以下载APR和APR-util，不需要有Apache，可以直接安装，而且它是跨平台的。</span></p>
<p><span style="font-size: 16px">BSM_Memcache是我在BS.Magic项目中开发的一个基于APR_Memcache的PHP扩展，说起来有点拗口，至少它把APR扯进了PHP扩展中。这个程序很简单，也没做太多的功能，只是一种形式的尝试，它支持服务器分组。</span></p>
<p><span style="font-size: 16px">和mcache扩展支持多服务器分布存储不同，BSM_Memcache支持多组服务器，每一组内的服务器还是按照hash方式来分布保存数据，但是两个组中保存的数据是一样的，也就是实现了热备，它不会因为一台服务器发生单点故障导致数据无法获取，除非所有的服务器组都损坏（例如机房停电）。当然实现这个功能的代价就是性能上的牺牲，在每次添加删除数据的时候都要扫描所有的组，在get数据的时候会随机选择一组服务器开始轮询，一直到找到数据为止，正常情况下一次就可以获取得到。</span></p>
<p><span style="font-size: 16px">BSM_Memcache只支持这几个函数：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">zend_function_entry bsm_memcache_functions[] =<br />
	{<br />
	&nbsp; &nbsp; PHP_FE(mc_get,&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; NULL)<br />
	&nbsp; &nbsp; PHP_FE(mc_set,&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; NULL)<br />
	&nbsp; &nbsp; PHP_FE(mc_del,&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; NULL)<br />
	&nbsp; &nbsp; PHP_FE(mc_add_group,&nbsp; &nbsp; NULL)<br />
	&nbsp; &nbsp; PHP_FE(mc_add_server,&nbsp; &nbsp;NULL)<br />
	&nbsp; &nbsp; PHP_FE(mc_shutdown,&nbsp; &nbsp;&nbsp;&nbsp;NULL)<br />
	&nbsp; &nbsp; {NULL, NULL, NULL}<br />
	};</span></div>
<p><span style="font-size: 16px">mc_add_group函数返回一个整形（其实应该是一个object，我偷懒了~_~）作为组ID，mc_add_server的时候要提供两个参数，一个是组ID，一个是服务器地址（ADDR<img align="absMiddle" alt="" border="0" src="http://www.phpx.com/happy/images/smilies/tongue.gif" />ORT）。</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Add a server group<br />
	*/<br />
	PHP_FUNCTION(mc_add_group)<br />
	{<br />
	&nbsp; &nbsp; apr_int32_t group_id;<br />
	&nbsp; &nbsp; apr_status_t rv;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (0 != ZEND_NUM_ARGS())<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;WRONG_PARAM_COUNT;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;RETURN_NULL();<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; group_id = free_group_id();<br />
		&nbsp; &nbsp; if (-1 == group_id)<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;RETURN_FALSE;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; apr_memcache_t *mc;<br />
		&nbsp; &nbsp; rv = apr_memcache_create(p, MAX_G_SERVER, 0, &amp;mc);</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; add_group(group_id, mc);</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; RETURN_DOUBLE(group_id);<br />
		}</span></p>
</div>
<p>&nbsp;</p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Add a server into group<br />
	*/<br />
	PHP_FUNCTION(mc_add_server)<br />
	{<br />
	&nbsp; &nbsp; apr_status_t rv;<br />
	&nbsp; &nbsp; apr_int32_t group_id;<br />
	&nbsp; &nbsp; double g;<br />
	&nbsp; &nbsp; char *srv_str;<br />
	&nbsp; &nbsp; int srv_str_l;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (2 != ZEND_NUM_ARGS())<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;WRONG_PARAM_COUNT;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, &ldquo;ds&rdquo;, &amp;g, &amp;srv_str, &amp;srv_str_l) == FAILURE)<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;RETURN_FALSE;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; group_id = (apr_int32_t) g;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (-1 == is_validate_group(group_id))<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;RETURN_FALSE;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; char *host, *scope;<br />
		&nbsp; &nbsp; apr_port_t port;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; rv = apr_parse_addr_port(&amp;host, &amp;scope, &amp;port, srv_str, p);<br />
		&nbsp; &nbsp; if (APR_SUCCESS == rv)<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;// Create this server object<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;apr_memcache_server_t *st;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;rv = apr_memcache_server_create(p, host, port, 0, 64, 1024, 600, &amp;st);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (APR_SUCCESS == rv)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (NULL == mc_groups[group_id])<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; RETURN_FALSE;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;// Add server<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;rv = apr_memcache_add_server(mc_groups[group_id], st);</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (APR_SUCCESS == rv)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; RETURN_TRUE;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; RETURN_FALSE;<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">在set和del数据的时候，要循环所有的组：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Store item into all groups<br />
	*/<br />
	PHP_FUNCTION(mc_set)<br />
	{<br />
	&nbsp; &nbsp; char *key, *value;<br />
	&nbsp; &nbsp; int key_l, value_l;<br />
	&nbsp; &nbsp; double ttl = 0;<br />
	&nbsp; &nbsp; double set_ct = 0;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (2 != ZEND_NUM_ARGS())<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;WRONG_PARAM_COUNT;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, &ldquo;ss|d&rdquo;, &amp;key, &amp;key_l, &amp;value, &amp;value_l, ttl) == FAILURE)<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;RETURN_FALSE;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; // Write data into every object<br />
		&nbsp; &nbsp; apr_int32_t i = 0;<br />
		&nbsp; &nbsp; if (ttl &lt; 0)<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;ttl = 0;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; apr_status_t rv;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; for (i = 0; i &lt; MAX_GROUP; i++)<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (0 == is_validate_group(i))<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;// Write it!<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;rv = apr_memcache_add(mc_groups[i], key, value, value_l, (apr_uint32_t) ttl, 0);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (APR_SUCCESS == rv)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; set_ct++;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; RETURN_DOUBLE(set_ct);<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">在mc_get中，首先要随机选择一个组，然后从这个组开始轮询：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Fetch a item from a random group<br />
	*/<br />
	PHP_FUNCTION(mc_get)<br />
	{&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
	&nbsp; &nbsp; char *key, *value = NULL;<br />
	&nbsp; &nbsp; int key_l;<br />
	&nbsp; &nbsp; apr_size_t value_l;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (1 != ZEND_NUM_ARGS())<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;WRONG_PARAM_COUNT;<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, &ldquo;s&rdquo;, &amp;key, &amp;key_l) == FAILURE)<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;RETURN_MULL();<br />
		&nbsp; &nbsp; }<br />
		&nbsp; &nbsp;&nbsp;<br />
		&nbsp; &nbsp; // I will try &hellip;<br />
		&nbsp; &nbsp; // Random read<br />
		&nbsp; &nbsp; apr_int32_t curr_group_id = random_group();<br />
		&nbsp; &nbsp; apr_int32_t i = 0;<br />
		&nbsp; &nbsp; apr_int32_t try = 0;<br />
		&nbsp; &nbsp; apr_uint32_t flag;<br />
		&nbsp; &nbsp; apr_memcache_t *oper;<br />
		&nbsp; &nbsp; apr_status_t rv;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; for (i = 0; i &lt; MAX_GROUP; i++)<br />
		&nbsp; &nbsp; {<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;try = i + curr_group_id;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;try = try % MAX_GROUP;<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if (0 == is_validate_group(try))<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;// Get a value<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;oper = mc_groups[try];<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;rv = apr_memcache_getp(mc_groups[try], p, (const char *) key, &amp;value, &amp;value_l, 0);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (APR_SUCCESS == rv)<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; RETURN_STRING(value, 1);<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<br />
		&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<br />
		&nbsp; &nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; RETURN_FALSE;<br />
		}</span></p>
</div>
<p>&nbsp;</p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px">/**<br />
	* Random group id<br />
	* For mc_get()<br />
	*/<br />
	apr_int32_t random_group()<br />
	{<br />
	&nbsp; &nbsp; struct timeval tv;<br />
	&nbsp; &nbsp; struct timezone tz;<br />
	&nbsp; &nbsp; int usec;</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">&nbsp; &nbsp; gettimeofday(&amp;tv, &amp;tz);</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; usec = tv.tv_usec;</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; int curr = usec % count_group();</span></p>
<p><span style="font-size: 16px">&nbsp; &nbsp; return (apr_int32_t) curr;<br />
		}</span></p>
</div>
<p><span style="font-size: 16px">BSM_Memcache的使用方式和其它的client类似：</span></p>
<div class="smalltxt">
<div><span style="font-size: 16px">CODE:</span></div>
<div>&nbsp;</div>
</div>
<div class="altbg2"><span style="font-size: 16px"><span><span>&lt;?php<br />
	$g1&nbsp;</span><span>=&nbsp;</span><span>mc_add_group</span><span>();&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;添加第一个组</span><br />
	<span>$g2&nbsp;</span><span>=&nbsp;</span><span>mc_add_group</span><span>();&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;添加第二个组</span><br />
	<span>mc_add_server</span><span>(</span><span>$g1</span><span>,&nbsp;</span><span>&lsquo;localhost:11211&prime;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;在第一个组中添加第一台服务器</span><br />
	<span>mc_add_server</span><span>(</span><span>$g1</span><span>,&nbsp;</span><span>&lsquo;localhost:11212&prime;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;在第一个组中添加第二台服务器</span><br />
	<span>mc_add_server</span><span>(</span><span>$g2</span><span>,&nbsp;</span><span>&lsquo;10.0.0.16:11211&prime;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;在第二个组中添加第一台服务器</span><br />
	<span>mc_add_server</span><span>(</span><span>$g2</span><span>,&nbsp;</span><span>&lsquo;10.0.0.17:11211&prime;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;在第二个组中添加第二台服务器 </span></span></span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 16px"><span><span><span>mc_set</span><span>(</span><span>&lsquo;key&rsquo;</span><span>,&nbsp;</span><span>&lsquo;Hello&rsquo;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;写入数据</span><br />
		<span>$key&nbsp;</span><span>=&nbsp;</span><span>mc_get</span><span>(</span><span>&lsquo;key&rsquo;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;读出数据</span><br />
		<span>mc_del</span><span>(</span><span>&lsquo;key&rsquo;</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;删除数据</span><br />
		<span>mc_shutdown</span><span>();&nbsp;&nbsp;&nbsp;&nbsp;</span><span>//&nbsp;关闭所有组</span><br />
		<span>?&gt;</span></span></span></span></p>
</div>
<p><span style="font-size: 16px">APR_Memcache的相关资料可以在这里找到，BSM_Memcache可以在本站下载。</span></p>
<p><span style="font-size: 16px"><strong>◎APR环境介绍</strong></span></p>
<p><span style="font-size: 16px">APR的全称：Apache Portable Runtime。它是Apache软件基金会创建并维持的一套跨平台的C语言库。它从Apache httpd1.x中抽取出来并独立于httpd之外，Apache httpd2.x就是建立在APR上。APR提供了很多方便的API接口可供使用，包括如内存池、字符串操作、网络、数组、hash表等实用的功能。开发 Apache2 Module要接触很多APR函数，当然APR可以独立安装独立使用，可以用来写自己的应用程序，不一定是Apache httpd的相关开发。</span></p>
<p><span style="font-size: 16px"><strong>◎后记</strong></span></p>
<p><span style="font-size: 16px">这是我在农历丙戌年（我的本命年）的最后一篇文章，由于Memcached的内涵很多，仓促整理一定有很多遗漏和错误。感谢新浪网提供的研究机会，感谢部门同事的帮助。</span></p>
<p>&nbsp;</p>
<p><span style="font-size: 16px">转自：<a href="http://blog.developers.api.sina.com.cn/?p=124">http://blog.developers.api.sina.com.cn/?p=124</a></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/memcached%e6%b7%b1%e5%ba%a6%e5%88%86%e6%9e%90%e8%bd%ac%e8%bd%bd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Debian中文参考手册</title>
		<link>http://www.411c.com/debian%e4%b8%ad%e6%96%87%e5%8f%82%e8%80%83%e6%89%8b%e5%86%8c/</link>
		<comments>http://www.411c.com/debian%e4%b8%ad%e6%96%87%e5%8f%82%e8%80%83%e6%89%8b%e5%86%8c/#comments</comments>
		<pubDate>Thu, 10 May 2012 12:23:07 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[DEBIAN]]></category>
		<category><![CDATA[LINUX技术]]></category>
		<category><![CDATA[Debian]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=450</guid>
		<description><![CDATA[<p><span style="font-size: 16px">&#160;&#160;&#160;&#160;&#160;&#160; 最近在学习debian，在debian官网上找了一份非常详细的中文参考手册，在这里把地址分享给大家，我也会在学习中通过这个网站去找我需要的命令解决遇到的问题：<br />
	&#160;&#160;&#160;&#160; &#160;<a href="http://www.debian.org/doc/manuals/debian-reference/">http://www.debian.org/doc/manuals/debian-reference/</a></span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最近在学习debian，在debian官网上找了一份非常详细的中文参考手册，在这里把地址分享给大家，我也会在学习中通过这个网站去找我需要的命令解决遇到的问题：<br />
	&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<a href="http://www.debian.org/doc/manuals/debian-reference/">http://www.debian.org/doc/manuals/debian-reference/</a></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/debian%e4%b8%ad%e6%96%87%e5%8f%82%e8%80%83%e6%89%8b%e5%86%8c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mysql导出表的一个字段数据并去除重复数据</title>
		<link>http://www.411c.com/mysql%e5%af%bc%e5%87%ba%e8%a1%a8%e7%9a%84%e4%b8%80%e4%b8%aa%e5%ad%97%e6%ae%b5%e6%95%b0%e6%8d%ae%e5%b9%b6%e5%8e%bb%e9%99%a4%e9%87%8d%e5%a4%8d%e6%95%b0%e6%8d%ae/</link>
		<comments>http://www.411c.com/mysql%e5%af%bc%e5%87%ba%e8%a1%a8%e7%9a%84%e4%b8%80%e4%b8%aa%e5%ad%97%e6%ae%b5%e6%95%b0%e6%8d%ae%e5%b9%b6%e5%8e%bb%e9%99%a4%e9%87%8d%e5%a4%8d%e6%95%b0%e6%8d%ae/#comments</comments>
		<pubDate>Wed, 09 May 2012 07:09:48 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[MYSQL]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=447</guid>
		<description><![CDATA[<p><span style="font-size:16px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; 今天，公司让我把一个项目里面的数据库的某个表的一个字段导出来，而且不要重复的数据，后来找了一些方法，做了改进，现在把我实现这个操作的SQL语句分享给大家</span></p>
<p><span style="font-size:16px;">select a into outfile &#39;/tmp/abc.txt&#39; LINES TERMINATED BY &#39;\n&#39; from tablename;<br />
	在这里：a是我们要查询的字段<br />
	&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; /tmp/abc.txt是我们保存查询结果的目录和文件<br />
	&#160;&#160; &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#160; tablename是我们要使用的表名</span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size:16px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天，公司让我把一个项目里面的数据库的某个表的一个字段导出来，而且不要重复的数据，后来找了一些方法，做了改进，现在把我实现这个操作的SQL语句分享给大家</span></p>
<p><span style="font-size:16px;">select a into outfile &#39;/tmp/abc.txt&#39; LINES TERMINATED BY &#39;\n&#39; from tablename;<br />
	在这里：a是我们要查询的字段<br />
	&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /tmp/abc.txt是我们保存查询结果的目录和文件<br />
	&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; tablename是我们要使用的表名</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/mysql%e5%af%bc%e5%87%ba%e8%a1%a8%e7%9a%84%e4%b8%80%e4%b8%aa%e5%ad%97%e6%ae%b5%e6%95%b0%e6%8d%ae%e5%b9%b6%e5%8e%bb%e9%99%a4%e9%87%8d%e5%a4%8d%e6%95%b0%e6%8d%ae/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iptables 对每个IP进行限速</title>
		<link>http://www.411c.com/iptables%e5%af%b9%e6%af%8f%e4%b8%aa%e6%9d%a5%e8%ae%bfip%e8%bf%9b%e8%a1%8c%e9%99%90%e9%80%9f/</link>
		<comments>http://www.411c.com/iptables%e5%af%b9%e6%af%8f%e4%b8%aa%e6%9d%a5%e8%ae%bfip%e8%bf%9b%e8%a1%8c%e9%99%90%e9%80%9f/#comments</comments>
		<pubDate>Mon, 07 May 2012 07:55:31 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[LINUX技术]]></category>
		<category><![CDATA[Iptable]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=442</guid>
		<description><![CDATA[<p><span style="font-size: 16px">限制每个IP进入服务器的数据包：<br />
	iptables -A INPUT -s 服务器IP地址 -m limit --limit 200/s -j ACCEPT<br />
	iptables -A INPUT -s 192.168.1.100 -m limit --limit 200/s -j ACCEPT</span></p>
<p><span style="font-size: 16px">限制每个客户从服务器下载的数据包：<br />
	iptables -A OUTPUT -s 服务器IP地址 -m limit --limit 200/s -j ACCEPT<br />
	iptables -A OUTPUT -s 192.168.1.100 -m limit --limit 200/s -j ACCEPT</span></p>
<p><span style="font-size: 16px">这里的192.168.1.100是服务器的IP地址，大家可以根据自己的需要修改成自己的服务器的IP地址</span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px">限制每个IP进入服务器的数据包：<br />
	iptables -A INPUT -s 服务器IP地址 -m limit &#8211;limit 200/s -j ACCEPT<br />
	iptables -A INPUT -s 192.168.1.100 -m limit &#8211;limit 200/s -j ACCEPT</span></p>
<p><span style="font-size: 16px">限制每个客户从服务器下载的数据包：<br />
	iptables -A OUTPUT -s 服务器IP地址 -m limit &#8211;limit 200/s -j ACCEPT<br />
	iptables -A OUTPUT -s 192.168.1.100 -m limit &#8211;limit 200/s -j ACCEPT</span></p>
<p><span style="font-size: 16px">这里的192.168.1.100是服务器的IP地址，大家可以根据自己的需要修改成自己的服务器的IP地址</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/iptables%e5%af%b9%e6%af%8f%e4%b8%aa%e6%9d%a5%e8%ae%bfip%e8%bf%9b%e8%a1%8c%e9%99%90%e9%80%9f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mysql忘记密码从新设置</title>
		<link>http://www.411c.com/mysql%e5%bf%98%e8%ae%b0%e5%af%86%e7%a0%81%e4%bb%8e%e6%96%b0%e8%ae%be%e7%bd%ae/</link>
		<comments>http://www.411c.com/mysql%e5%bf%98%e8%ae%b0%e5%af%86%e7%a0%81%e4%bb%8e%e6%96%b0%e8%ae%be%e7%bd%ae/#comments</comments>
		<pubDate>Sat, 05 May 2012 16:38:28 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[MYSQL]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=440</guid>
		<description><![CDATA[<p><span style="font-size: 16px">1、关闭mysql<br />
	&#160;&#160;&#160;&#160;&#160; service mysql stop</span></p>
<p><span style="font-size: 16px">2、启动mysql不加载授权表<br />
	&#160;&#160;&#160;&#160;&#160; /usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my.cnf --skip-grant-tables &#38;<br />
	&#160;&#160;&#160;&#160;&#160; 这时进入mysql不需要密码<br />
	&#160;&#160;&#160;&#160;&#160; #mysql -uroot<br />
	&#160;&#160;&#160;&#160;&#160; mysql&#62;use mysql;<br />
	&#160;&#160;&#160;&#160;&#160; mysql&#62;update user set Password=password(&#39;123456&#39;) where User=&#39;root&#39;;<br />
	&#160;&#160;&#160;&#160;&#160; 把mysql密码修改为123456</span></p>
<p><span style="font-size: 16px">3、重启mysql<br />
	&#160;&#160;&#160;&#160;&#160; service mysql restart<br />
	&#160;&#160;&#160;&#160;&#160; 用新密码进入mysql<br />
	&#160;&#160;&#160;&#160;&#160; ＃mysql -uroot -p123456</span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px">1、关闭mysql<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; service mysql stop</span></p>
<p><span style="font-size: 16px">2、启动mysql不加载授权表<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /usr/local/mysql/bin/mysqld_safe &#8211;defaults-file=/etc/my.cnf &#8211;skip-grant-tables &amp;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这时进入mysql不需要密码<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #mysql -uroot<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mysql&gt;use mysql;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mysql&gt;update user set Password=password(&#39;123456&#39;) where User=&#39;root&#39;;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把mysql密码修改为123456</span></p>
<p><span style="font-size: 16px">3、重启mysql<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; service mysql restart<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用新密码进入mysql<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ＃mysql -uroot -p123456</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/mysql%e5%bf%98%e8%ae%b0%e5%af%86%e7%a0%81%e4%bb%8e%e6%96%b0%e8%ae%be%e7%bd%ae/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>巧用netstat——查看当前访问最多的IP</title>
		<link>http://www.411c.com/%e5%b7%a7%e7%94%a8netstat-%e6%9f%a5%e7%9c%8b%e5%bd%93%e5%89%8d%e8%ae%bf%e9%97%ae%e6%9c%80%e5%a4%9a%e7%9a%84ip/</link>
		<comments>http://www.411c.com/%e5%b7%a7%e7%94%a8netstat-%e6%9f%a5%e7%9c%8b%e5%bd%93%e5%89%8d%e8%ae%bf%e9%97%ae%e6%9c%80%e5%a4%9a%e7%9a%84ip/#comments</comments>
		<pubDate>Sat, 05 May 2012 16:25:00 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[LINUX技术]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=437</guid>
		<description><![CDATA[<p><span style="font-size: 16px"><span style="color: #0000ff">通过netstat查看当前访问服务器最多的IP,非常实用，现在分享给大家</span>：<br />
	netstat -ntu &#124; tail -n +3 &#124; awk &#39;{ print $5}&#39; &#124; cut -d : -f 1 &#124; sort &#124; uniq -c&#124; sort -n -r &#124; head -n 3<br />
	&#160;&#160;&#160; awk &#39;{ print $5}&#39;：取netstat -ntu 出来值的第5列<br />
	&#160;&#160; &#160;cut -d : -f 1 ：取netstat -ntu 出来值的第5列的IP地址<br />
	&#160; &#160; sort：对IP部分进行排序。<br />
	&#160;&#160; &#160;uniq -c：打印每一重复行出现的次数。（并去掉重复行）<br />
	&#160;&#160;&#160; sort -n -r：按照重复行出现的次序倒序排列。<br />
	&#160;&#160;&#160;&#160;head -n 3：取排在前3位的IP 。</span></p>
<p><span style="font-size: 16px"># netstat -ntu &#124; tail -n +3&#124;awk &#39;{ print $5}&#39; &#124; cut -d : -f 1 &#124; sort &#124; uniq -c&#124; sort -n -r &#124; head -n 3<br />
	&#160;&#160;&#160;&#160; <span style="color: #ff0000">21</span> <span style="color: #00ff00">127.0.0.1</span><br />
	&#160;&#160;&#160;&#160; <span style="color: #ff0000">14</span> <span style="color: #00ff00">183.204.240.36</span><br />
	&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #ff0000">5</span>&#160;&#160;<span style="color: #00ff00">120.33.53.133</span></span></p>
<p><span style="font-size: 16px">打印出来的结果，红色显示次数，绿色显示的是IP地址</span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px"><span style="color: #0000ff">通过netstat查看当前访问服务器最多的IP,非常实用，现在分享给大家</span>：<br />
	netstat -ntu | tail -n +3 | awk &#39;{ print $5}&#39; | cut -d : -f 1 | sort | uniq -c| sort -n -r | head -n 3<br />
	&nbsp;&nbsp;&nbsp; awk &#39;{ print $5}&#39;：取netstat -ntu 出来值的第5列<br />
	&nbsp;&nbsp; &nbsp;cut -d : -f 1 ：取netstat -ntu 出来值的第5列的IP地址<br />
	&nbsp; &nbsp; sort：对IP部分进行排序。<br />
	&nbsp;&nbsp; &nbsp;uniq -c：打印每一重复行出现的次数。（并去掉重复行）<br />
	&nbsp;&nbsp;&nbsp; sort -n -r：按照重复行出现的次序倒序排列。<br />
	&nbsp;&nbsp;&nbsp;&nbsp;head -n 3：取排在前3位的IP 。</span></p>
<p><span style="font-size: 16px"># netstat -ntu | tail -n +3|awk &#39;{ print $5}&#39; | cut -d : -f 1 | sort | uniq -c| sort -n -r | head -n 3<br />
	&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000">21</span> <span style="color: #00ff00">127.0.0.1</span><br />
	&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000">14</span> <span style="color: #00ff00">183.204.240.36</span><br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000">5</span>&nbsp;&nbsp;<span style="color: #00ff00">120.33.53.133</span></span></p>
<p><span style="font-size: 16px">打印出来的结果，红色显示次数，绿色显示的是IP地址</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/%e5%b7%a7%e7%94%a8netstat-%e6%9f%a5%e7%9c%8b%e5%bd%93%e5%89%8d%e8%ae%bf%e9%97%ae%e6%9c%80%e5%a4%9a%e7%9a%84ip/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NGINX反向代理并将图片保存于缓存服务器</title>
		<link>http://www.411c.com/nginx%e5%8f%8d%e5%90%91%e4%bb%a3%e7%90%86%e5%b9%b6%e5%b0%86%e5%9b%be%e7%89%87%e4%bf%9d%e5%ad%98%e4%ba%8e%e7%bc%93%e5%ad%98%e6%9c%8d%e5%8a%a1%e5%99%a8/</link>
		<comments>http://www.411c.com/nginx%e5%8f%8d%e5%90%91%e4%bb%a3%e7%90%86%e5%b9%b6%e5%b0%86%e5%9b%be%e7%89%87%e4%bf%9d%e5%ad%98%e4%ba%8e%e7%bc%93%e5%ad%98%e6%9c%8d%e5%8a%a1%e5%99%a8/#comments</comments>
		<pubDate>Sat, 05 May 2012 11:01:01 +0000</pubDate>
		<dc:creator>老子黑牵翻</dc:creator>
				<category><![CDATA[LINUX技术]]></category>
		<category><![CDATA[NGINX]]></category>

		<guid isPermaLink="false">http://www.411c.com/?p=429</guid>
		<description><![CDATA[<p><span style="font-size: 16px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color: #ff0000">今天，一个朋友叫我帮忙在LINUX下搭建同步工具，开始我是想用<a href="http://www.411c.com/?s=rsync">rsync</a>的，但是具体了解了情况以后，我决定用<a href="http://www.411c.com/?s=nginx">NGINX</a>的反向代理。我朋友有个网站</span><a href="http://www.keatv.com"><span style="color: #ff0000">www.keatv.com</span></a><span style="color: #ff0000">是放在国外的，他想让这个站的图片用国内的服务器（img.411c.com），如果单纯用<a href="http://www.411c.com/?s=nginx">NGINX</a>的反向代理功能，用户每次访问这个站的时候，缓存服务器（这里就是img.411c.com所在的服务器）就会每次都去国外的站点上取出图片，这样反而会让图片打开的速度很慢，所以后来完善NGINX的反向代理，把访问过的图片都保存到缓存服务器上，这样第一次会比较慢，但以后用户访问图片的时候，都是从国内服务器上调用的，而不会再去国外服务器上了。在缓存服务器上具体<a href="http://www.411c.com/?s=nginx">NGINX</a>的配置如下</span>：<br />
	server {<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; include /usr/local/nginx/conf/port.conf;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; server_name&#160; img.411c.com;</span></p>
<p><span style="font-size: 16px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; root&#160;&#160; /www/img.411c.com;</span></p>
<p><span style="font-size: 16px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; location ~ .*\.(gif&#124;jpg&#124;jpeg&#124;png&#124;bmp&#124;swf)$<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; expires&#160;&#160;&#160;&#160;&#160;&#160; 7d;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; root /data/www/img.411c.com;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_store on;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_store_access user:rw group:rw all:r;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_temp_path /www/img.411c.com;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_redirect&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; off;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_set_header&#160;&#160;&#160;&#160;&#160;&#160;&#160; X-Real-IP $remote_addr;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_set_header&#160;&#160;&#160;&#160;&#160;&#160;&#160; X-Forwarded-For $proxy_add_x_forwarded_for;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; client_max_body_size&#160;&#160;&#160; 10m;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; client_body_buffer_size 1280k;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_connect_timeout&#160;&#160; 900;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_send_timeout&#160;&#160;&#160;&#160;&#160; 900;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_read_timeout&#160;&#160;&#160;&#160;&#160; 900;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_buffer_size&#160;&#160;&#160;&#160;&#160;&#160; 40k;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_buffers&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 40 320k;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_busy_buffers_size 640k;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_temp_file_write_size 640k;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!-e $request_filename)<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; {<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; proxy_pass&#160; <a href="http://img.keatv.com">http://img.keatv.com</a>; #后端服务器ip地址或域名<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</span></p>
<p><span style="font-size: 16px">&#160;&#160;&#160;&#160;&#160;&#160;&#160; error_page&#160;&#160; 500 502 503 504&#160; /50x.html;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; location = /50x.html {<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; root&#160;&#160; html;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</span></p>
<p><span style="font-size: 16px">&#160;&#160;&#160; log_format&#160; img.411c.comlog&#160; &#39;$remote_addr - $remote_user [$time_local] &#34;$request&#34; &#39;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#39;$status $body_bytes_sent &#34;$http_referer&#34; &#39;<br />
	&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &#39;&#34;$http_user_agent&#34; $http_x_forwarded_for&#39;;<br />
	&#160;&#160;&#160; access_log&#160; logs/img.411c.com.log&#160; img.411c.comlog;<br />
	&#160;&#160;&#160; }</span></p>
<p><span style="font-size: 16px">设置完了，保存img.411c.com.conf文件，并重启<a href="http://www.411c.com/tag/nginx/">NGINX</a>，这样，img.411c.com就会在用户第一次访问<a href="http://www.keatv.com">www.keatv.com</a>网站图片的时候自动去<a href="http://www.keatv.com">www.keatv.com</a>的服务器上去抓去图片并保存到缓存服务器上了</span></p>]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000">今天，一个朋友叫我帮忙在LINUX下搭建同步工具，开始我是想用<a href="http://www.411c.com/?s=rsync">rsync</a>的，但是具体了解了情况以后，我决定用<a href="http://www.411c.com/?s=nginx">NGINX</a>的反向代理。我朋友有个网站</span><a href="http://www.keatv.com"><span style="color: #ff0000">www.keatv.com</span></a><span style="color: #ff0000">是放在国外的，他想让这个站的图片用国内的服务器（img.411c.com），如果单纯用<a href="http://www.411c.com/?s=nginx">NGINX</a>的反向代理功能，用户每次访问这个站的时候，缓存服务器（这里就是img.411c.com所在的服务器）就会每次都去国外的站点上取出图片，这样反而会让图片打开的速度很慢，所以后来完善NGINX的反向代理，把访问过的图片都保存到缓存服务器上，这样第一次会比较慢，但以后用户访问图片的时候，都是从国内服务器上调用的，而不会再去国外服务器上了。在缓存服务器上具体<a href="http://www.411c.com/?s=nginx">NGINX</a>的配置如下</span>：<br />
	server {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; include /usr/local/nginx/conf/port.conf;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server_name&nbsp; img.411c.com;</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp; /www/img.411c.com;</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expires&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7d;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; root /data/www/img.411c.com;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_store on;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_store_access user:rw group:rw all:r;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_temp_path /www/img.411c.com;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_redirect&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; off;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_set_header&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; X-Real-IP $remote_addr;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_set_header&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; X-Forwarded-For $proxy_add_x_forwarded_for;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client_max_body_size&nbsp;&nbsp;&nbsp; 10m;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client_body_buffer_size 1280k;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_connect_timeout&nbsp;&nbsp; 900;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_send_timeout&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 900;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_read_timeout&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 900;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_buffer_size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 40k;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_buffers&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 40 320k;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_busy_buffers_size 640k;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_temp_file_write_size 640k;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!-e $request_filename)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy_pass&nbsp; <a href="http://img.keatv.com">http://img.keatv.com</a>; #后端服务器ip地址或域名<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error_page&nbsp;&nbsp; 500 502 503 504&nbsp; /50x.html;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location = /50x.html {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp; html;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-size: 16px">&nbsp;&nbsp;&nbsp; log_format&nbsp; img.411c.comlog&nbsp; &#39;$remote_addr &#8211; $remote_user [$time_local] &quot;$request&quot; &#39;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#39;$status $body_bytes_sent &quot;$http_referer&quot; &#39;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#39;&quot;$http_user_agent&quot; $http_x_forwarded_for&#39;;<br />
	&nbsp;&nbsp;&nbsp; access_log&nbsp; logs/img.411c.com.log&nbsp; img.411c.comlog;<br />
	&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-size: 16px">设置完了，保存img.411c.com.conf文件，并重启<a href="http://www.411c.com/tag/nginx/">NGINX</a>，这样，img.411c.com就会在用户第一次访问<a href="http://www.keatv.com">www.keatv.com</a>网站图片的时候自动去<a href="http://www.keatv.com">www.keatv.com</a>的服务器上去抓去图片并保存到缓存服务器上了</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.411c.com/nginx%e5%8f%8d%e5%90%91%e4%bb%a3%e7%90%86%e5%b9%b6%e5%b0%86%e5%9b%be%e7%89%87%e4%bf%9d%e5%ad%98%e4%ba%8e%e7%bc%93%e5%ad%98%e6%9c%8d%e5%8a%a1%e5%99%a8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

