<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
	<channel>
		<title>贵贵的博客</title>
		<link>http://blog.linuxphp.org/</link>
		<description>勤学似春起之苗，不见其增，日有所长； 辍学如磨刀之石，不见其损，日有所亏。</description>
		<copyright>Powered by SaBlog-X. Copyright (C) 2003-2010.</copyright>
		<generator>SaBlog-X Version 2.0 Build 20100301</generator>
		<lastBuildDate>Tue, 07 Feb 2012 11:05:40 +0000</lastBuildDate>
		<ttl>30</ttl>
		<item>
			<link>http://blog.linuxphp.org/archives/1490/</link>
			<guid>http://blog.linuxphp.org/archives/1490/</guid>
			<title>C语言之消息队列服务kesqs v0.1bata1</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	kesqs是基于socket的消息队列服务，主要进行异步处理数据。可以将较慢的处理逻辑、有并发数量限制的处理逻辑，通过消息队列放在后台处理，例如FLV视频转换、发送手机短信、发送电子邮件等。</p>
<p>
	&nbsp;</p>
<p>
	编译</p>
<p>
	&nbsp;</p>
<pre>
wget http://httpsqs.googlecode.com/files/libevent-2.0.12-stable.tar.gz
tar zxvf libevent-2.0.12-stable.tar.gz
cd libevent-2.0.12-stable/
./configure --prefix=/usr/local/libevent-2.0.12-stable/
make
make install
cd ../

cd kesqs-0.1-bata1
make</pre>
<p>
	&nbsp;</p>
<p>
	使用帮助：</p>
<p>
	./kesqs -h</p>
<p>
	&nbsp;</p>
<p>
	客户端：</p>
<p>
	参考api目录文件</p>
<p>
	&nbsp;</p>
<p>
	附：</p>
<p>
	软件参考其它开源软件如kmessage,httpsqs另外此版本属于bata版本，仅用于测试。</p>
<p><strong><a title="kesqs-0.1-bata1.rar" href="http://blog.linuxphp.org/attachment.php?id=427" target="_blank">kesqs-0.1-bata1.rar</a></strong> (12.23 K, 下载次数:0, 上传时间:Sat, 04 Feb 2012 23:45:24 +0000)</p>]]></description>
			<link>http://blog.linuxphp.org/archives/1490/</link>
			<category domain="http://blog.linuxphp.org/category/mysoft/">我的软件</category>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Sat, 04 Feb 2012 23:23:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1489/</link>
			<guid>http://blog.linuxphp.org/archives/1489/</guid>
			<title>C语言之valgrind内存泄露检查</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	&nbsp;</p>
<div class="asset-body" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 0px; padding-top: 8px; padding-right: 8px; padding-bottom: 8px; padding-left: 8px; clear: both; font-size: 18px; ">
	版权信息：原文来自<a href="http://www.cprogramming.com/debugging/valgrind.html" style="text-decoration: none; color: rgb(0, 32, 159); outline-style: none; outline-width: initial; outline-color: initial; ">cprogramming</a>，作者：Alexander Allain。 翻译：<a href="http://www.veryword.com/" style="text-decoration: none; color: rgb(0, 32, 159); outline-style: none; outline-width: initial; outline-color: initial; ">&nbsp;Vincent</a>。<br />
	<br />
	<a href="http://valgrind.org/" style="text-decoration: none; color: rgb(0, 32, 159); outline-style: none; outline-width: initial; outline-color: initial; ">Valgrind</a>是一款基于X86和AMD64第三版本构架的，Linux系统下的多功能的代码概要分析和内存调试工具。它可以模拟程序运行时候的内存使用情况，比如malloc和free调用（C++的是new和delete）。如果你使用未初始化的内存，在数组结尾后写，或者忘记释放指 针，valgrind都可以检测到。由于这些问题非常普遍，本教程将主要集中介绍使用valgrind找到这类简单的内存问题，尽管valgrind是一款可以做很多别的工作的工具。<br />
	<br />
	对于Windows用户，如果你没有Linux的工作环境，或者你想要开发基于Windows系统的软件，你可能会对IBM的<a href="http://www-306.ibm.com/software/awdtools/purify/" style="text-decoration: none; color: rgb(0, 32, 159); outline-style: none; outline-width: initial; outline-color: initial; ">Purify</a>感兴趣.Purify是和Valgrind类似的，查找内存泄漏和非法内存访问的工具，有试用版可以下载。<br />
	<br />
	<b>安装Valgrind</b><br />
	<br />
	Valgrind<a href="http://www.valgrind.org/downloads/current.html" style="text-decoration: none; color: rgb(0, 32, 159); outline-style: none; outline-width: initial; outline-color: initial; ">下载页面</a>。安装过程简单到只要使用bzip2解压缩和解包（以下例子中XYZ表示版本号）。<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		bzip2 -d valgrind-XYZ.tar.bz2<br />
		tar -xf valgrind-XYZ.tar</blockquote>
	以上步骤会创建一个叫valgrind-XYZ的目录。进入目录并运行：<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		./configure<br />
		make<br />
		make install</blockquote>
	现在，你的Valgrind已经安装好了，让我们看看如何使用。</div>
