<?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>记录点滴 &#187; 程序设计</title>
	<atom:link href="http://ganquan.org/blog/category/program/feed/" rel="self" type="application/rss+xml" />
	<link>http://ganquan.org/blog</link>
	<description>http://ganquan.org</description>
	<lastBuildDate>Sun, 05 Feb 2012 14:51:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>just a joke</title>
		<link>http://ganquan.org/blog/2011/07/just-a-joke/</link>
		<comments>http://ganquan.org/blog/2011/07/just-a-joke/#comments</comments>
		<pubDate>Thu, 07 Jul 2011 12:30:42 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[生活点滴]]></category>
		<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://ganquan.org/blog/?p=1701</guid>
		<description><![CDATA[  BOSS：拍照后可以对照片添加特效是我们产品的主要卖点，你们要动脑筋把这个功能做好做强，做出我们品牌的特色，做出让人眼前一亮的产品！   UE：啊哈，如果用户拍照后按井号键就可以给照片添加柔焦效果，按星号键就可以自动调整照片对比度，这是多么方便和易用啊。   DEV：SetKeyHandler(KEY_POUND, add_soft_focus_effect); SetKeyHandler(KEY_STAR, add_auto_contrast_effect);   QA：抱歉，我们的产品是全触屏的，没有物理的井号键和星号键，拍照后也没有弹出软键盘，所以我无法测试这个功能。  ]]></description>
			<content:encoded><![CDATA[<pre> </pre>
<p><span style="font-size: medium;">BOSS：拍照后可以对照片添加特效是我们产品的主要卖点，你们要动脑筋把这个功能做好做强，做出我们品牌的特色，做出让人眼前一亮的产品！</span></p>
<pre> </pre>
<p><span style="font-size: medium;">UE：啊哈，如果用户拍照后按井号键就可以给照片添加柔焦效果，按星号键就可以自动调整照片对比度，这是多么方便和易用啊。</span></p>
<pre> </pre>
<p><span style="font-size: medium;">DEV：SetKeyHandler(KEY_POUND, add_soft_focus_effect); SetKeyHandler(KEY_STAR, add_auto_contrast_effect);</span></p>
<pre> </pre>
<p><span style="font-size: medium;">QA：抱歉，我们的产品是全触屏的，没有物理的井号键和星号键，拍照后也没有弹出软键盘，所以我无法测试这个功能。</span></p>
<pre> </pre>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2011/07/just-a-joke/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>如何在内核空间对文件进行操作</title>
		<link>http://ganquan.org/blog/2010/05/file-operation/</link>
		<comments>http://ganquan.org/blog/2010/05/file-operation/#comments</comments>
		<pubDate>Thu, 06 May 2010 01:52:46 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://ganquan.org/blog/?p=1143</guid>
		<description><![CDATA[在内核空间中，是不能直接使用open, read, write之类的系统调用直接访问文件的。那怎么在驱动中访问一个文件呢？ 我们知道在内核中用struct file来表示一个打开的文件，file结构体中有一个字段f_op，它是struct file_operation的指针。有了这个指针，就可以对文件进行访问了。 代码如下： f_op-&#62;read()和f_op-&#62;write()的第二个参数是char __user *，也就是说是用户空间的地址，所以在驱动中直接传递kernel空间的指针，这两个函数都会返回失败-EFAULT。因此，在f_op-&#62;read()和f_op-&#62;write()之前需要使用void set_fs(mm_segment_t fs);来改变kernel对内存地址检查的处理方式。 该函数的参数fs只有两个取值：USER＿DS，KERNEL＿DS，分别代表用户空间和内核空间，默认情况下，kernel取值为USER_DS。所以可以用set_fs(KERNEL_DS);来让这个两个函数可以接受内核空间的内存。在内核空间中其他用__user修饰的参数，需要用kernel空间的内存代替时，都可以用类似的方法。 另外一个常用的函数f_op-&#62;mmap()则不需要设置fs，因为它的参数中不需要用户空间的内存。事实上，在struct file_operation中，只有read和write函数需要用户空间的内存，其他函数都不需要设置fs。 EOF]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: small;">在内核空间中，是不能直接使用open, read, write之类的系统调用直接访问文件的。那怎么在驱动中访问一个文件呢？</span></p>
<p><span style="font-size: small;">我们知道在内核中用struct file来表示一个打开的文件，file结构体中有一个字段f_op，它是struct file_operation的指针。有了这个指针，就可以对文件进行访问了。<br />
代码如下：</span></p>
<pre class="brush: cpp; title: ; notranslate">
struct file *filp;
mm_segment_t fs;

filp = filp_open(&quot;/path/to/your/file&quot;, O_RDWR, 0);

if(!IS_ERR(filp))
{
    fs=get_fs();
    set_fs(KERNEL_DS);

    filp-&gt;f_op-&gt;read(...);
    filp-&gt;f_op-&gt;write(...);

    set_fs(fs);

    filp-&gt;f_op-&gt;mmap(...);

    /* ... */
}

filp_close(filp, NULL);
</pre>
<p><span style="font-size: small;"><br />
f_op-&gt;read()和f_op-&gt;write()的第二个参数是char __user *，也就是说是用户空间的地址，所以在驱动中直接传递kernel空间的指针，这两个函数都会返回失败-EFAULT。因此，在f_op-&gt;read()和f_op-&gt;write()之前需要使用void set_fs(mm_segment_t fs);来改变kernel对内存地址检查的处理方式。<br />
该函数的参数fs只有两个取值：USER＿DS，KERNEL＿DS，分别代表用户空间和内核空间，默认情况下，kernel取值为USER_DS。所以可以用set_fs(KERNEL_DS);来让这个两个函数可以接受内核空间的内存。在内核空间中其他用__user修饰的参数，需要用kernel空间的内存代替时，都可以用类似的方法。<br />
另外一个常用的函数f_op-&gt;mmap()则不需要设置fs，因为它的参数中不需要用户空间的内存。事实上，在struct file_operation中，只有read和write函数需要用户空间的内存，其他函数都不需要设置fs。<br />
EOF</span></p>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2010/05/file-operation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>判斷一個IPV4地址是否有效</title>
		<link>http://ganquan.org/blog/2010/03/is_valid_ip/</link>
		<comments>http://ganquan.org/blog/2010/03/is_valid_ip/#comments</comments>
		<pubDate>Sat, 06 Mar 2010 17:37:05 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://ganquan.org/blog/?p=770</guid>
		<description><![CDATA[給一個xxx.xxx.xxx.xxx的字符串，判斷該串是否為合法的IP格式。 不要去耍酷用正則表達式，不要去絞盡腦汁寫一堆帶N多bug的C代碼然後覺得自己的代碼效率還可以。 實際生產中，只用這個方法：]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: medium;">給一個xxx.xxx.xxx.xxx的字符串，判斷該串是否為合法的IP格式。</span></p>
<p><span style="font-size: medium;">不要去耍酷用正則表達式，不要去絞盡腦汁寫一堆帶N多bug的C代碼然後覺得自己的代碼效率還可以。</span></p>
<p><span style="font-size: medium;">實際生產中，只用這個方法：</span></p>
<pre class="brush: cpp; title: ; notranslate">
bool  IsValidIP(const char*  ip)
{
    return inet_addr(ip) == -1 ? false : true;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2010/03/is_valid_ip/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>gtk中menu的item同时设置label和icon</title>
		<link>http://ganquan.org/blog/2009/12/gtk-menu-item-img-label/</link>
		<comments>http://ganquan.org/blog/2009/12/gtk-menu-item-img-label/#comments</comments>
		<pubDate>Tue, 08 Dec 2009 15:36:05 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://ganquan.org/blog/?p=580</guid>
		<description><![CDATA[我要同时设置label和使用STOCK中的icon，其实很简单，只要调用gtk_image_menu_item_set_image( )函数就可以了。 这个函数的原型是 void gtk_image_menu_item_set_image (GtkImageMenuItem *image_menu_item, GtkWidget *image); 这个函数这样调用比较方便： menu_item = gtk_image_menu_item_new_with_label (“Label you want to add”); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM(menu_item), gtk_image_new_from_stock (GTK_STOCK_ID, GTK_ICON_SIZE_MENU)); 起先没走对路子，折腾死人。]]></description>
			<content:encoded><![CDATA[<p>我要同时设置label和使用STOCK中的icon，其实很简单，只要调用<span style="font-size: medium;">gtk_image_menu_item_set_image( )</span>函数就可以了。</p>
<p>这个函数的原型是<br />
<span style="font-size: medium;">void gtk_image_menu_item_set_image (GtkImageMenuItem *image_menu_item, GtkWidget *image);</span></p>
<p>这个函数这样调用比较方便：</p>
<p><span style="font-size: medium;">menu_item =  gtk_image_menu_item_new_with_label (“Label you want to add”);<br />
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM(menu_item), gtk_image_new_from_stock (GTK_STOCK_ID, GTK_ICON_SIZE_MENU));</span></p>
<p>起先没走对路子，折腾死人。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2009/12/gtk-menu-item-img-label/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>程序员八荣八耻</title>
		<link>http://ganquan.org/blog/2009/11/%e7%a8%8b%e5%ba%8f%e5%91%98%e5%85%ab%e8%8d%a3%e5%85%ab%e8%80%bb/</link>
		<comments>http://ganquan.org/blog/2009/11/%e7%a8%8b%e5%ba%8f%e5%91%98%e5%85%ab%e8%8d%a3%e5%85%ab%e8%80%bb/#comments</comments>
		<pubDate>Thu, 19 Nov 2009 15:03:43 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://imganquan.org/blog/?p=525</guid>
		<description><![CDATA[今天无意见到，风趣幽默又富含经验，虽然是讲Python的，但是所有编程语言都是相通的，好好学习学习。 程序员八荣八耻 以动手实践为荣 , 以只看不练为耻; 以打印日志为荣 , 以单步跟踪为耻; 以空格缩进为荣 , 以制表缩进为耻; 以单元测试为荣 , 以人工测试为耻; 以模块复用为荣 , 以复制粘贴为耻; 以多态应用为荣 , 以分支判断为耻; 以Pythonic为荣 , 以冗余拖沓为耻; 以总结分享为荣 , 以跪求其解为耻;]]></description>
			<content:encoded><![CDATA[<p>今天无意见到，风趣幽默又富含经验，虽然是讲Python的，但是所有编程语言都是相通的，好好学习学习。</p>
<p style="text-align: center;">
<strong>程序员八荣八耻</strong></p>
<p style="text-align: center;">
以动手实践为荣 , 以只看不练为耻;</p>
<p style="text-align: center;">以打印日志为荣 , 以单步跟踪为耻;</p>
<p style="text-align: center;">以空格缩进为荣 , 以制表缩进为耻;</p>
<p style="text-align: center;">以单元测试为荣 , 以人工测试为耻;</p>
<p style="text-align: center;">以模块复用为荣 , 以复制粘贴为耻;</p>
<p style="text-align: center;">以多态应用为荣 , 以分支判断为耻;</p>
<p style="text-align: center;">以Pythonic为荣 , 以冗余拖沓为耻;</p>
<p style="text-align: center;">以总结分享为荣 , 以跪求其解为耻;</p>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2009/11/%e7%a8%8b%e5%ba%8f%e5%91%98%e5%85%ab%e8%8d%a3%e5%85%ab%e8%80%bb/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>TCP协议的TIME_WAIT状态详解</title>
		<link>http://ganquan.org/blog/2009/09/tcp%e5%8d%8f%e8%ae%ae%e7%9a%84time_wait%e7%8a%b6%e6%80%81%e8%af%a6%e8%a7%a3/</link>
		<comments>http://ganquan.org/blog/2009/09/tcp%e5%8d%8f%e8%ae%ae%e7%9a%84time_wait%e7%8a%b6%e6%80%81%e8%af%a6%e8%a7%a3/#comments</comments>
		<pubDate>Thu, 17 Sep 2009 06:09:07 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://imganquan.org/blog/?p=452</guid>
		<description><![CDATA[原创文章，转载请注明出处:www.ganquan.org 我发现有个公司面试的时候问了对TCP协议比较细节的地方，那就写一下帮助自己加深理解和记忆。在开始说TIME_WAIT状态之前，要知道TCP协议是如何关闭连接的。 很多人对TCP协议的三次握手都很熟悉（不知道的可以参考我以前的帖子），因为学校的垃圾考试都爱考三次握手，但是很多知道三次握手的人都对TCP协议是如何关闭连接不了解。不说废话了，TCP关闭连接过程如下图，寡人用photoshop画的，凑合看一下： 为了方便描述，我给这个TCP连接的一端起名为Client，给另外一端起名为Server。上图描述的是Client主动关闭的过程，FTP协议中就这样的。如果要描述Server主动关闭的过程，只要交换描述过程中的Server和Client就可以了，HTTP协议就是这样的。 描述过程： Client调用close()函数，给Server发送FIN，请求关闭连接；Server收到FIN之后给Client返回确认ACK，同时关闭读通道（不清楚就去看一下shutdown和close的差别），也就是说现在不能再从这个连接上读取东西，现在read返回0。此时Server的TCP状态转化为CLOSE_WAIT状态。 Client收到对自己的FIN确认后，关闭 写通道，不再向连接中写入任何数据。 接下来Server调用close()来关闭连接，给Client发送FIN，Client收到后给Server回复ACK确认，同时Client关闭读通道，进入TIME_WAIT状态。 Server接收到Client对自己的FIN的确认ACK，关闭写通道，TCP连接转化为CLOSED，也就是关闭连接。 Client在TIME_WAIT状态下要等待最大数据段生存期的两倍，然后才进入CLOSED状态，TCP协议关闭连接过程彻底结束。 以上就是TCP协议关闭连接的过程，现在说一下TIME_WAIT状态。 从上面可以看到，主动发起关闭连接的操作的一方将达到TIME_WAIT状态，而且这个状态要保持Maximum Segment Lifetime的两倍时间。为什么要这样做而不是直接进入CLOSED状态？ 原因有二： 一、保证TCP协议的全双工连接能够可靠关闭 二、保证这次连接的重复数据段从网络中消失 先说第一点，如果Client直接CLOSED了，那么由于IP协议的不可靠性或者是其它网络原因，导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN，此时由于Client已经CLOSED了，就找不到与重发的FIN对应的连接，最后Server就会收到RST而不是ACK，Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失，但是却导致TCP协议不符合可靠连接的要求。所以，Client不是直接进入CLOSED，而是要保持TIME_WAIT，当再次收到FIN的时候，能够保证对方收到ACK，最后正确的关闭连接。 再说第二点，如果Client直接CLOSED，然后又再向Server发起一个新连接，我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题，但是还是有特殊情况出现：假设新连接和已经关闭的老连接端口号是一样的，如果前一次连接的某些数据仍然滞留在网络中，这些延迟数据在建立新连接之后才到达Server，由于新连接和老连接的端口号是一样的，又因为TCP协议判断不同连接的依据是socket pair，于是，TCP协议就认为那个延迟的数据是属于新连接的，这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL，这样可以保证本次连接的所有数据都从网络中消失。 各种协议都是前人千锤百炼后得到的标准，规范。从细节中都能感受到精巧和严谨。每次深入都有同一个感觉，精妙。]]></description>
			<content:encoded><![CDATA[<p>原创文章，转载请注明出处:www.ganquan.org</p>
<p>我发现有个公司面试的时候问了对TCP协议比较细节的地方，那就写一下帮助自己加深理解和记忆。在开始说TIME_WAIT状态之前，要知道TCP协议是如何关闭连接的。<br />
很多人对TCP协议的三次握手都很熟悉（<a href="http://ganquan.org/blog/?p=415" target="_blank">不知道的可以参考我以前的帖子</a>），因为学校的垃圾考试都爱考三次握手，但是很多知道三次握手的人都对TCP协议是如何关闭连接不了解。不说废话了，TCP关闭连接过程如下图，寡人用photoshop画的，凑合看一下：<br />
<img class="alignnone size-full wp-image-453" title="tcpclose" src="http://ganquan.org/blog/wp-content/uploads/2009/09/tcpclose.jpg" alt="tcpclose" width="600" height="337" /><br />
为了方便描述，我给这个TCP连接的一端起名为Client，给另外一端起名为Server。上图描述的是Client主动关闭的过程，FTP协议中就这样的。如果要描述Server主动关闭的过程，只要交换描述过程中的Server和Client就可以了，HTTP协议就是这样的。</p>
<p>描述过程：<br />
Client调用close()函数，给Server发送FIN，请求关闭连接；Server收到FIN之后给Client返回确认ACK，同时关闭读通道（不清楚就去看一下shutdown和close的差别），也就是说现在不能再从这个连接上读取东西，现在read返回0。此时Server的TCP状态转化为CLOSE_WAIT状态。<br />
Client收到对自己的FIN确认后，关闭 写通道，不再向连接中写入任何数据。<br />
接下来Server调用close()来关闭连接，给Client发送FIN，Client收到后给Server回复ACK确认，同时Client关闭读通道，进入TIME_WAIT状态。<br />
Server接收到Client对自己的FIN的确认ACK，关闭写通道，TCP连接转化为CLOSED，也就是关闭连接。<br />
Client在TIME_WAIT状态下要等待最大数据段生存期的两倍，然后才进入CLOSED状态，TCP协议关闭连接过程彻底结束。</p>
<p>以上就是TCP协议关闭连接的过程，现在说一下TIME_WAIT状态。<br />
从上面可以看到，主动发起关闭连接的操作的一方将达到TIME_WAIT状态，而且这个状态要保持Maximum  Segment Lifetime的两倍时间。为什么要这样做而不是直接进入CLOSED状态？</p>
<p>原因有二：<br />
<span style="color: #ff0000;"> 一、保证TCP协议的全双工连接能够可靠关闭<br />
二、保证这次连接的重复数据段从网络中消失</span></p>
<p>先说第一点，如果Client直接CLOSED了，那么由于IP协议的不可靠性或者是其它网络原因，导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN，此时由于Client已经CLOSED了，就找不到与重发的FIN对应的连接，最后Server就会收到RST而不是ACK，Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失，但是却导致TCP协议不符合可靠连接的要求。所以，Client不是直接进入CLOSED，而是要保持TIME_WAIT，当再次收到FIN的时候，能够保证对方收到ACK，最后正确的关闭连接。</p>
<p>再说第二点，如果Client直接CLOSED，然后又再向Server发起一个新连接，我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题，但是还是有特殊情况出现：假设新连接和已经关闭的老连接端口号是一样的，如果前一次连接的某些数据仍然滞留在网络中，这些延迟数据在建立新连接之后才到达Server，由于新连接和老连接的端口号是一样的，又因为TCP协议判断不同连接的依据是socket pair，于是，TCP协议就认为那个延迟的数据是属于新连接的，这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL，这样可以保证本次连接的所有数据都从网络中消失。</p>
<p>各种协议都是前人千锤百炼后得到的标准，规范。从细节中都能感受到精巧和严谨。每次深入都有同一个感觉，精妙。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2009/09/tcp%e5%8d%8f%e8%ae%ae%e7%9a%84time_wait%e7%8a%b6%e6%80%81%e8%af%a6%e8%a7%a3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TCP: time wait bucket table overflow</title>
		<link>http://ganquan.org/blog/2009/09/tcp-time-wait-bucket-table-overflow/</link>
		<comments>http://ganquan.org/blog/2009/09/tcp-time-wait-bucket-table-overflow/#comments</comments>
		<pubDate>Tue, 15 Sep 2009 18:28:44 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://imganquan.org/blog/?p=441</guid>
		<description><![CDATA[在redhat网站查到下面的信息，说是因为内存不够的原因。我觉得这个可以当作出现这个问题的解释，但是却解释得不够“完美”，我仍旧还在疑惑中：如果是是因为内存不够的原因，那么在每次测试之前，只要保证机器状态一样，那么TCP: time wait bucket table overflow这条信息的输出次数就应该是一样的，或者差的不是很多，因为每次有一个socket来了，就给一个数据结构，直到内存用完输出TCP: time wait bucket table overflow信息，但是我测试的结果是差异不小，这就让我感觉疑惑了，莫非每次分配的数据结构不一样大？这不可能。我每次单独测试都是重启开发板，然后不运行任何其他程序就进行测试的，这样应该可以保证每次测试机器的状态不是差的很多吧。 The “TCP: time wait bucket table overflow” message shows when the kernel is unable to allocate a data structure to put a socket in the TIME_WAIT state. This is happening according to linux/net/ipv4/tcp_minisocks.c: if (tcp_tw_count &#60; sysctl_tcp_max_tw_buckets) tw = kmem_cache_alloc(tcp_timewait_cachep, SLAB_ATOMIC); if(tw != NULL) [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://kbase.redhat.com/faq/docs/DOC-1253">redhat网站</a>查到下面的信息，说是因为内存不够的原因。我觉得这个可以当作出现这个问题的解释，但是却解释得不够“完美”，我仍旧还在疑惑中：如果是是因为内存不够的原因，那么在每次测试之前，只要保证机器状态一样，那么TCP: time wait bucket table overflow这条信息的输出次数就应该是一样的，或者差的不是很多，因为每次有一个socket来了，就给一个数据结构，直到内存用完输出TCP: time wait bucket table overflow信息，但是我测试的结果是差异不小，这就让我感觉疑惑了，莫非每次分配的数据结构不一样大？这不可能。我每次单独测试都是重启开发板，然后不运行任何其他程序就进行测试的，这样应该可以保证每次测试机器的状态不是差的很多吧。</p>
<p>The “TCP: time wait bucket table overflow” message shows when the kernel is unable to allocate a data structure to put a socket in the TIME_WAIT state.</p>
<p>This is happening according to linux/net/ipv4/tcp_minisocks.c:</p>
<p>if (tcp_tw_count &lt; sysctl_tcp_max_tw_buckets)<br />
tw = kmem_cache_alloc(tcp_timewait_cachep, SLAB_ATOMIC);</p>
<p>if(tw != NULL) {<br />
(..)<br />
} else {<br />
/* Sorry, if we&#8217;re out of memory, just CLOSE this<br />
* socket up. We&#8217;ve got bigger problems than<br />
* non-graceful socket closings.<br />
*/<br />
if (net_ratelimit())<br />
printk(KERN_INFO “TCP: time wait bucket table overflow\\n”);<br />
}</p>
<p>This problem is more likely to happen on systems creating a lot of TCP connections at a fast pace. RFC 793 decided that those sockets should stay in the TIME_WAIT state for 2*MSL (Maximum Segment Life), but the Linux implementation seems to make the TIME_WAIT state last for 1 minute.</p>
<p>Monitor the resources used by those time wait buckets by watching:</p>
<p># cat /proc/slabinfo | grep tcp_tw_bucket</p>
<p>The size of the time wait bucket can be adjusted by writing to /proc/sys/net/ipv4/tcp_max_tw_buckets. However, the link between the client and the server cannot cause packets to arrive out of order, then the TIME_WAIT state can be skipped and sockets can be recycled immediately. Socket recycling can be configured in /proc/sys/net/ipv4/tcp_tw_recycle, but check with your network administrator to verify whether it&#8217;s safe to do so.</p>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2009/09/tcp-time-wait-bucket-table-overflow/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>服务器性能</title>
		<link>http://ganquan.org/blog/2009/09/%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd/</link>
		<comments>http://ganquan.org/blog/2009/09/%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd/#comments</comments>
		<pubDate>Tue, 15 Sep 2009 15:34:03 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://imganquan.org/blog/?p=439</guid>
		<description><![CDATA[时间好快啊，一眨眼就晚上11点过了。赶紧写今天的日记。 今天写了一个超级超级简陋的web服务器，采用epoll多线程的方式：主线程负责accpet，子线程处理链接。问题出在测试环节： 在笔记本上用ab模拟50个并发连接，10000次请求，进行测试，一切OK。可是移植到mini2440开发板上去运行，测试参数不变，就出现这样的输出（截取一部分） TCP: time wait bucket table overflow TCP: time wait bucket table overflow __ratelimit: 1745 callbacks suppressed TCP: time wait bucket table overflow TCP: time wait bucket table overflow 我第一反应是服务器连接队列太小，我立刻改大了再测试，还是有问题。我又找了一个别人写的单进程服务器移植到mini2440开发板上来测试，一样有这样的输出。但是这些服务器在笔记本上测试都没有这种问题，很明显是硬件处理能力跟不上的原因。 索性用开发板上自带的boa服务器来测试，靠，问题更加严重，出现好多好多这样的输出。 不解。回头一定要搞明白这个问题。 性能测试结果对比如下，都是在mini2440开发板上进行测试： 1.epoll+pthread服务器，处理10000次请求总共耗时22.341秒，平均每秒处理447.61个请求，测试结果性能最好 2.单进程服务器，处理10000次请求总共耗时28.922秒，平均每秒处理个345.75请求，测试结果性能最差 3.mini2440系统里面自带的boa服务器，处理10000次请求总共耗时26.739秒，平均每秒处理个373.98个请求，测试性能第二 从结果来看多线程方式比单进程的处理方式要好一点，估计boa性能略差的原因是因为boa比较完善，支持动态网页。而另外两个都只支持静态页面。不过这样分析也不对，因为测试时我没有让他们输出任何内容，仅仅是一个报文头而已。所以其实这三个服务器的测试基础是一样的。后面完善了服务器之后再测试一下。]]></description>
			<content:encoded><![CDATA[<p>时间好快啊，一眨眼就晚上11点过了。赶紧写今天的日记。</p>
<p>今天写了一个超级超级简陋的web服务器，采用epoll多线程的方式：主线程负责accpet，子线程处理链接。问题出在测试环节：</p>
<p>在笔记本上用ab模拟50个并发连接，10000次请求，进行测试，一切OK。可是移植到mini2440开发板上去运行，测试参数不变，就出现这样的输出（截取一部分）<br />
TCP: time wait bucket table overflow<br />
TCP: time wait bucket table overflow<br />
__ratelimit: 1745 callbacks suppressed<br />
TCP: time wait bucket table overflow<br />
TCP: time wait bucket table overflow</p>
<p>我第一反应是服务器连接队列太小，我立刻改大了再测试，还是有问题。我又找了一个别人写的单进程服务器移植到mini2440开发板上来测试，一样有这样的输出。但是这些服务器在笔记本上测试都没有这种问题，很明显是硬件处理能力跟不上的原因。</p>
<p>索性用开发板上自带的boa服务器来测试，靠，问题更加严重，出现好多好多这样的输出。</p>
<p>不解。回头一定要搞明白这个问题。</p>
<p>性能测试结果对比如下，都是在mini2440开发板上进行测试：</p>
<p>1.epoll+pthread服务器，处理10000次请求总共耗时22.341秒，平均每秒处理447.61个请求，测试结果性能最好<br />
2.单进程服务器，处理10000次请求总共耗时28.922秒，平均每秒处理个345.75请求，测试结果性能最差<br />
3.mini2440系统里面自带的boa服务器，处理10000次请求总共耗时26.739秒，平均每秒处理个373.98个请求，测试性能第二</p>
<p>从结果来看多线程方式比单进程的处理方式要好一点，估计boa性能略差的原因是因为boa比较完善，支持动态网页。而另外两个都只支持静态页面。不过这样分析也不对，因为测试时我没有让他们输出任何内容，仅仅是一个报文头而已。所以其实这三个服务器的测试基础是一样的。后面完善了服务器之后再测试一下。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2009/09/%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>一次DoS攻击</title>
		<link>http://ganquan.org/blog/2009/09/%e4%b8%80%e6%ac%a1dos%e6%94%bb%e5%87%bb/</link>
		<comments>http://ganquan.org/blog/2009/09/%e4%b8%80%e6%ac%a1dos%e6%94%bb%e5%87%bb/#comments</comments>
		<pubDate>Sat, 12 Sep 2009 16:47:17 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://imganquan.org/blog/?p=415</guid>
		<description><![CDATA[甲流了，封校了，在宿舍憋了一天。晚上学校孝敬我的饭菜没有拍照，只好明天再拍照纪念。 白天又蹉跎了，唉。。。 TCP协议的3次握手过程如下图所示（朕用photoshop随便画的，将就看一下）： 罗嗦一下：客户端发送SYN，服务器收到后发送对应的ACK 和自己的SYN，客户端收到对自己的SYN确认后，发送对服务器SYN的确认，客户端完成建立连接。服务器收到客户端对自己的SYN的确认，整个TCP连接建立完成。 如果要进行一次DoS攻击，则可以在3次握手的过程中进行破坏，破坏方法如下图： 利用原始socket修改源地址，使得服务器把ACK发送到一个不存在的地址，然后服务器就会傻逼一样等待这个不存在的地址的应答信息，那就一直等吧，等到地老天荒去。但是服务器还没有这个能耐等到地老天荒海枯石烂，服务器的未完成连接队列长度是有限的，所以当那些不存在的地址把这个队列霸占之后，合法的主机再来连接服务器就会被deny。 以上就是DoS攻击的原理。 下面开始攻击： 目标服务器：我的mini2440开发板，上面运行了一个http服务器，端口80，地址是192.168.0.222 在我的主机上访问这个地址是完全正常的，如下图： 然后开始攻击： 可以看到当前发送速率如下： 发送一段时候后，再次访问http://192.168.0.222，结果如下： 可以用tcpdump抓包看到，发送的数据包源地址都是随机伪造的： 21:49:04.958398 IP 165.73.220.37.8888 &#62; 192.168.0.222.www: S 1732610923:1732610923(0) win 0 21:49:04.958402 IP 182.200.63.94.8888 &#62; 192.168.0.222.www: S 1732610923:1732610923(0) win 0 21:49:04.958406 IP 218.31.87.38.8888 &#62; 192.168.0.222.www: S 1732610923:1732610923(0) win 0 21:49:04.966416 IP 132.23.33.98.8888 &#62; 192.168.0.222.www: S 1732610923:1732610923(0) win 0 21:49:04.966424 IP 200.16.93.88.8888 &#62; [...]]]></description>
			<content:encoded><![CDATA[<p>甲流了，封校了，在宿舍憋了一天。晚上学校孝敬我的饭菜没有拍照，只好明天再拍照纪念。<br />
白天又蹉跎了，唉。。。</p>
<p>TCP协议的3次握手过程如下图所示（朕用photoshop随便画的，将就看一下）：</p>
<p><img class="alignnone size-full wp-image-421" title="tcp_shakehand" src="http://ganquan.org/blog/wp-content/uploads/2009/09/tcp_shakehand.jpg" alt="tcp_shakehand" width="600" height="340" /></p>
<p>罗嗦一下：客户端发送SYN，服务器收到后发送对应的ACK 和自己的SYN，客户端收到对自己的SYN确认后，发送对服务器SYN的确认，客户端完成建立连接。服务器收到客户端对自己的SYN的确认，整个TCP连接建立完成。</p>
<p>如果要进行一次DoS攻击，则可以在3次握手的过程中进行破坏，破坏方法如下图：</p>
<p><img class="alignnone size-full wp-image-420" title="DoS" src="http://ganquan.org/blog/wp-content/uploads/2009/09/DoS.jpg" alt="DoS" width="600" height="340" /></p>
<p>利用原始socket修改源地址，使得服务器把ACK发送到一个不存在的地址，然后服务器就会傻逼一样等待这个不存在的地址的应答信息，那就一直等吧，等到地老天荒去。但是服务器还没有这个能耐等到地老天荒海枯石烂，服务器的未完成连接队列长度是有限的，所以当那些不存在的地址把这个队列霸占之后，合法的主机再来连接服务器就会被deny。</p>
<p>以上就是DoS攻击的原理。<br />
下面开始攻击：</p>
<p>目标服务器：我的mini2440开发板，上面运行了一个http服务器，端口80，地址是192.168.0.222<br />
在我的主机上访问这个地址是完全正常的，如下图：</p>
<p><img class="alignnone size-full wp-image-416" title="2009-09-12-235327_432x236_scrot" src="http://ganquan.org/blog/wp-content/uploads/2009/09/2009-09-12-235327_432x236_scrot.png" alt="2009-09-12-235327_432x236_scrot" width="432" height="236" /></p>
<p>然后开始攻击：</p>
<p><img class="alignnone size-full wp-image-417" title="2009-09-12-235534_482x125_scrot" src="http://ganquan.org/blog/wp-content/uploads/2009/09/2009-09-12-235534_482x125_scrot.png" alt="2009-09-12-235534_482x125_scrot" width="482" height="125" /></p>
<p>可以看到当前发送速率如下：</p>
<p><img class="alignnone size-large wp-image-418" title="2009-09-12-235648_698x138_scrot" src="http://ganquan.org/blog/wp-content/uploads/2009/09/2009-09-12-235648_698x138_scrot-500x98.png" alt="2009-09-12-235648_698x138_scrot" width="500" height="98" /></p>
<p>发送一段时候后，再次访问http://192.168.0.222，结果如下：</p>
<p><img class="alignnone size-full wp-image-419" title="2009-09-13-000325_473x376_scrot" src="http://ganquan.org/blog/wp-content/uploads/2009/09/2009-09-13-000325_473x376_scrot.png" alt="2009-09-13-000325_473x376_scrot" width="473" height="376" /></p>
<p>可以用tcpdump抓包看到，发送的数据包源地址都是随机伪造的：</p>
<p>21:49:04.958398 IP 165.73.220.37.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.958402 IP 182.200.63.94.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.958406 IP 218.31.87.38.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.966416 IP 132.23.33.98.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.966424 IP 200.16.93.88.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.966430 IP 171.242.43.18.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.966436 IP 78.39.31.69.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.966442 IP 34.132.193.5.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.966448 IP 55.211.162.46.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.966454 IP 89.112.75.6.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0<br />
21:49:04.966460 IP 215.57.37.36.8888 &gt; 192.168.0.222.www: S 1732610923:1732610923(0) win 0</p>
<p>代码过程如下：<br />
1.生成原始socket<br />
socket(AF_INET,SOCK_RAW,IPPROTO_TCP)<br />
2.设置socket选项<br />
setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&amp;on,sizeof(on));<br />
设置了IP_HDRINCL选项，需要手动为IP数据包填写IP数据包首部内容。<br />
3.填写两个结构体：struct ip 和 struct tcphdr<br />
4.伪造源地址并且发送给服务器<br />
ip-&gt;ip_src.s_addr = random();<br />
sendto(sockfd,buffer,head_len,0,(struct sockaddr *)&amp;addr,sizeof(struct sockaddr));</p>
<p>一点了，睡觉去。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2009/09/%e4%b8%80%e6%ac%a1dos%e6%94%bb%e5%87%bb/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>volatile关键字</title>
		<link>http://ganquan.org/blog/2009/08/volatile%e5%85%b3%e9%94%ae%e5%ad%97/</link>
		<comments>http://ganquan.org/blog/2009/08/volatile%e5%85%b3%e9%94%ae%e5%ad%97/#comments</comments>
		<pubDate>Sun, 23 Aug 2009 03:47:44 +0000</pubDate>
		<dc:creator>ganquan</dc:creator>
				<category><![CDATA[嵌入式]]></category>
		<category><![CDATA[程序设计]]></category>

		<guid isPermaLink="false">http://imganquan.org/blog/?p=367</guid>
		<description><![CDATA[用它声明的类型变量表示可以被某些编译器未知的因素更改，比如：操作系统、硬件或者其它线程等。遇到这个关键字声明的变量，编译器对访问该变量的代码就不再进行优化，从而可以提供对特殊地址的稳定访问。在嵌入式系统中，用来作为硬件动作的结果。 例如，通过一个串口接收到一个字符，结果串口状态寄存器更新，这完全在程序流程之外发生。最好就把该寄存器声明为volatile，编译器不会试图优化一个volatile寄存器，而是每次重载它。所以在嵌入式设备的程序中，将所有外设寄存器声明为volatile是一个好习惯。]]></description>
			<content:encoded><![CDATA[<p>用它声明的类型变量表示可以被某些编译器未知的因素更改，比如：操作系统、硬件或者其它线程等。遇到这个关键字声明的变量，编译器对访问该变量的代码就不再进行优化，从而可以提供对特殊地址的稳定访问。在嵌入式系统中，用来作为硬件动作的结果。</p>
<p>例如，通过一个串口接收到一个字符，结果串口状态寄存器更新，这完全在程序流程之外发生。最好就把该寄存器声明为volatile，编译器不会试图优化一个volatile寄存器，而是每次重载它。所以在嵌入式设备的程序中，将所有外设寄存器声明为volatile是一个好习惯。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganquan.org/blog/2009/08/volatile%e5%85%b3%e9%94%ae%e5%ad%97/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

