<?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>Just  for Fun</title>
	<atom:link href="http://blog.nlogn.cn/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://blog.nlogn.cn</link>
	<description>乐者为王</description>
	<lastBuildDate>Sat, 14 Apr 2012 15:22:29 +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>企鹅面试记</title>
		<link>http://blog.nlogn.cn/?p=552</link>
		<comments>http://blog.nlogn.cn/?p=552#comments</comments>
		<pubDate>Sat, 14 Apr 2012 10:04:31 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Others]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=552</guid>
		<description><![CDATA[本来没想现在去找实习的，不过既然腾讯来了，就去打一次酱油吧。这算是我第一次参加正式的招聘吧，虽然是实习生的，但是也有必要写篇博客记录一下。 1、流水帐 2、总结 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; 短信通知是今天上午8：30面试，今早一睁眼已经是8：42了，于是立马跳起来，没刷牙没洗脸就直接冲过去了。 面试的GG看到我不耐烦的说：“怎么到现在才来？” “对不起，不小心睡过头了。” “没开闹钟吗？” “开了，但是闹钟没叫&#8230;”（真的没叫） “&#8230;..” 接下来就是技术方面了，基本上简历里面写了什么他就问你什么，最好在面试之前把简历上写的项目都复习一下，这样他问的时候可以很快反应过来，这算经验吧，我写的东西是以前做的，所以问细节的东西有些已经想不起来了。 “你简历上有你的博客，一般都写些什么东西呢？” “一般就是记录一些笔记吧，把学到的东西记录下来&#8230;&#8230;” 然后开始问简历上的项目。第一个是关于数据挖掘的K-means算法。 “给我解释一下什么是K-means算法？” balabala解释了一堆，还算顺利吧，因为算法这东西说个大概就行了，因为面试的GG也不懂，也不可能问太细的东西。 第二个是以前学数据库系统实现的时候写过的一个实验。 “把你做的这个东西给我解释一下吧” 然后就解释了。 他一边听一边问里面的细节，所以说在去之前把东西复习一遍是很有必要的。 比如他问：“这个里面是线性的遍历吗？有没有想过更高效一点的方法？” 我：“想过，以前做的时候开始是用的大根堆，但是后来发现了一个很严重的问题。” “什么问题？” 问到这儿一下子想不起来了，然后他给我一只笔，一张纸，让我仔细想想。 想了一会儿在纸上画给他看：“堆不支持按给定的值查找，如果某一个节点的值更新了以后，我没法确定他在堆中的具体位置，所以没用, &#8230;&#8230;” 然后就问了一些其他的问题。 “你遇到过什么重大的挫折没有？” （擦，哥才二十几岁这么点人生阅历，有毛重大挫折啊。） 说：“没有。” 然后他又说了一些话，意思是让我再想想，（尼玛，你就对我的人生挫折这么感兴趣嘛） “要说学习上或者生活上还真没有，因为不懂的东西我可以学，要说挫折我觉得我眼睛的近视程度太高了，看屏幕时间长了会酸，而且做我们这行的整天盯着屏幕，对我的眼睛就更不好了。” 之后就是一些闲聊了，比如哪里人，希望在哪里实习什么的。 一面的GG问完紧接着就是二面。 二面相对简单。 “如果外面的这个电梯，让你设计它的控制程序，你打算怎么设计？” 我就说如果1楼、5楼都有人，但是电梯目前在3楼，可以按照电梯的运行方向优先考虑，如果向上则满足5楼等等。 之后问了一些团队合作之类的东西，比如如果在一个团队里面怎么协调不同的意见。 二面很快就完了，然后跟我说：“好了，出去以后不要跟别人交流，我们腾讯的理念之一就是正直。” 囧rz 注意，他没跟我说还有三面，我以为没有了，就直接去吃饭了，刚把饭吃完同学就来电话：“怎么走了？还有三面啊，叫你名字呢。” 我又屁颠屁颠的跑过去。 三面是位大叔。 问了笔试的编程题：“这个程序知道为什么扣分了吗？” “（想了一下）不知道。” “这个是线程不安全的，看出来了吗？” “恩，知道了，（简单说了一下）。” “怎么改正？” 这时候正好老妈打电话过来，干扰了一下，他看我没回答就提示了一点东西。 然后我又想了一会儿：”加锁？” “这么简单的程序加锁是不是太费资源了？” “（又想了一下）哦，对了，把这个全局变量放到参数里面去(balabala&#8230;.)” 然后又扯了一些一面问过的一些技术问题，感觉有点重复，不过他提的细节不太一样，着重点也不一样。 之后大叔又说：“看你简历对算法和后台这方面有兴趣，你知道我们是来招什么方向的吗？” “知道，是测试的，但是我看你们即时通讯部的主页上不是也有后台开发的方向吗？” [...]]]></description>
			<content:encoded><![CDATA[<p>本来没想现在去找实习的，不过既然腾讯来了，就去打一次酱油吧。这算是我第一次参加正式的招聘吧，虽然是实习生的，但是也有必要写篇博客记录一下。</p>
<p>1、流水帐<br />
2、总结<span id="more-552"></span></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
短信通知是今天上午8：30面试，今早一睁眼已经是8：42了，于是立马跳起来，没刷牙没洗脸就直接冲过去了。</p>
<p>面试的GG看到我不耐烦的说：“怎么到现在才来？”<br />
“对不起，不小心睡过头了。”<br />
“没开闹钟吗？”<br />
“开了，但是闹钟没叫&#8230;”（真的没叫）<br />
“&#8230;..”</p>
<p>接下来就是技术方面了，基本上简历里面写了什么他就问你什么，最好在面试之前把简历上写的项目都复习一下，这样他问的时候可以很快反应过来，这算经验吧，我写的东西是以前做的，所以问细节的东西有些已经想不起来了。</p>
<p>“你简历上有你的博客，一般都写些什么东西呢？”<br />
“一般就是记录一些笔记吧，把学到的东西记录下来&#8230;&#8230;”</p>
<p>然后开始问简历上的项目。第一个是关于数据挖掘的K-means算法。<br />
<strong>“给我解释一下什么是K-means算法？”</strong><br />
balabala解释了一堆，还算顺利吧，因为算法这东西说个大概就行了，因为面试的GG也不懂，也不可能问太细的东西。</p>
<p>第二个是以前学数据库系统实现的时候写过的一个实验。<br />
<strong>“把你做的这个东西给我解释一下吧”</strong><br />
然后就解释了。<br />
他一边听一边问里面的细节，所以说在去之前把东西复习一遍是很有必要的。<br />
比如他问：“这个里面是线性的遍历吗？有没有想过更高效一点的方法？”<br />
我：“想过，以前做的时候开始是用的大根堆，但是后来发现了一个很严重的问题。”<br />
“什么问题？”<br />
问到这儿一下子想不起来了，然后他给我一只笔，一张纸，让我仔细想想。<br />
想了一会儿在纸上画给他看：“堆不支持按给定的值查找，如果某一个节点的值更新了以后，我没法确定他在堆中的具体位置，所以没用, &#8230;&#8230;”</p>
<p>然后就问了一些其他的问题。<br />
<strong>“你遇到过什么重大的挫折没有？”</strong><br />
（擦，哥才二十几岁这么点人生阅历，有毛重大挫折啊。）<br />
说：“没有。”<br />
然后他又说了一些话，意思是让我再想想，（尼玛，你就对我的人生挫折这么感兴趣嘛）<br />
“要说学习上或者生活上还真没有，因为不懂的东西我可以学，要说挫折我觉得我眼睛的近视程度太高了，看屏幕时间长了会酸，而且做我们这行的整天盯着屏幕，对我的眼睛就更不好了。”</p>
<p>之后就是一些闲聊了，比如哪里人，希望在哪里实习什么的。</p>
<p>一面的GG问完紧接着就是二面。<br />
二面相对简单。<br />
<strong>“如果外面的这个电梯，让你设计它的控制程序，你打算怎么设计？”</strong><br />
我就说如果1楼、5楼都有人，但是电梯目前在3楼，可以按照电梯的运行方向优先考虑，如果向上则满足5楼等等。<br />
之后问了一些团队合作之类的东西，比如如果在一个团队里面怎么协调不同的意见。<br />
二面很快就完了，然后跟我说：“好了，出去以后不要跟别人交流，我们腾讯的理念之一就是正直。” <strong>囧rz</strong></p>
<p>注意，他没跟我说还有三面，我以为没有了，就直接去吃饭了，刚把饭吃完同学就来电话：“怎么走了？还有三面啊，叫你名字呢。” 我又屁颠屁颠的跑过去。</p>
<p>三面是位大叔。<br />
问了笔试的编程题：<strong>“这个程序知道为什么扣分了吗？”</strong><br />
“（想了一下）不知道。”<br />
“这个是线程不安全的，看出来了吗？”<br />
“恩，知道了，（简单说了一下）。”<br />
“怎么改正？”<br />
这时候正好老妈打电话过来，干扰了一下，他看我没回答就提示了一点东西。<br />
然后我又想了一会儿：”加锁？”<br />
“这么简单的程序加锁是不是太费资源了？”<br />
“（又想了一下）哦，对了，把这个全局变量放到参数里面去(balabala&#8230;.)”</p>
<p>然后又扯了一些一面问过的一些技术问题，感觉有点重复，不过他提的细节不太一样，着重点也不一样。</p>
<p>之后大叔又说：“看你简历对算法和后台这方面有兴趣，你知道我们是来招什么方向的吗？”<br />
“知道，是测试的，但是我看你们即时通讯部的主页上不是也有后台开发的方向吗？”<br />
“我们后台的不招实习生，因为后台保密级别比较高，那些源码的东西不可能让实习生来做的，对吧？”<br />
“嗯嗯，是的。”<br />
然后就聊了一些测试职位具体做些什么，还特地问了是不是测试QQ软件的，得到回复是主要黑盒测试QQ和Q+。<br />
然后问我有兴趣吗？<br />
(哥都来了，能没有兴趣嘛，先拿到offer再说啊。)<br />
就说：“有啊”，然后他就让我回去等结果了。<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<strong>说一下收获吧。</strong></p>
<p>看了同学的简历发现自己跟他们差得太多，很多人以前参与过大项目，甚至做了一些“群众喜闻乐见的产品”；还有的人对某一领域十分有研究，提起这个领域能滔滔不绝的讲，比如某猥琐男同学，竟然通读过Windows Research Kernel的源码，不得不佩服，相比自己典型的是什么都懂点皮毛，一深入就不行了，以后得注意这方面。
</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=552">企鹅面试记</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=552</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Linked List in Linux Kernel</title>
		<link>http://blog.nlogn.cn/?p=545</link>
		<comments>http://blog.nlogn.cn/?p=545#comments</comments>
		<pubDate>Sat, 31 Mar 2012 11:43:22 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Kernel]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=545</guid>
		<description><![CDATA[Linux Kernel实现的链表与众不同，大多数人所熟悉的方式应该是在“链表中包含结构体”，而Linux内核的实现方式则是所谓的“结构体中包含链表”。这样的说法听起来很玄乎，不如给出具体的定义和实例。 这就是内核代码中Linked List的结构体定义，从list_head定义可以看出，内核的链表是循环链表。我们可以这么用它： 链表的操作接口 一般在使用指针时要初始化，这是一个好的编程习惯，操作内核提供的链表也是如此。首先用INIT_LIST_HEAD()函数对其初始化。INIT_LIST_HEAD()做的工作很简单： 可以看到初始化就是把指针指向自己。在不同版本的源码中，这些函数的实现方式略有不同，例如在linux-2.6.26中，此函数不是宏定义，而是一个内联函数，不过它们所做的工作都是大同小异的，这一点在本文其他部分都一样，下面就不再提醒了。 list_add操作 插入操作所做的操作非常简单，只要学过基础的数据结构的人一眼就能看明白： 对于循环链表来说，在头部插入和在尾部插入是一样的，确实，内核也是这么做的，看一段很有意思的代码： 对于删除、合并等操作也都类似，有兴趣的读者可以自己翻看源码，比较简单的。 遍历操作、获取结构体成员操作 我认为kernel实现的链表最优意思的部分是list_entry、list_for_each等几个宏的实现。 首先有个问题：使用这种“Items contains the list”的方式如何通过这个list_head成员获得结构体的其他成员？ 答案是list_entry宏，我们先来看内核源码是怎么写的： 用法： tmp=list_entry(ptr, type, member) 其中， ptr是指向结构体中struct list_head 成员的指针； type是本结构体的名字； member是指在结构体中struct list_head叫什么名字，本例中叫list； 最后返回的是指向这个结构体的指针，有了这个指针，我们就可以调用其他成员了，比如tmp-&#62;name; 然后，我们来分析一下这个有意思的宏： 1、const typeof( ((type *)0)-&#62;member ) *__mptr = (ptr); 这句是声明了一个与名叫member的成员的类型一样的一个指针__mptr,并赋值为ptr; 2、(type *)( (char *)__mptr &#8211; offsetof(type,member) ); mptr指向的地址减去member在type中的偏移，就得到了type的地址了； 至于为什么要重新声明一个mptr，不直接用ptr呢？ 我想大概是因为不破坏ptr指针的内容，以便后续的代码继续正常使用ptr。 Update:  感谢 肉兔、Fleuria、Bearice 同学的帮助～ 感觉应该就是为了加一层类型的静态检查吧，C的宏是没有类型的，但可以做点功夫给它模拟一点类型检查。假如没有这句，这个ptr若是个无关的类型（比如偶尔可能会敲错变量）在编译时不会报错，甚至warning也不会有。 [...]]]></description>
			<content:encoded><![CDATA[<p>Linux Kernel实现的链表与众不同，大多数人所熟悉的方式应该是在“链表中包含结构体”，而Linux内核的实现方式则是所谓的“结构体中包含链表”。这样的说法听起来很玄乎，不如给出具体的定义和实例。</p>
<pre class="brush: cpp; title: ; notranslate">
struct list_head {
	struct list_head *next, *prev;
};
</pre>
<p>这就是内核代码中Linked List的结构体定义，从list_head定义可以看出，内核的链表是循环链表。我们可以这么用它：</p>
<p><span id="more-545"></span></p>
<pre class="brush: cpp; title: ; notranslate">
struct these_shit_dictators{
    char   name[MAX_LEN];
    char   country[MAX_LEN];
    struct list_head list;
};
</pre>
<p><strong>链表的操作接口</strong></p>
<p>一般在使用指针时要初始化，这是一个好的编程习惯，操作内核提供的链表也是如此。首先用INIT_LIST_HEAD()函数对其初始化。INIT_LIST_HEAD()做的工作很简单：</p>
<pre class="brush: cpp; title: ; notranslate">
#define INIT_LIST_HEAD(ptr) do { \
	(ptr)-&gt;next = (ptr); (ptr)-&gt;prev = (ptr); \
} while (0)
</pre>
<p>可以看到初始化就是把指针指向自己。在不同版本的源码中，这些函数的实现方式略有不同，例如在linux-2.6.26中，此函数不是宏定义，而是一个内联函数，不过它们所做的工作都是大同小异的，这一点在本文其他部分都一样，下面就不再提醒了。</p>
<p><strong>list_add操作</strong></p>
<p>插入操作所做的操作非常简单，只要学过基础的数据结构的人一眼就能看明白：</p>
<pre class="brush: cpp; title: ; notranslate">
static inline void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next){
	next-&gt;prev = new;
	new-&gt;next = next;
	new-&gt;prev = prev;
	prev-&gt;next = new;
}
</pre>
<p>对于循环链表来说，在头部插入和在尾部插入是一样的，确实，内核也是这么做的，看一段很有意思的代码：</p>
<pre class="brush: cpp; title: ; notranslate">
void list_add(struct list_head *new, struct list_head *head){
	__list_add(new, head, head-&gt;next);
}

void list_add_tail(struct list_head *new, struct list_head *head){
	__list_add(new, head-&gt;prev, head);
}
</pre>
<p>对于删除、合并等操作也都类似，有兴趣的读者可以自己翻看源码，比较简单的。</p>
<p><strong>遍历操作、获取结构体成员操作</strong></p>
<p>我认为kernel实现的链表最优意思的部分是list_entry、list_for_each等几个宏的实现。</p>
<p>首先有个问题：<strong><span style="color: #0000ff;">使用这种“Items contains the list”的方式如何通过这个list_head成员获得结构体的其他成员？</span></strong> 答案是list_entry宏，我们先来看内核源码是怎么写的：</p>
<pre class="brush: cpp; title: ; notranslate">
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

//  in include/linux/kernel.h

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({			\
	const typeof( ((type *)0)-&gt;member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})
</pre>
<p>用法： tmp=list_entry(ptr, type, member)<br />
其中，</p>
<p><strong><span style="color: #0000ff;">ptr是指向结构体中struct list_head 成员的指针；</span></strong><br />
<strong><span style="color: #0000ff;"> type是本结构体的名字；</span></strong><br />
<strong><span style="color: #0000ff;"> member是指在结构体中struct list_head叫什么名字，本例中叫list；</span></strong><br />
<strong><span style="color: #0000ff;"> 最后返回的是指向这个结构体的指针，有了这个指针，我们就可以调用其他成员了，比如tmp-&gt;name;</span></strong></p>
<p>然后，我们来分析一下这个有意思的宏：</p>
<p>1、const typeof( ((type *)0)-&gt;member ) *__mptr = (ptr);<br />
这句是声明了一个与名叫member的成员的类型一样的一个指针__mptr,并赋值为ptr;</p>
<p>2、(type *)( (char *)__mptr &#8211; offsetof(type,member) );<br />
mptr指向的地址减去member在type中的偏移，就得到了type的地址了；</p>
<p><strong>至于为什么要重新声明一个mptr，不直接用ptr呢？</strong><br />
<span style="color: #ff0000;"><del>我想大概是因为不破坏ptr指针的内容，以便后续的代码继续正常使用ptr。</del></span></p>
<p><strong><span style="color: #ff0000;">Update:  感谢 肉兔、Fleuria、Bearice 同学的帮助～</span></strong></p>
<blockquote><p>感觉应该就是为了加一层类型的静态检查吧，C的宏是没有类型的，但可以做点功夫给它模拟一点类型检查。假如没有这句，这个ptr若是个无关的类型（比如偶尔可能会敲错变量）在编译时不会报错，甚至warning也不会有。<br />
加上这么一句，如果ptr的类型不对，在编译时就可以报一个warning之类的了。删掉这句内核该依然可以正常编译，但有它可以让开发者的日子更好过一点。</p></blockquote>
<p>其他几个有意思的宏，例如list_for_each(pos, head)之类的实现原理也类似，在此就不详细写了，有兴趣的话请参看 include/linux/list.h</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
参考资料：<br />
1、《Linux内核设计与实现》<br />
2、<a href="http://basiccoder.com/intro-linux-kernel-list.html" target="_blank">浅析Linux Kernel中的那些链表</a>
</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=545">Linked List in Linux Kernel</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=545</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>用GDB追踪glibc代码执行过程</title>
		<link>http://blog.nlogn.cn/?p=522</link>
		<comments>http://blog.nlogn.cn/?p=522#comments</comments>
		<pubDate>Tue, 27 Mar 2012 06:29:58 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[glibc]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=522</guid>
		<description><![CDATA[这是Linux操作系统分析的实验， 要求是这样的：“通过glibc API函数追踪glibc代码和内核代码的调用过程，理解其关键机制，并能现场查找代码回答问题” 工欲善其事，必先利其器。 首先需要安装一下额外的工具包，一个是 libc6-dbg，这是带有debug symbol信息的 libc.so；另一个是libc6-dev，这是glibc的源代码，获取之后我们就可以在gdb中查看代码了。 在Ubuntu/Debian 系统上，我们可以通过以下2条命令获得： 在Fedora/Red Hat 系的OS上，需要安装的软件包的名字不叫 libc6-dbg，libc6-dev，貌似应该是glibc-debuginfo。 之后，以一段小程序为例来演示整个过程，小程序包含了一个系统调用fork()，一个库函数printf() 接着，编译产生带有调试信息的可执行文件 $gcc -g -o f fork.c 然后开启gdb调试 $gdb f 在开始调试之前，需要指定一下刚刚获得的带有libc6-dev源码文件夹的路径，比如我把这些源码放在了 ~/glibc/lib文件夹下，通常一般程序需要的是stdio-common这个目录内的文件，于是输入： (gdb) directory ~/glibc/lib/stdio-common 注意看其中几条命令的用法。 程序在调用fork函数后，其实执行的是glibc包装过的__libc_fork ，并且我们可以查看其源代码。 这里有几个常用命令： s 单步执行； list 查看源代码； start 程序开始执行，并在main函数处停下，相当于在main处加断点。 但是在执行了几步之后出现了这样的错误： _IO_list_lock () at genops.c:1299 1299 genops.c: No such file or directory. in genops.c 这是因为gdb没有找到genops.c文件，我们需要像刚才一样，用directory命令指明路径。 怎么知道路径呢？很简单，在刚刚获得的libc6-dev源码目录下搜索genops.c文件，然后拷贝路径就可以了。 (gdb)directory [...]]]></description>
			<content:encoded><![CDATA[<p>这是Linux操作系统分析的实验，<br />
要求是这样的：“通过glibc API函数追踪glibc代码和内核代码的调用过程，理解其关键机制，并能现场查找代码回答问题”</p>
<p>工欲善其事，必先利其器。<br />
首先需要安装一下额外的工具包，一个是 libc6-dbg，这是带有debug symbol信息的 libc.so；另一个是libc6-dev，这是glibc的源代码，获取之后我们就可以在gdb中查看代码了。</p>
<p>在Ubuntu/Debian 系统上，我们可以通过以下2条命令获得：<span id="more-522"></span>
<pre class="brush: bash; title: ; notranslate">
 $sudo apt-get install libc6-dbg
 $sudo apt-get source libc6-dev</pre>
<p>在Fedora/Red Hat 系的OS上，需要安装的软件包的名字不叫 libc6-dbg，libc6-dev，貌似应该是glibc-debuginfo。</p>
<p>之后，以一段小程序为例来演示整个过程，小程序包含了一个系统调用fork()，一个库函数printf()</p>
<pre class="brush: cpp; title: ; notranslate">
#include
#include
int main(){
    pid_t son;
    if((son=fork())==0)
        printf(&quot;I am son\n&quot;);
    else
        printf(&quot;I am farther\n&quot;);
    return 0;
}
</pre>
<p>接着，编译产生带有调试信息的可执行文件 $gcc -g -o f fork.c</p>
<p>然后开启gdb调试 $gdb f</p>
<p>在开始调试之前，需要指定一下刚刚获得的带有libc6-dev源码文件夹的路径，比如我把这些源码放在了 ~/glibc/lib文件夹下，通常一般程序需要的是stdio-common这个目录内的文件，于是输入： (gdb) directory ~/glibc/lib/stdio-common</p>
<p><a href="https://public.bay.livefilestore.com/y1pEagQpHK_SAie9sMb7XirlotS95sdRE0qa0gN-6aHOl0DCpBIbg5e7MV2CjjCFEbu0ZoEZbtuU_C0Nlppwa76yw/Screenshot-1.png?psid=1"><img class="alignnone" src="https://public.bay.livefilestore.com/y1pEagQpHK_SAie9sMb7XirlotS95sdRE0qa0gN-6aHOl0DCpBIbg5e7MV2CjjCFEbu0ZoEZbtuU_C0Nlppwa76yw/Screenshot-1.png?psid=1" alt="" width="635" height="265" /></a></p>
<p>注意看其中几条命令的用法。<br />
程序在调用fork函数后，其实执行的是glibc包装过的__libc_fork ，并且我们可以查看其源代码。<br />
这里有几个常用命令：<br />
s 单步执行；<br />
list 查看源代码；<br />
start 程序开始执行，并在main函数处停下，相当于在main处加断点。</p>
<p>但是在执行了几步之后出现了这样的错误：<br />
_IO_list_lock () at genops.c:1299<br />
1299 genops.c: No such file or directory.<br />
in genops.c</p>
<p>这是因为gdb没有找到genops.c文件，我们需要像刚才一样，用directory命令指明路径。<br />
怎么知道路径呢？很简单，在刚刚获得的libc6-dev源码目录下搜索genops.c文件，然后拷贝路径就可以了。<br />
(gdb)directory ~/glibc/lib/libio<br />
之后就可以正常执行了。</p>
<p>最后特别感谢一下 <a href="http://www.davelv.net/">Dave</a> 同学，在邮件列表里帮助我解决了不少困惑。<br />
这里是一篇<a href="http://fcamel-life.blogspot.ca/2012/01/glibc.html">参考资料</a>，来自blogspot，如何访问你懂的。
</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=522">用GDB追踪glibc代码执行过程</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=522</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>选课这件事儿</title>
		<link>http://blog.nlogn.cn/?p=499</link>
		<comments>http://blog.nlogn.cn/?p=499#comments</comments>
		<pubDate>Thu, 09 Feb 2012 11:02:33 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Others]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=499</guid>
		<description><![CDATA[2月8日凌晨紧张激烈的选课结束，很高兴想选的课都选到了，比如《编译工程》《Linux操作系统分析》《分布式与并行算法》等。 选课本身没什么好说的，但是其中的一些现象让我忍不住写下这篇博客。 早在选课前一天，班级的QQ群，学院QQ群，09级、10级的交流群变得异常的活跃，充斥着各种询问课程的消息。 本来，听取同学的意见、师兄师姐的经验作为自己的选课参考也是一件好事，但是事实上群里的讨论已经偏离这种意图。 因为很多人关心的问题是： 某门课好不好通过？考试难不难？ 老师布置的作业多不多？实验多不多？ 而没有人问 “这门课知识新不新？能不能收获很多东西？” 有些09级、10级的学长也是闲的蛋疼，在群里介绍着各种“经验”和“捷径”。 拿《企业领导学原理》来说，很明显这门课跟我们专业没有什么联系，学校开这门课充其量只是给我们开拓一下视野罢了。但是在选课开始5分钟之内已经爆满，原因很简单，因为这门课没有作业、没有卷面考试，只要在课程结束时交一篇论文即可获得2个学分，相比之下，《算法设计与分析》这样的专业课也才3个学分。 又比如，某些上学期选修过《Linux操作系统分析》的同学又开始在群里介绍他们的经验：“很多没选上的人都很庆幸”，“这门课很难，建议不要选”。也许他们本身其实并没有什么意图，然而却有很多人恰恰需要这样的信息，以便他们决定选不选这门课。 另外，此次选课更是出现了一些奇怪的现象，比如很多人一下选了很多课，群里给这种现象一个名词，叫“屯课”，就是把自己不要的课也选了，以后用来跟别人换课。看来“课”又多了一个价值——交换价值。 其实，仔细想想选课这件小事是我们这个浮躁、功利、道德滑坡的社会的一个缩影。 很多人都想着学习学得轻松，考试又能拿高分，这才出现了“考试难不难”、“作业多不多”这样的问题，却忘了我们来学校学习的目的。社会上，很多人抱怨着自己的工作不好，但是如果你问这些人什么是好工作？他们会说，钱多，工作很轻松，说出来又很有面子。没有人会说 “好工作就是自己喜欢的工作。” 很多人急着把课程快点学完，然后可以去早点找个实习。于是群里的师兄师姐介绍着各种经验：“原则上学校要求必须所有课程结束才允许，实际上&#8230;&#8230;.。” 非常经典的“原则上&#8230;..实际上&#8230;..”句式！也正是这些“原则上”破坏了原则，我们这个社会也正是这样。 很多人把努力、勤奋这两个字用在了寻找“捷径”上，通过“捷径”可以让自己快速成为“人上人”，而那些老老实实，按原则办事的人往往受尽排挤，生活在社会的最底层。每年的《感动中国》获奖者无一例外都是老老实实很本分的人，但是他们大多数都是社会底层最不起眼的人。 再拿“屯课”来说，不禁让我想起了前年的“抢盐”风波，其实不就是一些投机倒把的无良商人故意 “屯盐”，然后市场闹盐荒，最终导致高价盐等一系列闹剧，聪明的同学巧妙的把它用在了选课上，谁说我们学生没有创新意识！！ 话题貌似扯远了，以上说了一些不好的现象，其实还是有很多同学没有抱着很功利的态度的，比如我很某个同学私下交流，问他为什么要选2门数学课，他说：“就是觉得挺有用的。” 听了这句话我有一丝欣慰。 在这样一个大环境下，单纯的、不用功利的做一件事，并且有人能够理解，而不受世俗的偏见、排挤，究竟有多难？ 前几天读了一篇文章，《松本行弘：编程是可以干一辈子的》在这里推荐一下。 简简单单做人，开开心心做自己喜欢做的事。希望从今天开始，我能一直这样下去。 （备注：以上关于课程的评论是个人观点，只是拿来举例，没有故意偏见这门课的内容，没有故意贬低上课老师的意图。） &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 原创文章，转载请注明出处。 转载自Just for Fun 本文链接地址: 选课这件事儿]]></description>
			<content:encoded><![CDATA[<p>2月8日凌晨紧张激烈的选课结束，很高兴想选的课都选到了，比如《编译工程》《Linux操作系统分析》《分布式与并行算法》等。</p>
<p>选课本身没什么好说的，但是其中的一些现象让我忍不住写下这篇博客。</p>
<p>早在选课前一天，班级的QQ群，学院QQ群，09级、10级的交流群变得异常的活跃，充斥着各种询问课程的消息。</p>
<p>本来，<span style="color: #0000ff;"><span style="color: #000000;">听取同学的意见、师兄师姐的经验作为自己的选课参考也</span>是一件好事</span>，但是事实上群里的讨论已经<span style="color: #0000ff;">偏离</span>这种意图。<span id="more-499"></span></p>
<p>因为很多人关心的问题是：</p>
<blockquote><p>某门课好不好通过？考试难不难？</p>
<p>老师布置的作业多不多？实验多不多？</p></blockquote>
<p>而<span style="color: #ff0000;">没有人问 “这门课知识新不新？能不能收获很多东西？”</span></p>
<p>有些09级、10级的学长也是闲的蛋疼，在群里介绍着各种“经验”和“捷径”。</p>
<p>拿《企业领导学原理》来说，很明显这门课跟我们专业没有什么联系，学校开这门课充其量只是给我们开拓一下视野罢了。但是在选课开始5分钟之内已经爆满，原因很简单，因为这门课没有作业、没有卷面考试，只要在课程结束时交一篇论文即可获得2个学分，相比之下，《算法设计与分析》这样的专业课也才3个学分。</p>
<p>又比如，某些上学期选修过《Linux操作系统分析》的同学又开始在群里介绍他们的经验：<span style="color: #0000ff;">“很多没选上的人都很庆幸”，“这门课很难，建议不要选”。<span style="color: #000000;">也许他们本身其实并没有什么意图，然而却有很多人恰恰需要这样的信息，以便他们决定选不选这门课。</span></span></p>
<p>另外，此次选课更是出现了一些奇怪的现象，比如很多人一下选了很多课，群里给这种现象一个名词，叫“<span style="color: #0000ff;">屯课</span>”，就是把自己不要的课也选了，以后用来跟别人换课。看来“课”又多了一个价值——交换价值。</p>
<p>其实，仔细想想<strong><span style="color: #0000ff;">选课这件小事是我们这个浮躁、功利、道德滑坡的社会的一个缩影。</span></strong></p>
<p>很多人都想着学习学得轻松，考试又能拿高分，这才出现了“考试难不难”、“作业多不多”这样的问题，却忘了我们来学校学习的目的。社会上，很多人抱怨着自己的工作不好，但是如果你问这些人什么是好工作？他们会说，钱多，工作很轻松，说出来又很有面子。没有人会说 “好工作就是自己喜欢的工作。”</p>
<p>很多人急着把课程快点学完，然后可以去早点找个实习。于是群里的师兄师姐介绍着各种经验：“原则上学校要求必须所有课程结束才允许，实际上&#8230;&#8230;.。” <span style="color: #0000ff;">非常经典的“原则上&#8230;..实际上&#8230;..”句式！<span style="color: #000000;">也正是这些“原则上”破坏了原则，我们这个社会也正是这样。</span></span></p>
<p><span style="color: #0000ff;"><span style="color: #000000;">很</span><span style="color: #000000;">多人把努力、勤奋这两</span><span style="color: #000000;">个字用在了寻找<span style="color: #ff0000;">“捷径”</span>上，通过“捷径”可以让自己快速成为“人上人”，而那些老老实实，按原则办事的人往往受尽排挤，生活在社会的最底层。</span></span><span style="color: #000000;">每年的《感动中国》获奖者无一例外都是老老实实很本分的人，但是他们大多数都是社会底层最不起眼的人。</span></p>
<p>再拿“屯课”来说，不禁让我想起了前年的“抢盐”风波，其实不就是一些投机倒把的无良商人故意 “屯盐”，然后市场闹盐荒，最终导致高价盐等一系列闹剧，聪明的同学巧妙的把它用在了选课上<span style="color: #000000;">，谁说我们学生没有创新意识！！</span></p>
<p>话题貌似扯远了，以上说了一些不好的现象，其实还是有很多同学没有抱着很功利的态度的，比如我很某个同学私下交流，问他为什么要选2门数学课，他说：“就是觉得挺有用的。” 听了这句话我有一丝欣慰。</p>
<p><strong><span style="color: #0000ff;">在这样一个大环境下，单纯的、不用功利的做一件事，并且有人能够理解，而不受世俗的偏见、排挤，究竟有多难？</span></strong></p>
<p>前几天读了一篇文章，《<a href="http://topic.csdn.net/u/20120129/09/7E35A8BA-2211-4427-BF75-03B824043A70.html">松本行弘：编程是可以干一辈子的</a>》在这里推荐一下。</p>
<p><strong><span style="color: #ff0000;">简简单单做人，开开心心做自己喜欢做的事。希望从今天开始，我能一直这样下去。</span></strong></p>
<p>（备注：以上关于课程的评论是个人观点，只是拿来举例，没有故意偏见这门课的内容，没有故意贬低上课老师的意图。）
</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=499">选课这件事儿</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=499</wfw:commentRss>
		<slash:comments>37</slash:comments>
		</item>
		<item>
		<title>Database Buffer Manager</title>
		<link>http://blog.nlogn.cn/?p=491</link>
		<comments>http://blog.nlogn.cn/?p=491#comments</comments>
		<pubDate>Wed, 18 Jan 2012 15:02:37 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[Database]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=491</guid>
		<description><![CDATA[要求：模拟一个缓存管理器。给出的测试文件中包含50万个页面请求，每一行用（X，Y）表示，其中X=1表示对页面号为Y的页面写操作，X=0表示对页面号为Y的页面读操作。例如  0,6558     1,3024  等。 规定：磁盘上的页面大小等于Buffer的一个块大小，为4KB；磁盘上的数据库文件data.dbf包含5000个页面，缓冲区有1024个Buffer。设计一个缓存管理器，并统计页面请求的命中和不命中次数。 先说下我的思路，如图，问题牵涉到下面三个部分的协同工作，其中省略了磁盘的读写部分。 解释一下图中的各个部分： 1、Buffer的每一个块4KB，等于磁盘中的数据文件一页的大小，不同的是文件data.dbf有5000页，约193MB，Buffer只有1024块，因此不可能把文件全部加载到缓存中，所以需要有一种合适置换策略，尽量让访问最多的页面驻留在Buffer中，这样可以减少去磁盘读取的次数。 2、Buffer中的每一块都有一个相应的控制结构BCB，BCB详细的记录Buffer中块号（Frame ID）和这一块在文件中所对应的页面号（Page ID）。 3、先说Hash Table，然后再说Table。所有的BCB都散列在Hash Table中，对于每一个页面请求，先使用hash函数将请求的页面号（Page ID）定位到对应的bucket内，在bucket内是一串BCB组成的链表，逐个比较BCB结构内的页面号，如果有与所请求的页面相等的，则说明该页面在Buffer中，直接返回；如果没有搜索到，则说明该页面不再Buffer内，需要去data.dbf文件中将该页面读入，并且在Buffer中选择一个最久没有使用的块，将它的位置让给新的页面。 4、Table就是保存Buffer中每个块所对应的Page ID以及最近一次访问的时间，根据Table的内容，我们可以找出一个最久没有使用的块。 很显然的，如何有效的管理Table，使得我们可以高效的找出一个被替换出Buffer的牺牲页，是至关重要的。 如何来找呢？ 老师给的实验参考上说要用LRU算法，因此我最先想到的是以引用时间为关键字，建立一个小根堆，一旦某一个页面被访问，立即更新它的引用时间，这样堆顶元素就是最久没有使用的块了。于是我写好了堆的算法，但是发现了一个致命的弱点：由于页面请求是随机的，因此无法根据请求的页面ID在堆中快速的找到相应的Page ID是在第几个位置（因为堆中元素是在不断调整的），除非逐个搜索，但是这样就变成线性查找了，失去了本来用堆的目的，因此放弃。用链表法？显然查找最小的时间的复杂度也是O(n)。 然后尝试使用FIFO，但是牵涉到是队列的调整（因为某个页面已经在队列中，这时它又被访问了，它的位置并不一定在队头，那么需要将它从队列中取出放到队尾，取出后其他元素也要前移，所以它的复杂度并不比线性查找要好。 之后，想到的是将Table与Buffer一一对应，即Table[1]记录Buffer[1]储存的是哪一个Page，已经访问的时间，但要寻找最小的时间时，遍历整个Table找到最小，时间复杂度是O(n)，这个看似比较费时的操作在现在一般配置的机器上似乎并不慢，于是选择的是这种方法。 当我写好了以后，在CFAN的群里面和几位大牛讨论的时候还提到了一种NRU法，即最近未使用法，不过牵涉到对整个Table周期性的更新引用时间，我目前还没想好具体的做法，也许等写完这篇Blog后可以仔细的思考。 同样，程序的代码放在我的GitHub上，这里是运行结果： Dave GG说，可以不使用引用时间。 的确，因为现在的计算机执行指令的速度超乎想象，在2次更新引用时间之间，即使是gettimeofday()函数取得微秒级的时间，也有可能是一样的，但是有没有什么更好的方法呢？用计数器似乎还有一个溢出的问题。 纸上得来终觉浅，绝知此事要躬行。确实是这样的，每一次写这样的程序都会有很多收获，也有更多疑惑，会发现很多跟书上讲的不一致的东西。不知道真正的数据库里是用的什么替换算法？是怎么实现的？操作系统里真正又是用的什么置换算法？又是怎么实现的？ &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 原创文章，转载请注明出处。 转载自Just for Fun 本文链接地址: Database Buffer Manager]]></description>
			<content:encoded><![CDATA[<p>要求：模拟一个缓存管理器。给出的测试文件中包含50万个页面请求，每一行用（X，Y）表示，其中X=1表示对页面号为Y的页面写操作，X=0表示对页面号为Y的页面读操作。例如  0,6558     1,3024  等。<br />
规定：磁盘上的页面大小等于Buffer的一个块大小，为4KB；磁盘上的数据库文件data.dbf包含5000个页面，缓冲区有1024个Buffer。设计一个缓存管理器，并统计页面请求的命中和不命中次数。</p>
<p>先说下我的思路，如图，问题牵涉到下面三个部分的协同工作，其中省略了磁盘的读写部分。</p>
<p><iframe style="padding: 0; background-color: #fcfcfc;" title="Preview" src="https://skydrive.live.com/embed?cid=B0400D0662D23589&amp;resid=B0400D0662D23589%21676&amp;authkey=AHFJRHdpmpvL7m0" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="320px" height="280px"></iframe><br />
<span id="more-491"></span><br />
解释一下图中的各个部分：<br />
1、Buffer的每一个块4KB，等于磁盘中的数据文件一页的大小，不同的是文件data.dbf有5000页，约193MB，Buffer只有1024块，因此不可能把文件全部加载到缓存中，所以需要有一种合适置换策略，尽量让<span style="color: #ff0000;">访问最多</span>的页面驻留在Buffer中，这样可以减少去磁盘读取的次数。</p>
<p>2、Buffer中的每一块都有一个相应的控制结构BCB，BCB详细的记录Buffer中块号（Frame ID）和这一块在文件中所对应的页面号（Page ID）。</p>
<p>3、先说Hash Table，然后再说Table。所有的BCB都散列在Hash Table中，对于每一个页面请求，先使用hash函数将请求的页面号（Page ID）定位到对应的bucket内，在bucket内是一串BCB组成的链表，逐个比较BCB结构内的页面号，如果有与所请求的页面相等的，则说明该页面在Buffer中，直接返回；如果没有搜索到，则说明该页面不再Buffer内，需要去data.dbf文件中将该页面读入，并且在Buffer中选择一个<span style="color: #ff0000;">最久没有使用的块</span>，将它的位置让给新的页面。</p>
<p>4、Table就是保存Buffer中每个块所对应的Page ID以及最近一次访问的时间，根据Table的内容，我们可以找出一个最久没有使用的块。</p>
<p><strong><span style="color: #ff0000;">很显然的，如何有效的管理Table，使得我们可以高效的找出一个被替换出Buffer的牺牲页，是至关重要的。</span></strong></p>
<p><strong><span style="color: #000080;">如何来找呢？</span></strong><br />
老师给的实验参考上说要用LRU算法，因此我最先想到的是以引用时间为关键字，建立一个<strong><span style="color: #ff0000;">小根堆</span></strong>，一旦某一个页面被访问，立即更新它的引用时间，这样堆顶元素就是最久没有使用的块了。于是我写好了堆的算法，但是发现了一个致命的弱点<span style="color: #0000ff;">：由于页面请求是随机的，因此无法根据请求的页面ID在堆中快速的找到相应的Page ID是在第几个位置（因为堆中元素是在不断调整的）</span>，除非逐个搜索，但是这样就变成线性查找了，失去了本来用堆的目的，因此放弃。用链表法？显然查找最小的时间的复杂度也是O(n)。</p>
<p>然后尝试使用<strong><span style="color: #ff0000;">FIFO</span></strong>，但是牵涉到是队列的调整（因为某个页面已经在队列中，这时它又被访问了，它的位置并不一定在队头，那么需要将它从队列中取出放到队尾，取出后其他元素也要前移，所以它的复杂度并不比线性查找要好。</p>
<p>之后，想到的是将Table与Buffer一一对应，即Table[1]记录Buffer[1]储存的是哪一个Page，已经访问的时间，但要寻找最小的时间时，遍历整个Table找到最小，时间复杂度是O(n)，这个看似比较费时的操作在现在一般配置的机器上似乎并不慢，于是选择的是这种方法。</p>
<p>当我写好了以后，在CFAN的群里面和几位大牛讨论的时候还提到了一种<strong><span style="color: #ff0000;">NRU法</span></strong>，即最近未使用法，不过牵涉到对整个Table周期性的更新引用时间，我目前还没想好具体的做法，也许等写完这篇Blog后可以仔细的思考。<br />
同样，程序的代码放在我的GitHub上，这里是运行结果：<br />
<iframe style="padding: 0; background-color: #fcfcfc;" title="Preview" src="https://skydrive.live.com/embed?cid=B0400D0662D23589&amp;resid=B0400D0662D23589%21677&amp;authkey=ANThYtj8B3wf5SQ" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="263px" height="65px"></iframe><br />
Dave GG说，可以不使用引用时间。<br />
<span style="color: #0000ff;">的确，因为现在的计算机执行指令的速度超乎想象，在2次更新引用时间之间，即使是gettimeofday()函数取得微秒级的时间，也有可能是一样的，但是有没有什么更好的方法呢？用计数器似乎还有一个溢出的问题。</span></p>
<p><span style="color: #000080;">纸上得来终觉浅，绝知此事要躬行。确实是这样的，每一次写这样的程序都会有很多收获，也有更多疑惑，会发现很多跟书上讲的不一致的东西。不知道真正的数据库里是用的什么替换算法？是怎么实现的？操作系统里真正又是用的什么置换算法？又是怎么实现的？</span>
</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=491">Database Buffer Manager</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=491</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>kmeans算法 C语言实现</title>
		<link>http://blog.nlogn.cn/?p=479</link>
		<comments>http://blog.nlogn.cn/?p=479#comments</comments>
		<pubDate>Mon, 12 Dec 2011 01:42:37 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Data Mining]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=479</guid>
		<description><![CDATA[在上一篇博客里提到了一个简单的聚类算法kmeans。这里是用C语言写的一个命令行的版本，它似乎可以运行，也能产生结果，不过我也没有仔细验证过是否正确 :-)    这里是在GitHub上托管的源代码。 使用方法以Linux环境为例，首先make一下，生成可执行文件kmeans 然后 ./kmeans  input.csv  ouput.csv  3   random  means input.csv和ouput.csv是输入数据和输出数据文件，CSV文件不神秘，用文本编辑器打开看就是几行点坐标数据。 3是指定要把点集分成几个类。 中心点Center计算有两种方式：1.mean(均值); 2.median（中位数） 初始化方法有以下两种方式：1.random partition（随机初始化）; 2.k-means++（Knuth的弟子想出的方法） 其实kmeans原理很简单，但是实现起来还是比较繁琐的，特别是用C语言的时候很多处理过程都要自己写。 一开始我对kmeans算法的复杂度严重估计不足，以为两三百行代码可以搞定，于是打开电脑就开始写，后来越写越复杂，代码行数也越来越多，不得分成多个文件，但定义的一些全局变量就不好用了，于是全部塞进kmeans.h头文件。更坑爹的是程序要求先执行初始化后在调用后续的函数处理，我是上星期先写好了random partition和mean聚类法，然后过了几天又来写kmeans++的，当时也没有考虑周全，直接导致了用两种不同的数据结构保存产生的结果，使得后续的函数无法处理kmeans++的结果，于是创造出了saved这么一个奇葩的数据结构专门转换数据，总之代码是写的一团糟。 所以，写这篇博客的目的还是总结一下失败的经验教训，平时自己写程序习惯打开电脑就写，事先不做好设计，一旦代码量变多或者是遇到复杂一点的算法时总会出现各种各样的问题。 想到了Frederick Brooks大牛在《The Mythical Man-Month（人月神话）》中提到的： 三分之一的时间  Planning 六分之一的时间  Coding 四分之一的时间  Component test and early system test 四分之一的时间  System test,all components in hand 可见，先驱给我们的忠告是花大部分的时间做好整体的设计，设计好了再动手coding。像我写kmeans的时候根本就没有仔细的考虑过算法流程就动手coding，以至于后来发现了BUG想修改时已经有“牵一发动全身”的感觉了，就好像《The Mythical Man-Month》在书开头第一段就描绘的情景那样： No scene from prehistory is quite so vivid as [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://blog.nlogn.cn/?p=449">上一篇</a>博客里提到了一个简单的聚类算法kmeans。这里是用C语言写的一个命令行的版本，<span style="color: #ff6600;">它似乎可以运行</span>，也能产生结果，不过我也没有仔细验证过是否正确 :-)    这里是在<a href="https://github.com/Run/kmeans">GitHub</a>上托管的源代码。</p>
<p>使用方法以Linux环境为例，首先make一下，生成可执行文件kmeans</p>
<p>然后 ./kmeans  input.csv  ouput.csv  3   random  means</p>
<p>input.csv和ouput.csv是输入数据和输出数据文件，CSV文件不神秘，用文本编辑器打开看就是几行点坐标数据。<br />
3是指定要把点集分成几个类。<br />
中心点Center计算有两种方式：1.mean(均值); 2.median（中位数）<br />
初始化方法有以下两种方式：1.random partition（随机初始化）; 2.k-means++（Knuth的弟子想出的方法）<span id="more-479"></span></p>
<p>其实kmeans原理很简单，但是实现起来还是比较繁琐的，特别是用C语言的时候很多处理过程都要自己写。</p>
<p>一开始我对kmeans算法的复杂度严重估计不足，以为两三百行代码可以搞定，于是打开电脑就开始写，后来越写越复杂，代码行数也越来越多，不得分成多个文件，但定义的一些全局变量就不好用了，于是全部塞进kmeans.h头文件。更坑爹的是程序要求先执行初始化后在调用后续的函数处理，我是上星期先写好了random partition和mean聚类法，然后过了几天又来写kmeans++的，当时也没有考虑周全，直接导致了用两种不同的数据结构保存产生的结果，使得后续的函数无法处理kmeans++的结果，于是创造出了saved这么一个<span style="color: #ff0000;">奇葩</span>的数据结构专门转换数据，总之代码是写的一团糟。</p>
<p>所以，写这篇博客的目的还是总结一下失败的经验教训，平时自己写程序习惯打开电脑就写，事先不做好设计，<span style="color: #0000ff;">一旦代码量变多或者是遇到复杂一点的算法时总会出现各种各样的问题。</span></p>
<p>想到了Frederick Brooks大牛在《The Mythical Man-Month（人月神话）》中提到的：</p>
<p>三分之一的时间 <span style="color: #ff0000;"> <strong>Planning</strong></span><br />
六分之一的时间  <span style="color: #ff0000;"><strong>Coding</strong></span><br />
四分之一的时间  <span style="color: #ff0000;"><strong>Component test and early system test</strong></span><br />
四分之一的时间  <span style="color: #ff0000;"><strong>System test,all components in hand</strong></span></p>
<p>可见，先驱给我们的忠告是花大部分的时间做好整体的设计，设计好了再动手coding。像我写kmeans的时候根本就没有仔细的考虑过算法流程就动手coding，以至于后来发现了BUG想修改时已经有“<span style="color: #0000ff;">牵一发动全身</span>”的感觉了，就好像《The Mythical Man-Month》在书开头第一段就描绘的情景那样：</p>
<blockquote>
<h3>No scene from prehistory is quite so vivid as that of the mortal struggles of great beasts in the tar pits.In the mind&#8217;s eye one sees dinosaurs,mammoths,and sabertoothed tigers struggling against the grip of the tar.The fiercer the struggle,the more entagling the tar,and no beast is so strong or so skillful but that he ultimately sinks.</h3>
</blockquote>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=479">kmeans算法 C语言实现</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=479</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>数据挖掘 k-means 算法</title>
		<link>http://blog.nlogn.cn/?p=449</link>
		<comments>http://blog.nlogn.cn/?p=449#comments</comments>
		<pubDate>Fri, 18 Nov 2011 10:52:50 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Data Mining]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=449</guid>
		<description><![CDATA[K-means Clustering Algorithm 中文名也许叫“K均值聚类算法”，是统计学和数据挖掘领域中常用的一种算法。维基百科上是这样介绍的：k-means clustering is a method of cluster analysis which aims to partition n observations into k clusters in which each observation belongs to the cluster with the nearest mean（将n个观察值分成k个类，使得每一类中的观察值与该类的均值最接近，与其他的类的均值较远）。 先来看一个最简单、最直观的图示。 上图有很多点，现在想将他们分成3个cluster，怎么办？ 作为人，一眼就看出来了，但是计算机就没那么容易分类了，我们必须借助一些算法，而k-means就是其中的一种。K-means不仅可以处理二维空间的聚类，还可以扩展到n维向量空间，还可以处理字符、图像、声音等等。 以上图为例，K-means算法的基本步骤如下： 输入：一个要处理的数据集（例如上图的点集），分成cluster的个数（比如3个），一个mean的计算方法（比如两点之间的距离函数，） Step1. 首先随机的给每个点标上一种颜色，并计算同种颜色点坐标的算术平均值，表示出相 应的均值点。 Step2. 根据目前算出的均值点，将所有的点集分成3类，为每一类中的每个点，标上与离它最近的均值点相同的颜色。怎么分呢？这里要介绍一种“泰森多边形法”，英文名叫“Voronoi diagram”（见文章最后维基百科链接）。于是就有了下面这张图。 Step3.重复step2，直到所有点的颜色不再变化为止。 算法结束，输出如下结果。 上面的例子在简单的二维空间里，如果放在三维空间那么mean的计算方法就要修改了。事实上在处理多维空间、字符、图像等问题时，不同的问题有不同的计算公式，这时mean的意思可能就不是“均值”了，也许用“相似度”和“相异度”来衡量个体之间的关系会更好，详见参考文章一。 按照惯例，下面应该贴上我自己写的k-means算法代码了，不过很遗憾的是我现在还在摸索用python的numpy库和matplotlib库画图的方法，在参考文章二中有一个python语言的代码。 最后要感谢一下数据挖掘老师  Devert Alexandre，因为本文的图片都是从他的slides里截出来的。^_^ 参考文章一 参考文章二 维基百科k-means链接 泰森多边形法维基百科链接（Voronoi diagram） [...]]]></description>
			<content:encoded><![CDATA[<p>K-means Clustering Algorithm 中文名也许叫“K均值聚类算法”，是统计学和数据挖掘领域中常用的一种算法。维基百科上是这样介绍的：k-means clustering is a method of cluster analysis which aims to partition n observations into k clusters in which each observation belongs to the cluster with the nearest mean<span id="more-449"></span>（将n个观察值分成k个类，使得每一类中的观察值与该类的均值最接近，与其他的类的均值较远）。</p>
<p>先来看一个最简单、最直观的图示。</p>
<p><a href="https://byfiles.storage.live.com/y1pHfWGy4U0t5jZbXybnY8wsr43fZkJ_yylg1SaNX5NbDILKmwmL_hjTd3BLlreXuVx94S5TeuOeDk/k_means_1.jpg?psid=1"><img class="alignnone" src="https://byfiles.storage.live.com/y1pHfWGy4U0t5jZbXybnY8wsr43fZkJ_yylg1SaNX5NbDILKmwmL_hjTd3BLlreXuVx94S5TeuOeDk/k_means_1.jpg?psid=1" alt="" width="283" height="230" /></a></p>
<p>上图有很多点，现在想将他们分成3个cluster，怎么办？ 作为人，一眼就看出来了，但是计算机就没那么容易分类了，我们必须借助一些算法，而k-means就是其中的一种。K-means不仅可以处理二维空间的聚类，还可以扩展到n维向量空间，还可以处理字符、图像、声音等等。</p>
<p>以上图为例，K-means算法的基本步骤如下：<br />
<strong><span style="color: #0000ff;">输入</span></strong>：一个要处理的数据集（例如上图的点集），分成cluster的个数（比如3个），一个mean的计算方法（比如两点之间的距离函数，）<br />
<strong><span style="color: #0000ff;">Step1</span></strong>. 首先随机的给每个点标上一种颜色，并计算同种颜色点坐标的算术平均值，表示出相 应的均值点。<br />
<strong><span style="color: #0000ff;">Step2</span></strong>. 根据目前算出的均值点，将所有的点集分成3类，为每一类中的每个点，标上与离它最近的均值点相同的颜色。怎么分呢？这里要介绍一种“泰森多边形法”，英文名叫“Voronoi diagram”（见文章最后维基百科链接）。于是就有了下面这张图。</p>
<p><a href="https://byfiles.storage.live.com/y1pHfWGy4U0t5i629hiKJR2wmz-9XvcFmVoz0FmVXwiO7oxAi1lTv8Yf8oxG6MKoAh8D-6KAaZQRU0/k_means_2.jpg?psid=1"><img class="alignnone" src="https://byfiles.storage.live.com/y1pHfWGy4U0t5i629hiKJR2wmz-9XvcFmVoz0FmVXwiO7oxAi1lTv8Yf8oxG6MKoAh8D-6KAaZQRU0/k_means_2.jpg?psid=1" alt="" width="328" height="283" /></a></p>
<p><strong><span style="color: #0000ff;">Step3</span></strong>.重复step2，直到所有点的颜色不再变化为止。<br />
算法结束，输出如下结果。</p>
<p><a href="https://byfiles.storage.live.com/y1p6bA62TvL0TB9bb5RK7ZP-7_EINw79O2jtqRvCJArDi62b35LHRz8SdM_lh8q-orVDMCCIXEH96s/k_means_3.jpg?psid=1"><img class="alignnone" src="https://byfiles.storage.live.com/y1p6bA62TvL0TB9bb5RK7ZP-7_EINw79O2jtqRvCJArDi62b35LHRz8SdM_lh8q-orVDMCCIXEH96s/k_means_3.jpg?psid=1" alt="" width="306" height="279" /></a></p>
<p>上面的例子在简单的二维空间里，如果放在三维空间那么mean的计算方法就要修改了。事实上在处理多维空间、字符、图像等问题时，不同的问题有不同的计算公式，这时mean的意思可能就不是“<strong>均值</strong>”了，也许用“<strong>相似度</strong>”和“<strong>相异度</strong>”来衡量个体之间的关系会更好，详见参考文章一。</p>
<p>按照惯例，下面应该贴上我自己写的k-means算法代码了，不过很遗憾的是我现在还在摸索用python的numpy库和matplotlib库画图的方法，在参考文章二中有一个python语言的代码。</p>
<p>最后要感谢一下数据挖掘老师  Devert Alexandre，因为本文的图片都是从他的slides里截出来的。^_^</p>
<p><a href="http://www.cnblogs.com/leoo2sk/archive/2010/09/20/k-means.html" target="_blank">参考文章一</a><br />
<a href="http://blog.pluskid.org/?p=17" target="_blank">参考文章二</a><br />
<a href="http://en.wikipedia.org/wiki/K_means" target="_blank">维基百科k-means链接</a><br />
<a href="http://en.wikipedia.org/wiki/Voronoi_cell" target="_blank">泰森多边形法</a>维基百科链接（Voronoi diagram）
</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=449">数据挖掘 k-means 算法</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=449</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>从书上的一个错误说起</title>
		<link>http://blog.nlogn.cn/?p=441</link>
		<comments>http://blog.nlogn.cn/?p=441#comments</comments>
		<pubDate>Sat, 01 Oct 2011 04:01:51 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[C and CPP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[C]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=441</guid>
		<description><![CDATA[时间倒回到2011年5月的一天，大学的最后一门课《计算机信息安全技术》，讲到《缓冲区溢出》这一章，并且给出了一段示例代码来演示缓冲区溢出，回到宿舍后出于好奇我运行了一下这段代码，发现结果并不是书上所说的那样，当时在人人网也发过一篇吐槽的日志，但是一直拖到现在都没有仔细的去研究过，正好现在十一放假没事，就花点时间搞搞啦。 书第136页-137页。代码如下，出于简单考虑（其实书上的C++代码格式也是错的），我除去了头文件和cout函数，这样就跟纯C语言代码是一样了。 书上说最后x的值是10，不是1，而我的结果恰恰相反。 接着用gcc产生汇编代码，在这里用 gcc -O0 -S 命令告诉编译器不采用任何优化措施，产生最原始的汇编代码，这样有利于我们分析，即使是采用-O1级优化的时候，汇编代码已经很难读了，大家可以试一试。 书上详细的解释了为什么结果是10，下面我来逐条分析。首先画一张内存图，同样处于简洁考虑，只画function函数附近的内存分布，不影响分析。 1、书上说：“为char buffer[5]分配内存时，由于32位存储器需要4字节对齐，因此一共为buffer分配了8个字节”。 由 leal -9(%ebp), %eax 代码可以看出，编译器实际上没有为buffer分配8个字节，而是只分配了5个字节，还有4个字节给了 char *ret,因此正好是9个字节，见图中0xEB &#8211; 0xE3部分。 2、书上说：“执行 ret=buffer+12后，ret指向返回地址” 实际上，从图上看出加12后指向的是%ebp的最高字节0xEF。 3、书上说：“执行 ret+=8后，返回地址的值加上了8个字节，而x=1这条语句占有8个字节，因此正好跳过，执行下一条语句 cout&#60;&#60;x ”。 实际上是把%ebp=0&#215;00000108 的最高有效位 0&#215;00加8了，因此运行时会产生“段错误”（是这个原因吗？求指教） 因此，书上长篇大论的分析在我的机器上是行不通的，不过还要注意2点： 1、现代的编译器通常都引入了“栈随机化”、“破坏检测”等多种手段来阻止缓冲区溢出的攻击，像书中所说的那样通过把某个指针+8，程序就从某条特定语句开始执行并不是那么简单就能实现的。 2、即使是相同的OS和gcc版本，在不同的CPU上生成的汇编代码是不一样的，运行的结果也不一样。例如，Core i5和 奔腾双核，在Core i5上运行结果是1，甚至都不会产生“Segmentation fault”的错误，而在奔腾CPU上结果为1，产生“段错误”提示。 总之，书上的代码也许在某个特定的、老版本的编译器环境下会发生，但是结果是不可重复的，而且，在不同机器上也会表现出不同结果。 最后，这篇文章的分析过程是我根据实际运行结果结合汇编代码猜测出来的，我对这方面也不是很懂，所以，如果哪位读者看出了其中有什么错误，非常诚恳的欢迎你指出，我也很想知道错误的原因，Thanks~ &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 原创文章，转载请注明出处。 转载自Just for Fun 本文链接地址: 从书上的一个错误说起]]></description>
			<content:encoded><![CDATA[<p>时间倒回到2011年5月的一天，大学的最后一门课《计算机信息安全技术》，讲到《缓冲区溢出》这一章，并且给出了一段示例代码来演示缓冲区溢出，回到宿舍后出于好奇我运行了一下这段代码，发现结果并不是书上所说的那样，当时在人人网也发过一篇吐槽的日志，但是一直拖到现在都没有仔细的去研究过，正好现在十一放假没事，就花点时间搞搞啦。</p>
<p>书第136页-137页。代码如下，出于简单考虑（其实书上的C++代码格式也是错的），我除去了头文件和cout函数，这样就跟纯C语言代码是一样了。<span id="more-441"></span></p>
<pre class="brush: cpp; title: ; notranslate">
void function(int a)
{
    char buffer[5];
    char *ret;
    ret=buffer+12;
    *ret+=8;
}
int main()
{
    int x;
    x=10;
    function(7);
    x=1;
    return 0;
}</pre>
<p>书上说最后x的值是10，不是1，而我的结果恰恰相反。</p>
<p>接着用gcc产生汇编代码，在这里用 gcc -O0 -S 命令告诉编译器不采用任何优化措施，产生最原始的汇编代码，这样有利于我们分析，即使是采用-O1级优化的时候，汇编代码已经很难读了，大家可以试一试。</p>
<pre class="brush: bash; title: ; notranslate">
function:
    pushl %ebp
    movl %esp, %ebp
    subl $16, %esp
    leal -9(%ebp), %eax
    addl $12, %eax
    movl %eax, -4(%ebp)
    movl -4(%ebp), %eax
    movzbl (%eax), %eax
    addl $8, %eax
    movl %eax, %edx
    movl -4(%ebp), %eax
    movb %dl, (%eax)
    leave

main:
    pushl %ebp
    movl %esp, %ebp
    subl $20, %esp
    movl $10, -4(%ebp)
    movl $7, (%esp)
    call function
    movl $1, -4(%ebp)
    leave
    ret</pre>
<p>书上详细的解释了为什么结果是10，下面我来逐条分析。首先画一张内存图，同样处于简洁考虑，只画function函数附近的内存分布，不影响分析。</p>
<p><a href="https://byfiles.storage.live.com/y1pDz8ypvV8j7fF_I9w2D64CRSUbRy7E3u0x-S7T5oNHc_5DEDu71eZ_p4KspR_JpYU3OjBwVIWOrAdlG_z5klhYw"><img class="alignnone" src="https://byfiles.storage.live.com/y1pDz8ypvV8j7fF_I9w2D64CRSUbRy7E3u0x-S7T5oNHc_5DEDu71eZ_p4KspR_JpYU3OjBwVIWOrAdlG_z5klhYw" alt="" width="404" height="493" /></a></p>
<p><span style="color: #0000ff;">1、书上说：“为char buffer[5]分配内存时，由于32位存储器需要4字节对齐，因此一共为buffer分配了8个字节”。</span></p>
<p>由 leal -9(%ebp), %eax 代码可以看出，编译器实际上没有为buffer分配8个字节，而是只分配了5个字节，还有4个字节给了 char *ret,因此正好是9个字节，见图中0xEB &#8211; 0xE3部分。</p>
<p><span style="color: #0000ff;">2、书上说：“执行 ret=buffer+12后，ret指向返回地址”</span><br />
实际上，从图上看出加12后指向的是%ebp的最高字节0xEF。</p>
<p><span style="color: #0000ff;">3、书上说：“执行 ret+=8后，返回地址的值加上了8个字节，而x=1这条语句占有8个字节，因此正好跳过，执行下一条语句 cout&lt;&lt;x ”。</span></p>
<p>实际上是把%ebp=0&#215;00000108 的最高有效位 0&#215;00加8了，因此运行时会产生“段错误”（是这个原因吗？求指教）</p>
<p>因此，书上长篇大论的分析在我的机器上是行不通的，不过还要注意2点：</p>
<p><span style="color: #000080;">1、现代的编译器通常都引入了“栈随机化”、“破坏检测”等多种手段来阻止缓冲区溢出的攻击，像书中所说的那样通过把某个指针+8，程序就从某条特定语句开始执行并不是那么简单就能实现的。</span></p>
<p><span style="color: #000080;">2、即使是相同的OS和gcc版本，在不同的CPU上生成的汇编代码是不一样的，运行的结果也不一样。例如，Core i5和 奔腾双核，在Core i5上运行结果是1，甚至都不会产生“Segmentation fault”的错误，而在奔腾CPU上结果为1，产生“段错误”提示。</span></p>
<p><span style="color: #ff0000;"><strong>总之，书上的代码也许在某个特定的、老版本的编译器环境下会发生，但是结果是不可重复的，而且，在不同机器上也会表现出不同结果。</strong></span></p>
<p><span style="color: #ff0000;"><strong>最后，这篇文章的分析过程是我根据实际运行结果结合汇编代码猜测出来的，我对这方面也不是很懂，所以，如果哪位读者看出了其中有什么错误，非常诚恳的欢迎你指出，我也很想知道错误的原因，Thanks~</strong></span>
</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=441">从书上的一个错误说起</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=441</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>写在开学前</title>
		<link>http://blog.nlogn.cn/?p=434</link>
		<comments>http://blog.nlogn.cn/?p=434#comments</comments>
		<pubDate>Wed, 31 Aug 2011 16:31:23 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Others]]></category>
		<category><![CDATA[日记]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=434</guid>
		<description><![CDATA[暑假终究是要过完的，再次感叹一下时间过得飞快！ 翻一翻毕业照片，感觉好像也就是前几天发生的事情一样，熟悉的笑容，熟悉的校园，只是再也回不到那个计科07班了； 翻一翻暑假买的书，感觉好像也就是昨天才送来的一样，依旧没翻几页。 - 回想这个暑假，甚至都没写几行代码。 坐在书桌前的时间倒是不少，但真正好像也没看进去多少书，不然现在怎么回想不起来呢。 为毛会这个样子呢？（好像一直都这个样子的吧，囧） - 不过人还是要向前看的，过去的就过去了，要好好把握未来。 明天去学校又可以认识一大帮新同学，组成一个新班级，希望在新同学中能找到志同道合的朋友（虽然概率很小）。 然后好好学习，这学期开得课我都非常喜欢，用的教材也是非常经典的书，而且据说实验在整个教学中所占的比例很大，这些正是我希望的。 - 总之要把握好这短短的一年时间。 说到坑爹的选课就吐槽一下，本来通知上写的是8月20号开通选课系统，但是在那一天我打开了选课网站发现依然没开始，于是我想：继续等学院首页的通知吧！一转眼就到了23号，说到这里要感谢一下Dave的好友老妖前辈（他正好是我的学长），还是他那天在人人网上问我选课开始没，我一打开网站，发现好多课选得人数已经满了，尼玛欲哭无泪啊！然后我就一直刷屏看有没有人退课&#8230;.（中间省略n多字，5天过去了）&#8230;.28号上午，经过我的不懈的努力的刷屏，加上运气也不错，最终想学的课都被我选到了。 - 来欣赏一下这些苦逼的课程吧。 /* &#8212;&#8212;&#8212;&#8212;&#8212;&#8211;Update&#8212;&#8212;&#8212;&#8212; 最新的选课退掉了J2EE,换成坑爹的面向对象，高级网络也退掉了 */ 顺便说句，除了英语，J2EE,网络程序设计以外，其他课程都必须考到75分以上的&#8230;.. 学习任务还是相当繁重的，每周的课表也排的非常满。 - 写到这里来定个计划吧 1、算法设计与分析，经典的《算法导论》，暑假里看过一些，个人觉得这本书很严谨但是枯燥难懂。上课要认真听讲，对于实验课要求的算法必须要编程实现。 2、程序设计与计算机系统，经典的《CS:APP》，这本书上的习题应该要全部做完吧！除了个别四颗星号的难题。 3、其他的等开学上课以后再说吧，毕竟现在对某些情况还不了解。 4、要坚持写博客(呃&#8230;想到了Lieo同学)，尼玛这每年的域名费、主机费不能白交啊，技术上的、生活上的都要经常记录。 - 写到这里，已经是2011.9.1  00:19了，再次感叹一下时间过的太TM快了。 莫等闲，白了少年头，空悲切！ &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 原创文章，转载请注明出处。 转载自Just for Fun 本文链接地址: 写在开学前]]></description>
			<content:encoded><![CDATA[<div>
<div>暑假终究是要过完的，再次感叹一下时间过得飞快！</div>
<div>翻一翻毕业照片，感觉好像也就是前几天发生的事情一样，熟悉的笑容，熟悉的校园，只是再也回不到那个计科07班了；</div>
<div>翻一翻暑假买的书，感觉好像也就是昨天才送来的一样，依旧没翻几页。</div>
<div>-</div>
<div>回想这个暑假，甚至都没写几行代码。</div>
<div>坐在书桌前的时间倒是不少，但真正好像也没看进去多少书，不然现在怎么回想不起来呢。</div>
<div>为毛会这个样子呢？（好像一直都这个样子的吧，囧）<span id="more-434"></span></div>
<div>-</div>
<div>不过人还是要向前看的，过去的就过去了，要好好把握未来。</div>
<div>明天去学校又可以认识一大帮新同学，组成一个新班级，希望在新同学中能找到志同道合的朋友（虽然概率很小）。</div>
<div>然后好好学习，这学期开得课我都非常喜欢，用的教材也是非常经典的书，而且据说实验在整个教学中所占的比例很大，这些正是我希望的。</div>
<div>-</div>
<div>总之要把握好这短短的一年时间。</div>
<div>说到坑爹的选课就吐槽一下，本来通知上写的是8月20号开通选课系统，但是在那一天我打开了选课网站发现依然没开始，于是我想：继续等学院首页的通知吧！一转眼就到了23号，说到这里要感谢一下<strong><span style="color: #0000ff;">Dave</span></strong>的好友<a href="http://www.ju7tme.tk/" target="_blank">老妖前辈</a>（他正好是我的学长），还是他那天在人人网上问我选课开始没，我一打开网站，发现好多课选得人数已经满了，尼玛欲哭无泪啊！然后我就一直刷屏看有没有人退课&#8230;.（中间省略n多字，5天过去了）&#8230;.28号上午，经过我的不懈的努力的刷屏，加上运气也不错，最终想学的课都被我选到了。</div>
<div>-</div>
<div>来欣赏一下这些苦逼的课程吧。</div>
<div><a href="https://byfiles.storage.live.com/y1pwTs3N0nYGkTlYjEYUNllCA7uvYKVjLacjCo7lJADEj7DzbVJM5IKI8D-nnU7aVzyxd_aoxDs3hw/kecheng.png?psid=1"><img class="alignnone" src="https://byfiles.storage.live.com/y1pwTs3N0nYGkTlYjEYUNllCA7uvYKVjLacjCo7lJADEj7DzbVJM5IKI8D-nnU7aVzyxd_aoxDs3hw/kecheng.png?psid=1" alt="" width="163" height="224" /></a></div>
<div>/* &#8212;&#8212;&#8212;&#8212;&#8212;&#8211;Update&#8212;&#8212;&#8212;&#8212;</div>
<div>最新的选课退掉了J2EE,换成坑爹的面向对象，高级网络也退掉了</div>
<div>*/</div>
<div></div>
<div>顺便说句，除了英语，J2EE,网络程序设计以外，其他课程都必须考到<strong><span style="color: #ff0000;">75</span></strong>分以上的&#8230;..</div>
<div>学习任务还是相当繁重的，每周的课表也排的非常满。</div>
<div>-</div>
<div>写到这里来定个计划吧</div>
<div>1、算法设计与分析，经典的《算法导论》，暑假里看过一些，个人觉得这本书很严谨但是枯燥难懂。上课要认真听讲，对于实验课要求的算法必须要编程实现。</div>
<div>2、程序设计与计算机系统，经典的《CS:APP》，这本书上的习题应该要全部做完吧！除了个别四颗星号的难题。</div>
<div>3、其他的等开学上课以后再说吧，毕竟现在对某些情况还不了解。</div>
<div>4、<span style="color: #ff0000;">要坚持写博客</span>(呃&#8230;想到了Lieo同学)，尼玛这每年的域名费、主机费不能白交啊，技术上的、生活上的都要经常记录。</div>
<div>-</div>
<div>写到这里，已经是2011.9.1  00:19了，再次感叹一下时间过的太TM快了。</div>
<h2><strong><span style="color: #ff0000;">莫等闲，白了少年头，空悲切！</span></strong></h2>
</div>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=434">写在开学前</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=434</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>python版本问题导致iBus无法启动</title>
		<link>http://blog.nlogn.cn/?p=426</link>
		<comments>http://blog.nlogn.cn/?p=426#comments</comments>
		<pubDate>Thu, 18 Aug 2011 05:54:32 +0000</pubDate>
		<dc:creator>Snail</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://blog.nlogn.cn/?p=426</guid>
		<description><![CDATA[Python 新旧版本之间的语法不兼容，导致iBus图标不显示，候选词框也不显示；导致Wicd无法启动。如下图所示。 问题产生： 由于Ubuntu默认安装了python2.6 ，后来我自己又装了python3.1，但是在终端里面输入python启动的是2.6版本的，这个是系统的默认，我们可以查看/usr/bin/目录，如图： &#160; 可以看到，python实际上是一个软链接，指向了python2.6 。我把它修改指向python3.1，这样输入python直接启动3.1了，方便我在终端下启动python3.1的交互界面。 这样做带来了文章开头所说的问题，一开始我没想到跟python有关，以为是什么配置文件出错了，于是网上google之，用了不少方法都没有解决问题，后来想：在终端下启动，应该会有一些出错信息吧。于是我输入wicd ，果然出现了错误提示： File “/usr/share/wicd/daemon/wicd-daemon.py”,line 122 print “&#8211;no-autoconnect detected,not autoconnecting&#8230;” SyntaxError: invalid syntax 看到这句话第一反应是：程序的源码出现语法错误了？不可能啊，以前还正常运行过。 于是打开源代码。 看到print 这一句我恍然大悟了，因为在python3.X中是不允许这样的语法的，这个是2.X的语法，于是知道肯定是修改python链接的指向导致了这个问题，解决办法就是修改回去喽。之后这两个程序都正常显示图标，也能正常使用了。 写这篇文章的意义在于： 1、blog很久没更新了，发一篇充数。 2、发现了另外一种导致iBus 和wicd 等依赖python的程序无法正常启动的原因。网上很多iBus的错误都集中在配置文件和环境变量方面，像我这样的python版本之间语法不兼容导致的错误倒是没看到，所以记录之。 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- 原创文章，转载请注明出处。 转载自Just for Fun 本文链接地址: python版本问题导致iBus无法启动]]></description>
			<content:encoded><![CDATA[<p><strong><span style="color: #0000ff;">Python 新旧版本之间的语法不兼容，导致iBus图标不显示，候选词框也不显示；导致Wicd无法启动</span></strong>。如下图所示。</p>
<p><a href="https://byfiles.storage.live.com/y1pWcIE5lpbZXY4FfkDjNxYSv3qJkE-ddfJFtCG2oQ5uZsgCbxRvLDGq9S0hrSHXnh_iYK_cmvOuMpRqxF5zFoPhQ/1.png?psid=1"><img class="alignnone" src="https://byfiles.storage.live.com/y1pWcIE5lpbZXY4FfkDjNxYSv3qJkE-ddfJFtCG2oQ5uZsgCbxRvLDGq9S0hrSHXnh_iYK_cmvOuMpRqxF5zFoPhQ/1.png?psid=1" alt="" width="172" height="42" /></a></p>
<p><strong><span style="color: #ff0000;">问题产生：</span></strong></p>
<p><strong></strong>由于Ubuntu默认安装了python2.6 ，后来我自己又装了python3.1，但是在终端里面输入python启动的是2.6版本的，这个是系统的默认，我们可以查看/usr/bin/目录，如图：<span id="more-426"></span></p>
<p>&nbsp;</p>
<p><a href="https://byfiles.storage.live.com/y1pcgioKQrzNeaWnhRr-OV0ql3YudC01YJj_2KDoV7oSad71jPbqzQOFa_SNzWazZa6CsR9RFNrjvW8GgmwOrmYwA/6.png?psid=1"><img class="alignnone" src="https://byfiles.storage.live.com/y1pcgioKQrzNeaWnhRr-OV0ql3YudC01YJj_2KDoV7oSad71jPbqzQOFa_SNzWazZa6CsR9RFNrjvW8GgmwOrmYwA/6.png?psid=1" alt="" width="231" height="117" /></a></p>
<p>可以看到，python实际上是一个软链接，指向了python2.6 。我把它修改指向python3.1，这样输入python直接启动3.1了，方便我在终端下启动python3.1的交互界面。</p>
<p>这样做带来了文章开头所说的问题，一开始我没想到跟python有关，以为是什么配置文件出错了，于是网上google之，用了不少方法都没有解决问题，后来想：在终端下启动，应该会有一些出错信息吧。于是我输入wicd ，果然出现了错误提示：</p>
<blockquote><p>File “/usr/share/wicd/daemon/wicd-daemon.py”,line 122<br />
print “&#8211;no-autoconnect detected,not autoconnecting&#8230;”<br />
SyntaxError: invalid syntax</p></blockquote>
<p>看到这句话第一反应是：程序的源码出现语法错误了？不可能啊，以前还正常运行过。<br />
于是打开源代码。</p>
<p><a href="https://byfiles.storage.live.com/y1pcgioKQrzNebZ9Z-2yFyw6BGpL-fZoyZNlFQzHNZKRRwugC8ImJUsMnsnqy2oKWv5j4BGf5aWMzvEBa5gMb7R9A/4.png?psid=1"><img class="alignnone" src="https://byfiles.storage.live.com/y1pcgioKQrzNebZ9Z-2yFyw6BGpL-fZoyZNlFQzHNZKRRwugC8ImJUsMnsnqy2oKWv5j4BGf5aWMzvEBa5gMb7R9A/4.png?psid=1" alt="" width="608" height="98" /></a></p>
<p>看到print 这一句我恍然大悟了，因为在python3.X中是不允许这样的语法的，这个是2.X的语法，于是知道肯定是修改python链接的指向导致了这个问题，解决办法就是修改回去喽。之后这两个程序都正常显示图标，也能正常使用了。</p>
<p><a href="https://byfiles.storage.live.com/y1pGsogX6gHOG2iHmKz1hssdg5QhEQSHITbwWAeK7P540aocpqfyY-wZQkzk5JIFjgafSRXvKoSL9VVY5mF4JuUog/7.png?psid=1"><img class="alignnone" src="https://byfiles.storage.live.com/y1pGsogX6gHOG2iHmKz1hssdg5QhEQSHITbwWAeK7P540aocpqfyY-wZQkzk5JIFjgafSRXvKoSL9VVY5mF4JuUog/7.png?psid=1" alt="" width="182" height="33" /></a></p>
<p><span style="color: #0000ff;">写这篇文章的意义在于：</span><br />
<span style="color: #0000ff;"> 1、blog很久没更新了，发一篇充数。</span><br />
<span style="color: #0000ff;"> 2、发现了另外一种导致iBus 和wicd 等依赖python的程序无法正常启动的原因。网上很多iBus的错误都集中在配置文件和环境变量方面，像我这样的python版本之间语法不兼容导致的错误倒是没看到，所以记录之。</span>
</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-
<div style="margin-top: 15px; font-style: italic">
<p><strong>原创文章，转载请注明出处。</strong> 转载自<a href="http://blog.nlogn.cn/">Just  for Fun</a></p>
<p><strong>本文链接地址:</strong> <a href="http://blog.nlogn.cn/?p=426">python版本问题导致iBus无法启动</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.nlogn.cn/?feed=rss2&#038;p=426</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