<div class="asset-more" id="more" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 8px; clear: both; ">
	<br />
	<b>使用Valgrind查找内存泄漏</b><br />
	<br />
	内存泄漏是最难以检测到的bug，因为这类问题一般要到你耗尽内存时再申请内存才会表现 出来。实际上，当我们使用像C或者C++这类没有内存回收机制的语言的时候，基本上有一半的时间耗在正确的释放内存上。如果你的程序要运行相当长时间来跟 踪相应分支的代码，即使是一个错误的代价也是很高昂的。<br />
	<br />
	当你运行你的代码的时候，你需要指定你所要使用的工具。现在你可以使用 valgrind。本教程中我们将集中介绍内存检查工具，因为内存检查工具可以让我们检查内存是否被正确的使用。如果没有其他参数，Valgrind会列 出free和malloc的调用的概要。请注意，以下的18515是进程号，每次运行都不一样。<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		% valgrind --tool=memcheck program_name<br />
		...<br />
		=18515== malloc/free: in use at exit: 0 bytes in 0 blocks.<br />
		==18515== malloc/free: 1 allocs, 1 frees, 10 bytes allocated.<br />
		==18515== For a detailed leak analysis,&nbsp; rerun with: --leak-check=yes</blockquote>
	<br />
	如果有内存泄漏，申请和释放的内存数目就会不一样（你不能使用一个free来释放属于多个alloc的内存）。稍后我们将谈到错误摘要，现在，你只要知道，有一些错误是可以忽略的----因为一些错误来自标准的库函数而不是你的代码。<br />
	<br />
	如果申请和释放的内存数目不一样，你就要用泄漏检查选项来检查你的程序。这样才能够列出你的所有没有对应起来的malloc/new等调用。<br />
	<br />
	出于展示的目的，我将使用一个编译成名为&quot;example1&quot;的很简单的程序。<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		#include &lt;stdlib.h&gt;<br />
		int main()<br />
		{<br />
		&nbsp;&nbsp;&nbsp; char *x = malloc(100); /* or, in C++, &quot;char *x = new char[100] */<br />
		&nbsp;&nbsp;&nbsp; return 0;<br />
		}</blockquote>
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		% valgrind --tool=memcheck --leak-check=yes example1</blockquote>
	运行以上命令后会有出现一些程序运行的信息。最后会有调用了malloc但是没有free的明细：<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		==2116== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1<br />
		==2116==&nbsp;&nbsp;&nbsp; at 0x1B900DD0: malloc (vg_replace_malloc.c:131)<br />
		==2116==&nbsp;&nbsp;&nbsp; by 0x804840F: main (in /home/cprogram/example1)</blockquote>
	<br />
	这些并没有告诉我们太多信息，尽管我们知道了内存泄漏是在主函数中调用malloc引起的，但是我们并没有相应的行号。这个问题是由于我们在用gcc编译的时候没有使用-g选项引起的。如果我们用带有调试信号的选项重新编译，我们就会得到以下所示的更加有用的输出：<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		==2330== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1<br />
		==2330==&nbsp;&nbsp;&nbsp; at 0x1B900DD0: malloc (vg_replace_malloc.c:131)<br />
		==2330==&nbsp;&nbsp;&nbsp; by 0x804840F: main (example1.c:5)</blockquote>
	<br />
	现在我们知道发生了内存丢失问题的内存是在哪一行所申请的。尽管还要继续跟踪以确定需要释放内存的位置，但是至少你知道从哪里开始查找。由于每个malloc或new的内存你都要有一个管理的计划，知道内存在那里丢失可以让你知道从哪里开始。<br />
	<br />
	有 时候，--leak-check=yes选项不会显示出所有的内存泄漏。为了找到绝对的没有配对的free或new调用，你需要使用--show- reachable=yes选项。这个选项的输出基本上和--leak-check=yes一样，但是会列出更多的没有释放的内存。<br />
	<br />
	<b>用valgrind查找非法的指针使用</b><br />
	<br />
	你可以用Valgrind的内存检查工具找到非法的堆内存的使用。比如，你可以用malloc或new申请一个数组，然后试图访问超过数组结尾的内存：<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		char *x = malloc(10);<br />
		x[10] = &#39;a&#39;;<br />
		&nbsp;</blockquote>
	Valgrind能够检查到这个问题。比如将以下代码编译成名为example2的程序，并用valgrind运行。<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		#include &lt;stdlib.h&gt;<br />
		<br />
		int main()<br />
		{<br />
		&nbsp;&nbsp;&nbsp; char *x = malloc(10);<br />
		&nbsp;&nbsp;&nbsp; x[10] = &#39;a&#39;;<br />
		&nbsp;&nbsp;&nbsp; return 0;<br />
		}</blockquote>
	<br />
	运行：<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		valgrind --tool=memcheck --leak-check=yes example2</blockquote>
	<br />
	会有如下警告：<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		==9814==&nbsp; Invalid write of size 1<br />
		==9814==&nbsp;&nbsp;&nbsp; at 0x804841E: main (example2.c:6)<br />
		==9814==&nbsp; Address 0x1BA3607A is 0 bytes after a block of size 10 alloc&#39;d<br />
		==9814==&nbsp;&nbsp;&nbsp; at 0x1B900DD0: malloc (vg_replace_malloc.c:131)<br />
		==9814==&nbsp;&nbsp;&nbsp; by 0x804840F: main (example2.c:5)</blockquote>
	<br />
	以 上信息告诉我们，我们使用了一个包含有10个字节的空间，在紧跟着超出了这个数组范围的后面，有一个非法的写。如果我们试图从那块内存中读，我们会被警告 &#39;Invalid read of size X&#39;，其中的X表示我们试图去读的内存的大小。（对于一个字符，X是1，对于整型数，X将是2或4，具体依赖于系统）。像平常一样，Valgrind输出 了函数调用的堆栈轨迹，这样我们就能够知道错误发生的确切位置。<br />
	<br />
	<b>检查未初始化变量的使用</b><br />
	<br />
	Valgrind能够检测到的另外一种操作是条件语句中未初始化值的使用。尽管你应该养成初始化所有你创建了的变量，Valgrind可以帮你找到你没有做到的地方。将以下代码编译成名为example3的可执行程序。<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		#include &lt;stdio.h&gt;<br />
		<br />
		int main()<br />
		{<br />
		&nbsp;&nbsp;&nbsp; int x;<br />
		&nbsp;&nbsp;&nbsp; if(x == 0)<br />
		&nbsp;&nbsp;&nbsp; {<br />
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(&quot;X is zero&quot;); /* replace with cout and include&nbsp;<br />
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iostream for C++ */<br />
		&nbsp;&nbsp;&nbsp; }<br />
		&nbsp;&nbsp;&nbsp; return 0;<br />
		}</blockquote>
	<br />
	用Valgrind运行得到：<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		==17943== Conditional jump or move depends on uninitialised value(s)<br />
		==17943==&nbsp;&nbsp;&nbsp; at 0x804840A: main (example3.c:6)</blockquote>
	<br />
	Valgrind甚至聪明到知道一个变量被一个未初始化的变量赋值，被赋值的变量的状态仍然是未初始化的状态。<br />
	<br />
	将以下代码编译成example4:<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		#include &lt;stdio.h&gt;<br />
		<br />
		int foo(int x)<br />
		{<br />
		&nbsp;&nbsp;&nbsp; if(x &lt; 10)<br />
		&nbsp;&nbsp;&nbsp; {<br />
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(&quot;x is less than 10\n&quot;);<br />
		&nbsp;&nbsp;&nbsp; }<br />
		}<br />
		<br />
		int main()<br />
		{<br />
		&nbsp;&nbsp;&nbsp; int y;<br />
		&nbsp;&nbsp;&nbsp; foo(y);<br />
		}</blockquote>
	<br />
	在Valgrind中运行example4会有如下警告：&nbsp;<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		==4827== Conditional jump or move depends on uninitialised value(s)<br />
		==4827==&nbsp;&nbsp;&nbsp; at 0x8048366: foo (example4.c:5)<br />
		==4827==&nbsp;&nbsp;&nbsp; by 0x8048394: main (example4.c:14)</blockquote>
	<br />
	你也许会认为问题在foo，其余的堆栈调用不是那么重要。但是由于main函数把未初始化的值传递给foo，因此我们必须根据符值关系追溯，直到找到那个未初始化的变量。<br />
	<br />
	你必须测试了相应的分支的相应的条件语句才能够找到错误。因此测试的时候要尽可能包括程序的所有执行路径。<br />
	<br />
	<b>Valgrind还能找到什么</b><br />
	<br />
	Valgrind还能检测到一些其他不合适的内存使用：如果你对相同的指针释放了两次，Valgrind会提示如下错误：<br />
	<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		Invalid free()</blockquote>
	<br />
	Valgrind 还可以检测到不合适的内存释放方法。比如，在C++中有三种释放动态内存的选项：free，delete和delete[]。函数free只能和 malloc调用相对应----在一些系统，你可以不这么做，但是程序的可移植性就不好。此外，delete[]只能和new[]配对（用来申请动态数组）。 尽管有些编译器允许你不这么避开这一点，但是并无法保证所有的都是这样，因为这不是标准。<br />
	<br />
	如果你犯了这些错误，valgrind会提示:&nbsp;<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		&nbsp; Mismatched free() / delete / delete []</blockquote>
	<br />
	即使你的程序能够工作，这一类问题也应该及时解决。&nbsp;<br />
	<br />
	<b>Valgrind有什么找不到的错误？</b><br />
	<br />
	Valgrind不会检查静态数组的边界（分配在栈），所以你必须在函数内部声明：<br />
	<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0.75em; margin-left: 30px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(246, 230, 159); background-position: initial initial; background-repeat: initial initial; ">
		int main()<br />
		{<br />
		&nbsp;&nbsp;&nbsp; char x[10];<br />
		&nbsp;&nbsp;&nbsp; x[11] = &#39;a&#39;;<br />
		}</blockquote>
	<p>
		<br />
		Valgrind不会警告你以上代码的错误！一个出于测试目的的可能的解决方案是用动态申请的内存代替静态数组，这样申请的内存分配在堆，valgrind就会进行边界检查，尽管这可能和未释放内存混淆。<br />
		<br />
		<b>注意事项</b><br />
		<br />
		Valgrind 有什么缺点？它会消耗更多内存----可能多达程序正常运行时候的两倍。如果你测试的代码需要非常大的内存可能就会有问题。当你测试的时候运行程序的时间也比 较长，大多数时候这不会是个问题，这也只有在你测试的时候会有这个问题。除非你运行的程序本身已经相当慢，这才会成为困扰。<br />
		<br />
		最后，Valgrind不会检查到程序中的每一个错误。如果你没有用很长的字符串进行测试，valgrind就不会提示你缓冲区溢出。Valgrind也不会不能告诉你程序在不被允许的内存写。Valgrind就像其他的工具，需要合适的使用才能够发现问题。<br />
		<br />
		<b>小结</b><br />
		<br />
		Valgrind 是X86和AMD64构架下，运行于Linux的一个内存检查工具。它允许程序员在它所构造的环境中运行程序以检查没有配对的内存申请和其他非法内存使用 （比如未初始化内存），或是非法的内存操作（比如多次释放同一块内存或非法的内存释放命令）。Valgrind不会检查静态数组的边界。<br />
		<br />
		后话：Valgrind的一些有用的参考资料。<br />
		<a href="http://hi.baidu.com/timegoneby/blog/item/ffaad71790bf060dc93d6dd6.html" style="text-decoration: none; color: rgb(0, 32, 159); outline-style: none; outline-width: initial; outline-color: initial; ">Valgrind 使用简单说明</a></p>
	<p>
		<a href="http://www.ibm.com/developerworks/cn/linux/l-pow-debug/">http://www.ibm.com/developerworks/cn/linux/l-pow-debug/</a></p>
	<p>
		<a href="http://blog.s135.com/post/419/">http://blog.s135.com/post/419/</a></p>
</div>
]]></description>
			<link>http://blog.linuxphp.org/archives/1489/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Sat, 04 Feb 2012 18:40:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1488/</link>
			<guid>http://blog.linuxphp.org/archives/1488/</guid>
			<title>C语言之libevent和socket示例（三）</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	使用libevent内部提供的bufferd来处理可读可写的事件。</p>
<pre>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;errno.h&gt;
#include &lt;err.h&gt;
#include &lt;event.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;string.h&gt;

#define SERVER_PORT 5555

//error: two or more data types in declaration specifiers
//结构体定义的时候后面没写分号
struct client {
	//客户端socket
	int fd;
	//bufferedevent 对象
	struct bufferevent *buf_ev;
};

int setnonblock(int fd)
{
	int flags;
	
	flags = fcntl(fd, F_GETFL);
	if (flags &lt; 0) {
		return flags;
	}
	flags |= O_NONBLOCK;
	if (fcntl(fd, F_SETFL, flags) &lt; 0) {
		return -1;
	}
	return 0;
}
//写回读缓冲区。注意 bufferevent_write_buffer 将逐渐发送完输入的数据
void buffered_on_read(struct bufferevent *bev, void *arg) {
	bufferevent_write_buffer(bev, bev-&gt;input);
}
//当写缓冲为0时，libevent调用这个函数。我们写出这个函数只是因为libevent需要，但是我们没有使用它。
void buffered_on_write(struct bufferevent *bev, void *arg) {
	
}
//当基础的socket描述符发生错误时，libevent将调用这个函数
void buffered_on_error(struct bufferevent *bev, short ev, void *arg) {
	struct client *client = (struct client *)arg;
	if (ev &amp; EVBUFFER_EOF) {
		printf(&quot;client disconnected\n&quot;);
	} else {
		warn(&quot;client socket error, disconnecting\n&quot;);
	}
	bufferevent_free(client-&gt;buf_ev);
	close(client-&gt;fd);
	free(client);
}
void on_accept(int fd, short ev, void *arg) {
	int client_fd;
	struct sockaddr_in client_addr;
	socklen_t client_len = sizeof(client_addr);
	struct client *client;

	client_fd = accept(fd, (struct sockaddr *)&amp;client_addr, &amp;client_len);
	if (client_fd &lt; 0) {
		warn(&quot;accept faild&quot;);
		return;
	}

	if (setnonblock(client_fd) &lt; 0) {
		warn(&quot;failed to set client socket non-blocking&quot;);
	}

	client = calloc(1, sizeof(*client));
	if (client == NULL) {
		err(1,&quot;malloc failed&quot;);
	}
	client-&gt;fd = client_fd;
	/* 创建 buffered 事件。
	 * 第一个参数是引起事件的文件描述符，这里是客户端socket。
	 * 第二个参数是一个回调函数，当数据已经被socket读取并且对程序有效时，它被调用。
	 * 第三个参数是一个回调函数，当写缓冲区到达最小值时，它被调用。 
	 *		这通常意味着，当写入缓冲区长度为0时，这个回调函数将被调用。        
	 *		它必须被定义，但是实际上你可以在这个回调函数中不做任何处理。
	 * 第四个参数是一个回调函数，当有socket错误发生时，它被调用。         
	 *		在这里你可以探测客户端断开连接，或者其它的错误。
	 * 第五个参数用来存储一个将会传递给各个回调函数的参数。我们在这里存储客户数据对象。
	 */
	client-&gt;buf_ev = bufferevent_new(client_fd, buffered_on_read, buffered_on_write, buffered_on_error, client);

	/* 我们必须在回调函数被调用前，使它有效。 */
	bufferevent_enable(client-&gt;buf_ev, EV_READ);
}
int main(int argc, char *argv[])
{
	int listen_fd;
	struct sockaddr_in listen_addr;
	struct event ev_accept;
	int reuseaddr_on;

	event_init();
	listen_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (listen_fd &lt; 0) {
		err(1, &quot;listen failed&quot;);
	}

	memset(&amp;listen_addr, 0, sizeof(listen_addr));
	listen_addr.sin_family = AF_INET;
	listen_addr.sin_addr.s_addr = INADDR_ANY;
	listen_addr.sin_port = htons(SERVER_PORT);
	if (bind(listen_fd, (struct sockaddr *)&amp;listen_addr, sizeof(listen_addr)) &lt; 0) {
		err(1, &quot;bind failed&quot;);	
	}
	if (listen(listen_fd, 5) &lt; 0) {
		err(1, &quot;listen failed&quot;);
	}

	reuseaddr_on = 1;
	setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &amp;reuseaddr_on, sizeof(reuseaddr_on));

	if (setnonblock(listen_fd) &lt; 0) {
		err(1, &quot;failed to set server to non-blocking&quot;);
	}

	event_set(&amp;ev_accept, listen_fd, EV_READ|EV_PERSIST, on_accept, NULL);
	event_add(&amp;ev_accept, NULL);

	event_dispatch();
	return 0;
}
</pre>
<p>
	参考：</p>
<p>
	<a href="http://code.google.com/p/ishbits/source/browse/trunk/libevent-examples/buffered-echo-server/libevent_echosrv_buffered.c">http://code.google.com/p/ishbits/source/browse/trunk/libevent-examples/buffered-echo-server/libevent_echosrv_buffered.c</a></p>
<p><strong><a title="bufevent.c" href="http://blog.linuxphp.org/attachment.php?id=425" target="_blank">bufevent.c</a></strong> (3.84 K, 下载次数:0, 上传时间:Mon, 30 Jan 2012 13:38:02 +0000)</p>]]></description>
			<link>http://blog.linuxphp.org/archives/1488/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Mon, 30 Jan 2012 13:33:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1487/</link>
			<guid>http://blog.linuxphp.org/archives/1487/</guid>
			<title>C语言之libevent和socket示例（二）</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	使用tailq队列存储数据来处理可读可写的事件</p>
<pre>
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;netinet/tcp.h&gt;
#include &lt;event.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;errno.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;sys/queue.h&gt;
#include &lt;err.h&gt;
#include &lt;unistd.h&gt;


//客户端发送的内容的长度
#define BUFLEN 1024

//全局变量
struct event ev_accept;
int socket_fd;

//内部函数，只能被本文件中的函数调用
static short ListenPort = 8080;
static long ListenAddr = INADDR_ANY;//任意地址，值就是0
static int   MaxConnections = 2;//连接请求队列的最大长度

struct bufferq {
	//buffer
	u_char *buf;

	//buf的长度
	int len;

	//写操作的偏移，用于buf的重试输出
	int offset;

	/*指向队列的前一个和后一个元素*/
	TAILQ_ENTRY(bufferq) entries;
};

//客户数据结构
struct client {
	//两个事件对象
	struct event ev_read;
	struct event ev_write;

	//存储的数据队列
	TAILQ_HEAD(, bufferq) writeq;
};

//将一个socket设置成非阻塞模式
//不论什么平台编写网络程序，都应该使用NONBLOCK socket的方式。这样可以保证你的程序至少不会在recv/send/accept/connect这些操作上发生block从而将整个网络服务都停下来
int setnonblock(int fd)
{
	int flags;
	//fcntl()用来操作文件描述符的一些特性
	if ((flags = fcntl(fd, F_GETFL)) == -1) {
		return -1;
	}

	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
		return -1;
	}
	return 0;
}

//当客户端socket准备好写入时，libevent调用这个函数
void on_write(int fd, short ev, void *arg)
{
	struct client *client = (struct client *)arg;
	struct bufferq *bufferq;
	int len, wlen;

	// 将第一个元素移出写队列
	bufferq = TAILQ_FIRST(&amp;client-&gt;writeq);
	if (bufferq == NULL) {
		return;
	}

	//写buffer里的数据，如果有一部分已经输出过了，只输出剩余的部分
	len = bufferq-&gt;len - bufferq-&gt;offset;
	wlen = write(fd, bufferq-&gt;buf + bufferq-&gt;offset, len);
	if (wlen == -1) {
		//写操作被打断或不能写入数据
		if (errno == EINTR || errno == EAGAIN) {
			event_add(&amp;client-&gt;ev_write, NULL);
			return;
		} else {
			//其它的sock错误
			//close(fd);
			//free(client);
			err(1, &quot;write&quot;);
			return;
		}
	} else if (wlen &lt; len) {
		//只输出了一部分数据，更新偏移量，调整写入事件
		bufferq-&gt;offset += wlen;
		event_add(&amp;client-&gt;ev_write, NULL);
		return;
	}

	//队列中移除
	TAILQ_REMOVE(&amp;client-&gt;writeq, bufferq, entries);
	//回收calloc申请的内存
	//free(bufferq-&gt;buf);//不是calloc申请的内存不能free
	free(bufferq);
}
//这个函数当客户端的socket可读时由libevent调用
void on_read(int fd, short ev, void *arg)
{
	struct client *client = (struct client *)arg;
	u_char buf[BUFLEN];
	int len, wlen;
	struct bufferq *bufferq;

	memset(&amp;buf, 0, sizeof(buf));
	//会把参数fd 所指的文件传送count个字节到buf指针所指的内存中
	len = read(fd, buf, sizeof(buf));
	if (len == 0) {
		/* 客户端断开连接，在这里移除读事件并且释放客户数据结构 */
		printf(&quot;disconnected\n&quot;);
		close(fd);
		event_del(&amp;client-&gt;ev_read);
		free(client);
		return;
	} else if (len &lt; 0) {
		/* 出现了其它的错误，在这里关闭socket，移除事件并且释放客户数据结构 */
		printf(&quot;socket fail %s\n&quot;, strerror(errno));
		close(fd);
		event_del(&amp;client-&gt;ev_read);
		free(client);
		return;
	}
	//如果client发送来bye，则断开连接
	if (!strcmp(buf, &quot;bye\r\n&quot;)) {
		close(fd);
		event_del(&amp;client-&gt;ev_read);
		free(client);
		return;
	}

	bufferq = calloc(1, sizeof(*bufferq));
	if (bufferq == NULL) {
		close(fd);
		event_del(&amp;client-&gt;ev_read);
		free(client);
		printf(&quot;malloc fail&quot;);
		return;
	}
	bufferq-&gt;buf = buf;
	bufferq-&gt;len = len;
	bufferq-&gt;offset = 0;
	//插入队列
	TAILQ_INSERT_TAIL(&amp;client-&gt;writeq, bufferq, entries);

	//写事件
	event_add(&amp;client-&gt;ev_write, NULL);
}

/*
   当有一个连接请求准备被接受时，这个函数将被libevent调用并传递给三个变量: 
   int fd:触发事件的文件描述符. 
   short event:触发事件的类型EV_TIMEOUT,EV_SIGNAL, EV_READ, or EV_WRITE. 
   void* :由arg参数指定的变量. 
*/
void on_accept(int fd, short ev, void *arg)
{
	int cfd;
	struct sockaddr_in addr;
	socklen_t addrlen = sizeof(addr);
	int yes = 1;
	int retval;
	//为每个客户端建一个client而不是写成全局变量,从而可以接收多个请求
	struct client *client;

	//将从连接请求队列中获得连接信息，创建新的套接字，并返回该套接字的文件描述符。
	//新创建的套接字用于服务器与客户机的通信，而原来的套接字仍然处于监听状态。
	//该函数的第一个参数指定处于监听状态的流套接字
	cfd = accept(fd, (struct sockaddr *)&amp;addr, &amp;addrlen);
	if (cfd == -1) {
		printf(&quot;accept(): can not accept client connection&quot;);
		return;
	}
	if (setnonblock(cfd) == -1) {
		close(cfd);
		return;
	}

	//为新客户分配一个客户数据结构来保存这个客户状态
	client = calloc(1, sizeof(*client));
	if (client == NULL) {
		printf(&quot;calloc faild&quot;);
		close(cfd);
		return;
	}

	//设置与某个套接字关联的选项
	//参数二 IPPROTO_TCP:TCP选项
	//参数三 TCP_NODELAY 不使用Nagle算法 选择立即发送数据而不是等待产生更多的数据然后再一次发送
	//       更多参数TCP_NODELAY 和 TCP_CORK
	//参数四 新选项TCP_NODELAY的值
	if (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &amp;yes, sizeof(yes)) == -1) {
		printf(&quot;setsockopt(): TCP_NODELAY %s\n&quot;, strerror(errno));
		close(cfd);
		return;
	}

	event_set(&amp;client-&gt;ev_read, cfd, EV_READ | EV_PERSIST, on_read, client);
	event_add(&amp;client-&gt;ev_read, NULL);
	
	event_set(&amp;client-&gt;ev_write, cfd, EV_WRITE, on_write, client);
	//初始化客户端队列
	TAILQ_INIT(&amp;client-&gt;writeq);
	printf(&quot;Accepted connection from %s\n&quot;, inet_ntoa(addr.sin_addr));
}
int create_socket(void)
{
	struct sockaddr_in sa;

	//socket函数来创建一个能够进行网络通信的套接字。
	//第一个参数指定应用程序使用的通信协议的协议族，对于TCP/IP协议族，该参数置AF_INET;
	//第二个参数指定要创建的套接字类型
	//流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW
	//第三个参数指定应用程序所使用的通信协议。
	socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (socket_fd == -1) {
		printf(&quot;socket(): can not create server socket\n&quot;);
		return -1;
	}
	if (setnonblock(socket_fd) == -1) {
		return -1;
	}

	//如何避免等待60秒之后才能重启服务
	//in case of &#39;address already in use&#39; error message
	/*这个套接字选项通知内核，如果端口忙，但TCP状态位于 TIME_WAIT ，可以重用端口。
	  如果端口忙，而TCP状态位于其他状态，重用端口时依旧得到一个错误信息，指明&quot;地址已经使用中&quot;。
	  如果你的服务程序停止后想立即重启，而新套接字依旧使用同一端口，此时 SO_REUSEADDR 选项非常有用。
	  必须意识到，此时任何非期望数据到达，都可能导致服务程序反应混乱，不过这只是一种可能，事实上很不可能。
	*/
	int yes = 1;
	if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &amp;yes, sizeof(yes))) {
		//perror()用来将上一个函数发生错误的原因输出到标准设备(stderr) 
		perror(&quot;setsockopt failed&quot;);
		return -1;
	}


	//清空内存数据
	memset(&amp;sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	//htons将一个无符号短整型数值转换为网络字节序
	sa.sin_port = htons(ListenPort);
	//htonl将主机的无符号长整形数转换成网络字节顺序
	sa.sin_addr.s_addr = htonl(ListenAddr);

	//(struct sockaddr*)&amp;sa将sa强制转换为sockaddr类型的指针
	/*struct sockaddr 
		数据结构用做bind、connect、recvfrom、sendto等函数的参数，指明地址信息。
		但一般编程中并不直接针对此数据结构操作，而是使用另一个与sockaddr等价的数据结构 struct sockaddr_in
		sockaddr_in和sockaddr是并列的结构，指向sockaddr_in的结构体的指针也可以指向
		sockadd的结构体，并代替它。也就是说，你可以使用sockaddr_in建立你所需要的信息,
		在最后用进行类型转换就可以了
	*/
	//bind函数用于将套接字绑定到一个已知的地址上
	if (bind(socket_fd, (struct sockaddr *)&amp;sa, sizeof(sa)) == -1) {
		close(socket_fd);
		printf(&quot;bind(): can not bind server socket&quot;);
		return -1;
	}
	
	//执行listen 之后套接字进入被动模式
	//MaxConnections 连接请求队列的最大长度,队列满了以后，将拒绝新的连接请求
	if (listen(socket_fd, MaxConnections) == -1) {
		printf(&quot;listen(): can not listen server socket&quot;);
		close(socket_fd);
		return -1;
	}

	/*
	   event_set的参数：
	   + 参数1:  为要创建的event
	   + 参数2:  file descriptor，创建纯计时器可以设置其为-1，即宏evtimer_set定义的那样
	   + 参数3:  设置event种类，常用的EV_READ, EV_WRITE, EV_PERSIST, EV_SIGNAL, EV_TIMEOUT，纯计时器设置该参数为0
	   + 参数4:  event被激活之后触发的callback函数
	   + 参数5:  传递给callback函数的参数
	   备注：
			如果初始化event的时候设置其为persistent的(设置了EV_PERSIST)，
			则使用event_add将其添加到侦听事件集合后(pending状态)，
			该event会持续保持pending状态，即该event可以无限次参加libevent的事件侦听。
			每当其被激活触发callback函数执行之后，该event自动从active转回为pending状态，
			继续参加libevent的侦听(当激活条件满足，又可以继续执行其callback)。
			除非在代码中使用event_del()函数将该event从libevent的侦听事件集合中删除。
			如果不通过设置EV_PERSIST使得event是persistent的，需要在event的callback中再次调用event_add
			(即在每次pending变为active之后，在callback中再将其设置为pending)
	 */
	event_set(&amp;ev_accept, socket_fd, EV_READ | EV_PERSIST, on_accept, NULL);
	//将event添加到libevent侦听的事件集中
	if (event_add(&amp;ev_accept, NULL) == -1) {
		printf(&quot;event_add(): can not add accept event into libevent&quot;);
		close(socket_fd);
		return -1;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	int retval;
	
	//初始化event base 使用默认的全局current_base
	event_init();
	
	retval = create_socket();
	if (retval == -1) {
		exit(-1);
	}
	//event_dispatch() 启动事件队列系统，开始监听（并接受）请求
	event_dispatch();
	
	return 0;
}
</pre>
<p>
	参考：</p>
<p>
	<a href="http://code.google.com/p/ishbits/source/browse/trunk/libevent-examples/echo-server/libevent_echosrv2.c">http://code.google.com/p/ishbits/source/browse/trunk/libevent-examples/echo-server/libevent_echosrv2.c</a></p>
<p><strong><a title="socket.c" href="http://blog.linuxphp.org/attachment.php?id=424" target="_blank">socket.c</a></strong> (10.33 K, 下载次数:0, 上传时间:Mon, 30 Jan 2012 13:37:43 +0000)</p>]]></description>
			<link>http://blog.linuxphp.org/archives/1487/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Sat, 28 Jan 2012 18:49:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1486/</link>
			<guid>http://blog.linuxphp.org/archives/1486/</guid>
			<title>C语言之用 GDB 调试程序断错误</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	linux下断错误调试方法</p>
<p>
	<a href="http://alovelycat.blog.sohu.com/118756560.html">http://alovelycat.blog.sohu.com/118756560.html</a></p>
<p>
	用 GDB 调试程序</p>
<p>
	<a href="http://zhangyafeikimi.iteye.com/blog/249885">http://zhangyafeikimi.iteye.com/blog/249885</a></p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1486/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Sat, 28 Jan 2012 18:47:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1485/</link>
			<guid>http://blog.linuxphp.org/archives/1485/</guid>
			<title>C语言之尾队列tailq</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	queue和list的结构定义和操作都在&#39;sys/queue.h&#39;中完成， 主要定义了下面四种数据结构：</p>
<ul>
	<li>
		单向列表(single-linked lists)</li>
	<li>
		单向尾队列(single-linked tail queue)</li>
	<li>
		列表(lists)</li>
	<li>
		尾队列(tail queues)</li>
</ul>
<h3>
	尾队列图示</h3>
<p>
	<div class="attach"><a href="http://blog.linuxphp.org/attachment.php?id=422" target="_blank"><img src="http://blog.linuxphp.org/attachments/date_201201/thumb_c7351a0315666a6eed44efc913851879.png" border="0" alt="20120127213242.png&#13;&#13;大小: 9.99 K&#13;尺寸:  x &#13;浏览: 0 次&#13;点击打开新窗口浏览全图" width="500" height="255" /></a></div></p>
<p>
	&nbsp;</p>
<h3>
	尾队列常用宏</h3>
<table border="1" cellpadding="0" cellspacing="0">
	<tbody>
		<tr>
			<td style="font-size: 12pt; " valign="top" width="284">
				<p style="font-size: 12pt; ">
					<strong>宏名称</strong></p>
			</td>
			<td style="font-size: 12pt; " valign="top" width="202">
				<p style="font-size: 12pt; ">
					<strong>操作</strong></p>
			</td>
		</tr>
		<tr>
			<td style="font-size: 12pt; " valign="top" width="284">
				TAILQ_INIT</td>
			<td style="font-size: 12pt; " valign="top" width="202">
				<p style="font-size: 12pt; ">
					初始化队列</p>
			</td>
		</tr>
		<tr>
			<td style="font-size: 12pt; " valign="top" width="284">
				<p style="font-size: 12pt; ">
					TAILQ_FOREACH</p>
			</td>
			<td style="font-size: 12pt; " valign="top" width="202">
				<p style="font-size: 12pt; ">
					对队列进行遍历操作</p>
			</td>
		</tr>
		<tr>
			<td style="font-size: 12pt; " valign="top" width="284">
				<p style="font-size: 12pt; ">
					TAILQ_INSERT_BEFORE</p>
			</td>
			<td style="font-size: 12pt; " valign="top" width="202">
				<p style="font-size: 12pt; ">
					在指定元素之前插入元素</p>
			</td>
		</tr>
		<tr>
			<td style="font-size: 12pt; " valign="top" width="284">
				<p style="font-size: 12pt; ">
					TAILQ_INSERT_TAIL</p>
			</td>
			<td style="font-size: 12pt; " valign="top" width="202">
				<p style="font-size: 12pt; ">
					在队列尾部插入元素</p>
			</td>
		</tr>
		<tr>
			<td style="font-size: 12pt; " valign="top" width="284">
				<p style="font-size: 12pt; ">
					TAILQ_EMPTY</p>
			</td>
			<td style="font-size: 12pt; " valign="top" width="202">
				<p style="font-size: 12pt; ">
					检查队列是否为空</p>
			</td>
		</tr>
		<tr>
			<td style="font-size: 12pt; " valign="top" width="284">
				<p style="font-size: 12pt; ">
					TAILQ_REMOVE</p>
			</td>
			<td style="font-size: 12pt; " valign="top" width="202">
				<p style="font-size: 12pt; ">
					从队列中移除元素</p>
			</td>
		</tr>
	</tbody>
</table>
<h3>
	使用示例</h3>
<pre>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;sys/queue.h&gt;

/*
  定义一个结构体，它只是尾队列的一个元素
  它必须包含一个TAILQ_ENTRY来指向上一个和下一个元素
*/
struct tailq_entry {
	int value;

	TAILQ_ENTRY(tailq_entry) entries;
};

//定义队列的头部
TAILQ_HEAD(, tailq_entry) my_tailq_head;

int main(int argc, char  *argv[])
{
	//定义一个结构体指针
	struct tailq_entry *item;
	//定义另外一个指针
	struct tailq_entry *tmp_item;
	
	//初始化队列
	TAILQ_INIT(&amp;my_tailq_head);

	int i;
	//在队列里添加10个元素
	for(i=0; i&lt;10; i++) {
		//申请内存空间
		item = malloc(sizeof(*item));
		if (item == NULL) {
			perror(&quot;malloc failed&quot;);
			exit(-1);
		}
		//设置值
		item-&gt;value = i;

		/*
		   将元素加到队列尾部
		   参数1：指向队列头的指针
		   参数2：要添加的元素
		   参数3：结构体的变量名
		*/
		TAILQ_INSERT_TAIL(&amp;my_tailq_head, item, entries);
	}

	//遍历队列
	printf(&quot;Forward traversal: &quot;);
	TAILQ_FOREACH(item, &amp;my_tailq_head, entries) {
		printf(&quot;%d &quot;,item-&gt;value);
	}
	printf(&quot;\n&quot;);

	//添加一个新的元素
	printf(&quot;Adding new item after 5: &quot;);
	TAILQ_FOREACH(item, &amp;my_tailq_head, entries) {
		if (item-&gt;value == 5) {
			struct tailq_entry *new_item = malloc(sizeof(*new_item));
			if (new_item == NULL) {
				perror(&quot;malloc failed&quot;);
				exit(EXIT_FAILURE);
			}
			new_item-&gt;value = 10;
			//插入一个元素
			TAILQ_INSERT_AFTER(&amp;my_tailq_head, item, new_item, entries);
			break;
		}
	}
	TAILQ_FOREACH(item, &amp;my_tailq_head, entries) {
		printf(&quot;%d &quot;, item-&gt;value);
	}
	printf(&quot;\n&quot;);

	//删除一个元素
	printf(&quot;Deleting item with value 3: &quot;);
	for(item = TAILQ_FIRST(&amp;my_tailq_head); item != NULL; item = tmp_item) {
		if (item-&gt;value == 3) {
			//删除一个元素
			TAILQ_REMOVE(&amp;my_tailq_head, item, entries);
			//释放不需要的内存单元
			free(item);
			break;
		}
		tmp_item = TAILQ_NEXT(item, entries);
	}

	TAILQ_FOREACH(item, &amp;my_tailq_head, entries) {
		printf(&quot;%d &quot;, item-&gt;value);
	}
	printf(&quot;\n&quot;);

	//清空队列
	while (item = TAILQ_FIRST(&amp;my_tailq_head)) {
		TAILQ_REMOVE(&amp;my_tailq_head, item, entries);
		free(item);
	}

	//查看是否为空
	if (!TAILQ_EMPTY(&amp;my_tailq_head)) {
		printf(&quot;tail queue is  NOT empty!\n&quot;);
	}

	return 0;
	
}
</pre>
<p>
	示例原文</p>
<p>
	<a href="http://code.google.com/p/ishbits/source/browse/trunk/libevent-examples/">http://code.google.com/p/ishbits/source/browse/trunk/libevent-examples/</a></p>
<p>
	&nbsp;</p>
<p><strong><a title="tailq.c" href="http://blog.linuxphp.org/attachment.php?id=426" target="_blank">tailq.c</a></strong> (2.26 K, 下载次数:0, 上传时间:Mon, 30 Jan 2012 13:39:23 +0000)</p><p><strong><a title="tailq-example-r30.tar.gz" href="http://blog.linuxphp.org/attachment.php?id=423" target="_blank">tailq-example-r30.tar.gz</a></strong> (5.27 K, 下载次数:0, 上传时间:Fri, 27 Jan 2012 22:53:31 +0000)</p>]]></description>
			<link>http://blog.linuxphp.org/archives/1485/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Fri, 27 Jan 2012 22:40:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1484/</link>
			<guid>http://blog.linuxphp.org/archives/1484/</guid>
			<title>了解HTTP服务器推送技术COMET</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	一个WebChat的服务（可插拔jetty版module）</p>
<p>
	<a href="http://hedahai119.iteye.com/blog/736673">http://hedahai119.iteye.com/blog/736673</a></p>
<p>
	&nbsp;</p>
<p>
	jquerycomet</p>
<p>
	<a href="http://code.google.com/p/jquerycomet/source/checkout">http://code.google.com/p/jquerycomet/source/checkout</a></p>
<p>
	&nbsp;</p>
<h1 style="font-family: arial, nsimsun, sans-serif; font-size: 1.7em; font-weight: normal; height: 33px; list-style-type: none; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
	Comet：基于 HTTP 长连接的&ldquo;服务器推&rdquo;技术</h1>
<p>
	<a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/">http://www.ibm.com/developerworks/cn/web/wa-lo-comet/</a></p>
<p>
	&nbsp;</p>
<p>
	Grizzly是一种应用程序框架</p>
<p>
	<a href="http://download.java.net/maven/2/com/sun/grizzly/">http://download.java.net/maven/2/com/sun/grizzly/</a></p>
<p>
	&nbsp;</p>
<p>
	GlassFish 是一款强健的商业兼容应用服务器</p>
<p>
	<a href="http://glassfish.java.net/public/downloadsindex.html#top">http://glassfish.java.net/public/downloadsindex.html#top</a></p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1484/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/comet/">comet</category>
			<pubDate>Fri, 27 Jan 2012 10:27:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1483/</link>
			<guid>http://blog.linuxphp.org/archives/1483/</guid>
			<title>svn忽略文件新法</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	&nbsp;</p>
<pre>
vim .svnignore</pre>
<p>
	每种要忽略的文件加一行，如：<br />
	*.pyc<br />
	*.cash<br />
	test.php</p>
<p>
	然后</p>
<br />
<pre>
svn propset svn:ignore -F .svnignore .
svn status</pre>
<p>
	注意：</p>
<p>
	这个.svnignore文件在更改后都需要执行上面的添加操作</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1483/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/svn/">svn</category>
			<pubDate>Fri, 27 Jan 2012 10:08:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1482/</link>
			<guid>http://blog.linuxphp.org/archives/1482/</guid>
			<title>C语言之libevent和socket示例（一）</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	源码和注释：</p>
<pre>
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;netinet/tcp.h&gt;
#include &lt;event.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;errno.h&gt;
#include &lt;fcntl.h&gt;

//内部函数，只能被本文件中的函数调用
static short ListenPort = 8080;
static long ListenAddr = INADDR_ANY;//任意地址，值就是0
static int   MaxConnections = 1024;

static int ServerSocket;
//创建event
static struct event ServerEvent;

//将一个socket设置成非阻塞模式
//不论什么平台编写网络程序，都应该使用NONBLOCK socket的方式。这样可以保证你的程序至少不会在recv/send/accept/connect这些操作上发生block从而将整个网络服务都停下来
int SetNonblock(int fd)
{
	int flags;
	//fcntl()用来操作文件描述符的一些特性
	if ((flags = fcntl(fd, F_GETFL)) == -1) {
		return -1;
	}

	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
		return -1;
	}
	return 0;
}

//这个函数当客户端的socket可读时由libevent调用
void ServerRead(int fd, short ev, void *arg)
{
	struct client *client = (struct client *)arg;
	u_char buf[8196];
	int len, wlen;

	//会把参数fd 所指的文件传送count个字节到buf指针所指的内存中
	len = read(fd, buf, sizeof(buf));
	if (len == 0) {
		/* 客户端断开连接，在这里移除读事件并且释放客户数据结构 */
		printf(&quot;disconnected\n&quot;);
		close(fd);
		event_del(&amp;ServerEvent);
		free(client);
		return;
	} else if (len &lt; 0) {
		/* 出现了其它的错误，在这里关闭socket，移除事件并且释放客户数据结构 */
		printf(&quot;socket fail %s\n&quot;, strerror(errno));
		close(fd);
		event_del(&amp;ServerEvent);
		free(client);
		return;
	}
	/* 
	   为了简便，我们直接将数据写回到客户端。通常我们不能在非阻塞的应用程序中这么做，
       我们应该将数据放到队列中，等待可写事件的时候再写回客户端。 
	   如果使用多个终端进行socket连接会出现错误socket fail Bad file descriptor
	 */
	wlen = write(fd, buf, len);
	if (wlen &lt; len) {
		printf(&quot;not all data write back to client\n&quot;);
	}
}

/*
   当有一个连接请求准备被接受时，这个函数将被libevent调用并传递给三个变量: 
   int fd:触发事件的文件描述符. 
   short event:触发事件的类型EV_TIMEOUT,EV_SIGNAL, EV_READ, or EV_WRITE. 
   void* :由arg参数指定的变量. 
*/
void ServerAccept(int fd, short ev, void *arg)
{
	int cfd;
	struct sockaddr_in addr;
	socklen_t addrlen = sizeof(addr);
	int yes = 1;
	int retval;

	//将从连接请求队列中获得连接信息，创建新的套接字，并返回该套接字的文件描述符。
	//新创建的套接字用于服务器与客户机的通信，而原来的套接字仍然处于监听状态。
	//该函数的第一个参数指定处于监听状态的流套接字
	cfd = accept(fd, (struct sockaddr *)&amp;addr, &amp;addrlen);
	if (cfd == -1) {
		printf(&quot;accept(): can not accept client connection&quot;);
		return;
	}
	if (SetNonblock(cfd) == -1) {
		close(cfd);
		return;
	}

	//设置与某个套接字关联的选项
	//参数二 IPPROTO_TCP:TCP选项
	//参数三 TCP_NODELAY 不使用Nagle算法 选择立即发送数据而不是等待产生更多的数据然后再一次发送
	//       更多参数TCP_NODELAY 和 TCP_CORK
	//参数四 新选项TCP_NODELAY的值
	if (setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &amp;yes, sizeof(yes)) == -1) {
		printf(&quot;setsockopt(): TCP_NODELAY %s\n&quot;, strerror(errno));
		close(cfd);
		return;
	}

	event_set(&amp;ServerEvent, cfd, EV_READ | EV_PERSIST, ServerRead, NULL);
	event_add(&amp;ServerEvent, NULL);
	
	printf(&quot;Accepted connection from %s\n&quot;, inet_ntoa(addr.sin_addr));
}
int NewSocket(void)
{
	struct sockaddr_in sa;

	//socket函数来创建一个能够进行网络通信的套接字。
	//第一个参数指定应用程序使用的通信协议的协议族，对于TCP/IP协议族，该参数置AF_INET;
	//第二个参数指定要创建的套接字类型
	//流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW
	//第三个参数指定应用程序所使用的通信协议。
	ServerSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (ServerSocket == -1) {
		printf(&quot;socket(): can not create server socket\n&quot;);
		return -1;
	}
	if (SetNonblock(ServerSocket) == -1) {
		return -1;
	}

	//清空内存数据
	memset(&amp;sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	//htons将一个无符号短整型数值转换为网络字节序
	sa.sin_port = htons(ListenPort);
	//htonl将主机的无符号长整形数转换成网络字节顺序
	sa.sin_addr.s_addr = htonl(ListenAddr);

	//(struct sockaddr*)&amp;sa将sa强制转换为sockaddr类型的指针
	/*struct sockaddr 
		数据结构用做bind、connect、recvfrom、sendto等函数的参数，指明地址信息。
		但一般编程中并不直接针对此数据结构操作，而是使用另一个与sockaddr等价的数据结构 struct sockaddr_in
		sockaddr_in和sockaddr是并列的结构，指向sockaddr_in的结构体的指针也可以指向
		sockadd的结构体，并代替它。也就是说，你可以使用sockaddr_in建立你所需要的信息,
		在最后用进行类型转换就可以了
	*/
	//bind函数用于将套接字绑定到一个已知的地址上
	if (bind(ServerSocket, (struct sockaddr*)&amp;sa, sizeof(sa)) == -1) {
		close(ServerSocket);
		printf(&quot;bind(): can not bind server socket&quot;);
		return -1;
	}
	
	//执行listen 之后套接字进入被动模式
	//MaxConnections 连接请求队列的最大长度,队列满了以后，将拒绝新的连接请求
	if (listen(ServerSocket, MaxConnections) == -1) {
		printf(&quot;listen(): can not listen server socket&quot;);
		close(ServerSocket);
		return -1;
	}

	/*
	   event_set的参数：
	   + 参数1:  为要创建的event
	   + 参数2:  file descriptor，创建纯计时器可以设置其为-1，即宏evtimer_set定义的那样
	   + 参数3:  设置event种类，常用的EV_READ, EV_WRITE, EV_PERSIST, EV_SIGNAL, EV_TIMEOUT，纯计时器设置该参数为0
	   + 参数4:  event被激活之后触发的callback函数
	   + 参数5:  传递给callback函数的参数
	   备注：
			如果初始化event的时候设置其为persistent的(设置了EV_PERSIST)，
			则使用event_add将其添加到侦听事件集合后(pending状态)，
			该event会持续保持pending状态，即该event可以无限次参加libevent的事件侦听。
			每当其被激活触发callback函数执行之后，该event自动从active转回为pending状态，
			继续参加libevent的侦听(当激活条件满足，又可以继续执行其callback)。
			除非在代码中使用event_del()函数将该event从libevent的侦听事件集合中删除。
			如果不通过设置EV_PERSIST使得event是persistent的，需要在event的callback中再次调用event_add
			(即在每次pending变为active之后，在callback中再将其设置为pending)
	 */
	event_set(&amp;ServerEvent, ServerSocket, EV_READ | EV_PERSIST, ServerAccept, NULL);
	//将event添加到libevent侦听的事件集中
	if (event_add(&amp;ServerEvent, 0) == -1) {
		printf(&quot;event_add(): can not add accept event into libevent&quot;);
		close(ServerSocket);
		return -1;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	int retval;
	
	//初始化event base 使用默认的全局current_base
	event_init();
	
	retval = NewSocket();
	if (retval == -1) {
		exit(-1);
	}
	//event_dispatch() 启动事件队列系统，开始监听（并接受）请求
	event_dispatch();
	
	return 0;
}
</pre>
<p>
	&nbsp;</p>
<p>
	编译方法：</p>
<p>
	先安装libevent</p>
<p>
	&nbsp;</p>
<pre>
wget http://httpsqs.googlecode.com/files/libevent-2.0.12-stable.tar.gz
tar zxvf libevent-2.0.12-stable.tar.gz
cd libevent-2.0.12-stable/
./configure --prefix=/usr/local/libevent-2.0.12-stable/
make
make install
cd ../
</pre>
<p>
	&nbsp;编译</p>
<pre>
gcc socket.c -o socket -Wl,-rpath,/usr/local/libevent-2.0.12-stable/lib/ -L/usr/local/libevent-2.0.12-stable/lib/ -levent -I/usr/local/libevent-2.0.12-stable/include/</pre>
<p>
	测试:</p>
<p>
	&nbsp;</p>
<pre>
终端执行
./socket
另外开一个终端
telnet 127.0.0.1 8080</pre>
<p>
	<br />
	阅读：</p>
<p>
	<a href="http://nyim.blog.163.com/blog/static/241491642009112001525291/" target="_blank">http://nyim.blog.163.com/blog/static/241491642009112001525291/</a></p>
<p>
	<a href="http://www.cnblogs.com/cnspace/archive/2011/07/19/2110891.html">http://www.cnblogs.com/cnspace/archive/2011/07/19/2110891.html</a><br />
	<a href="http://54min.com/post/non-blocking-socket-server-using-libevent.html">http://54min.com/post/non-blocking-socket-server-using-libevent.html</a></p>
<p>
	<a href="http://blog.sina.com.cn/s/blog_53a2ecbf010095db.html">http://blog.sina.com.cn/s/blog_53a2ecbf010095db.html</a></p>
<p>
	<a href="http://code.google.com/p/ishbits/source/browse/trunk/libevent-examples/echo-server/libevent_echosrv1.c">http://code.google.com/p/ishbits/source/browse/trunk/libevent-examples/echo-server/libevent_echosrv1.c</a></p>
<p><strong><a title="socket.c" href="http://blog.linuxphp.org/attachment.php?id=421" target="_blank">socket.c</a></strong> (7.34 K, 下载次数:0, 上传时间:Fri, 27 Jan 2012 00:08:10 +0000)</p>]]></description>
			<link>http://blog.linuxphp.org/archives/1482/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Thu, 26 Jan 2012 23:58:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1481/</link>
			<guid>http://blog.linuxphp.org/archives/1481/</guid>
			<title>C语言之将socket设置为非阻塞方式</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	用以下方法将socket设置为非阻塞方式<br />
	int flags = fcntl(socket, F_GETFL, 0);<br />
	fcntl(socket, F_SETFL, flags | O_NONBLOCK);<br />
	&nbsp;</p>
<p>
	将非阻塞的设置回阻塞可以用<br />
	int flags = fcntl(socket, F_GETFL, 0);<br />
	fcntl(socket, F_SETFL, flags &amp; ~O_NONBLOCK);</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1481/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Thu, 26 Jan 2012 23:54:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1480/</link>
			<guid>http://blog.linuxphp.org/archives/1480/</guid>
			<title>C语言之IP处理函数</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	&nbsp;</p>
<div>
	inet_aton()将一个&quot;.&quot;点隔的字符串IP地址转换为一个32位的网络序列IP地址。</div>
<div>
	inet_ntoa() 将32位的网络序列IP地址转换成&quot;.&quot;点隔的字符串格式。</div>
<div>
	&nbsp;</div>
<div>
	ntohl()&nbsp;将网络字节顺]序转换为一个无符号长整形数主机字节顺序。</div>
<div>
	htonl()将一个无符号长整形数数值转换成网络字节顺序。</div>
<div>
	&nbsp;</div>
<div>
	<br />
	<div>
		ntohs()将网络字节顺序转换为一个无符号短整形数主机字节顺序。</div>
</div>
<div>
	htons()将一个无符号短整型数值转换为网络字节序。</div>
<p>
	&nbsp;</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1480/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Thu, 26 Jan 2012 23:42:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1479/</link>
			<guid>http://blog.linuxphp.org/archives/1479/</guid>
			<title>C语言之进程对资源使用的限制</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	core文件的最大字节数</p>
<pre>
#include &lt;sys/resource.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;

int main(int argc, char *argv[])
{
    //在Linux系统中，Resouce limit指在一个进程的执行过程中，它所能得到的资源的限制，比如进程的corefile的最大值，虚拟内存的最大值等。
    struct rlimit rlim_new;
    struct rlimit rlim;

    //getrlimit查询设定最大的core文件,若成功返回值为0
    if (getrlimit(RLIMIT_CORE, &amp;rlim)==0) {
        printf(&quot;old soft limit:%d\n&quot;, rlim.rlim_cur);
        printf(&quot;old hard limit:%d\n&quot;, rlim.rlim_max);
        //一个无限的限制由常量RLIM_INFINITY指定
        //rlim_t rlim_cur; /*当前（软）限制*/
        //rlim_t rlim_max; /*硬限制*/
        rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
        if (setrlimit(RLIMIT_CORE, &amp;rlim_new)!=0) {
            /* failed. try raising just to the old max */
            rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
            (void) setrlimit(RLIMIT_CORE, &amp;rlim_new);
        }
    }

    if ((getrlimit(RLIMIT_CORE, &amp;rlim)!=0) || rlim.rlim_cur==0) {
        exit(1);
    }
    printf(&quot;new soft limit:%d\n&quot;, rlim.rlim_cur);
    printf(&quot;new hard limit:%d\n&quot;, rlim.rlim_max);
}</pre>
<p>
	&nbsp;</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1479/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Thu, 26 Jan 2012 13:27:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1478/</link>
			<guid>http://blog.linuxphp.org/archives/1478/</guid>
			<title>C语言之忽略SIGPIPE信号</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	&nbsp;</p>
<pre>
#include &lt;stdlib.h&gt;
#include &lt;sys/signal.h&gt;

void SetupSignal() {
    struct sigaction sa;

    //在linux下写socket的程序的时候，如果尝试send到一个disconnected socket上，就会让底层抛出一个SIGPIPE信号。
    //这个信号的缺省处理方法是退出进程
    //重载这个信号的处理方法,如果接收到一个SIGPIPE信号，忽略该信号
    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    //sigemptyset()用来将参数set信号集初始化并清空
    if (sigemptyset(&amp;sa.sa_mask) == -1 ||
            sigaction(SIGPIPE, &amp;sa, 0) == -1) {
        exit(-1);
    }
}

int main(int argc, char *argv[])
{
    SetupSignal();
    while(1){}
    return 0;
}</pre>
<p>
	&nbsp;</p>
<p>
	测试方法：</p>
<p>
	1.执行signal程序</p>
<p>
	通过另外的一个Linux终端查看程序使用的pid</p>
<p>
	ps -ef|grep signal</p>
<p>
	root &nbsp; &nbsp; &nbsp;4234 &nbsp;3124 93 13:02 pts/3 &nbsp; &nbsp;00:00:06 ./signal</p>
<p>
	发送信号</p>
<p>
	kill -SIGPIPE 4234</p>
<p>
	可以看到signal程序不会退出</p>
<p>
	&nbsp;</p>
<p>
	2.将main函数中SetupSignal();行注释掉编译</p>
<p>
	&nbsp;</p>
<p style="line-height: 21px; ">
	执行signal程序</p>
<p style="line-height: 21px; ">
	通过另外的一个Linux终端查看程序使用的pid</p>
<p style="line-height: 21px; ">
	ps -ef|grep signal</p>
<p style="line-height: 21px; ">
	root &nbsp; &nbsp; &nbsp;4253 &nbsp;3124 99 13:03 pts/3 &nbsp; &nbsp;00:00:02 ./signal</p>
<p style="line-height: 21px; ">
	kill -SIGPIPE 4253</p>
<p style="line-height: 21px; ">
	可以看到signal程序退出了</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1478/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Thu, 26 Jan 2012 13:12:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1477/</link>
			<guid>http://blog.linuxphp.org/archives/1477/</guid>
			<title>linux信号和信号含义</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	&nbsp;</p>
<p style="line-height: normal; ">
	我们运行如下命令，可看到Linux支持的信号列表：</p>
<pre style="line-height: normal; ">
$ kill -l
1) SIGHUP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2) SIGINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3) SIGQUIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4) SIGILL
 5) SIGTRAP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6) SIGABRT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7) SIGBUS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8) SIGFPE
 9) SIGKILL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10) SIGUSR1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11) SIGSEGV&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12) SIGUSR2
13) SIGPIPE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 14) SIGALRM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 15) SIGTERM&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 17) SIGCHLD
18) SIGCONT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 19) SIGSTOP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20) SIGTSTP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 21) SIGTTIN
22) SIGTTOU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 23) SIGURG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 24) SIGXCPU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 25) SIGXFSZ
26) SIGVTALRM&nbsp;&nbsp;&nbsp; 27) SIGPROF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 28) SIGWINCH&nbsp;&nbsp;&nbsp;&nbsp; 29) SIGIO
30) SIGPWR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 31) SIGSYS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 34) SIGRTMIN&nbsp;&nbsp;&nbsp;&nbsp; 35) SIGRTMIN+1
36) SIGRTMIN+2&nbsp;&nbsp; 37) SIGRTMIN+3&nbsp;&nbsp; 38) SIGRTMIN+4&nbsp;&nbsp; 39) SIGRTMIN+5
40) SIGRTMIN+6&nbsp;&nbsp; 41) SIGRTMIN+7&nbsp;&nbsp; 42) SIGRTMIN+8&nbsp;&nbsp; 43) SIGRTMIN+9
44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13
52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8&nbsp;&nbsp; 57) SIGRTMAX-7&nbsp;&nbsp; 58) SIGRTMAX-6&nbsp;&nbsp; 59) SIGRTMAX-5
60) SIGRTMAX-4&nbsp;&nbsp; 61) SIGRTMAX-3&nbsp;&nbsp; 62) SIGRTMAX-2&nbsp;&nbsp; 63) SIGRTMAX-1
64) SIGRTMAX</pre>
<p style="line-height: normal; ">
	列表中，编号为1 ~ 31的信号为传统UNIX支持的信号，是不可靠信号(非实时的)，编号为32 ~ 63的信号是后来扩充的，称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队，可能会造成信号丢失，而后者不会。</p>
<p style="line-height: normal; ">
	下面我们对编号小于SIGRTMIN的信号进行讨论。</p>
<p style="line-height: normal; ">
	1) SIGHUP<br style="line-height: normal; " />
	本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。</p>
<p style="line-height: normal; ">
	登录Linux时，系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序，包括前台进程组和后台进程组，一般都属于这个Session。当用户退出Linux登录时，前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程，因此前台进程组和后台有终端输出的进程就会中止。不过可以捕获这个信号，比如wget能捕获SIGHUP信号，并忽略它，这样就算退出了Linux登录，wget也能继续下载。</p>
<p style="line-height: normal; ">
	此外，对于与终端脱离关系的守护进程，这个信号用于通知它重新读取配置文件。</p>
<p style="line-height: normal; ">
	2) SIGINT<br style="line-height: normal; " />
	程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出，用于通知前台进程组终止进程。</p>
<p style="line-height: normal; ">
	3) SIGQUIT<br style="line-height: normal; " />
	和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。</p>
<p style="line-height: normal; ">
	4) SIGILL<br style="line-height: normal; " />
	执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。</p>
<p style="line-height: normal; ">
	5) SIGTRAP<br style="line-height: normal; " />
	由断点指令或其它trap指令产生. 由debugger使用。</p>
<p style="line-height: normal; ">
	6) SIGABRT<br style="line-height: normal; " />
	调用abort函数生成的信号。</p>
<p style="line-height: normal; ">
	7) SIGBUS<br style="line-height: normal; " />
	非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。</p>
<p style="line-height: normal; ">
	8) SIGFPE<br style="line-height: normal; " />
	在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。</p>
<p style="line-height: normal; ">
	9) SIGKILL<br style="line-height: normal; " />
	用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了，可尝试发送这个信号。</p>
<p style="line-height: normal; ">
	10) SIGUSR1<br style="line-height: normal; " />
	留给用户使用</p>
<p style="line-height: normal; ">
	11) SIGSEGV<br style="line-height: normal; " />
	试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.</p>
<p style="line-height: normal; ">
	12) SIGUSR2<br style="line-height: normal; " />
	留给用户使用</p>
<p style="line-height: normal; ">
	13) SIGPIPE<br style="line-height: normal; " />
	管道破裂。这个信号通常在进程间通信产生，比如采用FIFO(管道)通信的两个进程，读管道没打开或者意外终止就往管道写，写进程会收到SIGPIPE信号。此外用Socket通信的两个进程，写进程在写Socket的时候，读进程已经终止。</p>
<p style="line-height: normal; ">
	14) SIGALRM<br style="line-height: normal; " />
	时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.</p>
<p style="line-height: normal; ">
	15) SIGTERM<br style="line-height: normal; " />
	程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出，shell命令kill缺省产生这个信号。如果进程终止不了，我们才会尝试SIGKILL。</p>
<p style="line-height: normal; ">
	17) SIGCHLD<br style="line-height: normal; " />
	子进程结束时, 父进程会收到这个信号。</p>
<p style="line-height: normal; ">
	如果父进程没有处理这个信号，也没有等待(wait)子进程，子进程虽然终止，但是还会在内核进程表中占有表项，这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号，或者捕捉它，或者wait它派生的子进程，或者父进程先终止，这时子进程的终止自动由init进程来接管)。</p>
<p style="line-height: normal; ">
	18) SIGCONT<br style="line-height: normal; " />
	让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符</p>
<p style="line-height: normal; ">
	19) SIGSTOP<br style="line-height: normal; " />
	停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.</p>
<p style="line-height: normal; ">
	20) SIGTSTP<br style="line-height: normal; " />
	停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号</p>
<p style="line-height: normal; ">
	21) SIGTTIN<br style="line-height: normal; " />
	当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.</p>
<p style="line-height: normal; ">
	22) SIGTTOU<br style="line-height: normal; " />
	类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.</p>
<p style="line-height: normal; ">
	23) SIGURG<br style="line-height: normal; " />
	有&quot;紧急&quot;数据或out-of-band数据到达socket时产生.</p>
<p style="line-height: normal; ">
	24) SIGXCPU<br style="line-height: normal; " />
	超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。</p>
<p style="line-height: normal; ">
	25) SIGXFSZ<br style="line-height: normal; " />
	当进程企图扩大文件以至于超过文件大小资源限制。</p>
<p style="line-height: normal; ">
	26) SIGVTALRM<br style="line-height: normal; " />
	虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.</p>
<p style="line-height: normal; ">
	27) SIGPROF<br style="line-height: normal; " />
	类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.</p>
<p style="line-height: normal; ">
	28) SIGWINCH<br style="line-height: normal; " />
	窗口大小改变时发出.</p>
<p style="line-height: normal; ">
	29) SIGIO<br style="line-height: normal; " />
	文件描述符准备就绪, 可以开始进行输入/输出操作.</p>
<p style="line-height: normal; ">
	30) SIGPWR<br style="line-height: normal; " />
	Power failure</p>
<p style="line-height: normal; ">
	31) SIGSYS<br style="line-height: normal; " />
	非法的系统调用。</p>
<p style="line-height: normal; ">
	在以上列出的信号中，程序不可捕获、阻塞或忽略的信号有：SIGKILL,SIGSTOP<br style="line-height: normal; " />
	不能恢复至默认动作的信号有：SIGILL,SIGTRAP<br style="line-height: normal; " />
	默认会导致进程流产的信号有：SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ<br style="line-height: normal; " />
	默认会导致进程退出的信号有：SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM<br style="line-height: normal; " />
	默认会导致进程停止的信号有：SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU<br style="line-height: normal; " />
	默认进程忽略的信号有：SIGCHLD,SIGPWR,SIGURG,SIGWINCH</p>
<p style="line-height: normal; ">
	此外，SIGIO在SVR4是退出，在4.3BSD中是忽略；SIGCONT在进程挂起时是继续，否则是忽略，不能被阻塞</p>
<p>
	原文：</p>
<p>
	<a href="http://hi.baidu.com/walfer/blog/item/fa649f457276f53e86947302.html">http://hi.baidu.com/walfer/blog/item/fa649f457276f53e86947302.html</a></p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1477/</link>
			<category domain="http://blog.linuxphp.org/category/yuedu/">知识阅读</category>
			<category domain="http://blog.linuxphp.org/category/zhuanzai/">转载收藏</category>
			<category domain="http://blog.linuxphp.org/tag/%E4%BF%A1%E5%8F%B7/">信号</category>
			<category domain="http://blog.linuxphp.org/tag/linux/">linux</category>
			<pubDate>Wed, 25 Jan 2012 23:26:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1476/</link>
			<guid>http://blog.linuxphp.org/archives/1476/</guid>
			<title>C语言之创建后台守护进程</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	实现一个后台进程需要完成一系列的工作，包括：关闭所有的文件描述字；改变当前工作目录；重设文件存取屏蔽码(umask) ；在后台执行；脱离进程组；忽略终端I/O信号；脱离控制终端。</p>
<pre>
#include &lt;stdio.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;

int daemonize(void)
{
        int fd;
        switch(fork()) {
                case -1:
                        return (-1);
                case 0:
                        break;
                default:
                        //将父进程结束,让子进程变成真正的孤儿进程,并被init进程接管
                        exit(0);
        }
        //子进程成为新的会话组长和新的进程组长，并与原来的登录会话和进程组脱离
        setsid();

        if ((fd = open(&quot;daemon.log&quot;, O_CREAT|O_RDWR|O_APPEND, 0)) != -1) {
                //dup2(int oldhandle, int newhandle)复制文件句柄
                dup2(fd, STDIN_FILENO);
                dup2(fd, STDOUT_FILENO);
                dup2(fd, STDERR_FILENO);
                //0,1和2文件句柄分别与标准输入,标准输出,标准错误输出相关联
                //所以用户应用程序调用open函数打开文件时,默认都是以3索引为开始句柄
                //fd已经由新的句柄代替,关闭fd句柄
                if (fd &gt; STDERR_FILENO) (void)close(fd);
        }
        return 0;
}

int main(int argc, char *argv[])
{
        daemonize();
        printf(&quot;%s\n&quot;,&quot;hello&quot;);
        return 0;
}</pre>
<p>
	&nbsp;</p>
<p>
	扩展阅读：</p>
<p>
	<a href="http://www.cnblogs.com/xuxm2007/archive/2011/07/29/2121280.html">http://www.cnblogs.com/xuxm2007/archive/2011/07/29/2121280.html</a></p>
<p>
	<a href="http://blog.csdn.net/yyyzlf/article/details/5267954">http://blog.csdn.net/yyyzlf/article/details/5267954</a></p>
<p>
	<a href="http://docs.linuxtone.org/ebooks/C&amp;CPP/c/ch34s03.html">http://docs.linuxtone.org/ebooks/C&amp;CPP/c/ch34s03.html</a></p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1476/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Wed, 25 Jan 2012 22:41:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1475/</link>
			<guid>http://blog.linuxphp.org/archives/1475/</guid>
			<title>C语言之分析命令行参数</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	&nbsp;</p>
<pre>
#include &lt;stdio.h&gt;
//unix类系统定义符号常量的头文件
#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;

void usage(void)
{
    printf(&quot;+--------------------------------------\n&quot;);
    printf(&quot;| -l &lt;ip_addr&gt; interface to listen on\n&quot;);
    printf(&quot;| -p &lt;num&gt; port num to listen on\n&quot;);
    printf(&quot;| -h print help and exit\n&quot;);
    printf(&quot;+--------------------------------------\n&quot;);
    printf(&quot;\n&quot;);
    return;
}
void ParseOptions(int argc, char *argv[])
{
    int c;
    short port;
    opterr = 0;
    // getopt()用来分析命令行参数
    //参数 optstring为选项字符串， 告知 getopt()可以处理哪个选项以及哪个选项需要参数
    //如果选项字符串里的字母后接着冒号&quot;:&quot;，则表示还有相关的参数,如果接着&quot;::&quot;，则表示选项与参数之间不能有空格。
    //全域变量optarg即会指向此额外参数。
    //如果在处理期间遇到了不符合optstring指定的其他选项getopt()将显示一个错误消息
    //如果不希望getopt()印出错信息，则只要将全域变量opterr设为0即可。
    while ((c = getopt(argc, argv, &quot;l:p::h&quot;)) != -1) {
        switch(c) {
            case &#39;l&#39;://listen
                printf(&quot;host:%s\n&quot;, optarg);
                break;
            case &#39;p&#39;://port
                port = atoi(optarg);
                printf(&quot;port:%d\n&quot;,port);
                break;
            case &#39;h&#39;://help
                usage();
                exit(0);
            default:
                printf(&quot;illegal argument:%c\n&quot;,c);
                break;
        }
    }
}


int main(int argc, char *argv[])
{
    ParseOptions(argc, argv);
    return 0;
}</pre>
<p>
	&nbsp;</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1475/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Wed, 25 Jan 2012 17:45:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1474/</link>
			<guid>http://blog.linuxphp.org/archives/1474/</guid>
			<title>C语言之变参日志函数</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	&nbsp;</p>
<pre>
#include &lt;stdio.h&gt;
//是Linux 系统的日期时间头文件。通常会包含include time.h
#include &lt;sys/time.h&gt;
// 是ISO C99 标准日期时间头文件。编写的代码平台无关的需要引入
#include &lt;time.h&gt;

//让函式能够接收不定量参数，va_list需要
#include &lt;stdarg.h&gt;

#define DEBUG 1
#define LOGFILE &quot;write.log&quot;

//编写的代码平台无关的
#define __need_time_t
#define __need_timespec

#define LOG_ERROR 0
#define LOG_NOTICE 1
#define LOG_DEBUG 2
//fmt为格式化字串，如%d%s%c%f
static void WriteLog(int level, const char *fmt, ...)
{
    va_list ap;
    FILE *fp;

#ifdef DEBUG
    fp = stdout;
#else
    fp = fopen(LOGFILE, &quot;a+&quot;);
    if (!fp) return;
#endif

    va_start(ap, fmt);
    char *c = &quot;-*+&quot;;
    char buf[64];
    time_t now;

    //1970年1月1日00:00:00（称为UNIX系统的Epoch时间）到当前时刻的秒数
    now = time(NULL);
    //gmtime 把日期和时间转换为格林威治(GMT)时间的函数,结果由结构tm返回
    //strftime将tm结构体时间格式化,%d 十进制表示的每月的第几天%b 月分的简写
    strftime(buf, 64, &quot;[%d %b %H:%M:%S]&quot;, gmtime(&amp;now));
    //格式化输出到一个流/文件中
    fprintf(fp, &quot;%s %c &quot;, buf, c[level]);
    //格式化输出到一个流/文件中
    int cnt = vfprintf(fp, fmt, ap);
    fprintf(fp, &quot; write %d bite&quot;,cnt);
    fprintf(fp, &quot;\n&quot;);
    //清除文件缓冲区，文件以写方式打开时将缓冲区内容写入文件
    //fflush(stdout)刷新标准输出缓冲区，把输出缓冲区里的东西打印到标准输出设备上
    fflush(fp);
    va_end(ap);
#ifndef DEBUG
    fclose(fp);
#endif

}
int main(int argc, char *argv[])
{
    WriteLog(LOG_ERROR, &quot;%s %s &quot;,&quot;this is a test&quot;, &quot;hello world&quot;);
    WriteLog(LOG_NOTICE, &quot;%s %s &quot;, &quot;this is a test2&quot;, &quot;hello world&quot;);
    WriteLog(LOG_DEBUG, &quot;%s %s &quot;, &quot;this is a test3&quot;, &quot;hello world&quot;);
}</pre>
<p>
	&nbsp;</p>
<p>
	此函数主要是学习va_list和vfprintf和使用，需要注意的是vfprintf传的第二个参数fmt是格式化字符串，而不是其它，刚开始漏传导致第二个参数不能打印。</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1474/</link>
			<category domain="http://blog.linuxphp.org/category/develop/">开发技术</category>
			<category domain="http://blog.linuxphp.org/tag/c%E8%AF%AD%E8%A8%80/">c语言</category>
			<pubDate>Tue, 24 Jan 2012 23:55:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1473/</link>
			<guid>http://blog.linuxphp.org/archives/1473/</guid>
			<title>新年到，全线清扫笔记本灰尘</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	彻底清理下IBM X61键盘下的灰尘，头发等杂物。</p>
<p>
	<div class="attach"><a href="http://blog.linuxphp.org/attachment.php?id=420" target="_blank"><img src="http://blog.linuxphp.org/attachments/date_201201/thumb_61d8307dc7a804acea0a7f5759860baa.jpg" border="0" alt="dsc_0051.jpg&#13;&#13;大小: 156.15 K&#13;尺寸:  x &#13;浏览: 1 次&#13;点击打开新窗口浏览全图" width="500" height="391" /></a></div></p>
<p>
	清理完键盘也同时把风扇也清理了下，才用一年，好多灰啦！！</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1473/</link>
			<category domain="http://blog.linuxphp.org/category/zatan/">杂七杂八</category>
			<pubDate>Sun, 22 Jan 2012 12:39:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1472/</link>
			<guid>http://blog.linuxphp.org/archives/1472/</guid>
			<title>蟑螂将最后的生命交给了下一代</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	租的老房子，蟑螂真的是疯狂的不得了，忍无可忍了。</p>
<p>
	&nbsp;</p>
<p>
	安速蟑螂屋真的很好用</p>
<p>
	<div class="attach"><a href="http://blog.linuxphp.org/attachment.php?id=417" target="_blank"><img src="http://blog.linuxphp.org/attachments/date_201201/thumb_67493f274a3b8fba7ecb268b320e4de9.jpg" border="0" alt="dsc_0093.jpg&#13;&#13;大小: 143.41 K&#13;尺寸:  x &#13;浏览: 0 次&#13;点击打开新窗口浏览全图" width="500" height="261" /></a></div></p>
<p>
	全景图</p>
<p>
	<div class="attach"><a href="http://blog.linuxphp.org/attachment.php?id=418" target="_blank"><img src="http://blog.linuxphp.org/attachments/date_201201/thumb_bb7108d14e587c5e5b8f65a1ec8aafc4.jpg" border="0" alt="dsc_0092.jpg&#13;&#13;大小: 141.24 K&#13;尺寸:  x &#13;浏览: 0 次&#13;点击打开新窗口浏览全图" width="500" height="332" /></a></div></p>
<p>
	特写</p>
<p>
	<div class="attach"><a href="http://blog.linuxphp.org/attachment.php?id=419" target="_blank"><img src="http://blog.linuxphp.org/attachments/date_201201/thumb_e594a4523d8349eb1a832cc29b4ff572.jpg" border="0" alt="dsc_0091.jpg&#13;&#13;大小: 113.41 K&#13;尺寸:  x &#13;浏览: 0 次&#13;点击打开新窗口浏览全图" width="500" height="266" /></a></div></p>
<p>
	不是微距镜头，拍的不是很清，可以看到蟑螂在生命的最后，生出一堆的小蟑螂，生命力顽强呀。</p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1472/</link>
			<category domain="http://blog.linuxphp.org/category/zatan/">杂七杂八</category>
			<pubDate>Sat, 21 Jan 2012 11:27:00 +0000</pubDate>
		</item>
		<item>
			<link>http://blog.linuxphp.org/archives/1471/</link>
			<guid>http://blog.linuxphp.org/archives/1471/</guid>
			<title>ImageMagick实现GIF动画缩略图</title>
			<author>linuxphp@qq.com(keminar)</author>
			<description><![CDATA[贵贵的博客 ( http://blog.linuxphp.org/ ) : <p>
	GIF动画为了压缩，会以第一帧为模板，其余各帧按照适当的偏移量依次累加，并只保留不同的像素，结果是导致各帧尺寸不尽相同，为缩略图造成障碍。</p>
<p>
	&nbsp;</p>
<p>
	如何用PHP中的Imagick模块来完美实现GIF动画缩略图</p>
<p>
	&nbsp;</p>
<pre>
&lt;?php

$image = new Imagick(&#39;old.gif&#39;);

$image = $image-&gt;coalesceImages();

foreach ($image as $frame) {
    $frame-&gt;thumbnailImage(50, 50);
}

$image = $image-&gt;optimizeImageLayers();

$image-&gt;writeImages(&#39;new.gif&#39;, true);

?&gt;</pre>
<p>
	&nbsp;</p>
<p>
	更多内容解释查看原文：<a href="http://huoding.com/2010/12/26/32">http://huoding.com/2010/12/26/32</a></p>
]]></description>
			<link>http://blog.linuxphp.org/archives/1471/</link>
			<category domain="http://blog.linuxphp.org/category/zhuanzai/">转载收藏</category>
			<category domain="http://blog.linuxphp.org/tag/imagemagick/">imagemagick</category>
			<pubDate>Thu, 19 Jan 2012 23:16:00 +0000</pubDate>
		</item>
	</channel>
</rss>

